Changes On Branch release
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch trunk Through [d2e07756d9] Excluding Merge-Ins

This is equivalent to a diff from c06edd231f to d2e07756d9

2013-02-16
14:12
Limit the complexity of the diff display on check-in information pages. check-in: 4f95ea8c56 user: drh tags: trunk
00:04
Version 1.25 check-in: d2e07756d9 user: drh tags: trunk, release, version-1.25
2013-02-15
19:57
Improvements to UTF-16 byte-order-mark detection. check-in: 3d988df670 user: mistachkin tags: trunk
2010-05-16
19:16
Rename the "clear-title" branch as the new trunk. The trunk is now relicensed using the Simplified BSD License. check-in: bf1c21ba16 user: drh tags: trunk
19:08
Change from GPL to the Simplified BSD License. Closed-Leaf check-in: c06edd231f user: drh tags: clear-title
11:18
Pull the latest trunk changes into clear-title. check-in: 96722b6d01 user: drh tags: clear-title

Added .project.

            1  +<?xml version="1.0" encoding="UTF-8"?>
            2  +<projectDescription>
            3  +	<name>fossil</name>
            4  +	<comment></comment>
            5  +	<projects>
            6  +	</projects>
            7  +	<buildSpec>
            8  +	</buildSpec>
            9  +	<natures>
           10  +	</natures>
           11  +</projectDescription>

Added .settings/org.eclipse.core.resources.prefs.

            1  +eclipse.preferences.version=1
            2  +encoding/<project>=UTF-8

Added .settings/org.eclipse.core.runtime.prefs.

            1  +eclipse.preferences.version=1
            2  +line.separator=\n

Changes to BUILD.txt.

     1         -All of the source code for fossil is contained in the src/ subdirectory.
     2         -But there is a lot of generated code, so you will probably want to
     3         -use the Makefile.  To do a complete build, just type:
            1  +To do a complete build, just type:
     4      2   
     5         -   make
            3  +   ./configure; make
     6      4   
     7         -That should work out-of-the-box on Macs and Linux systems. If you are
     8         -building on a Windows box, install MinGW as well as MinGW's make (or
     9         -MSYS). You can then type:
            5  +The ./configure script builds Makefile from Makefile.in based on
            6  +your system and any options you select (run "./configure --help"
            7  +for a listing of the available options.)
    10      8   
    11         -   make -f Makefile.w32
            9  +If you wish to use the original Makefile with no configuration, you can
           10  +instead use:
           11  +
           12  +   make -f Makefile.classic
           13  +
           14  +On a windows box, use one of the Makefiles in the win/ subdirectory,
           15  +according to your compiler and environment.  If you have MinGW or
           16  +MinGW-w64 installed on your system (Msys or Cygwin, or as
           17  +cross-compile environment on Linux or Darwin), then consider:
           18  +
           19  +   make -f win/Makefile.mingw
           20  +
           21  +If you have VC++ installed on your system, then consider:
           22  +
           23  +   cd win; nmake /f Makefile.msc
    12     24   
    13     25   If you have trouble, or you want to do something fancy, just look at
    14         -top level makefile. There are 6 configuration options that are all well
    15         -commented. Instead of editing the Makefile, consider copying the Makefile
    16         -to an alternative name such as "GNUMakefile", "BSDMakefile", or "makefile"
    17         -and editing the copy.
           26  +Makefile.classic. There are 6 configuration options that are all well
           27  +commented. Instead of editing the Makefile.classic, consider copying 
           28  +Makefile.classic to an alternative name such as "GNUMakefile",
           29  +"BSDMakefile", or "makefile" and editing the copy.
    18     30   
    19         -Out of source builds?
    20         ---------------------------------------------------------------------------
           31  +
           32  +BUILDING OUTSIDE THE SOURCE TREE
    21     33   
    22     34   An out of source build is pretty easy:
    23     35   
    24         -  1. Make a new directory to do the builds in.
    25         -  2. Copy "Makefile" from the source into the build directory and
    26         -  modify the SRCDIR macro along the lines of:
           36  +  1. Make and change to a new directory to do the builds in.
           37  +  2. Run the "configure" script from this directory.
           38  +  3. Type: "make"
           39  +
           40  +For example:
    27     41   
    28         -    SRCDIR=../src
           42  +  mkdir build
           43  +  cd build
           44  +  ../configure
           45  +  make
    29     46   
    30         -  3. type: "make"
    31         -
    32         -This will now keep all generates files seperate from the maintained
           47  +This will now keep all generates files separate from the maintained
    33     48   source code.
    34     49   
    35     50   --------------------------------------------------------------------------
    36     51   
    37     52   Here are some notes on what is happening behind the scenes:
           53  +
           54  +* The configure script (if used) examines the options given
           55  +  and runs various tests with the C compiler to create Makefile
           56  +  from the Makefile.in template as well as autoconfig.h
    38     57   
    39     58   * The Makefile just sets up a few macros and then invokes the
    40     59     real makefile in src/main.mk.  The src/main.mk makefile is
    41     60     automatically generated by a TCL script found at src/makemake.tcl.
    42     61     Do not edit src/main.mk directly.  Update src/makemake.tcl and
    43     62     then rerun it.
    44     63   
................................................................................
    50     69   * Most *.c source files are preprocessed using a program called
    51     70     "translate".  The sources to translate are found in src/translate.c.
    52     71     A header comment in src/translate.c explains in detail what it does.
    53     72   
    54     73   * The src/mkindex.c program generates some C code that implements
    55     74     static lookup tables.  See the header comment in the source code
    56     75     for details on what it does.
           76  +
           77  +Additional information on the build process is available from
           78  +http://www.fossil-scm.org/fossil/doc/trunk/www/makefile.wiki

Deleted Makefile.

     1         -#!/usr/bin/make
     2         -#
     3         -#### The toplevel directory of the source tree.  Fossil can be built
     4         -#    in a directory that is separate from the source tree.  Just change
     5         -#    the following to point from the build directory to the src/ folder.
     6         -#
     7         -SRCDIR = ./src
     8         -
     9         -#### The directory into which object code files should be written.
    10         -#
    11         -#
    12         -OBJDIR = ./obj
    13         -
    14         -#### C Compiler and options for use in building executables that
    15         -#    will run on the platform that is doing the build.  This is used
    16         -#    to compile code-generator programs as part of the build process.
    17         -#    See TCC below for the C compiler for building the finished binary.
    18         -#
    19         -BCC = gcc -g -O2
    20         -
    21         -#### The suffix to add to executable files.  ".exe" for windows.
    22         -#    Nothing for unix.
    23         -#
    24         -E =
    25         -
    26         -#### C Compile and options for use in building executables that 
    27         -#    will run on the target platform.  This is usually the same
    28         -#    as BCC, unless you are cross-compiling.  This C compiler builds
    29         -#    the finished binary for fossil.  The BCC compiler above is used
    30         -#    for building intermediate code-generator tools.
    31         -#
    32         -#TCC = gcc -O6
    33         -#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
    34         -TCC = gcc -g -Os -Wall
    35         -
    36         -#### Extra arguments for linking the finished binary.  Fossil needs
    37         -#    to link against the Z-Lib compression library.  There are no
    38         -#    other dependencies.  We sometimes add the -static option here
    39         -#    so that we can build a static executable that will run in a
    40         -#    chroot jail.
    41         -#
    42         -LIB = -lz $(LDFLAGS)
    43         -# If you're on OpenSolaris:
    44         -# LIB += lsocket
    45         -# Solaris 10 needs:
    46         -# LIB += -lsocket -lnsl
    47         -# My assumption is that the Sol10 flags will work for Sol8/9 and possibly 11.
    48         -# 
    49         -
    50         -#### Tcl shell for use in running the fossil testsuite.
    51         -#
    52         -TCLSH = tclsh
    53         -
    54         -# You should not need to change anything below this line
    55         -###############################################################################
    56         -include $(SRCDIR)/main.mk

Added Makefile.classic.

            1  +#!/usr/bin/make
            2  +#
            3  +# This is the top-level makefile for Fossil when the build is occurring
            4  +# on a unix platform.  This works out-of-the-box on most unix platforms.
            5  +# But you are free to vary some of the definitions if desired.
            6  +#
            7  +#### The toplevel directory of the source tree.  Fossil can be built
            8  +#    in a directory that is separate from the source tree.  Just change
            9  +#    the following to point from the build directory to the src/ folder.
           10  +#
           11  +SRCDIR = ./src
           12  +
           13  +#### The directory into which object code files should be written.
           14  +#
           15  +#
           16  +OBJDIR = ./bld
           17  +
           18  +#### C Compiler and options for use in building executables that
           19  +#    will run on the platform that is doing the build.  This is used
           20  +#    to compile code-generator programs as part of the build process.
           21  +#    See TCC below for the C compiler for building the finished binary.
           22  +#
           23  +BCC = gcc
           24  +
           25  +#### The suffix to add to final executable file.  When cross-compiling
           26  +#    to windows, make this ".exe".  Otherwise leave it blank.
           27  +#
           28  +E =
           29  +
           30  +#### C Compile and options for use in building executables that 
           31  +#    will run on the target platform.  This is usually the same
           32  +#    as BCC, unless you are cross-compiling.  This C compiler builds
           33  +#    the finished binary for fossil.  The BCC compiler above is used
           34  +#    for building intermediate code-generator tools.
           35  +#
           36  +#TCC = gcc -O6
           37  +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
           38  +TCC = gcc -g -Os -Wall
           39  +
           40  +# To add support for HTTPS
           41  +TCC += -DFOSSIL_ENABLE_SSL
           42  +
           43  +#### Extra arguments for linking the finished binary.  Fossil needs
           44  +#    to link against the Z-Lib compression library.  There are no
           45  +#    other dependencies.  We sometimes add the -static option here
           46  +#    so that we can build a static executable that will run in a
           47  +#    chroot jail.
           48  +#
           49  +LIB = -lz $(LDFLAGS)
           50  +
           51  +# If using HTTPS:
           52  +LIB += -lcrypto -lssl
           53  +
           54  +#### Tcl shell for use in running the fossil testsuite.  If you do not
           55  +#    care about testing the end result, this can be blank.
           56  +#
           57  +TCLSH = tclsh
           58  +
           59  +# You should not need to change anything below this line
           60  +###############################################################################
           61  +#
           62  +# Automatic platform-specific options.
           63  +HOST_OS_CMD = uname -s
           64  +HOST_OS = $(HOST_OS_CMD:sh)
           65  +
           66  +LIB.SunOS= -lsocket -lnsl
           67  +LIB += $(LIB.$(HOST_OS))
           68  +
           69  +TCC.DragonFly += -DUSE_PREAD
           70  +TCC.FreeBSD += -DUSE_PREAD
           71  +TCC.NetBSD += -DUSE_PREAD
           72  +TCC.OpenBSD += -DUSE_PREAD
           73  +TCC += $(TCC.$(HOST_OS))
           74  +
           75  +include $(SRCDIR)/main.mk

Added Makefile.in.

            1  +#!/usr/bin/make
            2  +#
            3  +# This is the top-level makefile for Fossil when the build is occurring
            4  +# on a unix platform.  This works out-of-the-box on most unix platforms.
            5  +# But you are free to vary some of the definitions if desired.
            6  +#
            7  +#### The toplevel directory of the source tree.  Fossil can be built
            8  +#    in a directory that is separate from the source tree.  Just change
            9  +#    the following to point from the build directory to the src/ folder.
           10  +#
           11  +SRCDIR = @srcdir@/src
           12  +
           13  +#### The directory into which object code files should be written.
           14  +#    Having a "./" prefix in the value of this variable breaks our use of the
           15  +#    "makeheaders" tool when running make on the MinGW platform, apparently
           16  +#    due to some command line argument manipulation performed automatically
           17  +#    by the shell.
           18  +#
           19  +#
           20  +OBJDIR = bld
           21  +
           22  +#### C Compiler and options for use in building executables that
           23  +#    will run on the platform that is doing the build.  This is used
           24  +#    to compile code-generator programs as part of the build process.
           25  +#    See TCC below for the C compiler for building the finished binary.
           26  +#
           27  +BCC = @CC_FOR_BUILD@
           28  +
           29  +#### The suffix to add to final executable file.  When cross-compiling
           30  +#    to windows, make this ".exe".  Otherwise leave it blank.
           31  +#
           32  +E = @EXEEXT@
           33  +
           34  +TCC = @CC@
           35  +
           36  +#### Tcl shell for use in running the fossil testsuite.  If you do not
           37  +#    care about testing the end result, this can be blank.
           38  +#
           39  +TCLSH = tclsh
           40  +
           41  +LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
           42  +TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
           43  +INSTALLDIR = $(DESTDIR)@prefix@/bin
           44  +USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
           45  +FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
           46  +FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@
           47  +
           48  +include $(SRCDIR)/main.mk
           49  +
           50  +distclean: clean
           51  +	rm -f autoconfig.h config.log Makefile

Deleted Makefile.w32.

     1         -#!/usr/bin/make
     2         -#
     3         -#### The toplevel directory of the source tree.  Fossil can be built
     4         -#    in a directory that is separate from the source tree.  Just change
     5         -#    the following to point from the build directory to the src/ folder.
     6         -#
     7         -SRCDIR = ./src
     8         -OBJDIR = ./wobj
     9         -
    10         -#### C Compiler and options for use in building executables that
    11         -#    will run on the platform that is doing the build.  This is used
    12         -#    to compile code-generator programs as part of the build process.
    13         -#    See TCC below for the C compiler for building the finished binary.
    14         -#
    15         -BCC = gcc -g -O2
    16         -
    17         -#### The suffix to add to executable files.  ".exe" for windows.
    18         -#    Nothing for unix.
    19         -#
    20         -E = .exe
    21         -
    22         -#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
    23         -#
    24         -# FOSSIL_ENABLE_SSL=1
    25         -
    26         -#### C Compile and options for use in building executables that 
    27         -#    will run on the target platform.  This is usually the same
    28         -#    as BCC, unless you are cross-compiling.  This C compiler builds
    29         -#    the finished binary for fossil.  The BCC compiler above is used
    30         -#    for building intermediate code-generator tools.
    31         -#
    32         -#TCC = gcc -O6
    33         -#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
    34         -#TCC = gcc -g -Os -Wall
    35         -#TCC = gcc -g -Os -Wall -DFOSSIL_I18N=0 -L/usr/local/lib -I/usr/local/include
    36         -TCC = gcc -Os -Wall -DFOSSIL_I18N=0 -L/mingw/lib -I/mingw/include
    37         -
    38         -# With HTTPS support
    39         -ifdef FOSSIL_ENABLE_SSL
    40         -TCC += -DFOSSIL_ENABLE_SSL=1
    41         -endif
    42         -
    43         -#### Extra arguments for linking the finished binary.  Fossil needs
    44         -#    to link against the Z-Lib compression library.  There are no
    45         -#    other dependencies.  We sometimes add the -static option here
    46         -#    so that we can build a static executable that will run in a
    47         -#    chroot jail.
    48         -#
    49         -#LIB = -lz
    50         -#LIB = -lz -lws2_32
    51         -LIB = -lmingwex -lz -lws2_32
    52         -# OpenSSL:
    53         -ifdef FOSSIL_ENABLE_SSL
    54         -LIB += -lcrypto -lssl
    55         -endif
    56         -
    57         -#### Tcl shell for use in running the fossil testsuite.
    58         -#
    59         -TCLSH = tclsh
    60         -
    61         -#### Include a configuration file that can override any one of these settings.
    62         -#
    63         --include config.w32
    64         -
    65         -# You should not need to change anything below this line
    66         -###############################################################################
    67         -include $(SRCDIR)/main.mk

Added VERSION.

            1  +1.25

Added ajax/README.

            1  +This is the README for how to set up the Fossil/JSON test web page
            2  +under Apache on Unix systems. This is only intended only for
            3  +Fossil/JSON developers/tinkerers:
            4  +
            5  +First, copy cgi-bin/fossil-json.cgi.example to
            6  +cgi-bin/fossil-json.cgi.  Edit it and correct the paths to the fossil
            7  +binary and the repo you want to serve. Make it executable.
            8  +
            9  +MAKE SURE that the fossil repo you use is world-writable OR that your
           10  +Web/CGI server is set up to run as the user ID of the owner of the
           11  +fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
           12  +writable by the CGI process.
           13  +
           14  +Next, set up an apache vhost entry. Mine looks like:
           15  +
           16  +<VirtualHost *:80>
           17  +    ServerAlias fjson
           18  +    ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
           19  +    DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
           20  +</VirtualHost>
           21  +
           22  +Now add your preferred vhost name (fjson in the above example) to /etc/hosts:
           23  +
           24  +  127.0.0.1 ...other aliases... fjson
           25  +
           26  +Restart your Apache.
           27  +
           28  +Now visit: http://fjson/
           29  +
           30  +that will show the test/demo page. If it doesn't, edit index.html and
           31  +make sure that:
           32  +
           33  +  WhAjaj.Connector.options.ajax.url = ...;
           34  +
           35  +points to your CGI script. In theory you can also do this over fossil
           36  +standalone server mode, but i haven't yet tested that particular test
           37  +page in that mode.
           38  +

Added ajax/cgi-bin/fossil-json.cgi.example.

            1  +#!/path/to/fossil/binary
            2  +repository: /path/to/repo.fsl

Added ajax/i-test/rhino-shell.js.

            1  +var FShell = {
            2  +    serverUrl:
            3  +        'http://localhost:8080'
            4  +        //'http://fjson/cgi-bin/fossil-json.cgi'
            5  +        //'http://192.168.1.62:8080'
            6  +        //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
            7  +        ,
            8  +    verbose:false,
            9  +    prompt:"fossil shell > ",
           10  +    wiki:{},
           11  +    consol:java.lang.System.console(),
           12  +    v:function(msg){
           13  +        if(this.verbose){
           14  +            print("VERBOSE: "+msg);
           15  +        }
           16  +    }
           17  +};
           18  +(function bootstrap() {
           19  +    var srcdir = '../js/';
           20  +    var includes = [srcdir+'json2.js',
           21  +                    srcdir+'whajaj.js',
           22  +                    srcdir+'fossil-ajaj.js'
           23  +                    ];
           24  +    for( var i in includes ) {
           25  +        load(includes[i]);
           26  +    }
           27  +    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
           28  +    FShell.fossil = new FossilAjaj({
           29  +        asynchronous:false, /* rhino-based impl doesn't support async. */
           30  +        timeout:10000,
           31  +        url:FShell.serverUrl
           32  +    });
           33  +    print("Server: "+FShell.serverUrl);
           34  +    var cb = FShell.fossil.ajaj.callbacks;
           35  +    cb.beforeSend = function(req,opt){
           36  +        if(!FShell.verbose) return;
           37  +        print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
           38  +        if(req) print("Request envelope="+WhAjaj.stringify(req));
           39  +    };
           40  +    cb.afterSend = function(req,opt){
           41  +        //if(!FShell.verbose) return;
           42  +        //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
           43  +        //if(req) print("Request="+WhAjaj.stringify(req));
           44  +    };
           45  +    cb.onError = function(req,opt){
           46  +        //if(!FShell.verbose) return;
           47  +        print("ERROR: "+WhAjaj.stringify(opt));
           48  +    };
           49  +    cb.onResponse = function(resp,req){
           50  +        if(!FShell.verbose) return;
           51  +        if(resp && resp.resultCode){
           52  +            print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
           53  +        }
           54  +        print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
           55  +    };
           56  +    FShell.fossil.HAI({
           57  +        onResponse:function(resp,opt){
           58  +            assertResponseOK(resp);
           59  +        }
           60  +    });
           61  +})();
           62  +
           63  +/**
           64  +    Throws an exception of cond is a falsy value.
           65  +*/
           66  +function assert(cond, descr){
           67  +    descr = descr || "Undescribed condition.";
           68  +    if(!cond){
           69  +        throw new Error("Assertion failed: "+descr);
           70  +    }else{
           71  +        //print("Assertion OK: "+descr);
           72  +    }
           73  +}
           74  +
           75  +/**
           76  +    Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
           77  +*/
           78  +function send(command,payload, ajajOpt){
           79  +    FShell.fossil.sendCommand(command,payload,ajajOpt);
           80  +}
           81  +
           82  +/**
           83  +    Asserts that resp is-a Object, resp.fossil is-a string, and
           84  +    !resp.resultCode.
           85  +*/
           86  +function assertResponseOK(resp){
           87  +    assert('object' === typeof resp,'Response is-a object.');
           88  +    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
           89  +    assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
           90  +}
           91  +/**
           92  +    Asserts that resp is-a Object, resp.fossil is-a string, and
           93  +    resp.resultCode is a truthy value. If expectCode is set then
           94  +    it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
           95  +*/
           96  +function assertResponseError(resp,expectCode){
           97  +    assert('object' === typeof resp,'Response is-a object.');
           98  +    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
           99  +    assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
          100  +    if(expectCode){
          101  +        assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
          102  +    }
          103  +}
          104  +
          105  +FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
          106  +     importPackage(java.io);
          107  +     importPackage(java.lang);
          108  +     var stdin = new BufferedReader(new InputStreamReader(System['in']));
          109  +     var self = this;
          110  +     return function(prompt) {
          111  +        if(prompt) print(prompt);
          112  +        var x = stdin.readLine();
          113  +        return null===x ? x : String(x) /*convert to JS string!*/;
          114  +     };
          115  +}());
          116  +
          117  +FShell.dispatchLine = function(line){
          118  +    var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
          119  +    var cmd = av[0];
          120  +    var key, h;
          121  +    if('/' == cmd[0]) key = '/';
          122  +    else key = this.commandAliases[cmd];
          123  +    if(!key) key = cmd;
          124  +    h = this.commandHandlers[key];
          125  +    if(!h){
          126  +        print("Command not known: "+cmd +" ("+key+")");
          127  +    }else if(!WhAjaj.isFunction(h)){
          128  +        print("Not a function: "+key);
          129  +    }
          130  +    else{
          131  +        print("Sending ["+key+"] command... ");
          132  +        try{h(av);}
          133  +        catch(e){ print("EXCEPTION: "+e); }
          134  +    }
          135  +};
          136  +
          137  +FShell.onResponseDefault = function(callback){
          138  +    return function(resp,req){
          139  +        assertResponseOK(resp);
          140  +        print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
          141  +        if(WhAjaj.isFunction(callback)){
          142  +            callback(resp,req);
          143  +        }
          144  +    };
          145  +};
          146  +FShell.commandHandlers = {
          147  +    "?":function(args){
          148  +        var k;
          149  +        print("Available commands...\n");
          150  +        var o = FShell.commandHandlers;
          151  +        for(k in o){
          152  +            if(! o.hasOwnProperty(k)) continue;
          153  +            print("\t"+k);
          154  +        }
          155  +    },
          156  +    "/":function(args){
          157  +        FShell.fossil.sendCommand('/json'+args[0],undefined,{
          158  +            beforeSend:function(req,opt){
          159  +                print("Sending to: "+opt.url);
          160  +            },
          161  +            onResponse:FShell.onResponseDefault()
          162  +        });
          163  +    },
          164  +    "eval":function(args){
          165  +        eval(args.join(' '));
          166  +    },
          167  +    "login":function(args){
          168  +        FShell.fossil.login(args[1], args[2], {
          169  +            onResponse:FShell.onResponseDefault()
          170  +        });
          171  +    },
          172  +    "whoami":function(args){
          173  +        FShell.fossil.whoami({
          174  +            onResponse:FShell.onResponseDefault()
          175  +        });
          176  +    },
          177  +    "HAI":function(args){
          178  +        FShell.fossil.HAI({
          179  +            onResponse:FShell.onResponseDefault()
          180  +        });
          181  +    }
          182  +
          183  +};
          184  +FShell.commandAliases = {
          185  +    "li":"login",
          186  +    "lo":"logout",
          187  +    "who":"whoami",
          188  +    "hi":"HAI",
          189  +    "tci":"/timeline/ci?limit=3"
          190  +};
          191  +FShell.mainLoop = function(){
          192  +    var line;
          193  +    var check = /\S/;
          194  +    //var isJavaNull = /java\.lang\.null/;
          195  +    //print(typeof java.lang['null']);
          196  +    while( null != (line=this.readline(this.prompt)) ){
          197  +        if(null===line) break /*EOF*/;
          198  +        else if( "" === line ) continue;
          199  +        //print("Got line: "+line);
          200  +        else if(!check.test(line)) continue;
          201  +        print('typeof line = '+typeof line);
          202  +        this.dispatchLine(line);
          203  +        print("");
          204  +    }
          205  +    print("Bye!");
          206  +};
          207  +
          208  +FShell.mainLoop();

Added ajax/i-test/rhino-test.js.

            1  +var TestApp = {
            2  +    serverUrl:
            3  +        'http://localhost:8080'
            4  +        //'http://fjson/cgi-bin/fossil-json.cgi'
            5  +        //'http://192.168.1.62:8080'
            6  +        //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
            7  +        ,
            8  +    verbose:true,
            9  +    fossilBinary:'fossil',
           10  +    wiki:{}
           11  +};
           12  +(function bootstrap() {
           13  +    var srcdir = '../js/';
           14  +    var includes = [srcdir+'json2.js',
           15  +                    srcdir+'whajaj.js',
           16  +                    srcdir+'fossil-ajaj.js'
           17  +                    ];
           18  +    for( var i in includes ) {
           19  +        load(includes[i]);
           20  +    }
           21  +    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
           22  +    TestApp.fossil = new FossilAjaj({
           23  +        asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
           24  +        timeout:0,
           25  +        url:TestApp.serverUrl,
           26  +        fossilBinary:TestApp.fossilBinary
           27  +    });
           28  +    var cb = TestApp.fossil.ajaj.callbacks;
           29  +    cb.beforeSend = function(req,opt){
           30  +        if(!TestApp.verbose) return;
           31  +        print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
           32  +        if(req) print("Request envelope="+WhAjaj.stringify(req));
           33  +    };
           34  +    cb.afterSend = function(req,opt){
           35  +        //if(!TestApp.verbose) return;
           36  +        //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
           37  +        //if(req) print("Request="+WhAjaj.stringify(req));
           38  +    };
           39  +    cb.onError = function(req,opt){
           40  +        if(!TestApp.verbose) return;
           41  +        print("ERROR: "+WhAjaj.stringify(opt));
           42  +    };
           43  +    cb.onResponse = function(resp,req){
           44  +        if(!TestApp.verbose) return;
           45  +        print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
           46  +    };
           47  +    
           48  +})();
           49  +
           50  +/**
           51  +    Throws an exception of cond is a falsy value.
           52  +*/
           53  +function assert(cond, descr){
           54  +    descr = descr || "Undescribed condition.";
           55  +    if(!cond){
           56  +        print("Assertion FAILED: "+descr);
           57  +        throw new Error("Assertion failed: "+descr);
           58  +        // aarrgghh. Exceptions are of course swallowed by
           59  +        // the AJAX layer, to keep from killing a browser's
           60  +        // script environment.
           61  +    }else{
           62  +        if(TestApp.verbose) print("Assertion OK: "+descr);
           63  +    }
           64  +}
           65  +
           66  +/**
           67  +    Calls func() in a try/catch block and throws an exception if
           68  +    func() does NOT throw.
           69  +*/
           70  +function assertThrows(func, descr){
           71  +    descr = descr || "Undescribed condition failed.";
           72  +    var ex;
           73  +    try{
           74  +        func();
           75  +    }catch(e){
           76  +        ex = e;
           77  +    }
           78  +    if(!ex){
           79  +        throw new Error("Function did not throw (as expected): "+descr);
           80  +    }else{
           81  +        if(TestApp.verbose) print("Function threw (as expected): "+descr+": "+ex);
           82  +    }
           83  +}
           84  +
           85  +/**
           86  +    Convenience form of TestApp.fossil.sendCommand(command,payload,ajajOpt).
           87  +*/
           88  +function send(command,payload, ajajOpt){
           89  +    TestApp.fossil.sendCommand(command,payload,ajajOpt);
           90  +}
           91  +
           92  +/**
           93  +    Asserts that resp is-a Object, resp.fossil is-a string, and
           94  +    !resp.resultCode.
           95  +*/
           96  +function assertResponseOK(resp){
           97  +    assert('object' === typeof resp,'Response is-a object.');
           98  +    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
           99  +    assert( undefined === resp.resultCode, 'resp.resultCode is not set');
          100  +}
          101  +/**
          102  +    Asserts that resp is-a Object, resp.fossil is-a string, and
          103  +    resp.resultCode is a truthy value. If expectCode is set then
          104  +    it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
          105  +*/
          106  +function assertResponseError(resp,expectCode){
          107  +    assert('object' === typeof resp,'Response is-a object.');
          108  +    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
          109  +    assert( !!resp.resultCode, 'resp.resultCode='+resp.resultCode);
          110  +    if(expectCode){
          111  +        assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
          112  +    }
          113  +}
          114  +
          115  +function testHAI(){
          116  +    var rs;
          117  +    TestApp.fossil.HAI({
          118  +        onResponse:function(resp,req){
          119  +            rs = resp;
          120  +        }
          121  +    });
          122  +    assertResponseOK(rs);
          123  +    TestApp.serverVersion = rs.fossil;
          124  +    assert( 'string' === typeof TestApp.serverVersion, 'server version = '+TestApp.serverVersion);
          125  +}
          126  +testHAI.description = 'Get server version info.';
          127  +
          128  +function testIAmNobody(){
          129  +    TestApp.fossil.whoami('/json/whoami');
          130  +    assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
          131  +    assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );
          132  +   
          133  +}
          134  +testIAmNobody.description = 'Ensure that current user is "nobody".';
          135  +
          136  +
          137  +function testAnonymousLogin(){
          138  +    TestApp.fossil.login();
          139  +    assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
          140  +    assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
          141  +    TestApp.fossil.userName = null;
          142  +    TestApp.fossil.whoami('/json/whoami');
          143  +    assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
          144  +}
          145  +testAnonymousLogin.description = 'Perform anonymous login.';
          146  +
          147  +function testAnonWiki(){
          148  +    var rs;
          149  +    TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
          150  +        beforeSend:function(req,opt){
          151  +            assert( req && (req.authToken==TestApp.fossil.auth.authToken), 'Request envelope contains expected authToken.'  );
          152  +        },
          153  +        onResponse:function(resp,req){
          154  +            rs = resp;
          155  +        }
          156  +    });
          157  +    assertResponseOK(rs);
          158  +    assert( (typeof [] === typeof rs.payload) && rs.payload.length,
          159  +        "Wiki list seems to be okay.");
          160  +    TestApp.wiki.list = rs.payload;
          161  +
          162  +    TestApp.fossil.sendCommand('/json/wiki/get',{
          163  +        name:TestApp.wiki.list[0]
          164  +    },{
          165  +        onResponse:function(resp,req){
          166  +            rs = resp;
          167  +        }
          168  +    });
          169  +    assertResponseOK(rs);
          170  +    assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches expectations.");
          171  +    print("Got first wiki page: "+WhAjaj.stringify(rs.payload));
          172  +
          173  +}
          174  +testAnonWiki.description = 'Fetch wiki list as anonymous user.';
          175  +
          176  +function testFetchCheckinArtifact(){
          177  +    var art = '18dd383e5e7684ece';
          178  +    var rs;
          179  +    TestApp.fossil.sendCommand('/json/artifact',{
          180  +        'name': art
          181  +        },
          182  +        {
          183  +            onResponse:function(resp,req){
          184  +                rs = resp;
          185  +            }
          186  +        });
          187  +    assertResponseOK(rs);
          188  +    assert(3 == rs.payload.artifact.parents.length, 'Got 3 parent artifacts.');
          189  +}
          190  +testFetchCheckinArtifact.description = '/json/artifact/CHECKIN';
          191  +
          192  +function testAnonLogout(){
          193  +    var rs;
          194  +    TestApp.fossil.logout({
          195  +        onResponse:function(resp,req){
          196  +            rs = resp;
          197  +        }
          198  +    });
          199  +    assertResponseOK(rs);
          200  +    print("Ensure that second logout attempt fails...");
          201  +    TestApp.fossil.logout({
          202  +        onResponse:function(resp,req){
          203  +            rs = resp;
          204  +        }
          205  +    });
          206  +    assertResponseError(rs);
          207  +}
          208  +testAnonLogout.description = 'Log out anonymous user.';
          209  +
          210  +function testExternalProcess(){
          211  +
          212  +    var req = { command:"HAI", requestId:'testExternalProcess()' };
          213  +    var args = [TestApp.fossilBinary, 'json', '--json-input', '-'];
          214  +    var p = java.lang.Runtime.getRuntime().exec(args);
          215  +    var outs = p.getOutputStream();
          216  +    var osr = new java.io.OutputStreamWriter(outs);
          217  +    var osb = new java.io.BufferedWriter(osr);
          218  +    var json = JSON.stringify(req);
          219  +    osb.write(json,0, json.length);
          220  +    osb.close();
          221  +    req = json = outs = osr = osb = undefined;
          222  +    var ins = p.getInputStream();
          223  +    var isr = new java.io.InputStreamReader(ins);
          224  +    var br = new java.io.BufferedReader(isr);
          225  +    var line;
          226  +    
          227  +    while( null !== (line=br.readLine())){
          228  +        print(line);
          229  +    }
          230  +    br.close();
          231  +    isr.close();
          232  +    ins.close();
          233  +    p.waitFor();
          234  +}
          235  +testExternalProcess.description = 'Run fossil as external process.';
          236  +
          237  +function testExternalProcessHandler(){
          238  +    var aj = TestApp.fossil.ajaj;
          239  +    var oldImpl = aj.sendImpl;
          240  +    aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl;
          241  +    var rs;
          242  +    TestApp.fossil.sendCommand('/json/HAI',undefined,{
          243  +        onResponse:function(resp,opt){
          244  +            rs = resp;
          245  +        }
          246  +    });
          247  +    aj.sendImpl = oldImpl;
          248  +    assertResponseOK(rs);
          249  +    print("Using local fossil binary via AJAX interface, we fetched: "+
          250  +        WhAjaj.stringify(rs));
          251  +}
          252  +testExternalProcessHandler.description = 'Try local fossil binary via AJAX interface.';
          253  +
          254  +(function runAllTests(){
          255  +    var testList = [
          256  +        testHAI,
          257  +        testIAmNobody,
          258  +        testAnonymousLogin,
          259  +        testAnonWiki,
          260  +        testFetchCheckinArtifact,
          261  +        testAnonLogout,
          262  +        testExternalProcess,
          263  +        testExternalProcessHandler
          264  +    ];
          265  +    var i, f;
          266  +    for( i = 0; i < testList.length; ++i ){
          267  +        f = testList[i];
          268  +        try{
          269  +            print("Running test #"+(i+1)+": "+(f.description || "no description."));
          270  +            f();
          271  +        }catch(e){
          272  +            print("Test #"+(i+1)+" failed: "+e);
          273  +            throw e;
          274  +        }
          275  +    }
          276  +
          277  +})();
          278  +
          279  +print("Done! If you don't see an exception message in the last few lines, you win!");

Added ajax/index.html.

            1  +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            2  +	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
            3  +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
            4  +
            5  +<head>
            6  +	<title>Fossil/JSON raw request sending</title>
            7  +	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
            8  +    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> 
            9  +    <script type="text/javascript" src="js/whajaj.js"></script>
           10  +    <script type="text/javascript" src="js/fossil-ajaj.js"></script>
           11  +
           12  +<style type='text/css'>
           13  +th {
           14  +  text-align: left;
           15  +  background-color: #ececec;  
           16  +}
           17  +
           18  +.dangerWillRobinson {
           19  +    background-color: yellow;
           20  +}
           21  +</style>
           22  +
           23  +<script type='text/javascript'>
           24  +WhAjaj.Connector.options.ajax.url =
           25  +/*
           26  +    Change this to your CGI/server root path:
           27  +*/
           28  +     //'http://fjson/cgi-bin/fossil.cgi'
           29  +     //'/repos/fossil-sgb/json.cgi'
           30  +    '/cgi-bin/fossil-json.cgi'
           31  +     ;
           32  +var TheApp = {
           33  +      response:null,
           34  +      sessionID:null,
           35  +      jqe:{}/*jqe==jQuery Elements*/,
           36  +      ajaxCount:0,
           37  +      cgi: new FossilAjaj()
           38  +};
           39  +
           40  +
           41  +TheApp.startAjaxNotif = function()
           42  +{
           43  +    ++this.ajaxCount;
           44  +    TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
           45  +    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
           46  +    if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
           47  +};
           48  +
           49  +TheApp.endAjaxNotif = function()
           50  +{
           51  +    --this.ajaxCount;
           52  +    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
           53  +    if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
           54  +};
           55  +
           56  +TheApp.responseContainsError = function(resp) {
           57  +    if( resp && resp.resultCode ) {
           58  +        //alert("Error response:\n"+JSON.stringify(resp,0,4));
           59  +        TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
           60  +        //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
           61  +        //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
           62  +        TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
           63  +        return true;
           64  +    }
           65  +    return false;
           66  +};
           67  +
           68  +
           69  +TheApp.sendRequest = function() {
           70  +    var path = this.jqe.textPath.val();
           71  +    var self = this;
           72  +    var data = this.jqe.taRequest.val();
           73  +    var doPost = (data && data.length);
           74  +    var req;
           75  +    if( doPost ) try {
           76  +        req = JSON.parse(data);
           77  +    }
           78  +    catch(e) {
           79  +        TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
           80  +        return;
           81  +    }
           82  +    if( req ) {
           83  +        req.requestId = this.cgi.generateRequestId();
           84  +    }
           85  +    var self = this;
           86  +    var opt = {
           87  +        url: WhAjaj.Connector.options.ajax.url + path,
           88  +        method: doPost ? 'POST' : 'GET'
           89  +    };
           90  +    this.cgi.sendRequest( req, opt );
           91  +};
           92  +jQuery.fn.animateHighlight = function(highlightColor, duration) {
           93  +    var highlightBg = highlightColor || "#FFFF9C";
           94  +    var animateMs = duration || 1500;
           95  +    var originalBg = this.css("backgroundColor");
           96  +    this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
           97  +};
           98  +jQuery.fn.flash = function( color, duration )
           99  +{
          100  +    var current = this.css( 'color' );
          101  +    this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
          102  +    this.animate( { color: current }, duration / 2 );
          103  +};
          104  +
          105  +function myJsonPCallback(obj){
          106  +    alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
          107  +}
          108  +
          109  +jQuery(document).ready(function(){
          110  +    var ids = [// list of HTML element IDs we use often.
          111  +        'btnSend',
          112  +        'ajaxNotification',
          113  +        'currentAuthToken',
          114  +        'responseContainer',
          115  +        'taRequest',
          116  +        'taRequestOpt',
          117  +        'taResponse',
          118  +        'textPath',
          119  +        'timer'
          120  +    ];
          121  +    var i, k;
          122  +    for( i = 0; i < ids.length; ++i ) {
          123  +        k = ids[i];
          124  +        TheApp.jqe[k] = jQuery('#'+k);
          125  +    }
          126  +    TheApp.jqe.textPath.
          127  +        keyup(function(event){
          128  +            if(event.keyCode == 13){
          129  +                TheApp.sendRequest();
          130  +            }
          131  +        });
          132  +    TheApp.timer = {
          133  +        _tstart:0,_tend:0,duration:0,
          134  +        start:function(){
          135  +            this._tstart = (new Date()).getTime();
          136  +        },
          137  +        end:function(){
          138  +            this._tend = (new Date()).getTime();
          139  +            return this.duration = this._tend - this._tstart;
          140  +        }
          141  +    };
          142  +
          143  +    var ajcb = TheApp.cgi.ajaj.callbacks;
          144  +    ajcb.beforeSend = function(req,opt) {
          145  +        TheApp.timer.start();
          146  +        var val =
          147  +            req ?
          148  +            (('string'===typeof req) ? req : WhAjaj.stringify(req))
          149  +            : '';
          150  +        TheApp.jqe.taResponse.val('');
          151  +        TheApp.jqe.taRequest.val( val );
          152  +        TheApp.jqe.taRequestOpt.val( opt ? WhAjaj.stringify(opt) : '' );
          153  +        TheApp.startAjaxNotif();
          154  +    };
          155  +    ajcb.afterSend = function(req,opt) {
          156  +        TheApp.timer.end();
          157  +        TheApp.endAjaxNotif();
          158  +        TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
          159  +    };
          160  +    ajcb.onResponse = function(resp,req, opt) {
          161  +        var val;
          162  +        if(this.jsonp) return /*was already handled*/;
          163  +        try {
          164  +            val = WhAjaj.stringify(resp);
          165  +        }
          166  +        catch(e) {
          167  +            val = WhAjaj.stringify(e)
          168  +        }
          169  +        //alert("onResponse this:"+WhAjaj.stringify(this));
          170  +        //alert("val="+val);
          171  +        // FIXME: this.url is hosed for login because of how i overload onResponse()
          172  +        if( opt.url ) TheApp.jqe.textPath.val(opt.url.replace(WhAjaj.Connector.options.ajax.url,''));
          173  +        TheApp.jqe.taResponse.val( val );
          174  +    };
          175  +    ajcb.onError = function(req,opt) {
          176  +        TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
          177  +    };
          178  +
          179  +    TheApp.cgi.onLogin = function(){
          180  +      TheApp.jqe.taResponse.val( "Logged in:\n"+WhAjaj.stringify(this.auth));
          181  +      TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
          182  +    };
          183  +    TheApp.cgi.onLogout = function(){
          184  +      TheApp.jqe.taResponse.val( "Logged out!" );
          185  +      TheApp.jqe.currentAuthToken.text("Not logged in");
          186  +    };
          187  +    TheApp.cgi.whoami();
          188  +    jQuery('#headerArea').click(function(){
          189  +        jQuery(this).slideUp('fast',function(){
          190  +            jQuery(this).remove();
          191  +        });
          192  +    });
          193  +});
          194  +
          195  +</script>
          196  +
          197  +</head>
          198  +
          199  +<body>
          200  +<span id='ajaxNotification'></span>
          201  +<div id='headerArea'>
          202  +<h1>You know, for sending raw JSON requests to Fossil...</h1>
          203  +
          204  +If you're actually using this page, then you know what you're doing and don't
          205  +need help text, hoverhelp, and a snazzy interface.
          206  +
          207  +<br><br>
          208  +
          209  +
          210  +JSON API docs: <a href='https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit'>https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit</a>
          211  +
          212  +</div><!-- #headerArea -->
          213  +See also: <a href='wiki-editor.html'>prototype wiki editor</a>.
          214  +
          215  +<h2>Request...</h2>
          216  +
          217  +Path: <input type='text' size='40' id='textPath' value='/json/HAI'/>
          218  +<input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
          219  +If the POST textarea is not empty then it will be posted with the request.
          220  +<hr/>
          221  +<strong>Quick-posts:</strong><br/>
          222  +<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
          223  +<input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
          224  +<input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
          225  +<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
          226  +<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
          227  +<input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
          228  +<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
          229  +<input type='button' value='g' onclick='TheApp.cgi.sendCommand("/json/g")' />
          230  +
          231  +<br/>
          232  +
          233  +<input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
          234  +<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
          235  +<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
          236  +<input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
          237  +<input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/json/timeline/branch")' />
          238  +
          239  +<br/>
          240  +
          241  +<input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
          242  +<input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand("/json/wiki/list",{verbose:1})' />
          243  +<input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{name:"Fossil"})' />
          244  +<input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
          245  +<input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wiki/diff/e32ccdcda59e930c77c/e15992f475760cdf3a9")' />
          246  +
          247  +<br/>
          248  +
          249  +<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
          250  +<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
          251  +<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
          252  +<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
          253  +<input type='button' value='tag/add'
          254  +    onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
          255  +<input type='button' value='tag/cancel'
          256  +    onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
          257  +<input type='button' value='tag/find'
          258  +    onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
          259  +
          260  +<br/>
          261  +
          262  +<input type='button' value='diff'
          263  +    onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
          264  +<input type='button' value='diff/A/B'
          265  +    onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />
          266  +
          267  +<input type='button' value='query'
          268  +    onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />
          269  +
          270  +<input type='button' value='report list'
          271  +    onclick='TheApp.cgi.sendCommand("/json/report/list")' />
          272  +<input type='button' value='report get'
          273  +    onclick='TheApp.cgi.sendCommand("/json/report/get",2)' />
          274  +
          275  +<input type='button' value='report run'
          276  +    onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"})' />
          277  +
          278  +<input type='button' value='config/get' onclick='TheApp.cgi.sendCommand("/json/config/get")' />
          279  +
          280  +<!-- not yet ready...
          281  +<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
          282  +-->
          283  +
          284  +<!--
          285  +<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
          286  +<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
          287  +<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
          288  +<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
          289  +-->
          290  +<hr/>
          291  +<b>Login:</b>
          292  +<br/>
          293  +<input type='button' value='Anon. PW' onclick='TheApp.cgi.sendCommand("/json/anonymousPassword")' />
          294  +<input type='button' value='Anon. PW+Login' onclick='TheApp.cgi.login()' />
          295  +<br/>
          296  +name:<input type='text' id='textUser' value='json-demo' size='12'/>
          297  +pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
          298  +<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
          299  +<input type='button' value='logout' onclick='TheApp.cgi.logout()' />
          300  +<br/>
          301  +<span id='currentAuthToken' style='font-family:monospaced'></span>
          302  +
          303  +<br/>
          304  +
          305  +<hr/>
          306  +
          307  +<table>
          308  +    <tr>
          309  +        <th>POST data</th>
          310  +        <th>Request AJAJ options</th>
          311  +    </tr>
          312  +    <tr>
          313  +        <td width='50%' valign='top'>
          314  +            <textarea id='taRequest' rows='10' cols='50'></textarea>
          315  +        </td>
          316  +        <td width='50%' valign='top'>
          317  +            <textarea id='taRequestOpt' rows='10' cols='40' readonly></textarea>
          318  +        </td>
          319  +    </tr>
          320  +    <tr>
          321  +        <th colspan='2'>Response <span id='timer'></span></th>
          322  +    </tr>
          323  +    <tr>
          324  +        <td colspan='2' id='responseContainer' valign='top'>
          325  +            <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
          326  +        </td>
          327  +    </tr>
          328  +</table>
          329  +<div></div>
          330  +<div></div>
          331  +<div></div>
          332  +
          333  +</body></html>

Added ajax/js/fossil-ajaj.js.

            1  +/**
            2  +    This file contains a WhAjaj extension for use with Fossil/JSON.
            3  +
            4  +    Author: Stephan Beal (sgbeal@googlemail.com)
            5  +
            6  +    License: Public Domain
            7  +*/
            8  +
            9  +/**
           10  +    Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
           11  +    object suitable for passing to the WhAjaj.Connector() constructor.
           12  +    
           13  +    On returning, this.ajaj is-a WhAjaj.Connector instance which can 
           14  +    be used to send requests to the back-end (though the convenience 
           15  +    functions of this class are the preferred way to do it). Clients 
           16  +    are encouraged to use FossilAjaj.sendCommand() (and friends) instead 
           17  +    of the underlying WhAjaj.Connector API, since this class' API 
           18  +    contains Fossil-specific request-calling handling (e.g. of authentication
           19  +    info) whereas WhAjaj is more generic.
           20  +*/
           21  +function FossilAjaj(ajajOpt)
           22  +{
           23  +    this.ajaj = new WhAjaj.Connector(ajajOpt);
           24  +    return this;
           25  +}
           26  +
           27  +FossilAjaj.prototype.generateRequestId = function() {
           28  +    return this.ajaj.generateRequestId();
           29  +};
           30  +
           31  +/**
           32  +   Proxy for this.ajaj.sendRequest().
           33  +*/
           34  +FossilAjaj.prototype.sendRequest = function(req,opt) {
           35  +    return this.ajaj.sendRequest(req,opt);
           36  +};
           37  +
           38  +/**
           39  +    Sends a command to the fossil back-end. Command should be the
           40  +    path part of the URL, e.g. /json/stat, payload is a request-specific
           41  +    value type (may often be null/undefined). ajajOpt is an optional object
           42  +    holding WhAjaj.sendRequest()-compatible options.
           43  +    
           44  +    This function constructs a Fossil/JSON request envelope based
           45  +    on the given arguments and adds this.auth.authToken and a requestId
           46  +    to it.
           47  +*/
           48  +FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
           49  +    var req;
           50  +    ajajOpt = ajajOpt || {};
           51  +    if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
           52  +        req = {
           53  +            payload:payload,
           54  +            requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
           55  +            authToken:(this.auth ? this.auth.authToken : undefined),
           56  +            jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
           57  +        };
           58  +    }
           59  +    ajajOpt.method = req ? 'POST' : 'GET';
           60  +    // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
           61  +    if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
           62  +    this.ajaj.sendRequest(req,ajajOpt);
           63  +};
           64  +
           65  +/**
           66  +    Sends a login request to the back-end.
           67  +
           68  +    ajajOpt is an optional configuration object suitable for passing 
           69  +    to sendCommand().
           70  +
           71  +    After the response returns, this.auth will be
           72  +    set to the response payload.
           73  +    
           74  +    If name === 'anonymous' (the default if none is passed in) then this
           75  +    function ignores the pw argument and must make two requests - the first
           76  +    one gets the captcha code and the second one submits it.
           77  +    ajajOpt.onResponse() (if set) is only called for the actual login
           78  +    response (the 2nd one), as opposed to being called for both requests.
           79  +    However, this.ajaj.callbacks.onResponse() _is_ called for both (because
           80  +    it happens at a lower level).
           81  +    
           82  +    If this object has an onLogin() function it is called (with
           83  +    no arguments) before the onResponse() handler of the login is called
           84  +    (that is the 2nd request for anonymous logins) and any exceptions
           85  +    it throws are ignored.
           86  +
           87  +*/
           88  +FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
           89  +    name = name || 'anonymous';
           90  +    var self = this;
           91  +    var loginReq = {
           92  +        name:name,
           93  +        password:pw
           94  +    };
           95  +    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
           96  +    var oldOnResponse = ajajOpt.onResponse;
           97  +    ajajOpt.onResponse = function(resp,req) {
           98  +        var thisOpt = this;
           99  +        //alert('login response:\n'+WhAjaj.stringify(resp));
          100  +        if( resp && resp.payload ) {
          101  +            //self.userName = resp.payload.name;
          102  +            //self.capabilities = resp.payload.capabilities;
          103  +            self.auth = resp.payload;
          104  +        }
          105  +        if( WhAjaj.isFunction( self.onLogin ) ){
          106  +            try{ self.onLogin(); }
          107  +            catch(e){}
          108  +        }
          109  +        if( WhAjaj.isFunction(oldOnResponse) ) {
          110  +            oldOnResponse.apply(thisOpt,[resp,req]);
          111  +        }
          112  +    };
          113  +    function doLogin(){
          114  +        //alert("Sending login request..."+WhAjaj.stringify(loginReq));
          115  +        self.sendCommand('/json/login', loginReq, ajajOpt);
          116  +    }
          117  +    if( 'anonymous' === name ){
          118  +      this.sendCommand('/json/anonymousPassword',undefined,{
          119  +          onResponse:function(resp,req){
          120  +/*
          121  +            if( WhAjaj.isFunction(oldOnResponse) ){
          122  +                oldOnResponse.apply(this, [resp,req]);
          123  +            };
          124  +*/
          125  +            if(resp && !resp.resultCode){
          126  +                //alert("Got PW. Trying to log in..."+WhAjaj.stringify(resp));
          127  +                loginReq.anonymousSeed = resp.payload.seed;
          128  +                loginReq.password = resp.payload.password;
          129  +                doLogin();
          130  +            }
          131  +          }
          132  +      });
          133  +    }
          134  +    else doLogin();
          135  +};
          136  +
          137  +/**
          138  +    Logs out of fossil, invaliding this login token.
          139  +
          140  +    ajajOpt is an optional configuration object suitable for passing 
          141  +    to sendCommand().
          142  +    
          143  +    If this object has an onLogout() function it is called (with
          144  +    no arguments) before the onResponse() handler is called.
          145  +    IFF the response succeeds then this.auth is unset.
          146  +*/
          147  +FossilAjaj.prototype.logout = function(ajajOpt) {
          148  +    var self = this;
          149  +    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
          150  +    var oldOnResponse = ajajOpt.onResponse;
          151  +    ajajOpt.onResponse = function(resp,req) {
          152  +        var thisOpt = this;
          153  +        self.auth = undefined;
          154  +        if( WhAjaj.isFunction( self.onLogout ) ){
          155  +            try{ self.onLogout(); }
          156  +            catch(e){}
          157  +        }
          158  +        if( WhAjaj.isFunction(oldOnResponse) ) {
          159  +            oldOnResponse.apply(thisOpt,[resp,req]);
          160  +        }
          161  +    };
          162  +    this.sendCommand('/json/logout', undefined, ajajOpt );
          163  +};
          164  +
          165  +/**
          166  +    Sends a HAI request to the server. /json/HAI is an alias /json/version.
          167  +
          168  +    ajajOpt is an optional configuration object suitable for passing 
          169  +    to sendCommand().
          170  +*/
          171  +FossilAjaj.prototype.HAI = function(ajajOpt) {
          172  +    this.sendCommand('/json/HAI', undefined, ajajOpt);
          173  +};
          174  +
          175  +
          176  +/**
          177  +    Sends a /json/whoami request. Updates this.auth to contain
          178  +    the login info, removing them if the response does not contain
          179  +    that data.
          180  +*/
          181  +FossilAjaj.prototype.whoami = function(ajajOpt) {
          182  +    var self = this;
          183  +    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
          184  +    var oldOnResponse = ajajOpt.onResponse;
          185  +    ajajOpt.onResponse = function(resp,req) {
          186  +        var thisOpt = this;
          187  +        if( resp && resp.payload ){
          188  +            if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
          189  +                self.auth = resp.payload;
          190  +                if( WhAjaj.isFunction(self.onLogin) ){
          191  +                    self.onLogin();
          192  +                }
          193  +            }
          194  +        }
          195  +        else { delete self.auth; }
          196  +        if( WhAjaj.isFunction(oldOnResponse) ) {
          197  +            oldOnResponse.apply(thisOpt,[resp,req]);
          198  +        }
          199  +    };
          200  +    self.sendCommand('/json/whoami', undefined, ajajOpt);
          201  +};
          202  +
          203  +/**
          204  +    EXPERIMENTAL concrete WhAjaj.Connector.sendImpl() implementation which
          205  +    uses Rhino to connect to a local fossil binary for input and output. Its
          206  +    signature and semantics are as described for
          207  +    WhAjaj.Connector.prototype.sendImpl(), with a few exceptions and
          208  +    additions:
          209  +
          210  +    - It does not support timeouts or asynchronous mode.
          211  +
          212  +    - The args.fossilBinary property must point to the local fossil binary
          213  +    (it need not be a complete path if fossil is in the $PATH). This
          214  +    function throws (without calling any request callbacks) if
          215  +    args.fossilBinary is not set. fossilBinary may be set on
          216  +    WhAjaj.Connector.options.ajax, in the FossilAjaj constructor call, as
          217  +    the ajax options parameter to any of the FossilAjaj.sendCommand() family
          218  +    of functions, or by setting
          219  +    aFossilAjajInstance.ajaj.options.fossilBinary on a specific
          220  +    FossilAjaj instance.
          221  +
          222  +    - It uses the args.url field to create the "command" property of the
          223  +    request, constructs a request envelope, spawns a fossil process in JSON
          224  +    mode, feeds it the request envelope, and returns the response envelope
          225  +    via the same mechanisms defined for the HTTP-based implementations.
          226  +
          227  +    The interface is otherwise compatible with the "normal"
          228  +    FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
          229  +    not back-end agnostic like the WhAjaj.sendImpl() interface intends).
          230  +
          231  +    
          232  +*/
          233  +FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
          234  +    var self = this;
          235  +    request = request || {};
          236  +    if(!args.fossilBinary){
          237  +        throw new Error("fossilBinary is not set on AJAX options!");
          238  +    }
          239  +    var url = args.url.split('?')[0].split(/\/+/);
          240  +    if(url.length>1){
          241  +        // 3x shift(): protocol, host, 'json' part of path
          242  +        request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
          243  +    }
          244  +    delete args.url;
          245  +    //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));    
          246  +    var json;
          247  +    try{
          248  +        var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
          249  +        var p = java.lang.Runtime.getRuntime().exec(pargs);
          250  +        var outs = p.getOutputStream();
          251  +        var osr = new java.io.OutputStreamWriter(outs);
          252  +        var osb = new java.io.BufferedWriter(osr);
          253  +        
          254  +        json = JSON.stringify(request);
          255  +        osb.write(json,0, json.length);
          256  +        osb.close();
          257  +        var ins = p.getInputStream();
          258  +        var isr = new java.io.InputStreamReader(ins);
          259  +        var br = new java.io.BufferedReader(isr);
          260  +        var line;
          261  +        json = [];
          262  +        while( null !== (line=br.readLine())){
          263  +            json.push(line);
          264  +        }
          265  +        ins.close();
          266  +    }catch(e){
          267  +        args.errorMessage = e.toString();
          268  +        WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
          269  +        return undefined;
          270  +    }
          271  +    json = json.join('');
          272  +    //print("READ IN JSON: "+json);
          273  +    WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
          274  +}/*rhinoLocalBinary*/

Added ajax/js/json2.js.

            1  +/*
            2  +    http://www.JSON.org/json2.js
            3  +    2009-06-29
            4  +
            5  +    Public Domain.
            6  +
            7  +    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
            8  +
            9  +    See http://www.JSON.org/js.html
           10  +
           11  +    This file creates a global JSON object containing two methods: stringify
           12  +    and parse.
           13  +
           14  +        JSON.stringify(value, replacer, space)
           15  +            value       any JavaScript value, usually an object or array.
           16  +
           17  +            replacer    an optional parameter that determines how object
           18  +                        values are stringified for objects. It can be a
           19  +                        function or an array of strings.
           20  +
           21  +            space       an optional parameter that specifies the indentation
           22  +                        of nested structures. If it is omitted, the text will
           23  +                        be packed without extra whitespace. If it is a number,
           24  +                        it will specify the number of spaces to indent at each
           25  +                        level. If it is a string (such as '\t' or '&nbsp;'),
           26  +                        it contains the characters used to indent at each level.
           27  +
           28  +            This method produces a JSON text from a JavaScript value.
           29  +
           30  +            When an object value is found, if the object contains a toJSON
           31  +            method, its toJSON method will be called and the result will be
           32  +            stringified. A toJSON method does not serialize: it returns the
           33  +            value represented by the name/value pair that should be serialized,
           34  +            or undefined if nothing should be serialized. The toJSON method
           35  +            will be passed the key associated with the value, and this will be
           36  +            bound to the object holding the key.
           37  +
           38  +            For example, this would serialize Dates as ISO strings.
           39  +
           40  +                Date.prototype.toJSON = function (key) {
           41  +                    function f(n) {
           42  +                        // Format integers to have at least two digits.
           43  +                        return n < 10 ? '0' + n : n;
           44  +                    }
           45  +
           46  +                    return this.getUTCFullYear()   + '-' +
           47  +                         f(this.getUTCMonth() + 1) + '-' +
           48  +                         f(this.getUTCDate())      + 'T' +
           49  +                         f(this.getUTCHours())     + ':' +
           50  +                         f(this.getUTCMinutes())   + ':' +
           51  +                         f(this.getUTCSeconds())   + 'Z';
           52  +                };
           53  +
           54  +            You can provide an optional replacer method. It will be passed the
           55  +            key and value of each member, with this bound to the containing
           56  +            object. The value that is returned from your method will be
           57  +            serialized. If your method returns undefined, then the member will
           58  +            be excluded from the serialization.
           59  +
           60  +            If the replacer parameter is an array of strings, then it will be
           61  +            used to select the members to be serialized. It filters the results
           62  +            such that only members with keys listed in the replacer array are
           63  +            stringified.
           64  +
           65  +            Values that do not have JSON representations, such as undefined or
           66  +            functions, will not be serialized. Such values in objects will be
           67  +            dropped; in arrays they will be replaced with null. You can use
           68  +            a replacer function to replace those with JSON values.
           69  +            JSON.stringify(undefined) returns undefined.
           70  +
           71  +            The optional space parameter produces a stringification of the
           72  +            value that is filled with line breaks and indentation to make it
           73  +            easier to read.
           74  +
           75  +            If the space parameter is a non-empty string, then that string will
           76  +            be used for indentation. If the space parameter is a number, then
           77  +            the indentation will be that many spaces.
           78  +
           79  +            Example:
           80  +
           81  +            text = JSON.stringify(['e', {pluribus: 'unum'}]);
           82  +            // text is '["e",{"pluribus":"unum"}]'
           83  +
           84  +
           85  +            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
           86  +            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
           87  +
           88  +            text = JSON.stringify([new Date()], function (key, value) {
           89  +                return this[key] instanceof Date ?
           90  +                    'Date(' + this[key] + ')' : value;
           91  +            });
           92  +            // text is '["Date(---current time---)"]'
           93  +
           94  +
           95  +        JSON.parse(text, reviver)
           96  +            This method parses a JSON text to produce an object or array.
           97  +            It can throw a SyntaxError exception.
           98  +
           99  +            The optional reviver parameter is a function that can filter and
          100  +            transform the results. It receives each of the keys and values,
          101  +            and its return value is used instead of the original value.
          102  +            If it returns what it received, then the structure is not modified.
          103  +            If it returns undefined then the member is deleted.
          104  +
          105  +            Example:
          106  +
          107  +            // Parse the text. Values that look like ISO date strings will
          108  +            // be converted to Date objects.
          109  +
          110  +            myData = JSON.parse(text, function (key, value) {
          111  +                var a;
          112  +                if (typeof value === 'string') {
          113  +                    a =
          114  +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
          115  +                    if (a) {
          116  +                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
          117  +                            +a[5], +a[6]));
          118  +                    }
          119  +                }
          120  +                return value;
          121  +            });
          122  +
          123  +            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
          124  +                var d;
          125  +                if (typeof value === 'string' &&
          126  +                        value.slice(0, 5) === 'Date(' &&
          127  +                        value.slice(-1) === ')') {
          128  +                    d = new Date(value.slice(5, -1));
          129  +                    if (d) {
          130  +                        return d;
          131  +                    }
          132  +                }
          133  +                return value;
          134  +            });
          135  +
          136  +
          137  +    This is a reference implementation. You are free to copy, modify, or
          138  +    redistribute.
          139  +
          140  +    This code should be minified before deployment.
          141  +    See http://javascript.crockford.com/jsmin.html
          142  +
          143  +    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
          144  +    NOT CONTROL.
          145  +*/
          146  +
          147  +/*jslint evil: true */
          148  +
          149  +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
          150  +    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
          151  +    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
          152  +    lastIndex, length, parse, prototype, push, replace, slice, stringify,
          153  +    test, toJSON, toString, valueOf
          154  +*/
          155  +
          156  +// Create a JSON object only if one does not already exist. We create the
          157  +// methods in a closure to avoid creating global variables.
          158  +
          159  +var JSON = JSON || {};
          160  +
          161  +(function () {
          162  +
          163  +    function f(n) {
          164  +        // Format integers to have at least two digits.
          165  +        return n < 10 ? '0' + n : n;
          166  +    }
          167  +
          168  +    if (typeof Date.prototype.toJSON !== 'function') {
          169  +
          170  +        Date.prototype.toJSON = function (key) {
          171  +
          172  +            return isFinite(this.valueOf()) ?
          173  +                   this.getUTCFullYear()   + '-' +
          174  +                 f(this.getUTCMonth() + 1) + '-' +
          175  +                 f(this.getUTCDate())      + 'T' +
          176  +                 f(this.getUTCHours())     + ':' +
          177  +                 f(this.getUTCMinutes())   + ':' +
          178  +                 f(this.getUTCSeconds())   + 'Z' : null;
          179  +        };
          180  +
          181  +        String.prototype.toJSON =
          182  +        Number.prototype.toJSON =
          183  +        Boolean.prototype.toJSON = function (key) {
          184  +            return this.valueOf();
          185  +        };
          186  +    }
          187  +
          188  +    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
          189  +        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
          190  +        gap,
          191  +        indent,
          192  +        meta = {    // table of character substitutions
          193  +            '\b': '\\b',
          194  +            '\t': '\\t',
          195  +            '\n': '\\n',
          196  +            '\f': '\\f',
          197  +            '\r': '\\r',
          198  +            '"' : '\\"',
          199  +            '\\': '\\\\'
          200  +        },
          201  +        rep;
          202  +
          203  +
          204  +    function quote(string) {
          205  +
          206  +// If the string contains no control characters, no quote characters, and no
          207  +// backslash characters, then we can safely slap some quotes around it.
          208  +// Otherwise we must also replace the offending characters with safe escape
          209  +// sequences.
          210  +
          211  +        escapable.lastIndex = 0;
          212  +        return escapable.test(string) ?
          213  +            '"' + string.replace(escapable, function (a) {
          214  +                var c = meta[a];
          215  +                return typeof c === 'string' ? c :
          216  +                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
          217  +            }) + '"' :
          218  +            '"' + string + '"';
          219  +    }
          220  +
          221  +
          222  +    function str(key, holder) {
          223  +
          224  +// Produce a string from holder[key].
          225  +
          226  +        var i,          // The loop counter.
          227  +            k,          // The member key.
          228  +            v,          // The member value.
          229  +            length,
          230  +            mind = gap,
          231  +            partial,
          232  +            value = holder[key];
          233  +
          234  +// If the value has a toJSON method, call it to obtain a replacement value.
          235  +
          236  +        if (value && typeof value === 'object' &&
          237  +                typeof value.toJSON === 'function') {
          238  +            value = value.toJSON(key);
          239  +        }
          240  +
          241  +// If we were called with a replacer function, then call the replacer to
          242  +// obtain a replacement value.
          243  +
          244  +        if (typeof rep === 'function') {
          245  +            value = rep.call(holder, key, value);
          246  +        }
          247  +
          248  +// What happens next depends on the value's type.
          249  +
          250  +        switch (typeof value) {
          251  +        case 'string':
          252  +            return quote(value);
          253  +
          254  +        case 'number':
          255  +
          256  +// JSON numbers must be finite. Encode non-finite numbers as null.
          257  +
          258  +            return isFinite(value) ? String(value) : 'null';
          259  +
          260  +        case 'boolean':
          261  +        case 'null':
          262  +
          263  +// If the value is a boolean or null, convert it to a string. Note:
          264  +// typeof null does not produce 'null'. The case is included here in
          265  +// the remote chance that this gets fixed someday.
          266  +
          267  +            return String(value);
          268  +
          269  +// If the type is 'object', we might be dealing with an object or an array or
          270  +// null.
          271  +
          272  +        case 'object':
          273  +
          274  +// Due to a specification blunder in ECMAScript, typeof null is 'object',
          275  +// so watch out for that case.
          276  +
          277  +            if (!value) {
          278  +                return 'null';
          279  +            }
          280  +
          281  +// Make an array to hold the partial results of stringifying this object value.
          282  +
          283  +            gap += indent;
          284  +            partial = [];
          285  +
          286  +// Is the value an array?
          287  +
          288  +            if (Object.prototype.toString.apply(value) === '[object Array]') {
          289  +
          290  +// The value is an array. Stringify every element. Use null as a placeholder
          291  +// for non-JSON values.
          292  +
          293  +                length = value.length;
          294  +                for (i = 0; i < length; i += 1) {
          295  +                    partial[i] = str(i, value) || 'null';
          296  +                }
          297  +
          298  +// Join all of the elements together, separated with commas, and wrap them in
          299  +// brackets.
          300  +
          301  +                v = partial.length === 0 ? '[]' :
          302  +                    gap ? '[\n' + gap +
          303  +                            partial.join(',\n' + gap) + '\n' +
          304  +                                mind + ']' :
          305  +                          '[' + partial.join(',') + ']';
          306  +                gap = mind;
          307  +                return v;
          308  +            }
          309  +
          310  +// If the replacer is an array, use it to select the members to be stringified.
          311  +
          312  +            if (rep && typeof rep === 'object') {
          313  +                length = rep.length;
          314  +                for (i = 0; i < length; i += 1) {
          315  +                    k = rep[i];
          316  +                    if (typeof k === 'string') {
          317  +                        v = str(k, value);
          318  +                        if (v) {
          319  +                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
          320  +                        }
          321  +                    }
          322  +                }
          323  +            } else {
          324  +
          325  +// Otherwise, iterate through all of the keys in the object.
          326  +
          327  +                for (k in value) {
          328  +                    if (Object.hasOwnProperty.call(value, k)) {
          329  +                        v = str(k, value);
          330  +                        if (v) {
          331  +                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
          332  +                        }
          333  +                    }
          334  +                }
          335  +            }
          336  +
          337  +// Join all of the member texts together, separated with commas,
          338  +// and wrap them in braces.
          339  +
          340  +            v = partial.length === 0 ? '{}' :
          341  +                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
          342  +                        mind + '}' : '{' + partial.join(',') + '}';
          343  +            gap = mind;
          344  +            return v;
          345  +        }
          346  +    }
          347  +
          348  +// If the JSON object does not yet have a stringify method, give it one.
          349  +
          350  +    if (typeof JSON.stringify !== 'function') {
          351  +        JSON.stringify = function (value, replacer, space) {
          352  +
          353  +// The stringify method takes a value and an optional replacer, and an optional
          354  +// space parameter, and returns a JSON text. The replacer can be a function
          355  +// that can replace values, or an array of strings that will select the keys.
          356  +// A default replacer method can be provided. Use of the space parameter can
          357  +// produce text that is more easily readable.
          358  +
          359  +            var i;
          360  +            gap = '';
          361  +            indent = '';
          362  +
          363  +// If the space parameter is a number, make an indent string containing that
          364  +// many spaces.
          365  +
          366  +            if (typeof space === 'number') {
          367  +                for (i = 0; i < space; i += 1) {
          368  +                    indent += ' ';
          369  +                }
          370  +
          371  +// If the space parameter is a string, it will be used as the indent string.
          372  +
          373  +            } else if (typeof space === 'string') {
          374  +                indent = space;
          375  +            }
          376  +
          377  +// If there is a replacer, it must be a function or an array.
          378  +// Otherwise, throw an error.
          379  +
          380  +            rep = replacer;
          381  +            if (replacer && typeof replacer !== 'function' &&
          382  +                    (typeof replacer !== 'object' ||
          383  +                     typeof replacer.length !== 'number')) {
          384  +                throw new Error('JSON.stringify');
          385  +            }
          386  +
          387  +// Make a fake root object containing our value under the key of ''.
          388  +// Return the result of stringifying the value.
          389  +
          390  +            return str('', {'': value});
          391  +        };
          392  +    }
          393  +
          394  +
          395  +// If the JSON object does not yet have a parse method, give it one.
          396  +
          397  +    if (typeof JSON.parse !== 'function') {
          398  +        JSON.parse = function (text, reviver) {
          399  +
          400  +// The parse method takes a text and an optional reviver function, and returns
          401  +// a JavaScript value if the text is a valid JSON text.
          402  +
          403  +            var j;
          404  +
          405  +            function walk(holder, key) {
          406  +
          407  +// The walk method is used to recursively walk the resulting structure so
          408  +// that modifications can be made.
          409  +
          410  +                var k, v, value = holder[key];
          411  +                if (value && typeof value === 'object') {
          412  +                    for (k in value) {
          413  +                        if (Object.hasOwnProperty.call(value, k)) {
          414  +                            v = walk(value, k);
          415  +                            if (v !== undefined) {
          416  +                                value[k] = v;
          417  +                            } else {
          418  +                                delete value[k];
          419  +                            }
          420  +                        }
          421  +                    }
          422  +                }
          423  +                return reviver.call(holder, key, value);
          424  +            }
          425  +
          426  +
          427  +// Parsing happens in four stages. In the first stage, we replace certain
          428  +// Unicode characters with escape sequences. JavaScript handles many characters
          429  +// incorrectly, either silently deleting them, or treating them as line endings.
          430  +
          431  +            cx.lastIndex = 0;
          432  +            if (cx.test(text)) {
          433  +                text = text.replace(cx, function (a) {
          434  +                    return '\\u' +
          435  +                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
          436  +                });
          437  +            }
          438  +
          439  +// In the second stage, we run the text against regular expressions that look
          440  +// for non-JSON patterns. We are especially concerned with '()' and 'new'
          441  +// because they can cause invocation, and '=' because it can cause mutation.
          442  +// But just to be safe, we want to reject all unexpected forms.
          443  +
          444  +// We split the second stage into 4 regexp operations in order to work around
          445  +// crippling inefficiencies in IE's and Safari's regexp engines. First we
          446  +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
          447  +// replace all simple value tokens with ']' characters. Third, we delete all
          448  +// open brackets that follow a colon or comma or that begin the text. Finally,
          449  +// we look to see that the remaining characters are only whitespace or ']' or
          450  +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
          451  +
          452  +            if (/^[\],:{}\s]*$/.
          453  +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
          454  +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
          455  +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
          456  +
          457  +// In the third stage we use the eval function to compile the text into a
          458  +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
          459  +// in JavaScript: it can begin a block or an object literal. We wrap the text
          460  +// in parens to eliminate the ambiguity.
          461  +
          462  +                j = eval('(' + text + ')');
          463  +
          464  +// In the optional fourth stage, we recursively walk the new structure, passing
          465  +// each name/value pair to a reviver function for possible transformation.
          466  +
          467  +                return typeof reviver === 'function' ?
          468  +                    walk({'': j}, '') : j;
          469  +            }
          470  +
          471  +// If the text is not JSON parseable, then a SyntaxError is thrown.
          472  +
          473  +            throw new SyntaxError('JSON.parse');
          474  +        };
          475  +    }
          476  +}());

Added ajax/js/whajaj.js.

            1  +/**
            2  +    This file provides a JS interface into the core functionality of
            3  +    JSON-centric back-ends. It sends GET or JSON POST requests to
            4  +    a back-end and expects JSON responses. The exact semantics of
            5  +    the underlying back-end and overlying front-end are not its concern,
            6  +    and it leaves the interpretation of the data up to the client/server
            7  +    insofar as possible.
            8  +
            9  +    All functionality is part of a class named WhAjaj, and that class
           10  +    acts as namespace for this framework.
           11  +
           12  +    Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
           13  +
           14  +    License: Public Domain
           15  +    
           16  +    This framework is directly derived from code originally found in 
           17  +    http://code.google.com/p/jsonmessage, and later in 
           18  +    http://whiki.wanderinghorse.net, where it contained quite a bit 
           19  +    of application-specific logic. It was eventually (the 3rd time i 
           20  +    needed it) split off into its own library to simplify inclusion 
           21  +    into my many mini-projects.
           22  +*/
           23  +
           24  +
           25  +/**
           26  +    The WhAjaj function is primarily a namespace, and not intended 
           27  +    to called or instantiated via the 'new' operator.
           28  +*/
           29  +function WhAjaj()
           30  +{
           31  +}
           32  +
           33  +/** Returns a millisecond Unix Epoch timestamp. */
           34  +WhAjaj.msTimestamp = function()
           35  +{
           36  +    return (new Date()).getTime();
           37  +};
           38  +
           39  +/** Returns a Unix Epoch timestamp (in seconds) in integer format.
           40  +
           41  +    Reminder to self: (1.1 %1.2) evaluates to a floating-point value 
           42  +    in JS, and thus this implementation is less than optimal.
           43  +*/
           44  +WhAjaj.unixTimestamp = function()
           45  +{
           46  +    var ts = (new Date()).getTime();
           47  +    return parseInt( ""+((ts / 1000) % ts) );
           48  +};
           49  +
           50  +/**
           51  +    Returns true if v is-a Array instance.
           52  +*/
           53  +WhAjaj.isArray = function( v )
           54  +{
           55  +    return (v &&
           56  +            (v instanceof Array) ||
           57  +            (Object.prototype.toString.call(v) === "[object Array]")
           58  +            );
           59  +    /* Reminders to self:
           60  +        typeof [] == "object"
           61  +        toString.call([]) == "[object Array]"
           62  +        ([]).toString() == empty
           63  +    */
           64  +};
           65  +
           66  +/**
           67  +    Returns true if v is-a Object instance.
           68  +*/
           69  +WhAjaj.isObject = function( v )
           70  +{
           71  +    return v &&
           72  +        (v instanceof Object) &&
           73  +        ('[object Object]' === Object.prototype.toString.apply(v) );
           74  +};
           75  +
           76  +/**
           77  +    Returns true if v is-a Function instance.
           78  +*/
           79  +WhAjaj.isFunction = function(obj)
           80  +{
           81  +    return obj
           82  +    && (
           83  +    (obj instanceof Function)
           84  +    || ('function' === typeof obj)
           85  +    || ("[object Function]" === Object.prototype.toString.call(obj))
           86  +    )
           87  +    ;
           88  +};
           89  +
           90  +/**
           91  +    Parses window.location.search-style string into an object
           92  +    containing key/value pairs of URL arguments (already urldecoded).
           93  +    
           94  +    If the str argument is not passed (arguments.length==0) then 
           95  +    window.location.search.substring(1) is used by default. If 
           96  +    neither str is passed in nor window exists then false is returned.
           97  +
           98  +    On success it returns an Object containing the key/value pairs
           99  +    parsed from the string. Keys which have no value are treated
          100  +    has having the boolean true value.
          101  +    
          102  +    FIXME: for keys in the form "name[]", build an array of results,
          103  +    like PHP does.
          104  + 
          105  +*/
          106  +WhAjaj.processUrlArgs = function(str) {
          107  +    if( 0 === arguments.length ) {
          108  +        if( ('undefined' === typeof window) ||
          109  +            !window.location ||
          110  +            !window.location.search )  return false;
          111  +        else str = (''+window.location.search).substring(1);
          112  +    }
          113  +    if( ! str ) return false;
          114  +    str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
          115  +    var args = {};
          116  +    var sp = str.split(/&+/);
          117  +    var rx = /^([^=]+)(=(.+))?/;
          118  +    var i, m;
          119  +    for( i in sp ) {
          120  +        m = rx.exec( sp[i] );
          121  +        if( ! m ) continue;
          122  +        args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
          123  +    }
          124  +    return args;
          125  +};
          126  +
          127  +/**
          128  +    A simple wrapper around JSON.stringify(), using my own personal
          129  +    preferred values for the 2nd and 3rd parameters. To globally
          130  +    set its indentation level, assign WhAjaj.stringify.indent to
          131  +    an integer value (0 for no intendation).
          132  +    
          133  +    This function is intended only for human-readable output, not
          134  +    generic over-the-wire JSON output (where JSON.stringify(val) will
          135  +    produce smaller results).
          136  +*/
          137  +WhAjaj.stringify = function(val) {
          138  +    if( ! arguments.callee.indent ) arguments.callee.indent = 4;
          139  +    return JSON.stringify(val,0,arguments.callee.indent);
          140  +};
          141  +
          142  +/**
          143  +    Each instance of this class holds state information for making 
          144  +    AJAJ requests to a back-end system. While clients may use one 
          145  +    "requester" object per connection attempt, for connections to the
          146  +    same back-end, using an instance configured for that back-end 
          147  +    can simplify usage. This class is designed so that the actual 
          148  +    connection-related details (i.e. _how_ it connects to the 
          149  +    back-end) may be re-implemented to use a client's preferred 
          150  +    connection mechanism (e.g. jQuery).
          151  +    
          152  +    The optional opt paramater may be an object with any (or all) of 
          153  +    the properties documented for WhAjaj.Connector.options.ajax. 
          154  +    Properties set here (or later via modification of the "options" 
          155  +    property of this object) will be used in calls to 
          156  +    WhAjaj.Connector.sendRequest(), and these override (normally) any
          157  +    options set in WhAjaj.Connector.options.ajax. Note that 
          158  +    WhAjaj.Connector.sendRequest() _also_ takes an options object, 
          159  +    and ones passed there will override, for purposes of that one 
          160  +    request, any options passed in here or defined in 
          161  +    WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
          162  +    and WhAjaj.Connector.prototype.sendRequest() for more details
          163  +    about the precedence of options.
          164  +    
          165  +    Sample usage:
          166  +    
          167  +    @code
          168  +    // Set up common connection-level options:
          169  +    var cgi = new WhAjaj.Connector({
          170  +        url: '/cgi-bin/my.cgi',
          171  +        timeout:10000,
          172  +        onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
          173  +        onError(req,opt) {
          174  +            alert(opt.errorMessage);
          175  +        }
          176  +    });
          177  +    // Any of those options may optionally be set globally in
          178  +    // WhAjaj.Connector.options.ajax (onError(), beforeSend(), and afterSend()
          179  +    // are often easiest/most useful to set globally).
          180  +
          181  +    // Get list of pages...
          182  +    cgi.sendRequest( null, {
          183  +        onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
          184  +    });
          185  +    @endcode
          186  +
          187  +    For common request types, clients can add functions to this
          188  +    object which act as wrappers for backend-specific functionality. As
          189  +    a simple example:
          190  +    
          191  +    @code
          192  +    cgi.login = function(name,pw,ajajOpt) {
          193  +        this.sendRequest(
          194  +            {command:"json/login",
          195  +              name:name,
          196  +              password:pw
          197  +            }, ajajOpt );
          198  +    };
          199  +    @endcode
          200  +    
          201  +    TODOs:
          202  +    
          203  +    - Caching of page-load requests, with a configurable lifetime.
          204  +    
          205  +    - Use-cases like the above login() function are a tiny bit
          206  +    problematic to implement when each request has a different URL
          207  +    path (i know this from the whiki and fossil implementations).
          208  +    This is partly a side-effect of design descisions made back in
          209  +    the very first days of this code's life. i need to go through
          210  +    and see where i can bend those conventions a bit (where it won't
          211  +    break my other apps unduly).
          212  +*/
          213  +WhAjaj.Connector = function(opt)
          214  +{
          215  +    if(WhAjaj.isObject(opt)) this.options = opt;
          216  +    //TODO?: this.$cache = {};
          217  +};
          218  +
          219  +/**
          220  +    The core options used by WhAjaj.Connector instances for performing
          221  +    network operations. These options can (and some _should_)
          222  +    be changed by a client application. They can also be changed
          223  +    on specific instances of WhAjaj.Connector, but for most applications
          224  +    it is simpler to set them here and not have to bother with configuring
          225  +    each WhAjaj.Connector instance. Apps which use multiple back-ends at one time,
          226  +    however, will need to customize each instance for a given back-end.
          227  +*/
          228  +WhAjaj.Connector.options = {
          229  +    /**
          230  +        A (meaningless) prefix to apply to WhAjaj.Connector-generated
          231  +        request IDs.
          232  +    */
          233  +    requestIdPrefix:'WhAjaj.Connector-',
          234  +    /**
          235  +        Default options for WhAjaj.Connector.sendRequest() connection 
          236  +        parameters. This object holds only connection-related 
          237  +        options and callbacks (all optional), and not options 
          238  +        related to the required JSON structure of any given request. 
          239  +        i.e. the page name used in a get-page request are not set 
          240  +        here but are specified as part of the request object.
          241  +
          242  +        These connection options are a "normalized form" of options 
          243  +        often found in various AJAX libraries like jQuery, 
          244  +        Prototype, dojo, etc. This approach allows us to swap out 
          245  +        the real connection-related parts by writing a simple proxy 
          246  +        which transforms our "normalized" form to the 
          247  +        backend-specific form. For examples, see the various 
          248  +        implementations stored in WhAjaj.Connector.sendImpls.
          249  +
          250  +        The following callback options are, in practice, almost 
          251  +        always set globally to some app-wide defaults:
          252  +
          253  +        - onError() to report errors using a common mechanism.
          254  +        - beforeSend() to start a visual activity notification
          255  +        - afterSend() to disable the visual activity notification
          256  +
          257  +        However, be aware that if any given WhAjaj.Connector instance is 
          258  +        given its own before/afterSend callback then those will 
          259  +        override these. Mixing shared/global and per-instance
          260  +        callbacks can potentially lead to confusing results if, e.g.,
          261  +        the beforeSend() and afterSend() functions have side-effects
          262  +        but are not used with their proper before/after partner.
          263  +        
          264  +        TODO: rename this to 'ajaj' (the name is historical). The 
          265  +        problem with renaming it is is that the word 'ajax' is 
          266  +        pretty prevelant in the source tree, so i can't globally 
          267  +        swap it out.
          268  +    */
          269  +    ajax: {
          270  +        /**
          271  +            URL of the back-end server/CGI.
          272  +        */
          273  +        url: '/some/path',
          274  +
          275  +        /**
          276  +            Connection method. Some connection-related functions might
          277  +            override any client-defined setting.
          278  +            
          279  +            Must be one of 'GET' or 'POST'. For custom connection 
          280  +            implementation, it may optionally be some 
          281  +            implementation-specified value.
          282  +
          283  +            Normally the API can derive this value automatically - if the
          284  +            request uses JSON data it is POSTed, else it is GETted.
          285  +        */
          286  +        method:'GET',
          287  +
          288  +        /**
          289  +            A hint whether to run the operation asynchronously or 
          290  +            not. Not all concrete WhAjaj.Connector.sendImpl() 
          291  +            implementations can support this. Interestingly, at 
          292  +            least one popular AJAX toolkit does not document 
          293  +            supporting _synchronous_ AJAX operations. All common 
          294  +            browser-side implementations support async operation, but 
          295  +            non-browser implementations might not.
          296  +        */
          297  +        asynchronous:true,
          298  +
          299  +        /**
          300  +            A HTTP authentication login name for the AJAX 
          301  +            connection. Not all concrete WhAjaj.Connector.sendImpl() 
          302  +            implementations can support this.
          303  +        */
          304  +        loginName:undefined,
          305  +
          306  +        /**
          307  +            An HTTP authentication login password for the AJAJ 
          308  +            connection. Not all concrete WhAjaj.Connector.sendImpl() 
          309  +            implementations can support this.
          310  +        */
          311  +        loginPassword:undefined,
          312  +
          313  +        /**
          314  +            A connection timeout, in milliseconds, for establishing 
          315  +            an AJAJ connection. Not all concrete 
          316  +            WhAjaj.Connector.sendImpl() implementations can support this.
          317  +        */
          318  +        timeout:10000,
          319  +
          320  +        /**
          321  +            If an AJAJ request receives JSON data from the back-end,
          322  +            that data is passed as a plain Object as the response
          323  +            parameter (exception: in jsonp mode it is passed a
          324  +            string (why???)). The initiating request object is
          325  +            passed as the second parameter, but clients can normally
          326  +            ignore it (only those which need a way to map specific
          327  +            requests to responses will need it). The 3rd parameter
          328  +            is the same as the 'this' object for the context of the
          329  +            callback, but is provided because the instance-level
          330  +            callbacks (set in (WhAjaj.Connector instance).callbacks,
          331  +            require it in some cases (because their 'this' is
          332  +            different!).
          333  +            
          334  +            Note that the response might contain error information
          335  +            which comes from the back-end. The difference between
          336  +            this error info and the info passed to the onError()
          337  +            callback is that this data indicates an
          338  +            application-level error, whereas onError() is used to
          339  +            report connection-level problems or when the backend
          340  +            produces non-JSON data (which, when not in jsonp mode,
          341  +            is unexpected and is as fatal to the request as a
          342  +            connection error).
          343  +        */
          344  +        onResponse: function(response, request, opt){},
          345  +
          346  +        /**
          347  +            If an AJAX request fails to establish a connection or it 
          348  +            receives non-JSON data from the back-end, this function 
          349  +            is called (e.g. timeout error or host name not 
          350  +            resolvable). It is passed the originating request and the
          351  +            "normalized" connection parameters used for that 
          352  +            request. The connectOpt object "should" (or "might") 
          353  +            have an "errorMessage" property which describes the 
          354  +            nature of the problem.
          355  +            
          356  +            Clients will almost always want to replace the default 
          357  +            implementation with something which integrates into 
          358  +            their application.
          359  +        */
          360  +        onError: function(request, connectOpt)
          361  +        {
          362  +            alert('AJAJ request failed:\n'
          363  +                +'Connection information:\n'
          364  +                +JSON.stringify(connectOpt,0,4)
          365  +            );
          366  +        },
          367  +
          368  +        /**
          369  +            Called before each connection attempt is made. Clients 
          370  +            can use this to, e.g.,  enable a visual "network activity
          371  +            notification" for the user. It is passed the original 
          372  +            request object and the normalized connection parameters 
          373  +            for the request. If this function changes opt, those 
          374  +            changes _are_ applied to the subsequent request. If this 
          375  +            function throws, neither the onError() nor afterSend() 
          376  +            callbacks are triggered and WhAjaj.Connector.sendImpl() 
          377  +            propagates the exception back to the caller.
          378  +        */
          379  +        beforeSend: function(request,opt){},
          380  +
          381  +        /**
          382  +            Called after an AJAJ connection attempt completes, 
          383  +            regardless of success or failure. Passed the same 
          384  +            parameters as beforeSend() (see that function for 
          385  +            details).
          386  +            
          387  +            Here's an example of setting up a visual notification on 
          388  +            ajax operations using jQuery (but it's also easy to do 
          389  +            without jQuery as well):
          390  +            
          391  +            @code
          392  +            function startAjaxNotif(req,opt) {
          393  +                var me = arguments.callee;
          394  +                var c = ++me.ajaxCount;
          395  +                me.element.text( c + " pending AJAX operation(s)..." );
          396  +                if( 1 == c ) me.element.stop().fadeIn();
          397  +            }
          398  +            startAjaxNotif.ajaxCount = 0.
          399  +            startAjaxNotif.element = jQuery('#whikiAjaxNotification');
          400  +            
          401  +            function endAjaxNotif() {
          402  +                var c = --startAjaxNotif.ajaxCount;
          403  +                startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
          404  +                if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
          405  +            }
          406  +            @endcode
          407  +
          408  +            Set the beforeSend/afterSend properties to those 
          409  +            functions to enable the notifications by default.            
          410  +        */
          411  +        afterSend: function(request,opt){},
          412  +
          413  +        /**
          414  +            If jsonp is a string then the WhAjaj-internal response
          415  +            handling code ASSUMES that the response contains a JSONP-style
          416  +            construct and eval()s it after afterSend() but before onResponse().
          417  +            In this case, onResponse() will get a string value for the response
          418  +            instead of a response object parsed from JSON. 
          419  +        */
          420  +        jsonp:undefined,
          421  +        /**
          422  +            Don't use yet. Planned future option.
          423  +        */
          424  +        propagateExceptions:false
          425  +    }
          426  +};
          427  +
          428  +
          429  +/**
          430  +    WhAjaj.Connector.prototype.callbacks defines callbacks analog
          431  +    to the onXXX callbacks defined in WhAjaj.Connector.options.ajax,
          432  +    with two notable differences:
          433  +
          434  +    1) these callbacks, if set, are called in addition to any
          435  +    request-specific callback. The intention is to allow a framework to set
          436  +    "framework-level" callbacks which should be called independently of the
          437  +    request-specific callbacks (without interfering with them, e.g.
          438  +    requiring special re-forwarding features).
          439  +
          440  +    2) The 'this' object in these callbacks is the Connector instance
          441  +    associated with the callback, whereas the "other" onXXX form has its
          442  +    "ajax options" object as its this.
          443  +
          444  +    When this API says that an onXXX callback will be called for a request,
          445  +    both the request's onXXX (if set) and this one (if set) will be called.
          446  +*/
          447  +WhAjaj.Connector.prototype.callbacks = {};
          448  +/**
          449  +    Instance-specific values for AJAJ-level properties (as opposed to
          450  +    application-level request properties). Options set here "override" those
          451  +    specified in WhAjaj.Connector.options.ajax and are "overridden" by
          452  +    options passed to sendRequest().
          453  +*/
          454  +WhAjaj.Connector.prototype.options = {};
          455  +
          456  +
          457  +/**
          458  +    Tries to find the given key in any of the following, returning
          459  +    the first match found: opt, this.options, WhAjaj.Connector.options.ajax.
          460  +
          461  +    Returns undefined if key is not found.
          462  +*/
          463  +WhAjaj.Connector.prototype.derivedOption = function(key,opt) {
          464  +    var v = opt ? opt[key] : undefined;
          465  +    if( undefined !== v ) return v;
          466  +    else v = this.options[key];
          467  +    if( undefined !== v ) return v;
          468  +    else v = WhAjaj.Connector.options.ajax[key];
          469  +    return v;
          470  +};
          471  +
          472  +/**
          473  +    Returns a unique string on each call containing a generic 
          474  +    reandom request identifier string. This is not used by the core 
          475  +    API but can be used by client code to generate unique IDs for 
          476  +    each request (if needed).
          477  +
          478  +    The exact format is unspecified and may change in the future.
          479  +
          480  +    Request IDs can be used by clients to "match up" responses to 
          481  +    specific requests if needed. In practice, however, they are 
          482  +    seldom, if ever, needed. When passing several concurrent 
          483  +    requests through the same response callback, it might be useful 
          484  +    for some clients to be able to distinguish, possibly re-routing 
          485  +    them through other handlers based on the originating request type.
          486  +    
          487  +    If this.options.requestIdPrefix or 
          488  +    WhAjaj.Connector.options.requestIdPrefix is set then that text
          489  +    is prefixed to the returned string.
          490  +*/
          491  +WhAjaj.Connector.prototype.generateRequestId = function()
          492  +{
          493  +    if( undefined === arguments.callee.sequence )
          494  +    {
          495  +        arguments.callee.sequence = 0;
          496  +    }
          497  +    var pref = this.options.requestIdPrefix || WhAjaj.Connector.options.requestIdPrefix || '';
          498  +    return pref +
          499  +        WhAjaj.msTimestamp() +
          500  +        '/'+(Math.round( Math.random() * 100000000) )+
          501  +        ':'+(++arguments.callee.sequence);
          502  +};
          503  +
          504  +/**
          505  +    Copies (SHALLOWLY) all properties in opt to this.options.
          506  +*/
          507  +WhAjaj.Connector.prototype.addOptions = function(opt) {
          508  +    var k, v;
          509  +    for( k in opt ) {
          510  +        if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
          511  +        this.options[k] = opt[k];
          512  +    }
          513  +    return this.options;
          514  +};
          515  +
          516  +/**
          517  +    An internal helper object which holds several functions intended 
          518  +    to simplify the creation of concrete communication channel 
          519  +    implementations for WhAjaj.Connector.sendImpl(). These operations
          520  +    take care of some of the more error-prone parts of ensuring that
          521  +    onResponse(), onError(), etc. callbacks are called consistently 
          522  +    using the same rules.
          523  +*/
          524  +WhAjaj.Connector.sendHelper = {
          525  +    /**
          526  +        opt is assumed to be a normalized set of 
          527  +        WhAjaj.Connector.sendRequest() options. This function 
          528  +        creates a url by concatenating opt.url and some form of 
          529  +        opt.urlParam.
          530  +        
          531  +        If opt.urlParam is an object or string then it is appended 
          532  +        to the url. An object is assumed to be a one-dimensional set 
          533  +        of simple (urlencodable) key/value pairs, and not larger 
          534  +        data structures. A string value is assumed to be a 
          535  +        well-formed, urlencoded set of key/value pairs separated by 
          536  +        '&' characters.
          537  +        
          538  +        The new/normalized URL is returned (opt is not modified). If 
          539  +        opt.urlParam is not set then opt.url is returned (or an 
          540  +        empty string if opt.url is itself a false value).
          541  +        
          542  +        TODO: if opt is-a Object and any key points to an array, 
          543  +        build up a list of keys in the form "keyname[]". We could 
          544  +        arguably encode sub-objects like "keyname[subkey]=...", but 
          545  +        i don't know if that's conventions-compatible with other 
          546  +        frameworks.
          547  +    */
          548  +    normalizeURL: function(opt) {
          549  +        var u = opt.url || '';
          550  +        if( opt.urlParam ) {
          551  +            var addQ = (u.indexOf('?') >= 0) ? false : true;
          552  +            var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
          553  +            var tail = '';
          554  +            if( WhAjaj.isObject(opt.urlParam) ) {
          555  +                var li = [], k;
          556  +                for( k in opt.urlParam) {
          557  +                    li.push( k+'='+encodeURIComponent( opt.urlParam[k] ) );
          558  +                }
          559  +                tail = li.join('&');
          560  +            }
          561  +            else if( 'string' === typeof opt.urlParam ) {
          562  +                tail = opt.urlParam;
          563  +            }
          564  +            u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
          565  +        }
          566  +        return u;
          567  +    },
          568  +    /**
          569  +        Should be called by WhAjaj.Connector.sendImpl() 
          570  +        implementations after a response has come back. This 
          571  +        function takes care of most of ensuring that framework-level 
          572  +        conventions involving WhAjaj.Connector.options.ajax 
          573  +        properties are followed.
          574  +        
          575  +        The request argument must be the original request passed to 
          576  +        the sendImpl() function. It may legally be null for GET requests.
          577  +        
          578  +        The opt object should be the normalized AJAX options used 
          579  +        for the connection.
          580  +        
          581  +        The resp argument may be either a plain Object or a string 
          582  +        (in which case it is assumed to be JSON).
          583  +
          584  +        The 'this' object for this call MUST be a WhAjaj.Connector
          585  +        instance in order for callback processing to work properly.
          586  +        
          587  +        This function takes care of the following:
          588  +        
          589  +        - Calling opt.afterSend()
          590  +        
          591  +        - If resp is a string, de-JSON-izing it to an object.
          592  +        
          593  +        - Calling opt.onResponse()
          594  +        
          595  +        - Calling opt.onError() in several common (potential) error 
          596  +        cases.
          597  +
          598  +        - If resp is-a String and opt.jsonp then resp is assumed to be
          599  +        a JSONP-form construct and is eval()d BEFORE opt.onResponse()
          600  +        is called. It is arguable to eval() it first, but the logic
          601  +        integrates better with the non-jsonp handler.
          602  +
          603  +        The sendImpl() should return immediately after calling this.
          604  +        
          605  +        The sendImpl() must call only one of onSendSuccess() or 
          606  +        onSendError(). It must call one of them or it must implement 
          607  +        its own response/error handling, which is not recommended 
          608  +        because getting the documented semantics of the 
          609  +        onError/onResponse/afterSend handling correct can be tedious.
          610  +    */
          611  +    onSendSuccess:function(request,resp,opt) {
          612  +        var cb = this.callbacks || {};
          613  +        if( WhAjaj.isFunction(cb.afterSend) ) {
          614  +            try {cb.afterSend( request, opt );}
          615  +            catch(e){}
          616  +        }
          617  +        if( WhAjaj.isFunction(opt.afterSend) ) {
          618  +            try {opt.afterSend( request, opt );}
          619  +            catch(e){}
          620  +        }
          621  +        function doErr(){
          622  +            if( WhAjaj.isFunction(cb.onError) ) {
          623  +                try {cb.onError( request, opt );}
          624  +                catch(e){}
          625  +            }
          626  +            if( WhAjaj.isFunction(opt.onError) ) {
          627  +                try {opt.onError( request, opt );}
          628  +                catch(e){}
          629  +            }
          630  +        }
          631  +        if( ! resp ) {
          632  +            opt.errorMessage = "Sending of request succeeded but returned no data!";
          633  +            doErr();
          634  +            return false;
          635  +        }
          636  +
          637  +        if( 'string' === typeof resp ) {
          638  +            try {
          639  +                resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
          640  +            } catch(e) {
          641  +                opt.errorMessage = e.toString();
          642  +                doErr();
          643  +                return;
          644  +            }
          645  +        }
          646  +        try {
          647  +            if( WhAjaj.isFunction( cb.onResponse  ) ) {
          648  +                cb.onResponse( resp, request, opt );
          649  +            }
          650  +            if( WhAjaj.isFunction( opt.onResponse  ) ) {
          651  +                opt.onResponse( resp, request, opt );
          652  +            }
          653  +            return true;
          654  +        }
          655  +        catch(e) {
          656  +            opt.errorMessage = "Exception while handling inbound JSON response:\n"
          657  +                + e
          658  +                +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
          659  +                ;
          660  +            ;
          661  +            doErr();
          662  +            return false;
          663  +        }
          664  +    },
          665  +   /**
          666  +        Should be called by sendImpl() implementations after a response
          667  +        has failed to connect (e.g. could not resolve host or timeout
          668  +        reached). This function takes care of most of ensuring that
          669  +        framework-level conventions involving WhAjaj.Connector.options.ajax
          670  +        properties are followed.
          671  +        
          672  +        The request argument must be the original request passed to 
          673  +        the sendImpl() function. It may legally be null for GET 
          674  +        requests.
          675  +
          676  +        The 'this' object for this call MUST be a WhAjaj.Connector
          677  +        instance in order for callback processing to work properly.
          678  +        
          679  +        The opt object should be the normalized AJAX options used 
          680  +        for the connection. By convention, the caller of this 
          681  +        function "should" set opt.errorMessage to contain a 
          682  +        human-readable description of the error.
          683  +        
          684  +        The sendImpl() should return immediately after calling this. The
          685  +        return value from this function is unspecified.
          686  +    */
          687  +    onSendError: function(request,opt) {
          688  +        var cb = this.callbacks || {};
          689  +        if( WhAjaj.isFunction(cb.afterSend) ) {
          690  +            try {cb.afterSend( request, opt );}
          691  +            catch(e){}
          692  +        }
          693  +        if( WhAjaj.isFunction(opt.afterSend) ) {
          694  +            try {opt.afterSend( request, opt );}
          695  +            catch(e){}
          696  +        }
          697  +        if( WhAjaj.isFunction( cb.onError ) ) {
          698  +            try {cb.onError( request, opt );}
          699  +            catch(e) {/*ignore*/}
          700  +        }
          701  +        if( WhAjaj.isFunction( opt.onError ) ) {
          702  +            try {opt.onError( request, opt );}
          703  +            catch(e) {/*ignore*/}
          704  +        }
          705  +    }
          706  +};
          707  +
          708  +/**
          709  +    WhAjaj.Connector.sendImpls holds several concrete 
          710  +    implementations of WhAjaj.Connector.prototype.sendImpl(). To use 
          711  +    a specific implementation by default assign 
          712  +    WhAjaj.Connector.prototype.sendImpl to one of these functions.
          713  +    
          714  +    The functions defined here require that the 'this' object be-a
          715  +    WhAjaj.Connector instance.
          716  +    
          717  +    Historical notes:
          718  +
          719  +    a) We once had an implementation based on Prototype, but that 
          720  +    library just pisses me off (they change base-most types' 
          721  +    prototypes, introducing side-effects in client code which 
          722  +    doesn't even use Prototype). The Prototype version at the time 
          723  +    had a serious toJSON() bug which caused empty arrays to 
          724  +    serialize as the string "[]", which broke a bunch of my code. 
          725  +    (That has been fixed in the mean time, but i don't use 
          726  +    Prototype.)
          727  +    
          728  +    b) We once had an implementation for the dojo library, 
          729  +    
          730  +    If/when the time comes to add Prototype/dojo support, we simply
          731  +    need to port:
          732  +    
          733  +    http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js
          734  +    
          735  +    (search that file for "dojo" and "Prototype") to this tree. That 
          736  +    code is this code's generic grandfather and they are still very 
          737  +    similar, so a port is trivial.    
          738  +    
          739  +*/
          740  +WhAjaj.Connector.sendImpls = {
          741  +    /**
          742  +        This is a concrete implementation of 
          743  +        WhAjaj.Connector.prototype.sendImpl() which uses the 
          744  +        environment's native XMLHttpRequest class to send whiki 
          745  +        requests and fetch the responses.
          746  +
          747  +        The only argument must be a connection properties object, as 
          748  +        constructed by WhAjaj.Connector.normalizeAjaxParameters().
          749  +
          750  +        If window.firebug is set then window.firebug.watchXHR() is 
          751  +        called to enable monitoring of the XMLHttpRequest object.
          752  +
          753  +        This implementation honors the loginName and loginPassword 
          754  +        connection parameters.
          755  +
          756  +        Returns the XMLHttpRequest object.
          757  +
          758  +        This implementation requires that the 'this' object be-a 
          759  +        WhAjaj.Connector.
          760  +        
          761  +        This implementation uses setTimeout() to implement the 
          762  +        timeout support, and thus the JS engine must provide that 
          763  +        functionality.
          764  +    */
          765  +    XMLHttpRequest: function(request, args)
          766  +    {
          767  +        var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
          768  +        var xhr = new XMLHttpRequest();
          769  +        var startTime = (new Date()).getTime();
          770  +        var timeout = args.timeout || 10000/*arbitrary!*/;
          771  +        var hitTimeout = false;
          772  +        var done = false;
          773  +        var tmid /* setTimeout() ID */;
          774  +        var whself = this;
          775  +        function handleTimeout()
          776  +        {
          777  +            hitTimeout = true;
          778  +            if( ! done )
          779  +            {
          780  +                var now = (new Date()).getTime();
          781  +                try { xhr.abort(); } catch(e) {/*ignore*/}
          782  +                // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
          783  +                args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
          784  +                WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          785  +            }
          786  +            return;
          787  +        }
          788  +        function onStateChange()
          789  +        { // reminder to self: apparently 'this' is-not-a XHR :/
          790  +            if( hitTimeout )
          791  +            { /* we're too late - the error was already triggered. */
          792  +                return;
          793  +            }
          794  +
          795  +            if( 4 == xhr.readyState )
          796  +            {
          797  +                done = true;
          798  +                if( tmid )
          799  +                {
          800  +                    clearTimeout( tmid );
          801  +                    tmid = null;
          802  +                }
          803  +                if( (xhr.status >= 200) && (xhr.status < 300) )
          804  +                {
          805  +                    WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
          806  +                    return;
          807  +                }
          808  +                else
          809  +                {
          810  +                    if( undefined === args.errorMessage )
          811  +                    {
          812  +                        args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
          813  +                                +"["+args.url+"]: "
          814  +                                +"Status text=["+xhr.statusText+"]"
          815  +                            ;
          816  +                        WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          817  +                    }
          818  +                    else { /*maybe it was was set by the timeout handler. */ }
          819  +                    return;
          820  +                }
          821  +            }
          822  +        };
          823  +
          824  +        xhr.onreadystatechange = onStateChange;
          825  +        if( ('undefined'!==(typeof window)) && ('firebug' in window) && ('watchXHR' in window.firebug) )
          826  +        { /* plug in to firebug lite's XHR monitor... */
          827  +            window.firebug.watchXHR( xhr );
          828  +        }
          829  +        try
          830  +        {
          831  +            //alert( JSON.stringify( args  ));
          832  +            function xhrOpen()
          833  +            {
          834  +                if( ('loginName' in args) && args.loginName )
          835  +                {
          836  +                    xhr.open( args.method, args.url, args.asynchronous, args.loginName, args.loginPassword );
          837  +                }
          838  +                else
          839  +                {
          840  +                    xhr.open( args.method, args.url, args.asynchronous  );
          841  +                }
          842  +            }
          843  +            if( json && ('POST' ===  args.method.toUpperCase()) )
          844  +            {
          845  +                xhrOpen();
          846  +                xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
          847  +                // Google Chrome warns that it refuses to set these
          848  +                // "unsafe" headers (his words, not mine):
          849  +                // xhr.setRequestHeader("Content-length", json.length);
          850  +                // xhr.setRequestHeader("Connection", "close");
          851  +                xhr.send( json );
          852  +            }
          853  +            else /* assume GET */
          854  +            {
          855  +                xhrOpen();
          856  +                xhr.send(null);
          857  +            }
          858  +            tmid = setTimeout( handleTimeout, timeout );
          859  +            return xhr;
          860  +        }
          861  +        catch(e)
          862  +        {
          863  +            args.errorMessage = e.toString();
          864  +            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          865  +            return undefined;
          866  +        }
          867  +    }/*XMLHttpRequest()*/,
          868  +    /**
          869  +        This is a concrete implementation of 
          870  +        WhAjaj.Connector.prototype.sendImpl() which uses the jQuery 
          871  +        AJAX API to send requests and fetch the responses.
          872  +
          873  +        The first argument may be either null/false, an Object 
          874  +        containing toJSON-able data to post to the back-end, or such an
          875  +        object in JSON string form.
          876  +
          877  +        The second argument must be a connection properties object, as 
          878  +        constructed by WhAjaj.Connector.normalizeAjaxParameters().
          879  +
          880  +        If window.firebug is set then window.firebug.watchXHR() is 
          881  +        called to enable monitoring of the XMLHttpRequest object.
          882  +
          883  +        This implementation honors the loginName and loginPassword 
          884  +        connection parameters.
          885  +
          886  +        Returns the XMLHttpRequest object.
          887  +
          888  +        This implementation requires that the 'this' object be-a 
          889  +        WhAjaj.Connector.
          890  +    */
          891  +    jQuery:function(request,args)
          892  +    {
          893  +        var data = request || undefined;
          894  +        var whself = this;
          895  +        if( data ) {
          896  +            if('string'!==typeof data) {
          897  +                try {
          898  +                    data = JSON.stringify(data);
          899  +                }
          900  +                catch(e) {
          901  +                    WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          902  +                    return;
          903  +                }
          904  +            }
          905  +        }
          906  +        var ajopt = {
          907  +            url: args.url,
          908  +            data: data,
          909  +            type: args.method,
          910  +            async: args.asynchronous,
          911  +            password: (undefined !== args.loginPassword) ? args.loginPassword : undefined,
          912  +            username: (undefined !== args.loginName) ? args.loginName : undefined,
          913  +            contentType: 'application/json; charset=utf-8',
          914  +            error: function(xhr, textStatus, errorThrown)
          915  +            {
          916  +                //this === the options for this ajax request
          917  +                args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
          918  +                        +"Status text=["+textStatus+"]"
          919  +                        +(errorThrown ? ("Error=["+errorThrown+"]") : "")
          920  +                    ;
          921  +                WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          922  +            },
          923  +            success: function(data)
          924  +            {
          925  +                WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
          926  +            },
          927  +            /* Set dataType=text instead of json to keep jQuery from doing our carefully
          928  +                written response handling for us.
          929  +            */
          930  +            dataType: 'text'
          931  +        };
          932  +        if( undefined !== args.timeout )
          933  +        {
          934  +            ajopt.timeout = args.timeout;
          935  +        }
          936  +        try
          937  +        {
          938  +            return jQuery.ajax(ajopt);
          939  +        }
          940  +        catch(e)
          941  +        {
          942  +            args.errorMessage = e.toString();
          943  +            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
          944  +            return undefined;
          945  +        }
          946  +    }/*jQuery()*/,
          947  +    /**
          948  +        This is a concrete implementation of 
          949  +        WhAjaj.Connector.prototype.sendImpl() which uses the rhino 
          950  +        Java API to send requests and fetch the responses.
          951  +
          952  +        Limitations vis-a-vis the interface:
          953  +
          954  +        - timeouts are not supported.
          955  +
          956  +        - asynchronous mode is not supported because implementing it
          957  +        requires the ability to kill a running thread (which is deprecated
          958  +        in the Java API).
          959  +
          960  +        TODOs:
          961  +
          962  +        - add socket timeouts.
          963  +
          964  +        - support HTTP proxy.
          965  +
          966  +        The Java APIs support this, it just hasn't been added here yet.
          967  +    */
          968  +    rhino:function(request,args)
          969  +    {
          970  +        var self = this;
          971  +        var data = request || undefined;
          972  +        if( data ) {
          973  +            if('string'!==typeof data) {
          974  +                try {
          975  +                    data = JSON.stringify(data);
          976  +                }
          977  +                catch(e) {
          978  +                    WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
          979  +                    return;
          980  +                }
          981  +            }
          982  +        }
          983  +        var url;
          984  +        var con;
          985  +        var IO = new JavaImporter(java.io);
          986  +        var wr;
          987  +        var rd, ln, json = [];
          988  +        function setIncomingCookies(list){
          989  +            if(!list || !list.length) return;
          990  +            if( !self.cookies ) self.cookies = {};
          991  +            var k, v, i;
          992  +            for( i = 0; i < list.length; ++i ){
          993  +                v = list[i].split('=',2);
          994  +                k = decodeURIComponent(v[0])
          995  +                v = v[0] ? decodeURIComponent(v[0].split(';',2)[0]) : null;
          996  +                //print("RECEIVED COOKIE: "+k+"="+v);
          997  +                if(!v) {
          998  +                    delete self.cookies[k];
          999  +                    continue;
         1000  +                }else{
         1001  +                    self.cookies[k] = v;
         1002  +                }
         1003  +            }
         1004  +        };
         1005  +        function setOutboundCookies(conn){
         1006  +            if(!self.cookies) return;
         1007  +            var k, v;
         1008  +            for( k in self.cookies ){
         1009  +                if(!self.cookies.hasOwnProperty(k)) continue /*kludge for broken JS libs*/;
         1010  +                v = self.cookies[k];
         1011  +                conn.addRequestProperty("Cookie", encodeURIComponent(k)+'='+encodeURIComponent(v));
         1012  +                //print("SENDING COOKIE: "+k+"="+v);
         1013  +            }
         1014  +        };
         1015  +        try{
         1016  +            url = new java.net.URL( args.url )
         1017  +            con = url.openConnection(/*FIXME: add proxy support!*/);
         1018  +            con.setRequestProperty("Accept-Charset","utf-8");
         1019  +            setOutboundCookies(con);
         1020  +            if(data){
         1021  +                con.setRequestProperty("Content-Type","application/json; charset=utf-8");
         1022  +                con.setDoOutput( true );
         1023  +                wr = new IO.OutputStreamWriter(con.getOutputStream())
         1024  +                wr.write(data);
         1025  +                wr.flush();
         1026  +                wr.close();
         1027  +                wr = null;
         1028  +                //print("POSTED: "+data);
         1029  +            }
         1030  +            rd = new IO.BufferedReader(new IO.InputStreamReader(con.getInputStream()));
         1031  +            //var skippedHeaders = false;
         1032  +            while ((line = rd.readLine()) !== null) {
         1033  +                //print("LINE: "+line);
         1034  +                //if(!line.length && !skippedHeaders){
         1035  +                //    skippedHeaders = true;
         1036  +                // json = [];
         1037  +                //    continue;
         1038  +                //}
         1039  +                json.push(line);
         1040  +            }
         1041  +            setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
         1042  +        }catch(e){
         1043  +            args.errorMessage = e.toString();
         1044  +            WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
         1045  +            return undefined;
         1046  +        }
         1047  +        try { if(wr) wr.close(); } catch(e) { /*ignore*/}
         1048  +        try { if(rd) rd.close(); } catch(e) { /*ignore*/}
         1049  +        json = json.join('');
         1050  +        //print("READ IN JSON: "+json);
         1051  +        WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
         1052  +    }/*rhino*/
         1053  +};
         1054  +
         1055  +/**
         1056  +    An internal function which takes an object containing properties 
         1057  +    for a WhAjaj.Connector network request. This function creates a new 
         1058  +    object containing a superset of the properties from:
         1059  +
         1060  +    a) opt
         1061  +    b) this.options
         1062  +    c) WhAjaj.Connector.options.ajax
         1063  +
         1064  +    in that order, using the first one it finds.
         1065  +
         1066  +    All non-function properties are _deeply_ copied via JSON cloning 
         1067  +    in order to prevent accidental "cross-request pollenation" (been 
         1068  +    there, done that). Functions cannot be cloned and are simply 
         1069  +    copied by reference.
         1070  +    
         1071  +    This function throws if JSON-copying one of the options fails
         1072  +    (e.g. due to cyclic data structures).
         1073  +    
         1074  +    Reminder to self: this function does not "normalize" opt.urlParam
         1075  +    by encoding it into opt.url, mainly for historical reasons, but
         1076  +    also because that behaviour was specifically undesirable in this
         1077  +    code's genetic father.
         1078  +*/
         1079  +WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
         1080  +{
         1081  +    var rc = {};
         1082  +    function merge(k,v)
         1083  +    {
         1084  +        if( rc.hasOwnProperty(k) ) return;
         1085  +        else if( WhAjaj.isFunction(v) ) {}
         1086  +        else if( WhAjaj.isObject(v) ) v = JSON.parse( JSON.stringify(v) );
         1087  +        rc[k]=v;
         1088  +    }
         1089  +    function cp(obj) {
         1090  +        if( ! WhAjaj.isObject(obj) ) return;
         1091  +        var k;
         1092  +        for( k in obj ) {
         1093  +            if( ! obj.hasOwnProperty(k) ) continue /* i will always hate the Prototype designers for this. */;
         1094  +            merge(k, obj[k]);
         1095  +        }
         1096  +    }
         1097  +    cp( opt );
         1098  +    cp( this.options );
         1099  +    cp( WhAjaj.Connector.options.ajax );
         1100  +    // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
         1101  +    return rc;
         1102  +};
         1103  +
         1104  +/**
         1105  +    This is the generic interface for making calls to a back-end 
         1106  +    JSON-producing request handler. It is a simple wrapper around 
         1107  +    WhAjaj.Connector.prototype.sendImpl(), which just normalizes the 
         1108  +    connection options for sendImpl() and makes sure that 
         1109  +    opt.beforeSend() is (possibly) called.
         1110  +    
         1111  +    The request parameter must either be false/null/empty or a 
         1112  +    fully-populated JSON-able request object (which will be sent as 
         1113  +    unencoded application/json text), depending on the type of 
         1114  +    request being made. It is never semantically legal (in this API) 
         1115  +    for request to be a string/number/true/array value. As a rule, 
         1116  +    only POST requests use the request data. GET requests should 
         1117  +    encode their data in opt.url or opt.urlParam (see below).
         1118  +    
         1119  +    opt must contain the network-related parameters for the request. 
         1120  +    Paramters _not_ set in opt are pulled from this.options or 
         1121  +    WhAjaj.Connector.options.ajax (in that order, using the first 
         1122  +    value it finds). Thus the set of connection-level options used 
         1123  +    for the request are a superset of those various sources.
         1124  +    
         1125  +    The "normalized" (or "superimposed") opt object's URL may be 
         1126  +    modified before the request is sent, as follows:
         1127  +
         1128  +    if opt.urlParam is a string then it is assumed to be properly 
         1129  +    URL-encoded parameters and is appended to the opt.url. If it is 
         1130  +    an Object then it is assumed to be a one-dimensional set of 
         1131  +    key/value pairs with simple values (numbers, strings, booleans, 
         1132  +    null, and NOT objects/arrays). The keys/values are URL-encoded 
         1133  +    and appended to the URL.
         1134  +
         1135  +    The beforeSend() callback (see below) can modify the options
         1136  +    object before the request attempt is made.
         1137  +   
         1138  +    The callbacks in the normalized opt object will be triggered as
         1139  +    follows (if they are set to Function values):
         1140  +    
         1141  +    - beforeSend(request,opt) will be called before any network 
         1142  +    processing starts. If beforeSend() throws then no other 
         1143  +    callbacks are triggered and this function propagates the 
         1144  +    exception. This function is passed normalized connection options
         1145  +    as its second parameter, and changes this function makes to that
         1146  +    object _will_ be used for the pending connection attempt.
         1147  +    
         1148  +    - onError(request,opt) will be called if a connection to the 
         1149  +    back-end cannot be established. It will be passed the original 
         1150  +    request object (which might be null, depending on the request 
         1151  +    type) and the normalized options object. In the error case, the 
         1152  +    opt object passed to onError() "should" have a property called 
         1153  +    "errorMessage" which contains a description of the problem.
         1154  +    
         1155  +    - onError(request,opt) will also be called if connection 
         1156  +    succeeds but the response is not JSON data.
         1157  +    
         1158  +    - onResponse(response,request) will be called if the response 
         1159  +    returns JSON data. That data might hold an error response code - 
         1160  +    clients need to check for that. It is passed the response object 
         1161  +    (a plain object) and the original request object.
         1162  +    
         1163  +    - afterSend(request,opt) will be called directly after the
         1164  +    AJAX request is finished, before onError() or onResonse() are
         1165  +    called. Possible TODO: we explicitly do NOT pass the response to
         1166  +    this function in order to keep the line between the responsibilities
         1167  +    of the various callback clear (otherwise this could be used the same
         1168  +    as onResponse()). In practice it would sometimes be useful have the
         1169  +    response passed to this function, mainly for logging/debugging
         1170  +    purposes.
         1171  +
         1172  +    The return value from this function is meaningless because
         1173  +    AJAX operations tend to take place asynchronously.
         1174  +
         1175  +*/
         1176  +WhAjaj.Connector.prototype.sendRequest = function(request,opt)
         1177  +{
         1178  +    if( !WhAjaj.isFunction(this.sendImpl) )
         1179  +    {
         1180  +        throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
         1181  +    }
         1182  +    var ex = false;
         1183  +    var av = Array.prototype.slice.apply( arguments, [0] );
         1184  +    
         1185  +    /**
         1186  +        FIXME: how to handle the error, vis-a-vis- the callbacks, if 
         1187  +        normalizeAjaxParameters() throws? It can throw if 
         1188  +        (de)JSON-izing fails.
         1189  +    */
         1190  +    var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
         1191  +    norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
         1192  +    if( ! request ) norm.method = 'GET';
         1193  +    var cb = this.callbacks || {};
         1194  +    if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
         1195  +        this.callbacks.beforeSend( request, norm );
         1196  +    }
         1197  +    if( WhAjaj.isFunction(norm.beforeSend) ){
         1198  +        norm.beforeSend( request, norm );
         1199  +    }
         1200  +    //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
         1201  +    try { this.sendImpl( request, norm ); }
         1202  +    catch(e) { ex = e; }
         1203  +    if(ex) throw ex;
         1204  +};
         1205  +
         1206  +/**
         1207  +    sendImpl() holds a concrete back-end connection implementation. It
         1208  +    can be replaced with a custom implementation if one follows the rules
         1209  +    described throughout this API. See WhAjaj.Connector.sendImpls for
         1210  +    the concrete implementations included with this API.
         1211  +*/
         1212  +//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
         1213  +//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
         1214  +//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
         1215  +
         1216  +if( 'undefined' !== typeof jQuery ){
         1217  +    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
         1218  +}
         1219  +else {
         1220  +    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
         1221  +}

Added ajax/wiki-editor.html.

            1  +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            2  +	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
            3  +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
            4  +
            5  +<head>
            6  +	<title>Fossil/JSON Wiki Editor Prototype</title>
            7  +	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
            8  +    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
            9  +    <script type="text/javascript" src="js/whajaj.js"></script>
           10  +    <script type="text/javascript" src="js/fossil-ajaj.js"></script>
           11  +
           12  +<style type='text/css'>
           13  +th {
           14  +  text-align: left;
           15  +  background-color: #ececec;  
           16  +}
           17  +
           18  +.dangerWillRobinson {
           19  +    background-color: yellow;
           20  +}
           21  +
           22  +.wikiPageLink {
           23  +    text-decoration: underline;
           24  +}
           25  +</style>
           26  +
           27  +<script type='text/javascript'>
           28  +WhAjaj.Connector.options.ajax.url =
           29  +/*
           30  +    Change this to your CGI/server root path:
           31  +*/
           32  +     //'http://fjson/cgi-bin/fossil.cgi'
           33  +     //'/repos/fossil-sgb/json.cgi'
           34  +    '/cgi-bin/fossil-json.cgi'
           35  +     ;
           36  +var TheApp = {
           37  +      response:null,
           38  +      sessionID:null,
           39  +      jqe:{}/*jqe==jQuery Elements*/,
           40  +      ajaxCount:0,
           41  +      cgi: new FossilAjaj(),
           42  +      pages:{}
           43  +};
           44  +
           45  +
           46  +TheApp.startAjaxNotif = function()
           47  +{
           48  +    ++this.ajaxCount;
           49  +    TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
           50  +    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
           51  +    if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
           52  +};
           53  +
           54  +TheApp.endAjaxNotif = function()
           55  +{
           56  +    --this.ajaxCount;
           57  +    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
           58  +    if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
           59  +};
           60  +
           61  +TheApp.responseContainsError = function(resp) {
           62  +    if( !resp || resp.resultCode ) {
           63  +        //alert("Error response:\n"+JSON.stringify(resp,0,4));
           64  +        TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
           65  +        //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
           66  +        //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
           67  +        TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
           68  +        return true;
           69  +    }
           70  +    return false;
           71  +};
           72  +
           73  +
           74  +TheApp.sendRequest = function() {
           75  +    var path = this.jqe.textPath.val();
           76  +    var self = this;
           77  +    var data = this.jqe.pageListArea.val();
           78  +    var doPost = (data && data.length);
           79  +    var req;
           80  +    if( doPost ) try {
           81  +        req = JSON.parse(data);
           82  +    }
           83  +    catch(e) {
           84  +        TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
           85  +        return;
           86  +    }
           87  +    if( req ) {
           88  +        req.requestId = this.cgi.generateRequestId();
           89  +    }
           90  +    var self = this;
           91  +    var opt = {
           92  +        url: WhAjaj.Connector.options.ajax.url + path,
           93  +        method: doPost ? 'POST' : 'GET'
           94  +    };
           95  +    this.cgi.sendRequest( req, opt );
           96  +};
           97  +jQuery.fn.animateHighlight = function(highlightColor, duration) {
           98  +    var highlightBg = highlightColor || "#FFFF9C";
           99  +    var animateMs = duration || 1500;
          100  +    var originalBg = this.css("backgroundColor");
          101  +    this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
          102  +};
          103  +jQuery.fn.flash = function( color, duration )
          104  +{
          105  +    var current = this.css( 'color' );
          106  +    this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
          107  +    this.animate( { color: current }, duration / 2 );
          108  +};
          109  +
          110  +jQuery(document).ready(function(){
          111  +    var ids = [
          112  +        'btnSend',
          113  +        'ajaxNotification',
          114  +        'currentAuthToken',
          115  +        'responseContainer',
          116  +        'spanPageName',
          117  +        'pageListArea',
          118  +        'taPageContent',
          119  +        'taResponse',
          120  +        'textPath', // list of HTML element IDs we use often.
          121  +        'timer'
          122  +    ];
          123  +    var i, k;
          124  +    for( i = 0; i < ids.length; ++i ) {
          125  +        k = ids[i];
          126  +        TheApp.jqe[k] = jQuery('#'+k);
          127  +    }
          128  +    TheApp.jqe.textPath.
          129  +        keyup(function(event){
          130  +            if(event.keyCode == 13){
          131  +                TheApp.sendRequest();
          132  +            }
          133  +        });
          134  +    TheApp.timer = {
          135  +        _tstart:0,_tend:0,duration:0,
          136  +        start:function(){
          137  +            this._tstart = (new Date()).getTime();
          138  +        },
          139  +        end:function(){
          140  +            this._tend = (new Date()).getTime();
          141  +            return this.duration = this._tend - this._tstart;
          142  +        }
          143  +    };
          144  +    var ajcb = TheApp.cgi.ajaj.callbacks;
          145  +    ajcb.beforeSend = TheApp.beforeSend = function(req,opt) {
          146  +        TheApp.timer.start();
          147  +        var val =
          148  +            req ?
          149  +            (('string'===typeof req) ? req : WhAjaj.stringify(req))
          150  +            : '';
          151  +        TheApp.jqe.taResponse.val('');
          152  +        TheApp.startAjaxNotif();
          153  +    };
          154  +    ajcb.afterSend = TheApp.afterSend = function(req,opt) {
          155  +        TheApp.timer.end();
          156  +        TheApp.endAjaxNotif();
          157  +        TheApp.jqe.timer.text( "(Round-trip time: "+TheApp.timer.duration+'ms)' );
          158  +    };
          159  +    ajcb.onResponse = TheApp.onResponse = function(resp,req) {
          160  +        var val;
          161  +        try {
          162  +            val = WhAjaj.stringify(resp);
          163  +        }
          164  +        catch(e) {
          165  +            val = WhAjaj.stringify(e)
          166  +        }
          167  +        if(resp.resultCode){
          168  +            alert("Response contains error info:\n"+val);
          169  +        }
          170  +        TheApp.jqe.taResponse.val( val );
          171  +    };
          172  +    ajcb.onError = function(req,opt) {
          173  +        TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
          174  +    };
          175  +
          176  +    TheApp.jqe.taPageContent.blur(function(){
          177  +        var p = TheApp.currentPage;
          178  +        if(! p ) return;
          179  +        p.content = TheApp.jqe.taPageContent.val();
          180  +    });
          181  +
          182  +    TheApp.cgi.onLogin = function(){
          183  +      TheApp.jqe.taResponse.val( "Logged in: "+WhAjaj.stringify(this.auth));
          184  +      TheApp.jqe.currentAuthToken.text("Logged in: "+WhAjaj.stringify(this.auth));
          185  +    };
          186  +    TheApp.cgi.onLogout = function(){
          187  +      TheApp.jqe.taResponse.val( "Logged out!" );
          188  +      TheApp.jqe.currentAuthToken.text("");
          189  +    };
          190  +
          191  +    TheApp.showPage = function(name){
          192  +        function doShow(page){
          193  +            TheApp.currentPage = page;
          194  +            TheApp.jqe.spanPageName.text('('+page.name+')');
          195  +            TheApp.jqe.taPageContent.val(page.content);
          196  +        }
          197  +        var p = ('object' === typeof name) ? name : TheApp.pages[name];
          198  +        if(('object' === typeof p) && p.content) {
          199  +            doShow(p);
          200  +            return;
          201  +        }
          202  +        TheApp.cgi.sendCommand('/json/wiki/get',{
          203  +            name:name
          204  +        },{
          205  +            onResponse:function(resp,req){
          206  +                TheApp.onResponse(resp,req);
          207  +                if(resp.resultCode) return;
          208  +                var p = resp.payload;
          209  +                doShow( TheApp.pages[p.name] = p );
          210  +            }
          211  +        });
          212  +    };
          213  +    TheApp.refreshPageListView = function(){
          214  +        var list = (function(){
          215  +            var k, v, li = [];
          216  +            for( k in TheApp.pages ){
          217  +                if(!TheApp.pages.hasOwnProperty(k)) continue;
          218  +                li.push(k);
          219  +            }
          220  +            return li;                
          221  +        })();
          222  +        var i, p, a, tgt = TheApp.jqe.pageListArea;
          223  +        tgt.text('');
          224  +        function makeLink(name){
          225  +            var link = jQuery('<span></span>');
          226  +            link.text(name);
          227  +            link.addClass('wikiPageLink');
          228  +            link.click(function(e){
          229  +                TheApp.showPage(name);
          230  +                e.preventDefault();
          231  +                return false;
          232  +            });
          233  +            return link;
          234  +        }
          235  +        list.sort();
          236  +        for( i = 0; i < list.length; ++i ){
          237  +            tgt.append(makeLink(list[i]));
          238  +            tgt.append('<br/>');
          239  +        }
          240  +    };
          241  +
          242  +    TheApp.loadPageList = function(){
          243  +        TheApp.cgi.sendCommand('/json/wiki/list',null,{
          244  +            onResponse:function(resp,req){
          245  +                TheApp.onResponse(resp,req);
          246  +                if(resp.resultCode) return;
          247  +                var i, v, p, ar = resp.payload;
          248  +                for( i = 0; i < ar.length; ++i ){
          249  +                    v = ar[i];
          250  +                    p = TheApp.pages[v];
          251  +                    if( !p ) TheApp.pages[v] = {name:v};
          252  +                }
          253  +                TheApp.refreshPageListView();
          254  +            }
          255  +        });
          256  +        return false /*for click handlers*/;
          257  +    }
          258  +
          259  +    TheApp.savePage = function(p){
          260  +        p = p || TheApp.currentPage;
          261  +        if( 'object' !== typeof p ){
          262  +            p = TheApp.pages[p];
          263  +        }
          264  +        if('object' !== typeof p){
          265  +            alert("savePage() argument is not a page object or known page name.");
          266  +        }
          267  +        TheApp.pages[p.name] = p;
          268  +        p.content = TheApp.jqe.taPageContent.val();
          269  +        var req = {
          270  +            name:p.name,
          271  +            content:p.content
          272  +        };
          273  +        if(! confirm("Really save wiki page ["+p.name+"]?") ) return;
          274  +        TheApp.cgi.sendCommand('/json/wiki/'+(p.isNew?'create':'save'),req,{
          275  +            onResponse:function(resp,req){
          276  +                TheApp.onResponse(resp,req);
          277  +                if(resp.resultCode) return;
          278  +                delete p.isNew;
          279  +                p.timestamp = resp.payload.timestamp;
          280  +            }
          281  +        });
          282  +        
          283  +    };
          284  +
          285  +    TheApp.createNewPage = function(){
          286  +        var name = prompt("New page name?");
          287  +        if(!name) return;
          288  +        var p = {
          289  +            name:name,
          290  +            content:"New, empty page.",
          291  +            isNew:true
          292  +        };
          293  +        TheApp.pages[name] = p;
          294  +        TheApp.refreshPageListView();
          295  +        TheApp.showPage(p);
          296  +/*
          297  +        if(! confirm("Really create new wiki page ["+name+"]?") ) return;
          298  +        TheApp.cgi.sendCommand('/json/wiki/create',req,{
          299  +            onResponse:function(resp,req){
          300  +                TheApp.onResponse(resp,req);
          301  +                if(resp.resultCode) return;
          302  +                TheApp.pages[p.name] = p;
          303  +                TheApp.refreshPageListView();
          304  +            }
          305  +        });
          306  +*/
          307  +    };
          308  +
          309  +    TheApp.cgi.whoami();
          310  +
          311  +});
          312  +
          313  +</script>
          314  +
          315  +</head>
          316  +
          317  +<body>
          318  +<span id='ajaxNotification'></span>
          319  +<h1>PROTOTYPE JSON-based Fossil Wiki Editor</h1>
          320  +
          321  +See also: <a href='index.html'>main test page</a>.
          322  +
          323  +<br>
          324  +<b>Login:</b>
          325  +<br/>
          326  +<input type='button' value='Anon. Login' onclick='TheApp.cgi.login()' />
          327  +or: 
          328  +name:<input type='text' id='textUser' value='json-demo' size='12'/>
          329  +pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
          330  +<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
          331  +<input type='button' value='logout' onclick='TheApp.cgi.logout()' />
          332  +
          333  +<br/>
          334  +<span id='currentAuthToken' style='font-family:monospaced'></span>
          335  +
          336  +<hr/>
          337  +<strong>Quick-posts:</strong><br/>
          338  +<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
          339  +<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
          340  +<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
          341  +<input type='button' value='wiki/list' onclick='TheApp.loadPageList()' />
          342  +<!--
          343  +<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' />
          344  +-->
          345  +
          346  +<!--
          347  +<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
          348  +<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
          349  +<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
          350  +<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
          351  +-->
          352  +<hr/>
          353  +
          354  +<table>
          355  +    <tr>
          356  +        <th>Page List</th>
          357  +        <th>Content <span id='spanPageName'></span></th>
          358  +    </tr>
          359  +    <tr>
          360  +        <td width='25%' valign='top'>
          361  +            <input type='button' value='Create new...' onclick='TheApp.createNewPage()' /><br/>
          362  +            <div id='pageListArea'></div>
          363  +        </td>
          364  +        <td width='75%' valign='top'>
          365  +            <input type='button' value='Save' onclick='TheApp.savePage()' /><br/>
          366  +            <textarea id='taPageContent' rows='20' cols='60'></textarea>
          367  +        </td>
          368  +    </tr>
          369  +    <tr>
          370  +        <th colspan='2'>Response <span id='timer'></span></th>
          371  +    </tr>
          372  +    <tr>
          373  +        <td colspan='2' id='responseContainer'>
          374  +            <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
          375  +        </td>
          376  +    </tr>
          377  +</table>
          378  +<div></div>
          379  +<div></div>
          380  +<div></div>
          381  +
          382  +</body></html>

Added auto.def.

            1  +# System autoconfiguration. Try: ./configure --help
            2  +
            3  +use cc cc-lib
            4  +
            5  +options {
            6  +    with-openssl:path|auto|none
            7  +                         => {Look for openssl in the given path, or auto or none}
            8  +    with-zlib:path       => {Look for zlib in the given path}
            9  +    with-tcl:path        => {Enable Tcl integration, with Tcl in the specified path}
           10  +    with-tcl-stubs=0     => {Enable Tcl integration via stubs mechanism}
           11  +    internal-sqlite=1    => {Don't use the internal sqlite, use the system one}
           12  +    static=0             => {Link a static executable}
           13  +    lineedit=1           => {Disable line editing}
           14  +    fossil-debug=0       => {Build with fossil debugging enabled}
           15  +    json=0               => {Build with fossil JSON API enabled}
           16  +    markdown=0           => {Build with markdown engine enabled}
           17  +}
           18  +
           19  +# sqlite wants these types if possible
           20  +cc-with {-includes {stdint.h inttypes.h}} {
           21  +    cc-check-types uint32_t uint16_t int16_t uint8_t
           22  +}
           23  +
           24  +# Use pread/pwrite system calls in place of seek + read/write if possible
           25  +define USE_PREAD [cc-check-functions pread]
           26  +
           27  +# Find tclsh for the test suite. Can't yet use jimsh for this.
           28  +cc-check-progs tclsh
           29  +
           30  +define EXTRA_CFLAGS ""
           31  +define EXTRA_LDFLAGS ""
           32  +define USE_SYSTEM_SQLITE ""
           33  +
           34  +if {![opt-bool internal-sqlite]} {
           35  +  proc find_internal_sqlite {} {
           36  +
           37  +    # On some systems (slackware), libsqlite3 requires -ldl to link. So
           38  +    # search for the system SQLite once with -ldl, and once without. If
           39  +    # the library can only be found with $extralibs set to -ldl, then
           40  +    # the code below will append -ldl to LIBS.
           41  +    #
           42  +    foreach extralibs {{} {-ldl}} {
           43  +
           44  +      # Locate the system SQLite by searching for sqlite3_open(). Then check
           45  +      # if sqlite3_wal_checkpoint() can be found as well. If we can find
           46  +      # open() but not wal_checkpoint(), then the system SQLite is too old
           47  +      # to link against fossil.
           48  +      #
           49  +      if {[cc-check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
           50  +        if {![cc-check-function-in-lib sqlite3_wal_checkpoint sqlite3 $extralibs]} {
           51  +          user-error "system sqlite3 too old (require >= 3.7.0)"
           52  +        }
           53  +
           54  +        # Success. Update symbols and return.
           55  +        #
           56  +        define USE_SYSTEM_SQLITE 1
           57  +        define-append LIBS $extralibs
           58  +        return
           59  +      }
           60  +    }
           61  +    user-error "system sqlite3 not found"
           62  +  }
           63  +
           64  +  find_internal_sqlite
           65  +}
           66  +
           67  +if {[opt-bool fossil-debug]} {
           68  +    define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
           69  +}
           70  +
           71  +if {[opt-bool json]} {
           72  +    # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON
           73  +    # is required in the CFLAGS because json*.c
           74  +    # have #ifdef guards around the whole file without
           75  +    # reading config.h first.
           76  +    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
           77  +    define FOSSIL_ENABLE_JSON
           78  +}
           79  +
           80  +if {[opt-bool markdown]} {
           81  +    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_MARKDOWN
           82  +    define FOSSIL_ENABLE_MARKDOWN
           83  +}
           84  +
           85  +if {[opt-bool static]} {
           86  +    # XXX: This will not work on all systems.
           87  +    define-append EXTRA_LDFLAGS -static
           88  +}
           89  +
           90  +# Check for zlib, using the given location if specified
           91  +set zlibpath [opt-val with-zlib]
           92  +if {$zlibpath ne ""} {
           93  +    cc-with [list -cflags "-I$zlibpath -L$zlibpath"]
           94  +    define-append EXTRA_CFLAGS -I$zlibpath
           95  +    define-append EXTRA_LDFLAGS -L$zlibpath
           96  +}
           97  +if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
           98  +    user-error "zlib not found please install it or specify the location with --with-zlib"
           99  +}
          100  +
          101  +set tclpath [opt-val with-tcl]
          102  +if {$tclpath ne ""} {
          103  +    # Note parse-tclconfig-sh is in autosetup/local.tcl
          104  +    if {$tclpath eq "1"} {
          105  +        # Use the system Tcl. Look in some likely places.
          106  +        array set tclconfig [parse-tclconfig-sh \
          107  +            /usr /usr/local /usr/share /opt/local]
          108  +        set msg "on your system"
          109  +    } else {
          110  +        array set tclconfig [parse-tclconfig-sh $tclpath]
          111  +        set msg "at $tclpath"
          112  +    }
          113  +    if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
          114  +        user-error "Cannot find Tcl $msg"
          115  +    }
          116  +    set tclstubs [opt-bool with-tcl-stubs]
          117  +    if {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
          118  +        set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
          119  +        define FOSSIL_ENABLE_TCL_STUBS
          120  +        define USE_TCL_STUBS
          121  +    } else {
          122  +        set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
          123  +    }
          124  +    set cflags $tclconfig(TCL_INCLUDE_SPEC)
          125  +    cc-with [list -cflags $cflags -libs $libs] {
          126  +        if {$tclstubs} {
          127  +            if {![cc-check-functions Tcl_InitStubs]} {
          128  +                user-error "Cannot find a usable Tcl stubs library $msg"
          129  +            }
          130  +        } else {
          131  +            if {![cc-check-functions Tcl_CreateInterp]} {
          132  +                user-error "Cannot find a usable Tcl library $msg"
          133  +            }
          134  +        }
          135  +    }
          136  +    set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
          137  +    msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
          138  +    define-append LIBS $libs
          139  +    define-append EXTRA_CFLAGS $cflags
          140  +    define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
          141  +    define FOSSIL_ENABLE_TCL
          142  +}
          143  +
          144  +# Helper for openssl checking
          145  +proc check-for-openssl {msg {cflags {}}} {
          146  +    msg-checking "Checking for $msg..."
          147  +    set rc 0
          148  +    msg-quiet cc-with [list -cflags $cflags -libs {-lssl -lcrypto}] {
          149  +        if {[cc-check-includes openssl/ssl.h] && [cc-check-functions SSL_new]} {
          150  +            incr rc
          151  +        }
          152  +    }
          153  +    if {$rc} {
          154  +        msg-result "ok"
          155  +        return 1
          156  +    } else {
          157  +        msg-result "no"
          158  +        return 0
          159  +    }
          160  +}
          161  +
          162  +set ssldirs [opt-val with-openssl]
          163  +if {$ssldirs ne "none"} {
          164  +    set found 0
          165  +    if {$ssldirs in {auto ""}} {
          166  +        catch {
          167  +            set cflags [exec pkg-config openssl --cflags-only-I]
          168  +            set ldflags [exec pkg-config openssl --libs-only-L]
          169  +
          170  +            set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"]
          171  +        } msg
          172  +        if {!$found} {
          173  +            set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr"
          174  +        }
          175  +    }
          176  +    if {!$found} {
          177  +        foreach dir $ssldirs {
          178  +            if {$dir eq ""} {
          179  +                set msg "system ssl"
          180  +                set cflags ""
          181  +                set ldflags ""
          182  +            } else {
          183  +                set msg "ssl in $dir"
          184  +                set cflags "-I$dir/include"
          185  +                set ldflags "-L$dir/lib"
          186  +            }
          187  +            if {[check-for-openssl $msg "$cflags $ldflags"]} {
          188  +                incr found
          189  +                break
          190  +            }
          191  +        }
          192  +    }
          193  +    if {$found} {
          194  +        define FOSSIL_ENABLE_SSL
          195  +        define-append EXTRA_CFLAGS $cflags
          196  +        define-append EXTRA_LDFLAGS $ldflags
          197  +        define-append LIBS -lssl -lcrypto
          198  +        msg-result "HTTPS support enabled"
          199  +
          200  +        # Silence OpenSSL deprecation warnings on Mac OS X 10.7.
          201  +        if {[string match *-darwin* [get-define host]]} {
          202  +            if {[cctest -cflags {-Wdeprecated-declarations}]} {
          203  +                define-append EXTRA_CFLAGS -Wdeprecated-declarations
          204  +            }
          205  +        }
          206  +    } else {
          207  +        user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
          208  +    }
          209  +}
          210  +
          211  +if {[opt-bool lineedit]} {
          212  +    # Need readline-compatible line editing
          213  +    cc-with {-includes stdio.h} {
          214  +        if {[cc-check-includes readline/readline.h] && [cc-check-function-in-lib readline readline]} {
          215  +            msg-result "Using readline for line editing"
          216  +        } elseif {[cc-check-includes editline/readline.h] && [cc-check-function-in-lib readline edit]} {
          217  +            define-feature editline
          218  +            msg-result "Using editline for line editing"
          219  +        }
          220  +    }
          221  +}
          222  +
          223  +# Network functions require libraries on some systems
          224  +cc-check-function-in-lib gethostbyname nsl
          225  +if {![cc-check-function-in-lib socket {socket network}]} {
          226  +    # Last resort, may be Windows
          227  +    if {[string match *mingw* [get-define host]]} {
          228  +        define-append LIBS -lwsock32
          229  +    }
          230  +}
          231  +cc-check-function-in-lib iconv iconv
          232  +
          233  +# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
          234  +if {![cc-check-functions getpassphrase]} {
          235  +    # Haiku needs this
          236  +    cc-check-function-in-lib getpass bsd
          237  +}
          238  +cc-check-function-in-lib dlopen dl
          239  +
          240  +make-template Makefile.in
          241  +make-config-header autoconfig.h -auto {USE_* FOSSIL_*}

Added autosetup/LICENSE.

            1  +Unless explicitly stated, all files which form part of autosetup
            2  +are released under the following license:
            3  +
            4  +---------------------------------------------------------------------
            5  +autosetup - A build environment "autoconfigurator"
            6  +
            7  +Copyright (c) 2010-2011, WorkWare Systems <http://workware.net.au/>
            8  +
            9  +Redistribution and use in source and binary forms, with or without
           10  +modification, are permitted provided that the following conditions
           11  +are met:
           12  +
           13  +1. Redistributions of source code must retain the above copyright
           14  +   notice, this list of conditions and the following disclaimer.
           15  +2. Redistributions in binary form must reproduce the above
           16  +   copyright notice, this list of conditions and the following
           17  +   disclaimer in the documentation and/or other materials
           18  +   provided with the distribution.
           19  +
           20  +THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY
           21  +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
           22  +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
           23  +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE
           24  +SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
           25  +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
           26  +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
           27  +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
           28  +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
           29  +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
           30  +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
           31  +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
           32  +
           33  +The views and conclusions contained in the software and documentation
           34  +are those of the authors and should not be interpreted as representing
           35  +official policies, either expressed or implied, of WorkWare Systems.

Added autosetup/README.autosetup.

            1  +This is autosetup v0.6.4. See http://msteveb.github.com/autosetup/

Added autosetup/autosetup.

            1  +#!/bin/sh
            2  +# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/
            3  +# All rights reserved
            4  +# vim:se syntax=tcl:
            5  +# \
            6  +dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@"
            7  +
            8  +set autosetup(version) 0.6.4
            9  +
           10  +# Can be set to 1 to debug early-init problems
           11  +set autosetup(debug) 0
           12  +
           13  +##################################################################
           14  +#
           15  +# Main flow of control, option handling
           16  +#
           17  +proc main {argv} {
           18  +	global autosetup define
           19  +
           20  +	# There are 3 potential directories involved:
           21  +	# 1. The directory containing autosetup (this script)
           22  +	# 2. The directory containing auto.def
           23  +	# 3. The current directory
           24  +
           25  +	# From this we need to determine:
           26  +	# a. The path to this script (and related support files)
           27  +	# b. The path to auto.def
           28  +	# c. The build directory, where output files are created
           29  +
           30  +	# This is also complicated by the fact that autosetup may
           31  +	# have been run via the configure wrapper ([getenv WRAPPER] is set)
           32  +
           33  +	# Here are the rules.
           34  +	# a. This script is $::argv0
           35  +	#    => dir, prog, exe, libdir
           36  +	# b. auto.def is in the directory containing the configure wrapper,
           37  +	#    otherwise it is in the current directory.
           38  +	#    => srcdir, autodef
           39  +	# c. The build directory is the current directory
           40  +	#    => builddir, [pwd]
           41  +
           42  +	# 'misc' is needed before we can do anything, so set a temporary libdir
           43  +	# in case this is the development version
           44  +	set autosetup(libdir) [file dirname $::argv0]/lib
           45  +	use misc
           46  +
           47  +	# (a)
           48  +	set autosetup(dir) [realdir [file dirname [realpath $::argv0]]]
           49  +	set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]]
           50  +	set autosetup(exe) [getenv WRAPPER $autosetup(prog)]
           51  +	if {$autosetup(installed)} {
           52  +		set autosetup(libdir) $autosetup(dir)
           53  +	} else {
           54  +		set autosetup(libdir) [file join $autosetup(dir) lib]
           55  +	}
           56  +	autosetup_add_dep $autosetup(prog)
           57  +
           58  +	# (b)
           59  +	if {[getenv WRAPPER ""] eq ""} {
           60  +		# Invoked directly
           61  +		set autosetup(srcdir) [pwd]
           62  +	} else {
           63  +		# Invoked via the configure wrapper
           64  +		set autosetup(srcdir) [file dirname $autosetup(exe)]
           65  +	}
           66  +	set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def]
           67  +
           68  +	# (c)
           69  +	set autosetup(builddir) [pwd]
           70  +
           71  +	set autosetup(argv) $argv
           72  +	set autosetup(cmdline) {}
           73  +	set autosetup(options) {}
           74  +	set autosetup(optionhelp) {}
           75  +	set autosetup(showhelp) 0
           76  +
           77  +	# Parse options
           78  +	use getopt
           79  +
           80  +	array set ::useropts [getopt argv]
           81  +
           82  +	#"=Core Options:"
           83  +	options-add {
           84  +		help:=local  => "display help and options. Optionally specify a module name, such as --help=system"
           85  +		version      => "display the version of autosetup"
           86  +		ref:=text manual:=text
           87  +		reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
           88  +		debug        => "display debugging output as autosetup runs"
           89  +		install:=.   => "install autosetup to the current or given directory (in the 'autosetup/' subdirectory)"
           90  +		force init   => "create an initial 'configure' script if none exists"
           91  +		# Undocumented options
           92  +		option-checking=1
           93  +		nopager
           94  +		quiet
           95  +		timing
           96  +		conf:
           97  +	}
           98  +
           99  +	#parray ::useropts
          100  +	if {[opt-bool version]} {
          101  +		puts $autosetup(version)
          102  +		exit 0
          103  +	}
          104  +
          105  +	# autosetup --conf=alternate-auto.def
          106  +	if {[opt-val conf] ne ""} {
          107  +		set autosetup(autodef) [opt-val conf]
          108  +	}
          109  +
          110  +	# Debugging output (set this early)
          111  +	incr autosetup(debug) [opt-bool debug]
          112  +	incr autosetup(force) [opt-bool force]
          113  +	incr autosetup(msg-quiet) [opt-bool quiet]
          114  +	incr autosetup(msg-timing) [opt-bool timing]
          115  +
          116  +	# If the local module exists, source it now to allow for
          117  +	# project-local customisations
          118  +	if {[file exists $autosetup(libdir)/local.tcl]} {
          119  +		use local
          120  +	}
          121  +
          122  +	if {[opt-val help] ne ""} {
          123  +		incr autosetup(showhelp)
          124  +		use help
          125  +		autosetup_help [opt-val help]
          126  +	}
          127  +
          128  +	if {[opt-val {manual ref reference}] ne ""} {
          129  +		use help
          130  +		autosetup_reference [opt-val {manual ref reference}]
          131  +	}
          132  +
          133  +	if {[opt-bool init]} {
          134  +		use init
          135  +		autosetup_init
          136  +	}
          137  +
          138  +	if {[opt-val install] ne ""} {
          139  +		use install
          140  +		autosetup_install [opt-val install]
          141  +	}
          142  +
          143  +	if {![file exists $autosetup(autodef)]} {
          144  +		# Check for invalid option first
          145  +		options {}
          146  +		user-error "No auto.def found in $autosetup(srcdir)"
          147  +	}
          148  +
          149  +	# Parse extra arguments into autosetup(cmdline)
          150  +	foreach arg $argv {
          151  +		if {[regexp {([^=]*)=(.*)} $arg -> n v]} {
          152  +			dict set autosetup(cmdline) $n $v
          153  +			define $n $v
          154  +		} else {
          155  +			user-error "Unexpected parameter: $arg"
          156  +		}
          157  +	}
          158  +
          159  +	autosetup_add_dep $autosetup(autodef)
          160  +
          161  +	set cmd [file-normalize $autosetup(exe)]
          162  +	foreach arg $autosetup(argv) {
          163  +		append cmd " [quote-if-needed $arg]"
          164  +	}
          165  +	define AUTOREMAKE $cmd
          166  +
          167  +	# Log how we were invoked
          168  +	configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"
          169  +
          170  +	source $autosetup(autodef)
          171  +
          172  +	# Could warn here if options {} was not specified
          173  +
          174  +	show-notices
          175  +
          176  +	if {$autosetup(debug)} {
          177  +		parray define
          178  +	}
          179  +
          180  +	exit 0
          181  +}
          182  +
          183  +# @opt-bool option ...
          184  +#
          185  +# Check each of the named, boolean options and return 1 if any of them have
          186  +# been set by the user.
          187  +#
          188  +proc opt-bool {args} {
          189  +	option-check-names {*}$args
          190  +	opt_bool ::useropts {*}$args
          191  +}
          192  +
          193  +# @opt-val option-list ?default=""?
          194  +#
          195  +# Returns a list containing all the values given for the non-boolean options in 'option-list'.
          196  +# There will be one entry in the list for each option given by the user, including if the
          197  +# same option was used multiple times.
          198  +# If only a single value is required, use something like:
          199  +#
          200  +## lindex [opt-val $names] end
          201  +#
          202  +# If no options were set, $default is returned (exactly, not as a list).
          203  +#
          204  +proc opt-val {names {default ""}} {
          205  +	option-check-names {*}$names
          206  +	join [opt_val ::useropts $names $default]
          207  +}
          208  +
          209  +proc option-check-names {args} {
          210  +	foreach o $args {
          211  +		if {$o ni $::autosetup(options)} {
          212  +			autosetup-error "Request for undeclared option --$o"
          213  +		}
          214  +	}
          215  +}
          216  +
          217  +# Parse the option definition in $opts and update
          218  +# ::useropts() and ::autosetup(optionhelp) appropriately
          219  +#
          220  +proc options-add {opts {header ""}} {
          221  +	global useropts autosetup
          222  +
          223  +	# First weed out comment lines
          224  +	set realopts {}
          225  +	foreach line [split $opts \n] {
          226  +		if {![string match "#*" [string trimleft $line]]} {
          227  +			append realopts $line \n
          228  +		}
          229  +	}
          230  +	set opts $realopts
          231  +
          232  +	for {set i 0} {$i < [llength $opts]} {incr i} {
          233  +		set opt [lindex $opts $i]
          234  +		if {[string match =* $opt]} {
          235  +			# This is a special heading
          236  +			lappend autosetup(optionhelp) $opt ""
          237  +			set header {}
          238  +			continue
          239  +		}
          240  +
          241  +		#puts "i=$i, opt=$opt"
          242  +		regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
          243  +		if {$name in $autosetup(options)} {
          244  +			autosetup-error "Option $name already specified"
          245  +		}
          246  +
          247  +		#puts "$opt => $name $colon $equal $value"
          248  +
          249  +		# Find the corresponding value in the user options
          250  +		# and set the default if necessary
          251  +		if {[string match "-*" $opt]} {
          252  +			# This is a documentation-only option, like "-C <dir>"
          253  +			set opthelp $opt
          254  +		} elseif {$colon eq ""} {
          255  +			# Boolean option
          256  +			lappend autosetup(options) $name
          257  +
          258  +			if {![info exists useropts($name)]} {
          259  +				set useropts($name) $value
          260  +			}
          261  +			if {$value eq "1"} {
          262  +				set opthelp "--disable-$name"
          263  +			} else {
          264  +				set opthelp "--$name"
          265  +			}
          266  +		} else {
          267  +			# String option.
          268  +			lappend autosetup(options) $name
          269  +
          270  +			if {$equal eq "="} {
          271  +				if {[info exists useropts($name)]} {
          272  +					# If the user specified the option with no value, the value will be "1"
          273  +					# Replace with the default
          274  +					if {$useropts($name) eq "1"} {
          275  +						set useropts($name) $value
          276  +					}
          277  +				}
          278  +				set opthelp "--$name?=$value?"
          279  +			} else {
          280  +				set opthelp "--$name=$value"
          281  +			}
          282  +		}
          283  +
          284  +		# Now create the help for this option if appropriate
          285  +		if {[lindex $opts $i+1] eq "=>"} {
          286  +			set desc [lindex $opts $i+2]
          287  +			#string match \n* $desc
          288  +			if {$header ne ""} {
          289  +				lappend autosetup(optionhelp) $header ""
          290  +				set header ""
          291  +			}
          292  +			# A multi-line description
          293  +			lappend autosetup(optionhelp) $opthelp $desc
          294  +			incr i 2
          295  +		}
          296  +	}
          297  +}
          298  +
          299  +# @module-options optionlist
          300  +#
          301  +# Like 'options', but used within a module.
          302  +proc module-options {opts} {
          303  +	set header ""
          304  +	if {$::autosetup(showhelp) > 1 && [llength $opts]} {
          305  +		set header "Module Options:"
          306  +	}
          307  +	options-add $opts $header
          308  +
          309  +	if {$::autosetup(showhelp)} {
          310  +		# Ensure that the module isn't executed on --help
          311  +		# We are running under eval or source, so use break
          312  +		# to prevent further execution
          313  +		#return -code break -level 2
          314  +		return -code break
          315  +	}
          316  +}
          317  +
          318  +proc max {a b} {
          319  +	expr {$a > $b ? $a : $b}
          320  +}
          321  +
          322  +proc options-wrap-desc {text length firstprefix nextprefix initial} {
          323  +	set len $initial
          324  +	set space $firstprefix
          325  +	foreach word [split $text] {
          326  +		set word [string trim $word]
          327  +		if {$word == ""} {
          328  +			continue
          329  +		}
          330  +		if {$len && [string length $space$word] + $len >= $length} {
          331  +			puts ""
          332  +			set len 0
          333  +			set space $nextprefix
          334  +		}
          335  +		incr len [string length $space$word]
          336  +		puts -nonewline $space$word
          337  +		set space " "
          338  +	}
          339  +	if {$len} {
          340  +		puts ""
          341  +	}
          342  +}
          343  +
          344  +proc options-show {} {
          345  +	# Determine the max option width
          346  +	set max 0
          347  +	foreach {opt desc} $::autosetup(optionhelp) {
          348  +		if {[string match =* $opt] || [string match \n* $desc]} {
          349  +			continue
          350  +		}
          351  +		set max [max $max [string length $opt]]
          352  +	}
          353  +	set indent [string repeat " " [expr $max+4]]
          354  +	set cols [getenv COLUMNS 80]
          355  +	catch {
          356  +		lassign [exec stty size] rows cols
          357  +	}
          358  +	incr cols -1
          359  +	# Now output
          360  +	foreach {opt desc} $::autosetup(optionhelp) {
          361  +		if {[string match =* $opt]} {
          362  +			puts [string range $opt 1 end]
          363  +			continue
          364  +		}
          365  +		puts -nonewline "  [format %-${max}s $opt]"
          366  +		if {[string match \n* $desc]} {
          367  +			puts $desc
          368  +		} else {
          369  +			options-wrap-desc [string trim $desc] $cols "  " $indent [expr $max + 2]
          370  +		}
          371  +	}
          372  +}
          373  +
          374  +# @options options-spec
          375  +#
          376  +# Specifies configuration-time options which may be selected by the user
          377  +# and checked with opt-val and opt-bool. The format of options-spec follows.
          378  +#
          379  +# A boolean option is of the form:
          380  +#
          381  +## name[=0|1]  => "Description of this boolean option"
          382  +#
          383  +# The default is name=0, meaning that the option is disabled by default.
          384  +# If name=1 is used to make the option enabled by default, the description should reflect
          385  +# that with text like "Disable support for ...".
          386  +#
          387  +# An argument option (one which takes a parameter) is of the form:
          388  +#
          389  +## name:[=]value  => "Description of this option"
          390  +#
          391  +# If the name:value form is used, the value must be provided with the option (as --name=myvalue).
          392  +# If the name:=value form is used, the value is optional and the given value is used as the default
          393  +# if is not provided.
          394  +#
          395  +# Undocumented options are also supported by omitting the "=> description.
          396  +# These options are not displayed with --help and can be useful for internal options or as aliases.
          397  +#
          398  +# For example, --disable-lfs is an alias for --disable=largefile:
          399  +#
          400  +## lfs=1 largefile=1 => "Disable large file support"
          401  +#
          402  +proc options {optlist} {
          403  +	# Allow options as a list or args
          404  +	options-add $optlist "Local Options:"
          405  +
          406  +	if {$::autosetup(showhelp)} {
          407  +		options-show
          408  +		exit 0
          409  +	}
          410  +
          411  +	# Check for invalid options
          412  +	if {[opt-bool option-checking]} {
          413  +		foreach o [array names ::useropts] {
          414  +			if {$o ni $::autosetup(options)} {
          415  +				user-error "Unknown option --$o"
          416  +			}
          417  +		}
          418  +	}
          419  +}
          420  +
          421  +proc config_guess {} {
          422  +	if {[file-isexec $::autosetup(dir)/config.guess]} {
          423  +		exec-with-stderr sh $::autosetup(dir)/config.guess
          424  +	} else {
          425  +		configlog "No config.guess, so using uname"
          426  +		string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r]
          427  +	}
          428  +}
          429  +
          430  +proc config_sub {alias} {
          431  +	if {[file-isexec $::autosetup(dir)/config.sub]} {
          432  +		exec-with-stderr sh $::autosetup(dir)/config.sub $alias
          433  +	} else {
          434  +		return $alias
          435  +	}
          436  +}
          437  +
          438  +# @define name ?value=1?
          439  +#
          440  +# Defines the named variable to the given value.
          441  +# These (name, value) pairs represent the results of the configuration check
          442  +# and are available to be checked, modified and substituted.
          443  +#
          444  +proc define {name {value 1}} {
          445  +	set ::define($name) $value
          446  +	#dputs "$name <= $value"
          447  +}
          448  +
          449  +# @define-append name value ...
          450  +#
          451  +# Appends the given value(s) to the given 'defined' variable.
          452  +# If the variable is not defined or empty, it is set to $value.
          453  +# Otherwise the value is appended, separated by a space.
          454  +# Any extra values are similarly appended.
          455  +# If any value is already contained in the variable (as a substring) it is omitted.
          456  +#
          457  +proc define-append {name args} {
          458  +	if {[get-define $name ""] ne ""} {
          459  +		# Make a token attempt to avoid duplicates
          460  +		foreach arg $args {
          461  +			if {[string first $arg $::define($name)] == -1} {
          462  +				append ::define($name) " " $arg
          463  +			}
          464  +		}
          465  +	} else {
          466  +		set ::define($name) [join $args]
          467  +	}
          468  +	#dputs "$name += [join $args] => $::define($name)"
          469  +}
          470  +
          471  +# @get-define name ?default=0?
          472  +#
          473  +# Returns the current value of the 'defined' variable, or $default
          474  +# if not set.
          475  +#
          476  +proc get-define {name {default 0}} {
          477  +	if {[info exists ::define($name)]} {
          478  +		#dputs "$name => $::define($name)"
          479  +		return $::define($name)
          480  +	}
          481  +	#dputs "$name => $default"
          482  +	return $default
          483  +}
          484  +
          485  +# @is-defined name
          486  +#
          487  +# Returns 1 if the given variable is defined.
          488  +#
          489  +proc is-defined {name} {
          490  +	info exists ::define($name)
          491  +}
          492  +
          493  +# @all-defines
          494  +#
          495  +# Returns a dictionary (name value list) of all defined variables.
          496  +#
          497  +# This is suitable for use with 'dict', 'array set' or 'foreach'
          498  +# and allows for arbitrary processing of the defined variables.
          499  +#
          500  +proc all-defines {} {
          501  +	array get ::define
          502  +}
          503  +
          504  +
          505  +# @get-env name default
          506  +#
          507  +# If $name was specified on the command line, return it.
          508  +# If $name was set in the environment, return it.
          509  +# Otherwise return $default.
          510  +#
          511  +proc get-env {name default} {
          512  +	if {[dict exists $::autosetup(cmdline) $name]} {
          513  +		return [dict get $::autosetup(cmdline) $name]
          514  +	}
          515  +	getenv $name $default
          516  +}
          517  +
          518  +# @env-is-set name
          519  +#
          520  +# Returns 1 if the $name was specified on the command line or in the environment.
          521  +# Note that an empty environment variable is not considered to be set.
          522  +#
          523  +proc env-is-set {name} {
          524  +	if {[dict exists $::autosetup(cmdline) $name]} {
          525  +		return 1
          526  +	}
          527  +	if {[getenv $name ""] ne ""} {
          528  +		return 1
          529  +	}
          530  +	return 0
          531  +}
          532  +
          533  +# @readfile filename ?default=""?
          534  +#
          535  +# Return the contents of the file, without the trailing newline.
          536  +# If the doesn't exist or can't be read, returns $default.
          537  +#
          538  +proc readfile {filename {default_value ""}} {
          539  +	set result $default_value
          540  +	catch {
          541  +		set f [open $filename]
          542  +		set result [read -nonewline $f]
          543  +		close $f
          544  +	}
          545  +	return $result
          546  +}
          547  +
          548  +# @writefile filename value
          549  +#
          550  +# Creates the given file containing $value.
          551  +# Does not add an extra newline.
          552  +#
          553  +proc writefile {filename value} {
          554  +	set f [open $filename w]
          555  +	puts -nonewline $f $value
          556  +	close $f
          557  +}
          558  +
          559  +proc quote-if-needed {str} {
          560  +	if {[string match {*[\" ]*} $str]} {
          561  +		return \"[string map [list \" \\" \\ \\\\] $str]\"
          562  +	}
          563  +	return $str
          564  +}
          565  +
          566  +proc quote-argv {argv} {
          567  +	set args {}
          568  +	foreach arg $argv {
          569  +		lappend args [quote-if-needed $arg]
          570  +	}
          571  +	join $args
          572  +}
          573  +
          574  +# @suffix suf list
          575  +#
          576  +# Takes a list and returns a new list with $suf appended
          577  +# to each element
          578  +#
          579  +## suffix .c {a b c} => {a.c b.c c.c}
          580  +#
          581  +proc suffix {suf list} {
          582  +	set result {}
          583  +	foreach p $list {
          584  +		lappend result $p$suf
          585  +	}
          586  +	return $result
          587  +}
          588  +
          589  +# @prefix pre list
          590  +#
          591  +# Takes a list and returns a new list with $pre prepended
          592  +# to each element
          593  +#
          594  +## prefix jim- {a.c b.c} => {jim-a.c jim-b.c}
          595  +#
          596  +proc prefix {pre list} {
          597  +	set result {}
          598  +	foreach p $list {
          599  +		lappend result $pre$p
          600  +	}
          601  +	return $result
          602  +}
          603  +
          604  +# @find-executable name
          605  +#
          606  +# Searches the path for an executable with the given name.
          607  +# Note that the name may include some parameters, e.g. "cc -mbig-endian",
          608  +# in which case the parameters are ignored.
          609  +# Returns 1 if found, or 0 if not.
          610  +#
          611  +proc find-executable {name} {
          612  +	# Ignore any parameters
          613  +	set name [lindex $name 0]
          614  +	if {$name eq ""} {
          615  +		# The empty string is never a valid executable
          616  +		return 0
          617  +	}
          618  +	foreach p [split-path] {
          619  +		dputs "Looking for $name in $p"
          620  +		set exec [file join $p $name]
          621  +		if {[file-isexec $exec]} {
          622  +			dputs "Found $name -> $exec"
          623  +			return 1
          624  +		}
          625  +	}
          626  +	return 0
          627  +}
          628  +
          629  +# @find-an-executable ?-required? name ...
          630  +#
          631  +# Given a list of possible executable names,
          632  +# searches for one of these on the path.
          633  +#
          634  +# Returns the name found, or "" if none found.
          635  +# If the first parameter is '-required', an error is generated
          636  +# if no executable is found.
          637  +#
          638  +proc find-an-executable {args} {
          639  +	set required 0
          640  +	if {[lindex $args 0] eq "-required"} {
          641  +		set args [lrange $args 1 end]
          642  +		incr required
          643  +	}
          644  +	foreach name $args {
          645  +		if {[find-executable $name]} {
          646  +			return $name
          647  +		}
          648  +	}
          649  +	if {$required} {
          650  +		if {[llength $args] == 1} {
          651  +			user-error "failed to find: [join $args]"
          652  +		} else {
          653  +			user-error "failed to find one of: [join $args]"
          654  +		}
          655  +	}
          656  +	return ""
          657  +}
          658  +
          659  +# @configlog msg
          660  +#
          661  +# Writes the given message to the configuration log, config.log
          662  +#
          663  +proc configlog {msg} {
          664  +	if {![info exists ::autosetup(logfh)]} {
          665  +		set ::autosetup(logfh) [open config.log w]
          666  +	}
          667  +	puts $::autosetup(logfh) $msg
          668  +}
          669  +
          670  +# @msg-checking msg
          671  +#
          672  +# Writes the message with no newline to stdout.
          673  +#
          674  +proc msg-checking {msg} {
          675  +	if {$::autosetup(msg-quiet) == 0} {
          676  +		maybe-show-timestamp
          677  +		puts -nonewline $msg
          678  +		set ::autosetup(msg-checking) 1
          679  +	}
          680  +}
          681  +
          682  +# @msg-result msg
          683  +#
          684  +# Writes the message to stdout.
          685  +#
          686  +proc msg-result {msg} {
          687  +	if {$::autosetup(msg-quiet) == 0} {
          688  +		maybe-show-timestamp
          689  +		puts $msg
          690  +		set ::autosetup(msg-checking) 0
          691  +		show-notices
          692  +	}
          693  +}
          694  +
          695  +# @msg-quiet command ...
          696  +#
          697  +# msg-quiet evaluates it's arguments as a command with output
          698  +# from msg-checking and msg-result suppressed.
          699  +#
          700  +# This is useful if a check needs to run a subcheck which isn't
          701  +# of interest to the user.
          702  +proc msg-quiet {args} {
          703  +	incr ::autosetup(msg-quiet)
          704  +	set rc [uplevel 1 $args]
          705  +	incr ::autosetup(msg-quiet) -1
          706  +	return $rc
          707  +}
          708  +
          709  +# Will be overridden by 'use misc'
          710  +proc error-stacktrace {msg} {
          711  +	return $msg
          712  +}
          713  +
          714  +proc error-location {msg} {
          715  +	return $msg
          716  +}
          717  +
          718  +##################################################################
          719  +#
          720  +# Debugging output
          721  +#
          722  +proc dputs {msg} {
          723  +	if {$::autosetup(debug)} {
          724  +		puts $msg
          725  +	}
          726  +}
          727  +
          728  +##################################################################
          729  +#
          730  +# User and system warnings and errors
          731  +#
          732  +# Usage errors such as wrong command line options
          733  +
          734  +# @user-error msg
          735  +#
          736  +# Indicate incorrect usage to the user, including if required components
          737  +# or features are not found.
          738  +# autosetup exits with a non-zero return code.
          739  +#
          740  +proc user-error {msg} {
          741  +	show-notices
          742  +	puts stderr "Error: $msg"
          743  +	puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options"
          744  +	exit 1
          745  +}
          746  +
          747  +# @user-notice msg
          748  +#
          749  +# Output the given message to stderr.
          750  +#
          751  +proc user-notice {msg} {
          752  +	lappend ::autosetup(notices) $msg
          753  +}
          754  +
          755  +# Incorrect usage in the auto.def file. Identify the location.
          756  +proc autosetup-error {msg} {
          757  +	show-notices
          758  +	puts stderr [error-location $msg]
          759  +	exit 1
          760  +}
          761  +
          762  +proc show-notices {} {
          763  +	if {$::autosetup(msg-checking)} {
          764  +		puts ""
          765  +		set ::autosetup(msg-checking) 0
          766  +	}
          767  +	flush stdout
          768  +	if {[info exists ::autosetup(notices)]} {
          769  +		puts stderr [join $::autosetup(notices) \n]
          770  +		unset ::autosetup(notices)
          771  +	}
          772  +}
          773  +
          774  +proc maybe-show-timestamp {} {
          775  +	if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} {
          776  +		puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]]
          777  +	}
          778  +}
          779  +
          780  +proc autosetup_version {} {
          781  +	return "autosetup v$::autosetup(version)"
          782  +}
          783  +
          784  +##################################################################
          785  +#
          786  +# Directory/path handling
          787  +#
          788  +
          789  +proc realdir {dir} {
          790  +	set oldpwd [pwd]
          791  +	cd $dir
          792  +	set pwd [pwd]
          793  +	cd $oldpwd
          794  +	return $pwd
          795  +}
          796  +
          797  +# Follow symlinks until we get to something which is not a symlink
          798  +proc realpath {path} {
          799  +	while {1} {
          800  +		if {[catch {
          801  +			set path [file link $path]
          802  +		}]} {
          803  +			# Not a link
          804  +			break
          805  +		}
          806  +	}
          807  +	return $path
          808  +}
          809  +
          810  +# Convert absolute path, $path into a path relative
          811  +# to the given directory (or the current dir, if not given).
          812  +#
          813  +proc relative-path {path {pwd {}}} {
          814  +	set diff 0
          815  +	set same 0
          816  +	set newf {}
          817  +	set prefix {}
          818  +	set path [file-normalize $path]
          819  +	if {$pwd eq ""} {
          820  +		set pwd [pwd]
          821  +	} else {
          822  +		set pwd [file-normalize $pwd]
          823  +	}
          824  +
          825  +	if {$path eq $pwd} {
          826  +		return .
          827  +	}
          828  +
          829  +	# Try to make the filename relative to the current dir
          830  +	foreach p [split $pwd /] f [split $path /] {
          831  +		if {$p ne $f} {
          832  +			incr diff
          833  +		} elseif {!$diff} {
          834  +			incr same
          835  +		}
          836  +		if {$diff} {
          837  +			if {$p ne ""} {
          838  +				# Add .. for sibling or parent dir
          839  +				lappend prefix ..
          840  +			}
          841  +			if {$f ne ""} {
          842  +				lappend newf $f
          843  +			}
          844  +		}
          845  +	}
          846  +	if {$same == 1 || [llength $prefix] > 3} {
          847  +		return $path
          848  +	}
          849  +
          850  +	file join [join $prefix /] [join $newf /]
          851  +}
          852  +
          853  +# Add filename as a dependency to rerun autosetup
          854  +# The name will be normalised (converted to a full path)
          855  +#
          856  +proc autosetup_add_dep {filename} {
          857  +	lappend ::autosetup(deps) [file-normalize $filename]
          858  +}
          859  +
          860  +##################################################################
          861  +#
          862  +# Library module support
          863  +#
          864  +
          865  +# @use module ...
          866  +#
          867  +# Load the given library modules.
          868  +# e.g. use cc cc-shared
          869  +#
          870  +proc use {args} {
          871  +	foreach m $args {
          872  +		if {[info exists ::libmodule($m)]} {
          873  +			continue
          874  +		}
          875  +		set ::libmodule($m) 1
          876  +		if {[info exists ::modsource($m)]} {
          877  +			uplevel #0 eval $::modsource($m)
          878  +		} else {
          879  +			set source $::autosetup(libdir)/${m}.tcl
          880  +			if {[file exists $source]} {
          881  +				uplevel #0 [list source $source]
          882  +				autosetup_add_dep $source
          883  +			} else {
          884  +				puts "Looking for $source"
          885  +				autosetup-error "use: No such module: $m"
          886  +			}
          887  +		}
          888  +	}
          889  +}
          890  +
          891  +# Initial settings
          892  +set autosetup(exe) $::argv0
          893  +set autosetup(istcl) 1
          894  +set autosetup(start) [clock millis]
          895  +set autosetup(installed) 0
          896  +set autosetup(msg-checking) 0
          897  +set autosetup(msg-quiet) 0
          898  +
          899  +# Embedded modules are inserted below here
          900  +set autosetup(installed) 1
          901  +# ----- module asciidoc-formatting -----
          902  +
          903  +set modsource(asciidoc-formatting) {
          904  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
          905  +# All rights reserved
          906  +
          907  +# Module which provides text formatting
          908  +# asciidoc format
          909  +
          910  +use formatting
          911  +
          912  +proc para {text} {
          913  +    regsub -all "\[ \t\n\]+" [string trim $text] " "
          914  +}
          915  +proc title {text} {
          916  +    underline [para $text] =
          917  +    nl
          918  +}
          919  +proc p {text} {
          920  +    puts [para $text]
          921  +    nl
          922  +}
          923  +proc code {text} {
          924  +    foreach line [parse_code_block $text] {
          925  +        puts "    $line"
          926  +    }
          927  +    nl
          928  +}
          929  +proc codelines {lines} {
          930  +    foreach line $lines {
          931  +        puts "    $line"
          932  +    }
          933  +    nl
          934  +}
          935  +proc nl {} {
          936  +    puts ""
          937  +}
          938  +proc underline {text char} {
          939  +    regexp "^(\[ \t\]*)(.*)" $text -> indent words
          940  +    puts $text
          941  +    puts $indent[string repeat $char [string length $words]]
          942  +}
          943  +proc section {text} {
          944  +    underline "[para $text]" -
          945  +    nl
          946  +}
          947  +proc subsection {text} {
          948  +    underline "$text" ~
          949  +    nl
          950  +}
          951  +proc bullet {text} {
          952  +    puts "* [para $text]"
          953  +}
          954  +proc indent {text} {
          955  +    puts " :: "
          956  +    puts [para $text]
          957  +}
          958  +proc defn {first args} {
          959  +    set sep ""
          960  +    if {$first ne ""} {
          961  +        puts "${first}::"
          962  +    } else {
          963  +        puts " :: "
          964  +    }
          965  +    set defn [string trim [join $args \n]]
          966  +    regsub -all "\n\n" $defn "\n ::\n" defn
          967  +    puts $defn
          968  +}
          969  +}
          970  +
          971  +# ----- module formatting -----
          972  +
          973  +set modsource(formatting) {
          974  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
          975  +# All rights reserved
          976  +
          977  +# Module which provides common text formatting
          978  +
          979  +# This is designed for documenation which looks like:
          980  +# code {...}
          981  +# or
          982  +# code {
          983  +#    ...
          984  +#    ...
          985  +# }
          986  +# In the second case, we need to work out the indenting
          987  +# and strip it from all lines but preserve the remaining indenting.
          988  +# Note that all lines need to be indented with the same initial
          989  +# spaces/tabs.
          990  +#
          991  +# Returns a list of lines with the indenting removed.
          992  +#
          993  +proc parse_code_block {text} {
          994  +    # If the text begins with newline, take the following text,
          995  +    # otherwise just return the original
          996  +    if {![regexp "^\n(.*)" $text -> text]} {
          997  +        return [list [string trim $text]]
          998  +    }
          999  +
         1000  +    # And trip spaces off the end
         1001  +    set text [string trimright $text]
         1002  +
         1003  +    set min 100
         1004  +    # Examine each line to determine the minimum indent
         1005  +    foreach line [split $text \n] {
         1006  +        if {$line eq ""} {
         1007  +            # Ignore empty lines for the indent calculation
         1008  +            continue
         1009  +        }
         1010  +        regexp "^(\[ \t\]*)" $line -> indent
         1011  +        set len [string length $indent]
         1012  +        if {$len < $min} {
         1013  +            set min $len
         1014  +        }
         1015  +    }
         1016  +
         1017  +    # Now make a list of lines with this indent removed
         1018  +    set lines {}
         1019  +    foreach line [split $text \n] {
         1020  +        lappend lines [string range $line $min end]
         1021  +    }
         1022  +
         1023  +    # Return the result
         1024  +    return $lines
         1025  +}
         1026  +}
         1027  +
         1028  +# ----- module getopt -----
         1029  +
         1030  +set modsource(getopt) {
         1031  +# Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/
         1032  +# All rights reserved
         1033  +
         1034  +# Simple getopt module
         1035  +
         1036  +# Parse everything out of the argv list which looks like an option
         1037  +# Knows about --enable-thing and --disable-thing as alternatives for --thing=0 or --thing=1
         1038  +# Everything which doesn't look like an option, or is after --, is left unchanged
         1039  +proc getopt {argvname} {
         1040  +	upvar $argvname argv
         1041  +	set nargv {}
         1042  +
         1043  +	for {set i 0} {$i < [llength $argv]} {incr i} {
         1044  +		set arg [lindex $argv $i]
         1045  +
         1046  +		#dputs arg=$arg
         1047  +
         1048  +		if {$arg eq "--"} {
         1049  +			# End of options
         1050  +			incr i
         1051  +			lappend nargv {*}[lrange $argv $i end]
         1052  +			break
         1053  +		}
         1054  +
         1055  +		if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} {
         1056  +			lappend opts($name) $value
         1057  +		} elseif {[regexp {^--(enable-|disable-)?([^=]*)$} $arg -> prefix name]} {
         1058  +			if {$prefix eq "disable-"} {
         1059  +				set value 0
         1060  +			} else {
         1061  +				set value 1
         1062  +			}
         1063  +			lappend opts($name) $value
         1064  +		} else {
         1065  +			lappend nargv $arg
         1066  +		}
         1067  +	}
         1068  +
         1069  +	#puts "getopt: argv=[join $argv] => [join $nargv]"
         1070  +	#parray opts
         1071  +
         1072  +	set argv $nargv
         1073  +
         1074  +	return [array get opts]
         1075  +}
         1076  +
         1077  +proc opt_val {optarrayname options {default {}}} {
         1078  +	upvar $optarrayname opts
         1079  +
         1080  +	set result {}
         1081  +
         1082  +	foreach o $options {
         1083  +		if {[info exists opts($o)]} {
         1084  +			lappend result {*}$opts($o)
         1085  +		}
         1086  +	}
         1087  +	if {[llength $result] == 0} {
         1088  +		return $default
         1089  +	}
         1090  +	return $result
         1091  +}
         1092  +
         1093  +proc opt_bool {optarrayname args} {
         1094  +	upvar $optarrayname opts
         1095  +
         1096  +	# Support the args being passed as a list
         1097  +	if {[llength $args] == 1} {
         1098  +		set args [lindex $args 0]
         1099  +	}
         1100  +
         1101  +	foreach o $args {
         1102  +		if {[info exists opts($o)]} {
         1103  +			if {"1" in $opts($o) || "yes" in $opts($o)} {
         1104  +				return 1
         1105  +			}
         1106  +		}
         1107  +	}
         1108  +	return 0
         1109  +}
         1110  +}
         1111  +
         1112  +# ----- module help -----
         1113  +
         1114  +set modsource(help) {
         1115  +# Copyright (c) 2010 WorkWare Systems http://workware.net.au/
         1116  +# All rights reserved
         1117  +
         1118  +# Module which provides usage, help and the command reference
         1119  +
         1120  +proc autosetup_help {what} {
         1121  +    use_pager
         1122  +
         1123  +    puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
         1124  +    puts "This is [autosetup_version], a build environment \"autoconfigurator\""
         1125  +    puts "See the documentation online at http://msteveb.github.com/autosetup/\n"
         1126  +
         1127  +    if {$what eq "local"} {
         1128  +        if {[file exists $::autosetup(autodef)]} {
         1129  +            # This relies on auto.def having a call to 'options'
         1130  +            # which will display options and quit
         1131  +            source $::autosetup(autodef)
         1132  +        } else {
         1133  +            options-show
         1134  +        }
         1135  +    } else {
         1136  +        incr ::autosetup(showhelp)
         1137  +        if {[catch {use $what}]} {
         1138  +            user-error "Unknown module: $what"
         1139  +        } else {
         1140  +            options-show
         1141  +        }
         1142  +    }
         1143  +    exit 0
         1144  +}
         1145  +
         1146  +# If not already paged and stdout is a tty, pipe the output through the pager
         1147  +# This is done by reinvoking autosetup with --nopager added
         1148  +proc use_pager {} {
         1149  +    if {![opt-bool nopager] && [getenv PAGER ""] ne "" && ![string match "not a tty" [exec tty]]} {
         1150  +        catch {
         1151  +            exec [info nameofexecutable] $::argv0 --nopager {*}$::argv | [getenv PAGER] >@stdout <@stdin 2>/dev/null
         1152  +        }
         1153  +        exit 0
         1154  +    }
         1155  +}
         1156  +
         1157  +# Outputs the autosetup references in one of several formats
         1158  +proc autosetup_reference {{type text}} {
         1159  +
         1160  +    use_pager
         1161  +
         1162  +    switch -glob -- $type {
         1163  +        wiki {use wiki-formatting}
         1164  +        ascii* {use asciidoc-formatting}
         1165  +        md - markdown {use markdown-formatting}
         1166  +        default {use text-formatting}
         1167  +    }
         1168  +
         1169  +    title "[autosetup_version] -- Command Reference"
         1170  +
         1171  +    section {Introduction}
         1172  +
         1173  +    p {
         1174  +        See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup'
         1175  +    }
         1176  +
         1177  +    p {
         1178  +        'autosetup' provides a number of built-in commands which
         1179  +        are documented below. These may be used from 'auto.def' to test
         1180  +        for features, define variables, create files from templates and
         1181  +        other similar actions.
         1182  +    }
         1183  +
         1184  +    automf_command_reference
         1185  +
         1186  +    exit 0
         1187  +}
         1188  +
         1189  +proc autosetup_output_block {type lines} {
         1190  +    if {[llength $lines]} {
         1191  +        switch $type {
         1192  +            code {
         1193  +                codelines $lines
         1194  +            }
         1195  +            p {
         1196  +                p [join $lines]
         1197  +            }
         1198  +            list {
         1199  +                foreach line $lines {
         1200  +                    bullet $line
         1201  +                }
         1202  +                nl
         1203  +            }
         1204  +        }
         1205  +    }
         1206  +}
         1207  +
         1208  +# Generate a command reference from inline documentation
         1209  +proc automf_command_reference {} {
         1210  +    lappend files $::autosetup(prog)
         1211  +    lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]]
         1212  +
         1213  +    section "Core Commands"
         1214  +    set type p
         1215  +    set lines {}
         1216  +    set cmd {}
         1217  +
         1218  +    foreach file $files {
         1219  +        set f [open $file]
         1220  +        while {![eof $f]} {
         1221  +            set line [gets $f]
         1222  +
         1223  +            # Find lines starting with "# @*" and continuing through the remaining comment lines
         1224  +            if {![regexp {^# @(.*)} $line -> cmd]} {
         1225  +                continue
         1226  +            }
         1227  +
         1228  +            # Synopsis or command?
         1229  +            if {$cmd eq "synopsis:"} {
         1230  +                section "Module: [file rootname [file tail $file]]"
         1231  +            } else {
         1232  +                subsection $cmd
         1233  +            }
         1234  +
         1235  +            set lines {}
         1236  +            set type p
         1237  +
         1238  +            # Now the description
         1239  +            while {![eof $f]} {
         1240  +                set line [gets $f]
         1241  +
         1242  +                if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} {
         1243  +                    break
         1244  +                }
         1245  +                if {$hash eq "#"} {
         1246  +                    set t code
         1247  +                } elseif {[regexp {^- (.*)} $cmd -> cmd]} {
         1248  +                    set t list
         1249  +                } else {
         1250  +                    set t p
         1251  +                }
         1252  +
         1253  +                #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd"
         1254  +
         1255  +                if {$t ne $type || $cmd eq ""} {
         1256  +                    # Finish the current block
         1257  +                    autosetup_output_block $type $lines
         1258  +                    set lines {}
         1259  +                    set type $t
         1260  +                }
         1261  +                if {$cmd ne ""} {
         1262  +                    lappend lines $cmd
         1263  +                }
         1264  +            }
         1265  +
         1266  +            autosetup_output_block $type $lines
         1267  +        }
         1268  +        close $f
         1269  +    }
         1270  +}
         1271  +}
         1272  +
         1273  +# ----- module init -----
         1274  +
         1275  +set modsource(init) {
         1276  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
         1277  +# All rights reserved
         1278  +
         1279  +# Module to help create auto.def and configure
         1280  +
         1281  +proc autosetup_init {} {
         1282  +	set create_configure 1
         1283  +	if {[file exists configure]} {
         1284  +		if {!$::autosetup(force)} {
         1285  +			# Could this be an autosetup configure?
         1286  +			if {![string match "*\nWRAPPER=*" [readfile configure]]} {
         1287  +				puts "I see configure, but not created by autosetup, so I won't overwrite it."
         1288  +				puts "Use autosetup --init --force to overwrite."
         1289  +				set create_configure 0
         1290  +			}
         1291  +		} else {
         1292  +			puts "I will overwrite the existing configure because you used --force."
         1293  +		}
         1294  +	} else {
         1295  +		puts "I don't see configure, so I will create it."
         1296  +	}
         1297  +	if {$create_configure} {
         1298  +		if {!$::autosetup(installed)} {
         1299  +			user-notice "Warning: Initialising from the development version of autosetup"
         1300  +
         1301  +			writefile configure "#!/bin/sh\nWRAPPER=\"\$0\"; export WRAPPER; exec $::autosetup(dir)/autosetup \"\$@\"\n"
         1302  +		} else {
         1303  +			writefile configure \
         1304  +{#!/bin/sh
         1305  +dir="`dirname "$0"`/autosetup"
         1306  +WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"
         1307  +}
         1308  +		}
         1309  +		catch {exec chmod 755 configure}
         1310  +	}
         1311  +	if {![file exists auto.def]} {
         1312  +		puts "I don't see auto.def, so I will create a default one."
         1313  +		writefile auto.def {# Initial auto.def created by 'autosetup --init'
         1314  +
         1315  +use cc
         1316  +
         1317  +# Add any user options here
         1318  +options {
         1319  +}
         1320  +
         1321  +make-config-header config.h
         1322  +make-template Makefile.in
         1323  +}
         1324  +	}
         1325  +	if {![file exists Makefile.in]} {
         1326  +		puts "Note: I don't see Makefile.in. You will probably need to create one."
         1327  +	}
         1328  +
         1329  +	exit 0
         1330  +}
         1331  +}
         1332  +
         1333  +# ----- module install -----
         1334  +
         1335  +set modsource(install) {
         1336  +# Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/
         1337  +# All rights reserved
         1338  +
         1339  +# Module which can install autosetup
         1340  +
         1341  +proc autosetup_install {dir} {
         1342  +	if {[catch {
         1343  +		cd $dir
         1344  +		file mkdir autosetup
         1345  +
         1346  +		set f [open autosetup/autosetup w]
         1347  +
         1348  +		set publicmodules {}
         1349  +
         1350  +		# First the main script, but only up until "CUT HERE"
         1351  +		set in [open $::autosetup(dir)/autosetup]
         1352  +		while {[gets $in buf] >= 0} {
         1353  +			if {$buf ne "##-- CUT HERE --##"} {
         1354  +				puts $f $buf
         1355  +				continue
         1356  +			}
         1357  +
         1358  +			# Insert the static modules here
         1359  +			# i.e. those which don't contain @synopsis:
         1360  +			puts $f "set autosetup(installed) 1"
         1361  +			foreach file [lsort [glob $::autosetup(libdir)/*.tcl]] {
         1362  +				set buf [readfile $file]
         1363  +				if {[string match "*\n# @synopsis:*" $buf]} {
         1364  +					lappend publicmodules $file
         1365  +					continue
         1366  +				}
         1367  +				set modname [file rootname [file tail $file]]
         1368  +				puts $f "# ----- module $modname -----"
         1369  +				puts $f "\nset modsource($modname) \{"
         1370  +				puts $f $buf
         1371  +				puts $f "\}\n"
         1372  +			}
         1373  +		}
         1374  +		close $in
         1375  +		close $f
         1376  +		exec chmod 755 autosetup/autosetup
         1377  +
         1378  +		# Install public modules
         1379  +		foreach file $publicmodules {
         1380  +			autosetup_install_file $file autosetup
         1381  +		}
         1382  +
         1383  +		# Install support files
         1384  +		foreach file {config.guess config.sub jimsh0.c find-tclsh test-tclsh LICENSE} {
         1385  +			autosetup_install_file $::autosetup(dir)/$file autosetup
         1386  +		}
         1387  +		exec chmod 755 autosetup/config.sub autosetup/config.guess autosetup/find-tclsh
         1388  +
         1389  +		writefile autosetup/README.autosetup \
         1390  +			"This is [autosetup_version]. See http://msteveb.github.com/autosetup/\n"
         1391  +
         1392  +	} error]} {
         1393  +		user-error "Failed to install autosetup: $error"
         1394  +	}
         1395  +	puts "Installed [autosetup_version] to autosetup/"
         1396  +	catch {exec [info nameofexecutable] autosetup/autosetup --init >@stdout 2>@stderr}
         1397  +
         1398  +	exit 0
         1399  +}
         1400  +
         1401  +# Append the contents of $file to filehandle $f
         1402  +proc autosetup_install_append {f file} {
         1403  +	set in [open $file]
         1404  +	puts $f [read $in]
         1405  +	close $in
         1406  +}
         1407  +
         1408  +proc autosetup_install_file {file dir} {
         1409  +	if {![file exists $file]} {
         1410  +		error "Missing installation file '$file'"
         1411  +	}
         1412  +	writefile [file join $dir [file tail $file]] [readfile $file]\n
         1413  +}
         1414  +
         1415  +if {$::autosetup(installed)} {
         1416  +	user-error "autosetup can only be installed from development source, not from installed copy"
         1417  +}
         1418  +}
         1419  +
         1420  +# ----- module markdown-formatting -----
         1421  +
         1422  +set modsource(markdown-formatting) {
         1423  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
         1424  +# All rights reserved
         1425  +
         1426  +# Module which provides text formatting
         1427  +# markdown format (kramdown syntax)
         1428  +
         1429  +use formatting
         1430  +
         1431  +proc para {text} {
         1432  +    regsub -all "\[ \t\n\]+" [string trim $text] " " text
         1433  +    regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text
         1434  +    regsub -all {^'([^']*)'} $text {**`\1`**} text
         1435  +    regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text
         1436  +    return $text
         1437  +}
         1438  +proc title {text} {
         1439  +    underline [para $text] =
         1440  +    nl
         1441  +}
         1442  +proc p {text} {
         1443  +    puts [para $text]
         1444  +    nl
         1445  +}
         1446  +proc codelines {lines} {
         1447  +    puts "~~~~~~~~~~~~"
         1448  +    foreach line $lines {
         1449  +        puts $line
         1450  +    }
         1451  +    puts "~~~~~~~~~~~~"
         1452  +    nl
         1453  +}
         1454  +proc code {text} {
         1455  +    puts "~~~~~~~~~~~~"
         1456  +    foreach line [parse_code_block $text] {
         1457  +        puts $line
         1458  +    }
         1459  +    puts "~~~~~~~~~~~~"
         1460  +    nl
         1461  +}
         1462  +proc nl {} {
         1463  +    puts ""
         1464  +}
         1465  +proc underline {text char} {
         1466  +    regexp "^(\[ \t\]*)(.*)" $text -> indent words
         1467  +    puts $text
         1468  +    puts $indent[string repeat $char [string length $words]]
         1469  +}
         1470  +proc section {text} {
         1471  +    underline "[para $text]" -
         1472  +    nl
         1473  +}
         1474  +proc subsection {text} {
         1475  +    puts "### `$text`"
         1476  +    nl
         1477  +}
         1478  +proc bullet {text} {
         1479  +    puts "* [para $text]"
         1480  +}
         1481  +proc defn {first args} {
         1482  +    puts "^"
         1483  +    set defn [string trim [join $args \n]]
         1484  +    if {$first ne ""} {
         1485  +        puts "**${first}**"
         1486  +        puts -nonewline ": "
         1487  +        regsub -all "\n\n" $defn "\n: " defn
         1488  +    }
         1489  +    puts "$defn"
         1490  +}
         1491  +}
         1492  +
         1493  +# ----- module misc -----
         1494  +
         1495  +set modsource(misc) {
         1496  +# Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/
         1497  +# All rights reserved
         1498  +
         1499  +# Module containing misc procs useful to modules
         1500  +# Largely for platform compatibility
         1501  +
         1502  +set autosetup(istcl) [info exists ::tcl_library]
         1503  +set autosetup(iswin) [string equal windows $tcl_platform(platform)]
         1504  +
         1505  +if {$autosetup(iswin)} {
         1506  +	# mingw/windows separates $PATH with semicolons
         1507  +	# and doesn't have an executable bit
         1508  +	proc split-path {} {
         1509  +		split [getenv PATH .] {;}
         1510  +	}
         1511  +	proc file-isexec {exec} {
         1512  +		# Basic test for windows. We ignore .bat
         1513  +		if {[file isfile $exec] || [file isfile $exec.exe]} {
         1514  +			return 1
         1515  +		}
         1516  +		return 0
         1517  +	}
         1518  +} else {
         1519  +	# unix separates $PATH with colons and has and executable bit
         1520  +	proc split-path {} {
         1521  +		split [getenv PATH .] :
         1522  +	}
         1523  +	proc file-isexec {exec} {
         1524  +		file executable $exec
         1525  +	}
         1526  +}
         1527  +
         1528  +# Assume that exec can return stdout and stderr
         1529  +proc exec-with-stderr {args} {
         1530  +	exec {*}$args 2>@1
         1531  +}
         1532  +
         1533  +if {$autosetup(istcl)} {
         1534  +	# Tcl doesn't have the env command
         1535  +	proc getenv {name args} {
         1536  +		if {[info exists ::env($name)]} {
         1537  +			return $::env($name)
         1538  +		}
         1539  +		if {[llength $args]} {
         1540  +			return [lindex $args 0]
         1541  +		}
         1542  +		return -code error "environment variable \"$name\" does not exist"
         1543  +	}
         1544  +} elseif {$autosetup(iswin)} {
         1545  +	# On Windows, backslash convert all environment variables
         1546  +	# (Assume that Tcl does this for us)
         1547  +	proc getenv {name args} {
         1548  +		string map {\\ /} [env $name {*}$args]
         1549  +	}
         1550  +} else {
         1551  +	# Jim on unix is simple
         1552  +	alias getenv env
         1553  +}
         1554  +
         1555  +# In case 'file normalize' doesn't exist
         1556  +#
         1557  +proc file-normalize {path} {
         1558  +	if {[catch {file normalize $path} result]} {
         1559  +		if {$path eq ""} {
         1560  +			return ""
         1561  +		}
         1562  +		set oldpwd [pwd]
         1563  +		if {[file isdir $path]} {
         1564  +			cd $path
         1565  +			set result [pwd]
         1566  +		} else {
         1567  +			cd [file dirname $path]
         1568  +			set result [file join [pwd] [file tail $path]]
         1569  +		}
         1570  +		cd $oldpwd
         1571  +	}
         1572  +	return $result
         1573  +}
         1574  +
         1575  +# If everything is working properly, the only errors which occur
         1576  +# should be generated in user code (e.g. auto.def).
         1577  +# By default, we only want to show the error location in user code.
         1578  +# We use [info frame] to achieve this, but it works differently on Tcl and Jim.
         1579  +#
         1580  +# This is designed to be called for incorrect usage in auto.def, via autosetup-error
         1581  +#
         1582  +proc error-location {msg} {
         1583  +	if {$::autosetup(debug)} {
         1584  +		return -code error $msg
         1585  +	}
         1586  +	# Search back through the stack trace for the first error in a .def file
         1587  +	for {set i 1} {$i < [info level]} {incr i} {
         1588  +		if {$::autosetup(istcl)} {
         1589  +			array set info [info frame -$i]
         1590  +		} else {
         1591  +			lassign [info frame -$i] info(caller) info(file) info(line)
         1592  +		}
         1593  +		if {[string match *.def $info(file)]} {
         1594  +			return "[relative-path $info(file)]:$info(line): Error: $msg"
         1595  +		}
         1596  +		#puts "Skipping $info(file):$info(line)"
         1597  +	}
         1598  +	return $msg
         1599  +}
         1600  +
         1601  +# Similar to error-location, but called when user code generates an error
         1602  +# In this case we want to show the stack trace in user code, but not in autosetup code
         1603  +# (unless --debug is enabled)
         1604  +#
         1605  +proc error-stacktrace {msg} {
         1606  +	if {$::autosetup(istcl)} {
         1607  +		if {[regexp {file "([^ ]*)" line ([0-9]*)} $::errorInfo dummy file line]} {
         1608  +			return "[relative-path $file]:$line $msg\n$::errorInfo"
         1609  +		}
         1610  +		return $::errorInfo
         1611  +	} else {
         1612  +		# Prepend a live stacktrace to the error stacktrace, omitting the current level
         1613  +		set stacktrace [concat [info stacktrace] [lrange [stacktrace] 3 end]]
         1614  +
         1615  +		if {!$::autosetup(debug)} {
         1616  +			# Omit any levels from autosetup or with no file
         1617  +			set newstacktrace {}
         1618  +			foreach {p f l} $stacktrace {
         1619  +				if {[string match "*autosetup" $f] || $f eq ""} {
         1620  +					#puts "Skipping $p $f:$l"
         1621  +					continue
         1622  +				}
         1623  +				lappend newstacktrace $p $f $l
         1624  +			}
         1625  +			set stacktrace $newstacktrace
         1626  +		}
         1627  +
         1628  +		# Convert filenames to relative paths
         1629  +		set newstacktrace {}
         1630  +		foreach {p f l} $stacktrace {
         1631  +			lappend newstacktrace $p [relative-path $f] $l
         1632  +		}
         1633  +		lassign $newstacktrace p f l
         1634  +		if {$f ne ""} {
         1635  +			set prefix "$f:$l: "
         1636  +		} else {
         1637  +			set prefix ""
         1638  +		}
         1639  +
         1640  +		return "${prefix}Error: $msg\n[stackdump $newstacktrace]"
         1641  +	}
         1642  +}
         1643  +}
         1644  +
         1645  +# ----- module text-formatting -----
         1646  +
         1647  +set modsource(text-formatting) {
         1648  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
         1649  +# All rights reserved
         1650  +
         1651  +# Module which provides text formatting
         1652  +
         1653  +use formatting
         1654  +
         1655  +proc wordwrap {text length {firstprefix ""} {nextprefix ""}} {
         1656  +    set len 0
         1657  +    set space $firstprefix
         1658  +    foreach word [split $text] {
         1659  +        set word [string trim $word]
         1660  +        if {$word == ""} {
         1661  +            continue
         1662  +        }
         1663  +        if {$len && [string length $space$word] + $len >= $length} {
         1664  +            puts ""
         1665  +            set len 0
         1666  +            set space $nextprefix
         1667  +        }
         1668  +        incr len [string length $space$word]
         1669  +
         1670  +        # Use man-page conventions for highlighting 'quoted' and *quoted*
         1671  +        # single words.
         1672  +        # Use x^Hx for *bold* and _^Hx for 'underline'.
         1673  +        #
         1674  +        # less and more will both understand this.
         1675  +        # Pipe through 'col -b' to remove them.
         1676  +        if {[regexp {^'(.*)'([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
         1677  +            regsub -all . $bareword "_\b&" word
         1678  +            append word $dot
         1679  +        } elseif {[regexp {^[*](.*)[*]([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
         1680  +            regsub -all . $bareword "&\b&" word
         1681  +            append word $dot
         1682  +        }
         1683  +        puts -nonewline $space$word
         1684  +        set space " "
         1685  +    }
         1686  +    if {$len} {
         1687  +        puts ""
         1688  +    }
         1689  +}
         1690  +proc title {text} {
         1691  +    underline [string trim $text] =
         1692  +    nl
         1693  +}
         1694  +proc p {text} {
         1695  +    wordwrap $text 80
         1696  +    nl
         1697  +}
         1698  +proc codelines {lines} {
         1699  +    foreach line $lines {
         1700  +        puts "    $line"
         1701  +    }
         1702  +    nl
         1703  +}
         1704  +proc nl {} {
         1705  +    puts ""
         1706  +}
         1707  +proc underline {text char} {
         1708  +    regexp "^(\[ \t\]*)(.*)" $text -> indent words
         1709  +    puts $text
         1710  +    puts $indent[string repeat $char [string length $words]]
         1711  +}
         1712  +proc section {text} {
         1713  +    underline "[string trim $text]" -
         1714  +    nl
         1715  +}
         1716  +proc subsection {text} {
         1717  +    underline "$text" ~
         1718  +    nl
         1719  +}
         1720  +proc bullet {text} {
         1721  +    wordwrap $text 76 "  * " "    "
         1722  +}
         1723  +proc indent {text} {
         1724  +    wordwrap $text 76 "    " "    "
         1725  +}
         1726  +proc defn {first args} {
         1727  +    if {$first ne ""} {
         1728  +        underline "    $first" ~
         1729  +    }
         1730  +    foreach p $args {
         1731  +        if {$p ne ""} {
         1732  +            indent $p
         1733  +        }
         1734  +    }
         1735  +}
         1736  +}
         1737  +
         1738  +# ----- module wiki-formatting -----
         1739  +
         1740  +set modsource(wiki-formatting) {
         1741  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
         1742  +# All rights reserved
         1743  +
         1744  +# Module which provides text formatting
         1745  +# wiki.tcl.tk format output
         1746  +
         1747  +use formatting
         1748  +
         1749  +proc joinlines {text} {
         1750  +    set lines {}
         1751  +    foreach l [split [string trim $text] \n] {
         1752  +        lappend lines [string trim $l]
         1753  +    }
         1754  +    join $lines
         1755  +}
         1756  +proc p {text} {
         1757  +    puts [joinlines $text]
         1758  +    puts ""
         1759  +}
         1760  +proc title {text} {
         1761  +    puts "*** [joinlines $text] ***"
         1762  +    puts ""
         1763  +}
         1764  +proc codelines {lines} {
         1765  +    puts "======"
         1766  +    foreach line $lines {
         1767  +        puts "    $line"
         1768  +    }
         1769  +    puts "======"
         1770  +}
         1771  +proc code {text} {
         1772  +    puts "======"
         1773  +    foreach line [parse_code_block $text] {
         1774  +        puts "    $line"
         1775  +    }
         1776  +    puts "======"
         1777  +}
         1778  +proc nl {} {
         1779  +}
         1780  +proc section {text} {
         1781  +    puts "'''$text'''"
         1782  +    puts ""
         1783  +}
         1784  +proc subsection {text} {
         1785  +    puts "''$text''"
         1786  +    puts ""
         1787  +}
         1788  +proc bullet {text} {
         1789  +    puts "   * [joinlines $text]"
         1790  +}
         1791  +proc indent {text} {
         1792  +    puts "    :    [joinlines $text]"
         1793  +}
         1794  +proc defn {first args} {
         1795  +    if {$first ne ""} {
         1796  +        indent '''$first'''
         1797  +    }
         1798  +
         1799  +    foreach p $args {
         1800  +        p $p
         1801  +    }
         1802  +}
         1803  +}
         1804  +
         1805  +
         1806  +##################################################################
         1807  +#
         1808  +# Entry/Exit
         1809  +#
         1810  +if {$autosetup(debug)} {
         1811  +	main $argv
         1812  +}
         1813  +if {[catch {main $argv} msg] == 1} {
         1814  +	show-notices
         1815  +	puts stderr [error-stacktrace $msg]
         1816  +	if {!$autosetup(debug) && !$autosetup(istcl)} {
         1817  +		puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace"
         1818  +	}
         1819  +	exit 1
         1820  +}

Added autosetup/cc-db.tcl.

            1  +# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
            2  +# All rights reserved
            3  +
            4  +# @synopsis:
            5  +#
            6  +# The 'cc-db' module provides a knowledge based of system idiosyncracies
            7  +# In general, this module can always be included
            8  +
            9  +use cc
           10  +
           11  +module-options {}
           12  +
           13  +# openbsd needs sys/types.h to detect some system headers
           14  +cc-include-needs sys/socket.h sys/types.h
           15  +cc-include-needs netinet/in.h sys/types.h

Added autosetup/cc-lib.tcl.

            1  +# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
            2  +# All rights reserved
            3  +
            4  +# @synopsis:
            5  +#
            6  +# Provides a library of common tests on top of the 'cc' module.
            7  +
            8  +use cc
            9  +
           10  +module-options {}
           11  +
           12  +# @cc-check-lfs
           13  +#
           14  +# The equivalent of the AC_SYS_LARGEFILE macro
           15  +# 
           16  +# defines 'HAVE_LFS' if LFS is available,
           17  +# and defines '_FILE_OFFSET_BITS=64' if necessary
           18  +#
           19  +# Returns 1 if 'LFS' is available or 0 otherwise
           20  +#
           21  +proc cc-check-lfs {} {
           22  +	cc-check-includes sys/types.h
           23  +	msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..."
           24  +	set lfs 1
           25  +	if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} {
           26  +		msg-result no
           27  +	} elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} {
           28  +		define _FILE_OFFSET_BITS 64
           29  +		msg-result yes
           30  +	} else {
           31  +		set lfs 0
           32  +		msg-result none
           33  +	}
           34  +	define-feature lfs $lfs
           35  +	return $lfs
           36  +}
           37  +
           38  +# @cc-check-endian
           39  +#
           40  +# The equivalent of the AC_C_BIGENDIAN macro
           41  +# 
           42  +# defines 'HAVE_BIG_ENDIAN' if endian is known to be big,
           43  +# or 'HAVE_LITTLE_ENDIAN' if endian is known to be little.
           44  +#
           45  +# Returns 1 if determined, or 0 if not.
           46  +#
           47  +proc cc-check-endian {} {
           48  +	cc-check-includes sys/types.h sys/param.h
           49  +	set rc 0
           50  +	msg-checking "Checking endian..."
           51  +	cc-with {-includes {sys/types.h sys/param.h}} {
           52  +		if {[cctest -code {
           53  +			#if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER)
           54  +				#error unknown
           55  +			#elif BYTE_ORDER != BIG_ENDIAN
           56  +				#error little
           57  +			#endif
           58  +		}]} {
           59  +			define-feature big-endian
           60  +			msg-result "big"
           61  +			set rc 1
           62  +		} elseif {[cctest -code {
           63  +			#if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER)
           64  +				#error unknown
           65  +			#elif BYTE_ORDER != LITTLE_ENDIAN
           66  +				#error big
           67  +			#endif
           68  +		}]} {
           69  +			define-feature little-endian
           70  +			msg-result "little"
           71  +			set rc 1
           72  +		} else {
           73  +			msg-result "unknown"
           74  +		}
           75  +	}
           76  +	return $rc
           77  +}

Added autosetup/cc-shared.tcl.

            1  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
            2  +# All rights reserved
            3  +
            4  +# @synopsis:
            5  +#
            6  +# The 'cc-shared' module provides support for shared libraries and shared objects.
            7  +# It defines the following variables:
            8  +#
            9  +## SH_CFLAGS         Flags to use compiling sources destined for a shared library
           10  +## SH_LDFLAGS        Flags to use linking a shared library
           11  +## SHOBJ_CFLAGS      Flags to use compiling sources destined for a shared object
           12  +## SHOBJ_LDFLAGS     Flags to use linking a shared object, undefined symbols allowed
           13  +## SHOBJ_LDFLAGS_R   - as above, but all symbols must be resolved
           14  +## SH_LINKFLAGS      Flags to use linking an executable which will load shared objects
           15  +## LD_LIBRARY_PATH   Environment variable which specifies path to shared libraries
           16  +
           17  +module-options {}
           18  +
           19  +foreach i {SH_LINKFLAGS SH_CFLAGS SH_LDFLAGS SHOBJ_CFLAGS SHOBJ_LDFLAGS} {
           20  +	define $i ""
           21  +}
           22  +
           23  +define LD_LIBRARY_PATH LD_LIBRARY_PATH
           24  +
           25  +switch -glob -- [get-define host] {
           26  +	*-*-darwin* {
           27  +		define SH_CFLAGS -dynamic
           28  +		define SH_LDFLAGS "-dynamiclib"
           29  +		define SHOBJ_CFLAGS "-dynamic -fno-common"
           30  +		define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup"
           31  +		define SHOBJ_LDFLAGS_R "-bundle"
           32  +		define LD_LIBRARY_PATH DYLD_LIBRARY_PATH
           33  +	}
           34  +	*-*-ming* {
           35  +		define SH_LDFLAGS -shared
           36  +		define SHOBJ_LDFLAGS -shared
           37  +		define SHOBJ_LDFLAGS_R -shared
           38  +	}
           39  +	*-*-cygwin {
           40  +		define SH_LDFLAGS -shared
           41  +		define SHOBJ_LDFLAGS -shared
           42  +	}
           43  +	*-*-solaris* {
           44  +		# XXX: These haven't been fully tested. 
           45  +		#define SH_LINKFLAGS -Wl,-export-dynamic
           46  +		define SH_CFLAGS -Kpic
           47  +		define SHOBJ_CFLAGS -Kpic
           48  +		define SHOBJ_LDFLAGS "-G"
           49  +	}
           50  +	*-*-hpux {
           51  +		# XXX: These haven't been tested
           52  +		define SH_LINKFLAGS -Wl,+s
           53  +		define SH_CFLAGS +z
           54  +		define SHOBJ_CFLAGS "+O3 +z"
           55  +		define SHOBJ_LDFLAGS -b
           56  +		define LD_LIBRARY_PATH SHLIB_PATH
           57  +	}
           58  +	sparc* {
           59  +		# sparc has a very small GOT table limit, so use -fPIC
           60  +		define SH_LINKFLAGS -rdynamic
           61  +		define SH_CFLAGS -fPIC
           62  +		define SH_LDFLAGS -shared
           63  +		define SHOBJ_CFLAGS -fPIC
           64  +		define SHOBJ_LDFLAGS -shared
           65  +	}
           66  +	* {
           67  +		# Generic Unix settings
           68  +		define SH_LINKFLAGS -rdynamic
           69  +		define SH_CFLAGS -fpic
           70  +		define SH_LDFLAGS -shared
           71  +		define SHOBJ_CFLAGS -fpic
           72  +		define SHOBJ_LDFLAGS -shared
           73  +	}
           74  +}
           75  +
           76  +if {![is-defined SHOBJ_LDFLAGS_R]} {
           77  +	define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS]
           78  +}

Added autosetup/cc.tcl.

            1  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
            2  +# All rights reserved
            3  +
            4  +# @synopsis:
            5  +#
            6  +# The 'cc' module supports checking various 'features' of the C or C++
            7  +# compiler/linker environment. Common commands are cc-check-includes,
            8  +# cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template.
            9  +#
           10  +# The following environment variables are used if set:
           11  +#
           12  +## CC       - C compiler
           13  +## CXX      - C++ compiler
           14  +## CCACHE   - Set to "none" to disable automatic use of ccache
           15  +## CFLAGS   - Additional C compiler flags
           16  +## CXXFLAGS - Additional C++ compiler flags
           17  +## LDFLAGS  - Additional compiler flags during linking
           18  +## LIBS     - Additional libraries to use (for all tests)
           19  +## CROSS    - Tool prefix for cross compilation
           20  +#
           21  +# The following variables are defined from the corresponding
           22  +# environment variables if set.
           23  +#
           24  +## CPPFLAGS
           25  +## LINKFLAGS
           26  +## CC_FOR_BUILD
           27  +## LD
           28  +
           29  +use system
           30  +
           31  +module-options {}
           32  +
           33  +# Note that the return code is not meaningful
           34  +proc cc-check-something {name code} {
           35  +	uplevel 1 $code
           36  +}
           37  +
           38  +# Checks for the existence of the given function by linking
           39  +#
           40  +proc cctest_function {function} {
           41  +	cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
           42  +}
           43  +
           44  +# Checks for the existence of the given type by compiling
           45  +proc cctest_type {type} {
           46  +	cctest -code "$type _x;"
           47  +}
           48  +
           49  +# Checks for the existence of the given type/structure member.
           50  +# e.g. "struct stat.st_mtime"
           51  +proc cctest_member {struct_member} {
           52  +	lassign [split $struct_member .] struct member
           53  +	cctest -code "static $struct _s; return sizeof(_s.$member);"
           54  +}
           55  +
           56  +# Checks for the existence of the given define by compiling
           57  +#
           58  +proc cctest_define {name} {
           59  +	cctest -code "#ifndef $name\n#error not defined\n#endif"
           60  +}
           61  +
           62  +# Checks for the existence of the given name either as
           63  +# a macro (#define) or an rvalue (such as an enum)
           64  +#
           65  +proc cctest_decl {name} {
           66  +	cctest -code "#ifndef $name\n(void)$name;\n#endif"
           67  +}
           68  +
           69  +# @cc-check-sizeof type ...
           70  +#
           71  +# Checks the size of the given types (between 1 and 32, inclusive).
           72  +# Defines a variable with the size determined, or "unknown" otherwise.
           73  +# e.g. for type 'long long', defines SIZEOF_LONG_LONG.
           74  +# Returns the size of the last type.
           75  +#
           76  +proc cc-check-sizeof {args} {
           77  +	foreach type $args {
           78  +		msg-checking "Checking for sizeof $type..."
           79  +		set size unknown
           80  +		# Try the most common sizes first
           81  +		foreach i {4 8 1 2 16 32} {
           82  +			if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
           83  +				set size $i
           84  +				break
           85  +			}
           86  +		}
           87  +		msg-result $size
           88  +		set define [feature-define-name $type SIZEOF_]
           89  +		define $define $size
           90  +	}
           91  +	# Return the last result
           92  +	get-define $define
           93  +}
           94  +
           95  +# Checks for each feature in $list by using the given script.
           96  +#
           97  +# When the script is evaluated, $each is set to the feature
           98  +# being checked, and $extra is set to any additional cctest args.
           99  +#
          100  +# Returns 1 if all features were found, or 0 otherwise.
          101  +proc cc-check-some-feature {list script} {
          102  +	set ret 1
          103  +	foreach each $list {
          104  +		if {![check-feature $each $script]} {
          105  +			set ret 0
          106  +		}
          107  +	}
          108  +	return $ret
          109  +}
          110  +
          111  +# @cc-check-includes includes ...
          112  +#
          113  +# Checks that the given include files can be used
          114  +proc cc-check-includes {args} {
          115  +	cc-check-some-feature $args {
          116  +		set with {}
          117  +		if {[dict exists $::autosetup(cc-include-deps) $each]} {
          118  +			set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]]
          119  +			msg-quiet cc-check-includes $deps
          120  +			foreach i $deps {
          121  +				if {[have-feature $i]} {
          122  +					lappend with $i
          123  +				}
          124  +			}
          125  +		}
          126  +		if {[llength $with]} {
          127  +			cc-with [list -includes $with] {
          128  +				cctest -includes $each
          129  +			}
          130  +		} else {
          131  +			cctest -includes $each
          132  +		}
          133  +	}
          134  +}
          135  +
          136  +# @cc-include-needs include required
          137  +#
          138  +# Ensures that when checking for 'include', a check is first
          139  +# made for 'required', and if found, it is #included
          140  +proc cc-include-needs {file depfile} {
          141  +	dict set ::autosetup(cc-include-deps) $file $depfile 1
          142  +}
          143  +
          144  +# @cc-check-types type ...
          145  +#
          146  +# Checks that the types exist.
          147  +proc cc-check-types {args} {
          148  +	cc-check-some-feature $args {
          149  +		cctest_type $each
          150  +	}
          151  +}
          152  +
          153  +# @cc-check-defines define ...
          154  +#
          155  +# Checks that the given preprocessor symbol is defined
          156  +proc cc-check-defines {args} {
          157  +	cc-check-some-feature $args {
          158  +		cctest_define $each
          159  +	}
          160  +}
          161  +
          162  +# @cc-check-decls name ...
          163  +#
          164  +# Checks that each given name is either a preprocessor symbol or rvalue
          165  +# such as an enum. Note that the define used for a decl is HAVE_DECL_xxx
          166  +# rather than HAVE_xxx
          167  +proc cc-check-decls {args} {
          168  +	set ret 1
          169  +	foreach name $args {
          170  +		msg-checking "Checking for $name..."
          171  +		set r [cctest_decl $name]
          172  +		define-feature "decl $name" $r
          173  +		if {$r} {
          174  +			msg-result "ok"
          175  +		} else {
          176  +			msg-result "not found"
          177  +			set ret 0
          178  +		}
          179  +	}
          180  +	return $ret
          181  +}
          182  +
          183  +# @cc-check-functions function ...
          184  +#
          185  +# Checks that the given functions exist (can be linked)
          186  +proc cc-check-functions {args} {
          187  +	cc-check-some-feature $args {
          188  +		cctest_function $each
          189  +	}
          190  +}
          191  +
          192  +# @cc-check-members type.member ...
          193  +#
          194  +# Checks that the given type/structure members exist.
          195  +# A structure member is of the form "struct stat.st_mtime"
          196  +proc cc-check-members {args} {
          197  +	cc-check-some-feature $args {
          198  +		cctest_member $each
          199  +	}
          200  +}
          201  +
          202  +# @cc-check-function-in-lib function libs ?otherlibs?
          203  +#
          204  +# Checks that the given given function can be found in one of the libs.
          205  +#
          206  +# First checks for no library required, then checks each of the libraries
          207  +# in turn.
          208  +#
          209  +# If the function is found, the feature is defined and lib_$function is defined
          210  +# to -l$lib where the function was found, or "" if no library required.
          211  +# In addition, -l$lib is added to the LIBS define.
          212  +#
          213  +# If additional libraries may be needed for linking, they should be specified
          214  +# as $extralibs as "-lotherlib1 -lotherlib2".
          215  +# These libraries are not automatically added to LIBS.
          216  +#
          217  +# Returns 1 if found or 0 if not.
          218  +# 
          219  +proc cc-check-function-in-lib {function libs {otherlibs {}}} {
          220  +	msg-checking "Checking libs for $function..."
          221  +	set found 0
          222  +	cc-with [list -libs $otherlibs] {
          223  +		if {[cctest_function $function]} {
          224  +			msg-result "none needed"
          225  +			define lib_$function ""
          226  +			incr found
          227  +		} else {
          228  +			foreach lib $libs {
          229  +				cc-with [list -libs -l$lib] {
          230  +					if {[cctest_function $function]} {
          231  +						msg-result -l$lib
          232  +						define lib_$function -l$lib
          233  +						define-append LIBS -l$lib
          234  +						incr found
          235  +						break
          236  +					}
          237  +				}
          238  +			}
          239  +		}
          240  +	}
          241  +	if {$found} {
          242  +		define [feature-define-name $function]
          243  +	} else {
          244  +		msg-result "no"
          245  +	}
          246  +	return $found
          247  +}
          248  +
          249  +# @cc-check-tools tool ...
          250  +#
          251  +# Checks for existence of the given compiler tools, taking
          252  +# into account any cross compilation prefix.
          253  +#
          254  +# For example, when checking for "ar", first AR is checked on the command
          255  +# line and then in the environment. If not found, "${host}-ar" or
          256  +# simply "ar" is assumed depending upon whether cross compiling.
          257  +# The path is searched for this executable, and if found AR is defined
          258  +# to the executable name.
          259  +#
          260  +# It is an error if the executable is not found.
          261  +#
          262  +proc cc-check-tools {args} {
          263  +	foreach tool $args {
          264  +		set TOOL [string toupper $tool]
          265  +		set exe [get-env $TOOL [get-define cross]$tool]
          266  +		if {![find-executable $exe]} {
          267  +			user-error "Failed to find $exe"
          268  +		}
          269  +		define $TOOL $exe
          270  +	}
          271  +}
          272  +
          273  +# @cc-check-progs prog ...
          274  +#
          275  +# Checks for existence of the given executables on the path.
          276  +#
          277  +# For example, when checking for "grep", the path is searched for
          278  +# the executable, 'grep', and if found GREP is defined as "grep".
          279  +#
          280  +# It the executable is not found, the variable is defined as false.
          281  +# Returns 1 if all programs were found, or 0 otherwise.
          282  +#
          283  +proc cc-check-progs {args} {
          284  +	set failed 0
          285  +	foreach prog $args {
          286  +		set PROG [string toupper $prog]
          287  +		msg-checking "Checking for $prog..."
          288  +		if {![find-executable $prog]} {
          289  +			msg-result no
          290  +			define $PROG false
          291  +			incr failed
          292  +		} else {
          293  +			msg-result ok
          294  +			define $PROG $prog
          295  +		}
          296  +	}
          297  +	expr {!$failed}
          298  +}
          299  +
          300  +# Adds the given settings to $::autosetup(ccsettings) and
          301  +# returns the old settings.
          302  +#
          303  +proc cc-add-settings {settings} {
          304  +	if {[llength $settings] % 2} {
          305  +		autosetup-error "settings list is missing a value: $settings"
          306  +	}
          307  +
          308  +	set prev [cc-get-settings]
          309  +	# workaround a bug in some versions of jimsh by forcing
          310  +	# conversion of $prev to a list
          311  +	llength $prev
          312  +
          313  +	array set new $prev
          314  +
          315  +	foreach {name value} $settings {
          316  +		switch -exact -- $name {
          317  +			-cflags - -includes {
          318  +				# These are given as lists
          319  +				lappend new($name) {*}$value
          320  +			}
          321  +			-declare {
          322  +				lappend new($name) $value
          323  +			}
          324  +			-libs {
          325  +				# Note that new libraries are added before previous libraries
          326  +				set new($name) [list {*}$value {*}$new($name)]
          327  +			}
          328  +			-link - -lang {
          329  +				set new($name) $value
          330  +			}
          331  +			-source - -sourcefile - -code {
          332  +				# XXX: These probably are only valid directly from cctest
          333  +				set new($name) $value
          334  +			}
          335  +			default {
          336  +				autosetup-error "unknown cctest setting: $name"
          337  +			}
          338  +		}
          339  +	}
          340  +
          341  +	cc-store-settings [array get new]
          342  +
          343  +	return $prev
          344  +}
          345  +
          346  +proc cc-store-settings {new} {
          347  +	set ::autosetup(ccsettings) $new
          348  +}
          349  +
          350  +proc cc-get-settings {} {
          351  +	return $::autosetup(ccsettings)
          352  +}
          353  +
          354  +# Similar to cc-add-settings, but each given setting
          355  +# simply replaces the existing value.
          356  +#
          357  +# Returns the previous settings
          358  +proc cc-update-settings {args} {
          359  +	set prev [cc-get-settings]
          360  +	cc-store-settings [dict merge $prev $args]
          361  +	return $prev
          362  +}
          363  +
          364  +# @cc-with settings ?{ script }?
          365  +#
          366  +# Sets the given 'cctest' settings and then runs the tests in 'script'.
          367  +# Note that settings such as -lang replace the current setting, while
          368  +# those such as -includes are appended to the existing setting.
          369  +#
          370  +# If no script is given, the settings become the default for the remainder
          371  +# of the auto.def file.
          372  +#
          373  +## cc-with {-lang c++} {
          374  +##   # This will check with the C++ compiler
          375  +##   cc-check-types bool
          376  +##   cc-with {-includes signal.h} {
          377  +##     # This will check with the C++ compiler, signal.h and any existing includes.
          378  +##     ...
          379  +##   }
          380  +##   # back to just the C++ compiler
          381  +## }
          382  +#
          383  +# The -libs setting is special in that newer values are added *before* earlier ones.
          384  +#
          385  +## cc-with {-libs {-lc -lm}} {
          386  +##   cc-with {-libs -ldl} {
          387  +##     cctest -libs -lsocket ...
          388  +##     # libs will be in this order: -lsocket -ldl -lc -lm
          389  +##   }
          390  +## }
          391  +proc cc-with {settings args} {
          392  +	if {[llength $args] == 0} {
          393  +		cc-add-settings $settings
          394  +	} elseif {[llength $args] > 1} {
          395  +		autosetup-error "usage: cc-with settings ?script?"
          396  +	} else {
          397  +		set save [cc-add-settings $settings]
          398  +		set rc [catch {uplevel 1 [lindex $args 0]} result info]
          399  +		cc-store-settings $save
          400  +		if {$rc != 0} {
          401  +			return -code [dict get $info -code] $result
          402  +		}
          403  +		return $result
          404  +	}
          405  +}
          406  +
          407  +# @cctest ?settings?
          408  +# 
          409  +# Low level C compiler checker. Compiles and or links a small C program
          410  +# according to the arguments and returns 1 if OK, or 0 if not.
          411  +#
          412  +# Supported settings are:
          413  +#
          414  +## -cflags cflags      A list of flags to pass to the compiler
          415  +## -includes list      A list of includes, e.g. {stdlib.h stdio.h}
          416  +## -declare code       Code to declare before main()
          417  +## -link 1             Don't just compile, link too
          418  +## -lang c|c++         Use the C (default) or C++ compiler
          419  +## -libs liblist       List of libraries to link, e.g. {-ldl -lm}
          420  +## -code code          Code to compile in the body of main()
          421  +## -source code        Compile a complete program. Ignore -includes, -declare and -code
          422  +## -sourcefile file    Shorthand for -source [readfile [get-define srcdir]/$file]
          423  +#
          424  +# Unless -source or -sourcefile is specified, the C program looks like:
          425  +#
          426  +## #include <firstinclude>   /* same for remaining includes in the list */
          427  +##
          428  +## declare-code              /* any code in -declare, verbatim */
          429  +##
          430  +## int main(void) {
          431  +##   code                    /* any code in -code, verbatim */
          432  +##   return 0;
          433  +## }
          434  +#
          435  +# Any failures are recorded in 'config.log'
          436  +#
          437  +proc cctest {args} {
          438  +	set src conftest__.c
          439  +	set tmp conftest__
          440  +
          441  +	# Easiest way to merge in the settings
          442  +	cc-with $args {
          443  +		array set opts [cc-get-settings]
          444  +	}
          445  +
          446  +	if {[info exists opts(-sourcefile)]} {
          447  +		set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
          448  +	}
          449  +	if {[info exists opts(-source)]} {
          450  +		set lines $opts(-source)
          451  +	} else {
          452  +		foreach i $opts(-includes) {
          453  +			if {$opts(-code) ne "" && ![feature-checked $i]} {
          454  +				# Compiling real code with an unchecked header file
          455  +				# Quickly (and silently) check for it now
          456  +
          457  +				# Remove all -includes from settings before checking
          458  +				set saveopts [cc-update-settings -includes {}]
          459  +				msg-quiet cc-check-includes $i
          460  +				cc-store-settings $saveopts
          461  +			}
          462  +			if {$opts(-code) eq "" || [have-feature $i]} {
          463  +				lappend source "#include <$i>"
          464  +			}
          465  +		}
          466  +		lappend source {*}$opts(-declare)
          467  +		lappend source "int main(void) {"
          468  +		lappend source $opts(-code)
          469  +		lappend source "return 0;"
          470  +		lappend source "}"
          471  +
          472  +		set lines [join $source \n]
          473  +	}
          474  +
          475  +	# Build the command line
          476  +	set cmdline {}
          477  +	lappend cmdline {*}[get-define CCACHE]
          478  +	switch -exact -- $opts(-lang) {
          479  +		c++ {
          480  +			lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS]
          481  +		}
          482  +		c {
          483  +			lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
          484  +		}
          485  +		default {
          486  +			autosetup-error "cctest called with unknown language: $opts(-lang)"
          487  +		}
          488  +	}
          489  +
          490  +	if {!$opts(-link)} {
          491  +		set tmp conftest__.o
          492  +		lappend cmdline -c
          493  +	}
          494  +	lappend cmdline {*}$opts(-cflags)
          495  +
          496  +	switch -glob -- [get-define host] {
          497  +		*-*-darwin* {
          498  +			# Don't generate .dSYM directories
          499  +			lappend cmdline -gstabs
          500  +		}
          501  +	}
          502  +	lappend cmdline $src -o $tmp {*}$opts(-libs)
          503  +
          504  +	# At this point we have the complete command line and the
          505  +	# complete source to be compiled. Get the result from cache if
          506  +	# we can
          507  +	if {[info exists ::cc_cache($cmdline,$lines)]} {
          508  +		msg-checking "(cached) "
          509  +		set ok $::cc_cache($cmdline,$lines)
          510  +		if {$::autosetup(debug)} {
          511  +			configlog "From cache (ok=$ok): [join $cmdline]"
          512  +			configlog "============"
          513  +			configlog $lines
          514  +			configlog "============"
          515  +		}
          516  +		return $ok
          517  +	}
          518  +
          519  +	writefile $src $lines\n
          520  +
          521  +	set ok 1
          522  +	if {[catch {exec-with-stderr {*}$cmdline} result errinfo]} {
          523  +		configlog "Failed: [join $cmdline]"
          524  +		configlog $result
          525  +		configlog "============"
          526  +		configlog "The failed code was:"
          527  +		configlog $lines
          528  +		configlog "============"
          529  +		set ok 0
          530  +	} elseif {$::autosetup(debug)} {
          531  +		configlog "Compiled OK: [join $cmdline]"
          532  +		configlog "============"
          533  +		configlog $lines
          534  +		configlog "============"
          535  +	}
          536  +	file delete $src
          537  +	file delete $tmp
          538  +
          539  +	# cache it
          540  +	set ::cc_cache($cmdline,$lines) $ok
          541  +
          542  +	return $ok
          543  +}
          544  +
          545  +# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
          546  +#
          547  +# Deprecated - see make-config-header
          548  +proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
          549  +	user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
          550  +	make-config-header $file -auto $autopatterns -bare $barepatterns
          551  +}
          552  +
          553  +# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
          554  +#
          555  +# Examines all defined variables which match the given patterns
          556  +# and writes an include file, $file, which defines each of these.
          557  +# Variables which match '-auto' are output as follows:
          558  +# - defines which have the value "0" are ignored.
          559  +# - defines which have integer values are defined as the integer value.
          560  +# - any other value is defined as a string, e.g. "value"
          561  +# Variables which match '-bare' are defined as-is.
          562  +# Variables which match '-str' are defined as a string, e.g. "value"
          563  +# Variables which match '-none' are omitted.
          564  +#
          565  +# Note that order is important. The first pattern which matches is selected
          566  +# Default behaviour is:
          567  +#
          568  +#  -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
          569  +#
          570  +# If the file would be unchanged, it is not written.
          571  +proc make-config-header {file args} {
          572  +	set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
          573  +	file mkdir [file dirname $file]
          574  +	set lines {}
          575  +	lappend lines "#ifndef $guard"
          576  +	lappend lines "#define $guard"
          577  +
          578  +	# Add some defaults
          579  +	lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
          580  +
          581  +	foreach n [lsort [dict keys [all-defines]]] {
          582  +		set value [get-define $n]
          583  +		set type [calc-define-output-type $n $args]
          584  +		switch -exact -- $type {
          585  +			-bare {
          586  +				# Just output the value unchanged
          587  +			}
          588  +			-none {
          589  +				continue
          590  +			}
          591  +			-str {
          592  +				set value \"$value\"
          593  +			}
          594  +			-auto {
          595  +				# Automatically determine the type
          596  +				if {$value eq "0"} {
          597  +					lappend lines "/* #undef $n */"
          598  +					continue
          599  +				}
          600  +				if {![string is integer -strict $value]} {
          601  +					set value \"$value\"
          602  +				}
          603  +			}
          604  +			"" {
          605  +				continue
          606  +			}
          607  +			default {
          608  +				autosetup-error "Unknown type in make-config-header: $type"
          609  +			}
          610  +		}
          611  +		lappend lines "#define $n $value"
          612  +	}
          613  +	lappend lines "#endif"
          614  +	set buf [join $lines \n]
          615  +	write-if-changed $file $buf {
          616  +		msg-result "Created $file"
          617  +	}
          618  +}
          619  +
          620  +proc calc-define-output-type {name spec} {
          621  +	foreach {type patterns} $spec {
          622  +		foreach pattern $patterns {
          623  +			if {[string match $pattern $name]} {
          624  +				return $type
          625  +			}
          626  +		}
          627  +	}
          628  +	return ""
          629  +}
          630  +
          631  +# Initialise some values from the environment or commandline or default settings
          632  +foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} {
          633  +	lassign $i var default
          634  +	define $var [get-env $var $default]
          635  +}
          636  +
          637  +if {[env-is-set CC]} {
          638  +	# Set by the user, so don't try anything else
          639  +	set try [list [get-env CC ""]]
          640  +} else {
          641  +	# Try some reasonable options
          642  +	set try [list [get-define cross]cc [get-define cross]gcc]
          643  +}
          644  +define CC [find-an-executable {*}$try]
          645  +if {[get-define CC] eq ""} {
          646  +	user-error "Could not find a C compiler. Tried: [join $try ", "]"
          647  +}
          648  +
          649  +define CPP [get-env CPP "[get-define CC] -E"]
          650  +
          651  +# XXX: Could avoid looking for a C++ compiler until requested
          652  +# Note that if CXX isn't found, we just set it to "false". It might not be needed.
          653  +if {[env-is-set CXX]} {
          654  +	define CXX [find-an-executable -required [get-env CXX ""]]
          655  +} else {
          656  +	define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false]
          657  +}
          658  +
          659  +# CXXFLAGS default to CFLAGS if not specified
          660  +define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
          661  +
          662  +cc-check-tools ld
          663  +
          664  +# May need a CC_FOR_BUILD, so look for one
          665  +define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
          666  +
          667  +if {[get-define CC] eq ""} {
          668  +	user-error "Could not find a C compiler. Tried: [join $try ", "]"
          669  +}
          670  +
          671  +define CCACHE [find-an-executable [get-env CCACHE ccache]]
          672  +
          673  +# Initial cctest settings
          674  +cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {}}
          675  +set autosetup(cc-include-deps) {}
          676  +
          677  +msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]"
          678  +if {[get-define CXX] ne "false"} {
          679  +	msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]"
          680  +}
          681  +msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
          682  +
          683  +if {![cc-check-includes stdlib.h]} {
          684  +	user-error "Compiler does not work. See config.log"
          685  +}

Added autosetup/config.guess.

            1  +#! /bin/sh
            2  +# Attempt to guess a canonical system name.
            3  +#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
            4  +#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
            5  +#   Free Software Foundation, Inc.
            6  +
            7  +timestamp='2010-09-24'
            8  +
            9  +# This file is free software; you can redistribute it and/or modify it
           10  +# under the terms of the GNU General Public License as published by
           11  +# the Free Software Foundation; either version 2 of the License, or
           12  +# (at your option) any later version.
           13  +#
           14  +# This program is distributed in the hope that it will be useful, but
           15  +# WITHOUT ANY WARRANTY; without even the implied warranty of
           16  +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
           17  +# General Public License for more details.
           18  +#
           19  +# You should have received a copy of the GNU General Public License
           20  +# along with this program; if not, write to the Free Software
           21  +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
           22  +# 02110-1301, USA.
           23  +#
           24  +# As a special exception to the GNU General Public License, if you
           25  +# distribute this file as part of a program that contains a
           26  +# configuration script generated by Autoconf, you may include it under
           27  +# the same distribution terms that you use for the rest of that program.
           28  +
           29  +
           30  +# Originally written by Per Bothner.  Please send patches (context
           31  +# diff format) to <config-patches@gnu.org> and include a ChangeLog
           32  +# entry.
           33  +#
           34  +# This script attempts to guess a canonical system name similar to
           35  +# config.sub.  If it succeeds, it prints the system name on stdout, and
           36  +# exits with 0.  Otherwise, it exits with 1.
           37  +#
           38  +# You can get the latest version of this script from:
           39  +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
           40  +
           41  +me=`echo "$0" | sed -e 's,.*/,,'`
           42  +
           43  +usage="\
           44  +Usage: $0 [OPTION]
           45  +
           46  +Output the configuration name of the system \`$me' is run on.
           47  +
           48  +Operation modes:
           49  +  -h, --help         print this help, then exit
           50  +  -t, --time-stamp   print date of last modification, then exit
           51  +  -v, --version      print version number, then exit
           52  +
           53  +Report bugs and patches to <config-patches@gnu.org>."
           54  +
           55  +version="\
           56  +GNU config.guess ($timestamp)
           57  +
           58  +Originally written by Per Bothner.
           59  +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
           60  +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free
           61  +Software Foundation, Inc.
           62  +
           63  +This is free software; see the source for copying conditions.  There is NO
           64  +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
           65  +
           66  +help="
           67  +Try \`$me --help' for more information."
           68  +
           69  +# Parse command line
           70  +while test $# -gt 0 ; do
           71  +  case $1 in
           72  +    --time-stamp | --time* | -t )
           73  +       echo "$timestamp" ; exit ;;
           74  +    --version | -v )
           75  +       echo "$version" ; exit ;;
           76  +    --help | --h* | -h )
           77  +       echo "$usage"; exit ;;
           78  +    -- )     # Stop option processing
           79  +       shift; break ;;
           80  +    - )	# Use stdin as input.
           81  +       break ;;
           82  +    -* )
           83  +       echo "$me: invalid option $1$help" >&2
           84  +       exit 1 ;;
           85  +    * )
           86  +       break ;;
           87  +  esac
           88  +done
           89  +
           90  +if test $# != 0; then
           91  +  echo "$me: too many arguments$help" >&2
           92  +  exit 1
           93  +fi
           94  +
           95  +trap 'exit 1' HUP INT TERM
           96  +
           97  +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
           98  +# compiler to aid in system detection is discouraged as it requires
           99  +# temporary files to be created and, as you can see below, it is a
          100  +# headache to deal with in a portable fashion.
          101  +
          102  +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
          103  +# use `HOST_CC' if defined, but it is deprecated.
          104  +
          105  +# Portable tmp directory creation inspired by the Autoconf team.
          106  +
          107  +set_cc_for_build='
          108  +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
          109  +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" HUP INT PIPE TERM ;
          110  +: ${TMPDIR=/tmp} ;
          111  + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
          112  + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
          113  + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
          114  + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
          115  +dummy=$tmp/dummy ;
          116  +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
          117  +case $CC_FOR_BUILD,$HOST_CC,$CC in
          118  + ,,)    echo "int x;" > $dummy.c ;
          119  +	for c in cc gcc c89 c99 ; do
          120  +	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
          121  +	     CC_FOR_BUILD="$c"; break ;
          122  +	  fi ;
          123  +	done ;
          124  +	if test x"$CC_FOR_BUILD" = x ; then
          125  +	  CC_FOR_BUILD=no_compiler_found ;
          126  +	fi
          127  +	;;
          128  + ,,*)   CC_FOR_BUILD=$CC ;;
          129  + ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
          130  +esac ; set_cc_for_build= ;'
          131  +
          132  +# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
          133  +# (ghazi@noc.rutgers.edu 1994-08-24)
          134  +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
          135  +	PATH=$PATH:/.attbin ; export PATH
          136  +fi
          137  +
          138  +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
          139  +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
          140  +UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
          141  +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
          142  +
          143  +# Note: order is significant - the case branches are not exclusive.
          144  +
          145  +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
          146  +    *:NetBSD:*:*)
          147  +	# NetBSD (nbsd) targets should (where applicable) match one or
          148  +	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
          149  +	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
          150  +	# switched to ELF, *-*-netbsd* would select the old
          151  +	# object file format.  This provides both forward
          152  +	# compatibility and a consistent mechanism for selecting the
          153  +	# object file format.
          154  +	#
          155  +	# Note: NetBSD doesn't particularly care about the vendor
          156  +	# portion of the name.  We always set it to "unknown".
          157  +	sysctl="sysctl -n hw.machine_arch"
          158  +	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
          159  +	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
          160  +	case "${UNAME_MACHINE_ARCH}" in
          161  +	    armeb) machine=armeb-unknown ;;
          162  +	    arm*) machine=arm-unknown ;;
          163  +	    sh3el) machine=shl-unknown ;;
          164  +	    sh3eb) machine=sh-unknown ;;
          165  +	    sh5el) machine=sh5le-unknown ;;
          166  +	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
          167  +	esac
          168  +	# The Operating System including object format, if it has switched
          169  +	# to ELF recently, or will in the future.
          170  +	case "${UNAME_MACHINE_ARCH}" in
          171  +	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
          172  +		eval $set_cc_for_build
          173  +		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
          174  +			| grep -q __ELF__
          175  +		then
          176  +		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
          177  +		    # Return netbsd for either.  FIX?
          178  +		    os=netbsd
          179  +		else
          180  +		    os=netbsdelf
          181  +		fi
          182  +		;;
          183  +	    *)
          184  +	        os=netbsd
          185  +		;;
          186  +	esac
          187  +	# The OS release
          188  +	# Debian GNU/NetBSD machines have a different userland, and
          189  +	# thus, need a distinct triplet. However, they do not need
          190  +	# kernel version information, so it can be replaced with a
          191  +	# suitable tag, in the style of linux-gnu.
          192  +	case "${UNAME_VERSION}" in
          193  +	    Debian*)
          194  +		release='-gnu'
          195  +		;;
          196  +	    *)
          197  +		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
          198  +		;;
          199  +	esac
          200  +	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
          201  +	# contains redundant information, the shorter form:
          202  +	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
          203  +	echo "${machine}-${os}${release}"
          204  +	exit ;;
          205  +    *:OpenBSD:*:*)
          206  +	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
          207  +	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
          208  +	exit ;;
          209  +    *:ekkoBSD:*:*)
          210  +	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
          211  +	exit ;;
          212  +    *:SolidBSD:*:*)
          213  +	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
          214  +	exit ;;
          215  +    macppc:MirBSD:*:*)
          216  +	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
          217  +	exit ;;
          218  +    *:MirBSD:*:*)
          219  +	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
          220  +	exit ;;
          221  +    alpha:OSF1:*:*)
          222  +	case $UNAME_RELEASE in
          223  +	*4.0)
          224  +		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
          225  +		;;
          226  +	*5.*)
          227  +	        UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
          228  +		;;
          229  +	esac
          230  +	# According to Compaq, /usr/sbin/psrinfo has been available on
          231  +	# OSF/1 and Tru64 systems produced since 1995.  I hope that
          232  +	# covers most systems running today.  This code pipes the CPU
          233  +	# types through head -n 1, so we only detect the type of CPU 0.
          234  +	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
          235  +	case "$ALPHA_CPU_TYPE" in
          236  +	    "EV4 (21064)")
          237  +		UNAME_MACHINE="alpha" ;;
          238  +	    "EV4.5 (21064)")
          239  +		UNAME_MACHINE="alpha" ;;
          240  +	    "LCA4 (21066/21068)")
          241  +		UNAME_MACHINE="alpha" ;;
          242  +	    "EV5 (21164)")
          243  +		UNAME_MACHINE="alphaev5" ;;
          244  +	    "EV5.6 (21164A)")
          245  +		UNAME_MACHINE="alphaev56" ;;
          246  +	    "EV5.6 (21164PC)")
          247  +		UNAME_MACHINE="alphapca56" ;;
          248  +	    "EV5.7 (21164PC)")
          249  +		UNAME_MACHINE="alphapca57" ;;
          250  +	    "EV6 (21264)")
          251  +		UNAME_MACHINE="alphaev6" ;;
          252  +	    "EV6.7 (21264A)")
          253  +		UNAME_MACHINE="alphaev67" ;;
          254  +	    "EV6.8CB (21264C)")
          255  +		UNAME_MACHINE="alphaev68" ;;
          256  +	    "EV6.8AL (21264B)")
          257  +		UNAME_MACHINE="alphaev68" ;;
          258  +	    "EV6.8CX (21264D)")
          259  +		UNAME_MACHINE="alphaev68" ;;
          260  +	    "EV6.9A (21264/EV69A)")
          261  +		UNAME_MACHINE="alphaev69" ;;
          262  +	    "EV7 (21364)")
          263  +		UNAME_MACHINE="alphaev7" ;;
          264  +	    "EV7.9 (21364A)")
          265  +		UNAME_MACHINE="alphaev79" ;;
          266  +	esac
          267  +	# A Pn.n version is a patched version.
          268  +	# A Vn.n version is a released version.
          269  +	# A Tn.n version is a released field test version.
          270  +	# A Xn.n version is an unreleased experimental baselevel.
          271  +	# 1.2 uses "1.2" for uname -r.
          272  +	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
          273  +	exit ;;
          274  +    Alpha\ *:Windows_NT*:*)
          275  +	# How do we know it's Interix rather than the generic POSIX subsystem?
          276  +	# Should we change UNAME_MACHINE based on the output of uname instead
          277  +	# of the specific Alpha model?
          278  +	echo alpha-pc-interix
          279  +	exit ;;
          280  +    21064:Windows_NT:50:3)
          281  +	echo alpha-dec-winnt3.5
          282  +	exit ;;
          283  +    Amiga*:UNIX_System_V:4.0:*)
          284  +	echo m68k-unknown-sysv4
          285  +	exit ;;
          286  +    *:[Aa]miga[Oo][Ss]:*:*)
          287  +	echo ${UNAME_MACHINE}-unknown-amigaos
          288  +	exit ;;
          289  +    *:[Mm]orph[Oo][Ss]:*:*)
          290  +	echo ${UNAME_MACHINE}-unknown-morphos
          291  +	exit ;;
          292  +    *:OS/390:*:*)
          293  +	echo i370-ibm-openedition
          294  +	exit ;;
          295  +    *:z/VM:*:*)
          296  +	echo s390-ibm-zvmoe
          297  +	exit ;;
          298  +    *:OS400:*:*)
          299  +        echo powerpc-ibm-os400
          300  +	exit ;;
          301  +    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
          302  +	echo arm-acorn-riscix${UNAME_RELEASE}
          303  +	exit ;;
          304  +    arm:riscos:*:*|arm:RISCOS:*:*)
          305  +	echo arm-unknown-riscos
          306  +	exit ;;
          307  +    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
          308  +	echo hppa1.1-hitachi-hiuxmpp
          309  +	exit ;;
          310  +    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
          311  +	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
          312  +	if test "`(/bin/universe) 2>/dev/null`" = att ; then
          313  +		echo pyramid-pyramid-sysv3
          314  +	else
          315  +		echo pyramid-pyramid-bsd
          316  +	fi
          317  +	exit ;;
          318  +    NILE*:*:*:dcosx)
          319  +	echo pyramid-pyramid-svr4
          320  +	exit ;;
          321  +    DRS?6000:unix:4.0:6*)
          322  +	echo sparc-icl-nx6
          323  +	exit ;;
          324  +    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
          325  +	case `/usr/bin/uname -p` in
          326  +	    sparc) echo sparc-icl-nx7; exit ;;
          327  +	esac ;;
          328  +    s390x:SunOS:*:*)
          329  +	echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          330  +	exit ;;
          331  +    sun4H:SunOS:5.*:*)
          332  +	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          333  +	exit ;;
          334  +    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
          335  +	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          336  +	exit ;;
          337  +    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
          338  +	echo i386-pc-auroraux${UNAME_RELEASE}
          339  +	exit ;;
          340  +    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
          341  +	eval $set_cc_for_build
          342  +	SUN_ARCH="i386"
          343  +	# If there is a compiler, see if it is configured for 64-bit objects.
          344  +	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
          345  +	# This test works for both compilers.
          346  +	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
          347  +	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
          348  +		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
          349  +		grep IS_64BIT_ARCH >/dev/null
          350  +	    then
          351  +		SUN_ARCH="x86_64"
          352  +	    fi
          353  +	fi
          354  +	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          355  +	exit ;;
          356  +    sun4*:SunOS:6*:*)
          357  +	# According to config.sub, this is the proper way to canonicalize
          358  +	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
          359  +	# it's likely to be more like Solaris than SunOS4.
          360  +	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          361  +	exit ;;
          362  +    sun4*:SunOS:*:*)
          363  +	case "`/usr/bin/arch -k`" in
          364  +	    Series*|S4*)
          365  +		UNAME_RELEASE=`uname -v`
          366  +		;;
          367  +	esac
          368  +	# Japanese Language versions have a version number like `4.1.3-JL'.
          369  +	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
          370  +	exit ;;
          371  +    sun3*:SunOS:*:*)
          372  +	echo m68k-sun-sunos${UNAME_RELEASE}
          373  +	exit ;;
          374  +    sun*:*:4.2BSD:*)
          375  +	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
          376  +	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
          377  +	case "`/bin/arch`" in
          378  +	    sun3)
          379  +		echo m68k-sun-sunos${UNAME_RELEASE}
          380  +		;;
          381  +	    sun4)
          382  +		echo sparc-sun-sunos${UNAME_RELEASE}
          383  +		;;
          384  +	esac
          385  +	exit ;;
          386  +    aushp:SunOS:*:*)
          387  +	echo sparc-auspex-sunos${UNAME_RELEASE}
          388  +	exit ;;
          389  +    # The situation for MiNT is a little confusing.  The machine name
          390  +    # can be virtually everything (everything which is not
          391  +    # "atarist" or "atariste" at least should have a processor
          392  +    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
          393  +    # to the lowercase version "mint" (or "freemint").  Finally
          394  +    # the system name "TOS" denotes a system which is actually not
          395  +    # MiNT.  But MiNT is downward compatible to TOS, so this should
          396  +    # be no problem.
          397  +    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
          398  +        echo m68k-atari-mint${UNAME_RELEASE}
          399  +	exit ;;
          400  +    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
          401  +	echo m68k-atari-mint${UNAME_RELEASE}
          402  +        exit ;;
          403  +    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
          404  +        echo m68k-atari-mint${UNAME_RELEASE}
          405  +	exit ;;
          406  +    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
          407  +        echo m68k-milan-mint${UNAME_RELEASE}
          408  +        exit ;;
          409  +    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
          410  +        echo m68k-hades-mint${UNAME_RELEASE}
          411  +        exit ;;
          412  +    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
          413  +        echo m68k-unknown-mint${UNAME_RELEASE}
          414  +        exit ;;
          415  +    m68k:machten:*:*)
          416  +	echo m68k-apple-machten${UNAME_RELEASE}
          417  +	exit ;;
          418  +    powerpc:machten:*:*)
          419  +	echo powerpc-apple-machten${UNAME_RELEASE}
          420  +	exit ;;
          421  +    RISC*:Mach:*:*)
          422  +	echo mips-dec-mach_bsd4.3
          423  +	exit ;;
          424  +    RISC*:ULTRIX:*:*)
          425  +	echo mips-dec-ultrix${UNAME_RELEASE}
          426  +	exit ;;
          427  +    VAX*:ULTRIX*:*:*)
          428  +	echo vax-dec-ultrix${UNAME_RELEASE}
          429  +	exit ;;
          430  +    2020:CLIX:*:* | 2430:CLIX:*:*)
          431  +	echo clipper-intergraph-clix${UNAME_RELEASE}
          432  +	exit ;;
          433  +    mips:*:*:UMIPS | mips:*:*:RISCos)
          434  +	eval $set_cc_for_build
          435  +	sed 's/^	//' << EOF >$dummy.c
          436  +#ifdef __cplusplus
          437  +#include <stdio.h>  /* for printf() prototype */
          438  +	int main (int argc, char *argv[]) {
          439  +#else
          440  +	int main (argc, argv) int argc; char *argv[]; {
          441  +#endif
          442  +	#if defined (host_mips) && defined (MIPSEB)
          443  +	#if defined (SYSTYPE_SYSV)
          444  +	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
          445  +	#endif
          446  +	#if defined (SYSTYPE_SVR4)
          447  +	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
          448  +	#endif
          449  +	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
          450  +	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
          451  +	#endif
          452  +	#endif
          453  +	  exit (-1);
          454  +	}
          455  +EOF
          456  +	$CC_FOR_BUILD -o $dummy $dummy.c &&
          457  +	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
          458  +	  SYSTEM_NAME=`$dummy $dummyarg` &&
          459  +	    { echo "$SYSTEM_NAME"; exit; }
          460  +	echo mips-mips-riscos${UNAME_RELEASE}
          461  +	exit ;;
          462  +    Motorola:PowerMAX_OS:*:*)
          463  +	echo powerpc-motorola-powermax
          464  +	exit ;;
          465  +    Motorola:*:4.3:PL8-*)
          466  +	echo powerpc-harris-powermax
          467  +	exit ;;
          468  +    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
          469  +	echo powerpc-harris-powermax
          470  +	exit ;;
          471  +    Night_Hawk:Power_UNIX:*:*)
          472  +	echo powerpc-harris-powerunix
          473  +	exit ;;
          474  +    m88k:CX/UX:7*:*)
          475  +	echo m88k-harris-cxux7
          476  +	exit ;;
          477  +    m88k:*:4*:R4*)
          478  +	echo m88k-motorola-sysv4
          479  +	exit ;;
          480  +    m88k:*:3*:R3*)
          481  +	echo m88k-motorola-sysv3
          482  +	exit ;;
          483  +    AViiON:dgux:*:*)
          484  +        # DG/UX returns AViiON for all architectures
          485  +        UNAME_PROCESSOR=`/usr/bin/uname -p`
          486  +	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
          487  +	then
          488  +	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
          489  +	       [ ${TARGET_BINARY_INTERFACE}x = x ]
          490  +	    then
          491  +		echo m88k-dg-dgux${UNAME_RELEASE}
          492  +	    else
          493  +		echo m88k-dg-dguxbcs${UNAME_RELEASE}
          494  +	    fi
          495  +	else
          496  +	    echo i586-dg-dgux${UNAME_RELEASE}
          497  +	fi
          498  + 	exit ;;
          499  +    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
          500  +	echo m88k-dolphin-sysv3
          501  +	exit ;;
          502  +    M88*:*:R3*:*)
          503  +	# Delta 88k system running SVR3
          504  +	echo m88k-motorola-sysv3
          505  +	exit ;;
          506  +    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
          507  +	echo m88k-tektronix-sysv3
          508  +	exit ;;
          509  +    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
          510  +	echo m68k-tektronix-bsd
          511  +	exit ;;
          512  +    *:IRIX*:*:*)
          513  +	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
          514  +	exit ;;
          515  +    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
          516  +	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
          517  +	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
          518  +    i*86:AIX:*:*)
          519  +	echo i386-ibm-aix
          520  +	exit ;;
          521  +    ia64:AIX:*:*)
          522  +	if [ -x /usr/bin/oslevel ] ; then
          523  +		IBM_REV=`/usr/bin/oslevel`
          524  +	else
          525  +		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
          526  +	fi
          527  +	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
          528  +	exit ;;
          529  +    *:AIX:2:3)
          530  +	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
          531  +		eval $set_cc_for_build
          532  +		sed 's/^		//' << EOF >$dummy.c
          533  +		#include <sys/systemcfg.h>
          534  +
          535  +		main()
          536  +			{
          537  +			if (!__power_pc())
          538  +				exit(1);
          539  +			puts("powerpc-ibm-aix3.2.5");
          540  +			exit(0);
          541  +			}
          542  +EOF
          543  +		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
          544  +		then
          545  +			echo "$SYSTEM_NAME"
          546  +		else
          547  +			echo rs6000-ibm-aix3.2.5
          548  +		fi
          549  +	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
          550  +		echo rs6000-ibm-aix3.2.4
          551  +	else
          552  +		echo rs6000-ibm-aix3.2
          553  +	fi
          554  +	exit ;;
          555  +    *:AIX:*:[4567])
          556  +	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
          557  +	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
          558  +		IBM_ARCH=rs6000
          559  +	else
          560  +		IBM_ARCH=powerpc
          561  +	fi
          562  +	if [ -x /usr/bin/oslevel ] ; then
          563  +		IBM_REV=`/usr/bin/oslevel`
          564  +	else
          565  +		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
          566  +	fi
          567  +	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
          568  +	exit ;;
          569  +    *:AIX:*:*)
          570  +	echo rs6000-ibm-aix
          571  +	exit ;;
          572  +    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
          573  +	echo romp-ibm-bsd4.4
          574  +	exit ;;
          575  +    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
          576  +	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
          577  +	exit ;;                             # report: romp-ibm BSD 4.3
          578  +    *:BOSX:*:*)
          579  +	echo rs6000-bull-bosx
          580  +	exit ;;
          581  +    DPX/2?00:B.O.S.:*:*)
          582  +	echo m68k-bull-sysv3
          583  +	exit ;;
          584  +    9000/[34]??:4.3bsd:1.*:*)
          585  +	echo m68k-hp-bsd
          586  +	exit ;;
          587  +    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
          588  +	echo m68k-hp-bsd4.4
          589  +	exit ;;
          590  +    9000/[34678]??:HP-UX:*:*)
          591  +	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
          592  +	case "${UNAME_MACHINE}" in
          593  +	    9000/31? )            HP_ARCH=m68000 ;;
          594  +	    9000/[34]?? )         HP_ARCH=m68k ;;
          595  +	    9000/[678][0-9][0-9])
          596  +		if [ -x /usr/bin/getconf ]; then
          597  +		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
          598  +                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
          599  +                    case "${sc_cpu_version}" in
          600  +                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
          601  +                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
          602  +                      532)                      # CPU_PA_RISC2_0
          603  +                        case "${sc_kernel_bits}" in
          604  +                          32) HP_ARCH="hppa2.0n" ;;
          605  +                          64) HP_ARCH="hppa2.0w" ;;
          606  +			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
          607  +                        esac ;;
          608  +                    esac
          609  +		fi
          610  +		if [ "${HP_ARCH}" = "" ]; then
          611  +		    eval $set_cc_for_build
          612  +		    sed 's/^              //' << EOF >$dummy.c
          613  +
          614  +              #define _HPUX_SOURCE
          615  +              #include <stdlib.h>
          616  +              #include <unistd.h>
          617  +
          618  +              int main ()
          619  +              {
          620  +              #if defined(_SC_KERNEL_BITS)
          621  +                  long bits = sysconf(_SC_KERNEL_BITS);
          622  +              #endif
          623  +                  long cpu  = sysconf (_SC_CPU_VERSION);
          624  +
          625  +                  switch (cpu)
          626  +              	{
          627  +              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
          628  +              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
          629  +              	case CPU_PA_RISC2_0:
          630  +              #if defined(_SC_KERNEL_BITS)
          631  +              	    switch (bits)
          632  +              		{
          633  +              		case 64: puts ("hppa2.0w"); break;
          634  +              		case 32: puts ("hppa2.0n"); break;
          635  +              		default: puts ("hppa2.0"); break;
          636  +              		} break;
          637  +              #else  /* !defined(_SC_KERNEL_BITS) */
          638  +              	    puts ("hppa2.0"); break;
          639  +              #endif
          640  +              	default: puts ("hppa1.0"); break;
          641  +              	}
          642  +                  exit (0);
          643  +              }
          644  +EOF
          645  +		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
          646  +		    test -z "$HP_ARCH" && HP_ARCH=hppa
          647  +		fi ;;
          648  +	esac
          649  +	if [ ${HP_ARCH} = "hppa2.0w" ]
          650  +	then
          651  +	    eval $set_cc_for_build
          652  +
          653  +	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
          654  +	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
          655  +	    # generating 64-bit code.  GNU and HP use different nomenclature:
          656  +	    #
          657  +	    # $ CC_FOR_BUILD=cc ./config.guess
          658  +	    # => hppa2.0w-hp-hpux11.23
          659  +	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
          660  +	    # => hppa64-hp-hpux11.23
          661  +
          662  +	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
          663  +		grep -q __LP64__
          664  +	    then
          665  +		HP_ARCH="hppa2.0w"
          666  +	    else
          667  +		HP_ARCH="hppa64"
          668  +	    fi
          669  +	fi
          670  +	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
          671  +	exit ;;
          672  +    ia64:HP-UX:*:*)
          673  +	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
          674  +	echo ia64-hp-hpux${HPUX_REV}
          675  +	exit ;;
          676  +    3050*:HI-UX:*:*)
          677  +	eval $set_cc_for_build
          678  +	sed 's/^	//' << EOF >$dummy.c
          679  +	#include <unistd.h>
          680  +	int
          681  +	main ()
          682  +	{
          683  +	  long cpu = sysconf (_SC_CPU_VERSION);
          684  +	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
          685  +	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
          686  +	     results, however.  */
          687  +	  if (CPU_IS_PA_RISC (cpu))
          688  +	    {
          689  +	      switch (cpu)
          690  +		{
          691  +		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
          692  +		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
          693  +		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
          694  +		  default: puts ("hppa-hitachi-hiuxwe2"); break;
          695  +		}
          696  +	    }
          697  +	  else if (CPU_IS_HP_MC68K (cpu))
          698  +	    puts ("m68k-hitachi-hiuxwe2");
          699  +	  else puts ("unknown-hitachi-hiuxwe2");
          700  +	  exit (0);
          701  +	}
          702  +EOF
          703  +	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
          704  +		{ echo "$SYSTEM_NAME"; exit; }
          705  +	echo unknown-hitachi-hiuxwe2
          706  +	exit ;;
          707  +    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
          708  +	echo hppa1.1-hp-bsd
          709  +	exit ;;
          710  +    9000/8??:4.3bsd:*:*)
          711  +	echo hppa1.0-hp-bsd
          712  +	exit ;;
          713  +    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
          714  +	echo hppa1.0-hp-mpeix
          715  +	exit ;;
          716  +    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
          717  +	echo hppa1.1-hp-osf
          718  +	exit ;;
          719  +    hp8??:OSF1:*:*)
          720  +	echo hppa1.0-hp-osf
          721  +	exit ;;
          722  +    i*86:OSF1:*:*)
          723  +	if [ -x /usr/sbin/sysversion ] ; then
          724  +	    echo ${UNAME_MACHINE}-unknown-osf1mk
          725  +	else
          726  +	    echo ${UNAME_MACHINE}-unknown-osf1
          727  +	fi
          728  +	exit ;;
          729  +    parisc*:Lites*:*:*)
          730  +	echo hppa1.1-hp-lites
          731  +	exit ;;
          732  +    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
          733  +	echo c1-convex-bsd
          734  +        exit ;;
          735  +    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
          736  +	if getsysinfo -f scalar_acc
          737  +	then echo c32-convex-bsd
          738  +	else echo c2-convex-bsd
          739  +	fi
          740  +        exit ;;
          741  +    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
          742  +	echo c34-convex-bsd
          743  +        exit ;;
          744  +    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
          745  +	echo c38-convex-bsd
          746  +        exit ;;
          747  +    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
          748  +	echo c4-convex-bsd
          749  +        exit ;;
          750  +    CRAY*Y-MP:*:*:*)
          751  +	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
          752  +	exit ;;
          753  +    CRAY*[A-Z]90:*:*:*)
          754  +	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
          755  +	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
          756  +	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
          757  +	      -e 's/\.[^.]*$/.X/'
          758  +	exit ;;
          759  +    CRAY*TS:*:*:*)
          760  +	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
          761  +	exit ;;
          762  +    CRAY*T3E:*:*:*)
          763  +	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
          764  +	exit ;;
          765  +    CRAY*SV1:*:*:*)
          766  +	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
          767  +	exit ;;
          768  +    *:UNICOS/mp:*:*)
          769  +	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
          770  +	exit ;;
          771  +    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
          772  +	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
          773  +        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
          774  +        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
          775  +        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
          776  +        exit ;;
          777  +    5000:UNIX_System_V:4.*:*)
          778  +        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
          779  +        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
          780  +        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
          781  +	exit ;;
          782  +    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
          783  +	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
          784  +	exit ;;
          785  +    sparc*:BSD/OS:*:*)
          786  +	echo sparc-unknown-bsdi${UNAME_RELEASE}
          787  +	exit ;;
          788  +    *:BSD/OS:*:*)
          789  +	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
          790  +	exit ;;
          791  +    *:FreeBSD:*:*)
          792  +	case ${UNAME_MACHINE} in
          793  +	    pc98)
          794  +		echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
          795  +	    amd64)
          796  +		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
          797  +	    *)
          798  +		echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
          799  +	esac
          800  +	exit ;;
          801  +    i*:CYGWIN*:*)
          802  +	echo ${UNAME_MACHINE}-pc-cygwin
          803  +	exit ;;
          804  +    *:MINGW*:*)
          805  +	echo ${UNAME_MACHINE}-pc-mingw32
          806  +	exit ;;
          807  +    i*:windows32*:*)
          808  +    	# uname -m includes "-pc" on this system.
          809  +    	echo ${UNAME_MACHINE}-mingw32
          810  +	exit ;;
          811  +    i*:PW*:*)
          812  +	echo ${UNAME_MACHINE}-pc-pw32
          813  +	exit ;;
          814  +    *:Interix*:*)
          815  +    	case ${UNAME_MACHINE} in
          816  +	    x86)
          817  +		echo i586-pc-interix${UNAME_RELEASE}
          818  +		exit ;;
          819  +	    authenticamd | genuineintel | EM64T)
          820  +		echo x86_64-unknown-interix${UNAME_RELEASE}
          821  +		exit ;;
          822  +	    IA64)
          823  +		echo ia64-unknown-interix${UNAME_RELEASE}
          824  +		exit ;;
          825  +	esac ;;
          826  +    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
          827  +	echo i${UNAME_MACHINE}-pc-mks
          828  +	exit ;;
          829  +    8664:Windows_NT:*)
          830  +	echo x86_64-pc-mks
          831  +	exit ;;
          832  +    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
          833  +	# How do we know it's Interix rather than the generic POSIX subsystem?
          834  +	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
          835  +	# UNAME_MACHINE based on the output of uname instead of i386?
          836  +	echo i586-pc-interix
          837  +	exit ;;
          838  +    i*:UWIN*:*)
          839  +	echo ${UNAME_MACHINE}-pc-uwin
          840  +	exit ;;
          841  +    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
          842  +	echo x86_64-unknown-cygwin
          843  +	exit ;;
          844  +    p*:CYGWIN*:*)
          845  +	echo powerpcle-unknown-cygwin
          846  +	exit ;;
          847  +    prep*:SunOS:5.*:*)
          848  +	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
          849  +	exit ;;
          850  +    *:GNU:*:*)
          851  +	# the GNU system
          852  +	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
          853  +	exit ;;
          854  +    *:GNU/*:*:*)
          855  +	# other systems with GNU libc and userland
          856  +	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
          857  +	exit ;;
          858  +    i*86:Minix:*:*)
          859  +	echo ${UNAME_MACHINE}-pc-minix
          860  +	exit ;;
          861  +    alpha:Linux:*:*)
          862  +	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
          863  +	  EV5)   UNAME_MACHINE=alphaev5 ;;
          864  +	  EV56)  UNAME_MACHINE=alphaev56 ;;
          865  +	  PCA56) UNAME_MACHINE=alphapca56 ;;
          866  +	  PCA57) UNAME_MACHINE=alphapca56 ;;
          867  +	  EV6)   UNAME_MACHINE=alphaev6 ;;
          868  +	  EV67)  UNAME_MACHINE=alphaev67 ;;
          869  +	  EV68*) UNAME_MACHINE=alphaev68 ;;
          870  +        esac
          871  +	objdump --private-headers /bin/sh | grep -q ld.so.1
          872  +	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
          873  +	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
          874  +	exit ;;
          875  +    arm*:Linux:*:*)
          876  +	eval $set_cc_for_build
          877  +	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
          878  +	    | grep -q __ARM_EABI__
          879  +	then
          880  +	    echo ${UNAME_MACHINE}-unknown-linux-gnu
          881  +	else
          882  +	    echo ${UNAME_MACHINE}-unknown-linux-gnueabi
          883  +	fi
          884  +	exit ;;
          885  +    avr32*:Linux:*:*)
          886  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          887  +	exit ;;
          888  +    cris:Linux:*:*)
          889  +	echo cris-axis-linux-gnu
          890  +	exit ;;
          891  +    crisv32:Linux:*:*)
          892  +	echo crisv32-axis-linux-gnu
          893  +	exit ;;
          894  +    frv:Linux:*:*)
          895  +    	echo frv-unknown-linux-gnu
          896  +	exit ;;
          897  +    i*86:Linux:*:*)
          898  +	LIBC=gnu
          899  +	eval $set_cc_for_build
          900  +	sed 's/^	//' << EOF >$dummy.c
          901  +	#ifdef __dietlibc__
          902  +	LIBC=dietlibc
          903  +	#endif
          904  +EOF
          905  +	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
          906  +	echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
          907  +	exit ;;
          908  +    ia64:Linux:*:*)
          909  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          910  +	exit ;;
          911  +    m32r*:Linux:*:*)
          912  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          913  +	exit ;;
          914  +    m68*:Linux:*:*)
          915  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          916  +	exit ;;
          917  +    mips:Linux:*:* | mips64:Linux:*:*)
          918  +	eval $set_cc_for_build
          919  +	sed 's/^	//' << EOF >$dummy.c
          920  +	#undef CPU
          921  +	#undef ${UNAME_MACHINE}
          922  +	#undef ${UNAME_MACHINE}el
          923  +	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
          924  +	CPU=${UNAME_MACHINE}el
          925  +	#else
          926  +	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
          927  +	CPU=${UNAME_MACHINE}
          928  +	#else
          929  +	CPU=
          930  +	#endif
          931  +	#endif
          932  +EOF
          933  +	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
          934  +	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
          935  +	;;
          936  +    or32:Linux:*:*)
          937  +	echo or32-unknown-linux-gnu
          938  +	exit ;;
          939  +    padre:Linux:*:*)
          940  +	echo sparc-unknown-linux-gnu
          941  +	exit ;;
          942  +    parisc64:Linux:*:* | hppa64:Linux:*:*)
          943  +	echo hppa64-unknown-linux-gnu
          944  +	exit ;;
          945  +    parisc:Linux:*:* | hppa:Linux:*:*)
          946  +	# Look for CPU level
          947  +	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
          948  +	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
          949  +	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
          950  +	  *)    echo hppa-unknown-linux-gnu ;;
          951  +	esac
          952  +	exit ;;
          953  +    ppc64:Linux:*:*)
          954  +	echo powerpc64-unknown-linux-gnu
          955  +	exit ;;
          956  +    ppc:Linux:*:*)
          957  +	echo powerpc-unknown-linux-gnu
          958  +	exit ;;
          959  +    s390:Linux:*:* | s390x:Linux:*:*)
          960  +	echo ${UNAME_MACHINE}-ibm-linux
          961  +	exit ;;
          962  +    sh64*:Linux:*:*)
          963  +    	echo ${UNAME_MACHINE}-unknown-linux-gnu
          964  +	exit ;;
          965  +    sh*:Linux:*:*)
          966  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          967  +	exit ;;
          968  +    sparc:Linux:*:* | sparc64:Linux:*:*)
          969  +	echo ${UNAME_MACHINE}-unknown-linux-gnu
          970  +	exit ;;
          971  +    tile*:Linux:*:*)
          972  +	echo ${UNAME_MACHINE}-tilera-linux-gnu
          973  +	exit ;;
          974  +    vax:Linux:*:*)
          975  +	echo ${UNAME_MACHINE}-dec-linux-gnu
          976  +	exit ;;
          977  +    x86_64:Linux:*:*)
          978  +	echo x86_64-unknown-linux-gnu
          979  +	exit ;;
          980  +    xtensa*:Linux:*:*)
          981  +    	echo ${UNAME_MACHINE}-unknown-linux-gnu
          982  +	exit ;;
          983  +    i*86:DYNIX/ptx:4*:*)
          984  +	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
          985  +	# earlier versions are messed up and put the nodename in both
          986  +	# sysname and nodename.
          987  +	echo i386-sequent-sysv4
          988  +	exit ;;
          989  +    i*86:UNIX_SV:4.2MP:2.*)
          990  +        # Unixware is an offshoot of SVR4, but it has its own version
          991  +        # number series starting with 2...
          992  +        # I am not positive that other SVR4 systems won't match this,
          993  +	# I just have to hope.  -- rms.
          994  +        # Use sysv4.2uw... so that sysv4* matches it.
          995  +	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
          996  +	exit ;;
          997  +    i*86:OS/2:*:*)
          998  +	# If we were able to find `uname', then EMX Unix compatibility
          999  +	# is probably installed.
         1000  +	echo ${UNAME_MACHINE}-pc-os2-emx
         1001  +	exit ;;
         1002  +    i*86:XTS-300:*:STOP)
         1003  +	echo ${UNAME_MACHINE}-unknown-stop
         1004  +	exit ;;
         1005  +    i*86:atheos:*:*)
         1006  +	echo ${UNAME_MACHINE}-unknown-atheos
         1007  +	exit ;;
         1008  +    i*86:syllable:*:*)
         1009  +	echo ${UNAME_MACHINE}-pc-syllable
         1010  +	exit ;;
         1011  +    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
         1012  +	echo i386-unknown-lynxos${UNAME_RELEASE}
         1013  +	exit ;;
         1014  +    i*86:*DOS:*:*)
         1015  +	echo ${UNAME_MACHINE}-pc-msdosdjgpp
         1016  +	exit ;;
         1017  +    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
         1018  +	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
         1019  +	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
         1020  +		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
         1021  +	else
         1022  +		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
         1023  +	fi
         1024  +	exit ;;
         1025  +    i*86:*:5:[678]*)
         1026  +    	# UnixWare 7.x, OpenUNIX and OpenServer 6.
         1027  +	case `/bin/uname -X | grep "^Machine"` in
         1028  +	    *486*)	     UNAME_MACHINE=i486 ;;
         1029  +	    *Pentium)	     UNAME_MACHINE=i586 ;;
         1030  +	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
         1031  +	esac
         1032  +	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
         1033  +	exit ;;
         1034  +    i*86:*:3.2:*)
         1035  +	if test -f /usr/options/cb.name; then
         1036  +		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
         1037  +		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
         1038  +	elif /bin/uname -X 2>/dev/null >/dev/null ; then
         1039  +		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
         1040  +		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
         1041  +		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
         1042  +			&& UNAME_MACHINE=i586
         1043  +		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
         1044  +			&& UNAME_MACHINE=i686
         1045  +		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
         1046  +			&& UNAME_MACHINE=i686
         1047  +		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
         1048  +	else
         1049  +		echo ${UNAME_MACHINE}-pc-sysv32
         1050  +	fi
         1051  +	exit ;;
         1052  +    pc:*:*:*)
         1053  +	# Left here for compatibility:
         1054  +        # uname -m prints for DJGPP always 'pc', but it prints nothing about
         1055  +        # the processor, so we play safe by assuming i586.
         1056  +	# Note: whatever this is, it MUST be the same as what config.sub
         1057  +	# prints for the "djgpp" host, or else GDB configury will decide that
         1058  +	# this is a cross-build.
         1059  +	echo i586-pc-msdosdjgpp
         1060  +        exit ;;
         1061  +    Intel:Mach:3*:*)
         1062  +	echo i386-pc-mach3
         1063  +	exit ;;
         1064  +    paragon:*:*:*)
         1065  +	echo i860-intel-osf1
         1066  +	exit ;;
         1067  +    i860:*:4.*:*) # i860-SVR4
         1068  +	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
         1069  +	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
         1070  +	else # Add other i860-SVR4 vendors below as they are discovered.
         1071  +	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
         1072  +	fi
         1073  +	exit ;;
         1074  +    mini*:CTIX:SYS*5:*)
         1075  +	# "miniframe"
         1076  +	echo m68010-convergent-sysv
         1077  +	exit ;;
         1078  +    mc68k:UNIX:SYSTEM5:3.51m)
         1079  +	echo m68k-convergent-sysv
         1080  +	exit ;;
         1081  +    M680?0:D-NIX:5.3:*)
         1082  +	echo m68k-diab-dnix
         1083  +	exit ;;
         1084  +    M68*:*:R3V[5678]*:*)
         1085  +	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
         1086  +    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
         1087  +	OS_REL=''
         1088  +	test -r /etc/.relid \
         1089  +	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
         1090  +	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
         1091  +	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
         1092  +	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
         1093  +	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
         1094  +    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
         1095  +        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
         1096  +          && { echo i486-ncr-sysv4; exit; } ;;
         1097  +    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
         1098  +	OS_REL='.3'
         1099  +	test -r /etc/.relid \
         1100  +	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
         1101  +	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
         1102  +	    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
         1103  +	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
         1104  +	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
         1105  +	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
         1106  +	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
         1107  +    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
         1108  +	echo m68k-unknown-lynxos${UNAME_RELEASE}
         1109  +	exit ;;
         1110  +    mc68030:UNIX_System_V:4.*:*)
         1111  +	echo m68k-atari-sysv4
         1112  +	exit ;;
         1113  +    TSUNAMI:LynxOS:2.*:*)
         1114  +	echo sparc-unknown-lynxos${UNAME_RELEASE}
         1115  +	exit ;;
         1116  +    rs6000:LynxOS:2.*:*)
         1117  +	echo rs6000-unknown-lynxos${UNAME_RELEASE}
         1118  +	exit ;;
         1119  +    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
         1120  +	echo powerpc-unknown-lynxos${UNAME_RELEASE}
         1121  +	exit ;;
         1122  +    SM[BE]S:UNIX_SV:*:*)
         1123  +	echo mips-dde-sysv${UNAME_RELEASE}
         1124  +	exit ;;
         1125  +    RM*:ReliantUNIX-*:*:*)
         1126  +	echo mips-sni-sysv4
         1127  +	exit ;;
         1128  +    RM*:SINIX-*:*:*)
         1129  +	echo mips-sni-sysv4
         1130  +	exit ;;
         1131  +    *:SINIX-*:*:*)
         1132  +	if uname -p 2>/dev/null >/dev/null ; then
         1133  +		UNAME_MACHINE=`(uname -p) 2>/dev/null`
         1134  +		echo ${UNAME_MACHINE}-sni-sysv4
         1135  +	else
         1136  +		echo ns32k-sni-sysv
         1137  +	fi
         1138  +	exit ;;
         1139  +    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
         1140  +                      # says <Richard.M.Bartel@ccMail.Census.GOV>
         1141  +        echo i586-unisys-sysv4
         1142  +        exit ;;
         1143  +    *:UNIX_System_V:4*:FTX*)
         1144  +	# From Gerald Hewes <hewes@openmarket.com>.
         1145  +	# How about differentiating between stratus architectures? -djm
         1146  +	echo hppa1.1-stratus-sysv4
         1147  +	exit ;;
         1148  +    *:*:*:FTX*)
         1149  +	# From seanf@swdc.stratus.com.
         1150  +	echo i860-stratus-sysv4
         1151  +	exit ;;
         1152  +    i*86:VOS:*:*)
         1153  +	# From Paul.Green@stratus.com.
         1154  +	echo ${UNAME_MACHINE}-stratus-vos
         1155  +	exit ;;
         1156  +    *:VOS:*:*)
         1157  +	# From Paul.Green@stratus.com.
         1158  +	echo hppa1.1-stratus-vos
         1159  +	exit ;;
         1160  +    mc68*:A/UX:*:*)
         1161  +	echo m68k-apple-aux${UNAME_RELEASE}
         1162  +	exit ;;
         1163  +    news*:NEWS-OS:6*:*)
         1164  +	echo mips-sony-newsos6
         1165  +	exit ;;
         1166  +    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
         1167  +	if [ -d /usr/nec ]; then
         1168  +	        echo mips-nec-sysv${UNAME_RELEASE}
         1169  +	else
         1170  +	        echo mips-unknown-sysv${UNAME_RELEASE}
         1171  +	fi
         1172  +        exit ;;
         1173  +    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
         1174  +	echo powerpc-be-beos
         1175  +	exit ;;
         1176  +    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
         1177  +	echo powerpc-apple-beos
         1178  +	exit ;;
         1179  +    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
         1180  +	echo i586-pc-beos
         1181  +	exit ;;
         1182  +    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
         1183  +	echo i586-pc-haiku
         1184  +	exit ;;
         1185  +    SX-4:SUPER-UX:*:*)
         1186  +	echo sx4-nec-superux${UNAME_RELEASE}
         1187  +	exit ;;
         1188  +    SX-5:SUPER-UX:*:*)
         1189  +	echo sx5-nec-superux${UNAME_RELEASE}
         1190  +	exit ;;
         1191  +    SX-6:SUPER-UX:*:*)
         1192  +	echo sx6-nec-superux${UNAME_RELEASE}
         1193  +	exit ;;
         1194  +    SX-7:SUPER-UX:*:*)
         1195  +	echo sx7-nec-superux${UNAME_RELEASE}
         1196  +	exit ;;
         1197  +    SX-8:SUPER-UX:*:*)
         1198  +	echo sx8-nec-superux${UNAME_RELEASE}
         1199  +	exit ;;
         1200  +    SX-8R:SUPER-UX:*:*)
         1201  +	echo sx8r-nec-superux${UNAME_RELEASE}
         1202  +	exit ;;
         1203  +    Power*:Rhapsody:*:*)
         1204  +	echo powerpc-apple-rhapsody${UNAME_RELEASE}
         1205  +	exit ;;
         1206  +    *:Rhapsody:*:*)
         1207  +	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
         1208  +	exit ;;
         1209  +    *:Darwin:*:*)
         1210  +	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
         1211  +	case $UNAME_PROCESSOR in
         1212  +	    i386)
         1213  +		eval $set_cc_for_build
         1214  +		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
         1215  +		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
         1216  +		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
         1217  +		      grep IS_64BIT_ARCH >/dev/null
         1218  +		  then
         1219  +		      UNAME_PROCESSOR="x86_64"
         1220  +		  fi
         1221  +		fi ;;
         1222  +	    unknown) UNAME_PROCESSOR=powerpc ;;
         1223  +	esac
         1224  +	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
         1225  +	exit ;;
         1226  +    *:procnto*:*:* | *:QNX:[0123456789]*:*)
         1227  +	UNAME_PROCESSOR=`uname -p`
         1228  +	if test "$UNAME_PROCESSOR" = "x86"; then
         1229  +		UNAME_PROCESSOR=i386
         1230  +		UNAME_MACHINE=pc
         1231  +	fi
         1232  +	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
         1233  +	exit ;;
         1234  +    *:QNX:*:4*)
         1235  +	echo i386-pc-qnx
         1236  +	exit ;;
         1237  +    NEO-?:NONSTOP_KERNEL:*:*)
         1238  +	echo neo-tandem-nsk${UNAME_RELEASE}
         1239  +	exit ;;
         1240  +    NSE-?:NONSTOP_KERNEL:*:*)
         1241  +	echo nse-tandem-nsk${UNAME_RELEASE}
         1242  +	exit ;;
         1243  +    NSR-?:NONSTOP_KERNEL:*:*)
         1244  +	echo nsr-tandem-nsk${UNAME_RELEASE}
         1245  +	exit ;;
         1246  +    *:NonStop-UX:*:*)
         1247  +	echo mips-compaq-nonstopux
         1248  +	exit ;;
         1249  +    BS2000:POSIX*:*:*)
         1250  +	echo bs2000-siemens-sysv
         1251  +	exit ;;
         1252  +    DS/*:UNIX_System_V:*:*)
         1253  +	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
         1254  +	exit ;;
         1255  +    *:Plan9:*:*)
         1256  +	# "uname -m" is not consistent, so use $cputype instead. 386
         1257  +	# is converted to i386 for consistency with other x86
         1258  +	# operating systems.
         1259  +	if test "$cputype" = "386"; then
         1260  +	    UNAME_MACHINE=i386
         1261  +	else
         1262  +	    UNAME_MACHINE="$cputype"
         1263  +	fi
         1264  +	echo ${UNAME_MACHINE}-unknown-plan9
         1265  +	exit ;;
         1266  +    *:TOPS-10:*:*)
         1267  +	echo pdp10-unknown-tops10
         1268  +	exit ;;
         1269  +    *:TENEX:*:*)
         1270  +	echo pdp10-unknown-tenex
         1271  +	exit ;;
         1272  +    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
         1273  +	echo pdp10-dec-tops20
         1274  +	exit ;;
         1275  +    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
         1276  +	echo pdp10-xkl-tops20
         1277  +	exit ;;
         1278  +    *:TOPS-20:*:*)
         1279  +	echo pdp10-unknown-tops20
         1280  +	exit ;;
         1281  +    *:ITS:*:*)
         1282  +	echo pdp10-unknown-its
         1283  +	exit ;;
         1284  +    SEI:*:*:SEIUX)
         1285  +        echo mips-sei-seiux${UNAME_RELEASE}
         1286  +	exit ;;
         1287  +    *:DragonFly:*:*)
         1288  +	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
         1289  +	exit ;;
         1290  +    *:*VMS:*:*)
         1291  +    	UNAME_MACHINE=`(uname -p) 2>/dev/null`
         1292  +	case "${UNAME_MACHINE}" in
         1293  +	    A*) echo alpha-dec-vms ; exit ;;
         1294  +	    I*) echo ia64-dec-vms ; exit ;;
         1295  +	    V*) echo vax-dec-vms ; exit ;;
         1296  +	esac ;;
         1297  +    *:XENIX:*:SysV)
         1298  +	echo i386-pc-xenix
         1299  +	exit ;;
         1300  +    i*86:skyos:*:*)
         1301  +	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
         1302  +	exit ;;
         1303  +    i*86:rdos:*:*)
         1304  +	echo ${UNAME_MACHINE}-pc-rdos
         1305  +	exit ;;
         1306  +    i*86:AROS:*:*)
         1307  +	echo ${UNAME_MACHINE}-pc-aros
         1308  +	exit ;;
         1309  +esac
         1310  +
         1311  +#echo '(No uname command or uname output not recognized.)' 1>&2
         1312  +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
         1313  +
         1314  +eval $set_cc_for_build
         1315  +cat >$dummy.c <<EOF
         1316  +#ifdef _SEQUENT_
         1317  +# include <sys/types.h>
         1318  +# include <sys/utsname.h>
         1319  +#endif
         1320  +main ()
         1321  +{
         1322  +#if defined (sony)
         1323  +#if defined (MIPSEB)
         1324  +  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
         1325  +     I don't know....  */
         1326  +  printf ("mips-sony-bsd\n"); exit (0);
         1327  +#else
         1328  +#include <sys/param.h>
         1329  +  printf ("m68k-sony-newsos%s\n",
         1330  +#ifdef NEWSOS4
         1331  +          "4"
         1332  +#else
         1333  +	  ""
         1334  +#endif
         1335  +         ); exit (0);
         1336  +#endif
         1337  +#endif
         1338  +
         1339  +#if defined (__arm) && defined (__acorn) && defined (__unix)
         1340  +  printf ("arm-acorn-riscix\n"); exit (0);
         1341  +#endif
         1342  +
         1343  +#if defined (hp300) && !defined (hpux)
         1344  +  printf ("m68k-hp-bsd\n"); exit (0);
         1345  +#endif
         1346  +
         1347  +#if defined (NeXT)
         1348  +#if !defined (__ARCHITECTURE__)
         1349  +#define __ARCHITECTURE__ "m68k"
         1350  +#endif
         1351  +  int version;
         1352  +  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
         1353  +  if (version < 4)
         1354  +    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
         1355  +  else
         1356  +    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
         1357  +  exit (0);
         1358  +#endif
         1359  +
         1360  +#if defined (MULTIMAX) || defined (n16)
         1361  +#if defined (UMAXV)
         1362  +  printf ("ns32k-encore-sysv\n"); exit (0);
         1363  +#else
         1364  +#if defined (CMU)
         1365  +  printf ("ns32k-encore-mach\n"); exit (0);
         1366  +#else
         1367  +  printf ("ns32k-encore-bsd\n"); exit (0);
         1368  +#endif
         1369  +#endif
         1370  +#endif
         1371  +
         1372  +#if defined (__386BSD__)
         1373  +  printf ("i386-pc-bsd\n"); exit (0);
         1374  +#endif
         1375  +
         1376  +#if defined (sequent)
         1377  +#if defined (i386)
         1378  +  printf ("i386-sequent-dynix\n"); exit (0);
         1379  +#endif
         1380  +#if defined (ns32000)
         1381  +  printf ("ns32k-sequent-dynix\n"); exit (0);
         1382  +#endif
         1383  +#endif
         1384  +
         1385  +#if defined (_SEQUENT_)
         1386  +    struct utsname un;
         1387  +
         1388  +    uname(&un);
         1389  +
         1390  +    if (strncmp(un.version, "V2", 2) == 0) {
         1391  +	printf ("i386-sequent-ptx2\n"); exit (0);
         1392  +    }
         1393  +    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
         1394  +	printf ("i386-sequent-ptx1\n"); exit (0);
         1395  +    }
         1396  +    printf ("i386-sequent-ptx\n"); exit (0);
         1397  +
         1398  +#endif
         1399  +
         1400  +#if defined (vax)
         1401  +# if !defined (ultrix)
         1402  +#  include <sys/param.h>
         1403  +#  if defined (BSD)
         1404  +#   if BSD == 43
         1405  +      printf ("vax-dec-bsd4.3\n"); exit (0);
         1406  +#   else
         1407  +#    if BSD == 199006
         1408  +      printf ("vax-dec-bsd4.3reno\n"); exit (0);
         1409  +#    else
         1410  +      printf ("vax-dec-bsd\n"); exit (0);
         1411  +#    endif
         1412  +#   endif
         1413  +#  else
         1414  +    printf ("vax-dec-bsd\n"); exit (0);
         1415  +#  endif
         1416  +# else
         1417  +    printf ("vax-dec-ultrix\n"); exit (0);
         1418  +# endif
         1419  +#endif
         1420  +
         1421  +#if defined (alliant) && defined (i860)
         1422  +  printf ("i860-alliant-bsd\n"); exit (0);
         1423  +#endif
         1424  +
         1425  +  exit (1);
         1426  +}
         1427  +EOF
         1428  +
         1429  +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
         1430  +	{ echo "$SYSTEM_NAME"; exit; }
         1431  +
         1432  +# Apollos put the system type in the environment.
         1433  +
         1434  +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
         1435  +
         1436  +# Convex versions that predate uname can use getsysinfo(1)
         1437  +
         1438  +if [ -x /usr/convex/getsysinfo ]
         1439  +then
         1440  +    case `getsysinfo -f cpu_type` in
         1441  +    c1*)
         1442  +	echo c1-convex-bsd
         1443  +	exit ;;
         1444  +    c2*)
         1445  +	if getsysinfo -f scalar_acc
         1446  +	then echo c32-convex-bsd
         1447  +	else echo c2-convex-bsd
         1448  +	fi
         1449  +	exit ;;
         1450  +    c34*)
         1451  +	echo c34-convex-bsd
         1452  +	exit ;;
         1453  +    c38*)
         1454  +	echo c38-convex-bsd
         1455  +	exit ;;
         1456  +    c4*)
         1457  +	echo c4-convex-bsd
         1458  +	exit ;;
         1459  +    esac
         1460  +fi
         1461  +
         1462  +cat >&2 <<EOF
         1463  +$0: unable to guess system type
         1464  +
         1465  +This script, last modified $timestamp, has failed to recognize
         1466  +the operating system you are using. It is advised that you
         1467  +download the most up to date version of the config scripts from
         1468  +
         1469  +  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
         1470  +and
         1471  +  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
         1472  +
         1473  +If the version you run ($0) is already up to date, please
         1474  +send the following data and any information you think might be
         1475  +pertinent to <config-patches@gnu.org> in order to provide the needed
         1476  +information to handle your system.
         1477  +
         1478  +config.guess timestamp = $timestamp
         1479  +
         1480  +uname -m = `(uname -m) 2>/dev/null || echo unknown`
         1481  +uname -r = `(uname -r) 2>/dev/null || echo unknown`
         1482  +uname -s = `(uname -s) 2>/dev/null || echo unknown`
         1483  +uname -v = `(uname -v) 2>/dev/null || echo unknown`
         1484  +
         1485  +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
         1486  +/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
         1487  +
         1488  +hostinfo               = `(hostinfo) 2>/dev/null`
         1489  +/bin/universe          = `(/bin/universe) 2>/dev/null`
         1490  +/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
         1491  +/bin/arch              = `(/bin/arch) 2>/dev/null`
         1492  +/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
         1493  +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
         1494  +
         1495  +UNAME_MACHINE = ${UNAME_MACHINE}
         1496  +UNAME_RELEASE = ${UNAME_RELEASE}
         1497  +UNAME_SYSTEM  = ${UNAME_SYSTEM}
         1498  +UNAME_VERSION = ${UNAME_VERSION}
         1499  +EOF
         1500  +
         1501  +exit 1
         1502  +
         1503  +# Local variables:
         1504  +# eval: (add-hook 'write-file-hooks 'time-stamp)
         1505  +# time-stamp-start: "timestamp='"
         1506  +# time-stamp-format: "%:y-%02m-%02d"
         1507  +# time-stamp-end: "'"
         1508  +# End:

Added autosetup/config.sub.

            1  +#! /bin/sh
            2  +# Configuration validation subroutine script.
            3  +#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
            4  +#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
            5  +#   Free Software Foundation, Inc.
            6  +
            7  +timestamp='2010-09-11'
            8  +
            9  +# This file is (in principle) common to ALL GNU software.
           10  +# The presence of a machine in this file suggests that SOME GNU software
           11  +# can handle that machine.  It does not imply ALL GNU software can.
           12  +#
           13  +# This file is free software; you can redistribute it and/or modify
           14  +# it under the terms of the GNU General Public License as published by
           15  +# the Free Software Foundation; either version 2 of the License, or
           16  +# (at your option) any later version.
           17  +#
           18  +# This program is distributed in the hope that it will be useful,
           19  +# but WITHOUT ANY WARRANTY; without even the implied warranty of
           20  +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
           21  +# GNU General Public License for more details.
           22  +#
           23  +# You should have received a copy of the GNU General Public License
           24  +# along with this program; if not, write to the Free Software
           25  +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
           26  +# 02110-1301, USA.
           27  +#
           28  +# As a special exception to the GNU General Public License, if you
           29  +# distribute this file as part of a program that contains a
           30  +# configuration script generated by Autoconf, you may include it under
           31  +# the same distribution terms that you use for the rest of that program.
           32  +
           33  +
           34  +# Please send patches to <config-patches@gnu.org>.  Submit a context
           35  +# diff and a properly formatted GNU ChangeLog entry.
           36  +#
           37  +# Configuration subroutine to validate and canonicalize a configuration type.
           38  +# Supply the specified configuration type as an argument.
           39  +# If it is invalid, we print an error message on stderr and exit with code 1.
           40  +# Otherwise, we print the canonical config type on stdout and succeed.
           41  +
           42  +# You can get the latest version of this script from:
           43  +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
           44  +
           45  +# This file is supposed to be the same for all GNU packages
           46  +# and recognize all the CPU types, system types and aliases
           47  +# that are meaningful with *any* GNU software.
           48  +# Each package is responsible for reporting which valid configurations
           49  +# it does not support.  The user should be able to distinguish
           50  +# a failure to support a valid configuration from a meaningless
           51  +# configuration.
           52  +
           53  +# The goal of this file is to map all the various variations of a given
           54  +# machine specification into a single specification in the form:
           55  +#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
           56  +# or in some cases, the newer four-part form:
           57  +#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
           58  +# It is wrong to echo any other type of specification.
           59  +
           60  +me=`echo "$0" | sed -e 's,.*/,,'`
           61  +
           62  +usage="\
           63  +Usage: $0 [OPTION] CPU-MFR-OPSYS
           64  +       $0 [OPTION] ALIAS
           65  +
           66  +Canonicalize a configuration name.
           67  +
           68  +Operation modes:
           69  +  -h, --help         print this help, then exit
           70  +  -t, --time-stamp   print date of last modification, then exit
           71  +  -v, --version      print version number, then exit
           72  +
           73  +Report bugs and patches to <config-patches@gnu.org>."
           74  +
           75  +version="\
           76  +GNU config.sub ($timestamp)
           77  +
           78  +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
           79  +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free
           80  +Software Foundation, Inc.
           81  +
           82  +This is free software; see the source for copying conditions.  There is NO
           83  +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
           84  +
           85  +help="
           86  +Try \`$me --help' for more information."
           87  +
           88  +# Parse command line
           89  +while test $# -gt 0 ; do
           90  +  case $1 in
           91  +    --time-stamp | --time* | -t )
           92  +       echo "$timestamp" ; exit ;;
           93  +    --version | -v )
           94  +       echo "$version" ; exit ;;
           95  +    --help | --h* | -h )
           96  +       echo "$usage"; exit ;;
           97  +    -- )     # Stop option processing
           98  +       shift; break ;;
           99  +    - )	# Use stdin as input.
          100  +       break ;;
          101  +    -* )
          102  +       echo "$me: invalid option $1$help"
          103  +       exit 1 ;;
          104  +
          105  +    *local*)
          106  +       # First pass through any local machine types.
          107  +       echo $1
          108  +       exit ;;
          109  +
          110  +    * )
          111  +       break ;;
          112  +  esac
          113  +done
          114  +
          115  +case $# in
          116  + 0) echo "$me: missing argument$help" >&2
          117  +    exit 1;;
          118  + 1) ;;
          119  + *) echo "$me: too many arguments$help" >&2
          120  +    exit 1;;
          121  +esac
          122  +
          123  +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
          124  +# Here we must recognize all the valid KERNEL-OS combinations.
          125  +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
          126  +case $maybe_os in
          127  +  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
          128  +  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
          129  +  knetbsd*-gnu* | netbsd*-gnu* | \
          130  +  kopensolaris*-gnu* | \
          131  +  storm-chaos* | os2-emx* | rtmk-nova*)
          132  +    os=-$maybe_os
          133  +    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
          134  +    ;;
          135  +  *)
          136  +    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
          137  +    if [ $basic_machine != $1 ]
          138  +    then os=`echo $1 | sed 's/.*-/-/'`
          139  +    else os=; fi
          140  +    ;;
          141  +esac
          142  +
          143  +### Let's recognize common machines as not being operating systems so
          144  +### that things like config.sub decstation-3100 work.  We also
          145  +### recognize some manufacturers as not being operating systems, so we
          146  +### can provide default operating systems below.
          147  +case $os in
          148  +	-sun*os*)
          149  +		# Prevent following clause from handling this invalid input.
          150  +		;;
          151  +	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
          152  +	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
          153  +	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
          154  +	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
          155  +	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
          156  +	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
          157  +	-apple | -axis | -knuth | -cray | -microblaze)
          158  +		os=
          159  +		basic_machine=$1
          160  +		;;
          161  +        -bluegene*)
          162  +	        os=-cnk
          163  +		;;
          164  +	-sim | -cisco | -oki | -wec | -winbond)
          165  +		os=
          166  +		basic_machine=$1
          167  +		;;
          168  +	-scout)
          169  +		;;
          170  +	-wrs)
          171  +		os=-vxworks
          172  +		basic_machine=$1
          173  +		;;
          174  +	-chorusos*)
          175  +		os=-chorusos
          176  +		basic_machine=$1
          177  +		;;
          178  + 	-chorusrdb)
          179  + 		os=-chorusrdb
          180  +		basic_machine=$1
          181  + 		;;
          182  +	-hiux*)
          183  +		os=-hiuxwe2
          184  +		;;
          185  +	-sco6)
          186  +		os=-sco5v6
          187  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          188  +		;;
          189  +	-sco5)
          190  +		os=-sco3.2v5
          191  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          192  +		;;
          193  +	-sco4)
          194  +		os=-sco3.2v4
          195  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          196  +		;;
          197  +	-sco3.2.[4-9]*)
          198  +		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
          199  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          200  +		;;
          201  +	-sco3.2v[4-9]*)
          202  +		# Don't forget version if it is 3.2v4 or newer.
          203  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          204  +		;;
          205  +	-sco5v6*)
          206  +		# Don't forget version if it is 3.2v4 or newer.
          207  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          208  +		;;
          209  +	-sco*)
          210  +		os=-sco3.2v2
          211  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          212  +		;;
          213  +	-udk*)
          214  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          215  +		;;
          216  +	-isc)
          217  +		os=-isc2.2
          218  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          219  +		;;
          220  +	-clix*)
          221  +		basic_machine=clipper-intergraph
          222  +		;;
          223  +	-isc*)
          224  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
          225  +		;;
          226  +	-lynx*)
          227  +		os=-lynxos
          228  +		;;
          229  +	-ptx*)
          230  +		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
          231  +		;;
          232  +	-windowsnt*)
          233  +		os=`echo $os | sed -e 's/windowsnt/winnt/'`
          234  +		;;
          235  +	-psos*)
          236  +		os=-psos
          237  +		;;
          238  +	-mint | -mint[0-9]*)
          239  +		basic_machine=m68k-atari
          240  +		os=-mint
          241  +		;;
          242  +esac
          243  +
          244  +# Decode aliases for certain CPU-COMPANY combinations.
          245  +case $basic_machine in
          246  +	# Recognize the basic CPU types without company name.
          247  +	# Some are omitted here because they have special meanings below.
          248  +	1750a | 580 \
          249  +	| a29k \
          250  +	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
          251  +	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
          252  +	| am33_2.0 \
          253  +	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
          254  +	| bfin \
          255  +	| c4x | clipper \
          256  +	| d10v | d30v | dlx | dsp16xx \
          257  +	| fido | fr30 | frv \
          258  +	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
          259  +	| i370 | i860 | i960 | ia64 \
          260  +	| ip2k | iq2000 \
          261  +	| lm32 \
          262  +	| m32c | m32r | m32rle | m68000 | m68k | m88k \
          263  +	| maxq | mb | microblaze | mcore | mep | metag \
          264  +	| mips | mipsbe | mipseb | mipsel | mipsle \
          265  +	| mips16 \
          266  +	| mips64 | mips64el \
          267  +	| mips64octeon | mips64octeonel \
          268  +	| mips64orion | mips64orionel \
          269  +	| mips64r5900 | mips64r5900el \
          270  +	| mips64vr | mips64vrel \
          271  +	| mips64vr4100 | mips64vr4100el \
          272  +	| mips64vr4300 | mips64vr4300el \
          273  +	| mips64vr5000 | mips64vr5000el \
          274  +	| mips64vr5900 | mips64vr5900el \
          275  +	| mipsisa32 | mipsisa32el \
          276  +	| mipsisa32r2 | mipsisa32r2el \
          277  +	| mipsisa64 | mipsisa64el \
          278  +	| mipsisa64r2 | mipsisa64r2el \
          279  +	| mipsisa64sb1 | mipsisa64sb1el \
          280  +	| mipsisa64sr71k | mipsisa64sr71kel \
          281  +	| mipstx39 | mipstx39el \
          282  +	| mn10200 | mn10300 \
          283  +	| moxie \
          284  +	| mt \
          285  +	| msp430 \
          286  +	| nds32 | nds32le | nds32be \
          287  +	| nios | nios2 \
          288  +	| ns16k | ns32k \
          289  +	| or32 \
          290  +	| pdp10 | pdp11 | pj | pjl \
          291  +	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
          292  +	| pyramid \
          293  +	| rx \
          294  +	| score \
          295  +	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
          296  +	| sh64 | sh64le \
          297  +	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
          298  +	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
          299  +	| spu | strongarm \
          300  +	| tahoe | thumb | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
          301  +	| ubicom32 \
          302  +	| v850 | v850e \
          303  +	| we32k \
          304  +	| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
          305  +	| z8k | z80)
          306  +		basic_machine=$basic_machine-unknown
          307  +		;;
          308  +	c54x)
          309  +		basic_machine=tic54x-unknown
          310  +		;;
          311  +	c55x)
          312  +		basic_machine=tic55x-unknown
          313  +		;;
          314  +	c6x)
          315  +		basic_machine=tic6x-unknown
          316  +		;;
          317  +	m6811 | m68hc11 | m6812 | m68hc12 | picochip)
          318  +		# Motorola 68HC11/12.
          319  +		basic_machine=$basic_machine-unknown
          320  +		os=-none
          321  +		;;
          322  +	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
          323  +		;;
          324  +	ms1)
          325  +		basic_machine=mt-unknown
          326  +		;;
          327  +
          328  +	# We use `pc' rather than `unknown'
          329  +	# because (1) that's what they normally are, and
          330  +	# (2) the word "unknown" tends to confuse beginning users.
          331  +	i*86 | x86_64)
          332  +	  basic_machine=$basic_machine-pc
          333  +	  ;;
          334  +	# Object if more than one company name word.
          335  +	*-*-*)
          336  +		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
          337  +		exit 1
          338  +		;;
          339  +	# Recognize the basic CPU types with company name.
          340  +	580-* \
          341  +	| a29k-* \
          342  +	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
          343  +	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
          344  +	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
          345  +	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
          346  +	| avr-* | avr32-* \
          347  +	| bfin-* | bs2000-* \
          348  +	| c[123]* | c30-* | [cjt]90-* | c4x-* \
          349  +	| clipper-* | craynv-* | cydra-* \
          350  +	| d10v-* | d30v-* | dlx-* \
          351  +	| elxsi-* \
          352  +	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
          353  +	| h8300-* | h8500-* \
          354  +	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
          355  +	| i*86-* | i860-* | i960-* | ia64-* \
          356  +	| ip2k-* | iq2000-* \
          357  +	| lm32-* \
          358  +	| m32c-* | m32r-* | m32rle-* \
          359  +	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
          360  +	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
          361  +	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
          362  +	| mips16-* \
          363  +	| mips64-* | mips64el-* \
          364  +	| mips64octeon-* | mips64octeonel-* \
          365  +	| mips64orion-* | mips64orionel-* \
          366  +	| mips64r5900-* | mips64r5900el-* \
          367  +	| mips64vr-* | mips64vrel-* \
          368  +	| mips64vr4100-* | mips64vr4100el-* \
          369  +	| mips64vr4300-* | mips64vr4300el-* \
          370  +	| mips64vr5000-* | mips64vr5000el-* \
          371  +	| mips64vr5900-* | mips64vr5900el-* \
          372  +	| mipsisa32-* | mipsisa32el-* \
          373  +	| mipsisa32r2-* | mipsisa32r2el-* \
          374  +	| mipsisa64-* | mipsisa64el-* \
          375  +	| mipsisa64r2-* | mipsisa64r2el-* \
          376  +	| mipsisa64sb1-* | mipsisa64sb1el-* \
          377  +	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
          378  +	| mipstx39-* | mipstx39el-* \
          379  +	| mmix-* \
          380  +	| mt-* \
          381  +	| msp430-* \
          382  +	| nds32-* | nds32le-* | nds32be-* \
          383  +	| nios-* | nios2-* \
          384  +	| none-* | np1-* | ns16k-* | ns32k-* \
          385  +	| orion-* \
          386  +	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
          387  +	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
          388  +	| pyramid-* \
          389  +	| romp-* | rs6000-* | rx-* \
          390  +	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
          391  +	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
          392  +	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
          393  +	| sparclite-* \
          394  +	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
          395  +	| tahoe-* | thumb-* \
          396  +	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
          397  +	| tile-* | tilegx-* \
          398  +	| tron-* \
          399  +	| ubicom32-* \
          400  +	| v850-* | v850e-* | vax-* \
          401  +	| we32k-* \
          402  +	| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
          403  +	| xstormy16-* | xtensa*-* \
          404  +	| ymp-* \
          405  +	| z8k-* | z80-*)
          406  +		;;
          407  +	# Recognize the basic CPU types without company name, with glob match.
          408  +	xtensa*)
          409  +		basic_machine=$basic_machine-unknown
          410  +		;;
          411  +	# Recognize the various machine names and aliases which stand
          412  +	# for a CPU type and a company and sometimes even an OS.
          413  +	386bsd)
          414  +		basic_machine=i386-unknown
          415  +		os=-bsd
          416  +		;;
          417  +	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
          418  +		basic_machine=m68000-att
          419  +		;;
          420  +	3b*)
          421  +		basic_machine=we32k-att
          422  +		;;
          423  +	a29khif)
          424  +		basic_machine=a29k-amd
          425  +		os=-udi
          426  +		;;
          427  +    	abacus)
          428  +		basic_machine=abacus-unknown
          429  +		;;
          430  +	adobe68k)
          431  +		basic_machine=m68010-adobe
          432  +		os=-scout
          433  +		;;
          434  +	alliant | fx80)
          435  +		basic_machine=fx80-alliant
          436  +		;;
          437  +	altos | altos3068)
          438  +		basic_machine=m68k-altos
          439  +		;;
          440  +	am29k)
          441  +		basic_machine=a29k-none
          442  +		os=-bsd
          443  +		;;
          444  +	amd64)
          445  +		basic_machine=x86_64-pc
          446  +		;;
          447  +	amd64-*)
          448  +		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
          449  +		;;
          450  +	amdahl)
          451  +		basic_machine=580-amdahl
          452  +		os=-sysv
          453  +		;;
          454  +	amiga | amiga-*)
          455  +		basic_machine=m68k-unknown
          456  +		;;
          457  +	amigaos | amigados)
          458  +		basic_machine=m68k-unknown
          459  +		os=-amigaos
          460  +		;;
          461  +	amigaunix | amix)
          462  +		basic_machine=m68k-unknown
          463  +		os=-sysv4
          464  +		;;
          465  +	apollo68)
          466  +		basic_machine=m68k-apollo
          467  +		os=-sysv
          468  +		;;
          469  +	apollo68bsd)
          470  +		basic_machine=m68k-apollo
          471  +		os=-bsd
          472  +		;;
          473  +	aros)
          474  +		basic_machine=i386-pc
          475  +		os=-aros
          476  +		;;
          477  +	aux)
          478  +		basic_machine=m68k-apple
          479  +		os=-aux
          480  +		;;
          481  +	balance)
          482  +		basic_machine=ns32k-sequent
          483  +		os=-dynix
          484  +		;;
          485  +	blackfin)
          486  +		basic_machine=bfin-unknown
          487  +		os=-linux
          488  +		;;
          489  +	blackfin-*)
          490  +		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
          491  +		os=-linux
          492  +		;;
          493  +	bluegene*)
          494  +		basic_machine=powerpc-ibm
          495  +		os=-cnk
          496  +		;;
          497  +	c54x-*)
          498  +		basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
          499  +		;;
          500  +	c55x-*)
          501  +		basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
          502  +		;;
          503  +	c6x-*)
          504  +		basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
          505  +		;;
          506  +	c90)
          507  +		basic_machine=c90-cray
          508  +		os=-unicos
          509  +		;;
          510  +        cegcc)
          511  +		basic_machine=arm-unknown
          512  +		os=-cegcc
          513  +		;;
          514  +	convex-c1)
          515  +		basic_machine=c1-convex
          516  +		os=-bsd
          517  +		;;
          518  +	convex-c2)
          519  +		basic_machine=c2-convex
          520  +		os=-bsd
          521  +		;;
          522  +	convex-c32)
          523  +		basic_machine=c32-convex
          524  +		os=-bsd
          525  +		;;
          526  +	convex-c34)
          527  +		basic_machine=c34-convex
          528  +		os=-bsd
          529  +		;;
          530  +	convex-c38)
          531  +		basic_machine=c38-convex
          532  +		os=-bsd
          533  +		;;
          534  +	cray | j90)
          535  +		basic_machine=j90-cray
          536  +		os=-unicos
          537  +		;;
          538  +	craynv)
          539  +		basic_machine=craynv-cray
          540  +		os=-unicosmp
          541  +		;;
          542  +	cr16)
          543  +		basic_machine=cr16-unknown
          544  +		os=-elf
          545  +		;;
          546  +	crds | unos)
          547  +		basic_machine=m68k-crds
          548  +		;;
          549  +	crisv32 | crisv32-* | etraxfs*)
          550  +		basic_machine=crisv32-axis
          551  +		;;
          552  +	cris | cris-* | etrax*)
          553  +		basic_machine=cris-axis
          554  +		;;
          555  +	crx)
          556  +		basic_machine=crx-unknown
          557  +		os=-elf
          558  +		;;
          559  +	da30 | da30-*)
          560  +		basic_machine=m68k-da30
          561  +		;;
          562  +	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
          563  +		basic_machine=mips-dec
          564  +		;;
          565  +	decsystem10* | dec10*)
          566  +		basic_machine=pdp10-dec
          567  +		os=-tops10
          568  +		;;
          569  +	decsystem20* | dec20*)
          570  +		basic_machine=pdp10-dec
          571  +		os=-tops20
          572  +		;;
          573  +	delta | 3300 | motorola-3300 | motorola-delta \
          574  +	      | 3300-motorola | delta-motorola)
          575  +		basic_machine=m68k-motorola
          576  +		;;
          577  +	delta88)
          578  +		basic_machine=m88k-motorola
          579  +		os=-sysv3
          580  +		;;
          581  +	dicos)
          582  +		basic_machine=i686-pc
          583  +		os=-dicos
          584  +		;;
          585  +	djgpp)
          586  +		basic_machine=i586-pc
          587  +		os=-msdosdjgpp
          588  +		;;
          589  +	dpx20 | dpx20-*)
          590  +		basic_machine=rs6000-bull
          591  +		os=-bosx
          592  +		;;
          593  +	dpx2* | dpx2*-bull)
          594  +		basic_machine=m68k-bull
          595  +		os=-sysv3
          596  +		;;
          597  +	ebmon29k)
          598  +		basic_machine=a29k-amd
          599  +		os=-ebmon
          600  +		;;
          601  +	elxsi)
          602  +		basic_machine=elxsi-elxsi
          603  +		os=-bsd
          604  +		;;
          605  +	encore | umax | mmax)
          606  +		basic_machine=ns32k-encore
          607  +		;;
          608  +	es1800 | OSE68k | ose68k | ose | OSE)
          609  +		basic_machine=m68k-ericsson
          610  +		os=-ose
          611  +		;;
          612  +	fx2800)
          613  +		basic_machine=i860-alliant
          614  +		;;
          615  +	genix)
          616  +		basic_machine=ns32k-ns
          617  +		;;
          618  +	gmicro)
          619  +		basic_machine=tron-gmicro
          620  +		os=-sysv
          621  +		;;
          622  +	go32)
          623  +		basic_machine=i386-pc
          624  +		os=-go32
          625  +		;;
          626  +	h3050r* | hiux*)
          627  +		basic_machine=hppa1.1-hitachi
          628  +		os=-hiuxwe2
          629  +		;;
          630  +	h8300hms)
          631  +		basic_machine=h8300-hitachi
          632  +		os=-hms
          633  +		;;
          634  +	h8300xray)
          635  +		basic_machine=h8300-hitachi
          636  +		os=-xray
          637  +		;;
          638  +	h8500hms)
          639  +		basic_machine=h8500-hitachi
          640  +		os=-hms
          641  +		;;
          642  +	harris)
          643  +		basic_machine=m88k-harris
          644  +		os=-sysv3
          645  +		;;
          646  +	hp300-*)
          647  +		basic_machine=m68k-hp
          648  +		;;
          649  +	hp300bsd)
          650  +		basic_machine=m68k-hp
          651  +		os=-bsd
          652  +		;;
          653  +	hp300hpux)
          654  +		basic_machine=m68k-hp
          655  +		os=-hpux
          656  +		;;
          657  +	hp3k9[0-9][0-9] | hp9[0-9][0-9])
          658  +		basic_machine=hppa1.0-hp
          659  +		;;
          660  +	hp9k2[0-9][0-9] | hp9k31[0-9])
          661  +		basic_machine=m68000-hp
          662  +		;;
          663  +	hp9k3[2-9][0-9])
          664  +		basic_machine=m68k-hp
          665  +		;;
          666  +	hp9k6[0-9][0-9] | hp6[0-9][0-9])
          667  +		basic_machine=hppa1.0-hp
          668  +		;;
          669  +	hp9k7[0-79][0-9] | hp7[0-79][0-9])
          670  +		basic_machine=hppa1.1-hp
          671  +		;;
          672  +	hp9k78[0-9] | hp78[0-9])
          673  +		# FIXME: really hppa2.0-hp
          674  +		basic_machine=hppa1.1-hp
          675  +		;;
          676  +	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
          677  +		# FIXME: really hppa2.0-hp
          678  +		basic_machine=hppa1.1-hp
          679  +		;;
          680  +	hp9k8[0-9][13679] | hp8[0-9][13679])
          681  +		basic_machine=hppa1.1-hp
          682  +		;;
          683  +	hp9k8[0-9][0-9] | hp8[0-9][0-9])
          684  +		basic_machine=hppa1.0-hp
          685  +		;;
          686  +	hppa-next)
          687  +		os=-nextstep3
          688  +		;;
          689  +	hppaosf)
          690  +		basic_machine=hppa1.1-hp
          691  +		os=-osf
          692  +		;;
          693  +	hppro)
          694  +		basic_machine=hppa1.1-hp
          695  +		os=-proelf
          696  +		;;
          697  +	i370-ibm* | ibm*)
          698  +		basic_machine=i370-ibm
          699  +		;;
          700  +# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
          701  +	i*86v32)
          702  +		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
          703  +		os=-sysv32
          704  +		;;
          705  +	i*86v4*)
          706  +		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
          707  +		os=-sysv4
          708  +		;;
          709  +	i*86v)
          710  +		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
          711  +		os=-sysv
          712  +		;;
          713  +	i*86sol2)
          714  +		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
          715  +		os=-solaris2
          716  +		;;
          717  +	i386mach)
          718  +		basic_machine=i386-mach
          719  +		os=-mach
          720  +		;;
          721  +	i386-vsta | vsta)
          722  +		basic_machine=i386-unknown
          723  +		os=-vsta
          724  +		;;
          725  +	iris | iris4d)
          726  +		basic_machine=mips-sgi
          727  +		case $os in
          728  +		    -irix*)
          729  +			;;
          730  +		    *)
          731  +			os=-irix4
          732  +			;;
          733  +		esac
          734  +		;;
          735  +	isi68 | isi)
          736  +		basic_machine=m68k-isi
          737  +		os=-sysv
          738  +		;;
          739  +	m68knommu)
          740  +		basic_machine=m68k-unknown
          741  +		os=-linux
          742  +		;;
          743  +	m68knommu-*)
          744  +		basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
          745  +		os=-linux
          746  +		;;
          747  +	m88k-omron*)
          748  +		basic_machine=m88k-omron
          749  +		;;
          750  +	magnum | m3230)
          751  +		basic_machine=mips-mips
          752  +		os=-sysv
          753  +		;;
          754  +	merlin)
          755  +		basic_machine=ns32k-utek
          756  +		os=-sysv
          757  +		;;
          758  +        microblaze)
          759  +		basic_machine=microblaze-xilinx
          760  +		;;
          761  +	mingw32)
          762  +		basic_machine=i386-pc
          763  +		os=-mingw32
          764  +		;;
          765  +	mingw32ce)
          766  +		basic_machine=arm-unknown
          767  +		os=-mingw32ce
          768  +		;;
          769  +	miniframe)
          770  +		basic_machine=m68000-convergent
          771  +		;;
          772  +	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
          773  +		basic_machine=m68k-atari
          774  +		os=-mint
          775  +		;;
          776  +	mips3*-*)
          777  +		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
          778  +		;;
          779  +	mips3*)
          780  +		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
          781  +		;;
          782  +	monitor)
          783  +		basic_machine=m68k-rom68k
          784  +		os=-coff
          785  +		;;
          786  +	morphos)
          787  +		basic_machine=powerpc-unknown
          788  +		os=-morphos
          789  +		;;
          790  +	msdos)
          791  +		basic_machine=i386-pc
          792  +		os=-msdos
          793  +		;;
          794  +	ms1-*)
          795  +		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
          796  +		;;
          797  +	mvs)
          798  +		basic_machine=i370-ibm
          799  +		os=-mvs
          800  +		;;
          801  +	ncr3000)
          802  +		basic_machine=i486-ncr
          803  +		os=-sysv4
          804  +		;;
          805  +	netbsd386)
          806  +		basic_machine=i386-unknown
          807  +		os=-netbsd
          808  +		;;
          809  +	netwinder)
          810  +		basic_machine=armv4l-rebel
          811  +		os=-linux
          812  +		;;
          813  +	news | news700 | news800 | news900)
          814  +		basic_machine=m68k-sony
          815  +		os=-newsos
          816  +		;;
          817  +	news1000)
          818  +		basic_machine=m68030-sony
          819  +		os=-newsos
          820  +		;;
          821  +	news-3600 | risc-news)
          822  +		basic_machine=mips-sony
          823  +		os=-newsos
          824  +		;;
          825  +	necv70)
          826  +		basic_machine=v70-nec
          827  +		os=-sysv
          828  +		;;
          829  +	next | m*-next )
          830  +		basic_machine=m68k-next
          831  +		case $os in
          832  +		    -nextstep* )
          833  +			;;
          834  +		    -ns2*)
          835  +		      os=-nextstep2
          836  +			;;
          837  +		    *)
          838  +		      os=-nextstep3
          839  +			;;
          840  +		esac
          841  +		;;
          842  +	nh3000)
          843  +		basic_machine=m68k-harris
          844  +		os=-cxux
          845  +		;;
          846  +	nh[45]000)
          847  +		basic_machine=m88k-harris
          848  +		os=-cxux
          849  +		;;
          850  +	nindy960)
          851  +		basic_machine=i960-intel
          852  +		os=-nindy
          853  +		;;
          854  +	mon960)
          855  +		basic_machine=i960-intel
          856  +		os=-mon960
          857  +		;;
          858  +	nonstopux)
          859  +		basic_machine=mips-compaq
          860  +		os=-nonstopux
          861  +		;;
          862  +	np1)
          863  +		basic_machine=np1-gould
          864  +		;;
          865  +        neo-tandem)
          866  +		basic_machine=neo-tandem
          867  +		;;
          868  +        nse-tandem)
          869  +		basic_machine=nse-tandem
          870  +		;;
          871  +	nsr-tandem)
          872  +		basic_machine=nsr-tandem
          873  +		;;
          874  +	op50n-* | op60c-*)
          875  +		basic_machine=hppa1.1-oki
          876  +		os=-proelf
          877  +		;;
          878  +	openrisc | openrisc-*)
          879  +		basic_machine=or32-unknown
          880  +		;;
          881  +	os400)
          882  +		basic_machine=powerpc-ibm
          883  +		os=-os400
          884  +		;;
          885  +	OSE68000 | ose68000)
          886  +		basic_machine=m68000-ericsson
          887  +		os=-ose
          888  +		;;
          889  +	os68k)
          890  +		basic_machine=m68k-none
          891  +		os=-os68k
          892  +		;;
          893  +	pa-hitachi)
          894  +		basic_machine=hppa1.1-hitachi
          895  +		os=-hiuxwe2
          896  +		;;
          897  +	paragon)
          898  +		basic_machine=i860-intel
          899  +		os=-osf
          900  +		;;
          901  +	parisc)
          902  +		basic_machine=hppa-unknown
          903  +		os=-linux
          904  +		;;
          905  +	parisc-*)
          906  +		basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
          907  +		os=-linux
          908  +		;;
          909  +	pbd)
          910  +		basic_machine=sparc-tti
          911  +		;;
          912  +	pbb)
          913  +		basic_machine=m68k-tti
          914  +		;;
          915  +	pc532 | pc532-*)
          916  +		basic_machine=ns32k-pc532
          917  +		;;
          918  +	pc98)
          919  +		basic_machine=i386-pc
          920  +		;;
          921  +	pc98-*)
          922  +		basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
          923  +		;;
          924  +	pentium | p5 | k5 | k6 | nexgen | viac3)
          925  +		basic_machine=i586-pc
          926  +		;;
          927  +	pentiumpro | p6 | 6x86 | athlon | athlon_*)
          928  +		basic_machine=i686-pc
          929  +		;;
          930  +	pentiumii | pentium2 | pentiumiii | pentium3)
          931  +		basic_machine=i686-pc
          932  +		;;
          933  +	pentium4)
          934  +		basic_machine=i786-pc
          935  +		;;
          936  +	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
          937  +		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
          938  +		;;
          939  +	pentiumpro-* | p6-* | 6x86-* | athlon-*)
          940  +		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
          941  +		;;
          942  +	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
          943  +		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
          944  +		;;
          945  +	pentium4-*)
          946  +		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
          947  +		;;
          948  +	pn)
          949  +		basic_machine=pn-gould
          950  +		;;
          951  +	power)	basic_machine=power-ibm
          952  +		;;
          953  +	ppc)	basic_machine=powerpc-unknown
          954  +		;;
          955  +	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
          956  +		;;
          957  +	ppcle | powerpclittle | ppc-le | powerpc-little)
          958  +		basic_machine=powerpcle-unknown
          959  +		;;
          960  +	ppcle-* | powerpclittle-*)
          961  +		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
          962  +		;;
          963  +	ppc64)	basic_machine=powerpc64-unknown
          964  +		;;
          965  +	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
          966  +		;;
          967  +	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
          968  +		basic_machine=powerpc64le-unknown
          969  +		;;
          970  +	ppc64le-* | powerpc64little-*)
          971  +		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
          972  +		;;
          973  +	ps2)
          974  +		basic_machine=i386-ibm
          975  +		;;
          976  +	pw32)
          977  +		basic_machine=i586-unknown
          978  +		os=-pw32
          979  +		;;
          980  +	rdos)
          981  +		basic_machine=i386-pc
          982  +		os=-rdos
          983  +		;;
          984  +	rom68k)
          985  +		basic_machine=m68k-rom68k
          986  +		os=-coff
          987  +		;;
          988  +	rm[46]00)
          989  +		basic_machine=mips-siemens
          990  +		;;
          991  +	rtpc | rtpc-*)
          992  +		basic_machine=romp-ibm
          993  +		;;
          994  +	s390 | s390-*)
          995  +		basic_machine=s390-ibm
          996  +		;;
          997  +	s390x | s390x-*)
          998  +		basic_machine=s390x-ibm
          999  +		;;
         1000  +	sa29200)
         1001  +		basic_machine=a29k-amd
         1002  +		os=-udi
         1003  +		;;
         1004  +	sb1)
         1005  +		basic_machine=mipsisa64sb1-unknown
         1006  +		;;
         1007  +	sb1el)
         1008  +		basic_machine=mipsisa64sb1el-unknown
         1009  +		;;
         1010  +	sde)
         1011  +		basic_machine=mipsisa32-sde
         1012  +		os=-elf
         1013  +		;;
         1014  +	sei)
         1015  +		basic_machine=mips-sei
         1016  +		os=-seiux
         1017  +		;;
         1018  +	sequent)
         1019  +		basic_machine=i386-sequent
         1020  +		;;
         1021  +	sh)
         1022  +		basic_machine=sh-hitachi
         1023  +		os=-hms
         1024  +		;;
         1025  +	sh5el)
         1026  +		basic_machine=sh5le-unknown
         1027  +		;;
         1028  +	sh64)
         1029  +		basic_machine=sh64-unknown
         1030  +		;;
         1031  +	sparclite-wrs | simso-wrs)
         1032  +		basic_machine=sparclite-wrs
         1033  +		os=-vxworks
         1034  +		;;
         1035  +	sps7)
         1036  +		basic_machine=m68k-bull
         1037  +		os=-sysv2
         1038  +		;;
         1039  +	spur)
         1040  +		basic_machine=spur-unknown
         1041  +		;;
         1042  +	st2000)
         1043  +		basic_machine=m68k-tandem
         1044  +		;;
         1045  +	stratus)
         1046  +		basic_machine=i860-stratus
         1047  +		os=-sysv4
         1048  +		;;
         1049  +	sun2)
         1050  +		basic_machine=m68000-sun
         1051  +		;;
         1052  +	sun2os3)
         1053  +		basic_machine=m68000-sun
         1054  +		os=-sunos3
         1055  +		;;
         1056  +	sun2os4)
         1057  +		basic_machine=m68000-sun
         1058  +		os=-sunos4
         1059  +		;;
         1060  +	sun3os3)
         1061  +		basic_machine=m68k-sun
         1062  +		os=-sunos3
         1063  +		;;
         1064  +	sun3os4)
         1065  +		basic_machine=m68k-sun
         1066  +		os=-sunos4
         1067  +		;;
         1068  +	sun4os3)
         1069  +		basic_machine=sparc-sun
         1070  +		os=-sunos3
         1071  +		;;
         1072  +	sun4os4)
         1073  +		basic_machine=sparc-sun
         1074  +		os=-sunos4
         1075  +		;;
         1076  +	sun4sol2)
         1077  +		basic_machine=sparc-sun
         1078  +		os=-solaris2
         1079  +		;;
         1080  +	sun3 | sun3-*)
         1081  +		basic_machine=m68k-sun
         1082  +		;;
         1083  +	sun4)
         1084  +		basic_machine=sparc-sun
         1085  +		;;
         1086  +	sun386 | sun386i | roadrunner)
         1087  +		basic_machine=i386-sun
         1088  +		;;
         1089  +	sv1)
         1090  +		basic_machine=sv1-cray
         1091  +		os=-unicos
         1092  +		;;
         1093  +	symmetry)
         1094  +		basic_machine=i386-sequent
         1095  +		os=-dynix
         1096  +		;;
         1097  +	t3e)
         1098  +		basic_machine=alphaev5-cray
         1099  +		os=-unicos
         1100  +		;;
         1101  +	t90)
         1102  +		basic_machine=t90-cray
         1103  +		os=-unicos
         1104  +		;;
         1105  +        # This must be matched before tile*.
         1106  +        tilegx*)
         1107  +		basic_machine=tilegx-unknown
         1108  +		os=-linux-gnu
         1109  +		;;
         1110  +	tile*)
         1111  +		basic_machine=tile-unknown
         1112  +		os=-linux-gnu
         1113  +		;;
         1114  +	tx39)
         1115  +		basic_machine=mipstx39-unknown
         1116  +		;;
         1117  +	tx39el)
         1118  +		basic_machine=mipstx39el-unknown
         1119  +		;;
         1120  +	toad1)
         1121  +		basic_machine=pdp10-xkl
         1122  +		os=-tops20
         1123  +		;;
         1124  +	tower | tower-32)
         1125  +		basic_machine=m68k-ncr
         1126  +		;;
         1127  +	tpf)
         1128  +		basic_machine=s390x-ibm
         1129  +		os=-tpf
         1130  +		;;
         1131  +	udi29k)
         1132  +		basic_machine=a29k-amd
         1133  +		os=-udi
         1134  +		;;
         1135  +	ultra3)
         1136  +		basic_machine=a29k-nyu
         1137  +		os=-sym1
         1138  +		;;
         1139  +	v810 | necv810)
         1140  +		basic_machine=v810-nec
         1141  +		os=-none
         1142  +		;;
         1143  +	vaxv)
         1144  +		basic_machine=vax-dec
         1145  +		os=-sysv
         1146  +		;;
         1147  +	vms)
         1148  +		basic_machine=vax-dec
         1149  +		os=-vms
         1150  +		;;
         1151  +	vpp*|vx|vx-*)
         1152  +		basic_machine=f301-fujitsu
         1153  +		;;
         1154  +	vxworks960)
         1155  +		basic_machine=i960-wrs
         1156  +		os=-vxworks
         1157  +		;;
         1158  +	vxworks68)
         1159  +		basic_machine=m68k-wrs
         1160  +		os=-vxworks
         1161  +		;;
         1162  +	vxworks29k)
         1163  +		basic_machine=a29k-wrs
         1164  +		os=-vxworks
         1165  +		;;
         1166  +	w65*)
         1167  +		basic_machine=w65-wdc
         1168  +		os=-none
         1169  +		;;
         1170  +	w89k-*)
         1171  +		basic_machine=hppa1.1-winbond
         1172  +		os=-proelf
         1173  +		;;
         1174  +	xbox)
         1175  +		basic_machine=i686-pc
         1176  +		os=-mingw32
         1177  +		;;
         1178  +	xps | xps100)
         1179  +		basic_machine=xps100-honeywell
         1180  +		;;
         1181  +	ymp)
         1182  +		basic_machine=ymp-cray
         1183  +		os=-unicos
         1184  +		;;
         1185  +	z8k-*-coff)
         1186  +		basic_machine=z8k-unknown
         1187  +		os=-sim
         1188  +		;;
         1189  +	z80-*-coff)
         1190  +		basic_machine=z80-unknown
         1191  +		os=-sim
         1192  +		;;
         1193  +	none)
         1194  +		basic_machine=none-none
         1195  +		os=-none
         1196  +		;;
         1197  +
         1198  +# Here we handle the default manufacturer of certain CPU types.  It is in
         1199  +# some cases the only manufacturer, in others, it is the most popular.
         1200  +	w89k)
         1201  +		basic_machine=hppa1.1-winbond
         1202  +		;;
         1203  +	op50n)
         1204  +		basic_machine=hppa1.1-oki
         1205  +		;;
         1206  +	op60c)
         1207  +		basic_machine=hppa1.1-oki
         1208  +		;;
         1209  +	romp)
         1210  +		basic_machine=romp-ibm
         1211  +		;;
         1212  +	mmix)
         1213  +		basic_machine=mmix-knuth
         1214  +		;;
         1215  +	rs6000)
         1216  +		basic_machine=rs6000-ibm
         1217  +		;;
         1218  +	vax)
         1219  +		basic_machine=vax-dec
         1220  +		;;
         1221  +	pdp10)
         1222  +		# there are many clones, so DEC is not a safe bet
         1223  +		basic_machine=pdp10-unknown
         1224  +		;;
         1225  +	pdp11)
         1226  +		basic_machine=pdp11-dec
         1227  +		;;
         1228  +	we32k)
         1229  +		basic_machine=we32k-att
         1230  +		;;
         1231  +	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
         1232  +		basic_machine=sh-unknown
         1233  +		;;
         1234  +	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
         1235  +		basic_machine=sparc-sun
         1236  +		;;
         1237  +	cydra)
         1238  +		basic_machine=cydra-cydrome
         1239  +		;;
         1240  +	orion)
         1241  +		basic_machine=orion-highlevel
         1242  +		;;
         1243  +	orion105)
         1244  +		basic_machine=clipper-highlevel
         1245  +		;;
         1246  +	mac | mpw | mac-mpw)
         1247  +		basic_machine=m68k-apple
         1248  +		;;
         1249  +	pmac | pmac-mpw)
         1250  +		basic_machine=powerpc-apple
         1251  +		;;
         1252  +	*-unknown)
         1253  +		# Make sure to match an already-canonicalized machine name.
         1254  +		;;
         1255  +	*)
         1256  +		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
         1257  +		exit 1
         1258  +		;;
         1259  +esac
         1260  +
         1261  +# Here we canonicalize certain aliases for manufacturers.
         1262  +case $basic_machine in
         1263  +	*-digital*)
         1264  +		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
         1265  +		;;
         1266  +	*-commodore*)
         1267  +		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
         1268  +		;;
         1269  +	*)
         1270  +		;;
         1271  +esac
         1272  +
         1273  +# Decode manufacturer-specific aliases for certain operating systems.
         1274  +
         1275  +if [ x"$os" != x"" ]
         1276  +then
         1277  +case $os in
         1278  +        # First match some system type aliases
         1279  +        # that might get confused with valid system types.
         1280  +	# -solaris* is a basic system type, with this one exception.
         1281  +        -auroraux)
         1282  +	        os=-auroraux
         1283  +		;;
         1284  +	-solaris1 | -solaris1.*)
         1285  +		os=`echo $os | sed -e 's|solaris1|sunos4|'`
         1286  +		;;
         1287  +	-solaris)
         1288  +		os=-solaris2
         1289  +		;;
         1290  +	-svr4*)
         1291  +		os=-sysv4
         1292  +		;;
         1293  +	-unixware*)
         1294  +		os=-sysv4.2uw
         1295  +		;;
         1296  +	-gnu/linux*)
         1297  +		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
         1298  +		;;
         1299  +	# First accept the basic system types.
         1300  +	# The portable systems comes first.
         1301  +	# Each alternative MUST END IN A *, to match a version number.
         1302  +	# -sysv* is not here because it comes later, after sysvr4.
         1303  +	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
         1304  +	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
         1305  +	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
         1306  +	      | -sym* | -kopensolaris* \
         1307  +	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
         1308  +	      | -aos* | -aros* \
         1309  +	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
         1310  +	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
         1311  +	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
         1312  +	      | -openbsd* | -solidbsd* \
         1313  +	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
         1314  +	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
         1315  +	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
         1316  +	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
         1317  +	      | -chorusos* | -chorusrdb* | -cegcc* \
         1318  +	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
         1319  +	      | -mingw32* | -linux-gnu* | -linux-android* \
         1320  +	      | -linux-newlib* | -linux-uclibc* \
         1321  +	      | -uxpv* | -beos* | -mpeix* | -udk* \
         1322  +	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
         1323  +	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
         1324  +	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
         1325  +	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
         1326  +	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
         1327  +	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
         1328  +	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
         1329  +	# Remember, each alternative MUST END IN *, to match a version number.
         1330  +		;;
         1331  +	-qnx*)
         1332  +		case $basic_machine in
         1333  +		    x86-* | i*86-*)
         1334  +			;;
         1335  +		    *)
         1336  +			os=-nto$os
         1337  +			;;
         1338  +		esac
         1339  +		;;
         1340  +	-nto-qnx*)
         1341  +		;;
         1342  +	-nto*)
         1343  +		os=`echo $os | sed -e 's|nto|nto-qnx|'`
         1344  +		;;
         1345  +	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
         1346  +	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
         1347  +	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
         1348  +		;;
         1349  +	-mac*)
         1350  +		os=`echo $os | sed -e 's|mac|macos|'`
         1351  +		;;
         1352  +	-linux-dietlibc)
         1353  +		os=-linux-dietlibc
         1354  +		;;
         1355  +	-linux*)
         1356  +		os=`echo $os | sed -e 's|linux|linux-gnu|'`
         1357  +		;;
         1358  +	-sunos5*)
         1359  +		os=`echo $os | sed -e 's|sunos5|solaris2|'`
         1360  +		;;
         1361  +	-sunos6*)
         1362  +		os=`echo $os | sed -e 's|sunos6|solaris3|'`
         1363  +		;;
         1364  +	-opened*)
         1365  +		os=-openedition
         1366  +		;;
         1367  +        -os400*)
         1368  +		os=-os400
         1369  +		;;
         1370  +	-wince*)
         1371  +		os=-wince
         1372  +		;;
         1373  +	-osfrose*)
         1374  +		os=-osfrose
         1375  +		;;
         1376  +	-osf*)
         1377  +		os=-osf
         1378  +		;;
         1379  +	-utek*)
         1380  +		os=-bsd
         1381  +		;;
         1382  +	-dynix*)
         1383  +		os=-bsd
         1384  +		;;
         1385  +	-acis*)
         1386  +		os=-aos
         1387  +		;;
         1388  +	-atheos*)
         1389  +		os=-atheos
         1390  +		;;
         1391  +	-syllable*)
         1392  +		os=-syllable
         1393  +		;;
         1394  +	-386bsd)
         1395  +		os=-bsd
         1396  +		;;
         1397  +	-ctix* | -uts*)
         1398  +		os=-sysv
         1399  +		;;
         1400  +	-nova*)
         1401  +		os=-rtmk-nova
         1402  +		;;
         1403  +	-ns2 )
         1404  +		os=-nextstep2
         1405  +		;;
         1406  +	-nsk*)
         1407  +		os=-nsk
         1408  +		;;
         1409  +	# Preserve the version number of sinix5.
         1410  +	-sinix5.*)
         1411  +		os=`echo $os | sed -e 's|sinix|sysv|'`
         1412  +		;;
         1413  +	-sinix*)
         1414  +		os=-sysv4
         1415  +		;;
         1416  +        -tpf*)
         1417  +		os=-tpf
         1418  +		;;
         1419  +	-triton*)
         1420  +		os=-sysv3
         1421  +		;;
         1422  +	-oss*)
         1423  +		os=-sysv3
         1424  +		;;
         1425  +	-svr4)
         1426  +		os=-sysv4
         1427  +		;;
         1428  +	-svr3)
         1429  +		os=-sysv3
         1430  +		;;
         1431  +	-sysvr4)
         1432  +		os=-sysv4
         1433  +		;;
         1434  +	# This must come after -sysvr4.
         1435  +	-sysv*)
         1436  +		;;
         1437  +	-ose*)
         1438  +		os=-ose
         1439  +		;;
         1440  +	-es1800*)
         1441  +		os=-ose
         1442  +		;;
         1443  +	-xenix)
         1444  +		os=-xenix
         1445  +		;;
         1446  +	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
         1447  +		os=-mint
         1448  +		;;
         1449  +	-aros*)
         1450  +		os=-aros
         1451  +		;;
         1452  +	-kaos*)
         1453  +		os=-kaos
         1454  +		;;
         1455  +	-zvmoe)
         1456  +		os=-zvmoe
         1457  +		;;
         1458  +	-dicos*)
         1459  +		os=-dicos
         1460  +		;;
         1461  +        -nacl*)
         1462  +	        ;;
         1463  +	-none)
         1464  +		;;
         1465  +	*)
         1466  +		# Get rid of the `-' at the beginning of $os.
         1467  +		os=`echo $os | sed 's/[^-]*-//'`
         1468  +		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
         1469  +		exit 1
         1470  +		;;
         1471  +esac
         1472  +else
         1473  +
         1474  +# Here we handle the default operating systems that come with various machines.
         1475  +# The value should be what the vendor currently ships out the door with their
         1476  +# machine or put another way, the most popular os provided with the machine.
         1477  +
         1478  +# Note that if you're going to try to match "-MANUFACTURER" here (say,
         1479  +# "-sun"), then you have to tell the case statement up towards the top
         1480  +# that MANUFACTURER isn't an operating system.  Otherwise, code above
         1481  +# will signal an error saying that MANUFACTURER isn't an operating
         1482  +# system, and we'll never get to this point.
         1483  +
         1484  +case $basic_machine in
         1485  +        score-*)
         1486  +		os=-elf
         1487  +		;;
         1488  +        spu-*)
         1489  +		os=-elf
         1490  +		;;
         1491  +	*-acorn)
         1492  +		os=-riscix1.2
         1493  +		;;
         1494  +	arm*-rebel)
         1495  +		os=-linux
         1496  +		;;
         1497  +	arm*-semi)
         1498  +		os=-aout
         1499  +		;;
         1500  +        c4x-* | tic4x-*)
         1501  +        	os=-coff
         1502  +		;;
         1503  +	tic54x-*)
         1504  +		os=-coff
         1505  +		;;
         1506  +	tic55x-*)
         1507  +		os=-coff
         1508  +		;;
         1509  +	tic6x-*)
         1510  +		os=-coff
         1511  +		;;
         1512  +	# This must come before the *-dec entry.
         1513  +	pdp10-*)
         1514  +		os=-tops20
         1515  +		;;
         1516  +	pdp11-*)
         1517  +		os=-none
         1518  +		;;
         1519  +	*-dec | vax-*)
         1520  +		os=-ultrix4.2
         1521  +		;;
         1522  +	m68*-apollo)
         1523  +		os=-domain
         1524  +		;;
         1525  +	i386-sun)
         1526  +		os=-sunos4.0.2
         1527  +		;;
         1528  +	m68000-sun)
         1529  +		os=-sunos3
         1530  +		# This also exists in the configure program, but was not the
         1531  +		# default.
         1532  +		# os=-sunos4
         1533  +		;;
         1534  +	m68*-cisco)
         1535  +		os=-aout
         1536  +		;;
         1537  +        mep-*)
         1538  +		os=-elf
         1539  +		;;
         1540  +	mips*-cisco)
         1541  +		os=-elf
         1542  +		;;
         1543  +	mips*-*)
         1544  +		os=-elf
         1545  +		;;
         1546  +	or32-*)
         1547  +		os=-coff
         1548  +		;;
         1549  +	*-tti)	# must be before sparc entry or we get the wrong os.
         1550  +		os=-sysv3
         1551  +		;;
         1552  +	sparc-* | *-sun)
         1553  +		os=-sunos4.1.1
         1554  +		;;
         1555  +	*-be)
         1556  +		os=-beos
         1557  +		;;
         1558  +	*-haiku)
         1559  +		os=-haiku
         1560  +		;;
         1561  +	*-ibm)
         1562  +		os=-aix
         1563  +		;;
         1564  +    	*-knuth)
         1565  +		os=-mmixware
         1566  +		;;
         1567  +	*-wec)
         1568  +		os=-proelf
         1569  +		;;
         1570  +	*-winbond)
         1571  +		os=-proelf
         1572  +		;;
         1573  +	*-oki)
         1574  +		os=-proelf
         1575  +		;;
         1576  +	*-hp)
         1577  +		os=-hpux
         1578  +		;;
         1579  +	*-hitachi)
         1580  +		os=-hiux
         1581  +		;;
         1582  +	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
         1583  +		os=-sysv
         1584  +		;;
         1585  +	*-cbm)
         1586  +		os=-amigaos
         1587  +		;;
         1588  +	*-dg)
         1589  +		os=-dgux
         1590  +		;;
         1591  +	*-dolphin)
         1592  +		os=-sysv3
         1593  +		;;
         1594  +	m68k-ccur)
         1595  +		os=-rtu
         1596  +		;;
         1597  +	m88k-omron*)
         1598  +		os=-luna
         1599  +		;;
         1600  +	*-next )
         1601  +		os=-nextstep
         1602  +		;;
         1603  +	*-sequent)
         1604  +		os=-ptx
         1605  +		;;
         1606  +	*-crds)
         1607  +		os=-unos
         1608  +		;;
         1609  +	*-ns)
         1610  +		os=-genix
         1611  +		;;
         1612  +	i370-*)
         1613  +		os=-mvs
         1614  +		;;
         1615  +	*-next)
         1616  +		os=-nextstep3
         1617  +		;;
         1618  +	*-gould)
         1619  +		os=-sysv
         1620  +		;;
         1621  +	*-highlevel)
         1622  +		os=-bsd
         1623  +		;;
         1624  +	*-encore)
         1625  +		os=-bsd
         1626  +		;;
         1627  +	*-sgi)
         1628  +		os=-irix
         1629  +		;;
         1630  +	*-siemens)
         1631  +		os=-sysv4
         1632  +		;;
         1633  +	*-masscomp)
         1634  +		os=-rtu
         1635  +		;;
         1636  +	f30[01]-fujitsu | f700-fujitsu)
         1637  +		os=-uxpv
         1638  +		;;
         1639  +	*-rom68k)
         1640  +		os=-coff
         1641  +		;;
         1642  +	*-*bug)
         1643  +		os=-coff
         1644  +		;;
         1645  +	*-apple)
         1646  +		os=-macos
         1647  +		;;
         1648  +	*-atari*)
         1649  +		os=-mint
         1650  +		;;
         1651  +	*)
         1652  +		os=-none
         1653  +		;;
         1654  +esac
         1655  +fi
         1656  +
         1657  +# Here we handle the case where we know the os, and the CPU type, but not the
         1658  +# manufacturer.  We pick the logical manufacturer.
         1659  +vendor=unknown
         1660  +case $basic_machine in
         1661  +	*-unknown)
         1662  +		case $os in
         1663  +			-riscix*)
         1664  +				vendor=acorn
         1665  +				;;
         1666  +			-sunos*)
         1667  +				vendor=sun
         1668  +				;;
         1669  +			-cnk*|-aix*)
         1670  +				vendor=ibm
         1671  +				;;
         1672  +			-beos*)
         1673  +				vendor=be
         1674  +				;;
         1675  +			-hpux*)
         1676  +				vendor=hp
         1677  +				;;
         1678  +			-mpeix*)
         1679  +				vendor=hp
         1680  +				;;
         1681  +			-hiux*)
         1682  +				vendor=hitachi
         1683  +				;;
         1684  +			-unos*)
         1685  +				vendor=crds
         1686  +				;;
         1687  +			-dgux*)
         1688  +				vendor=dg
         1689  +				;;
         1690  +			-luna*)
         1691  +				vendor=omron
         1692  +				;;
         1693  +			-genix*)
         1694  +				vendor=ns
         1695  +				;;
         1696  +			-mvs* | -opened*)
         1697  +				vendor=ibm
         1698  +				;;
         1699  +			-os400*)
         1700  +				vendor=ibm
         1701  +				;;
         1702  +			-ptx*)
         1703  +				vendor=sequent
         1704  +				;;
         1705  +			-tpf*)
         1706  +				vendor=ibm
         1707  +				;;
         1708  +			-vxsim* | -vxworks* | -windiss*)
         1709  +				vendor=wrs
         1710  +				;;
         1711  +			-aux*)
         1712  +				vendor=apple
         1713  +				;;
         1714  +			-hms*)
         1715  +				vendor=hitachi
         1716  +				;;
         1717  +			-mpw* | -macos*)
         1718  +				vendor=apple
         1719  +				;;
         1720  +			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
         1721  +				vendor=atari
         1722  +				;;
         1723  +			-vos*)
         1724  +				vendor=stratus
         1725  +				;;
         1726  +		esac
         1727  +		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
         1728  +		;;
         1729  +esac
         1730  +
         1731  +echo $basic_machine$os
         1732  +exit
         1733  +
         1734  +# Local variables:
         1735  +# eval: (add-hook 'write-file-hooks 'time-stamp)
         1736  +# time-stamp-start: "timestamp='"
         1737  +# time-stamp-format: "%:y-%02m-%02d"
         1738  +# time-stamp-end: "'"
         1739  +# End:

Added autosetup/find-tclsh.

            1  +#!/bin/sh
            2  +# Looks for a suitable tclsh or jimsh in the PATH
            3  +# If not found, builds a bootstrap jimsh from source
            4  +d=`dirname "$0"`
            5  +{ "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0
            6  +PATH="$PATH:$d"; export PATH
            7  +for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do
            8  +	{ $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0
            9  +done
           10  +echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0"
           11  +for cc in ${CC_FOR_BUILD:-cc} gcc; do
           12  +	{ $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue
           13  +	"$d/jimsh0" "$d/test-tclsh" && exit 0
           14  +done
           15  +echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
           16  +echo false

Added autosetup/jimsh0.c.

more than 10,000 changes

Added autosetup/local.tcl.

            1  +# For this project, disable the pager for --help
            2  +set useropts(nopager) 1
            3  +
            4  +# Searches for a usable Tcl (prefer 8.6, 8.5, 8.4) in the given paths
            5  +# Returns a dictionary of the contents of the tclConfig.sh file, or
            6  +# empty if not found
            7  +proc parse-tclconfig-sh {args} {
            8  +	foreach p $args {
            9  +		# Allow pointing directly to the path containing tclConfig.sh
           10  +		if {[file exists $p/tclConfig.sh]} {
           11  +			return [parse-tclconfig-sh-file $p/tclConfig.sh]
           12  +		}
           13  +		# Some systems allow for multiple versions
           14  +		foreach libpath {lib/tcl8.6 lib/tcl8.5 lib/tcl8.4 lib/tcl tcl lib}  {
           15  +			if {[file exists $p/$libpath/tclConfig.sh]} {
           16  +				return [parse-tclconfig-sh-file $p/$libpath/tclConfig.sh]
           17  +			}
           18  +		}
           19  +	}
           20  +}
           21  +
           22  +proc parse-tclconfig-sh-file {filename} {
           23  +	foreach line [split [readfile $filename] \n] {
           24  +		if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} {
           25  +			set value [regsub -all {\$\{.*\}} $value ""]
           26  +			set tclconfig($name) [string trim $value ']
           27  +		}
           28  +	}
           29  +	return [array get tclconfig]
           30  +}

Added autosetup/system.tcl.

            1  +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
            2  +# All rights reserved
            3  +
            4  +# @synopsis:
            5  +#
            6  +# This module supports common system interrogation and options
            7  +# such as --host, --build, --prefix, and setting srcdir, builddir, and EXEXT.
            8  +#
            9  +# It also support the 'feature' naming convention, where searching
           10  +# for a feature such as sys/type.h defines HAVE_SYS_TYPES_H
           11  +#
           12  +module-options {
           13  +	host:host-alias =>		{a complete or partial cpu-vendor-opsys for the system where
           14  +							the application will run (defaults to the same value as --build)}
           15  +	build:build-alias =>	{a complete or partial cpu-vendor-opsys for the system
           16  +							where the application will be built (defaults to the
           17  +							result of running config.guess)}
           18  +	prefix:dir =>			{the target directory for the build (defaults to /usr/local)}
           19  +
           20  +	# These (hidden) options are supported for autoconf/automake compatibility
           21  +	exec-prefix:
           22  +	bindir:
           23  +	sbindir:
           24  +	includedir:
           25  +	mandir:
           26  +	infodir:
           27  +	libexecdir:
           28  +	datadir:
           29  +	libdir:
           30  +	sysconfdir:
           31  +	sharedstatedir:
           32  +	localstatedir:
           33  +	maintainer-mode=0
           34  +	dependency-tracking=0
           35  +}
           36  +
           37  +# Returns 1 if exists, or 0 if  not
           38  +#
           39  +proc check-feature {name code} {
           40  +	msg-checking "Checking for $name..."
           41  +	set r [uplevel 1 $code]
           42  +	define-feature $name $r
           43  +	if {$r} {
           44  +		msg-result "ok"
           45  +	} else {
           46  +		msg-result "not found"
           47  +	}
           48  +	return $r
           49  +}
           50  +
           51  +# @have-feature name ?default=0?
           52  +#
           53  +# Returns the value of the feature if defined, or $default if not.
           54  +# See 'feature-define-name' for how the feature name
           55  +# is translated into the define name.
           56  +#
           57  +proc have-feature {name {default 0}} {
           58  +	get-define [feature-define-name $name] $default
           59  +}
           60  +
           61  +# @define-feature name ?value=1?
           62  +#
           63  +# Sets the feature 'define' to the given value.
           64  +# See 'feature-define-name' for how the feature name
           65  +# is translated into the define name.
           66  +#
           67  +proc define-feature {name {value 1}} {
           68  +	define [feature-define-name $name] $value
           69  +}
           70  +
           71  +# @feature-checked name
           72  +#
           73  +# Returns 1 if the feature has been checked, whether true or not
           74  +#
           75  +proc feature-checked {name} {
           76  +	is-defined [feature-define-name $name]
           77  +}
           78  +
           79  +# @feature-define-name name ?prefix=HAVE_?
           80  +#
           81  +# Converts a name to the corresponding define,
           82  +# e.g. sys/stat.h becomes HAVE_SYS_STAT_H.
           83  +#
           84  +# Converts * to P and all non-alphanumeric to underscore.
           85  +#
           86  +proc feature-define-name {name {prefix HAVE_}} {
           87  +	string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _]
           88  +}
           89  +
           90  +# If $file doesn't exist, or it's contents are different than $buf,
           91  +# the file is written and $script is executed.
           92  +# Otherwise a "file is unchanged" message is displayed.
           93  +proc write-if-changed {file buf {script {}}} {
           94  +	set old [readfile $file ""]
           95  +	if {$old eq $buf && [file exists $file]} {
           96  +		msg-result "$file is unchanged"
           97  +	} else {
           98  +		writefile $file $buf\n
           99  +		uplevel 1 $script
          100  +	}
          101  +}
          102  +
          103  +# @make-template template ?outfile?
          104  +#
          105  +# Reads the input file <srcdir>/$template and writes the output file $outfile.
          106  +# If $outfile is blank/omitted, $template should end with ".in" which
          107  +# is removed to create the output file name.
          108  +#
          109  +# Each pattern of the form @define@ is replaced by the corresponding
          110  +# define, if it exists, or left unchanged if not.
          111  +# 
          112  +# The special value @srcdir@ is subsituted with the relative
          113  +# path to the source directory from the directory where the output
          114  +# file is created. Use @top_srcdir@ for the absolute path.
          115  +#
          116  +# Conditional sections may be specified as follows:
          117  +## @if name == value
          118  +## lines
          119  +## @else
          120  +## lines
          121  +## @endif
          122  +#
          123  +# Where 'name' is a defined variable name and @else is optional.
          124  +# If the expression does not match, all lines through '@endif' are ignored.
          125  +#
          126  +# The alternative forms may also be used:
          127  +## @if name
          128  +## @if name != value
          129  +#
          130  +# Where the first form is true if the variable is defined, but not empty or 0
          131  +#
          132  +# Currently these expressions can't be nested.
          133  +#
          134  +proc make-template {template {out {}}} {
          135  +	set infile [file join $::autosetup(srcdir) $template]
          136  +
          137  +	if {![file exists $infile]} {
          138  +		user-error "Template $template is missing"
          139  +	}
          140  +
          141  +	# Define this as late as possible
          142  +	define AUTODEPS $::autosetup(deps)
          143  +
          144  +	if {$out eq ""} {
          145  +		if {[file ext $template] ne ".in"} {
          146  +			autosetup-error "make_template $template has no target file and can't guess"
          147  +		}
          148  +		set out [file rootname $template]
          149  +	}
          150  +
          151  +	set outdir [file dirname $out]
          152  +
          153  +	# Make sure the directory exists
          154  +	file mkdir $outdir
          155  +
          156  +	# Set up srcdir to be relative to the target dir
          157  +	define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir]
          158  +
          159  +	set mapping {}
          160  +	foreach {n v} [array get ::define] {
          161  +		lappend mapping @$n@ $v
          162  +	}
          163  +	set result {}
          164  +	foreach line [split [readfile $infile] \n] {
          165  +		if {[info exists cond]} {
          166  +			set l [string trimright $line]
          167  +			if {$l eq "@endif"} {
          168  +				unset cond
          169  +				continue
          170  +			}
          171  +			if {$l eq "@else"} {
          172  +				set cond [expr {!$cond}]
          173  +				continue
          174  +			}
          175  +			if {$cond} {
          176  +				lappend result $line
          177  +			}
          178  +			continue
          179  +		}
          180  +		if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} {
          181  +			lassign $expression equal value
          182  +			set varval [get-define $name ""]
          183  +			if {$equal eq ""} {
          184  +				set cond [expr {$varval ni {"" 0}}]
          185  +			} else {
          186  +				set cond [expr {$varval eq $value}]
          187  +				if {$equal ne "=="} {
          188  +					set cond [expr {!$cond}]
          189  +				}
          190  +			}
          191  +			continue
          192  +		}
          193  +		lappend result $line
          194  +	}
          195  +	writefile $out [string map $mapping [join $result \n]]\n
          196  +
          197  +	msg-result "Created [relative-path $out] from [relative-path $template]"
          198  +}
          199  +
          200  +# build/host tuples and cross-compilation prefix
          201  +set build [opt-val build]
          202  +define build_alias $build
          203  +if {$build eq ""} {
          204  +	define build [config_guess]
          205  +} else {
          206  +	define build [config_sub $build]
          207  +}
          208  +
          209  +set host [opt-val host]
          210  +define host_alias $host
          211  +if {$host eq ""} {
          212  +	define host [get-define build]
          213  +	set cross ""
          214  +} else {
          215  +	define host [config_sub $host]
          216  +	set cross $host-
          217  +}
          218  +define cross [get-env CROSS $cross]
          219  +
          220  +set prefix [opt-val prefix /usr/local]
          221  +
          222  +# These are for compatibility with autoconf
          223  +define target [get-define host]
          224  +define prefix $prefix
          225  +define builddir $autosetup(builddir)
          226  +define srcdir $autosetup(srcdir)
          227  +# Allow this to come from the environment
          228  +define top_srcdir [get-env top_srcdir [get-define srcdir]]
          229  +
          230  +# autoconf supports all of these
          231  +set exec_prefix [opt-val exec-prefix $prefix]
          232  +define exec_prefix $exec_prefix
          233  +foreach {name defpath} {
          234  +	bindir /bin
          235  +	sbindir /sbin
          236  +	libexecdir /libexec
          237  +	libdir /lib
          238  +} {
          239  +	define $name [opt-val $name $exec_prefix$defpath]
          240  +}
          241  +foreach {name defpath} {
          242  +	datadir /share
          243  +	sysconfdir /etc
          244  +	sharedstatedir /com
          245  +	localstatedir /var
          246  +	infodir /share/info
          247  +	mandir /share/man
          248  +	includedir /include
          249  +} {
          250  +	define $name [opt-val $name $prefix$defpath]
          251  +}
          252  +
          253  +define SHELL [get-env SHELL [find-an-executable sh bash ksh]]
          254  +
          255  +# Windows vs. non-Windows
          256  +switch -glob -- [get-define host] {
          257  +	*-*-ming* - *-*-cygwin {
          258  +		define-feature windows
          259  +		define EXEEXT .exe
          260  +	}
          261  +	default {
          262  +		define EXEEXT ""
          263  +	}
          264  +}
          265  +
          266  +# Display
          267  +msg-result "Host System...[get-define host]"
          268  +msg-result "Build System...[get-define build]"

Added autosetup/test-tclsh.

            1  +# A small Tcl script to verify that the chosen
            2  +# interpreter works. Sometimes we might e.g. pick up
            3  +# an interpreter for a different arch.
            4  +# Outputs the full path to the interpreter
            5  +
            6  +if {[catch {info version} version] == 0} {
            7  +	# This is Jim Tcl
            8  +	if {$version >= 0.72} {
            9  +		# Ensure that regexp works
           10  +		regexp (a.*?) a
           11  +		puts [info nameofexecutable]
           12  +		exit 0
           13  +	}
           14  +} elseif {[catch {info tclversion} version] == 0} {
           15  +	if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} {
           16  +		puts [info nameofexecutable]
           17  +		exit 0
           18  +	}
           19  +}
           20  +exit 1

Deleted ci_cvs.txt.

     1         -===============================================================================
     2         -
     3         -First experimental codes ...
     4         -
     5         -tools/import-cvs.tcl
     6         -tools/lib/rcsparser.tcl
     7         -
     8         -No actual import, right now only working on getting csets right. The
     9         -code uses CVSROOT/history as foundation, and augments that with data
    10         -from the individual RCS files (commit messages).
    11         -
    12         -Statistics of a run ...
    13         -	3516 csets.
    14         -
    15         -	1545 breaks on user change
    16         -	 558 breaks on file duplicate
    17         -	  13 breaks on branch/trunk change
    18         -	1402 breaks on commit message change
    19         -
    20         -Time statistics ...
    21         -	3297 were processed in <= 1 seconds (93.77%)
    22         -	 217 were processed in between 2 seconds and 14 minutes.
    23         -	   1 was  processed in ~41 minutes
    24         -	   1 was  processed in ~22 hours
    25         -
    26         -Time fuzz - Differences between csets range from 0 seconds to 66
    27         -days. Needs stats analysis to see if there is an obvious break. Even
    28         -so the times within csets and between csets overlap a great deal,
    29         -making time a bad criterium for cset separation, IMHO.
    30         -
    31         -Leaving that topic, back to the current cset separator ...
    32         -
    33         -It has a problem:
    34         -	The history file is not starting at the root!
    35         -
    36         -Examples:
    37         -	The first three changesets are
    38         -
    39         -	=============================/user
    40         -	M {Wed Nov 22 09:28:49 AM PST 2000} ericm 1.4 tcllib/modules/ftpd/ChangeLog
    41         -	M {Wed Nov 22 09:28:49 AM PST 2000} ericm 1.7 tcllib/modules/ftpd/ftpd.tcl
    42         -	files: 2
    43         -	delta: 0
    44         -	range: 0 seconds
    45         -	=============================/cmsg
    46         -	M {Wed Nov 29 02:14:33 PM PST 2000} ericm 1.3 tcllib/aclocal.m4
    47         -	files: 1
    48         -	delta: 
    49         -	range: 0 seconds
    50         -	=============================/cmsg
    51         -	M {Sun Feb 04 12:28:35 AM PST 2001} ericm 1.9 tcllib/modules/mime/ChangeLog
    52         -	M {Sun Feb 04 12:28:35 AM PST 2001} ericm 1.12 tcllib/modules/mime/mime.tcl
    53         -	files: 2
    54         -	delta: 0
    55         -	range: 0 seconds
    56         -
    57         -All csets modify files which already have several revisions. We have
    58         -no csets from before that in the history, but these csets are in the
    59         -RCS files.
    60         -
    61         -I wonder, is SF maybe removing old entries from the history when it
    62         -grows too large ?
    63         -
    64         -This also affects incremental import ... I cannot assume that the
    65         -history always grows. It may shrink ... I cannot keep an offset, will
    66         -have to record the time of the last entry, or even the full entry
    67         -processed last, to allow me to skip ahead to anything not known yet.
    68         -
    69         -I might have to try to implement the algorithm outlined below,
    70         -matching the revision trees of the individual RCS files to each other
    71         -to form the global tree of revisions. Maybe we can use the history to
    72         -help in the matchup, for the parts where we do have it.
    73         -
    74         -Wait. This might be easier ... Take the delta information from the RCS
    75         -files and generate a fake history ... Actually, this might even allow
    76         -us to create a total history ... No, not quite, the merge entries the
    77         -actual history may contain will be missing. These we can mix in from
    78         -the actual history, as much as we have.
    79         -
    80         -Still, lets try that, a fake history, and then run this script on it
    81         -to see if/where are differences.
    82         -
    83         -===============================================================================
    84         -
    85         -
    86         -Notes about CVS import, regarding CVS.
    87         -
    88         -- Problem: CVS does not really track changesets, but only individual
    89         -  revisions of files. To recover changesets it is necessary to look at
    90         -  author, branch, timestamp information, and the commit messages. Even
    91         -  so this is only heuristic, not foolproof.
    92         -
    93         -  Existing tool: cvsps.
    94         -
    95         -  Processes the output of 'cvs log' to recover changesets. Problem:
    96         -  Sees only a linear list of revisions, does not see branchpoints,
    97         -  etc. Cannot use the tree structure to help in making the decisions.
    98         -
    99         -- Problem: CVS does not track merge-points at all. Recovery through
   100         -  heuristics is brittle at best, looking for keywords in commit
   101         -  messages which might indicate that a branch was merged with some
   102         -  other.
   103         -
   104         -
   105         -Ideas regarding an algorithm to recover changesets.
   106         -
   107         -Key feature: Uses the per-file revision trees to help in uncovering
   108         -the underlying changesets and global revision tree G.
   109         -
   110         -The per-file revision tree for a file X is in essence the global
   111         -revision tree with all nodes not pertaining to X removed from it. In
   112         -the reverse this allows us to built up the global revision tree from
   113         -the per-file trees by matching nodes to each other and extending.
   114         -
   115         -Start with the per file revision tree of a single file as initial
   116         -approximation of the global tree. All nodes of this tree refer to the
   117         -revision of the file belonging to it, and through that the file
   118         -itself. At each step the global tree contains the nodes for a finite
   119         -set of files, and all nodes in the tree refer to revisions of all
   120         -files in the set, making the mapping total.
   121         -
   122         -To add a file X to the tree take the per-file revision tree R and
   123         -performs the following actions:
   124         -
   125         -- For each node N in R use the tuple <author, branch, commit message>
   126         -  to identify a set of nodes in G which may match N. Use the timestamp
   127         -  to locate the node nearest in time.
   128         -
   129         -- This process will leave nodes in N unmapped. If there are unmapped
   130         -  nodes which have no neighbouring mapped nodes we have to
   131         -  abort.
   132         -
   133         -  Otherwise take the nodes which have mapped neighbours. Trace the
   134         -  edges and see which of these nodes are connected in the local
   135         -  tree. Then look at the identified neighbours and trace their
   136         -  connections.
   137         -
   138         -  If two global nodes have a direct connection, but a multi-edge
   139         -  connection in the local tree insert global nodes mapping to the
   140         -  local nodes and map them together. This expands the global tree to
   141         -  hold the revisions added by the new file.
   142         -
   143         -  Otherwise, both sides have multi-edge connections then abort. This
   144         -  looks like a merge of two different branches, but there are no such
   145         -  in CVS ... Wait ... sort the nodes over time and fit the new nodes
   146         -  in between the other nodes, per the timestamps. We have overlapping
   147         -  / alternating changes to one file and others.
   148         -
   149         -  A last possibility is that a node is only connected to a mapped
   150         -  parent. This may be a new branch, or again an alternating change on
   151         -  the given line. Symbols on the revisions will help to map this.
   152         -
   153         -- We now have an extended global tree which incorporates the revisions
   154         -  of the new file. However new nodes will refer only to the new file,
   155         -  and old nodes may not refer to the new file. This has to be fixed,
   156         -  as all nodes have to refer to all files.
   157         -
   158         -  Run over the tree and look at each parent/child pair. If a file is
   159         -  not referenced in the child, but the parent, then copy a reference
   160         -  to the file revision on the parent forward to the child. This
   161         -  signals that the file did not change in the given revision.
   162         -
   163         -- After all files have been integrated in this manner we have global
   164         -  revision tree capturing all changesets, including the unchanged
   165         -  files per changeset.
   166         -
   167         -
   168         -This algorithm has to be refined to also take Attic/ files into
   169         -account.
   170         -
   171         --------------------------------------------------------------------------
   172         -
   173         -Two archive files mapping to the same user file. How are they
   174         -interleaved ?
   175         -
   176         -(a)	sqlite/src/os_unix.h,v
   177         -(b)	sqlite/src/Attic/os_unix.h,v
   178         -
   179         -Problem: Max version of (a) is 1.9
   180         -	 Max version of (b) is 1.11
   181         -	 cvs co 1.10 -> no longer in the repository.
   182         -
   183         -This seems to indicate that the non-Attic file is relevant.
   184         -
   185         ---------------------------------------------------------------------------
   186         -
   187         -tcllib - more problems - tklib/pie.tcl,v -
   188         -
   189         -invalid change text in
   190         -/home/aku/Projects/Tcl/Fossil/Devel/Examples/cvs-tcllib/tklib/modules/tkpiechart/pie.tcl,v
   191         -
   192         -Possibly braces ?

Deleted ci_fossil.txt.

     1         -
     2         -To perform CVS imports for fossil we need at least the ability to
     3         -parse CVS files, i.e. RCS files, with slight differences.
     4         -
     5         -For the general architecture of the import facility we have two major
     6         -paths to choose between.
     7         -
     8         -One is to use an external tool which processes a cvs repository and
     9         -drives fossil through its CLI to insert the found changesets.
    10         -
    11         -The other is to integrate the whole facility into the fossil binary
    12         -itself.
    13         -
    14         -I dislike the second choice. It may be faster, as the implementation
    15         -can use all internal functionality of fossil to perform the import,
    16         -however it will also bloat the binary with functionality not needed
    17         -most of the time. Which becomes especially obvious if more importers
    18         -are to be written, like for monotone, bazaar, mercurial, bitkeeper,
    19         -git, SVN, Arc, etc. Keeping all this out of the core fossil binary is
    20         -IMHO more beneficial in the long term, also from a maintenance point
    21         -of view. The tools can evolve separately. Especially important for CVS
    22         -as it will have to deal with lots of broken repositories, all
    23         -different.
    24         -
    25         -However, nothing speaks against looking for common parts in all
    26         -possible import tools, and having these in the fossil core, as a
    27         -general backend all importer may use. Something like that has already
    28         -been proposed: The deconstruct|reconstruct methods. For us, actually
    29         -only reconstruct is important. Taking an unordered collection of files
    30         -(data, and manifests) it generates a proper fossil repository.  With
    31         -that method implemented all import tools only have to generate the
    32         -necessary collection and then leave the main work of filling the
    33         -database to fossil itself.
    34         -
    35         -The disadvantage of this method is however that it will gobble up a
    36         -lot of temporary space in the filesystem to hold all unique revisions
    37         -of all files in their expanded form.
    38         -
    39         -It might be worthwhile to consider an extension of 'reconstruct' which
    40         -is able to incrementally add a set of files to an existing fossil
    41         -repository already containing revisions. In that case the import tool
    42         -can be changed to incrementally generate the collection for a
    43         -particular revision, import it, and iterate over all revisions in the
    44         -origin repository. This is of course also dependent on the origin
    45         -repository itself, how well it supports such incremental export.
    46         -
    47         -This also leads to a possible method for performing the import using
    48         -only existing functionality ('reconstruct' has not been implemented
    49         -yet). Instead generating an unordered collection for each revision
    50         -generate a properly setup workspace, simply commit it. This will
    51         -require use of rm, add and update methods as well, to remove old and
    52         -enter new files, and point the fossil repository to the correct parent
    53         -revision from the new revision is derived.
    54         -
    55         -The relative efficiency (in time) of these incremental methods versus
    56         -importing a complete collection of files encoding the entire origin
    57         -repository however is not clear.
    58         -
    59         -----------------------------------
    60         -
    61         -reconstruct
    62         -
    63         -The core logic for handling content is in the file "content.c", in
    64         -particular the functions 'content_put' and 'content_deltify'. One of
    65         -the main users of these functions is in the file "checkin.c", see the
    66         -function 'commit_cmd'.
    67         -
    68         -The logic is clear. The new modified files are simply stored without
    69         -delta-compression, using 'content_put'. And should fosssil have an id
    70         -for the _previous_ revision of the committed file it uses
    71         -'content_deltify' to convert the already stored data for that revision
    72         -into a delta with the just stored new revision as origin.
    73         -
    74         -In other words, fossil produces reverse deltas, with leaf revisions
    75         -stored just zip-compressed (plain) and older revisions using both zip-
    76         -and delta-compression.
    77         -
    78         -Of note is that the underlying logic in 'content_deltify' gives up on
    79         -delta compression if the involved files are either not large enough,
    80         -or if the achieved compression factor was not high enough. In that
    81         -case the old revision of the file is left plain.
    82         -
    83         -The scheme can thus be called a 'truncated reverse delta'.
    84         -
    85         -The manifest is created and committed after the modified files. It
    86         -uses the same logic as for the regular files. The new leaf is stored
    87         -plain, and storage of the parent manifest is modified to be a delta
    88         -with the current as origin.
    89         -
    90         -Further note that for a checkin of a merge result oonly the primary
    91         -parent is modified in that way. The secondary parent, the one merged
    92         -into the current revision is not touched. I.e. from the storage layer
    93         -point of view this revision is still a leaf and the data is kept
    94         -stored plain, not delta-compressed.
    95         -
    96         -
    97         -
    98         -Now the "reconstruct" can be done like so:
    99         -
   100         -- Scan the files in the indicated directory, and look for a manifest.
   101         -
   102         -- When the manifest has been found parse its contents and follow the
   103         -  chain of parent links to locate the root manifest (no parent).
   104         -
   105         -- Import the files referenced by the root manifest, then the manifest
   106         -  itself. This can be done using a modified form of the 'commit_cmd'
   107         -  which does not have to construct a manifest on its own from vfile,
   108         -  vmerge, etc.
   109         -
   110         -- After that recursively apply the import of the previous step to the
   111         -  children of the root, and so on.
   112         -
   113         -For an incremental "reconstruct" the collection of files would not be
   114         -a single tree with a root, but a forest, and the roots to look for are
   115         -not manifests without parent, but with a parent which is already
   116         -present in the repository. After one such root has been found and
   117         -processed the unprocessed files have to be searched further for more
   118         -roots, and only if no such are found anymore will the remaining files
   119         -be considered as superfluous.
   120         -
   121         -We can use the functions in "manifest.c" for the parsing and following
   122         -the parental chain.
   123         -
   124         -Hm. But we have no direct child information. So the above algorithm
   125         -has to be modified, we have to scan all manifests before we start
   126         -importing, and we have to create a reverse index, from manifest to
   127         -children so that we can perform the import from root to leaves.

Added compat/zlib/CMakeLists.txt.

            1  +cmake_minimum_required(VERSION 2.4.4)
            2  +set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
            3  +
            4  +project(zlib C)
            5  +
            6  +set(VERSION "1.2.7")
            7  +
            8  +set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables")
            9  +set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries")
           10  +set(INSTALL_INC_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Installation directory for headers")
           11  +set(INSTALL_MAN_DIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Installation directory for manual pages")
           12  +set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
           13  +
           14  +include(CheckTypeSize)
           15  +include(CheckFunctionExists)
           16  +include(CheckIncludeFile)
           17  +include(CheckCSourceCompiles)
           18  +enable_testing()
           19  +
           20  +check_include_file(sys/types.h HAVE_SYS_TYPES_H)
           21  +check_include_file(stdint.h    HAVE_STDINT_H)
           22  +check_include_file(stddef.h    HAVE_STDDEF_H)
           23  +
           24  +#
           25  +# Check to see if we have large file support
           26  +#
           27  +set(CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE64_SOURCE=1)
           28  +# We add these other definitions here because CheckTypeSize.cmake
           29  +# in CMake 2.4.x does not automatically do so and we want
           30  +# compatibility with CMake 2.4.x.
           31  +if(HAVE_SYS_TYPES_H)
           32  +    list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_TYPES_H)
           33  +endif()
           34  +if(HAVE_STDINT_H)
           35  +    list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDINT_H)
           36  +endif()
           37  +if(HAVE_STDDEF_H)
           38  +    list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDDEF_H)
           39  +endif()
           40  +check_type_size(off64_t OFF64_T)
           41  +if(HAVE_OFF64_T)
           42  +   add_definitions(-D_LARGEFILE64_SOURCE=1)
           43  +endif()
           44  +set(CMAKE_REQUIRED_DEFINITIONS) # clear variable
           45  +
           46  +#
           47  +# Check for fseeko
           48  +#
           49  +check_function_exists(fseeko HAVE_FSEEKO)
           50  +if(NOT HAVE_FSEEKO)
           51  +    add_definitions(-DNO_FSEEKO)
           52  +endif()
           53  +
           54  +#
           55  +# Check for unistd.h
           56  +#
           57  +check_include_file(unistd.h Z_HAVE_UNISTD_H)
           58  +
           59  +if(MSVC)
           60  +    set(CMAKE_DEBUG_POSTFIX "d")
           61  +    add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
           62  +    add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
           63  +    include_directories(${CMAKE_CURRENT_SOURCE_DIR})
           64  +endif()
           65  +
           66  +if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
           67  +    # If we're doing an out of source build and the user has a zconf.h
           68  +    # in their source tree...
           69  +    if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h)
           70  +        message(STATUS "Renaming")
           71  +        message(STATUS "    ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h")
           72  +        message(STATUS "to 'zconf.h.included' because this file is included with zlib")
           73  +        message(STATUS "but CMake generates it automatically in the build directory.")
           74  +        file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.included)
           75  +  endif()
           76  +endif()
           77  +
           78  +set(ZLIB_PC ${CMAKE_CURRENT_BINARY_DIR}/zlib.pc)
           79  +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zlib.pc.cmakein
           80  +		${ZLIB_PC} @ONLY)
           81  +configure_file(	${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.cmakein
           82  +		${CMAKE_CURRENT_BINARY_DIR}/zconf.h @ONLY)
           83  +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR})
           84  +
           85  +
           86  +#============================================================================
           87  +# zlib
           88  +#============================================================================
           89  +
           90  +set(ZLIB_PUBLIC_HDRS
           91  +    ${CMAKE_CURRENT_BINARY_DIR}/zconf.h
           92  +    zlib.h
           93  +)
           94  +set(ZLIB_PRIVATE_HDRS
           95  +    crc32.h
           96  +    deflate.h
           97  +    gzguts.h
           98  +    inffast.h
           99  +    inffixed.h
          100  +    inflate.h
          101  +    inftrees.h
          102  +    trees.h
          103  +    zutil.h
          104  +)
          105  +set(ZLIB_SRCS
          106  +    adler32.c
          107  +    compress.c
          108  +    crc32.c
          109  +    deflate.c
          110  +    gzclose.c
          111  +    gzlib.c
          112  +    gzread.c
          113  +    gzwrite.c
          114  +    inflate.c
          115  +    infback.c
          116  +    inftrees.c
          117  +    inffast.c
          118  +    trees.c
          119  +    uncompr.c
          120  +    zutil.c
          121  +)
          122  +
          123  +if(NOT MINGW)
          124  +    set(ZLIB_SRCS ${ZLIB_SRCS}
          125  +        win32/zlib1.rc # If present will override custom build rule below.
          126  +    )
          127  +endif()
          128  +
          129  +# parse the full version number from zlib.h and include in ZLIB_FULL_VERSION
          130  +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/zlib.h _zlib_h_contents)
          131  +string(REGEX REPLACE ".*#define[ \t]+ZLIB_VERSION[ \t]+\"([-0-9A-Za-z.]+)\".*"
          132  +    "\\1" ZLIB_FULL_VERSION ${_zlib_h_contents})
          133  +
          134  +if(MINGW)
          135  +    # This gets us DLL resource information when compiling on MinGW.
          136  +    if(NOT CMAKE_RC_COMPILER)
          137  +        SET(CMAKE_RC_COMPILER windres.exe)
          138  +    endif()
          139  +
          140  +    add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
          141  +                       COMMAND ${CMAKE_RC_COMPILER}
          142  +                            -D GCC_WINDRES
          143  +                            -I ${CMAKE_CURRENT_SOURCE_DIR}
          144  +                            -I ${CMAKE_CURRENT_BINARY_DIR}
          145  +                            -o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
          146  +                            -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc)
          147  +    set(ZLIB_SRCS ${ZLIB_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
          148  +endif(MINGW)
          149  +
          150  +add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
          151  +add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
          152  +set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
          153  +set_target_properties(zlib PROPERTIES SOVERSION 1)
          154  +
          155  +if(NOT CYGWIN)
          156  +    # This property causes shared libraries on Linux to have the full version
          157  +    # encoded into their final filename.  We disable this on Cygwin because
          158  +    # it causes cygz-${ZLIB_FULL_VERSION}.dll to be created when cygz.dll
          159  +    # seems to be the default.
          160  +    #
          161  +    # This has no effect with MSVC, on that platform the version info for
          162  +    # the DLL comes from the resource file win32/zlib1.rc
          163  +    set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION})
          164  +endif()
          165  +
          166  +if(UNIX)
          167  +    # On unix-like platforms the library is almost always called libz
          168  +   set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)
          169  +   set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/zlib.map")
          170  +elseif(BUILD_SHARED_LIBS AND WIN32)
          171  +    # Creates zlib1.dll when building shared library version
          172  +    set_target_properties(zlib PROPERTIES SUFFIX "1.dll")
          173  +endif()
          174  +
          175  +if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL )
          176  +    install(TARGETS zlib zlibstatic
          177  +        RUNTIME DESTINATION "${INSTALL_BIN_DIR}"
          178  +        ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
          179  +        LIBRARY DESTINATION "${INSTALL_LIB_DIR}" )
          180  +endif()
          181  +if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL )
          182  +    install(FILES ${ZLIB_PUBLIC_HDRS} DESTINATION "${INSTALL_INC_DIR}")
          183  +endif()
          184  +if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
          185  +    install(FILES zlib.3 DESTINATION "${INSTALL_MAN_DIR}/man3")
          186  +endif()
          187  +if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
          188  +    install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
          189  +endif()
          190  +
          191  +#============================================================================
          192  +# Example binaries
          193  +#============================================================================
          194  +
          195  +add_executable(example test/example.c)
          196  +target_link_libraries(example zlib)
          197  +add_test(example example)
          198  +
          199  +add_executable(minigzip test/minigzip.c)
          200  +target_link_libraries(minigzip zlib)
          201  +
          202  +if(HAVE_OFF64_T)
          203  +    add_executable(example64 test/example.c)
          204  +    target_link_libraries(example64 zlib)
          205  +    set_target_properties(example64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
          206  +    add_test(example64 example64)
          207  +
          208  +    add_executable(minigzip64 test/minigzip.c)
          209  +    target_link_libraries(minigzip64 zlib)
          210  +    set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
          211  +endif()

Added compat/zlib/ChangeLog.

            1  +
            2  +                ChangeLog file for zlib
            3  +
            4  +Changes in 1.2.7 (2 May 2012)
            5  +- Replace use of memmove() with a simple copy for portability
            6  +- Test for existence of strerror
            7  +- Restore gzgetc_ for backward compatibility with 1.2.6
            8  +- Fix build with non-GNU make on Solaris
            9  +- Require gcc 4.0 or later on Mac OS X to use the hidden attribute
           10  +- Include unistd.h for Watcom C
           11  +- Use __WATCOMC__ instead of __WATCOM__
           12  +- Do not use the visibility attribute if NO_VIZ defined
           13  +- Improve the detection of no hidden visibility attribute
           14  +- Avoid using __int64 for gcc or solo compilation
           15  +- Cast to char * in gzprintf to avoid warnings [Zinser]
           16  +- Fix make_vms.com for VAX [Zinser]
           17  +- Don't use library or built-in byte swaps
           18  +- Simplify test and use of gcc hidden attribute
           19  +- Fix bug in gzclose_w() when gzwrite() fails to allocate memory
           20  +- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen()
           21  +- Fix bug in test/minigzip.c for configure --solo
           22  +- Fix contrib/vstudio project link errors [Mohanathas]
           23  +- Add ability to choose the builder in make_vms.com [Schweda]
           24  +- Add DESTDIR support to mingw32 win32/Makefile.gcc
           25  +- Fix comments in win32/Makefile.gcc for proper usage
           26  +- Allow overriding the default install locations for cmake
           27  +- Generate and install the pkg-config file with cmake
           28  +- Build both a static and a shared version of zlib with cmake
           29  +- Include version symbols for cmake builds
           30  +- If using cmake with MSVC, add the source directory to the includes
           31  +- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta]
           32  +- Move obsolete emx makefile to old [Truta]
           33  +- Allow the use of -Wundef when compiling or using zlib
           34  +- Avoid the use of the -u option with mktemp
           35  +- Improve inflate() documentation on the use of Z_FINISH
           36  +- Recognize clang as gcc
           37  +- Add gzopen_w() in Windows for wide character path names
           38  +- Rename zconf.h in CMakeLists.txt to move it out of the way
           39  +- Add source directory in CMakeLists.txt for building examples
           40  +- Look in build directory for zlib.pc in CMakeLists.txt
           41  +- Remove gzflags from zlibvc.def in vc9 and vc10
           42  +- Fix contrib/minizip compilation in the MinGW environment
           43  +- Update ./configure for Solaris, support --64 [Mooney]
           44  +- Remove -R. from Solaris shared build (possible security issue)
           45  +- Avoid race condition for parallel make (-j) running example
           46  +- Fix type mismatch between get_crc_table() and crc_table
           47  +- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler]
           48  +- Fix the path to zlib.map in CMakeLists.txt
           49  +- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe]
           50  +- Add instructions to win32/Makefile.gcc for shared install [Torri]
           51  +
           52  +Changes in 1.2.6.1 (12 Feb 2012)
           53  +- Avoid the use of the Objective-C reserved name "id"
           54  +- Include io.h in gzguts.h for Microsoft compilers
           55  +- Fix problem with ./configure --prefix and gzgetc macro
           56  +- Include gz_header definition when compiling zlib solo
           57  +- Put gzflags() functionality back in zutil.c
           58  +- Avoid library header include in crc32.c for Z_SOLO
           59  +- Use name in GCC_CLASSIC as C compiler for coverage testing, if set
           60  +- Minor cleanup in contrib/minizip/zip.c [Vollant]
           61  +- Update make_vms.com [Zinser]
           62  +- Remove unnecessary gzgetc_ function
           63  +- Use optimized byte swap operations for Microsoft and GNU [Snyder]
           64  +- Fix minor typo in zlib.h comments [Rzesniowiecki]
           65  +
           66  +Changes in 1.2.6 (29 Jan 2012)
           67  +- Update the Pascal interface in contrib/pascal
           68  +- Fix function numbers for gzgetc_ in zlibvc.def files
           69  +- Fix configure.ac for contrib/minizip [Schiffer]
           70  +- Fix large-entry detection in minizip on 64-bit systems [Schiffer]
           71  +- Have ./configure use the compiler return code for error indication
           72  +- Fix CMakeLists.txt for cross compilation [McClure]
           73  +- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes]
           74  +- Fix compilation of contrib/minizip on FreeBSD [Marquez]
           75  +- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath]
           76  +- Include io.h for Turbo C / Borland C on all platforms [Truta]
           77  +- Make version explicit in contrib/minizip/configure.ac [Bosmans]
           78  +- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant]
           79  +- Minor cleanup up contrib/minizip/unzip.c [Vollant]
           80  +- Fix bug when compiling minizip with C++ [Vollant]
           81  +- Protect for long name and extra fields in contrib/minizip [Vollant]
           82  +- Avoid some warnings in contrib/minizip [Vollant]
           83  +- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip
           84  +- Add missing libs to minizip linker command
           85  +- Add support for VPATH builds in contrib/minizip
           86  +- Add an --enable-demos option to contrib/minizip/configure
           87  +- Add the generation of configure.log by ./configure
           88  +- Exit when required parameters not provided to win32/Makefile.gcc
           89  +- Have gzputc return the character written instead of the argument
           90  +- Use the -m option on ldconfig for BSD systems [Tobias]
           91  +- Correct in zlib.map when deflateResetKeep was added
           92  +
           93  +Changes in 1.2.5.3 (15 Jan 2012)
           94  +- Restore gzgetc function for binary compatibility
           95  +- Do not use _lseeki64 under Borland C++ [Truta]
           96  +- Update win32/Makefile.msc to build test/*.c [Truta]
           97  +- Remove old/visualc6 given CMakefile and other alternatives
           98  +- Update AS400 build files and documentation [Monnerat]
           99  +- Update win32/Makefile.gcc to build test/*.c [Truta]
          100  +- Permit stronger flushes after Z_BLOCK flushes
          101  +- Avoid extraneous empty blocks when doing empty flushes
          102  +- Permit Z_NULL arguments to deflatePending
          103  +- Allow deflatePrime() to insert bits in the middle of a stream
          104  +- Remove second empty static block for Z_PARTIAL_FLUSH
          105  +- Write out all of the available bits when using Z_BLOCK
          106  +- Insert the first two strings in the hash table after a flush
          107  +
          108  +Changes in 1.2.5.2 (17 Dec 2011)
          109  +- fix ld error: unable to find version dependency 'ZLIB_1.2.5'
          110  +- use relative symlinks for shared libs
          111  +- Avoid searching past window for Z_RLE strategy
          112  +- Assure that high-water mark initialization is always applied in deflate
          113  +- Add assertions to fill_window() in deflate.c to match comments
          114  +- Update python link in README
          115  +- Correct spelling error in gzread.c
          116  +- Fix bug in gzgets() for a concatenated empty gzip stream
          117  +- Correct error in comment for gz_make()
          118  +- Change gzread() and related to ignore junk after gzip streams
          119  +- Allow gzread() and related to continue after gzclearerr()
          120  +- Allow gzrewind() and gzseek() after a premature end-of-file
          121  +- Simplify gzseek() now that raw after gzip is ignored
          122  +- Change gzgetc() to a macro for speed (~40% speedup in testing)
          123  +- Fix gzclose() to return the actual error last encountered
          124  +- Always add large file support for windows
          125  +- Include zconf.h for windows large file support
          126  +- Include zconf.h.cmakein for windows large file support
          127  +- Update zconf.h.cmakein on make distclean
          128  +- Merge vestigial vsnprintf determination from zutil.h to gzguts.h
          129  +- Clarify how gzopen() appends in zlib.h comments
          130  +- Correct documentation of gzdirect() since junk at end now ignored
          131  +- Add a transparent write mode to gzopen() when 'T' is in the mode
          132  +- Update python link in zlib man page
          133  +- Get inffixed.h and MAKEFIXED result to match
          134  +- Add a ./config --solo option to make zlib subset with no libary use
          135  +- Add undocumented inflateResetKeep() function for CAB file decoding
          136  +- Add --cover option to ./configure for gcc coverage testing
          137  +- Add #define ZLIB_CONST option to use const in the z_stream interface
          138  +- Add comment to gzdopen() in zlib.h to use dup() when using fileno()
          139  +- Note behavior of uncompress() to provide as much data as it can
          140  +- Add files in contrib/minizip to aid in building libminizip
          141  +- Split off AR options in Makefile.in and configure
          142  +- Change ON macro to Z_ARG to avoid application conflicts
          143  +- Facilitate compilation with Borland C++ for pragmas and vsnprintf
          144  +- Include io.h for Turbo C / Borland C++
          145  +- Move example.c and minigzip.c to test/
          146  +- Simplify incomplete code table filling in inflate_table()
          147  +- Remove code from inflate.c and infback.c that is impossible to execute
          148  +- Test the inflate code with full coverage
          149  +- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw)
          150  +- Add deflateResetKeep and fix inflateResetKeep to retain dictionary
          151  +- Fix gzwrite.c to accommodate reduced memory zlib compilation
          152  +- Have inflate() with Z_FINISH avoid the allocation of a window
          153  +- Do not set strm->adler when doing raw inflate
          154  +- Fix gzeof() to behave just like feof() when read is not past end of file
          155  +- Fix bug in gzread.c when end-of-file is reached
          156  +- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF
          157  +- Document gzread() capability to read concurrently written files
          158  +- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo]
          159  +
          160  +Changes in 1.2.5.1 (10 Sep 2011)
          161  +- Update FAQ entry on shared builds (#13)
          162  +- Avoid symbolic argument to chmod in Makefile.in
          163  +- Fix bug and add consts in contrib/puff [Oberhumer]
          164  +- Update contrib/puff/zeros.raw test file to have all block types
          165  +- Add full coverage test for puff in contrib/puff/Makefile
          166  +- Fix static-only-build install in Makefile.in
          167  +- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno]
          168  +- Add libz.a dependency to shared in Makefile.in for parallel builds
          169  +- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out
          170  +- Replace $(...) with `...` in configure for non-bash sh [Bowler]
          171  +- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen]
          172  +- Add solaris* to Linux* in configure to allow gcc use [Groffen]
          173  +- Add *bsd* to Linux* case in configure [Bar-Lev]
          174  +- Add inffast.obj to dependencies in win32/Makefile.msc
          175  +- Correct spelling error in deflate.h [Kohler]
          176  +- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc
          177  +- Add test to configure for GNU C looking for gcc in output of $cc -v
          178  +- Add zlib.pc generation to win32/Makefile.gcc [Weigelt]
          179  +- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not
          180  +- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense
          181  +- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser)
          182  +- Make stronger test in zconf.h to include unistd.h for LFS
          183  +- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack]
          184  +- Fix zlib.h LFS support when Z_PREFIX used
          185  +- Add updated as400 support (removed from old) [Monnerat]
          186  +- Avoid deflate sensitivity to volatile input data
          187  +- Avoid division in adler32_combine for NO_DIVIDE
          188  +- Clarify the use of Z_FINISH with deflateBound() amount of space
          189  +- Set binary for output file in puff.c
          190  +- Use u4 type for crc_table to avoid conversion warnings
          191  +- Apply casts in zlib.h to avoid conversion warnings
          192  +- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller]
          193  +- Improve inflateSync() documentation to note indeterminancy
          194  +- Add deflatePending() function to return the amount of pending output
          195  +- Correct the spelling of "specification" in FAQ [Randers-Pehrson]
          196  +- Add a check in configure for stdarg.h, use for gzprintf()
          197  +- Check that pointers fit in ints when gzprint() compiled old style
          198  +- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler]
          199  +- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt]
          200  +- Add debug records in assmebler code [Londer]
          201  +- Update RFC references to use http://tools.ietf.org/html/... [Li]
          202  +- Add --archs option, use of libtool to configure for Mac OS X [Borstel]
          203  +
          204  +Changes in 1.2.5 (19 Apr 2010)
          205  +- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev]
          206  +- Default to libdir as sharedlibdir in configure [Nieder]
          207  +- Update copyright dates on modified source files
          208  +- Update trees.c to be able to generate modified trees.h
          209  +- Exit configure for MinGW, suggesting win32/Makefile.gcc
          210  +- Check for NULL path in gz_open [Homurlu]
          211  +
          212  +Changes in 1.2.4.5 (18 Apr 2010)
          213  +- Set sharedlibdir in configure [Torok]
          214  +- Set LDFLAGS in Makefile.in [Bar-Lev]
          215  +- Avoid mkdir objs race condition in Makefile.in [Bowler]
          216  +- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays
          217  +- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C
          218  +- Don't use hidden attribute when it is a warning generator (e.g. Solaris)
          219  +
          220  +Changes in 1.2.4.4 (18 Apr 2010)
          221  +- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok]
          222  +- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty
          223  +- Try to use bash or ksh regardless of functionality of /bin/sh
          224  +- Fix configure incompatibility with NetBSD sh
          225  +- Remove attempt to run under bash or ksh since have better NetBSD fix
          226  +- Fix win32/Makefile.gcc for MinGW [Bar-Lev]
          227  +- Add diagnostic messages when using CROSS_PREFIX in configure
          228  +- Added --sharedlibdir option to configure [Weigelt]
          229  +- Use hidden visibility attribute when available [Frysinger]
          230  +
          231  +Changes in 1.2.4.3 (10 Apr 2010)
          232  +- Only use CROSS_PREFIX in configure for ar and ranlib if they exist
          233  +- Use CROSS_PREFIX for nm [Bar-Lev]
          234  +- Assume _LARGEFILE64_SOURCE defined is equivalent to true
          235  +- Avoid use of undefined symbols in #if with && and ||
          236  +- Make *64 prototypes in gzguts.h consistent with functions
          237  +- Add -shared load option for MinGW in configure [Bowler]
          238  +- Move z_off64_t to public interface, use instead of off64_t
          239  +- Remove ! from shell test in configure (not portable to Solaris)
          240  +- Change +0 macro tests to -0 for possibly increased portability
          241  +
          242  +Changes in 1.2.4.2 (9 Apr 2010)
          243  +- Add consistent carriage returns to readme.txt's in masmx86 and masmx64
          244  +- Really provide prototypes for *64 functions when building without LFS
          245  +- Only define unlink() in minigzip.c if unistd.h not included
          246  +- Update README to point to contrib/vstudio project files
          247  +- Move projects/vc6 to old/ and remove projects/
          248  +- Include stdlib.h in minigzip.c for setmode() definition under WinCE
          249  +- Clean up assembler builds in win32/Makefile.msc [Rowe]
          250  +- Include sys/types.h for Microsoft for off_t definition
          251  +- Fix memory leak on error in gz_open()
          252  +- Symbolize nm as $NM in configure [Weigelt]
          253  +- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt]
          254  +- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined
          255  +- Fix bug in gzeof() to take into account unused input data
          256  +- Avoid initialization of structures with variables in puff.c
          257  +- Updated win32/README-WIN32.txt [Rowe]
          258  +
          259  +Changes in 1.2.4.1 (28 Mar 2010)
          260  +- Remove the use of [a-z] constructs for sed in configure [gentoo 310225]
          261  +- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech]
          262  +- Restore "for debugging" comment on sprintf() in gzlib.c
          263  +- Remove fdopen for MVS from gzguts.h
          264  +- Put new README-WIN32.txt in win32 [Rowe]
          265  +- Add check for shell to configure and invoke another shell if needed
          266  +- Fix big fat stinking bug in gzseek() on uncompressed files
          267  +- Remove vestigial F_OPEN64 define in zutil.h
          268  +- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE
          269  +- Avoid errors on non-LFS systems when applications define LFS macros
          270  +- Set EXE to ".exe" in configure for MINGW [Kahle]
          271  +- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill]
          272  +- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev]
          273  +- Add DLL install in win32/makefile.gcc [Bar-Lev]
          274  +- Allow Linux* or linux* from uname in configure [Bar-Lev]
          275  +- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev]
          276  +- Add cross-compilation prefixes to configure [Bar-Lev]
          277  +- Match type exactly in gz_load() invocation in gzread.c
          278  +- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func
          279  +- Provide prototypes for *64 functions when building zlib without LFS
          280  +- Don't use -lc when linking shared library on MinGW
          281  +- Remove errno.h check in configure and vestigial errno code in zutil.h
          282  +
          283  +Changes in 1.2.4 (14 Mar 2010)
          284  +- Fix VER3 extraction in configure for no fourth subversion
          285  +- Update zlib.3, add docs to Makefile.in to make .pdf out of it
          286  +- Add zlib.3.pdf to distribution
          287  +- Don't set error code in gzerror() if passed pointer is NULL
          288  +- Apply destination directory fixes to CMakeLists.txt [Lowman]
          289  +- Move #cmakedefine's to a new zconf.in.cmakein
          290  +- Restore zconf.h for builds that don't use configure or cmake
          291  +- Add distclean to dummy Makefile for convenience
          292  +- Update and improve INDEX, README, and FAQ
          293  +- Update CMakeLists.txt for the return of zconf.h [Lowman]
          294  +- Update contrib/vstudio/vc9 and vc10 [Vollant]
          295  +- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc
          296  +- Apply license and readme changes to contrib/asm686 [Raiter]
          297  +- Check file name lengths and add -c option in minigzip.c [Li]
          298  +- Update contrib/amd64 and contrib/masmx86/ [Vollant]
          299  +- Avoid use of "eof" parameter in trees.c to not shadow library variable
          300  +- Update make_vms.com for removal of zlibdefs.h [Zinser]
          301  +- Update assembler code and vstudio projects in contrib [Vollant]
          302  +- Remove outdated assembler code contrib/masm686 and contrib/asm586
          303  +- Remove old vc7 and vc8 from contrib/vstudio
          304  +- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe]
          305  +- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open()
          306  +- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant]
          307  +- Remove *64 functions from win32/zlib.def (they're not 64-bit yet)
          308  +- Fix bug in void-returning vsprintf() case in gzwrite.c
          309  +- Fix name change from inflate.h in contrib/inflate86/inffas86.c
          310  +- Check if temporary file exists before removing in make_vms.com [Zinser]
          311  +- Fix make install and uninstall for --static option
          312  +- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta]
          313  +- Update readme.txt in contrib/masmx64 and masmx86 to assemble
          314  +
          315  +Changes in 1.2.3.9 (21 Feb 2010)
          316  +- Expunge gzio.c
          317  +- Move as400 build information to old
          318  +- Fix updates in contrib/minizip and contrib/vstudio
          319  +- Add const to vsnprintf test in configure to avoid warnings [Weigelt]
          320  +- Delete zconf.h (made by configure) [Weigelt]
          321  +- Change zconf.in.h to zconf.h.in per convention [Weigelt]
          322  +- Check for NULL buf in gzgets()
          323  +- Return empty string for gzgets() with len == 1 (like fgets())
          324  +- Fix description of gzgets() in zlib.h for end-of-file, NULL return
          325  +- Update minizip to 1.1 [Vollant]
          326  +- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c
          327  +- Note in zlib.h that gzerror() should be used to distinguish from EOF
          328  +- Remove use of snprintf() from gzlib.c
          329  +- Fix bug in gzseek()
          330  +- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant]
          331  +- Fix zconf.h generation in CMakeLists.txt [Lowman]
          332  +- Improve comments in zconf.h where modified by configure
          333  +
          334  +Changes in 1.2.3.8 (13 Feb 2010)
          335  +- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer]
          336  +- Use z_off64_t in gz_zero() and gz_skip() to match state->skip
          337  +- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t)
          338  +- Revert to Makefile.in from 1.2.3.6 (live with the clutter)
          339  +- Fix missing error return in gzflush(), add zlib.h note
          340  +- Add *64 functions to zlib.map [Levin]
          341  +- Fix signed/unsigned comparison in gz_comp()
          342  +- Use SFLAGS when testing shared linking in configure
          343  +- Add --64 option to ./configure to use -m64 with gcc
          344  +- Fix ./configure --help to correctly name options
          345  +- Have make fail if a test fails [Levin]
          346  +- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson]
          347  +- Remove assembler object files from contrib
          348  +
          349  +Changes in 1.2.3.7 (24 Jan 2010)
          350  +- Always gzopen() with O_LARGEFILE if available
          351  +- Fix gzdirect() to work immediately after gzopen() or gzdopen()
          352  +- Make gzdirect() more precise when the state changes while reading
          353  +- Improve zlib.h documentation in many places
          354  +- Catch memory allocation failure in gz_open()
          355  +- Complete close operation if seek forward in gzclose_w() fails
          356  +- Return Z_ERRNO from gzclose_r() if close() fails
          357  +- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL
          358  +- Return zero for gzwrite() errors to match zlib.h description
          359  +- Return -1 on gzputs() error to match zlib.h description
          360  +- Add zconf.in.h to allow recovery from configure modification [Weigelt]
          361  +- Fix static library permissions in Makefile.in [Weigelt]
          362  +- Avoid warnings in configure tests that hide functionality [Weigelt]
          363  +- Add *BSD and DragonFly to Linux case in configure [gentoo 123571]
          364  +- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212]
          365  +- Avoid access of uninitialized data for first inflateReset2 call [Gomes]
          366  +- Keep object files in subdirectories to reduce the clutter somewhat
          367  +- Remove default Makefile and zlibdefs.h, add dummy Makefile
          368  +- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_
          369  +- Remove zlibdefs.h completely -- modify zconf.h instead
          370  +
          371  +Changes in 1.2.3.6 (17 Jan 2010)
          372  +- Avoid void * arithmetic in gzread.c and gzwrite.c
          373  +- Make compilers happier with const char * for gz_error message
          374  +- Avoid unused parameter warning in inflate.c
          375  +- Avoid signed-unsigned comparison warning in inflate.c
          376  +- Indent #pragma's for traditional C
          377  +- Fix usage of strwinerror() in glib.c, change to gz_strwinerror()
          378  +- Correct email address in configure for system options
          379  +- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser]
          380  +- Update zlib.map [Brown]
          381  +- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok]
          382  +- Apply various fixes to CMakeLists.txt [Lowman]
          383  +- Add checks on len in gzread() and gzwrite()
          384  +- Add error message for no more room for gzungetc()
          385  +- Remove zlib version check in gzwrite()
          386  +- Defer compression of gzprintf() result until need to
          387  +- Use snprintf() in gzdopen() if available
          388  +- Remove USE_MMAP configuration determination (only used by minigzip)
          389  +- Remove examples/pigz.c (available separately)
          390  +- Update examples/gun.c to 1.6
          391  +
          392  +Changes in 1.2.3.5 (8 Jan 2010)
          393  +- Add space after #if in zutil.h for some compilers
          394  +- Fix relatively harmless bug in deflate_fast() [Exarevsky]
          395  +- Fix same problem in deflate_slow()
          396  +- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown]
          397  +- Add deflate_rle() for faster Z_RLE strategy run-length encoding
          398  +- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding
          399  +- Change name of "write" variable in inffast.c to avoid library collisions
          400  +- Fix premature EOF from gzread() in gzio.c [Brown]
          401  +- Use zlib header window size if windowBits is 0 in inflateInit2()
          402  +- Remove compressBound() call in deflate.c to avoid linking compress.o
          403  +- Replace use of errno in gz* with functions, support WinCE [Alves]
          404  +- Provide alternative to perror() in minigzip.c for WinCE [Alves]
          405  +- Don't use _vsnprintf on later versions of MSVC [Lowman]
          406  +- Add CMake build script and input file [Lowman]
          407  +- Update contrib/minizip to 1.1 [Svensson, Vollant]
          408  +- Moved nintendods directory from contrib to .
          409  +- Replace gzio.c with a new set of routines with the same functionality
          410  +- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above
          411  +- Update contrib/minizip to 1.1b
          412  +- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h
          413  +
          414  +Changes in 1.2.3.4 (21 Dec 2009)
          415  +- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility
          416  +- Update comments in configure and Makefile.in for default --shared
          417  +- Fix test -z's in configure [Marquess]
          418  +- Build examplesh and minigzipsh when not testing
          419  +- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h
          420  +- Import LDFLAGS from the environment in configure
          421  +- Fix configure to populate SFLAGS with discovered CFLAGS options
          422  +- Adapt make_vms.com to the new Makefile.in [Zinser]
          423  +- Add zlib2ansi script for C++ compilation [Marquess]
          424  +- Add _FILE_OFFSET_BITS=64 test to make test (when applicable)
          425  +- Add AMD64 assembler code for longest match to contrib [Teterin]
          426  +- Include options from $SFLAGS when doing $LDSHARED
          427  +- Simplify 64-bit file support by introducing z_off64_t type
          428  +- Make shared object files in objs directory to work around old Sun cc
          429  +- Use only three-part version number for Darwin shared compiles
          430  +- Add rc option to ar in Makefile.in for when ./configure not run
          431  +- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4*
          432  +- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile
          433  +- Protect against _FILE_OFFSET_BITS being defined when compiling zlib
          434  +- Rename Makefile.in targets allstatic to static and allshared to shared
          435  +- Fix static and shared Makefile.in targets to be independent
          436  +- Correct error return bug in gz_open() by setting state [Brown]
          437  +- Put spaces before ;;'s in configure for better sh compatibility
          438  +- Add pigz.c (parallel implementation of gzip) to examples/
          439  +- Correct constant in crc32.c to UL [Leventhal]
          440  +- Reject negative lengths in crc32_combine()
          441  +- Add inflateReset2() function to work like inflateEnd()/inflateInit2()
          442  +- Include sys/types.h for _LARGEFILE64_SOURCE [Brown]
          443  +- Correct typo in doc/algorithm.txt [Janik]
          444  +- Fix bug in adler32_combine() [Zhu]
          445  +- Catch missing-end-of-block-code error in all inflates and in puff
          446  +    Assures that random input to inflate eventually results in an error
          447  +- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/
          448  +- Update ENOUGH and its usage to reflect discovered bounds
          449  +- Fix gzerror() error report on empty input file [Brown]
          450  +- Add ush casts in trees.c to avoid pedantic runtime errors
          451  +- Fix typo in zlib.h uncompress() description [Reiss]
          452  +- Correct inflate() comments with regard to automatic header detection
          453  +- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays)
          454  +- Put new version of gzlog (2.0) in examples with interruption recovery
          455  +- Add puff compile option to permit invalid distance-too-far streams
          456  +- Add puff TEST command options, ability to read piped input
          457  +- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but
          458  +  _LARGEFILE64_SOURCE not defined
          459  +- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart
          460  +- Fix deflateSetDictionary() to use all 32K for output consistency
          461  +- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h)
          462  +- Clear bytes after deflate lookahead to avoid use of uninitialized data
          463  +- Change a limit in inftrees.c to be more transparent to Coverity Prevent
          464  +- Update win32/zlib.def with exported symbols from zlib.h
          465  +- Correct spelling errors in zlib.h [Willem, Sobrado]
          466  +- Allow Z_BLOCK for deflate() to force a new block
          467  +- Allow negative bits in inflatePrime() to delete existing bit buffer
          468  +- Add Z_TREES flush option to inflate() to return at end of trees
          469  +- Add inflateMark() to return current state information for random access
          470  +- Add Makefile for NintendoDS to contrib [Costa]
          471  +- Add -w in configure compile tests to avoid spurious warnings [Beucler]
          472  +- Fix typos in zlib.h comments for deflateSetDictionary()
          473  +- Fix EOF detection in transparent gzread() [Maier]
          474  +
          475  +Changes in 1.2.3.3 (2 October 2006)
          476  +- Make --shared the default for configure, add a --static option
          477  +- Add compile option to permit invalid distance-too-far streams
          478  +- Add inflateUndermine() function which is required to enable above
          479  +- Remove use of "this" variable name for C++ compatibility [Marquess]
          480  +- Add testing of shared library in make test, if shared library built
          481  +- Use ftello() and fseeko() if available instead of ftell() and fseek()
          482  +- Provide two versions of all functions that use the z_off_t type for
          483  +  binary compatibility -- a normal version and a 64-bit offset version,
          484  +  per the Large File Support Extension when _LARGEFILE64_SOURCE is
          485  +  defined; use the 64-bit versions by default when _FILE_OFFSET_BITS
          486  +  is defined to be 64
          487  +- Add a --uname= option to configure to perhaps help with cross-compiling
          488  +
          489  +Changes in 1.2.3.2 (3 September 2006)
          490  +- Turn off silly Borland warnings [Hay]
          491  +- Use off64_t and define _LARGEFILE64_SOURCE when present
          492  +- Fix missing dependency on inffixed.h in Makefile.in
          493  +- Rig configure --shared to build both shared and static [Teredesai, Truta]
          494  +- Remove zconf.in.h and instead create a new zlibdefs.h file
          495  +- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant]
          496  +- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt]
          497  +
          498  +Changes in 1.2.3.1 (16 August 2006)
          499  +- Add watcom directory with OpenWatcom make files [Daniel]
          500  +- Remove #undef of FAR in zconf.in.h for MVS [Fedtke]
          501  +- Update make_vms.com [Zinser]
          502  +- Use -fPIC for shared build in configure [Teredesai, Nicholson]
          503  +- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen]
          504  +- Use fdopen() (not _fdopen()) for Interix in zutil.h [BŠck]
          505  +- Add some FAQ entries about the contrib directory
          506  +- Update the MVS question in the FAQ
          507  +- Avoid extraneous reads after EOF in gzio.c [Brown]
          508  +- Correct spelling of "successfully" in gzio.c [Randers-Pehrson]
          509  +- Add comments to zlib.h about gzerror() usage [Brown]
          510  +- Set extra flags in gzip header in gzopen() like deflate() does
          511  +- Make configure options more compatible with double-dash conventions
          512  +  [Weigelt]
          513  +- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen]
          514  +- Fix uninstall target in Makefile.in [Truta]
          515  +- Add pkgconfig support [Weigelt]
          516  +- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt]
          517  +- Replace set_data_type() with a more accurate detect_data_type() in
          518  +  trees.c, according to the txtvsbin.txt document [Truta]
          519  +- Swap the order of #include <stdio.h> and #include "zlib.h" in
          520  +  gzio.c, example.c and minigzip.c [Truta]
          521  +- Shut up annoying VS2005 warnings about standard C deprecation [Rowe,
          522  +  Truta] (where?)
          523  +- Fix target "clean" from win32/Makefile.bor [Truta]
          524  +- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe]
          525  +- Update zlib www home address in win32/DLL_FAQ.txt [Truta]
          526  +- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove]
          527  +- Enable browse info in the "Debug" and "ASM Debug" configurations in
          528  +  the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta]
          529  +- Add pkgconfig support [Weigelt]
          530  +- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h,
          531  +  for use in win32/zlib1.rc [Polushin, Rowe, Truta]
          532  +- Add a document that explains the new text detection scheme to
          533  +  doc/txtvsbin.txt [Truta]
          534  +- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta]
          535  +- Move algorithm.txt into doc/ [Truta]
          536  +- Synchronize FAQ with website
          537  +- Fix compressBound(), was low for some pathological cases [Fearnley]
          538  +- Take into account wrapper variations in deflateBound()
          539  +- Set examples/zpipe.c input and output to binary mode for Windows
          540  +- Update examples/zlib_how.html with new zpipe.c (also web site)
          541  +- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems
          542  +  that gcc became pickier in 4.0)
          543  +- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain
          544  +  un-versioned, the patch adds versioning only for symbols introduced in
          545  +  zlib-1.2.0 or later.  It also declares as local those symbols which are
          546  +  not designed to be exported." [Levin]
          547  +- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure
          548  +- Do not initialize global static by default in trees.c, add a response
          549  +  NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess]
          550  +- Don't use strerror() in gzio.c under WinCE [Yakimov]
          551  +- Don't use errno.h in zutil.h under WinCE [Yakimov]
          552  +- Move arguments for AR to its usage to allow replacing ar [Marot]
          553  +- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson]
          554  +- Improve inflateInit() and inflateInit2() documentation
          555  +- Fix structure size comment in inflate.h
          556  +- Change configure help option from --h* to --help [Santos]
          557  +
          558  +Changes in 1.2.3 (18 July 2005)
          559  +- Apply security vulnerability fixes to contrib/infback9 as well
          560  +- Clean up some text files (carriage returns, trailing space)
          561  +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant]
          562  +
          563  +Changes in 1.2.2.4 (11 July 2005)
          564  +- Add inflatePrime() function for starting inflation at bit boundary
          565  +- Avoid some Visual C warnings in deflate.c
          566  +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit
          567  +  compile
          568  +- Fix some spelling errors in comments [Betts]
          569  +- Correct inflateInit2() error return documentation in zlib.h
          570  +- Add zran.c example of compressed data random access to examples
          571  +  directory, shows use of inflatePrime()
          572  +- Fix cast for assignments to strm->state in inflate.c and infback.c
          573  +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer]
          574  +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer]
          575  +- Add cast in trees.c t avoid a warning [Oberhumer]
          576  +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer]
          577  +- Update make_vms.com [Zinser]
          578  +- Initialize state->write in inflateReset() since copied in inflate_fast()
          579  +- Be more strict on incomplete code sets in inflate_table() and increase
          580  +  ENOUGH and MAXD -- this repairs a possible security vulnerability for
          581  +  invalid inflate input.  Thanks to Tavis Ormandy and Markus Oberhumer for
          582  +  discovering the vulnerability and providing test cases.
          583  +- Add ia64 support to configure for HP-UX [Smith]
          584  +- Add error return to gzread() for format or i/o error [Levin]
          585  +- Use malloc.h for OS/2 [Necasek]
          586  +
          587  +Changes in 1.2.2.3 (27 May 2005)
          588  +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile
          589  +- Typecast fread() return values in gzio.c [Vollant]
          590  +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it)
          591  +- Fix crc check bug in gzread() after gzungetc() [Heiner]
          592  +- Add the deflateTune() function to adjust internal compression parameters
          593  +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack)
          594  +- Remove an incorrect assertion in examples/zpipe.c
          595  +- Add C++ wrapper in infback9.h [Donais]
          596  +- Fix bug in inflateCopy() when decoding fixed codes
          597  +- Note in zlib.h how much deflateSetDictionary() actually uses
          598  +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used)
          599  +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer]
          600  +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer]
          601  +- Add gzdirect() function to indicate transparent reads
          602  +- Update contrib/minizip [Vollant]
          603  +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer]
          604  +- Add casts in crc32.c to avoid warnings [Oberhumer]
          605  +- Add contrib/masmx64 [Vollant]
          606  +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant]
          607  +
          608  +Changes in 1.2.2.2 (30 December 2004)
          609  +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to
          610  +  avoid implicit memcpy calls (portability for no-library compilation)
          611  +- Increase sprintf() buffer size in gzdopen() to allow for large numbers
          612  +- Add INFLATE_STRICT to check distances against zlib header
          613  +- Improve WinCE errno handling and comments [Chang]
          614  +- Remove comment about no gzip header processing in FAQ
          615  +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees
          616  +- Add updated make_vms.com [Coghlan], update README
          617  +- Create a new "examples" directory, move gzappend.c there, add zpipe.c,
          618  +  fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html.
          619  +- Add FAQ entry and comments in deflate.c on uninitialized memory access
          620  +- Add Solaris 9 make options in configure [Gilbert]
          621  +- Allow strerror() usage in gzio.c for STDC
          622  +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer]
          623  +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant]
          624  +- Use z_off_t for adler32_combine() and crc32_combine() lengths
          625  +- Make adler32() much faster for small len
          626  +- Use OS_CODE in deflate() default gzip header
          627  +
          628  +Changes in 1.2.2.1 (31 October 2004)
          629  +- Allow inflateSetDictionary() call for raw inflate
          630  +- Fix inflate header crc check bug for file names and comments
          631  +- Add deflateSetHeader() and gz_header structure for custom gzip headers
          632  +- Add inflateGetheader() to retrieve gzip headers
          633  +- Add crc32_combine() and adler32_combine() functions
          634  +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list
          635  +- Use zstreamp consistently in zlib.h (inflate_back functions)
          636  +- Remove GUNZIP condition from definition of inflate_mode in inflate.h
          637  +  and in contrib/inflate86/inffast.S [Truta, Anderson]
          638  +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson]
          639  +- Update projects/README.projects and projects/visualc6 [Truta]
          640  +- Update win32/DLL_FAQ.txt [Truta]
          641  +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta]
          642  +- Deprecate Z_ASCII; use Z_TEXT instead [Truta]
          643  +- Use a new algorithm for setting strm->data_type in trees.c [Truta]
          644  +- Do not define an exit() prototype in zutil.c unless DEBUG defined
          645  +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta]
          646  +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate()
          647  +- Fix Darwin build version identification [Peterson]
          648  +
          649  +Changes in 1.2.2 (3 October 2004)
          650  +- Update zlib.h comments on gzip in-memory processing
          651  +- Set adler to 1 in inflateReset() to support Java test suite [Walles]
          652  +- Add contrib/dotzlib [Ravn]
          653  +- Update win32/DLL_FAQ.txt [Truta]
          654  +- Update contrib/minizip [Vollant]
          655  +- Move contrib/visual-basic.txt to old/ [Truta]
          656  +- Fix assembler builds in projects/visualc6/ [Truta]
          657  +
          658  +Changes in 1.2.1.2 (9 September 2004)
          659  +- Update INDEX file
          660  +- Fix trees.c to update strm->data_type (no one ever noticed!)
          661  +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown]
          662  +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE)
          663  +- Add limited multitasking protection to DYNAMIC_CRC_TABLE
          664  +- Add NO_vsnprintf for VMS in zutil.h [Mozilla]
          665  +- Don't declare strerror() under VMS [Mozilla]
          666  +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize
          667  +- Update contrib/ada [Anisimkov]
          668  +- Update contrib/minizip [Vollant]
          669  +- Fix configure to not hardcode directories for Darwin [Peterson]
          670  +- Fix gzio.c to not return error on empty files [Brown]
          671  +- Fix indentation; update version in contrib/delphi/ZLib.pas and
          672  +  contrib/pascal/zlibpas.pas [Truta]
          673  +- Update mkasm.bat in contrib/masmx86 [Truta]
          674  +- Update contrib/untgz [Truta]
          675  +- Add projects/README.projects [Truta]
          676  +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta]
          677  +- Update win32/DLL_FAQ.txt [Truta]
          678  +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta]
          679  +- Remove an unnecessary assignment to curr in inftrees.c [Truta]
          680  +- Add OS/2 to exe builds in configure [Poltorak]
          681  +- Remove err dummy parameter in zlib.h [Kientzle]
          682  +
          683  +Changes in 1.2.1.1 (9 January 2004)
          684  +- Update email address in README
          685  +- Several FAQ updates
          686  +- Fix a big fat bug in inftrees.c that prevented decoding valid
          687  +  dynamic blocks with only literals and no distance codes --
          688  +  Thanks to "Hot Emu" for the bug report and sample file
          689  +- Add a note to puff.c on no distance codes case.
          690  +
          691  +Changes in 1.2.1 (17 November 2003)
          692  +- Remove a tab in contrib/gzappend/gzappend.c
          693  +- Update some interfaces in contrib for new zlib functions
          694  +- Update zlib version number in some contrib entries
          695  +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta]
          696  +- Support shared libraries on Hurd and KFreeBSD [Brown]
          697  +- Fix error in NO_DIVIDE option of adler32.c
          698  +
          699  +Changes in 1.2.0.8 (4 November 2003)
          700  +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas
          701  +- Add experimental NO_DIVIDE #define in adler32.c
          702  +    - Possibly faster on some processors (let me know if it is)
          703  +- Correct Z_BLOCK to not return on first inflate call if no wrap
          704  +- Fix strm->data_type on inflate() return to correctly indicate EOB
          705  +- Add deflatePrime() function for appending in the middle of a byte
          706  +- Add contrib/gzappend for an example of appending to a stream
          707  +- Update win32/DLL_FAQ.txt [Truta]
          708  +- Delete Turbo C comment in README [Truta]
          709  +- Improve some indentation in zconf.h [Truta]
          710  +- Fix infinite loop on bad input in configure script [Church]
          711  +- Fix gzeof() for concatenated gzip files [Johnson]
          712  +- Add example to contrib/visual-basic.txt [Michael B.]
          713  +- Add -p to mkdir's in Makefile.in [vda]
          714  +- Fix configure to properly detect presence or lack of printf functions
          715  +- Add AS400 support [Monnerat]
          716  +- Add a little Cygwin support [Wilson]
          717  +
          718  +Changes in 1.2.0.7 (21 September 2003)
          719  +- Correct some debug formats in contrib/infback9
          720  +- Cast a type in a debug statement in trees.c
          721  +- Change search and replace delimiter in configure from % to # [Beebe]
          722  +- Update contrib/untgz to 0.2 with various fixes [Truta]
          723  +- Add build support for Amiga [Nikl]
          724  +- Remove some directories in old that have been updated to 1.2
          725  +- Add dylib building for Mac OS X in configure and Makefile.in
          726  +- Remove old distribution stuff from Makefile
          727  +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X
          728  +- Update links in README
          729  +
          730  +Changes in 1.2.0.6 (13 September 2003)
          731  +- Minor FAQ updates
          732  +- Update contrib/minizip to 1.00 [Vollant]
          733  +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta]
          734  +- Update POSTINC comment for 68060 [Nikl]
          735  +- Add contrib/infback9 with deflate64 decoding (unsupported)
          736  +- For MVS define NO_vsnprintf and undefine FAR [van Burik]
          737  +- Add pragma for fdopen on MVS [van Burik]
          738  +
          739  +Changes in 1.2.0.5 (8 September 2003)
          740  +- Add OF to inflateBackEnd() declaration in zlib.h
          741  +- Remember start when using gzdopen in the middle of a file
          742  +- Use internal off_t counters in gz* functions to properly handle seeks
          743  +- Perform more rigorous check for distance-too-far in inffast.c
          744  +- Add Z_BLOCK flush option to return from inflate at block boundary
          745  +- Set strm->data_type on return from inflate
          746  +    - Indicate bits unused, if at block boundary, and if in last block
          747  +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size
          748  +- Add condition so old NO_DEFLATE define still works for compatibility
          749  +- FAQ update regarding the Windows DLL [Truta]
          750  +- INDEX update: add qnx entry, remove aix entry [Truta]
          751  +- Install zlib.3 into mandir [Wilson]
          752  +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta]
          753  +- Adapt the zlib interface to the new DLL convention guidelines [Truta]
          754  +- Introduce ZLIB_WINAPI macro to allow the export of functions using
          755  +  the WINAPI calling convention, for Visual Basic [Vollant, Truta]
          756  +- Update msdos and win32 scripts and makefiles [Truta]
          757  +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta]
          758  +- Add contrib/ada [Anisimkov]
          759  +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta]
          760  +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant]
          761  +- Add contrib/masm686 [Truta]
          762  +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm
          763  +  [Truta, Vollant]
          764  +- Update contrib/delphi; rename to contrib/pascal; add example [Truta]
          765  +- Remove contrib/delphi2; add a new contrib/delphi [Truta]
          766  +- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream,
          767  +  and fix some method prototypes [Truta]
          768  +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip
          769  +  [Truta]
          770  +- Avoid the use of backslash (\) in contrib/minizip [Vollant]
          771  +- Fix file time handling in contrib/untgz; update makefiles [Truta]
          772  +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines
          773  +  [Vollant]
          774  +- Remove contrib/vstudio/vc15_16 [Vollant]
          775  +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta]
          776  +- Update README.contrib [Truta]
          777  +- Invert the assignment order of match_head and s->prev[...] in
          778  +  INSERT_STRING [Truta]
          779  +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings
          780  +  [Truta]
          781  +- Compare function pointers with 0, not with NULL or Z_NULL [Truta]
          782  +- Fix prototype of syncsearch in inflate.c [Truta]
          783  +- Introduce ASMINF macro to be enabled when using an ASM implementation
          784  +  of inflate_fast [Truta]
          785  +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta]
          786  +- Modify test_gzio in example.c to take a single file name as a
          787  +  parameter [Truta]
          788  +- Exit the example.c program if gzopen fails [Truta]
          789  +- Add type casts around strlen in example.c [Truta]
          790  +- Remove casting to sizeof in minigzip.c; give a proper type
          791  +  to the variable compared with SUFFIX_LEN [Truta]
          792  +- Update definitions of STDC and STDC99 in zconf.h [Truta]
          793  +- Synchronize zconf.h with the new Windows DLL interface [Truta]
          794  +- Use SYS16BIT instead of __32BIT__ to distinguish between
          795  +  16- and 32-bit platforms [Truta]
          796  +- Use far memory allocators in small 16-bit memory models for
          797  +  Turbo C [Truta]
          798  +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in
          799  +  zlibCompileFlags [Truta]
          800  +- Cygwin has vsnprintf [Wilson]
          801  +- In Windows16, OS_CODE is 0, as in MSDOS [Truta]
          802  +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson]
          803  +
          804  +Changes in 1.2.0.4 (10 August 2003)
          805  +- Minor FAQ updates
          806  +- Be more strict when checking inflateInit2's windowBits parameter
          807  +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well
          808  +- Add gzip wrapper option to deflateInit2 using windowBits
          809  +- Add updated QNX rule in configure and qnx directory [Bonnefoy]
          810  +- Make inflate distance-too-far checks more rigorous
          811  +- Clean up FAR usage in inflate
          812  +- Add casting to sizeof() in gzio.c and minigzip.c
          813  +
          814  +Changes in 1.2.0.3 (19 July 2003)
          815  +- Fix silly error in gzungetc() implementation [Vollant]
          816  +- Update contrib/minizip and contrib/vstudio [Vollant]
          817  +- Fix printf format in example.c
          818  +- Correct cdecl support in zconf.in.h [Anisimkov]
          819  +- Minor FAQ updates
          820  +
          821  +Changes in 1.2.0.2 (13 July 2003)
          822  +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons
          823  +- Attempt to avoid warnings in crc32.c for pointer-int conversion
          824  +- Add AIX to configure, remove aix directory [Bakker]
          825  +- Add some casts to minigzip.c
          826  +- Improve checking after insecure sprintf() or vsprintf() calls
          827  +- Remove #elif's from crc32.c
          828  +- Change leave label to inf_leave in inflate.c and infback.c to avoid
          829  +  library conflicts
          830  +- Remove inflate gzip decoding by default--only enable gzip decoding by
          831  +  special request for stricter backward compatibility
          832  +- Add zlibCompileFlags() function to return compilation information
          833  +- More typecasting in deflate.c to avoid warnings
          834  +- Remove leading underscore from _Capital #defines [Truta]
          835  +- Fix configure to link shared library when testing
          836  +- Add some Windows CE target adjustments [Mai]
          837  +- Remove #define ZLIB_DLL in zconf.h [Vollant]
          838  +- Add zlib.3 [Rodgers]
          839  +- Update RFC URL in deflate.c and algorithm.txt [Mai]
          840  +- Add zlib_dll_FAQ.txt to contrib [Truta]
          841  +- Add UL to some constants [Truta]
          842  +- Update minizip and vstudio [Vollant]
          843  +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h
          844  +- Expand use of NO_DUMMY_DECL to avoid all dummy structures
          845  +- Added iostream3 to contrib [Schwardt]
          846  +- Replace rewind() with fseek() for WinCE [Truta]
          847  +- Improve setting of zlib format compression level flags
          848  +    - Report 0 for huffman and rle strategies and for level == 0 or 1
          849  +    - Report 2 only for level == 6
          850  +- Only deal with 64K limit when necessary at compile time [Truta]
          851  +- Allow TOO_FAR check to be turned off at compile time [Truta]
          852  +- Add gzclearerr() function [Souza]
          853  +- Add gzungetc() function
          854  +
          855  +Changes in 1.2.0.1 (17 March 2003)
          856  +- Add Z_RLE strategy for run-length encoding [Truta]
          857  +    - When Z_RLE requested, restrict matches to distance one
          858  +    - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE
          859  +- Correct FASTEST compilation to allow level == 0
          860  +- Clean up what gets compiled for FASTEST
          861  +- Incorporate changes to zconf.in.h [Vollant]
          862  +    - Refine detection of Turbo C need for dummy returns
          863  +    - Refine ZLIB_DLL compilation
          864  +    - Include additional header file on VMS for off_t typedef
          865  +- Try to use _vsnprintf where it supplants vsprintf [Vollant]
          866  +- Add some casts in inffast.c
          867  +- Enchance comments in zlib.h on what happens if gzprintf() tries to
          868  +  write more than 4095 bytes before compression
          869  +- Remove unused state from inflateBackEnd()
          870  +- Remove exit(0) from minigzip.c, example.c
          871  +- Get rid of all those darn tabs
          872  +- Add "check" target to Makefile.in that does the same thing as "test"
          873  +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in
          874  +- Update contrib/inflate86 [Anderson]
          875  +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant]
          876  +- Add msdos and win32 directories with makefiles [Truta]
          877  +- More additions and improvements to the FAQ
          878  +
          879  +Changes in 1.2.0 (9 March 2003)
          880  +- New and improved inflate code
          881  +    - About 20% faster
          882  +    - Does not allocate 32K window unless and until needed
          883  +    - Automatically detects and decompresses gzip streams
          884  +    - Raw inflate no longer needs an extra dummy byte at end
          885  +    - Added inflateBack functions using a callback interface--even faster
          886  +      than inflate, useful for file utilities (gzip, zip)
          887  +    - Added inflateCopy() function to record state for random access on
          888  +      externally generated deflate streams (e.g. in gzip files)
          889  +    - More readable code (I hope)
          890  +- New and improved crc32()
          891  +    - About 50% faster, thanks to suggestions from Rodney Brown
          892  +- Add deflateBound() and compressBound() functions
          893  +- Fix memory leak in deflateInit2()
          894  +- Permit setting dictionary for raw deflate (for parallel deflate)
          895  +- Fix const declaration for gzwrite()
          896  +- Check for some malloc() failures in gzio.c
          897  +- Fix bug in gzopen() on single-byte file 0x1f
          898  +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer
          899  +  and next buffer doesn't start with 0x8b
          900  +- Fix uncompress() to return Z_DATA_ERROR on truncated input
          901  +- Free memory at end of example.c
          902  +- Remove MAX #define in trees.c (conflicted with some libraries)
          903  +- Fix static const's in deflate.c, gzio.c, and zutil.[ch]
          904  +- Declare malloc() and free() in gzio.c if STDC not defined
          905  +- Use malloc() instead of calloc() in zutil.c if int big enough
          906  +- Define STDC for AIX
          907  +- Add aix/ with approach for compiling shared library on AIX
          908  +- Add HP-UX support for shared libraries in configure
          909  +- Add OpenUNIX support for shared libraries in configure
          910  +- Use $cc instead of gcc to build shared library
          911  +- Make prefix directory if needed when installing
          912  +- Correct Macintosh avoidance of typedef Byte in zconf.h
          913  +- Correct Turbo C memory allocation when under Linux
          914  +- Use libz.a instead of -lz in Makefile (assure use of compiled library)
          915  +- Update configure to check for snprintf or vsnprintf functions and their
          916  +  return value, warn during make if using an insecure function
          917  +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that
          918  +  is lost when library is used--resolution is to build new zconf.h
          919  +- Documentation improvements (in zlib.h):
          920  +    - Document raw deflate and inflate
          921  +    - Update RFCs URL
          922  +    - Point out that zlib and gzip formats are different
          923  +    - Note that Z_BUF_ERROR is not fatal
          924  +    - Document string limit for gzprintf() and possible buffer overflow
          925  +    - Note requirement on avail_out when flushing
          926  +    - Note permitted values of flush parameter of inflate()
          927  +- Add some FAQs (and even answers) to the FAQ
          928  +- Add contrib/inflate86/ for x86 faster inflate
          929  +- Add contrib/blast/ for PKWare Data Compression Library decompression
          930  +- Add contrib/puff/ simple inflate for deflate format description
          931  +
          932  +Changes in 1.1.4 (11 March 2002)
          933  +- ZFREE was repeated on same allocation on some error conditions.
          934  +  This creates a security problem described in
          935  +  http://www.zlib.org/advisory-2002-03-11.txt
          936  +- Returned incorrect error (Z_MEM_ERROR) on some invalid data
          937  +- Avoid accesses before window for invalid distances with inflate window
          938  +  less than 32K.
          939  +- force windowBits > 8 to avoid a bug in the encoder for a window size
          940  +  of 256 bytes. (A complete fix will be available in 1.1.5).
          941  +
          942  +Changes in 1.1.3 (9 July 1998)
          943  +- fix "an inflate input buffer bug that shows up on rare but persistent
          944  +  occasions" (Mark)
          945  +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
          946  +- fix gzseek(..., SEEK_SET) in write mode
          947  +- fix crc check after a gzeek (Frank Faubert)
          948  +- fix miniunzip when the last entry in a zip file is itself a zip file
          949  +  (J Lillge)
          950  +- add contrib/asm586 and contrib/asm686 (Brian Raiter)
          951  +  See http://www.muppetlabs.com/~breadbox/software/assembly.html
          952  +- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
          953  +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
          954  +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
          955  +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
          956  +- added a FAQ file
          957  +
          958  +- Support gzdopen on Mac with Metrowerks (Jason Linhart)
          959  +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
          960  +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
          961  +- avoid some warnings with Borland C (Tom Tanner)
          962  +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
          963  +- emulate utime() for WIN32 in contrib/untgz  (Gilles Vollant)
          964  +- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
          965  +- use libdir and includedir in Makefile.in (Tim Mooney)
          966  +- support shared libraries on OSF1 V4 (Tim Mooney)
          967  +- remove so_locations in "make clean"  (Tim Mooney)
          968  +- fix maketree.c compilation error (Glenn, Mark)
          969  +- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
          970  +- new Makefile.riscos (Rich Walker)
          971  +- initialize static descriptors in trees.c for embedded targets (Nick Smith)
          972  +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
          973  +- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
          974  +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
          975  +- fix maketree.c to allow clean compilation of inffixed.h (Mark)
          976  +- fix parameter check in deflateCopy (Gunther Nikl)
          977  +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
          978  +- Many portability patches by Christian Spieler:
          979  +  . zutil.c, zutil.h: added "const" for zmem*
          980  +  . Make_vms.com: fixed some typos
          981  +  . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
          982  +  . msdos/Makefile.msc: remove "default rtl link library" info from obj files
          983  +  . msdos/Makefile.*: use model-dependent name for the built zlib library
          984  +  . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
          985  +     new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
          986  +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
          987  +- replace __far with _far for better portability (Christian Spieler, Tom Lane)
          988  +- fix test for errno.h in configure (Tim Newsham)
          989  +
          990  +Changes in 1.1.2 (19 March 98)
          991  +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
          992  +  See http://www.winimage.com/zLibDll/unzip.html
          993  +- preinitialize the inflate tables for fixed codes, to make the code
          994  +  completely thread safe (Mark)
          995  +- some simplifications and slight speed-up to the inflate code (Mark)
          996  +- fix gzeof on non-compressed files (Allan Schrum)
          997  +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
          998  +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
          999  +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
         1000  +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
         1001  +- do not wrap extern "C" around system includes (Tom Lane)
         1002  +- mention zlib binding for TCL in README (Andreas Kupries)
         1003  +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
         1004  +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
         1005  +- allow "configure --prefix $HOME" (Tim Mooney)
         1006  +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
         1007  +- move Makefile.sas to amiga/Makefile.sas
         1008  +
         1009  +Changes in 1.1.1 (27 Feb 98)
         1010  +- fix macros _tr_tally_* in deflate.h for debug mode  (Glenn Randers-Pehrson)
         1011  +- remove block truncation heuristic which had very marginal effect for zlib
         1012  +  (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
         1013  +  compression ratio on some files. This also allows inlining _tr_tally for
         1014  +  matches in deflate_slow.
         1015  +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
         1016  +
         1017  +Changes in 1.1.0 (24 Feb 98)
         1018  +- do not return STREAM_END prematurely in inflate (John Bowler)
         1019  +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
         1020  +- compile with -DFASTEST to get compression code optimized for speed only
         1021  +- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
         1022  +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
         1023  +  on Sun but significant on HP)
         1024  +
         1025  +- add a pointer to experimental unzip library in README (Gilles Vollant)
         1026  +- initialize variable gcc in configure (Chris Herborth)
         1027  +
         1028  +Changes in 1.0.9 (17 Feb 1998)
         1029  +- added gzputs and gzgets functions
         1030  +- do not clear eof flag in gzseek (Mark Diekhans)
         1031  +- fix gzseek for files in transparent mode (Mark Diekhans)
         1032  +- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
         1033  +- replace EXPORT with ZEXPORT to avoid conflict with other programs
         1034  +- added compress2 in zconf.h, zlib.def, zlib.dnt
         1035  +- new asm code from Gilles Vollant in contrib/asm386
         1036  +- simplify the inflate code (Mark):
         1037  + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
         1038  + . ZALLOC the length list in inflate_trees_fixed() instead of using stack
         1039  + . ZALLOC the value area for huft_build() instead of using stack
         1040  + . Simplify Z_FINISH check in inflate()
         1041  +
         1042  +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
         1043  +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
         1044  +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
         1045  +  the declaration of FAR (Gilles VOllant)
         1046  +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
         1047  +- read_buf buf parameter of type Bytef* instead of charf*
         1048  +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
         1049  +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
         1050  +- fix check for presence of directories in "make install" (Ian Willis)
         1051  +
         1052  +Changes in 1.0.8 (27 Jan 1998)
         1053  +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
         1054  +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
         1055  +- added compress2() to allow setting the compression level
         1056  +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
         1057  +- use constant arrays for the static trees in trees.c instead of computing
         1058  +  them at run time (thanks to Ken Raeburn for this suggestion). To create
         1059  +  trees.h, compile with GEN_TREES_H and run "make test".
         1060  +- check return code of example in "make test" and display result
         1061  +- pass minigzip command line options to file_compress
         1062  +- simplifying code of inflateSync to avoid gcc 2.8 bug
         1063  +
         1064  +- support CC="gcc -Wall" in configure -s (QingLong)
         1065  +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
         1066  +- fix test for shared library support to avoid compiler warnings
         1067  +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
         1068  +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
         1069  +- do not use fdopen for Metrowerks on Mac (Brad Pettit))
         1070  +- add checks for gzputc and gzputc in example.c
         1071  +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
         1072  +- use const for the CRC table (Ken Raeburn)
         1073  +- fixed "make uninstall" for shared libraries
         1074  +- use Tracev instead of Trace in infblock.c
         1075  +- in example.c use correct compressed length for test_sync
         1076  +- suppress +vnocompatwarnings in configure for HPUX (not always supported)
         1077  +
         1078  +Changes in 1.0.7 (20 Jan 1998)
         1079  +- fix gzseek which was broken in write mode
         1080  +- return error for gzseek to negative absolute position
         1081  +- fix configure for Linux (Chun-Chung Chen)
         1082  +- increase stack space for MSC (Tim Wegner)
         1083  +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
         1084  +- define EXPORTVA for gzprintf (Gilles Vollant)
         1085  +- added man page zlib.3 (Rick Rodgers)
         1086  +- for contrib/untgz, fix makedir() and improve Makefile
         1087  +
         1088  +- check gzseek in write mode in example.c
         1089  +- allocate extra buffer for seeks only if gzseek is actually called
         1090  +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
         1091  +- add inflateSyncPoint in zconf.h
         1092  +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
         1093  +
         1094  +Changes in 1.0.6 (19 Jan 1998)
         1095  +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
         1096  +  gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
         1097  +- Fix a deflate bug occurring only with compression level 0 (thanks to
         1098  +  Andy Buckler for finding this one).
         1099  +- In minigzip, pass transparently also the first byte for .Z files.
         1100  +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
         1101  +- check Z_FINISH in inflate (thanks to Marc Schluper)
         1102  +- Implement deflateCopy (thanks to Adam Costello)
         1103  +- make static libraries by default in configure, add --shared option.
         1104  +- move MSDOS or Windows specific files to directory msdos
         1105  +- suppress the notion of partial flush to simplify the interface
         1106  +  (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
         1107  +- suppress history buffer provided by application to simplify the interface
         1108  +  (this feature was not implemented anyway in 1.0.4)
         1109  +- next_in and avail_in must be initialized before calling inflateInit or
         1110  +  inflateInit2
         1111  +- add EXPORT in all exported functions (for Windows DLL)
         1112  +- added Makefile.nt (thanks to Stephen Williams)
         1113  +- added the unsupported "contrib" directory:
         1114  +   contrib/asm386/ by Gilles Vollant <info@winimage.com>
         1115  +        386 asm code replacing longest_match().
         1116  +   contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
         1117  +        A C++ I/O streams interface to the zlib gz* functions
         1118  +   contrib/iostream2/  by Tyge Løvset <Tyge.Lovset@cmr.no>
         1119  +        Another C++ I/O streams interface
         1120  +   contrib/untgz/  by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
         1121  +        A very simple tar.gz file extractor using zlib
         1122  +   contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
         1123  +        How to use compress(), uncompress() and the gz* functions from VB.
         1124  +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
         1125  +  level) in minigzip (thanks to Tom Lane)
         1126  +
         1127  +- use const for rommable constants in deflate
         1128  +- added test for gzseek and gztell in example.c
         1129  +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
         1130  +- add undocumented function zError to convert error code to string
         1131  +  (for Tim Smithers)
         1132  +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
         1133  +- Use default memcpy for Symantec MSDOS compiler.
         1134  +- Add EXPORT keyword for check_func (needed for Windows DLL)
         1135  +- add current directory to LD_LIBRARY_PATH for "make test"
         1136  +- create also a link for libz.so.1
         1137  +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
         1138  +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
         1139  +- added -soname for Linux in configure (Chun-Chung Chen,
         1140  +- assign numbers to the exported functions in zlib.def (for Windows DLL)
         1141  +- add advice in zlib.h for best usage of deflateSetDictionary
         1142  +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
         1143  +- allow compilation with ANSI keywords only enabled for TurboC in large model
         1144  +- avoid "versionString"[0] (Borland bug)
         1145  +- add NEED_DUMMY_RETURN for Borland
         1146  +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
         1147  +- allow compilation with CC
         1148  +- defined STDC for OS/2 (David Charlap)
         1149  +- limit external names to 8 chars for MVS (Thomas Lund)
         1150  +- in minigzip.c, use static buffers only for 16-bit systems
         1151  +- fix suffix check for "minigzip -d foo.gz"
         1152  +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
         1153  +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
         1154  +- added makelcc.bat for lcc-win32 (Tom St Denis)
         1155  +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
         1156  +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
         1157  +- check for unistd.h in configure (for off_t)
         1158  +- remove useless check parameter in inflate_blocks_free
         1159  +- avoid useless assignment of s->check to itself in inflate_blocks_new
         1160  +- do not flush twice in gzclose (thanks to Ken Raeburn)
         1161  +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
         1162  +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
         1163  +- work around buggy fclose on pipes for HP/UX
         1164  +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
         1165  +- fix configure if CC is already equal to gcc
         1166  +
         1167  +Changes in 1.0.5 (3 Jan 98)
         1168  +- Fix inflate to terminate gracefully when fed corrupted or invalid data
         1169  +- Use const for rommable constants in inflate
         1170  +- Eliminate memory leaks on error conditions in inflate
         1171  +- Removed some vestigial code in inflate
         1172  +- Update web address in README
         1173  +
         1174  +Changes in 1.0.4 (24 Jul 96)
         1175  +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
         1176  +  bit, so the decompressor could decompress all the correct data but went
         1177  +  on to attempt decompressing extra garbage data. This affected minigzip too.
         1178  +- zlibVersion and gzerror return const char* (needed for DLL)
         1179  +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
         1180  +- use z_error only for DEBUG (avoid problem with DLLs)
         1181  +
         1182  +Changes in 1.0.3 (2 Jul 96)
         1183  +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
         1184  +  small and medium models; this makes the library incompatible with previous
         1185  +  versions for these models. (No effect in large model or on other systems.)
         1186  +- return OK instead of BUF_ERROR if previous deflate call returned with
         1187  +  avail_out as zero but there is nothing to do
         1188  +- added memcmp for non STDC compilers
         1189  +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
         1190  +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
         1191  +- better check for 16-bit mode MSC (avoids problem with Symantec)
         1192  +
         1193  +Changes in 1.0.2 (23 May 96)
         1194  +- added Windows DLL support
         1195  +- added a function zlibVersion (for the DLL support)
         1196  +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
         1197  +- Bytef is define's instead of typedef'd only for Borland C
         1198  +- avoid reading uninitialized memory in example.c
         1199  +- mention in README that the zlib format is now RFC1950
         1200  +- updated Makefile.dj2
         1201  +- added algorithm.doc
         1202  +
         1203  +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
         1204  +- fix array overlay in deflate.c which sometimes caused bad compressed data
         1205  +- fix inflate bug with empty stored block
         1206  +- fix MSDOS medium model which was broken in 0.99
         1207  +- fix deflateParams() which could generated bad compressed data.
         1208  +- Bytef is define'd instead of typedef'ed (work around Borland bug)
         1209  +- added an INDEX file
         1210  +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
         1211  +  Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
         1212  +- speed up adler32 for modern machines without auto-increment
         1213  +- added -ansi for IRIX in configure
         1214  +- static_init_done in trees.c is an int
         1215  +- define unlink as delete for VMS
         1216  +- fix configure for QNX
         1217  +- add configure branch for SCO and HPUX
         1218  +- avoid many warnings (unused variables, dead assignments, etc...)
         1219  +- no fdopen for BeOS
         1220  +- fix the Watcom fix for 32 bit mode (define FAR as empty)
         1221  +- removed redefinition of Byte for MKWERKS
         1222  +- work around an MWKERKS bug (incorrect merge of all .h files)
         1223  +
         1224  +Changes in 0.99 (27 Jan 96)
         1225  +- allow preset dictionary shared between compressor and decompressor
         1226  +- allow compression level 0 (no compression)
         1227  +- add deflateParams in zlib.h: allow dynamic change of compression level
         1228  +  and compression strategy.
         1229  +- test large buffers and deflateParams in example.c
         1230  +- add optional "configure" to build zlib as a shared library
         1231  +- suppress Makefile.qnx, use configure instead
         1232  +- fixed deflate for 64-bit systems (detected on Cray)
         1233  +- fixed inflate_blocks for 64-bit systems (detected on Alpha)
         1234  +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
         1235  +- always return Z_BUF_ERROR when deflate() has nothing to do
         1236  +- deflateInit and inflateInit are now macros to allow version checking
         1237  +- prefix all global functions and types with z_ with -DZ_PREFIX
         1238  +- make falloc completely reentrant (inftrees.c)
         1239  +- fixed very unlikely race condition in ct_static_init
         1240  +- free in reverse order of allocation to help memory manager
         1241  +- use zlib-1.0/* instead of zlib/* inside the tar.gz
         1242  +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
         1243  +  -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
         1244  +- allow gzread on concatenated .gz files
         1245  +- deflateEnd now returns Z_DATA_ERROR if it was premature
         1246  +- deflate is finally (?) fully deterministic (no matches beyond end of input)
         1247  +- Document Z_SYNC_FLUSH
         1248  +- add uninstall in Makefile
         1249  +- Check for __cpluplus in zlib.h
         1250  +- Better test in ct_align for partial flush
         1251  +- avoid harmless warnings for Borland C++
         1252  +- initialize hash_head in deflate.c
         1253  +- avoid warning on fdopen (gzio.c) for HP cc -Aa
         1254  +- include stdlib.h for STDC compilers
         1255  +- include errno.h for Cray
         1256  +- ignore error if ranlib doesn't exist
         1257  +- call ranlib twice for NeXTSTEP
         1258  +- use exec_prefix instead of prefix for libz.a
         1259  +- renamed ct_* as _tr_* to avoid conflict with applications
         1260  +- clear z->msg in inflateInit2 before any error return
         1261  +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
         1262  +- fixed typo in zconf.h (_GNUC__ => __GNUC__)
         1263  +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
         1264  +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
         1265  +- in fcalloc, normalize pointer if size > 65520 bytes
         1266  +- don't use special fcalloc for 32 bit Borland C++
         1267  +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
         1268  +- use Z_BINARY instead of BINARY
         1269  +- document that gzclose after gzdopen will close the file
         1270  +- allow "a" as mode in gzopen.
         1271  +- fix error checking in gzread
         1272  +- allow skipping .gz extra-field on pipes
         1273  +- added reference to Perl interface in README
         1274  +- put the crc table in FAR data (I dislike more and more the medium model :)
         1275  +- added get_crc_table
         1276  +- added a dimension to all arrays (Borland C can't count).
         1277  +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
         1278  +- guard against multiple inclusion of *.h (for precompiled header on Mac)
         1279  +- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
         1280  +- don't use unsized arrays to avoid silly warnings by Visual C++:
         1281  +     warning C4746: 'inflate_mask' : unsized array treated as  '__far'
         1282  +     (what's wrong with far data in far model?).
         1283  +- define enum out of inflate_blocks_state to allow compilation with C++
         1284  +
         1285  +Changes in 0.95 (16 Aug 95)
         1286  +- fix MSDOS small and medium model (now easier to adapt to any compiler)
         1287  +- inlined send_bits
         1288  +- fix the final (:-) bug for deflate with flush (output was correct but
         1289  +  not completely flushed in rare occasions).
         1290  +- default window size is same for compression and decompression
         1291  +  (it's now sufficient to set MAX_WBITS in zconf.h).
         1292  +- voidp -> voidpf and voidnp -> voidp (for consistency with other
         1293  +  typedefs and because voidnp was not near in large model).
         1294  +
         1295  +Changes in 0.94 (13 Aug 95)
         1296  +- support MSDOS medium model
         1297  +- fix deflate with flush (could sometimes generate bad output)
         1298  +- fix deflateReset (zlib header was incorrectly suppressed)
         1299  +- added support for VMS
         1300  +- allow a compression level in gzopen()
         1301  +- gzflush now calls fflush
         1302  +- For deflate with flush, flush even if no more input is provided.
         1303  +- rename libgz.a as libz.a
         1304  +- avoid complex expression in infcodes.c triggering Turbo C bug
         1305  +- work around a problem with gcc on Alpha (in INSERT_STRING)
         1306  +- don't use inline functions (problem with some gcc versions)
         1307  +- allow renaming of Byte, uInt, etc... with #define.
         1308  +- avoid warning about (unused) pointer before start of array in deflate.c
         1309  +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
         1310  +- avoid reserved word 'new' in trees.c
         1311  +
         1312  +Changes in 0.93 (25 June 95)
         1313  +- temporarily disable inline functions
         1314  +- make deflate deterministic
         1315  +- give enough lookahead for PARTIAL_FLUSH
         1316  +- Set binary mode for stdin/stdout in minigzip.c for OS/2
         1317  +- don't even use signed char in inflate (not portable enough)
         1318  +- fix inflate memory leak for segmented architectures
         1319  +
         1320  +Changes in 0.92 (3 May 95)
         1321  +- don't assume that char is signed (problem on SGI)
         1322  +- Clear bit buffer when starting a stored block
         1323  +- no memcpy on Pyramid
         1324  +- suppressed inftest.c
         1325  +- optimized fill_window, put longest_match inline for gcc
         1326  +- optimized inflate on stored blocks.
         1327  +- untabify all sources to simplify patches
         1328  +
         1329  +Changes in 0.91 (2 May 95)
         1330  +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
         1331  +- Document the memory requirements in zconf.h
         1332  +- added "make install"
         1333  +- fix sync search logic in inflateSync
         1334  +- deflate(Z_FULL_FLUSH) now works even if output buffer too short
         1335  +- after inflateSync, don't scare people with just "lo world"
         1336  +- added support for DJGPP
         1337  +
         1338  +Changes in 0.9 (1 May 95)
         1339  +- don't assume that zalloc clears the allocated memory (the TurboC bug
         1340  +  was Mark's bug after all :)
         1341  +- let again gzread copy uncompressed data unchanged (was working in 0.71)
         1342  +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
         1343  +- added a test of inflateSync in example.c
         1344  +- moved MAX_WBITS to zconf.h because users might want to change that.
         1345  +- document explicitly that zalloc(64K) on MSDOS must return a normalized
         1346  +  pointer (zero offset)
         1347  +- added Makefiles for Microsoft C, Turbo C, Borland C++
         1348  +- faster crc32()
         1349  +
         1350  +Changes in 0.8 (29 April 95)
         1351  +- added fast inflate (inffast.c)
         1352  +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
         1353  +  is incompatible with previous versions of zlib which returned Z_OK.
         1354  +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
         1355  +  (actually that was not a compiler bug, see 0.81 above)
         1356  +- gzread no longer reads one extra byte in certain cases
         1357  +- In gzio destroy(), don't reference a freed structure
         1358  +- avoid many warnings for MSDOS
         1359  +- avoid the ERROR symbol which is used by MS Windows
         1360  +
         1361  +Changes in 0.71 (14 April 95)
         1362  +- Fixed more MSDOS compilation problems :( There is still a bug with
         1363  +  TurboC large model.
         1364  +
         1365  +Changes in 0.7 (14 April 95)
         1366  +- Added full inflate support.
         1367  +- Simplified the crc32() interface. The pre- and post-conditioning
         1368  +  (one's complement) is now done inside crc32(). WARNING: this is
         1369  +  incompatible with previous versions; see zlib.h for the new usage.
         1370  +
         1371  +Changes in 0.61 (12 April 95)
         1372  +- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
         1373  +
         1374  +Changes in 0.6 (11 April 95)
         1375  +- added minigzip.c
         1376  +- added gzdopen to reopen a file descriptor as gzFile
         1377  +- added transparent reading of non-gziped files in gzread.
         1378  +- fixed bug in gzread (don't read crc as data)
         1379  +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
         1380  +- don't allocate big arrays in the stack (for MSDOS)
         1381  +- fix some MSDOS compilation problems
         1382  +
         1383  +Changes in 0.5:
         1384  +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
         1385  +  not yet Z_FULL_FLUSH.
         1386  +- support decompression but only in a single step (forced Z_FINISH)
         1387  +- added opaque object for zalloc and zfree.
         1388  +- added deflateReset and inflateReset
         1389  +- added a variable zlib_version for consistency checking.
         1390  +- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
         1391  +  Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
         1392  +
         1393  +Changes in 0.4:
         1394  +- avoid "zip" everywhere, use zlib instead of ziplib.
         1395  +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
         1396  +  if compression method == 8.
         1397  +- added adler32 and crc32
         1398  +- renamed deflateOptions as deflateInit2, call one or the other but not both
         1399  +- added the method parameter for deflateInit2.
         1400  +- added inflateInit2
         1401  +- simplied considerably deflateInit and inflateInit by not supporting
         1402  +  user-provided history buffer. This is supported only in deflateInit2
         1403  +  and inflateInit2.
         1404  +
         1405  +Changes in 0.3:
         1406  +- prefix all macro names with Z_
         1407  +- use Z_FINISH instead of deflateEnd to finish compression.
         1408  +- added Z_HUFFMAN_ONLY
         1409  +- added gzerror()

Added compat/zlib/FAQ.

            1  +
            2  +                Frequently Asked Questions about zlib
            3  +
            4  +
            5  +If your question is not there, please check the zlib home page
            6  +http://zlib.net/ which may have more recent information.
            7  +The lastest zlib FAQ is at http://zlib.net/zlib_faq.html
            8  +
            9  +
           10  + 1. Is zlib Y2K-compliant?
           11  +
           12  +    Yes. zlib doesn't handle dates.
           13  +
           14  + 2. Where can I get a Windows DLL version?
           15  +
           16  +    The zlib sources can be compiled without change to produce a DLL.  See the
           17  +    file win32/DLL_FAQ.txt in the zlib distribution.  Pointers to the
           18  +    precompiled DLL are found in the zlib web site at http://zlib.net/ .
           19  +
           20  + 3. Where can I get a Visual Basic interface to zlib?
           21  +
           22  +    See
           23  +        * http://marknelson.us/1997/01/01/zlib-engine/
           24  +        * win32/DLL_FAQ.txt in the zlib distribution
           25  +
           26  + 4. compress() returns Z_BUF_ERROR.
           27  +
           28  +    Make sure that before the call of compress(), the length of the compressed
           29  +    buffer is equal to the available size of the compressed buffer and not
           30  +    zero.  For Visual Basic, check that this parameter is passed by reference
           31  +    ("as any"), not by value ("as long").
           32  +
           33  + 5. deflate() or inflate() returns Z_BUF_ERROR.
           34  +
           35  +    Before making the call, make sure that avail_in and avail_out are not zero.
           36  +    When setting the parameter flush equal to Z_FINISH, also make sure that
           37  +    avail_out is big enough to allow processing all pending input.  Note that a
           38  +    Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be
           39  +    made with more input or output space.  A Z_BUF_ERROR may in fact be
           40  +    unavoidable depending on how the functions are used, since it is not
           41  +    possible to tell whether or not there is more output pending when
           42  +    strm.avail_out returns with zero.  See http://zlib.net/zlib_how.html for a
           43  +    heavily annotated example.
           44  +
           45  + 6. Where's the zlib documentation (man pages, etc.)?
           46  +
           47  +    It's in zlib.h .  Examples of zlib usage are in the files test/example.c
           48  +    and test/minigzip.c, with more in examples/ .
           49  +
           50  + 7. Why don't you use GNU autoconf or libtool or ...?
           51  +
           52  +    Because we would like to keep zlib as a very small and simple package.
           53  +    zlib is rather portable and doesn't need much configuration.
           54  +
           55  + 8. I found a bug in zlib.
           56  +
           57  +    Most of the time, such problems are due to an incorrect usage of zlib.
           58  +    Please try to reproduce the problem with a small program and send the
           59  +    corresponding source to us at zlib@gzip.org .  Do not send multi-megabyte
           60  +    data files without prior agreement.
           61  +
           62  + 9. Why do I get "undefined reference to gzputc"?
           63  +
           64  +    If "make test" produces something like
           65  +
           66  +       example.o(.text+0x154): undefined reference to `gzputc'
           67  +
           68  +    check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
           69  +    /usr/X11R6/lib. Remove any old versions, then do "make install".
           70  +
           71  +10. I need a Delphi interface to zlib.
           72  +
           73  +    See the contrib/delphi directory in the zlib distribution.
           74  +
           75  +11. Can zlib handle .zip archives?
           76  +
           77  +    Not by itself, no.  See the directory contrib/minizip in the zlib
           78  +    distribution.
           79  +
           80  +12. Can zlib handle .Z files?
           81  +
           82  +    No, sorry.  You have to spawn an uncompress or gunzip subprocess, or adapt
           83  +    the code of uncompress on your own.
           84  +
           85  +13. How can I make a Unix shared library?
           86  +
           87  +    By default a shared (and a static) library is built for Unix.  So:
           88  +
           89  +    make distclean
           90  +    ./configure
           91  +    make
           92  +
           93  +14. How do I install a shared zlib library on Unix?
           94  +
           95  +    After the above, then:
           96  +
           97  +    make install
           98  +
           99  +    However, many flavors of Unix come with a shared zlib already installed.
          100  +    Before going to the trouble of compiling a shared version of zlib and
          101  +    trying to install it, you may want to check if it's already there!  If you
          102  +    can #include <zlib.h>, it's there.  The -lz option will probably link to
          103  +    it.  You can check the version at the top of zlib.h or with the
          104  +    ZLIB_VERSION symbol defined in zlib.h .
          105  +
          106  +15. I have a question about OttoPDF.
          107  +
          108  +    We are not the authors of OttoPDF. The real author is on the OttoPDF web
          109  +    site: Joel Hainley, jhainley@myndkryme.com.
          110  +
          111  +16. Can zlib decode Flate data in an Adobe PDF file?
          112  +
          113  +    Yes. See http://www.pdflib.com/ . To modify PDF forms, see
          114  +    http://sourceforge.net/projects/acroformtool/ .
          115  +
          116  +17. Why am I getting this "register_frame_info not found" error on Solaris?
          117  +
          118  +    After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib
          119  +    generates an error such as:
          120  +
          121  +        ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so:
          122  +        symbol __register_frame_info: referenced symbol not found
          123  +
          124  +    The symbol __register_frame_info is not part of zlib, it is generated by
          125  +    the C compiler (cc or gcc).  You must recompile applications using zlib
          126  +    which have this problem.  This problem is specific to Solaris.  See
          127  +    http://www.sunfreeware.com for Solaris versions of zlib and applications
          128  +    using zlib.
          129  +
          130  +18. Why does gzip give an error on a file I make with compress/deflate?
          131  +
          132  +    The compress and deflate functions produce data in the zlib format, which
          133  +    is different and incompatible with the gzip format.  The gz* functions in
          134  +    zlib on the other hand use the gzip format.  Both the zlib and gzip formats
          135  +    use the same compressed data format internally, but have different headers
          136  +    and trailers around the compressed data.
          137  +
          138  +19. Ok, so why are there two different formats?
          139  +
          140  +    The gzip format was designed to retain the directory information about a
          141  +    single file, such as the name and last modification date.  The zlib format
          142  +    on the other hand was designed for in-memory and communication channel
          143  +    applications, and has a much more compact header and trailer and uses a
          144  +    faster integrity check than gzip.
          145  +
          146  +20. Well that's nice, but how do I make a gzip file in memory?
          147  +
          148  +    You can request that deflate write the gzip format instead of the zlib
          149  +    format using deflateInit2().  You can also request that inflate decode the
          150  +    gzip format using inflateInit2().  Read zlib.h for more details.
          151  +
          152  +21. Is zlib thread-safe?
          153  +
          154  +    Yes.  However any library routines that zlib uses and any application-
          155  +    provided memory allocation routines must also be thread-safe.  zlib's gz*
          156  +    functions use stdio library routines, and most of zlib's functions use the
          157  +    library memory allocation routines by default.  zlib's *Init* functions
          158  +    allow for the application to provide custom memory allocation routines.
          159  +
          160  +    Of course, you should only operate on any given zlib or gzip stream from a
          161  +    single thread at a time.
          162  +
          163  +22. Can I use zlib in my commercial application?
          164  +
          165  +    Yes.  Please read the license in zlib.h.
          166  +
          167  +23. Is zlib under the GNU license?
          168  +
          169  +    No.  Please read the license in zlib.h.
          170  +
          171  +24. The license says that altered source versions must be "plainly marked". So
          172  +    what exactly do I need to do to meet that requirement?
          173  +
          174  +    You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h.  In
          175  +    particular, the final version number needs to be changed to "f", and an
          176  +    identification string should be appended to ZLIB_VERSION.  Version numbers
          177  +    x.x.x.f are reserved for modifications to zlib by others than the zlib
          178  +    maintainers.  For example, if the version of the base zlib you are altering
          179  +    is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and
          180  +    ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3".  You can also
          181  +    update the version strings in deflate.c and inftrees.c.
          182  +
          183  +    For altered source distributions, you should also note the origin and
          184  +    nature of the changes in zlib.h, as well as in ChangeLog and README, along
          185  +    with the dates of the alterations.  The origin should include at least your
          186  +    name (or your company's name), and an email address to contact for help or
          187  +    issues with the library.
          188  +
          189  +    Note that distributing a compiled zlib library along with zlib.h and
          190  +    zconf.h is also a source distribution, and so you should change
          191  +    ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes
          192  +    in zlib.h as you would for a full source distribution.
          193  +
          194  +25. Will zlib work on a big-endian or little-endian architecture, and can I
          195  +    exchange compressed data between them?
          196  +
          197  +    Yes and yes.
          198  +
          199  +26. Will zlib work on a 64-bit machine?
          200  +
          201  +    Yes.  It has been tested on 64-bit machines, and has no dependence on any
          202  +    data types being limited to 32-bits in length.  If you have any
          203  +    difficulties, please provide a complete problem report to zlib@gzip.org
          204  +
          205  +27. Will zlib decompress data from the PKWare Data Compression Library?
          206  +
          207  +    No.  The PKWare DCL uses a completely different compressed data format than
          208  +    does PKZIP and zlib.  However, you can look in zlib's contrib/blast
          209  +    directory for a possible solution to your problem.
          210  +
          211  +28. Can I access data randomly in a compressed stream?
          212  +
          213  +    No, not without some preparation.  If when compressing you periodically use
          214  +    Z_FULL_FLUSH, carefully write all the pending data at those points, and
          215  +    keep an index of those locations, then you can start decompression at those
          216  +    points.  You have to be careful to not use Z_FULL_FLUSH too often, since it
          217  +    can significantly degrade compression.  Alternatively, you can scan a
          218  +    deflate stream once to generate an index, and then use that index for
          219  +    random access.  See examples/zran.c .
          220  +
          221  +29. Does zlib work on MVS, OS/390, CICS, etc.?
          222  +
          223  +    It has in the past, but we have not heard of any recent evidence.  There
          224  +    were working ports of zlib 1.1.4 to MVS, but those links no longer work.
          225  +    If you know of recent, successful applications of zlib on these operating
          226  +    systems, please let us know.  Thanks.
          227  +
          228  +30. Is there some simpler, easier to read version of inflate I can look at to
          229  +    understand the deflate format?
          230  +
          231  +    First off, you should read RFC 1951.  Second, yes.  Look in zlib's
          232  +    contrib/puff directory.
          233  +
          234  +31. Does zlib infringe on any patents?
          235  +
          236  +    As far as we know, no.  In fact, that was originally the whole point behind
          237  +    zlib.  Look here for some more information:
          238  +
          239  +    http://www.gzip.org/#faq11
          240  +
          241  +32. Can zlib work with greater than 4 GB of data?
          242  +
          243  +    Yes.  inflate() and deflate() will process any amount of data correctly.
          244  +    Each call of inflate() or deflate() is limited to input and output chunks
          245  +    of the maximum value that can be stored in the compiler's "unsigned int"
          246  +    type, but there is no limit to the number of chunks.  Note however that the
          247  +    strm.total_in and strm_total_out counters may be limited to 4 GB.  These
          248  +    counters are provided as a convenience and are not used internally by
          249  +    inflate() or deflate().  The application can easily set up its own counters
          250  +    updated after each call of inflate() or deflate() to count beyond 4 GB.
          251  +    compress() and uncompress() may be limited to 4 GB, since they operate in a
          252  +    single call.  gzseek() and gztell() may be limited to 4 GB depending on how
          253  +    zlib is compiled.  See the zlibCompileFlags() function in zlib.h.
          254  +
          255  +    The word "may" appears several times above since there is a 4 GB limit only
          256  +    if the compiler's "long" type is 32 bits.  If the compiler's "long" type is
          257  +    64 bits, then the limit is 16 exabytes.
          258  +
          259  +33. Does zlib have any security vulnerabilities?
          260  +
          261  +    The only one that we are aware of is potentially in gzprintf().  If zlib is
          262  +    compiled to use sprintf() or vsprintf(), then there is no protection
          263  +    against a buffer overflow of an 8K string space (or other value as set by
          264  +    gzbuffer()), other than the caller of gzprintf() assuring that the output
          265  +    will not exceed 8K.  On the other hand, if zlib is compiled to use
          266  +    snprintf() or vsnprintf(), which should normally be the case, then there is
          267  +    no vulnerability.  The ./configure script will display warnings if an
          268  +    insecure variation of sprintf() will be used by gzprintf().  Also the
          269  +    zlibCompileFlags() function will return information on what variant of
          270  +    sprintf() is used by gzprintf().
          271  +
          272  +    If you don't have snprintf() or vsnprintf() and would like one, you can
          273  +    find a portable implementation here:
          274  +
          275  +        http://www.ijs.si/software/snprintf/
          276  +
          277  +    Note that you should be using the most recent version of zlib.  Versions
          278  +    1.1.3 and before were subject to a double-free vulnerability, and versions
          279  +    1.2.1 and 1.2.2 were subject to an access exception when decompressing
          280  +    invalid compressed data.
          281  +
          282  +34. Is there a Java version of zlib?
          283  +
          284  +    Probably what you want is to use zlib in Java. zlib is already included
          285  +    as part of the Java SDK in the java.util.zip package. If you really want
          286  +    a version of zlib written in the Java language, look on the zlib home
          287  +    page for links: http://zlib.net/ .
          288  +
          289  +35. I get this or that compiler or source-code scanner warning when I crank it
          290  +    up to maximally-pedantic. Can't you guys write proper code?
          291  +
          292  +    Many years ago, we gave up attempting to avoid warnings on every compiler
          293  +    in the universe.  It just got to be a waste of time, and some compilers
          294  +    were downright silly as well as contradicted each other.  So now, we simply
          295  +    make sure that the code always works.
          296  +
          297  +36. Valgrind (or some similar memory access checker) says that deflate is
          298  +    performing a conditional jump that depends on an uninitialized value.
          299  +    Isn't that a bug?
          300  +
          301  +    No.  That is intentional for performance reasons, and the output of deflate
          302  +    is not affected.  This only started showing up recently since zlib 1.2.x
          303  +    uses malloc() by default for allocations, whereas earlier versions used
          304  +    calloc(), which zeros out the allocated memory.  Even though the code was
          305  +    correct, versions 1.2.4 and later was changed to not stimulate these
          306  +    checkers.
          307  +
          308  +37. Will zlib read the (insert any ancient or arcane format here) compressed
          309  +    data format?
          310  +
          311  +    Probably not. Look in the comp.compression FAQ for pointers to various
          312  +    formats and associated software.
          313  +
          314  +38. How can I encrypt/decrypt zip files with zlib?
          315  +
          316  +    zlib doesn't support encryption.  The original PKZIP encryption is very
          317  +    weak and can be broken with freely available programs.  To get strong
          318  +    encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib
          319  +    compression.  For PKZIP compatible "encryption", look at
          320  +    http://www.info-zip.org/
          321  +
          322  +39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
          323  +
          324  +    "gzip" is the gzip format, and "deflate" is the zlib format.  They should
          325  +    probably have called the second one "zlib" instead to avoid confusion with
          326  +    the raw deflate compressed data format.  While the HTTP 1.1 RFC 2616
          327  +    correctly points to the zlib specification in RFC 1950 for the "deflate"
          328  +    transfer encoding, there have been reports of servers and browsers that
          329  +    incorrectly produce or expect raw deflate data per the deflate
          330  +    specification in RFC 1951, most notably Microsoft.  So even though the
          331  +    "deflate" transfer encoding using the zlib format would be the more
          332  +    efficient approach (and in fact exactly what the zlib format was designed
          333  +    for), using the "gzip" transfer encoding is probably more reliable due to
          334  +    an unfortunate choice of name on the part of the HTTP 1.1 authors.
          335  +
          336  +    Bottom line: use the gzip format for HTTP 1.1 encoding.
          337  +
          338  +40. Does zlib support the new "Deflate64" format introduced by PKWare?
          339  +
          340  +    No.  PKWare has apparently decided to keep that format proprietary, since
          341  +    they have not documented it as they have previous compression formats.  In
          342  +    any case, the compression improvements are so modest compared to other more
          343  +    modern approaches, that it's not worth the effort to implement.
          344  +
          345  +41. I'm having a problem with the zip functions in zlib, can you help?
          346  +
          347  +    There are no zip functions in zlib.  You are probably using minizip by
          348  +    Giles Vollant, which is found in the contrib directory of zlib.  It is not
          349  +    part of zlib.  In fact none of the stuff in contrib is part of zlib.  The
          350  +    files in there are not supported by the zlib authors.  You need to contact
          351  +    the authors of the respective contribution for help.
          352  +
          353  +42. The match.asm code in contrib is under the GNU General Public License.
          354  +    Since it's part of zlib, doesn't that mean that all of zlib falls under the
          355  +    GNU GPL?
          356  +
          357  +    No.  The files in contrib are not part of zlib.  They were contributed by
          358  +    other authors and are provided as a convenience to the user within the zlib
          359  +    distribution.  Each item in contrib has its own license.
          360  +
          361  +43. Is zlib subject to export controls?  What is its ECCN?
          362  +
          363  +    zlib is not subject to export controls, and so is classified as EAR99.
          364  +
          365  +44. Can you please sign these lengthy legal documents and fax them back to us
          366  +    so that we can use your software in our product?
          367  +
          368  +    No. Go away. Shoo.

Added compat/zlib/INDEX.

            1  +CMakeLists.txt  cmake build file
            2  +ChangeLog       history of changes
            3  +FAQ             Frequently Asked Questions about zlib
            4  +INDEX           this file
            5  +Makefile        dummy Makefile that tells you to ./configure
            6  +Makefile.in     template for Unix Makefile
            7  +README          guess what
            8  +configure       configure script for Unix
            9  +make_vms.com    makefile for VMS
           10  +test/example.c  zlib usages examples for build testing
           11  +test/minigzip.c minimal gzip-like functionality for build testing
           12  +test/infcover.c inf*.c code coverage for build coverage testing
           13  +treebuild.xml   XML description of source file dependencies
           14  +zconf.h.cmakein zconf.h template for cmake
           15  +zconf.h.in      zconf.h template for configure
           16  +zlib.3          Man page for zlib
           17  +zlib.3.pdf      Man page in PDF format
           18  +zlib.map        Linux symbol information
           19  +zlib.pc.in      Template for pkg-config descriptor
           20  +zlib.pc.cmakein zlib.pc template for cmake
           21  +zlib2ansi       perl script to convert source files for C++ compilation
           22  +
           23  +amiga/          makefiles for Amiga SAS C
           24  +as400/          makefiles for AS/400
           25  +doc/            documentation for formats and algorithms
           26  +msdos/          makefiles for MSDOS
           27  +nintendods/     makefile for Nintendo DS
           28  +old/            makefiles for various architectures and zlib documentation
           29  +                files that have not yet been updated for zlib 1.2.x
           30  +qnx/            makefiles for QNX
           31  +watcom/         makefiles for OpenWatcom
           32  +win32/          makefiles for Windows
           33  +
           34  +                zlib public header files (required for library use):
           35  +zconf.h
           36  +zlib.h
           37  +
           38  +                private source files used to build the zlib library:
           39  +adler32.c
           40  +compress.c
           41  +crc32.c
           42  +crc32.h
           43  +deflate.c
           44  +deflate.h
           45  +gzclose.c
           46  +gzguts.h
           47  +gzlib.c
           48  +gzread.c
           49  +gzwrite.c
           50  +infback.c
           51  +inffast.c
           52  +inffast.h
           53  +inffixed.h
           54  +inflate.c
           55  +inflate.h
           56  +inftrees.c
           57  +inftrees.h
           58  +trees.c
           59  +trees.h
           60  +uncompr.c
           61  +zutil.c
           62  +zutil.h
           63  +
           64  +                source files for sample programs
           65  +See examples/README.examples
           66  +
           67  +                unsupported contributions by third parties
           68  +See contrib/README.contrib

Added compat/zlib/Makefile.

            1  +all:
            2  +	-@echo "Please use ./configure first.  Thank you."
            3  +
            4  +distclean:
            5  +	make -f Makefile.in distclean

Added compat/zlib/Makefile.in.

            1  +# Makefile for zlib
            2  +# Copyright (C) 1995-2011 Jean-loup Gailly.
            3  +# For conditions of distribution and use, see copyright notice in zlib.h
            4  +
            5  +# To compile and test, type:
            6  +#    ./configure; make test
            7  +# Normally configure builds both a static and a shared library.
            8  +# If you want to build just a static library, use: ./configure --static
            9  +
           10  +# To use the asm code, type:
           11  +#    cp contrib/asm?86/match.S ./match.S
           12  +#    make LOC=-DASMV OBJA=match.o
           13  +
           14  +# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
           15  +#    make install
           16  +# To install in $HOME instead of /usr/local, use:
           17  +#    make install prefix=$HOME
           18  +
           19  +CC=cc
           20  +
           21  +CFLAGS=-O
           22  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
           23  +#CFLAGS=-g -DDEBUG
           24  +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
           25  +#           -Wstrict-prototypes -Wmissing-prototypes
           26  +
           27  +SFLAGS=-O
           28  +LDFLAGS=
           29  +TEST_LDFLAGS=-L. libz.a
           30  +LDSHARED=$(CC)
           31  +CPP=$(CC) -E
           32  +
           33  +STATICLIB=libz.a
           34  +SHAREDLIB=libz.so
           35  +SHAREDLIBV=libz.so.1.2.7
           36  +SHAREDLIBM=libz.so.1
           37  +LIBS=$(STATICLIB) $(SHAREDLIBV)
           38  +
           39  +AR=ar
           40  +ARFLAGS=rc
           41  +RANLIB=ranlib
           42  +LDCONFIG=ldconfig
           43  +LDSHAREDLIBC=-lc
           44  +TAR=tar
           45  +SHELL=/bin/sh
           46  +EXE=
           47  +
           48  +prefix = /usr/local
           49  +exec_prefix = ${prefix}
           50  +libdir = ${exec_prefix}/lib
           51  +sharedlibdir = ${libdir}
           52  +includedir = ${prefix}/include
           53  +mandir = ${prefix}/share/man
           54  +man3dir = ${mandir}/man3
           55  +pkgconfigdir = ${libdir}/pkgconfig
           56  +
           57  +OBJZ = adler32.o crc32.o deflate.o infback.o inffast.o inflate.o inftrees.o trees.o zutil.o
           58  +OBJG = compress.o uncompr.o gzclose.o gzlib.o gzread.o gzwrite.o
           59  +OBJC = $(OBJZ) $(OBJG)
           60  +
           61  +PIC_OBJZ = adler32.lo crc32.lo deflate.lo infback.lo inffast.lo inflate.lo inftrees.lo trees.lo zutil.lo
           62  +PIC_OBJG = compress.lo uncompr.lo gzclose.lo gzlib.lo gzread.lo gzwrite.lo
           63  +PIC_OBJC = $(PIC_OBJZ) $(PIC_OBJG)
           64  +
           65  +# to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo
           66  +OBJA =
           67  +PIC_OBJA =
           68  +
           69  +OBJS = $(OBJC) $(OBJA)
           70  +
           71  +PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA)
           72  +
           73  +all: static shared
           74  +
           75  +static: example$(EXE) minigzip$(EXE)
           76  +
           77  +shared: examplesh$(EXE) minigzipsh$(EXE)
           78  +
           79  +all64: example64$(EXE) minigzip64$(EXE)
           80  +
           81  +check: test
           82  +
           83  +test: all teststatic testshared
           84  +
           85  +teststatic: static
           86  +	@TMPST=`mktemp fooXXXXXX`; \
           87  +	if echo hello world | ./minigzip | ./minigzip -d && ./example $$TMPST ; then \
           88  +	  echo '		*** zlib test OK ***'; \
           89  +	else \
           90  +	  echo '		*** zlib test FAILED ***'; false; \
           91  +	fi; \
           92  +	rm -f $$TMPST
           93  +
           94  +testshared: shared
           95  +	@LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
           96  +	LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \
           97  +	DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \
           98  +	SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \
           99  +	TMPSH=`mktemp fooXXXXXX`; \
          100  +	if echo hello world | ./minigzipsh | ./minigzipsh -d && ./examplesh $$TMPSH; then \
          101  +	  echo '		*** zlib shared test OK ***'; \
          102  +	else \
          103  +	  echo '		*** zlib shared test FAILED ***'; false; \
          104  +	fi; \
          105  +	rm -f $$TMPSH
          106  +
          107  +test64: all64
          108  +	@TMP64=`mktemp fooXXXXXX`; \
          109  +	if echo hello world | ./minigzip64 | ./minigzip64 -d && ./example64 $$TMP64; then \
          110  +	  echo '		*** zlib 64-bit test OK ***'; \
          111  +	else \
          112  +	  echo '		*** zlib 64-bit test FAILED ***'; false; \
          113  +	fi; \
          114  +	rm -f $$TMP64
          115  +
          116  +infcover.o: test/infcover.c zlib.h zconf.h
          117  +	$(CC) $(CFLAGS) -I. -c -o $@ test/infcover.c
          118  +
          119  +infcover: infcover.o libz.a
          120  +	$(CC) $(CFLAGS) -o $@ infcover.o libz.a
          121  +
          122  +cover: infcover
          123  +	rm -f *.gcda
          124  +	./infcover
          125  +	gcov inf*.c
          126  +
          127  +libz.a: $(OBJS)
          128  +	$(AR) $(ARFLAGS) $@ $(OBJS)
          129  +	-@ ($(RANLIB) $@ || true) >/dev/null 2>&1
          130  +
          131  +match.o: match.S
          132  +	$(CPP) match.S > _match.s
          133  +	$(CC) -c _match.s
          134  +	mv _match.o match.o
          135  +	rm -f _match.s
          136  +
          137  +match.lo: match.S
          138  +	$(CPP) match.S > _match.s
          139  +	$(CC) -c -fPIC _match.s
          140  +	mv _match.o match.lo
          141  +	rm -f _match.s
          142  +
          143  +example.o: test/example.c zlib.h zconf.h
          144  +	$(CC) $(CFLAGS) -I. -c -o $@ test/example.c
          145  +
          146  +minigzip.o: test/minigzip.c zlib.h zconf.h
          147  +	$(CC) $(CFLAGS) -I. -c -o $@ test/minigzip.c
          148  +
          149  +example64.o: test/example.c zlib.h zconf.h
          150  +	$(CC) $(CFLAGS) -I. -D_FILE_OFFSET_BITS=64 -c -o $@ test/example.c
          151  +
          152  +minigzip64.o: test/minigzip.c zlib.h zconf.h
          153  +	$(CC) $(CFLAGS) -I. -D_FILE_OFFSET_BITS=64 -c -o $@ test/minigzip.c
          154  +
          155  +.SUFFIXES: .lo
          156  +
          157  +.c.lo:
          158  +	-@mkdir objs 2>/dev/null || test -d objs
          159  +	$(CC) $(SFLAGS) -DPIC -c -o objs/$*.o $<
          160  +	-@mv objs/$*.o $@
          161  +
          162  +placebo $(SHAREDLIBV): $(PIC_OBJS) libz.a
          163  +	$(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS)
          164  +	rm -f $(SHAREDLIB) $(SHAREDLIBM)
          165  +	ln -s $@ $(SHAREDLIB)
          166  +	ln -s $@ $(SHAREDLIBM)
          167  +	-@rmdir objs
          168  +
          169  +example$(EXE): example.o $(STATICLIB)
          170  +	$(CC) $(CFLAGS) -o $@ example.o $(TEST_LDFLAGS)
          171  +
          172  +minigzip$(EXE): minigzip.o $(STATICLIB)
          173  +	$(CC) $(CFLAGS) -o $@ minigzip.o $(TEST_LDFLAGS)
          174  +
          175  +examplesh$(EXE): example.o $(SHAREDLIBV)
          176  +	$(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV)
          177  +
          178  +minigzipsh$(EXE): minigzip.o $(SHAREDLIBV)
          179  +	$(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV)
          180  +
          181  +example64$(EXE): example64.o $(STATICLIB)
          182  +	$(CC) $(CFLAGS) -o $@ example64.o $(TEST_LDFLAGS)
          183  +
          184  +minigzip64$(EXE): minigzip64.o $(STATICLIB)
          185  +	$(CC) $(CFLAGS) -o $@ minigzip64.o $(TEST_LDFLAGS)
          186  +
          187  +install-libs: $(LIBS)
          188  +	-@if [ ! -d $(DESTDIR)$(exec_prefix)  ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi
          189  +	-@if [ ! -d $(DESTDIR)$(libdir)       ]; then mkdir -p $(DESTDIR)$(libdir); fi
          190  +	-@if [ ! -d $(DESTDIR)$(sharedlibdir) ]; then mkdir -p $(DESTDIR)$(sharedlibdir); fi
          191  +	-@if [ ! -d $(DESTDIR)$(man3dir)      ]; then mkdir -p $(DESTDIR)$(man3dir); fi
          192  +	-@if [ ! -d $(DESTDIR)$(pkgconfigdir) ]; then mkdir -p $(DESTDIR)$(pkgconfigdir); fi
          193  +	cp $(STATICLIB) $(DESTDIR)$(libdir)
          194  +	chmod 644 $(DESTDIR)$(libdir)/$(STATICLIB)
          195  +	-@($(RANLIB) $(DESTDIR)$(libdir)/libz.a || true) >/dev/null 2>&1
          196  +	-@if test -n "$(SHAREDLIBV)"; then \
          197  +	  cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir); \
          198  +	  echo "cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)"; \
          199  +	  chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \
          200  +	  echo "chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV)"; \
          201  +	  rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \
          202  +	  ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB); \
          203  +	  ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \
          204  +	  ($(LDCONFIG) || true)  >/dev/null 2>&1; \
          205  +	fi
          206  +	cp zlib.3 $(DESTDIR)$(man3dir)
          207  +	chmod 644 $(DESTDIR)$(man3dir)/zlib.3
          208  +	cp zlib.pc $(DESTDIR)$(pkgconfigdir)
          209  +	chmod 644 $(DESTDIR)$(pkgconfigdir)/zlib.pc
          210  +# The ranlib in install is needed on NeXTSTEP which checks file times
          211  +# ldconfig is for Linux
          212  +
          213  +install: install-libs
          214  +	-@if [ ! -d $(DESTDIR)$(includedir)   ]; then mkdir -p $(DESTDIR)$(includedir); fi
          215  +	cp zlib.h zconf.h $(DESTDIR)$(includedir)
          216  +	chmod 644 $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h
          217  +
          218  +uninstall:
          219  +	cd $(DESTDIR)$(includedir); rm -f zlib.h zconf.h
          220  +	cd $(DESTDIR)$(libdir); rm -f libz.a; \
          221  +	if test -n "$(SHAREDLIBV)" -a -f $(SHAREDLIBV); then \
          222  +	  rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
          223  +	fi
          224  +	cd $(DESTDIR)$(man3dir); rm -f zlib.3
          225  +	cd $(DESTDIR)$(pkgconfigdir); rm -f zlib.pc
          226  +
          227  +docs: zlib.3.pdf
          228  +
          229  +zlib.3.pdf: zlib.3
          230  +	groff -mandoc -f H -T ps zlib.3 | ps2pdf - zlib.3.pdf
          231  +
          232  +zconf.h.cmakein: zconf.h.in
          233  +	-@ TEMPFILE=`mktemp __XXXXXX`; \
          234  +	echo "/#define ZCONF_H/ a\\\\\n#cmakedefine Z_PREFIX\\\\\n#cmakedefine Z_HAVE_UNISTD_H\n" >> $$TEMPFILE &&\
          235  +	sed -f $$TEMPFILE zconf.h.in > zconf.h.cmakein &&\
          236  +	touch -r zconf.h.in zconf.h.cmakein &&\
          237  +	rm $$TEMPFILE
          238  +
          239  +zconf: zconf.h.in
          240  +	cp -p zconf.h.in zconf.h
          241  +
          242  +mostlyclean: clean
          243  +clean:
          244  +	rm -f *.o *.lo *~ \
          245  +	   example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \
          246  +	   example64$(EXE) minigzip64$(EXE) \
          247  +	   infcover \
          248  +	   libz.* foo.gz so_locations \
          249  +	   _match.s maketree contrib/infback9/*.o
          250  +	rm -rf objs
          251  +	rm -f *.gcda *.gcno *.gcov
          252  +	rm -f contrib/infback9/*.gcda contrib/infback9/*.gcno contrib/infback9/*.gcov
          253  +
          254  +maintainer-clean: distclean
          255  +distclean: clean zconf zconf.h.cmakein docs
          256  +	rm -f Makefile zlib.pc configure.log
          257  +	-@rm -f .DS_Store
          258  +	-@printf 'all:\n\t-@echo "Please use ./configure first.  Thank you."\n' > Makefile
          259  +	-@printf '\ndistclean:\n\tmake -f Makefile.in distclean\n' >> Makefile
          260  +	-@touch -r Makefile.in Makefile
          261  +
          262  +tags:
          263  +	etags *.[ch]
          264  +
          265  +depend:
          266  +	makedepend -- $(CFLAGS) -- *.[ch]
          267  +
          268  +# DO NOT DELETE THIS LINE -- make depend depends on it.
          269  +
          270  +adler32.o zutil.o: zutil.h zlib.h zconf.h
          271  +gzclose.o gzlib.o gzread.o gzwrite.o: zlib.h zconf.h gzguts.h
          272  +compress.o example.o minigzip.o uncompr.o: zlib.h zconf.h
          273  +crc32.o: zutil.h zlib.h zconf.h crc32.h
          274  +deflate.o: deflate.h zutil.h zlib.h zconf.h
          275  +infback.o inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h
          276  +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
          277  +inftrees.o: zutil.h zlib.h zconf.h inftrees.h
          278  +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
          279  +
          280  +adler32.lo zutil.lo: zutil.h zlib.h zconf.h
          281  +gzclose.lo gzlib.lo gzread.lo gzwrite.lo: zlib.h zconf.h gzguts.h
          282  +compress.lo example.lo minigzip.lo uncompr.lo: zlib.h zconf.h
          283  +crc32.lo: zutil.h zlib.h zconf.h crc32.h
          284  +deflate.lo: deflate.h zutil.h zlib.h zconf.h
          285  +infback.lo inflate.lo: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h
          286  +inffast.lo: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
          287  +inftrees.lo: zutil.h zlib.h zconf.h inftrees.h
          288  +trees.lo: deflate.h zutil.h zlib.h zconf.h trees.h

Added compat/zlib/README.

            1  +ZLIB DATA COMPRESSION LIBRARY
            2  +
            3  +zlib 1.2.7 is a general purpose data compression library.  All the code is
            4  +thread safe.  The data format used by the zlib library is described by RFCs
            5  +(Request for Comments) 1950 to 1952 in the files
            6  +http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and
            7  +rfc1952 (gzip format).
            8  +
            9  +All functions of the compression library are documented in the file zlib.h
           10  +(volunteer to write man pages welcome, contact zlib@gzip.org).  A usage example
           11  +of the library is given in the file test/example.c which also tests that
           12  +the library is working correctly.  Another example is given in the file
           13  +test/minigzip.c.  The compression library itself is composed of all source
           14  +files in the root directory.
           15  +
           16  +To compile all files and run the test program, follow the instructions given at
           17  +the top of Makefile.in.  In short "./configure; make test", and if that goes
           18  +well, "make install" should work for most flavors of Unix.  For Windows, use
           19  +one of the special makefiles in win32/ or contrib/vstudio/ .  For VMS, use
           20  +make_vms.com.
           21  +
           22  +Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
           23  +<info@winimage.com> for the Windows DLL version.  The zlib home page is
           24  +http://zlib.net/ .  Before reporting a problem, please check this site to
           25  +verify that you have the latest version of zlib; otherwise get the latest
           26  +version and check whether the problem still exists or not.
           27  +
           28  +PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help.
           29  +
           30  +Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan.  1997
           31  +issue of Dr.  Dobb's Journal; a copy of the article is available at
           32  +http://marknelson.us/1997/01/01/zlib-engine/ .
           33  +
           34  +The changes made in version 1.2.7 are documented in the file ChangeLog.
           35  +
           36  +Unsupported third party contributions are provided in directory contrib/ .
           37  +
           38  +zlib is available in Java using the java.util.zip package, documented at
           39  +http://java.sun.com/developer/technicalArticles/Programming/compression/ .
           40  +
           41  +A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is available
           42  +at CPAN (Comprehensive Perl Archive Network) sites, including
           43  +http://search.cpan.org/~pmqs/IO-Compress-Zlib/ .
           44  +
           45  +A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is
           46  +available in Python 1.5 and later versions, see
           47  +http://docs.python.org/library/zlib.html .
           48  +
           49  +zlib is built into tcl: http://wiki.tcl.tk/4610 .
           50  +
           51  +An experimental package to read and write files in .zip format, written on top
           52  +of zlib by Gilles Vollant <info@winimage.com>, is available in the
           53  +contrib/minizip directory of zlib.
           54  +
           55  +
           56  +Notes for some targets:
           57  +
           58  +- For Windows DLL versions, please see win32/DLL_FAQ.txt
           59  +
           60  +- For 64-bit Irix, deflate.c must be compiled without any optimization. With
           61  +  -O, one libpng test fails. The test works in 32 bit mode (with the -n32
           62  +  compiler flag). The compiler bug has been reported to SGI.
           63  +
           64  +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
           65  +  when compiled with cc.
           66  +
           67  +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
           68  +  necessary to get gzprintf working correctly. This is done by configure.
           69  +
           70  +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
           71  +  other compilers. Use "make test" to check your compiler.
           72  +
           73  +- gzdopen is not supported on RISCOS or BEOS.
           74  +
           75  +- For PalmOs, see http://palmzlib.sourceforge.net/
           76  +
           77  +
           78  +Acknowledgments:
           79  +
           80  +  The deflate format used by zlib was defined by Phil Katz.  The deflate and
           81  +  zlib specifications were written by L.  Peter Deutsch.  Thanks to all the
           82  +  people who reported problems and suggested various improvements in zlib; they
           83  +  are too numerous to cite here.
           84  +
           85  +Copyright notice:
           86  +
           87  + (C) 1995-2012 Jean-loup Gailly and Mark Adler
           88  +
           89  +  This software is provided 'as-is', without any express or implied
           90  +  warranty.  In no event will the authors be held liable for any damages
           91  +  arising from the use of this software.
           92  +
           93  +  Permission is granted to anyone to use this software for any purpose,
           94  +  including commercial applications, and to alter it and redistribute it
           95  +  freely, subject to the following restrictions:
           96  +
           97  +  1. The origin of this software must not be misrepresented; you must not
           98  +     claim that you wrote the original software. If you use this software
           99  +     in a product, an acknowledgment in the product documentation would be
          100  +     appreciated but is not required.
          101  +  2. Altered source versions must be plainly marked as such, and must not be
          102  +     misrepresented as being the original software.
          103  +  3. This notice may not be removed or altered from any source distribution.
          104  +
          105  +  Jean-loup Gailly        Mark Adler
          106  +  jloup@gzip.org          madler@alumni.caltech.edu
          107  +
          108  +If you use the zlib library in a product, we would appreciate *not* receiving
          109  +lengthy legal documents to sign.  The sources are provided for free but without
          110  +warranty of any kind.  The library has been entirely written by Jean-loup
          111  +Gailly and Mark Adler; it does not include third-party code.
          112  +
          113  +If you redistribute modified sources, we would appreciate that you include in
          114  +the file ChangeLog history information documenting your changes.  Please read
          115  +the FAQ for more information on the distribution of modified source versions.

Added compat/zlib/adler32.c.

            1  +/* adler32.c -- compute the Adler-32 checksum of a data stream
            2  + * Copyright (C) 1995-2011 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#include "zutil.h"
            9  +
           10  +#define local static
           11  +
           12  +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
           13  +
           14  +#define BASE 65521      /* largest prime smaller than 65536 */
           15  +#define NMAX 5552
           16  +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
           17  +
           18  +#define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}
           19  +#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
           20  +#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
           21  +#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
           22  +#define DO16(buf)   DO8(buf,0); DO8(buf,8);
           23  +
           24  +/* use NO_DIVIDE if your processor does not do division in hardware --
           25  +   try it both ways to see which is faster */
           26  +#ifdef NO_DIVIDE
           27  +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15
           28  +   (thank you to John Reiser for pointing this out) */
           29  +#  define CHOP(a) \
           30  +    do { \
           31  +        unsigned long tmp = a >> 16; \
           32  +        a &= 0xffffUL; \
           33  +        a += (tmp << 4) - tmp; \
           34  +    } while (0)
           35  +#  define MOD28(a) \
           36  +    do { \
           37  +        CHOP(a); \
           38  +        if (a >= BASE) a -= BASE; \
           39  +    } while (0)
           40  +#  define MOD(a) \
           41  +    do { \
           42  +        CHOP(a); \
           43  +        MOD28(a); \
           44  +    } while (0)
           45  +#  define MOD63(a) \
           46  +    do { /* this assumes a is not negative */ \
           47  +        z_off64_t tmp = a >> 32; \
           48  +        a &= 0xffffffffL; \
           49  +        a += (tmp << 8) - (tmp << 5) + tmp; \
           50  +        tmp = a >> 16; \
           51  +        a &= 0xffffL; \
           52  +        a += (tmp << 4) - tmp; \
           53  +        tmp = a >> 16; \
           54  +        a &= 0xffffL; \
           55  +        a += (tmp << 4) - tmp; \
           56  +        if (a >= BASE) a -= BASE; \
           57  +    } while (0)
           58  +#else
           59  +#  define MOD(a) a %= BASE
           60  +#  define MOD28(a) a %= BASE
           61  +#  define MOD63(a) a %= BASE
           62  +#endif
           63  +
           64  +/* ========================================================================= */
           65  +uLong ZEXPORT adler32(adler, buf, len)
           66  +    uLong adler;
           67  +    const Bytef *buf;
           68  +    uInt len;
           69  +{
           70  +    unsigned long sum2;
           71  +    unsigned n;
           72  +
           73  +    /* split Adler-32 into component sums */
           74  +    sum2 = (adler >> 16) & 0xffff;
           75  +    adler &= 0xffff;
           76  +
           77  +    /* in case user likes doing a byte at a time, keep it fast */
           78  +    if (len == 1) {
           79  +        adler += buf[0];
           80  +        if (adler >= BASE)
           81  +            adler -= BASE;
           82  +        sum2 += adler;
           83  +        if (sum2 >= BASE)
           84  +            sum2 -= BASE;
           85  +        return adler | (sum2 << 16);
           86  +    }
           87  +
           88  +    /* initial Adler-32 value (deferred check for len == 1 speed) */
           89  +    if (buf == Z_NULL)
           90  +        return 1L;
           91  +
           92  +    /* in case short lengths are provided, keep it somewhat fast */
           93  +    if (len < 16) {
           94  +        while (len--) {
           95  +            adler += *buf++;
           96  +            sum2 += adler;
           97  +        }
           98  +        if (adler >= BASE)
           99  +            adler -= BASE;
          100  +        MOD28(sum2);            /* only added so many BASE's */
          101  +        return adler | (sum2 << 16);
          102  +    }
          103  +
          104  +    /* do length NMAX blocks -- requires just one modulo operation */
          105  +    while (len >= NMAX) {
          106  +        len -= NMAX;
          107  +        n = NMAX / 16;          /* NMAX is divisible by 16 */
          108  +        do {
          109  +            DO16(buf);          /* 16 sums unrolled */
          110  +            buf += 16;
          111  +        } while (--n);
          112  +        MOD(adler);
          113  +        MOD(sum2);
          114  +    }
          115  +
          116  +    /* do remaining bytes (less than NMAX, still just one modulo) */
          117  +    if (len) {                  /* avoid modulos if none remaining */
          118  +        while (len >= 16) {
          119  +            len -= 16;
          120  +            DO16(buf);
          121  +            buf += 16;
          122  +        }
          123  +        while (len--) {
          124  +            adler += *buf++;
          125  +            sum2 += adler;
          126  +        }
          127  +        MOD(adler);
          128  +        MOD(sum2);
          129  +    }
          130  +
          131  +    /* return recombined sums */
          132  +    return adler | (sum2 << 16);
          133  +}
          134  +
          135  +/* ========================================================================= */
          136  +local uLong adler32_combine_(adler1, adler2, len2)
          137  +    uLong adler1;
          138  +    uLong adler2;
          139  +    z_off64_t len2;
          140  +{
          141  +    unsigned long sum1;
          142  +    unsigned long sum2;
          143  +    unsigned rem;
          144  +
          145  +    /* for negative len, return invalid adler32 as a clue for debugging */
          146  +    if (len2 < 0)
          147  +        return 0xffffffffUL;
          148  +
          149  +    /* the derivation of this formula is left as an exercise for the reader */
          150  +    MOD63(len2);                /* assumes len2 >= 0 */
          151  +    rem = (unsigned)len2;
          152  +    sum1 = adler1 & 0xffff;
          153  +    sum2 = rem * sum1;
          154  +    MOD(sum2);
          155  +    sum1 += (adler2 & 0xffff) + BASE - 1;
          156  +    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
          157  +    if (sum1 >= BASE) sum1 -= BASE;
          158  +    if (sum1 >= BASE) sum1 -= BASE;
          159  +    if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1);
          160  +    if (sum2 >= BASE) sum2 -= BASE;
          161  +    return sum1 | (sum2 << 16);
          162  +}
          163  +
          164  +/* ========================================================================= */
          165  +uLong ZEXPORT adler32_combine(adler1, adler2, len2)
          166  +    uLong adler1;
          167  +    uLong adler2;
          168  +    z_off_t len2;
          169  +{
          170  +    return adler32_combine_(adler1, adler2, len2);
          171  +}
          172  +
          173  +uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
          174  +    uLong adler1;
          175  +    uLong adler2;
          176  +    z_off64_t len2;
          177  +{
          178  +    return adler32_combine_(adler1, adler2, len2);
          179  +}

Added compat/zlib/amiga/Makefile.pup.

            1  +# Amiga powerUP (TM) Makefile
            2  +# makefile for libpng and SAS C V6.58/7.00 PPC compiler
            3  +# Copyright (C) 1998 by Andreas R. Kleinert
            4  +
            5  +LIBNAME	= libzip.a
            6  +
            7  +CC	= scppc
            8  +CFLAGS	= NOSTKCHK NOSINT OPTIMIZE OPTGO OPTPEEP OPTINLOCAL OPTINL \
            9  +	  OPTLOOP OPTRDEP=8 OPTDEP=8 OPTCOMP=8 NOVER
           10  +AR	= ppc-amigaos-ar cr
           11  +RANLIB	= ppc-amigaos-ranlib
           12  +LD	= ppc-amigaos-ld -r
           13  +LDFLAGS	= -o
           14  +LDLIBS	= LIB:scppc.a LIB:end.o
           15  +RM	= delete quiet
           16  +
           17  +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \
           18  +       uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o
           19  +
           20  +TEST_OBJS = example.o minigzip.o
           21  +
           22  +all: example minigzip
           23  +
           24  +check: test
           25  +test: all
           26  +	example
           27  +	echo hello world | minigzip | minigzip -d
           28  +
           29  +$(LIBNAME): $(OBJS)
           30  +	$(AR) $@ $(OBJS)
           31  +	-$(RANLIB) $@
           32  +
           33  +example: example.o $(LIBNAME)
           34  +	$(LD) $(LDFLAGS) $@ LIB:c_ppc.o $@.o $(LIBNAME) $(LDLIBS)
           35  +
           36  +minigzip: minigzip.o $(LIBNAME)
           37  +	$(LD) $(LDFLAGS) $@ LIB:c_ppc.o $@.o $(LIBNAME) $(LDLIBS)
           38  +
           39  +mostlyclean: clean
           40  +clean:
           41  +	$(RM) *.o example minigzip $(LIBNAME) foo.gz
           42  +
           43  +zip:
           44  +	zip -ul9 zlib README ChangeLog Makefile Make????.??? Makefile.?? \
           45  +	  descrip.mms *.[ch]
           46  +
           47  +tgz:
           48  +	cd ..; tar cfz zlib/zlib.tgz zlib/README zlib/ChangeLog zlib/Makefile \
           49  +	  zlib/Make????.??? zlib/Makefile.?? zlib/descrip.mms zlib/*.[ch]
           50  +
           51  +# DO NOT DELETE THIS LINE -- make depend depends on it.
           52  +
           53  +adler32.o: zlib.h zconf.h
           54  +compress.o: zlib.h zconf.h
           55  +crc32.o: crc32.h zlib.h zconf.h
           56  +deflate.o: deflate.h zutil.h zlib.h zconf.h
           57  +example.o: zlib.h zconf.h
           58  +gzclose.o: zlib.h zconf.h gzguts.h
           59  +gzlib.o: zlib.h zconf.h gzguts.h
           60  +gzread.o: zlib.h zconf.h gzguts.h
           61  +gzwrite.o: zlib.h zconf.h gzguts.h
           62  +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           63  +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           64  +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           65  +inftrees.o: zutil.h zlib.h zconf.h inftrees.h
           66  +minigzip.o: zlib.h zconf.h
           67  +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
           68  +uncompr.o: zlib.h zconf.h
           69  +zutil.o: zutil.h zlib.h zconf.h

Added compat/zlib/amiga/Makefile.sas.

            1  +# SMakefile for zlib
            2  +# Modified from the standard UNIX Makefile Copyright Jean-loup Gailly
            3  +# Osma Ahvenlampi <Osma.Ahvenlampi@hut.fi>
            4  +# Amiga, SAS/C 6.56 & Smake
            5  +
            6  +CC=sc
            7  +CFLAGS=OPT
            8  +#CFLAGS=OPT CPU=68030
            9  +#CFLAGS=DEBUG=LINE
           10  +LDFLAGS=LIB z.lib
           11  +
           12  +SCOPTIONS=OPTSCHED OPTINLINE OPTALIAS OPTTIME OPTINLOCAL STRMERGE \
           13  +       NOICONS PARMS=BOTH NOSTACKCHECK UTILLIB NOVERSION ERRORREXX \
           14  +       DEF=POSTINC
           15  +
           16  +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \
           17  +       uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o
           18  +
           19  +TEST_OBJS = example.o minigzip.o
           20  +
           21  +all: SCOPTIONS example minigzip
           22  +
           23  +check: test
           24  +test: all
           25  +	example
           26  +	echo hello world | minigzip | minigzip -d
           27  +
           28  +install: z.lib
           29  +	copy clone zlib.h zconf.h INCLUDE:
           30  +	copy clone z.lib LIB:
           31  +
           32  +z.lib: $(OBJS)
           33  +	oml z.lib r $(OBJS)
           34  +
           35  +example: example.o z.lib
           36  +	$(CC) $(CFLAGS) LINK TO $@ example.o $(LDFLAGS)
           37  +
           38  +minigzip: minigzip.o z.lib
           39  +	$(CC) $(CFLAGS) LINK TO $@ minigzip.o $(LDFLAGS)
           40  +
           41  +mostlyclean: clean
           42  +clean:
           43  +	-delete force quiet example minigzip *.o z.lib foo.gz *.lnk SCOPTIONS
           44  +
           45  +SCOPTIONS: Makefile.sas
           46  +	copy to $@ <from <
           47  +$(SCOPTIONS)
           48  +<
           49  +
           50  +# DO NOT DELETE THIS LINE -- make depend depends on it.
           51  +
           52  +adler32.o: zlib.h zconf.h
           53  +compress.o: zlib.h zconf.h
           54  +crc32.o: crc32.h zlib.h zconf.h
           55  +deflate.o: deflate.h zutil.h zlib.h zconf.h
           56  +example.o: zlib.h zconf.h
           57  +gzclose.o: zlib.h zconf.h gzguts.h
           58  +gzlib.o: zlib.h zconf.h gzguts.h
           59  +gzread.o: zlib.h zconf.h gzguts.h
           60  +gzwrite.o: zlib.h zconf.h gzguts.h
           61  +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           62  +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           63  +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
           64  +inftrees.o: zutil.h zlib.h zconf.h inftrees.h
           65  +minigzip.o: zlib.h zconf.h
           66  +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
           67  +uncompr.o: zlib.h zconf.h
           68  +zutil.o: zutil.h zlib.h zconf.h

Added compat/zlib/as400/bndsrc.

            1  +STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('ZLIB')
            2  +
            3  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
            4  +/*   Version 1.1.3 entry points.                                    */
            5  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
            6  +
            7  +/********************************************************************/
            8  +/*   *MODULE      ADLER32      ZLIB         01/02/01  00:15:09      */
            9  +/********************************************************************/
           10  +
           11  +  EXPORT SYMBOL("adler32")
           12  +
           13  +/********************************************************************/
           14  +/*   *MODULE      COMPRESS     ZLIB         01/02/01  00:15:09      */
           15  +/********************************************************************/
           16  +
           17  +  EXPORT SYMBOL("compress")
           18  +  EXPORT SYMBOL("compress2")
           19  +
           20  +/********************************************************************/
           21  +/*   *MODULE      CRC32        ZLIB         01/02/01  00:15:09      */
           22  +/********************************************************************/
           23  +
           24  +  EXPORT SYMBOL("crc32")
           25  +  EXPORT SYMBOL("get_crc_table")
           26  +
           27  +/********************************************************************/
           28  +/*   *MODULE      DEFLATE      ZLIB         01/02/01  00:15:09      */
           29  +/********************************************************************/
           30  +
           31  +  EXPORT SYMBOL("deflate")
           32  +  EXPORT SYMBOL("deflateEnd")
           33  +  EXPORT SYMBOL("deflateSetDictionary")
           34  +  EXPORT SYMBOL("deflateCopy")
           35  +  EXPORT SYMBOL("deflateReset")
           36  +  EXPORT SYMBOL("deflateParams")
           37  +  EXPORT SYMBOL("deflatePrime")
           38  +  EXPORT SYMBOL("deflateInit_")
           39  +  EXPORT SYMBOL("deflateInit2_")
           40  +
           41  +/********************************************************************/
           42  +/*   *MODULE      GZIO         ZLIB         01/02/01  00:15:09      */
           43  +/********************************************************************/
           44  +
           45  +  EXPORT SYMBOL("gzopen")
           46  +  EXPORT SYMBOL("gzdopen")
           47  +  EXPORT SYMBOL("gzsetparams")
           48  +  EXPORT SYMBOL("gzread")
           49  +  EXPORT SYMBOL("gzwrite")
           50  +  EXPORT SYMBOL("gzprintf")
           51  +  EXPORT SYMBOL("gzputs")
           52  +  EXPORT SYMBOL("gzgets")
           53  +  EXPORT SYMBOL("gzputc")
           54  +  EXPORT SYMBOL("gzgetc")
           55  +  EXPORT SYMBOL("gzflush")
           56  +  EXPORT SYMBOL("gzseek")
           57  +  EXPORT SYMBOL("gzrewind")
           58  +  EXPORT SYMBOL("gztell")
           59  +  EXPORT SYMBOL("gzeof")
           60  +  EXPORT SYMBOL("gzclose")
           61  +  EXPORT SYMBOL("gzerror")
           62  +
           63  +/********************************************************************/
           64  +/*   *MODULE      INFLATE      ZLIB         01/02/01  00:15:09      */
           65  +/********************************************************************/
           66  +
           67  +  EXPORT SYMBOL("inflate")
           68  +  EXPORT SYMBOL("inflateEnd")
           69  +  EXPORT SYMBOL("inflateSetDictionary")
           70  +  EXPORT SYMBOL("inflateSync")
           71  +  EXPORT SYMBOL("inflateReset")
           72  +  EXPORT SYMBOL("inflateInit_")
           73  +  EXPORT SYMBOL("inflateInit2_")
           74  +  EXPORT SYMBOL("inflateSyncPoint")
           75  +
           76  +/********************************************************************/
           77  +/*   *MODULE      UNCOMPR      ZLIB         01/02/01  00:15:09      */
           78  +/********************************************************************/
           79  +
           80  +  EXPORT SYMBOL("uncompress")
           81  +
           82  +/********************************************************************/
           83  +/*   *MODULE      ZUTIL        ZLIB         01/02/01  00:15:09      */
           84  +/********************************************************************/
           85  +
           86  +  EXPORT SYMBOL("zlibVersion")
           87  +  EXPORT SYMBOL("zError")
           88  +
           89  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
           90  +/*   Version 1.2.1 additional entry points.                         */
           91  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
           92  +
           93  +/********************************************************************/
           94  +/*   *MODULE      COMPRESS     ZLIB         01/02/01  00:15:09      */
           95  +/********************************************************************/
           96  +
           97  +  EXPORT SYMBOL("compressBound")
           98  +
           99  +/********************************************************************/
          100  +/*   *MODULE      DEFLATE      ZLIB         01/02/01  00:15:09      */
          101  +/********************************************************************/
          102  +
          103  +  EXPORT SYMBOL("deflateBound")
          104  +
          105  +/********************************************************************/
          106  +/*   *MODULE      GZIO         ZLIB         01/02/01  00:15:09      */
          107  +/********************************************************************/
          108  +
          109  +  EXPORT SYMBOL("gzungetc")
          110  +  EXPORT SYMBOL("gzclearerr")
          111  +
          112  +/********************************************************************/
          113  +/*   *MODULE      INFBACK      ZLIB         01/02/01  00:15:09      */
          114  +/********************************************************************/
          115  +
          116  +  EXPORT SYMBOL("inflateBack")
          117  +  EXPORT SYMBOL("inflateBackEnd")
          118  +  EXPORT SYMBOL("inflateBackInit_")
          119  +
          120  +/********************************************************************/
          121  +/*   *MODULE      INFLATE      ZLIB         01/02/01  00:15:09      */
          122  +/********************************************************************/
          123  +
          124  +  EXPORT SYMBOL("inflateCopy")
          125  +
          126  +/********************************************************************/
          127  +/*   *MODULE      ZUTIL        ZLIB         01/02/01  00:15:09      */
          128  +/********************************************************************/
          129  +
          130  +  EXPORT SYMBOL("zlibCompileFlags")
          131  +
          132  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
          133  +/*   Version 1.2.5 additional entry points.                         */
          134  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
          135  +
          136  +/********************************************************************/
          137  +/*   *MODULE      ADLER32      ZLIB         01/02/01  00:15:09      */
          138  +/********************************************************************/
          139  +
          140  +  EXPORT SYMBOL("adler32_combine")
          141  +  EXPORT SYMBOL("adler32_combine64")
          142  +
          143  +/********************************************************************/
          144  +/*   *MODULE      CRC32        ZLIB         01/02/01  00:15:09      */
          145  +/********************************************************************/
          146  +
          147  +  EXPORT SYMBOL("crc32_combine")
          148  +  EXPORT SYMBOL("crc32_combine64")
          149  +
          150  +/********************************************************************/
          151  +/*   *MODULE      GZLIB        ZLIB         01/02/01  00:15:09      */
          152  +/********************************************************************/
          153  +
          154  +  EXPORT SYMBOL("gzbuffer")
          155  +  EXPORT SYMBOL("gzoffset")
          156  +  EXPORT SYMBOL("gzoffset64")
          157  +  EXPORT SYMBOL("gzopen64")
          158  +  EXPORT SYMBOL("gzseek64")
          159  +  EXPORT SYMBOL("gztell64")
          160  +
          161  +/********************************************************************/
          162  +/*   *MODULE      GZREAD       ZLIB         01/02/01  00:15:09      */
          163  +/********************************************************************/
          164  +
          165  +  EXPORT SYMBOL("gzclose_r")
          166  +
          167  +/********************************************************************/
          168  +/*   *MODULE      GZWRITE      ZLIB         01/02/01  00:15:09      */
          169  +/********************************************************************/
          170  +
          171  +  EXPORT SYMBOL("gzclose_w")
          172  +
          173  +/********************************************************************/
          174  +/*   *MODULE      INFLATE      ZLIB         01/02/01  00:15:09      */
          175  +/********************************************************************/
          176  +
          177  +  EXPORT SYMBOL("inflateMark")
          178  +  EXPORT SYMBOL("inflatePrime")
          179  +  EXPORT SYMBOL("inflateReset2")
          180  +  EXPORT SYMBOL("inflateUndermine")
          181  +
          182  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
          183  +/*   Version 1.2.6 additional entry points.                         */
          184  +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
          185  +
          186  +/********************************************************************/
          187  +/*   *MODULE      DEFLATE      ZLIB         01/02/01  00:15:09      */
          188  +/********************************************************************/
          189  +
          190  +  EXPORT SYMBOL("deflateResetKeep")
          191  +  EXPORT SYMBOL("deflatePending")
          192  +
          193  +/********************************************************************/
          194  +/*   *MODULE      GZWRITE      ZLIB         01/02/01  00:15:09      */
          195  +/********************************************************************/
          196  +
          197  +  EXPORT SYMBOL("gzgetc_")
          198  +
          199  +/********************************************************************/
          200  +/*   *MODULE      INFLATE      ZLIB         01/02/01  00:15:09      */
          201  +/********************************************************************/
          202  +
          203  +  EXPORT SYMBOL("inflateResetKeep")
          204  +
          205  +ENDPGMEXP

Added compat/zlib/as400/compile.clp.

            1  +/******************************************************************************/
            2  +/*                                                                            */
            3  +/*  ZLIB                                                                      */
            4  +/*                                                                            */
            5  +/*    Compile sources into modules and link them into a service program.      */
            6  +/*                                                                            */
            7  +/******************************************************************************/
            8  +
            9  +             PGM
           10  +
           11  +/*      Configuration adjustable parameters.                                  */
           12  +
           13  +             DCL        VAR(&SRCLIB) TYPE(*CHAR) LEN(10) +
           14  +                          VALUE('ZLIB')                         /* Source library. */
           15  +             DCL        VAR(&SRCFILE) TYPE(*CHAR) LEN(10) +
           16  +                          VALUE('SOURCES')                      /* Source member file. */
           17  +             DCL        VAR(&CTLFILE) TYPE(*CHAR) LEN(10) +
           18  +                          VALUE('TOOLS')                        /* Control member file. */
           19  +
           20  +             DCL        VAR(&MODLIB) TYPE(*CHAR) LEN(10) +
           21  +                          VALUE('ZLIB')                         /* Module library. */
           22  +
           23  +             DCL        VAR(&SRVLIB) TYPE(*CHAR) LEN(10) +
           24  +                          VALUE('LGPL')                         /* Service program library. */
           25  +
           26  +             DCL        VAR(&CFLAGS) TYPE(*CHAR) +
           27  +                          VALUE('OPTIMIZE(40)')                 /* Compile options. */
           28  +
           29  +             DCL        VAR(&TGTRLS) TYPE(*CHAR) +
           30  +                          VALUE('V5R3M0')                       /* Target release. */
           31  +
           32  +
           33  +/*      Working storage.                                                      */
           34  +
           35  +             DCL        VAR(&CMDLEN) TYPE(*DEC) LEN(15 5) VALUE(300)    /* Command length. */
           36  +             DCL        VAR(&CMD) TYPE(*CHAR) LEN(512)
           37  +             DCL        VAR(&FIXDCMD) TYPE(*CHAR) LEN(512)
           38  +
           39  +
           40  +/*      Compile sources into modules.                                         */
           41  +
           42  +             CHGVAR     VAR(&FIXDCMD) VALUE('CRTCMOD' *BCAT &CFLAGS *BCAT      +
           43  +                        'SYSIFCOPT(*IFS64IO)' *BCAT                            +
           44  +                        'DEFINE(''_LARGEFILE64_SOURCE''' *BCAT                 +
           45  +                        '''_LFS64_LARGEFILE=1'') TGTRLS(' *TCAT &TGTRLS *TCAT  +
           46  +                        ') SRCFILE(' *TCAT &SRCLIB *TCAT '/' *TCAT             +
           47  +                        &SRCFILE *TCAT ') MODULE(' *TCAT &MODLIB *TCAT '/')
           48  +
           49  +
           50  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'ADLER32)')
           51  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           52  +
           53  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'COMPRESS)')
           54  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           55  +
           56  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'CRC32)')
           57  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           58  +
           59  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'DEFLATE)')
           60  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           61  +
           62  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'GZCLOSE)')
           63  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           64  +
           65  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'GZLIB)')
           66  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           67  +
           68  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'GZREAD)')
           69  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           70  +
           71  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'GZWRITE)')
           72  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           73  +
           74  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'INFBACK)')
           75  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           76  +
           77  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'INFFAST)')
           78  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           79  +
           80  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'INFLATE)')
           81  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           82  +
           83  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'INFTREES)')
           84  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           85  +
           86  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'TREES)')
           87  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           88  +
           89  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'UNCOMPR)')
           90  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           91  +
           92  +             CHGVAR     VAR(&CMD) VALUE(&FIXDCMD *TCAT 'ZUTIL)')
           93  +             CALL       PGM(QCMDEXC) PARM(&CMD &CMDLEN)
           94  +
           95  +
           96  +/*      Link modules into a service program.                                  */
           97  +
           98  +             CRTSRVPGM  SRVPGM(&SRVLIB/ZLIB) +
           99  +                          MODULE(&MODLIB/ADLER32     &MODLIB/COMPRESS    +
          100  +                                 &MODLIB/CRC32       &MODLIB/DEFLATE     +
          101  +                                 &MODLIB/GZCLOSE     &MODLIB/GZLIB       +
          102  +                                 &MODLIB/GZREAD      &MODLIB/GZWRITE     +
          103  +                                 &MODLIB/INFBACK     &MODLIB/INFFAST     +
          104  +                                 &MODLIB/INFLATE     &MODLIB/INFTREES    +
          105  +                                 &MODLIB/TREES       &MODLIB/UNCOMPR     +
          106  +                                 &MODLIB/ZUTIL)                          +
          107  +                          SRCFILE(&SRCLIB/&CTLFILE) SRCMBR(BNDSRC)       +
          108  +                          TEXT('ZLIB 1.2.7') TGTRLS(&TGTRLS)
          109  +
          110  +             ENDPGM

Added compat/zlib/as400/readme.txt.

            1  +        ZLIB version 1.2.7 for AS400 installation instructions
            2  +
            3  +I) From an AS400 *SAVF file:
            4  +
            5  +1)      Unpacking archive to an AS400 save file
            6  +
            7  +On the AS400:
            8  +
            9  +_       Create the ZLIB AS400 library:
           10  +
           11  +        CRTLIB LIB(ZLIB) TYPE(*PROD) TEXT('ZLIB compression API library')
           12  +
           13  +_       Create a work save file, for example:
           14  +
           15  +                CRTSAVF FILE(ZLIB/ZLIBSAVF)
           16  +
           17  +On a PC connected to the target AS400:
           18  +
           19  +_       Unpack the save file image to a PC file "ZLIBSAVF"
           20  +_       Upload this file into the save file on the AS400, for example
           21  +                using ftp in BINARY mode.
           22  +
           23  +
           24  +2)      Populating the ZLIB AS400 source library
           25  +
           26  +On the AS400:
           27  +
           28  +_       Extract the saved objects into the ZLIB AS400 library using:
           29  +
           30  +RSTOBJ OBJ(*ALL) SAVLIB(ZLIB) DEV(*SAVF) SAVF(ZLIB/ZLIBSAVF) RSTLIB(ZLIB)
           31  +
           32  +
           33  +3)      Customize installation:
           34  +
           35  +_       Edit CL member ZLIB/TOOLS(COMPILE) and change parameters if needed,
           36  +                according to the comments.
           37  +
           38  +_       Compile this member with:
           39  +
           40  +        CRTCLPGM PGM(ZLIB/COMPILE) SRCFILE(ZLIB/TOOLS) SRCMBR(COMPILE)
           41  +
           42  +
           43  +4)      Compile and generate the service program:
           44  +
           45  +_       This can now be done by executing:
           46  +
           47  +        CALL PGM(ZLIB/COMPILE)
           48  +
           49  +
           50  +
           51  +II) From the original source distribution:
           52  +
           53  +1)      On the AS400, create the source library:
           54  +
           55  +        CRTLIB LIB(ZLIB) TYPE(*PROD) TEXT('ZLIB compression API library')
           56  +
           57  +2)      Create the source files:
           58  +
           59  +        CRTSRCPF FILE(ZLIB/SOURCES) RCDLEN(112) TEXT('ZLIB library modules')
           60  +        CRTSRCPF FILE(ZLIB/H)       RCDLEN(112) TEXT('ZLIB library includes')
           61  +        CRTSRCPF FILE(ZLIB/TOOLS)   RCDLEN(112) TEXT('ZLIB library control utilities')
           62  +
           63  +3)      From the machine hosting the distribution files, upload them (with
           64  +                FTP in text mode, for example) according to the following table:
           65  +
           66  +    Original    AS400   AS400    AS400 AS400
           67  +    file        file    member   type  description
           68  +                SOURCES                Original ZLIB C subprogram sources
           69  +    adler32.c           ADLER32  C     ZLIB - Compute the Adler-32 checksum of a dta strm
           70  +    compress.c          COMPRESS C     ZLIB - Compress a memory buffer
           71  +    crc32.c             CRC32    C     ZLIB - Compute the CRC-32 of a data stream
           72  +    deflate.c           DEFLATE  C     ZLIB - Compress data using the deflation algorithm
           73  +    gzclose.c           GZCLOSE  C     ZLIB - Close .gz files
           74  +    gzlib.c             GZLIB    C     ZLIB - Miscellaneous .gz files IO support
           75  +    gzread.c            GZREAD   C     ZLIB - Read .gz files
           76  +    gzwrite.c           GZWRITE  C     ZLIB - Write .gz files
           77  +    infback.c           INFBACK  C     ZLIB - Inflate using a callback interface
           78  +    inffast.c           INFFAST  C     ZLIB - Fast proc. literals & length/distance pairs
           79  +    inflate.c           INFLATE  C     ZLIB - Interface to inflate modules
           80  +    inftrees.c          INFTREES C     ZLIB - Generate Huffman trees for efficient decode
           81  +    trees.c             TREES    C     ZLIB - Output deflated data using Huffman coding
           82  +    uncompr.c           UNCOMPR  C     ZLIB - Decompress a memory buffer
           83  +    zutil.c             ZUTIL    C     ZLIB - Target dependent utility functions
           84  +                H                      Original ZLIB C and ILE/RPG include files
           85  +    crc32.h             CRC32    C     ZLIB - CRC32 tables
           86  +    deflate.h           DEFLATE  C     ZLIB - Internal compression state
           87  +    gzguts.h            GZGUTS   C     ZLIB - Definitions for the gzclose module
           88  +    inffast.h           INFFAST  C     ZLIB - Header to use inffast.c
           89  +    inffixed.h          INFFIXED C     ZLIB - Table for decoding fixed codes
           90  +    inflate.h           INFLATE  C     ZLIB - Internal inflate state definitions
           91  +    inftrees.h          INFTREES C     ZLIB - Header to use inftrees.c
           92  +    trees.h             TREES    C     ZLIB - Created automatically with -DGEN_TREES_H
           93  +    zconf.h             ZCONF    C     ZLIB - Compression library configuration
           94  +    zlib.h              ZLIB     C     ZLIB - Compression library C user interface
           95  +    as400/zlib.inc      ZLIB.INC RPGLE ZLIB - Compression library ILE RPG user interface
           96  +    zutil.h             ZUTIL    C     ZLIB - Internal interface and configuration
           97  +                TOOLS                  Building source software & AS/400 README
           98  +    as400/bndsrc        BNDSRC         Entry point exportation list
           99  +    as400/compile.clp   COMPILE  CLP   Compile sources & generate service program
          100  +    as400/readme.txt    README   TXT   Installation instructions
          101  +
          102  +4)      Continue as in I)3).
          103  +
          104  +
          105  +
          106  +
          107  +Notes:  For AS400 ILE RPG programmers, a /copy member defining the ZLIB
          108  +                API prototypes for ILE RPG can be found in ZLIB/H(ZLIB.INC).
          109  +                Please read comments in this member for more information.
          110  +
          111  +        Remember that most foreign textual data are ASCII coded: this
          112  +                implementation does not handle conversion from/to ASCII, so
          113  +                text data code conversions must be done explicitely.
          114  +
          115  +        Mainly for the reason above, always open zipped files in binary mode.

Added compat/zlib/as400/zlib.inc.

            1  +      *  ZLIB.INC - Interface to the general purpose compression library
            2  +      *
            3  +      *  ILE RPG400 version by Patrick Monnerat, DATASPHERE.
            4  +      *  Version 1.2.7
            5  +      *
            6  +      *
            7  +      *  WARNING:
            8  +      *     Procedures inflateInit(), inflateInit2(), deflateInit(),
            9  +      *         deflateInit2() and inflateBackInit() need to be called with
           10  +      *         two additional arguments:
           11  +      *         the package version string and the stream control structure.
           12  +      *         size. This is needed because RPG lacks some macro feature.
           13  +      *         Call these procedures as:
           14  +      *             inflateInit(...: ZLIB_VERSION: %size(z_stream))
           15  +      *
           16  +      /if not defined(ZLIB_H_)
           17  +      /define ZLIB_H_
           18  +      *
           19  +      **************************************************************************
           20  +      *                               Constants
           21  +      **************************************************************************
           22  +      *
           23  +      *  Versioning information.
           24  +      *
           25  +     D ZLIB_VERSION    C                   '1.2.7'
           26  +     D ZLIB_VERNUM     C                   X'1270'
           27  +     D ZLIB_VER_MAJOR  C                   1
           28  +     D ZLIB_VER_MINOR  C                   2
           29  +     D ZLIB_VER_REVISION...
           30  +     D                 C                   7
           31  +     D ZLIB_VER_SUBREVISION...
           32  +     D                 C                   0
           33  +      *
           34  +      *  Other equates.
           35  +      *
           36  +     D Z_NO_FLUSH      C                   0
           37  +     D Z_PARTIAL_FLUSH...
           38  +     D                 C                   1
           39  +     D Z_SYNC_FLUSH    C                   2
           40  +     D Z_FULL_FLUSH    C                   3
           41  +     D Z_FINISH        C                   4
           42  +     D Z_BLOCK         C                   5
           43  +     D Z_TREES         C                   6
           44  +      *
           45  +     D Z_OK            C                   0
           46  +     D Z_STREAM_END    C                   1
           47  +     D Z_NEED_DICT     C                   2
           48  +     D Z_ERRNO         C                   -1
           49  +     D Z_STREAM_ERROR  C                   -2
           50  +     D Z_DATA_ERROR    C                   -3
           51  +     D Z_MEM_ERROR     C                   -4
           52  +     D Z_BUF_ERROR     C                   -5
           53  +     DZ_VERSION_ERROR  C                   -6
           54  +      *
           55  +     D Z_NO_COMPRESSION...
           56  +     D                 C                   0
           57  +     D Z_BEST_SPEED    C                   1
           58  +     D Z_BEST_COMPRESSION...
           59  +     D                 C                   9
           60  +     D Z_DEFAULT_COMPRESSION...
           61  +     D                 C                   -1
           62  +      *
           63  +     D Z_FILTERED      C                   1
           64  +     D Z_HUFFMAN_ONLY  C                   2
           65  +     D Z_RLE           C                   3
           66  +     D Z_DEFAULT_STRATEGY...
           67  +     D                 C                   0
           68  +      *
           69  +     D Z_BINARY        C                   0
           70  +     D Z_ASCII         C                   1
           71  +     D Z_UNKNOWN       C                   2
           72  +      *
           73  +     D Z_DEFLATED      C                   8
           74  +      *
           75  +     D Z_NULL          C                   0
           76  +      *
           77  +      **************************************************************************
           78  +      *                                 Types
           79  +      **************************************************************************
           80  +      *
           81  +     D z_streamp       S               *                                        Stream struct ptr
           82  +     D gzFile          S               *                                        File pointer
           83  +     D z_off_t         S             10i 0                                      Stream offsets
           84  +     D z_off64_t       S             20i 0                                      Stream offsets
           85  +      *
           86  +      **************************************************************************
           87  +      *                               Structures
           88  +      **************************************************************************
           89  +      *
           90  +      *  The GZIP encode/decode stream support structure.
           91  +      *
           92  +     D z_stream        DS                  align based(z_streamp)
           93  +     D  zs_next_in                     *                                        Next input byte
           94  +     D  zs_avail_in                  10U 0                                      Byte cnt at next_in
           95  +     D  zs_total_in                  10U 0                                      Total bytes read
           96  +     D  zs_next_out                    *                                        Output buffer ptr
           97  +     D  zs_avail_out                 10U 0                                      Room left @ next_out
           98  +     D  zs_total_out                 10U 0                                      Total bytes written
           99  +     D  zs_msg                         *                                        Last errmsg or null
          100  +     D  zs_state                       *                                        Internal state
          101  +     D  zs_zalloc                      *   procptr                              Int. state allocator
          102  +     D  zs_free                        *   procptr                              Int. state dealloc.
          103  +     D  zs_opaque                      *                                        Private alloc. data
          104  +     D  zs_data_type                 10i 0                                      ASC/BIN best guess
          105  +     D  zs_adler                     10u 0                                      Uncompr. adler32 val
          106  +     D                               10U 0                                      Reserved
          107  +     D                               10U 0                                      Ptr. alignment
          108  +      *
          109  +      **************************************************************************
          110  +      *                     Utility function prototypes
          111  +      **************************************************************************
          112  +      *
          113  +     D compress        PR            10I 0 extproc('compress')
          114  +     D  dest                      65535    options(*varsize)                    Destination buffer
          115  +     D  destLen                      10U 0                                      Destination length
          116  +     D  source                    65535    const options(*varsize)              Source buffer
          117  +     D  sourceLen                    10u 0 value                                Source length
          118  +      *
          119  +     D compress2       PR            10I 0 extproc('compress2')
          120  +     D  dest                      65535    options(*varsize)                    Destination buffer
          121  +     D  destLen                      10U 0                                      Destination length
          122  +     D  source                    65535    const options(*varsize)              Source buffer
          123  +     D  sourceLen                    10U 0 value                                Source length
          124  +     D  level                        10I 0 value                                Compression level
          125  +      *
          126  +     D compressBound   PR            10U 0 extproc('compressBound')
          127  +     D  sourceLen                    10U 0 value
          128  +      *
          129  +     D uncompress      PR            10I 0 extproc('uncompress')
          130  +     D  dest                      65535    options(*varsize)                    Destination buffer
          131  +     D  destLen                      10U 0                                      Destination length
          132  +     D  source                    65535    const options(*varsize)              Source buffer
          133  +     D  sourceLen                    10U 0 value                                Source length
          134  +      *
          135  +      /if not defined(LARGE_FILES)
          136  +     D gzopen          PR                  extproc('gzopen')
          137  +     D                                     like(gzFile)
          138  +     D  path                           *   value options(*string)               File pathname
          139  +     D  mode                           *   value options(*string)               Open mode
          140  +      /else
          141  +     D gzopen          PR                  extproc('gzopen64')
          142  +     D                                     like(gzFile)
          143  +     D  path                           *   value options(*string)               File pathname
          144  +     D  mode                           *   value options(*string)               Open mode
          145  +      *
          146  +     D gzopen64        PR                  extproc('gzopen64')
          147  +     D                                     like(gzFile)
          148  +     D  path                           *   value options(*string)               File pathname
          149  +     D  mode                           *   value options(*string)               Open mode
          150  +      /endif
          151  +      *
          152  +     D gzdopen         PR                  extproc('gzdopen')
          153  +     D                                     like(gzFile)
          154  +     D  fd                           10I 0 value                                File descriptor
          155  +     D  mode                           *   value options(*string)               Open mode
          156  +      *
          157  +     D gzbuffer        PR            10I 0 extproc('gzbuffer')
          158  +     D  file                               value like(gzFile)                   File pointer
          159  +     D  size                         10U 0 value
          160  +      *
          161  +     D gzsetparams     PR            10I 0 extproc('gzsetparams')
          162  +     D  file                               value like(gzFile)                   File pointer
          163  +     D  level                        10I 0 value
          164  +     D  strategy                     10I 0 value
          165  +      *
          166  +     D gzread          PR            10I 0 extproc('gzread')
          167  +     D  file                               value like(gzFile)                   File pointer
          168  +     D  buf                       65535    options(*varsize)                    Buffer
          169  +     D  len                          10u 0 value                                Buffer length
          170  +      *
          171  +     D gzwrite         PR            10I 0 extproc('gzwrite')
          172  +     D  file                               value like(gzFile)                   File pointer
          173  +     D  buf                       65535    const options(*varsize)              Buffer
          174  +     D  len                          10u 0 value                                Buffer length
          175  +      *
          176  +     D gzputs          PR            10I 0 extproc('gzputs')
          177  +     D  file                               value like(gzFile)                   File pointer
          178  +     D  s                              *   value options(*string)               String to output
          179  +      *
          180  +     D gzgets          PR              *   extproc('gzgets')
          181  +     D  file                               value like(gzFile)                   File pointer
          182  +     D  buf                       65535    options(*varsize)                    Read buffer
          183  +     D  len                          10i 0 value                                Buffer length
          184  +      *
          185  +     D gzputc          PR            10i 0 extproc('gzputc')
          186  +     D  file                               value like(gzFile)                   File pointer
          187  +     D  c                            10I 0 value                                Character to write
          188  +      *
          189  +     D gzgetc          PR            10i 0 extproc('gzgetc')
          190  +     D  file                               value like(gzFile)                   File pointer
          191  +      *
          192  +     D gzgetc_         PR            10i 0 extproc('gzgetc_')
          193  +     D  file                               value like(gzFile)                   File pointer
          194  +      *
          195  +     D gzungetc        PR            10i 0 extproc('gzungetc')
          196  +     D  c                            10I 0 value                                Character to push
          197  +     D  file                               value like(gzFile)                   File pointer
          198  +      *
          199  +     D gzflush         PR            10i 0 extproc('gzflush')
          200  +     D  file                               value like(gzFile)                   File pointer
          201  +     D  flush                        10I 0 value                                Type of flush
          202  +      *
          203  +      /if not defined(LARGE_FILES)
          204  +     D gzseek          PR                  extproc('gzseek')
          205  +     D                                     like(z_off_t)
          206  +     D  file                               value like(gzFile)                   File pointer
          207  +     D  offset                             value like(z_off_t)                  Offset
          208  +     D  whence                       10i 0 value                                Origin
          209  +      /else
          210  +     D gzseek          PR                  extproc('gzseek64')
          211  +     D                                     like(z_off_t)
          212  +     D  file                               value like(gzFile)                   File pointer
          213  +     D  offset                             value like(z_off_t)                  Offset
          214  +     D  whence                       10i 0 value                                Origin
          215  +      *
          216  +     D gzseek64        PR                  extproc('gzseek64')
          217  +     D                                     like(z_off64_t)
          218  +     D  file                               value like(gzFile)                   File pointer
          219  +     D  offset                             value like(z_off64_t)                Offset
          220  +     D  whence                       10i 0 value                                Origin
          221  +      /endif
          222  +      *
          223  +     D gzrewind        PR            10i 0 extproc('gzrewind')
          224  +     D  file                               value like(gzFile)                   File pointer
          225  +      *
          226  +      /if not defined(LARGE_FILES)
          227  +     D gztell          PR                  extproc('gztell')
          228  +     D                                     like(z_off_t)
          229  +     D  file                               value like(gzFile)                   File pointer
          230  +      /else
          231  +     D gztell          PR                  extproc('gztell64')
          232  +     D                                     like(z_off_t)
          233  +     D  file                               value like(gzFile)                   File pointer
          234  +      *
          235  +     D gztell64        PR                  extproc('gztell64')
          236  +     D                                     like(z_off64_t)
          237  +     D  file                               value like(gzFile)                   File pointer
          238  +      /endif
          239  +      *
          240  +      /if not defined(LARGE_FILES)
          241  +     D gzoffset        PR                  extproc('gzoffset')
          242  +     D                                     like(z_off_t)
          243  +     D  file                               value like(gzFile)                   File pointer
          244  +      /else
          245  +     D gzoffset        PR                  extproc('gzoffset64')
          246  +     D                                     like(z_off_t)
          247  +     D  file                               value like(gzFile)                   File pointer
          248  +      *
          249  +     D gzoffset64      PR                  extproc('gzoffset64')
          250  +     D                                     like(z_off64_t)
          251  +     D  file                               value like(gzFile)                   File pointer
          252  +      /endif
          253  +      *
          254  +     D gzeof           PR            10i 0 extproc('gzeof')
          255  +     D  file                               value like(gzFile)                   File pointer
          256  +      *
          257  +     D gzclose_r       PR            10i 0 extproc('gzclose_r')
          258  +     D  file                               value like(gzFile)                   File pointer
          259  +      *
          260  +     D gzclose_w       PR            10i 0 extproc('gzclose_w')
          261  +     D  file                               value like(gzFile)                   File pointer
          262  +      *
          263  +     D gzclose         PR            10i 0 extproc('gzclose')
          264  +     D  file                               value like(gzFile)                   File pointer
          265  +      *
          266  +     D gzerror         PR              *   extproc('gzerror')                   Error string
          267  +     D  file                               value like(gzFile)                   File pointer
          268  +     D  errnum                       10I 0                                      Error code
          269  +      *
          270  +     D gzclearerr      PR                  extproc('gzclearerr')
          271  +     D  file                               value like(gzFile)                   File pointer
          272  +      *
          273  +      **************************************************************************
          274  +      *                        Basic function prototypes
          275  +      **************************************************************************
          276  +      *
          277  +     D zlibVersion     PR              *   extproc('zlibVersion')               Version string
          278  +      *
          279  +     D deflateInit     PR            10I 0 extproc('deflateInit_')              Init. compression
          280  +     D  strm                               like(z_stream)                       Compression stream
          281  +     D  level                        10I 0 value                                Compression level
          282  +     D  version                        *   value options(*string)               Version string
          283  +     D  stream_size                  10i 0 value                                Stream struct. size
          284  +      *
          285  +     D deflate         PR            10I 0 extproc('deflate')                   Compress data
          286  +     D  strm                               like(z_stream)                       Compression stream
          287  +     D  flush                        10I 0 value                                Flush type required
          288  +      *
          289  +     D deflateEnd      PR            10I 0 extproc('deflateEnd')                Termin. compression
          290  +     D  strm                               like(z_stream)                       Compression stream
          291  +      *
          292  +     D inflateInit     PR            10I 0 extproc('inflateInit_')              Init. expansion
          293  +     D  strm                               like(z_stream)                       Expansion stream
          294  +     D  version                        *   value options(*string)               Version string
          295  +     D  stream_size                  10i 0 value                                Stream struct. size
          296  +      *
          297  +     D inflate         PR            10I 0 extproc('inflate')                   Expand data
          298  +     D  strm                               like(z_stream)                       Expansion stream
          299  +     D  flush                        10I 0 value                                Flush type required
          300  +      *
          301  +     D inflateEnd      PR            10I 0 extproc('inflateEnd')                Termin. expansion
          302  +     D  strm                               like(z_stream)                       Expansion stream
          303  +      *
          304  +      **************************************************************************
          305  +      *                        Advanced function prototypes
          306  +      **************************************************************************
          307  +      *
          308  +     D deflateInit2    PR            10I 0 extproc('deflateInit2_')             Init. compression
          309  +     D  strm                               like(z_stream)                       Compression stream
          310  +     D  level                        10I 0 value                                Compression level
          311  +     D  method                       10I 0 value                                Compression method
          312  +     D  windowBits                   10I 0 value                                log2(window size)
          313  +     D  memLevel                     10I 0 value                                Mem/cmpress tradeoff
          314  +     D  strategy                     10I 0 value                                Compression stategy
          315  +     D  version                        *   value options(*string)               Version string
          316  +     D  stream_size                  10i 0 value                                Stream struct. size
          317  +      *
          318  +     D deflateSetDictionary...
          319  +     D                 PR            10I 0 extproc('deflateSetDictionary')      Init. dictionary
          320  +     D  strm                               like(z_stream)                       Compression stream
          321  +     D  dictionary                65535    const options(*varsize)              Dictionary bytes
          322  +     D  dictLength                   10U 0 value                                Dictionary length
          323  +      *
          324  +     D deflateCopy     PR            10I 0 extproc('deflateCopy')               Compress strm 2 strm
          325  +     D  dest                               like(z_stream)                       Destination stream
          326  +     D  source                             like(z_stream)                       Source stream
          327  +      *
          328  +     D deflateReset    PR            10I 0 extproc('deflateReset')              End and init. stream
          329  +     D  strm                               like(z_stream)                       Compression stream
          330  +      *
          331  +     D deflateParams   PR            10I 0 extproc('deflateParams')             Change level & strat
          332  +     D  strm                               like(z_stream)                       Compression stream
          333  +     D  level                        10I 0 value                                Compression level
          334  +     D  strategy                     10I 0 value                                Compression stategy
          335  +      *
          336  +     D deflateBound    PR            10U 0 extproc('deflateBound')              Change level & strat
          337  +     D  strm                               like(z_stream)                       Compression stream
          338  +     D  sourcelen                    10U 0 value                                Compression level
          339  +      *
          340  +     D deflatePending  PR            10I 0 extproc('deflatePending')            Change level & strat
          341  +     D  strm                               like(z_stream)                       Compression stream
          342  +     D  pending                      10U 0                                      Pending bytes
          343  +     D  bits                         10I 0                                      Pending bits
          344  +      *
          345  +     D deflatePrime    PR            10I 0 extproc('deflatePrime')              Change level & strat
          346  +     D  strm                               like(z_stream)                       Compression stream
          347  +     D  bits                         10I 0 value                                # of bits to insert
          348  +     D  value                        10I 0 value                                Bits to insert
          349  +      *
          350  +     D inflateInit2    PR            10I 0 extproc('inflateInit2_')             Init. expansion
          351  +     D  strm                               like(z_stream)                       Expansion stream
          352  +     D  windowBits                   10I 0 value                                log2(window size)
          353  +     D  version                        *   value options(*string)               Version string
          354  +     D  stream_size                  10i 0 value                                Stream struct. size
          355  +      *
          356  +     D inflateSetDictionary...
          357  +     D                 PR            10I 0 extproc('inflateSetDictionary')      Init. dictionary
          358  +     D  strm                               like(z_stream)                       Expansion stream
          359  +     D  dictionary                65535    const options(*varsize)              Dictionary bytes
          360  +     D  dictLength                   10U 0 value                                Dictionary length
          361  +      *
          362  +     D inflateSync     PR            10I 0 extproc('inflateSync')               Sync. expansion
          363  +     D  strm                               like(z_stream)                       Expansion stream
          364  +      *
          365  +     D inflateCopy     PR            10I 0 extproc('inflateCopy')
          366  +     D  dest                               like(z_stream)                       Destination stream
          367  +     D  source                             like(z_stream)                       Source stream
          368  +      *
          369  +     D inflateReset    PR            10I 0 extproc('inflateReset')              End and init. stream
          370  +     D  strm                               like(z_stream)                       Expansion stream
          371  +      *
          372  +     D inflateReset2   PR            10I 0 extproc('inflateReset2')             End and init. stream
          373  +     D  strm                               like(z_stream)                       Expansion stream
          374  +     D  windowBits                   10I 0 value                                Log2(buffer size)
          375  +      *
          376  +     D inflatePrime    PR            10I 0 extproc('inflatePrime')              Insert bits
          377  +     D  strm                               like(z_stream)                       Expansion stream
          378  +     D  bits                         10I 0 value                                Bit count
          379  +     D  value                        10I 0 value                                Bits to insert
          380  +      *
          381  +     D inflateMark     PR            10I 0 extproc('inflateMark')               Get inflate info
          382  +     D  strm                               like(z_stream)                       Expansion stream
          383  +      *
          384  +     D inflateBackInit...
          385  +     D                 PR            10I 0 extproc('inflateBackInit_')
          386  +     D  strm                               like(z_stream)                       Expansion stream
          387  +     D  windowBits                   10I 0 value                                Log2(buffer size)
          388  +     D  window                    65535    options(*varsize)                    Buffer
          389  +     D  version                        *   value options(*string)               Version string
          390  +     D  stream_size                  10i 0 value                                Stream struct. size
          391  +      *
          392  +     D inflateBack     PR            10I 0 extproc('inflateBack')
          393  +     D  strm                               like(z_stream)                       Expansion stream
          394  +     D  in                             *   value procptr                        Input function
          395  +     D  in_desc                        *   value                                Input descriptor
          396  +     D  out                            *   value procptr                        Output function
          397  +     D  out_desc                       *   value                                Output descriptor
          398  +      *
          399  +     D inflateBackEnd  PR            10I 0 extproc('inflateBackEnd')
          400  +     D  strm                               like(z_stream)                       Expansion stream
          401  +      *
          402  +     D zlibCompileFlags...
          403  +     D                 PR            10U 0 extproc('zlibCompileFlags')
          404  +      *
          405  +      **************************************************************************
          406  +      *                        Checksum function prototypes
          407  +      **************************************************************************
          408  +      *
          409  +     D adler32         PR            10U 0 extproc('adler32')                   New checksum
          410  +     D  adler                        10U 0 value                                Old checksum
          411  +     D  buf                       65535    const options(*varsize)              Bytes to accumulate
          412  +     D  len                          10U 0 value                                Buffer length
          413  +      *
          414  +     D crc32           PR            10U 0 extproc('crc32')                     New checksum
          415  +     D  crc                          10U 0 value                                Old checksum
          416  +     D  buf                       65535    const options(*varsize)              Bytes to accumulate
          417  +     D  len                          10U 0 value                                Buffer length
          418  +      *
          419  +      **************************************************************************
          420  +      *                     Miscellaneous function prototypes
          421  +      **************************************************************************
          422  +      *
          423  +     D zError          PR              *   extproc('zError')                    Error string
          424  +     D  err                          10I 0 value                                Error code
          425  +      *
          426  +     D inflateSyncPoint...
          427  +     D                 PR            10I 0 extproc('inflateSyncPoint')
          428  +     D  strm                               like(z_stream)                       Expansion stream
          429  +      *
          430  +     D get_crc_table   PR              *   extproc('get_crc_table')             Ptr to ulongs
          431  +      *
          432  +     D inflateUndermine...
          433  +     D                 PR            10I 0 extproc('inflateUndermine')
          434  +     D  strm                               like(z_stream)                       Expansion stream
          435  +     D  arg                          10I 0 value                                Error code
          436  +      *
          437  +     D inflateResetKeep...
          438  +     D                 PR            10I 0 extproc('inflateResetKeep')          End and init. stream
          439  +     D  strm                               like(z_stream)                       Expansion stream
          440  +      *
          441  +     D deflateResetKeep...
          442  +     D                 PR            10I 0 extproc('deflateResetKeep')          End and init. stream
          443  +     D  strm                               like(z_stream)                       Expansion stream
          444  +      *
          445  +      /endif

Added compat/zlib/compress.c.

            1  +/* compress.c -- compress a memory buffer
            2  + * Copyright (C) 1995-2005 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#define ZLIB_INTERNAL
            9  +#include "zlib.h"
           10  +
           11  +/* ===========================================================================
           12  +     Compresses the source buffer into the destination buffer. The level
           13  +   parameter has the same meaning as in deflateInit.  sourceLen is the byte
           14  +   length of the source buffer. Upon entry, destLen is the total size of the
           15  +   destination buffer, which must be at least 0.1% larger than sourceLen plus
           16  +   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
           17  +
           18  +     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
           19  +   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
           20  +   Z_STREAM_ERROR if the level parameter is invalid.
           21  +*/
           22  +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
           23  +    Bytef *dest;
           24  +    uLongf *destLen;
           25  +    const Bytef *source;
           26  +    uLong sourceLen;
           27  +    int level;
           28  +{
           29  +    z_stream stream;
           30  +    int err;
           31  +
           32  +    stream.next_in = (Bytef*)source;
           33  +    stream.avail_in = (uInt)sourceLen;
           34  +#ifdef MAXSEG_64K
           35  +    /* Check for source > 64K on 16-bit machine: */
           36  +    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
           37  +#endif
           38  +    stream.next_out = dest;
           39  +    stream.avail_out = (uInt)*destLen;
           40  +    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
           41  +
           42  +    stream.zalloc = (alloc_func)0;
           43  +    stream.zfree = (free_func)0;
           44  +    stream.opaque = (voidpf)0;
           45  +
           46  +    err = deflateInit(&stream, level);
           47  +    if (err != Z_OK) return err;
           48  +
           49  +    err = deflate(&stream, Z_FINISH);
           50  +    if (err != Z_STREAM_END) {
           51  +        deflateEnd(&stream);
           52  +        return err == Z_OK ? Z_BUF_ERROR : err;
           53  +    }
           54  +    *destLen = stream.total_out;
           55  +
           56  +    err = deflateEnd(&stream);
           57  +    return err;
           58  +}
           59  +
           60  +/* ===========================================================================
           61  + */
           62  +int ZEXPORT compress (dest, destLen, source, sourceLen)
           63  +    Bytef *dest;
           64  +    uLongf *destLen;
           65  +    const Bytef *source;
           66  +    uLong sourceLen;
           67  +{
           68  +    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
           69  +}
           70  +
           71  +/* ===========================================================================
           72  +     If the default memLevel or windowBits for deflateInit() is changed, then
           73  +   this function needs to be updated.
           74  + */
           75  +uLong ZEXPORT compressBound (sourceLen)
           76  +    uLong sourceLen;
           77  +{
           78  +    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
           79  +           (sourceLen >> 25) + 13;
           80  +}

Added compat/zlib/configure.

            1  +#!/bin/sh
            2  +# configure script for zlib.
            3  +#
            4  +# Normally configure builds both a static and a shared library.
            5  +# If you want to build just a static library, use: ./configure --static
            6  +#
            7  +# To impose specific compiler or flags or install directory, use for example:
            8  +#    prefix=$HOME CC=cc CFLAGS="-O4" ./configure
            9  +# or for csh/tcsh users:
           10  +#    (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
           11  +
           12  +# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
           13  +# If you have problems, try without defining CC and CFLAGS before reporting
           14  +# an error.
           15  +
           16  +# start off configure.log
           17  +echo -------------------- >> configure.log
           18  +echo $0 $* >> configure.log
           19  +date >> configure.log
           20  +
           21  +# set command prefix for cross-compilation
           22  +if [ -n "${CHOST}" ]; then
           23  +    uname="`echo "${CHOST}" | sed -e 's/^[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)-.*$/\1/'`"
           24  +    CROSS_PREFIX="${CHOST}-"
           25  +fi
           26  +
           27  +# destination name for static library
           28  +STATICLIB=libz.a
           29  +
           30  +# extract zlib version numbers from zlib.h
           31  +VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
           32  +VER3=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\\.[0-9]*\).*/\1/p' < zlib.h`
           33  +VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h`
           34  +VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h`
           35  +
           36  +# establish commands for library building
           37  +if "${CROSS_PREFIX}ar" --version >/dev/null 2>/dev/null || test $? -lt 126; then
           38  +    AR=${AR-"${CROSS_PREFIX}ar"}
           39  +    test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log
           40  +else
           41  +    AR=${AR-"ar"}
           42  +    test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log
           43  +fi
           44  +ARFLAGS=${ARFLAGS-"rc"}
           45  +if "${CROSS_PREFIX}ranlib" --version >/dev/null 2>/dev/null || test $? -lt 126; then
           46  +    RANLIB=${RANLIB-"${CROSS_PREFIX}ranlib"}
           47  +    test -n "${CROSS_PREFIX}" && echo Using ${RANLIB} | tee -a configure.log
           48  +else
           49  +    RANLIB=${RANLIB-"ranlib"}
           50  +fi
           51  +if "${CROSS_PREFIX}nm" --version >/dev/null 2>/dev/null || test $? -lt 126; then
           52  +    NM=${NM-"${CROSS_PREFIX}nm"}
           53  +    test -n "${CROSS_PREFIX}" && echo Using ${NM} | tee -a configure.log
           54  +else
           55  +    NM=${NM-"nm"}
           56  +fi
           57  +
           58  +# set defaults before processing command line options
           59  +LDCONFIG=${LDCONFIG-"ldconfig"}
           60  +LDSHAREDLIBC="${LDSHAREDLIBC--lc}"
           61  +ARCHS=
           62  +prefix=${prefix-/usr/local}
           63  +exec_prefix=${exec_prefix-'${prefix}'}
           64  +libdir=${libdir-'${exec_prefix}/lib'}
           65  +sharedlibdir=${sharedlibdir-'${libdir}'}
           66  +includedir=${includedir-'${prefix}/include'}
           67  +mandir=${mandir-'${prefix}/share/man'}
           68  +shared_ext='.so'
           69  +shared=1
           70  +solo=0
           71  +cover=0
           72  +zprefix=0
           73  +build64=0
           74  +gcc=0
           75  +old_cc="$CC"
           76  +old_cflags="$CFLAGS"
           77  +OBJC='$(OBJZ) $(OBJG)'
           78  +PIC_OBJC='$(PIC_OBJZ) $(PIC_OBJG)'
           79  +
           80  +# process command line options
           81  +while test $# -ge 1
           82  +do
           83  +case "$1" in
           84  +    -h* | --help)
           85  +      echo 'usage:' | tee -a configure.log
           86  +      echo '  configure [--zprefix] [--prefix=PREFIX]  [--eprefix=EXPREFIX]' | tee -a configure.log
           87  +      echo '    [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log
           88  +      echo '    [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log
           89  +        exit 0 ;;
           90  +    -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;;
           91  +    -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/.*=//'`; shift ;;
           92  +    -l*=* | --libdir=*) libdir=`echo $1 | sed 's/.*=//'`; shift ;;
           93  +    --sharedlibdir=*) sharedlibdir=`echo $1 | sed 's/.*=//'`; shift ;;
           94  +    -i*=* | --includedir=*) includedir=`echo $1 | sed 's/.*=//'`;shift ;;
           95  +    -u*=* | --uname=*) uname=`echo $1 | sed 's/.*=//'`;shift ;;
           96  +    -p* | --prefix) prefix="$2"; shift; shift ;;
           97  +    -e* | --eprefix) exec_prefix="$2"; shift; shift ;;
           98  +    -l* | --libdir) libdir="$2"; shift; shift ;;
           99  +    -i* | --includedir) includedir="$2"; shift; shift ;;
          100  +    -s* | --shared | --enable-shared) shared=1; shift ;;
          101  +    -t | --static) shared=0; shift ;;
          102  +    --solo) solo=1; shift ;;
          103  +    --cover) cover=1; shift ;;
          104  +    -z* | --zprefix) zprefix=1; shift ;;
          105  +    -6* | --64) build64=1; shift ;;
          106  +    -a*=* | --archs=*) ARCHS=`echo $1 | sed 's/.*=//'`; shift ;;
          107  +    --sysconfdir=*) echo "ignored option: --sysconfdir" | tee -a configure.log; shift ;;
          108  +    --localstatedir=*) echo "ignored option: --localstatedir" | tee -a configure.log; shift ;;
          109  +    *) echo "unknown option: $1"; echo "$0 --help for help" | tee -a configure.log; exit 1 ;;
          110  +    esac
          111  +done
          112  +
          113  +# define functions for testing compiler and library characteristics and logging the results
          114  +test=ztest$$
          115  +
          116  +show()
          117  +{
          118  +  case "$*" in
          119  +    *$test.c*)
          120  +      echo === $test.c === >> configure.log
          121  +      cat $test.c >> configure.log
          122  +      echo === >> configure.log;;
          123  +  esac
          124  +  echo $* >> configure.log
          125  +}
          126  +
          127  +cat > $test.c <<EOF
          128  +#error error
          129  +EOF
          130  +if ($CC -c $CFLAGS $test.c) 2>/dev/null; then
          131  +  try()
          132  +  {
          133  +    show $*
          134  +    test "`( $* ) 2>&1 | tee -a configure.log`" = ""
          135  +  }
          136  +  echo - using any output from compiler to indicate an error >> configure.log
          137  +else
          138  +  try()
          139  +  {
          140  +    show $*
          141  +    ( $* ) >> configure.log 2>&1
          142  +    ret=$?
          143  +    if test $ret -ne 0; then
          144  +      echo "(exit code "$ret")" >> configure.log
          145  +    fi
          146  +    return $ret
          147  +  }
          148  +fi
          149  +
          150  +tryboth()
          151  +{
          152  +  show $*
          153  +  got=`( $* ) 2>&1`
          154  +  ret=$?
          155  +  printf %s "$got" >> configure.log
          156  +  if test $ret -ne 0; then
          157  +    return $ret
          158  +  fi
          159  +  test "$got" = ""
          160  +}
          161  +
          162  +echo >> configure.log
          163  +
          164  +# check for gcc vs. cc and set compile and link flags based on the system identified by uname
          165  +cat > $test.c <<EOF
          166  +extern int getchar();
          167  +int hello() {return getchar();}
          168  +EOF
          169  +
          170  +test -z "$CC" && echo Checking for ${CROSS_PREFIX}gcc... | tee -a configure.log
          171  +cc=${CC-${CROSS_PREFIX}gcc}
          172  +cflags=${CFLAGS-"-O3"}
          173  +# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
          174  +case "$cc" in
          175  +  *gcc*) gcc=1 ;;
          176  +  *clang*) gcc=1 ;;
          177  +esac
          178  +case `$cc -v 2>&1` in
          179  +  *gcc*) gcc=1 ;;
          180  +esac
          181  +
          182  +show $cc -c $cflags $test.c
          183  +if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) >> configure.log 2>&1; then
          184  +  echo ... using gcc >> configure.log
          185  +  CC="$cc"
          186  +  CFLAGS="${CFLAGS--O3} ${ARCHS}"
          187  +  SFLAGS="${CFLAGS--O3} -fPIC"
          188  +  LDFLAGS="${LDFLAGS} ${ARCHS}"
          189  +  if test $build64 -eq 1; then
          190  +    CFLAGS="${CFLAGS} -m64"
          191  +    SFLAGS="${SFLAGS} -m64"
          192  +  fi
          193  +  if test "${ZLIBGCCWARN}" = "YES"; then
          194  +    CFLAGS="${CFLAGS} -Wall -Wextra -pedantic"
          195  +  fi
          196  +  if test -z "$uname"; then
          197  +    uname=`(uname -s || echo unknown) 2>/dev/null`
          198  +  fi
          199  +  case "$uname" in
          200  +  Linux* | linux* | GNU | GNU/* | solaris*)
          201  +        LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map"} ;;
          202  +  *BSD | *bsd* | DragonFly)
          203  +        LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map"}
          204  +        LDCONFIG="ldconfig -m" ;;
          205  +  CYGWIN* | Cygwin* | cygwin* | OS/2*)
          206  +        EXE='.exe' ;;
          207  +  MINGW* | mingw*)
          208  +# temporary bypass
          209  +        rm -f $test.[co] $test $test$shared_ext
          210  +        echo "Please use win32/Makefile.gcc instead." | tee -a configure.log
          211  +        exit 1
          212  +        LDSHARED=${LDSHARED-"$cc -shared"}
          213  +        LDSHAREDLIBC=""
          214  +        EXE='.exe' ;;
          215  +  QNX*)  # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4
          216  +         # (alain.bonnefoy@icbt.com)
          217  +                 LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"} ;;
          218  +  HP-UX*)
          219  +         LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"}
          220  +         case `(uname -m || echo unknown) 2>/dev/null` in
          221  +         ia64)
          222  +                 shared_ext='.so'
          223  +                 SHAREDLIB='libz.so' ;;
          224  +         *)
          225  +                 shared_ext='.sl'
          226  +                 SHAREDLIB='libz.sl' ;;
          227  +         esac ;;
          228  +  Darwin* | darwin*)
          229  +             shared_ext='.dylib'
          230  +             SHAREDLIB=libz$shared_ext
          231  +             SHAREDLIBV=libz.$VER$shared_ext
          232  +             SHAREDLIBM=libz.$VER1$shared_ext
          233  +             LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"}
          234  +             AR="/usr/bin/libtool"
          235  +             ARFLAGS="-o" ;;
          236  +  *)             LDSHARED=${LDSHARED-"$cc -shared"} ;;
          237  +  esac
          238  +else
          239  +  # find system name and corresponding cc options
          240  +  CC=${CC-cc}
          241  +  gcc=0
          242  +  echo ... using $CC >> configure.log
          243  +  if test -z "$uname"; then
          244  +    uname=`(uname -sr || echo unknown) 2>/dev/null`
          245  +  fi
          246  +  case "$uname" in
          247  +  HP-UX*)    SFLAGS=${CFLAGS-"-O +z"}
          248  +             CFLAGS=${CFLAGS-"-O"}
          249  +#            LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
          250  +             LDSHARED=${LDSHARED-"ld -b"}
          251  +         case `(uname -m || echo unknown) 2>/dev/null` in
          252  +         ia64)
          253  +             shared_ext='.so'
          254  +             SHAREDLIB='libz.so' ;;
          255  +         *)
          256  +             shared_ext='.sl'
          257  +             SHAREDLIB='libz.sl' ;;
          258  +         esac ;;
          259  +  IRIX*)     SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
          260  +             CFLAGS=${CFLAGS-"-ansi -O2"}
          261  +             LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;;
          262  +  OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
          263  +             CFLAGS=${CFLAGS-"-O -std1"}
          264  +             LDFLAGS="${LDFLAGS} -Wl,-rpath,."
          265  +             LDSHARED=${LDSHARED-"cc -shared  -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"} ;;
          266  +  OSF1*)     SFLAGS=${CFLAGS-"-O -std1"}
          267  +             CFLAGS=${CFLAGS-"-O -std1"}
          268  +             LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;;
          269  +  QNX*)      SFLAGS=${CFLAGS-"-4 -O"}
          270  +             CFLAGS=${CFLAGS-"-4 -O"}
          271  +             LDSHARED=${LDSHARED-"cc"}
          272  +             RANLIB=${RANLIB-"true"}
          273  +             AR="cc"
          274  +             ARFLAGS="-A" ;;
          275  +  SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
          276  +             CFLAGS=${CFLAGS-"-O3"}
          277  +             LDSHARED=${LDSHARED-"cc -dy -KPIC -G"} ;;
          278  +  SunOS\ 5* | solaris*)
          279  +         LDSHARED=${LDSHARED-"cc -G -h libz$shared_ext.$VER1"}
          280  +         SFLAGS=${CFLAGS-"-fast -KPIC"}
          281  +         CFLAGS=${CFLAGS-"-fast"}
          282  +         if test $build64 -eq 1; then
          283  +             # old versions of SunPRO/Workshop/Studio don't support -m64,
          284  +             # but newer ones do.  Check for it.
          285  +             flag64=`$CC -flags | egrep -- '^-m64'`
          286  +             if test x"$flag64" != x"" ; then
          287  +                 CFLAGS="${CFLAGS} -m64"
          288  +                 SFLAGS="${SFLAGS} -m64"
          289  +             else
          290  +                 case `(uname -m || echo unknown) 2>/dev/null` in
          291  +                   i86*)
          292  +                     SFLAGS="$SFLAGS -xarch=amd64"
          293  +                     CFLAGS="$CFLAGS -xarch=amd64" ;;
          294  +                   *)
          295  +                     SFLAGS="$SFLAGS -xarch=v9"
          296  +                     CFLAGS="$CFLAGS -xarch=v9" ;;
          297  +                 esac
          298  +             fi
          299  +         fi
          300  +         ;;
          301  +  SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
          302  +             CFLAGS=${CFLAGS-"-O2"}
          303  +             LDSHARED=${LDSHARED-"ld"} ;;
          304  +  SunStudio\ 9*) SFLAGS=${CFLAGS-"-fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"}
          305  +             CFLAGS=${CFLAGS-"-fast -xtarget=ultra3 -xarch=v9b"}
          306  +             LDSHARED=${LDSHARED-"cc -xarch=v9b"} ;;
          307  +  UNIX_System_V\ 4.2.0)
          308  +             SFLAGS=${CFLAGS-"-KPIC -O"}
          309  +             CFLAGS=${CFLAGS-"-O"}
          310  +             LDSHARED=${LDSHARED-"cc -G"} ;;
          311  +  UNIX_SV\ 4.2MP)
          312  +             SFLAGS=${CFLAGS-"-Kconform_pic -O"}
          313  +             CFLAGS=${CFLAGS-"-O"}
          314  +             LDSHARED=${LDSHARED-"cc -G"} ;;
          315  +  OpenUNIX\ 5)
          316  +             SFLAGS=${CFLAGS-"-KPIC -O"}
          317  +             CFLAGS=${CFLAGS-"-O"}
          318  +             LDSHARED=${LDSHARED-"cc -G"} ;;
          319  +  AIX*)  # Courtesy of dbakker@arrayasolutions.com
          320  +             SFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
          321  +             CFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
          322  +             LDSHARED=${LDSHARED-"xlc -G"} ;;
          323  +  # send working options for other systems to zlib@gzip.org
          324  +  *)         SFLAGS=${CFLAGS-"-O"}
          325  +             CFLAGS=${CFLAGS-"-O"}
          326  +             LDSHARED=${LDSHARED-"cc -shared"} ;;
          327  +  esac
          328  +fi
          329  +
          330  +# destination names for shared library if not defined above
          331  +SHAREDLIB=${SHAREDLIB-"libz$shared_ext"}
          332  +SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"}
          333  +SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"}
          334  +
          335  +echo >> configure.log
          336  +
          337  +# see if shared library build supported
          338  +if test $shared -eq 1; then
          339  +  echo Checking for shared library support... | tee -a configure.log
          340  +  # we must test in two steps (cc then ld), required at least on SunOS 4.x
          341  +  if try $CC -w -c $SFLAGS $test.c &&
          342  +     try $LDSHARED $SFLAGS -o $test$shared_ext $test.o; then
          343  +    echo Building shared library $SHAREDLIBV with $CC. | tee -a configure.log
          344  +  elif test -z "$old_cc" -a -z "$old_cflags"; then
          345  +    echo No shared library support. | tee -a configure.log
          346  +    shared=0;
          347  +  else
          348  +    echo 'No shared library support; try without defining CC and CFLAGS' | tee -a configure.log
          349  +    shared=0;
          350  +  fi
          351  +fi
          352  +if test $shared -eq 0; then
          353  +  LDSHARED="$CC"
          354  +  ALL="static"
          355  +  TEST="all teststatic"
          356  +  SHAREDLIB=""
          357  +  SHAREDLIBV=""
          358  +  SHAREDLIBM=""
          359  +  echo Building static library $STATICLIB version $VER with $CC. | tee -a configure.log
          360  +else
          361  +  ALL="static shared"
          362  +  TEST="all teststatic testshared"
          363  +fi
          364  +
          365  +echo >> configure.log
          366  +
          367  +# check for underscores in external names for use by assembler code
          368  +CPP=${CPP-"$CC -E"}
          369  +case $CFLAGS in
          370  +  *ASMV*)
          371  +    echo >> configure.log
          372  +    show "$NM $test.o | grep _hello"
          373  +    if test "`$NM $test.o | grep _hello | tee -a configure.log`" = ""; then
          374  +      CPP="$CPP -DNO_UNDERLINE"
          375  +      echo Checking for underline in external names... No. | tee -a configure.log
          376  +    else
          377  +      echo Checking for underline in external names... Yes. | tee -a configure.log
          378  +    fi ;;
          379  +esac
          380  +
          381  +echo >> configure.log
          382  +
          383  +# check for large file support, and if none, check for fseeko()
          384  +cat > $test.c <<EOF
          385  +#include <sys/types.h>
          386  +off64_t dummy = 0;
          387  +EOF
          388  +if try $CC -c $CFLAGS -D_LARGEFILE64_SOURCE=1 $test.c; then
          389  +  CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE=1"
          390  +  SFLAGS="${SFLAGS} -D_LARGEFILE64_SOURCE=1"
          391  +  ALL="${ALL} all64"
          392  +  TEST="${TEST} test64"
          393  +  echo "Checking for off64_t... Yes." | tee -a configure.log
          394  +  echo "Checking for fseeko... Yes." | tee -a configure.log
          395  +else
          396  +  echo "Checking for off64_t... No." | tee -a configure.log
          397  +  echo >> configure.log
          398  +  cat > $test.c <<EOF
          399  +#include <stdio.h>
          400  +int main(void) {
          401  +  fseeko(NULL, 0, 0);
          402  +  return 0;
          403  +}
          404  +EOF
          405  +  if try $CC $CFLAGS -o $test $test.c; then
          406  +    echo "Checking for fseeko... Yes." | tee -a configure.log
          407  +  else
          408  +    CFLAGS="${CFLAGS} -DNO_FSEEKO"
          409  +    SFLAGS="${SFLAGS} -DNO_FSEEKO"
          410  +    echo "Checking for fseeko... No." | tee -a configure.log
          411  +  fi
          412  +fi
          413  +
          414  +echo >> configure.log
          415  +
          416  +# check for strerror() for use by gz* functions
          417  +cat > $test.c <<EOF
          418  +#include <string.h>
          419  +#include <errno.h>
          420  +int main() { return strlen(strerror(errno)); }
          421  +EOF
          422  +if try $CC $CFLAGS -o $test $test.c; then
          423  +  echo "Checking for strerror... Yes." | tee -a configure.log
          424  +else
          425  +  CFLAGS="${CFLAGS} -DNO_STRERROR"
          426  +  SFLAGS="${SFLAGS} -DNO_STRERROR"
          427  +  echo "Checking for strerror... No." | tee -a configure.log
          428  +fi
          429  +
          430  +# copy clean zconf.h for subsequent edits
          431  +cp -p zconf.h.in zconf.h
          432  +
          433  +echo >> configure.log
          434  +
          435  +# check for unistd.h and save result in zconf.h
          436  +cat > $test.c <<EOF
          437  +#include <unistd.h>
          438  +int main() { return 0; }
          439  +EOF
          440  +if try $CC -c $CFLAGS $test.c; then
          441  +  sed < zconf.h "/^#ifdef HAVE_UNISTD_H.* may be/s/def HAVE_UNISTD_H\(.*\) may be/ 1\1 was/" > zconf.temp.h
          442  +  mv zconf.temp.h zconf.h
          443  +  echo "Checking for unistd.h... Yes." | tee -a configure.log
          444  +else
          445  +  echo "Checking for unistd.h... No." | tee -a configure.log
          446  +fi
          447  +
          448  +echo >> configure.log
          449  +
          450  +# check for stdarg.h and save result in zconf.h
          451  +cat > $test.c <<EOF
          452  +#include <stdarg.h>
          453  +int main() { return 0; }
          454  +EOF
          455  +if try $CC -c $CFLAGS $test.c; then
          456  +  sed < zconf.h "/^#ifdef HAVE_STDARG_H.* may be/s/def HAVE_STDARG_H\(.*\) may be/ 1\1 was/" > zconf.temp.h
          457  +  mv zconf.temp.h zconf.h
          458  +  echo "Checking for stdarg.h... Yes." | tee -a configure.log
          459  +else
          460  +  echo "Checking for stdarg.h... No." | tee -a configure.log
          461  +fi
          462  +
          463  +# if the z_ prefix was requested, save that in zconf.h
          464  +if test $zprefix -eq 1; then
          465  +  sed < zconf.h "/#ifdef Z_PREFIX.* may be/s/def Z_PREFIX\(.*\) may be/ 1\1 was/" > zconf.temp.h
          466  +  mv zconf.temp.h zconf.h
          467  +  echo >> configure.log
          468  +  echo "Using z_ prefix on all symbols." | tee -a configure.log
          469  +fi
          470  +
          471  +# if --solo compilation was requested, save that in zconf.h and remove gz stuff from object lists
          472  +if test $solo -eq 1; then
          473  +  sed '/#define ZCONF_H/a\
          474  +#define Z_SOLO
          475  +
          476  +' < zconf.h > zconf.temp.h
          477  +  mv zconf.temp.h zconf.h
          478  +OBJC='$(OBJZ)'
          479  +PIC_OBJC='$(PIC_OBJZ)'
          480  +fi
          481  +
          482  +# if code coverage testing was requested, use older gcc if defined, e.g. "gcc-4.2" on Mac OS X
          483  +if test $cover -eq 1; then
          484  +  CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage"
          485  +  if test -n "$GCC_CLASSIC"; then
          486  +    CC=$GCC_CLASSIC
          487  +  fi
          488  +fi
          489  +
          490  +echo >> configure.log
          491  +
          492  +# conduct a series of tests to resolve eight possible cases of using "vs" or "s" printf functions
          493  +# (using stdarg or not), with or without "n" (proving size of buffer), and with or without a
          494  +# return value.  The most secure result is vsnprintf() with a return value.  snprintf() with a
          495  +# return value is secure as well, but then gzprintf() will be limited to 20 arguments.
          496  +cat > $test.c <<EOF
          497  +#include <stdio.h>
          498  +#include <stdarg.h>
          499  +#include "zconf.h"
          500  +int main()
          501  +{
          502  +#ifndef STDC
          503  +  choke me
          504  +#endif
          505  +  return 0;
          506  +}
          507  +EOF
          508  +if try $CC -c $CFLAGS $test.c; then
          509  +  echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()." | tee -a configure.log
          510  +
          511  +  echo >> configure.log
          512  +  cat > $test.c <<EOF
          513  +#include <stdio.h>
          514  +#include <stdarg.h>
          515  +int mytest(const char *fmt, ...)
          516  +{
          517  +  char buf[20];
          518  +  va_list ap;
          519  +  va_start(ap, fmt);
          520  +  vsnprintf(buf, sizeof(buf), fmt, ap);
          521  +  va_end(ap);
          522  +  return 0;
          523  +}
          524  +int main()
          525  +{
          526  +  return (mytest("Hello%d\n", 1));
          527  +}
          528  +EOF
          529  +  if try $CC $CFLAGS -o $test $test.c; then
          530  +    echo "Checking for vsnprintf() in stdio.h... Yes." | tee -a configure.log
          531  +
          532  +    echo >> configure.log
          533  +    cat >$test.c <<EOF
          534  +#include <stdio.h>
          535  +#include <stdarg.h>
          536  +int mytest(const char *fmt, ...)
          537  +{
          538  +  int n;
          539  +  char buf[20];
          540  +  va_list ap;
          541  +  va_start(ap, fmt);
          542  +  n = vsnprintf(buf, sizeof(buf), fmt, ap);
          543  +  va_end(ap);
          544  +  return n;
          545  +}
          546  +int main()
          547  +{
          548  +  return (mytest("Hello%d\n", 1));
          549  +}
          550  +EOF
          551  +
          552  +    if try $CC -c $CFLAGS $test.c; then
          553  +      echo "Checking for return value of vsnprintf()... Yes." | tee -a configure.log
          554  +    else
          555  +      CFLAGS="$CFLAGS -DHAS_vsnprintf_void"
          556  +      SFLAGS="$SFLAGS -DHAS_vsnprintf_void"
          557  +      echo "Checking for return value of vsnprintf()... No." | tee -a configure.log
          558  +      echo "  WARNING: apparently vsnprintf() does not return a value. zlib" | tee -a configure.log
          559  +      echo "  can build but will be open to possible string-format security" | tee -a configure.log
          560  +      echo "  vulnerabilities." | tee -a configure.log
          561  +    fi
          562  +  else
          563  +    CFLAGS="$CFLAGS -DNO_vsnprintf"
          564  +    SFLAGS="$SFLAGS -DNO_vsnprintf"
          565  +    echo "Checking for vsnprintf() in stdio.h... No." | tee -a configure.log
          566  +    echo "  WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log
          567  +    echo "  can build but will be open to possible buffer-overflow security" | tee -a configure.log
          568  +    echo "  vulnerabilities." | tee -a configure.log
          569  +
          570  +    echo >> configure.log
          571  +    cat >$test.c <<EOF
          572  +#include <stdio.h>
          573  +#include <stdarg.h>
          574  +int mytest(const char *fmt, ...)
          575  +{
          576  +  int n;
          577  +  char buf[20];
          578  +  va_list ap;
          579  +  va_start(ap, fmt);
          580  +  n = vsprintf(buf, fmt, ap);
          581  +  va_end(ap);
          582  +  return n;
          583  +}
          584  +int main()
          585  +{
          586  +  return (mytest("Hello%d\n", 1));
          587  +}
          588  +EOF
          589  +
          590  +    if try $CC -c $CFLAGS $test.c; then
          591  +      echo "Checking for return value of vsprintf()... Yes." | tee -a configure.log
          592  +    else
          593  +      CFLAGS="$CFLAGS -DHAS_vsprintf_void"
          594  +      SFLAGS="$SFLAGS -DHAS_vsprintf_void"
          595  +      echo "Checking for return value of vsprintf()... No." | tee -a configure.log
          596  +      echo "  WARNING: apparently vsprintf() does not return a value. zlib" | tee -a configure.log
          597  +      echo "  can build but will be open to possible string-format security" | tee -a configure.log
          598  +      echo "  vulnerabilities." | tee -a configure.log
          599  +    fi
          600  +  fi
          601  +else
          602  +  echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()." | tee -a configure.log
          603  +
          604  +  echo >> configure.log
          605  +  cat >$test.c <<EOF
          606  +#include <stdio.h>
          607  +int mytest()
          608  +{
          609  +  char buf[20];
          610  +  snprintf(buf, sizeof(buf), "%s", "foo");
          611  +  return 0;
          612  +}
          613  +int main()
          614  +{
          615  +  return (mytest());
          616  +}
          617  +EOF
          618  +
          619  +  if try $CC $CFLAGS -o $test $test.c; then
          620  +    echo "Checking for snprintf() in stdio.h... Yes." | tee -a configure.log
          621  +
          622  +    echo >> configure.log
          623  +    cat >$test.c <<EOF
          624  +#include <stdio.h>
          625  +int mytest()
          626  +{
          627  +  char buf[20];
          628  +  return snprintf(buf, sizeof(buf), "%s", "foo");
          629  +}
          630  +int main()
          631  +{
          632  +  return (mytest());
          633  +}
          634  +EOF
          635  +
          636  +    if try $CC -c $CFLAGS $test.c; then
          637  +      echo "Checking for return value of snprintf()... Yes." | tee -a configure.log
          638  +    else
          639  +      CFLAGS="$CFLAGS -DHAS_snprintf_void"
          640  +      SFLAGS="$SFLAGS -DHAS_snprintf_void"
          641  +      echo "Checking for return value of snprintf()... No." | tee -a configure.log
          642  +      echo "  WARNING: apparently snprintf() does not return a value. zlib" | tee -a configure.log
          643  +      echo "  can build but will be open to possible string-format security" | tee -a configure.log
          644  +      echo "  vulnerabilities." | tee -a configure.log
          645  +    fi
          646  +  else
          647  +    CFLAGS="$CFLAGS -DNO_snprintf"
          648  +    SFLAGS="$SFLAGS -DNO_snprintf"
          649  +    echo "Checking for snprintf() in stdio.h... No." | tee -a configure.log
          650  +    echo "  WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log
          651  +    echo "  can build but will be open to possible buffer-overflow security" | tee -a configure.log
          652  +    echo "  vulnerabilities." | tee -a configure.log
          653  +
          654  +    echo >> configure.log
          655  +    cat >$test.c <<EOF
          656  +#include <stdio.h>
          657  +int mytest()
          658  +{
          659  +  char buf[20];
          660  +  return sprintf(buf, "%s", "foo");
          661  +}
          662  +int main()
          663  +{
          664  +  return (mytest());
          665  +}
          666  +EOF
          667  +
          668  +    if try $CC -c $CFLAGS $test.c; then
          669  +      echo "Checking for return value of sprintf()... Yes." | tee -a configure.log
          670  +    else
          671  +      CFLAGS="$CFLAGS -DHAS_sprintf_void"
          672  +      SFLAGS="$SFLAGS -DHAS_sprintf_void"
          673  +      echo "Checking for return value of sprintf()... No." | tee -a configure.log
          674  +      echo "  WARNING: apparently sprintf() does not return a value. zlib" | tee -a configure.log
          675  +      echo "  can build but will be open to possible string-format security" | tee -a configure.log
          676  +      echo "  vulnerabilities." | tee -a configure.log
          677  +    fi
          678  +  fi
          679  +fi
          680  +
          681  +# see if we can hide zlib internal symbols that are linked between separate source files
          682  +if test "$gcc" -eq 1; then
          683  +  echo >> configure.log
          684  +  cat > $test.c <<EOF
          685  +#define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
          686  +int ZLIB_INTERNAL foo;
          687  +int main()
          688  +{
          689  +  return 0;
          690  +}
          691  +EOF
          692  +  if tryboth $CC -c $CFLAGS $test.c; then
          693  +    CFLAGS="$CFLAGS -DHAVE_HIDDEN"
          694  +    SFLAGS="$SFLAGS -DHAVE_HIDDEN"
          695  +    echo "Checking for attribute(visibility) support... Yes." | tee -a configure.log
          696  +  else
          697  +    echo "Checking for attribute(visibility) support... No." | tee -a configure.log
          698  +  fi
          699  +fi
          700  +
          701  +echo >> configure.log
          702  +
          703  +# find a four-byte unsiged integer type for crc calculations
          704  +cat > $test.c <<EOF
          705  +#include <stdio.h>
          706  +#define is32(n,t) for(n=1,k=0;n;n<<=1,k++);if(k==32){puts(t);return 0;}
          707  +int main() {
          708  +    int k;
          709  +    unsigned i;
          710  +    unsigned long l;
          711  +    unsigned short s;
          712  +    is32(i, "unsigned")
          713  +    is32(l, "unsigned long")
          714  +    is32(s, "unsigned short")
          715  +    return 1;
          716  +}
          717  +EOF
          718  +Z_U4=""
          719  +if try $CC $CFLAGS $test.c -o $test && Z_U4=`./$test` && test -n "$Z_U4"; then
          720  +  sed < zconf.h "/#define Z_U4/s/\/\* \.\/configure may/#define Z_U4 $Z_U4   \/* .\/configure put the/" > zconf.temp.h
          721  +  mv zconf.temp.h zconf.h
          722  +  echo "Looking for a four-byte integer type... Found." | tee -a configure.log
          723  +else
          724  +  echo "Looking for a four-byte integer type... Not found." | tee -a configure.log
          725  +fi
          726  +
          727  +# clean up files produced by running the compiler and linker
          728  +rm -f $test.[co] $test $test$shared_ext $test.gcno
          729  +
          730  +# show the results in the log
          731  +echo >> configure.log
          732  +echo ALL = $ALL >> configure.log
          733  +echo AR = $AR >> configure.log
          734  +echo ARFLAGS = $ARFLAGS >> configure.log
          735  +echo CC = $CC >> configure.log
          736  +echo CFLAGS = $CFLAGS >> configure.log
          737  +echo CPP = $CPP >> configure.log
          738  +echo EXE = $EXE >> configure.log
          739  +echo LDCONFIG = $LDCONFIG >> configure.log
          740  +echo LDFLAGS = $LDFLAGS >> configure.log
          741  +echo LDSHARED = $LDSHARED >> configure.log
          742  +echo LDSHAREDLIBC = $LDSHAREDLIBC >> configure.log
          743  +echo OBJC = $OBJC >> configure.log
          744  +echo PIC_OBJC = $PIC_OBJC >> configure.log
          745  +echo RANLIB = $RANLIB >> configure.log
          746  +echo SFLAGS = $SFLAGS >> configure.log
          747  +echo SHAREDLIB = $SHAREDLIB >> configure.log
          748  +echo SHAREDLIBM = $SHAREDLIBM >> configure.log
          749  +echo SHAREDLIBV = $SHAREDLIBV >> configure.log
          750  +echo STATICLIB = $STATICLIB >> configure.log
          751  +echo TEST = $TEST >> configure.log
          752  +echo VER = $VER >> configure.log
          753  +echo Z_U4 = $Z_U4 >> configure.log
          754  +echo exec_prefix = $exec_prefix >> configure.log
          755  +echo includedir = $includedir >> configure.log
          756  +echo libdir = $libdir >> configure.log
          757  +echo mandir = $mandir >> configure.log
          758  +echo prefix = $prefix >> configure.log
          759  +echo sharedlibdir = $sharedlibdir >> configure.log
          760  +echo uname = $uname >> configure.log
          761  +echo -------------------- >> configure.log
          762  +echo >> configure.log
          763  +echo >> configure.log
          764  +
          765  +# udpate Makefile with the configure results
          766  +sed < Makefile.in "
          767  +/^CC *=/s#=.*#=$CC#
          768  +/^CFLAGS *=/s#=.*#=$CFLAGS#
          769  +/^SFLAGS *=/s#=.*#=$SFLAGS#
          770  +/^LDFLAGS *=/s#=.*#=$LDFLAGS#
          771  +/^LDSHARED *=/s#=.*#=$LDSHARED#
          772  +/^CPP *=/s#=.*#=$CPP#
          773  +/^STATICLIB *=/s#=.*#=$STATICLIB#
          774  +/^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
          775  +/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
          776  +/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
          777  +/^AR *=/s#=.*#=$AR#
          778  +/^ARFLAGS *=/s#=.*#=$ARFLAGS#
          779  +/^RANLIB *=/s#=.*#=$RANLIB#
          780  +/^LDCONFIG *=/s#=.*#=$LDCONFIG#
          781  +/^LDSHAREDLIBC *=/s#=.*#=$LDSHAREDLIBC#
          782  +/^EXE *=/s#=.*#=$EXE#
          783  +/^prefix *=/s#=.*#=$prefix#
          784  +/^exec_prefix *=/s#=.*#=$exec_prefix#
          785  +/^libdir *=/s#=.*#=$libdir#
          786  +/^sharedlibdir *=/s#=.*#=$sharedlibdir#
          787  +/^includedir *=/s#=.*#=$includedir#
          788  +/^mandir *=/s#=.*#=$mandir#
          789  +/^OBJC *=/s#=.*#= $OBJC#
          790  +/^PIC_OBJC *=/s#=.*#= $PIC_OBJC#
          791  +/^all: */s#:.*#: $ALL#
          792  +/^test: */s#:.*#: $TEST#
          793  +" > Makefile
          794  +
          795  +# create zlib.pc with the configure results
          796  +sed < zlib.pc.in "
          797  +/^CC *=/s#=.*#=$CC#
          798  +/^CFLAGS *=/s#=.*#=$CFLAGS#
          799  +/^CPP *=/s#=.*#=$CPP#
          800  +/^LDSHARED *=/s#=.*#=$LDSHARED#
          801  +/^STATICLIB *=/s#=.*#=$STATICLIB#
          802  +/^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
          803  +/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
          804  +/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
          805  +/^AR *=/s#=.*#=$AR#
          806  +/^ARFLAGS *=/s#=.*#=$ARFLAGS#
          807  +/^RANLIB *=/s#=.*#=$RANLIB#
          808  +/^EXE *=/s#=.*#=$EXE#
          809  +/^prefix *=/s#=.*#=$prefix#
          810  +/^exec_prefix *=/s#=.*#=$exec_prefix#
          811  +/^libdir *=/s#=.*#=$libdir#
          812  +/^sharedlibdir *=/s#=.*#=$sharedlibdir#
          813  +/^includedir *=/s#=.*#=$includedir#
          814  +/^mandir *=/s#=.*#=$mandir#
          815  +/^LDFLAGS *=/s#=.*#=$LDFLAGS#
          816  +" | sed -e "
          817  +s/\@VERSION\@/$VER/g;
          818  +" > zlib.pc
          819  +#

Added compat/zlib/contrib/README.contrib.

            1  +All files under this contrib directory are UNSUPPORTED. There were
            2  +provided by users of zlib and were not tested by the authors of zlib.
            3  +Use at your own risk. Please contact the authors of the contributions
            4  +for help about these, not the zlib authors. Thanks.
            5  +
            6  +
            7  +ada/        by Dmitriy Anisimkov <anisimkov@yahoo.com>
            8  +        Support for Ada
            9  +        See http://zlib-ada.sourceforge.net/
           10  +
           11  +amd64/      by Mikhail Teterin <mi@ALDAN.algebra.com>
           12  +        asm code for AMD64
           13  +        See patch at http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/96393
           14  +
           15  +asm686/     by Brian Raiter <breadbox@muppetlabs.com>
           16  +        asm code for Pentium and PPro/PII, using the AT&T (GNU as) syntax
           17  +        See http://www.muppetlabs.com/~breadbox/software/assembly.html
           18  +
           19  +blast/      by Mark Adler <madler@alumni.caltech.edu>
           20  +        Decompressor for output of PKWare Data Compression Library (DCL)
           21  +
           22  +delphi/     by Cosmin Truta <cosmint@cs.ubbcluj.ro>
           23  +        Support for Delphi and C++ Builder
           24  +
           25  +dotzlib/    by Henrik Ravn <henrik@ravn.com>
           26  +        Support for Microsoft .Net and Visual C++ .Net
           27  +
           28  +gcc_gvmat64/by Gilles Vollant <info@winimage.com>
           29  +        GCC Version of x86 64-bit (AMD64 and Intel EM64t) code for x64
           30  +        assembler to replace longest_match() and inflate_fast()
           31  +
           32  +infback9/   by Mark Adler <madler@alumni.caltech.edu>
           33  +        Unsupported diffs to infback to decode the deflate64 format
           34  +
           35  +inflate86/  by Chris Anderson <christop@charm.net>
           36  +        Tuned x86 gcc asm code to replace inflate_fast()
           37  +
           38  +iostream/   by Kevin Ruland <kevin@rodin.wustl.edu>
           39  +        A C++ I/O streams interface to the zlib gz* functions
           40  +
           41  +iostream2/  by Tyge Løvset <Tyge.Lovset@cmr.no>
           42  +        Another C++ I/O streams interface
           43  +
           44  +iostream3/  by Ludwig Schwardt <schwardt@sun.ac.za>
           45  +            and Kevin Ruland <kevin@rodin.wustl.edu>
           46  +        Yet another C++ I/O streams interface
           47  +
           48  +masmx64/    by Gilles Vollant <info@winimage.com>
           49  +        x86 64-bit (AMD64 and Intel EM64t) code for x64 assembler to
           50  +        replace longest_match() and inflate_fast(),  also masm x86
           51  +        64-bits translation of Chris Anderson inflate_fast()
           52  +
           53  +masmx86/    by Gilles Vollant <info@winimage.com>
           54  +        x86 asm code to replace longest_match() and inflate_fast(),
           55  +        for Visual C++ and MASM (32 bits).
           56  +        Based on Brian Raiter (asm686) and Chris Anderson (inflate86)
           57  +
           58  +minizip/    by Gilles Vollant <info@winimage.com>
           59  +        Mini zip and unzip based on zlib
           60  +        Includes Zip64 support by Mathias Svensson <mathias@result42.com>
           61  +        See http://www.winimage.com/zLibDll/unzip.html
           62  +
           63  +pascal/     by Bob Dellaca <bobdl@xtra.co.nz> et al.
           64  +        Support for Pascal
           65  +
           66  +puff/       by Mark Adler <madler@alumni.caltech.edu>
           67  +        Small, low memory usage inflate.  Also serves to provide an
           68  +        unambiguous description of the deflate format.
           69  +
           70  +testzlib/   by Gilles Vollant <info@winimage.com>
           71  +        Example of the use of zlib
           72  +
           73  +untgz/      by Pedro A. Aranda Gutierrez <paag@tid.es>
           74  +        A very simple tar.gz file extractor using zlib
           75  +
           76  +vstudio/    by Gilles Vollant <info@winimage.com>
           77  +        Building a minizip-enhanced zlib with Microsoft Visual Studio

Added compat/zlib/contrib/ada/buffer_demo.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2004 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +--
            9  +--  $Id: buffer_demo.adb,v 1.3 2004/09/06 06:55:35 vagul Exp $
           10  +
           11  +--  This demo program provided by Dr Steve Sangwine <sjs@essex.ac.uk>
           12  +--
           13  +--  Demonstration of a problem with Zlib-Ada (already fixed) when a buffer
           14  +--  of exactly the correct size is used for decompressed data, and the last
           15  +--  few bytes passed in to Zlib are checksum bytes.
           16  +
           17  +--  This program compresses a string of text, and then decompresses the
           18  +--  compressed text into a buffer of the same size as the original text.
           19  +
           20  +with Ada.Streams; use Ada.Streams;
           21  +with Ada.Text_IO;
           22  +
           23  +with ZLib; use ZLib;
           24  +
           25  +procedure Buffer_Demo is
           26  +   EOL  : Character renames ASCII.LF;
           27  +   Text : constant String
           28  +     := "Four score and seven years ago our fathers brought forth," & EOL &
           29  +        "upon this continent, a new nation, conceived in liberty," & EOL &
           30  +        "and dedicated to the proposition that `all men are created equal'.";
           31  +
           32  +   Source : Stream_Element_Array (1 .. Text'Length);
           33  +   for Source'Address use Text'Address;
           34  +
           35  +begin
           36  +   Ada.Text_IO.Put (Text);
           37  +   Ada.Text_IO.New_Line;
           38  +   Ada.Text_IO.Put_Line
           39  +     ("Uncompressed size : " & Positive'Image (Text'Length) & " bytes");
           40  +
           41  +   declare
           42  +      Compressed_Data : Stream_Element_Array (1 .. Text'Length);
           43  +      L               : Stream_Element_Offset;
           44  +   begin
           45  +      Compress : declare
           46  +         Compressor : Filter_Type;
           47  +         I : Stream_Element_Offset;
           48  +      begin
           49  +         Deflate_Init (Compressor);
           50  +
           51  +         --  Compress the whole of T at once.
           52  +
           53  +         Translate (Compressor, Source, I, Compressed_Data, L, Finish);
           54  +         pragma Assert (I = Source'Last);
           55  +
           56  +         Close (Compressor);
           57  +
           58  +         Ada.Text_IO.Put_Line
           59  +           ("Compressed size :   "
           60  +            & Stream_Element_Offset'Image (L) & " bytes");
           61  +      end Compress;
           62  +
           63  +      --  Now we decompress the data, passing short blocks of data to Zlib
           64  +      --  (because this demonstrates the problem - the last block passed will
           65  +      --  contain checksum information and there will be no output, only a
           66  +      --  check inside Zlib that the checksum is correct).
           67  +
           68  +      Decompress : declare
           69  +         Decompressor : Filter_Type;
           70  +
           71  +         Uncompressed_Data : Stream_Element_Array (1 .. Text'Length);
           72  +
           73  +         Block_Size : constant := 4;
           74  +         --  This makes sure that the last block contains
           75  +         --  only Adler checksum data.
           76  +
           77  +         P : Stream_Element_Offset := Compressed_Data'First - 1;
           78  +         O : Stream_Element_Offset;
           79  +      begin
           80  +         Inflate_Init (Decompressor);
           81  +
           82  +         loop
           83  +            Translate
           84  +              (Decompressor,
           85  +               Compressed_Data
           86  +                 (P + 1 .. Stream_Element_Offset'Min (P + Block_Size, L)),
           87  +               P,
           88  +               Uncompressed_Data
           89  +                 (Total_Out (Decompressor) + 1 .. Uncompressed_Data'Last),
           90  +               O,
           91  +               No_Flush);
           92  +
           93  +               Ada.Text_IO.Put_Line
           94  +                 ("Total in : " & Count'Image (Total_In (Decompressor)) &
           95  +                  ", out : " & Count'Image (Total_Out (Decompressor)));
           96  +
           97  +               exit when P = L;
           98  +         end loop;
           99  +
          100  +         Ada.Text_IO.New_Line;
          101  +         Ada.Text_IO.Put_Line
          102  +           ("Decompressed text matches original text : "
          103  +             & Boolean'Image (Uncompressed_Data = Source));
          104  +      end Decompress;
          105  +   end;
          106  +end Buffer_Demo;

Added compat/zlib/contrib/ada/mtest.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +--  Continuous test for ZLib multithreading. If the test would fail
            9  +--  we should provide thread safe allocation routines for the Z_Stream.
           10  +--
           11  +--  $Id: mtest.adb,v 1.4 2004/07/23 07:49:54 vagul Exp $
           12  +
           13  +with ZLib;
           14  +with Ada.Streams;
           15  +with Ada.Numerics.Discrete_Random;
           16  +with Ada.Text_IO;
           17  +with Ada.Exceptions;
           18  +with Ada.Task_Identification;
           19  +
           20  +procedure MTest is
           21  +   use Ada.Streams;
           22  +   use ZLib;
           23  +
           24  +   Stop : Boolean := False;
           25  +
           26  +   pragma Atomic (Stop);
           27  +
           28  +   subtype Visible_Symbols is Stream_Element range 16#20# .. 16#7E#;
           29  +
           30  +   package Random_Elements is
           31  +      new Ada.Numerics.Discrete_Random (Visible_Symbols);
           32  +
           33  +   task type Test_Task;
           34  +
           35  +   task body Test_Task is
           36  +      Buffer : Stream_Element_Array (1 .. 100_000);
           37  +      Gen : Random_Elements.Generator;
           38  +
           39  +      Buffer_First  : Stream_Element_Offset;
           40  +      Compare_First : Stream_Element_Offset;
           41  +
           42  +      Deflate : Filter_Type;
           43  +      Inflate : Filter_Type;
           44  +
           45  +      procedure Further (Item : in Stream_Element_Array);
           46  +
           47  +      procedure Read_Buffer
           48  +        (Item : out Ada.Streams.Stream_Element_Array;
           49  +         Last : out Ada.Streams.Stream_Element_Offset);
           50  +
           51  +      -------------
           52  +      -- Further --
           53  +      -------------
           54  +
           55  +      procedure Further (Item : in Stream_Element_Array) is
           56  +
           57  +         procedure Compare (Item : in Stream_Element_Array);
           58  +
           59  +         -------------
           60  +         -- Compare --
           61  +         -------------
           62  +
           63  +         procedure Compare (Item : in Stream_Element_Array) is
           64  +            Next_First : Stream_Element_Offset := Compare_First + Item'Length;
           65  +         begin
           66  +            if Buffer (Compare_First .. Next_First - 1) /= Item then
           67  +               raise Program_Error;
           68  +            end if;
           69  +
           70  +            Compare_First := Next_First;
           71  +         end Compare;
           72  +
           73  +         procedure Compare_Write is new ZLib.Write (Write => Compare);
           74  +      begin
           75  +         Compare_Write (Inflate, Item, No_Flush);
           76  +      end Further;
           77  +
           78  +      -----------------
           79  +      -- Read_Buffer --
           80  +      -----------------
           81  +
           82  +      procedure Read_Buffer
           83  +        (Item : out Ada.Streams.Stream_Element_Array;
           84  +         Last : out Ada.Streams.Stream_Element_Offset)
           85  +      is
           86  +         Buff_Diff   : Stream_Element_Offset := Buffer'Last - Buffer_First;
           87  +         Next_First : Stream_Element_Offset;
           88  +      begin
           89  +         if Item'Length <= Buff_Diff then
           90  +            Last := Item'Last;
           91  +
           92  +            Next_First := Buffer_First + Item'Length;
           93  +
           94  +            Item := Buffer (Buffer_First .. Next_First - 1);
           95  +
           96  +            Buffer_First := Next_First;
           97  +         else
           98  +            Last := Item'First + Buff_Diff;
           99  +            Item (Item'First .. Last) := Buffer (Buffer_First .. Buffer'Last);
          100  +            Buffer_First := Buffer'Last + 1;
          101  +         end if;
          102  +      end Read_Buffer;
          103  +
          104  +      procedure Translate is new Generic_Translate
          105  +                                   (Data_In  => Read_Buffer,
          106  +                                    Data_Out => Further);
          107  +
          108  +   begin
          109  +      Random_Elements.Reset (Gen);
          110  +
          111  +      Buffer := (others => 20);
          112  +
          113  +      Main : loop
          114  +         for J in Buffer'Range loop
          115  +            Buffer (J) := Random_Elements.Random (Gen);
          116  +
          117  +            Deflate_Init (Deflate);
          118  +            Inflate_Init (Inflate);
          119  +
          120  +            Buffer_First  := Buffer'First;
          121  +            Compare_First := Buffer'First;
          122  +
          123  +            Translate (Deflate);
          124  +
          125  +            if Compare_First /= Buffer'Last + 1 then
          126  +               raise Program_Error;
          127  +            end if;
          128  +
          129  +            Ada.Text_IO.Put_Line
          130  +              (Ada.Task_Identification.Image
          131  +                 (Ada.Task_Identification.Current_Task)
          132  +               & Stream_Element_Offset'Image (J)
          133  +               & ZLib.Count'Image (Total_Out (Deflate)));
          134  +
          135  +            Close (Deflate);
          136  +            Close (Inflate);
          137  +
          138  +            exit Main when Stop;
          139  +         end loop;
          140  +      end loop Main;
          141  +   exception
          142  +      when E : others =>
          143  +         Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
          144  +         Stop := True;
          145  +   end Test_Task;
          146  +
          147  +   Test : array (1 .. 4) of Test_Task;
          148  +
          149  +   pragma Unreferenced (Test);
          150  +
          151  +   Dummy : Character;
          152  +
          153  +begin
          154  +   Ada.Text_IO.Get_Immediate (Dummy);
          155  +   Stop := True;
          156  +end MTest;

Added compat/zlib/contrib/ada/read.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: read.adb,v 1.8 2004/05/31 10:53:40 vagul Exp $
           10  +
           11  +--  Test/demo program for the generic read interface.
           12  +
           13  +with Ada.Numerics.Discrete_Random;
           14  +with Ada.Streams;
           15  +with Ada.Text_IO;
           16  +
           17  +with ZLib;
           18  +
           19  +procedure Read is
           20  +
           21  +   use Ada.Streams;
           22  +
           23  +   ------------------------------------
           24  +   --  Test configuration parameters --
           25  +   ------------------------------------
           26  +
           27  +   File_Size   : Stream_Element_Offset := 100_000;
           28  +
           29  +   Continuous  : constant Boolean          := False;
           30  +   --  If this constant is True, the test would be repeated again and again,
           31  +   --  with increment File_Size for every iteration.
           32  +
           33  +   Header      : constant ZLib.Header_Type := ZLib.Default;
           34  +   --  Do not use Header other than Default in ZLib versions 1.1.4 and older.
           35  +
           36  +   Init_Random : constant := 8;
           37  +   --  We are using the same random sequence, in case of we catch bug,
           38  +   --  so we would be able to reproduce it.
           39  +
           40  +   -- End --
           41  +
           42  +   Pack_Size : Stream_Element_Offset;
           43  +   Offset    : Stream_Element_Offset;
           44  +
           45  +   Filter     : ZLib.Filter_Type;
           46  +
           47  +   subtype Visible_Symbols
           48  +      is Stream_Element range 16#20# .. 16#7E#;
           49  +
           50  +   package Random_Elements is new
           51  +      Ada.Numerics.Discrete_Random (Visible_Symbols);
           52  +
           53  +   Gen : Random_Elements.Generator;
           54  +   Period  : constant Stream_Element_Offset := 200;
           55  +   --  Period constant variable for random generator not to be very random.
           56  +   --  Bigger period, harder random.
           57  +
           58  +   Read_Buffer : Stream_Element_Array (1 .. 2048);
           59  +   Read_First  : Stream_Element_Offset;
           60  +   Read_Last   : Stream_Element_Offset;
           61  +
           62  +   procedure Reset;
           63  +
           64  +   procedure Read
           65  +     (Item : out Stream_Element_Array;
           66  +      Last : out Stream_Element_Offset);
           67  +   --  this procedure is for generic instantiation of
           68  +   --  ZLib.Read
           69  +   --  reading data from the File_In.
           70  +
           71  +   procedure Read is new ZLib.Read
           72  +                           (Read,
           73  +                            Read_Buffer,
           74  +                            Rest_First => Read_First,
           75  +                            Rest_Last  => Read_Last);
           76  +
           77  +   ----------
           78  +   -- Read --
           79  +   ----------
           80  +
           81  +   procedure Read
           82  +     (Item : out Stream_Element_Array;
           83  +      Last : out Stream_Element_Offset) is
           84  +   begin
           85  +      Last := Stream_Element_Offset'Min
           86  +               (Item'Last,
           87  +                Item'First + File_Size - Offset);
           88  +
           89  +      for J in Item'First .. Last loop
           90  +         if J < Item'First + Period then
           91  +            Item (J) := Random_Elements.Random (Gen);
           92  +         else
           93  +            Item (J) := Item (J - Period);
           94  +         end if;
           95  +
           96  +         Offset   := Offset + 1;
           97  +      end loop;
           98  +   end Read;
           99  +
          100  +   -----------
          101  +   -- Reset --
          102  +   -----------
          103  +
          104  +   procedure Reset is
          105  +   begin
          106  +      Random_Elements.Reset (Gen, Init_Random);
          107  +      Pack_Size := 0;
          108  +      Offset := 1;
          109  +      Read_First := Read_Buffer'Last + 1;
          110  +      Read_Last  := Read_Buffer'Last;
          111  +   end Reset;
          112  +
          113  +begin
          114  +   Ada.Text_IO.Put_Line ("ZLib " & ZLib.Version);
          115  +
          116  +   loop
          117  +      for Level in ZLib.Compression_Level'Range loop
          118  +
          119  +         Ada.Text_IO.Put ("Level ="
          120  +            & ZLib.Compression_Level'Image (Level));
          121  +
          122  +         --  Deflate using generic instantiation.
          123  +
          124  +         ZLib.Deflate_Init
          125  +               (Filter,
          126  +                Level,
          127  +                Header => Header);
          128  +
          129  +         Reset;
          130  +
          131  +         Ada.Text_IO.Put
          132  +           (Stream_Element_Offset'Image (File_Size) & " ->");
          133  +
          134  +         loop
          135  +            declare
          136  +               Buffer : Stream_Element_Array (1 .. 1024);
          137  +               Last   : Stream_Element_Offset;
          138  +            begin
          139  +               Read (Filter, Buffer, Last);
          140  +
          141  +               Pack_Size := Pack_Size + Last - Buffer'First + 1;
          142  +
          143  +               exit when Last < Buffer'Last;
          144  +            end;
          145  +         end loop;
          146  +
          147  +         Ada.Text_IO.Put_Line (Stream_Element_Offset'Image (Pack_Size));
          148  +
          149  +         ZLib.Close (Filter);
          150  +      end loop;
          151  +
          152  +      exit when not Continuous;
          153  +
          154  +      File_Size := File_Size + 1;
          155  +   end loop;
          156  +end Read;

Added compat/zlib/contrib/ada/readme.txt.

            1  +                        ZLib for Ada thick binding (ZLib.Ada)
            2  +                        Release 1.3
            3  +
            4  +ZLib.Ada is a thick binding interface to the popular ZLib data
            5  +compression library, available at http://www.gzip.org/zlib/.
            6  +It provides Ada-style access to the ZLib C library.
            7  +
            8  +
            9  +        Here are the main changes since ZLib.Ada 1.2:
           10  +
           11  +- Attension: ZLib.Read generic routine have a initialization requirement
           12  +  for Read_Last parameter now. It is a bit incompartible with previous version,
           13  +  but extends functionality, we could use new parameters Allow_Read_Some and
           14  +  Flush now.
           15  +
           16  +- Added Is_Open routines to ZLib and ZLib.Streams packages.
           17  +
           18  +- Add pragma Assert to check Stream_Element is 8 bit.
           19  +
           20  +- Fix extraction to buffer with exact known decompressed size. Error reported by
           21  +  Steve Sangwine.
           22  +
           23  +- Fix definition of ULong (changed to unsigned_long), fix regression on 64 bits
           24  +  computers. Patch provided by Pascal Obry.
           25  +
           26  +- Add Status_Error exception definition.
           27  +
           28  +- Add pragma Assertion that Ada.Streams.Stream_Element size is 8 bit.
           29  +
           30  +
           31  +        How to build ZLib.Ada under GNAT
           32  +
           33  +You should have the ZLib library already build on your computer, before
           34  +building ZLib.Ada. Make the directory of ZLib.Ada sources current and
           35  +issue the command:
           36  +
           37  +  gnatmake test -largs -L<directory where libz.a is> -lz
           38  +
           39  +Or use the GNAT project file build for GNAT 3.15 or later:
           40  +
           41  +  gnatmake -Pzlib.gpr -L<directory where libz.a is>
           42  +
           43  +
           44  +        How to build ZLib.Ada under Aonix ObjectAda for Win32 7.2.2
           45  +
           46  +1. Make a project with all *.ads and *.adb files from the distribution.
           47  +2. Build the libz.a library from the ZLib C sources.
           48  +3. Rename libz.a to z.lib.
           49  +4. Add the library z.lib to the project.
           50  +5. Add the libc.lib library from the ObjectAda distribution to the project.
           51  +6. Build the executable using test.adb as a main procedure.
           52  +
           53  +
           54  +        How to use ZLib.Ada
           55  +
           56  +The source files test.adb and read.adb are small demo programs that show
           57  +the main functionality of ZLib.Ada.
           58  +
           59  +The routines from the package specifications are commented.
           60  +
           61  +
           62  +Homepage: http://zlib-ada.sourceforge.net/
           63  +Author: Dmitriy Anisimkov <anisimkov@yahoo.com>
           64  +
           65  +Contributors: Pascal Obry <pascal@obry.org>, Steve Sangwine <sjs@essex.ac.uk>

Added compat/zlib/contrib/ada/test.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: test.adb,v 1.17 2003/08/12 12:13:30 vagul Exp $
           10  +
           11  +--  The program has a few aims.
           12  +--  1. Test ZLib.Ada95 thick binding functionality.
           13  +--  2. Show the example of use main functionality of the ZLib.Ada95 binding.
           14  +--  3. Build this program automatically compile all ZLib.Ada95 packages under
           15  +--     GNAT Ada95 compiler.
           16  +
           17  +with ZLib.Streams;
           18  +with Ada.Streams.Stream_IO;
           19  +with Ada.Numerics.Discrete_Random;
           20  +
           21  +with Ada.Text_IO;
           22  +
           23  +with Ada.Calendar;
           24  +
           25  +procedure Test is
           26  +
           27  +   use Ada.Streams;
           28  +   use Stream_IO;
           29  +
           30  +   ------------------------------------
           31  +   --  Test configuration parameters --
           32  +   ------------------------------------
           33  +
           34  +   File_Size   : Count   := 100_000;
           35  +   Continuous  : constant Boolean := False;
           36  +
           37  +   Header      : constant ZLib.Header_Type := ZLib.Default;
           38  +                                              --  ZLib.None;
           39  +                                              --  ZLib.Auto;
           40  +                                              --  ZLib.GZip;
           41  +   --  Do not use Header other then Default in ZLib versions 1.1.4
           42  +   --  and older.
           43  +
           44  +   Strategy    : constant ZLib.Strategy_Type := ZLib.Default_Strategy;
           45  +   Init_Random : constant := 10;
           46  +
           47  +   -- End --
           48  +
           49  +   In_File_Name  : constant String := "testzlib.in";
           50  +   --  Name of the input file
           51  +
           52  +   Z_File_Name   : constant String := "testzlib.zlb";
           53  +   --  Name of the compressed file.
           54  +
           55  +   Out_File_Name : constant String := "testzlib.out";
           56  +   --  Name of the decompressed file.
           57  +
           58  +   File_In   : File_Type;
           59  +   File_Out  : File_Type;
           60  +   File_Back : File_Type;
           61  +   File_Z    : ZLib.Streams.Stream_Type;
           62  +
           63  +   Filter : ZLib.Filter_Type;
           64  +
           65  +   Time_Stamp : Ada.Calendar.Time;
           66  +
           67  +   procedure Generate_File;
           68  +   --  Generate file of spetsified size with some random data.
           69  +   --  The random data is repeatable, for the good compression.
           70  +
           71  +   procedure Compare_Streams
           72  +     (Left, Right : in out Root_Stream_Type'Class);
           73  +   --  The procedure compearing data in 2 streams.
           74  +   --  It is for compare data before and after compression/decompression.
           75  +
           76  +   procedure Compare_Files (Left, Right : String);
           77  +   --  Compare files. Based on the Compare_Streams.
           78  +
           79  +   procedure Copy_Streams
           80  +     (Source, Target : in out Root_Stream_Type'Class;
           81  +      Buffer_Size    : in     Stream_Element_Offset := 1024);
           82  +   --  Copying data from one stream to another. It is for test stream
           83  +   --  interface of the library.
           84  +
           85  +   procedure Data_In
           86  +     (Item : out Stream_Element_Array;
           87  +      Last : out Stream_Element_Offset);
           88  +   --  this procedure is for generic instantiation of
           89  +   --  ZLib.Generic_Translate.
           90  +   --  reading data from the File_In.
           91  +
           92  +   procedure Data_Out (Item : in Stream_Element_Array);
           93  +   --  this procedure is for generic instantiation of
           94  +   --  ZLib.Generic_Translate.
           95  +   --  writing data to the File_Out.
           96  +
           97  +   procedure Stamp;
           98  +   --  Store the timestamp to the local variable.
           99  +
          100  +   procedure Print_Statistic (Msg : String; Data_Size : ZLib.Count);
          101  +   --  Print the time statistic with the message.
          102  +
          103  +   procedure Translate is new ZLib.Generic_Translate
          104  +                                (Data_In  => Data_In,
          105  +                                 Data_Out => Data_Out);
          106  +   --  This procedure is moving data from File_In to File_Out
          107  +   --  with compression or decompression, depend on initialization of
          108  +   --  Filter parameter.
          109  +
          110  +   -------------------
          111  +   -- Compare_Files --
          112  +   -------------------
          113  +
          114  +   procedure Compare_Files (Left, Right : String) is
          115  +      Left_File, Right_File : File_Type;
          116  +   begin
          117  +      Open (Left_File, In_File, Left);
          118  +      Open (Right_File, In_File, Right);
          119  +      Compare_Streams (Stream (Left_File).all, Stream (Right_File).all);
          120  +      Close (Left_File);
          121  +      Close (Right_File);
          122  +   end Compare_Files;
          123  +
          124  +   ---------------------
          125  +   -- Compare_Streams --
          126  +   ---------------------
          127  +
          128  +   procedure Compare_Streams
          129  +     (Left, Right : in out Ada.Streams.Root_Stream_Type'Class)
          130  +   is
          131  +      Left_Buffer, Right_Buffer : Stream_Element_Array (0 .. 16#FFF#);
          132  +      Left_Last, Right_Last : Stream_Element_Offset;
          133  +   begin
          134  +      loop
          135  +         Read (Left, Left_Buffer, Left_Last);
          136  +         Read (Right, Right_Buffer, Right_Last);
          137  +
          138  +         if Left_Last /= Right_Last then
          139  +            Ada.Text_IO.Put_Line ("Compare error :"
          140  +              & Stream_Element_Offset'Image (Left_Last)
          141  +              & " /= "
          142  +              & Stream_Element_Offset'Image (Right_Last));
          143  +
          144  +            raise Constraint_Error;
          145  +
          146  +         elsif Left_Buffer (0 .. Left_Last)
          147  +               /= Right_Buffer (0 .. Right_Last)
          148  +         then
          149  +            Ada.Text_IO.Put_Line ("ERROR: IN and OUT files is not equal.");
          150  +            raise Constraint_Error;
          151  +
          152  +         end if;
          153  +
          154  +         exit when Left_Last < Left_Buffer'Last;
          155  +      end loop;
          156  +   end Compare_Streams;
          157  +
          158  +   ------------------
          159  +   -- Copy_Streams --
          160  +   ------------------
          161  +
          162  +   procedure Copy_Streams
          163  +     (Source, Target : in out Ada.Streams.Root_Stream_Type'Class;
          164  +      Buffer_Size    : in     Stream_Element_Offset := 1024)
          165  +   is
          166  +      Buffer : Stream_Element_Array (1 .. Buffer_Size);
          167  +      Last   : Stream_Element_Offset;
          168  +   begin
          169  +      loop
          170  +         Read  (Source, Buffer, Last);
          171  +         Write (Target, Buffer (1 .. Last));
          172  +
          173  +         exit when Last < Buffer'Last;
          174  +      end loop;
          175  +   end Copy_Streams;
          176  +
          177  +   -------------
          178  +   -- Data_In --
          179  +   -------------
          180  +
          181  +   procedure Data_In
          182  +     (Item : out Stream_Element_Array;
          183  +      Last : out Stream_Element_Offset) is
          184  +   begin
          185  +      Read (File_In, Item, Last);
          186  +   end Data_In;
          187  +
          188  +   --------------
          189  +   -- Data_Out --
          190  +   --------------
          191  +
          192  +   procedure Data_Out (Item : in Stream_Element_Array) is
          193  +   begin
          194  +      Write (File_Out, Item);
          195  +   end Data_Out;
          196  +
          197  +   -------------------
          198  +   -- Generate_File --
          199  +   -------------------
          200  +
          201  +   procedure Generate_File is
          202  +      subtype Visible_Symbols is Stream_Element range 16#20# .. 16#7E#;
          203  +
          204  +      package Random_Elements is
          205  +         new Ada.Numerics.Discrete_Random (Visible_Symbols);
          206  +
          207  +      Gen    : Random_Elements.Generator;
          208  +      Buffer : Stream_Element_Array := (1 .. 77 => 16#20#) & 10;
          209  +
          210  +      Buffer_Count : constant Count := File_Size / Buffer'Length;
          211  +      --  Number of same buffers in the packet.
          212  +
          213  +      Density : constant Count := 30; --  from 0 to Buffer'Length - 2;
          214  +
          215  +      procedure Fill_Buffer (J, D : in Count);
          216  +      --  Change the part of the buffer.
          217  +
          218  +      -----------------
          219  +      -- Fill_Buffer --
          220  +      -----------------
          221  +
          222  +      procedure Fill_Buffer (J, D : in Count) is
          223  +      begin
          224  +         for K in 0 .. D loop
          225  +            Buffer
          226  +              (Stream_Element_Offset ((J + K) mod (Buffer'Length - 1) + 1))
          227  +             := Random_Elements.Random (Gen);
          228  +
          229  +         end loop;
          230  +      end Fill_Buffer;
          231  +
          232  +   begin
          233  +      Random_Elements.Reset (Gen, Init_Random);
          234  +
          235  +      Create (File_In, Out_File, In_File_Name);
          236  +
          237  +      Fill_Buffer (1, Buffer'Length - 2);
          238  +
          239  +      for J in 1 .. Buffer_Count loop
          240  +         Write (File_In, Buffer);
          241  +
          242  +         Fill_Buffer (J, Density);
          243  +      end loop;
          244  +
          245  +      --  fill remain size.
          246  +
          247  +      Write
          248  +        (File_In,
          249  +         Buffer
          250  +           (1 .. Stream_Element_Offset
          251  +                   (File_Size - Buffer'Length * Buffer_Count)));
          252  +
          253  +      Flush (File_In);
          254  +      Close (File_In);
          255  +   end Generate_File;
          256  +
          257  +   ---------------------
          258  +   -- Print_Statistic --
          259  +   ---------------------
          260  +
          261  +   procedure Print_Statistic (Msg : String; Data_Size : ZLib.Count) is
          262  +      use Ada.Calendar;
          263  +      use Ada.Text_IO;
          264  +
          265  +      package Count_IO is new Integer_IO (ZLib.Count);
          266  +
          267  +      Curr_Dur : Duration := Clock - Time_Stamp;
          268  +   begin
          269  +      Put (Msg);
          270  +
          271  +      Set_Col (20);
          272  +      Ada.Text_IO.Put ("size =");
          273  +
          274  +      Count_IO.Put
          275  +        (Data_Size,
          276  +         Width => Stream_IO.Count'Image (File_Size)'Length);
          277  +
          278  +      Put_Line (" duration =" & Duration'Image (Curr_Dur));
          279  +   end Print_Statistic;
          280  +
          281  +   -----------
          282  +   -- Stamp --
          283  +   -----------
          284  +
          285  +   procedure Stamp is
          286  +   begin
          287  +      Time_Stamp := Ada.Calendar.Clock;
          288  +   end Stamp;
          289  +
          290  +begin
          291  +   Ada.Text_IO.Put_Line ("ZLib " & ZLib.Version);
          292  +
          293  +   loop
          294  +      Generate_File;
          295  +
          296  +      for Level in ZLib.Compression_Level'Range loop
          297  +
          298  +         Ada.Text_IO.Put_Line ("Level ="
          299  +            & ZLib.Compression_Level'Image (Level));
          300  +
          301  +         --  Test generic interface.
          302  +         Open   (File_In, In_File, In_File_Name);
          303  +         Create (File_Out, Out_File, Z_File_Name);
          304  +
          305  +         Stamp;
          306  +
          307  +         --  Deflate using generic instantiation.
          308  +
          309  +         ZLib.Deflate_Init
          310  +               (Filter   => Filter,
          311  +                Level    => Level,
          312  +                Strategy => Strategy,
          313  +                Header   => Header);
          314  +
          315  +         Translate (Filter);
          316  +         Print_Statistic ("Generic compress", ZLib.Total_Out (Filter));
          317  +         ZLib.Close (Filter);
          318  +
          319  +         Close (File_In);
          320  +         Close (File_Out);
          321  +
          322  +         Open   (File_In, In_File, Z_File_Name);
          323  +         Create (File_Out, Out_File, Out_File_Name);
          324  +
          325  +         Stamp;
          326  +
          327  +         --  Inflate using generic instantiation.
          328  +
          329  +         ZLib.Inflate_Init (Filter, Header => Header);
          330  +
          331  +         Translate (Filter);
          332  +         Print_Statistic ("Generic decompress", ZLib.Total_Out (Filter));
          333  +
          334  +         ZLib.Close (Filter);
          335  +
          336  +         Close (File_In);
          337  +         Close (File_Out);
          338  +
          339  +         Compare_Files (In_File_Name, Out_File_Name);
          340  +
          341  +         --  Test stream interface.
          342  +
          343  +         --  Compress to the back stream.
          344  +
          345  +         Open   (File_In, In_File, In_File_Name);
          346  +         Create (File_Back, Out_File, Z_File_Name);
          347  +
          348  +         Stamp;
          349  +
          350  +         ZLib.Streams.Create
          351  +           (Stream          => File_Z,
          352  +            Mode            => ZLib.Streams.Out_Stream,
          353  +            Back            => ZLib.Streams.Stream_Access
          354  +                                 (Stream (File_Back)),
          355  +            Back_Compressed => True,
          356  +            Level           => Level,
          357  +            Strategy        => Strategy,
          358  +            Header          => Header);
          359  +
          360  +         Copy_Streams
          361  +           (Source => Stream (File_In).all,
          362  +            Target => File_Z);
          363  +
          364  +         --  Flushing internal buffers to the back stream.
          365  +
          366  +         ZLib.Streams.Flush (File_Z, ZLib.Finish);
          367  +
          368  +         Print_Statistic ("Write compress",
          369  +                          ZLib.Streams.Write_Total_Out (File_Z));
          370  +
          371  +         ZLib.Streams.Close (File_Z);
          372  +
          373  +         Close (File_In);
          374  +         Close (File_Back);
          375  +
          376  +         --  Compare reading from original file and from
          377  +         --  decompression stream.
          378  +
          379  +         Open (File_In,   In_File, In_File_Name);
          380  +         Open (File_Back, In_File, Z_File_Name);
          381  +
          382  +         ZLib.Streams.Create
          383  +           (Stream          => File_Z,
          384  +            Mode            => ZLib.Streams.In_Stream,
          385  +            Back            => ZLib.Streams.Stream_Access
          386  +                                 (Stream (File_Back)),
          387  +            Back_Compressed => True,
          388  +            Header          => Header);
          389  +
          390  +         Stamp;
          391  +         Compare_Streams (Stream (File_In).all, File_Z);
          392  +
          393  +         Print_Statistic ("Read decompress",
          394  +                          ZLib.Streams.Read_Total_Out (File_Z));
          395  +
          396  +         ZLib.Streams.Close (File_Z);
          397  +         Close (File_In);
          398  +         Close (File_Back);
          399  +
          400  +         --  Compress by reading from compression stream.
          401  +
          402  +         Open (File_Back, In_File, In_File_Name);
          403  +         Create (File_Out, Out_File, Z_File_Name);
          404  +
          405  +         ZLib.Streams.Create
          406  +           (Stream          => File_Z,
          407  +            Mode            => ZLib.Streams.In_Stream,
          408  +            Back            => ZLib.Streams.Stream_Access
          409  +                                 (Stream (File_Back)),
          410  +            Back_Compressed => False,
          411  +            Level           => Level,
          412  +            Strategy        => Strategy,
          413  +            Header          => Header);
          414  +
          415  +         Stamp;
          416  +         Copy_Streams
          417  +           (Source => File_Z,
          418  +            Target => Stream (File_Out).all);
          419  +
          420  +         Print_Statistic ("Read compress",
          421  +                          ZLib.Streams.Read_Total_Out (File_Z));
          422  +
          423  +         ZLib.Streams.Close (File_Z);
          424  +
          425  +         Close (File_Out);
          426  +         Close (File_Back);
          427  +
          428  +         --  Decompress to decompression stream.
          429  +
          430  +         Open   (File_In,   In_File, Z_File_Name);
          431  +         Create (File_Back, Out_File, Out_File_Name);
          432  +
          433  +         ZLib.Streams.Create
          434  +           (Stream          => File_Z,
          435  +            Mode            => ZLib.Streams.Out_Stream,
          436  +            Back            => ZLib.Streams.Stream_Access
          437  +                                 (Stream (File_Back)),
          438  +            Back_Compressed => False,
          439  +            Header          => Header);
          440  +
          441  +         Stamp;
          442  +
          443  +         Copy_Streams
          444  +           (Source => Stream (File_In).all,
          445  +            Target => File_Z);
          446  +
          447  +         Print_Statistic ("Write decompress",
          448  +                          ZLib.Streams.Write_Total_Out (File_Z));
          449  +
          450  +         ZLib.Streams.Close (File_Z);
          451  +         Close (File_In);
          452  +         Close (File_Back);
          453  +
          454  +         Compare_Files (In_File_Name, Out_File_Name);
          455  +      end loop;
          456  +
          457  +      Ada.Text_IO.Put_Line (Count'Image (File_Size) & " Ok.");
          458  +
          459  +      exit when not Continuous;
          460  +
          461  +      File_Size := File_Size + 1;
          462  +   end loop;
          463  +end Test;

Added compat/zlib/contrib/ada/zlib-streams.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: zlib-streams.adb,v 1.10 2004/05/31 10:53:40 vagul Exp $
           10  +
           11  +with Ada.Unchecked_Deallocation;
           12  +
           13  +package body ZLib.Streams is
           14  +
           15  +   -----------
           16  +   -- Close --
           17  +   -----------
           18  +
           19  +   procedure Close (Stream : in out Stream_Type) is
           20  +      procedure Free is new Ada.Unchecked_Deallocation
           21  +         (Stream_Element_Array, Buffer_Access);
           22  +   begin
           23  +      if Stream.Mode = Out_Stream or Stream.Mode = Duplex then
           24  +         --  We should flush the data written by the writer.
           25  +
           26  +         Flush (Stream, Finish);
           27  +
           28  +         Close (Stream.Writer);
           29  +      end if;
           30  +
           31  +      if Stream.Mode = In_Stream or Stream.Mode = Duplex then
           32  +         Close (Stream.Reader);
           33  +         Free (Stream.Buffer);
           34  +      end if;
           35  +   end Close;
           36  +
           37  +   ------------
           38  +   -- Create --
           39  +   ------------
           40  +
           41  +   procedure Create
           42  +     (Stream            :    out Stream_Type;
           43  +      Mode              : in     Stream_Mode;
           44  +      Back              : in     Stream_Access;
           45  +      Back_Compressed   : in     Boolean;
           46  +      Level             : in     Compression_Level := Default_Compression;
           47  +      Strategy          : in     Strategy_Type     := Default_Strategy;
           48  +      Header            : in     Header_Type       := Default;
           49  +      Read_Buffer_Size  : in     Ada.Streams.Stream_Element_Offset
           50  +                                    := Default_Buffer_Size;
           51  +      Write_Buffer_Size : in     Ada.Streams.Stream_Element_Offset
           52  +                                    := Default_Buffer_Size)
           53  +   is
           54  +
           55  +      subtype Buffer_Subtype is Stream_Element_Array (1 .. Read_Buffer_Size);
           56  +
           57  +      procedure Init_Filter
           58  +         (Filter   : in out Filter_Type;
           59  +          Compress : in     Boolean);
           60  +
           61  +      -----------------
           62  +      -- Init_Filter --
           63  +      -----------------
           64  +
           65  +      procedure Init_Filter
           66  +         (Filter   : in out Filter_Type;
           67  +          Compress : in     Boolean) is
           68  +      begin
           69  +         if Compress then
           70  +            Deflate_Init
           71  +              (Filter, Level, Strategy, Header => Header);
           72  +         else
           73  +            Inflate_Init (Filter, Header => Header);
           74  +         end if;
           75  +      end Init_Filter;
           76  +
           77  +   begin
           78  +      Stream.Back := Back;
           79  +      Stream.Mode := Mode;
           80  +
           81  +      if Mode = Out_Stream or Mode = Duplex then
           82  +         Init_Filter (Stream.Writer, Back_Compressed);
           83  +         Stream.Buffer_Size := Write_Buffer_Size;
           84  +      else
           85  +         Stream.Buffer_Size := 0;
           86  +      end if;
           87  +
           88  +      if Mode = In_Stream or Mode = Duplex then
           89  +         Init_Filter (Stream.Reader, not Back_Compressed);
           90  +
           91  +         Stream.Buffer     := new Buffer_Subtype;
           92  +         Stream.Rest_First := Stream.Buffer'Last + 1;
           93  +         Stream.Rest_Last  := Stream.Buffer'Last;
           94  +      end if;
           95  +   end Create;
           96  +
           97  +   -----------
           98  +   -- Flush --
           99  +   -----------
          100  +
          101  +   procedure Flush
          102  +     (Stream : in out Stream_Type;
          103  +      Mode   : in     Flush_Mode := Sync_Flush)
          104  +   is
          105  +      Buffer : Stream_Element_Array (1 .. Stream.Buffer_Size);
          106  +      Last   : Stream_Element_Offset;
          107  +   begin
          108  +      loop
          109  +         Flush (Stream.Writer, Buffer, Last, Mode);
          110  +
          111  +         Ada.Streams.Write (Stream.Back.all, Buffer (1 .. Last));
          112  +
          113  +         exit when Last < Buffer'Last;
          114  +      end loop;
          115  +   end Flush;
          116  +
          117  +   -------------
          118  +   -- Is_Open --
          119  +   -------------
          120  +
          121  +   function Is_Open (Stream : Stream_Type) return Boolean is
          122  +   begin
          123  +      return Is_Open (Stream.Reader) or else Is_Open (Stream.Writer);
          124  +   end Is_Open;
          125  +
          126  +   ----------
          127  +   -- Read --
          128  +   ----------
          129  +
          130  +   procedure Read
          131  +     (Stream : in out Stream_Type;
          132  +      Item   :    out Stream_Element_Array;
          133  +      Last   :    out Stream_Element_Offset)
          134  +   is
          135  +
          136  +      procedure Read
          137  +        (Item : out Stream_Element_Array;
          138  +         Last : out Stream_Element_Offset);
          139  +
          140  +      ----------
          141  +      -- Read --
          142  +      ----------
          143  +
          144  +      procedure Read
          145  +        (Item : out Stream_Element_Array;
          146  +         Last : out Stream_Element_Offset) is
          147  +      begin
          148  +         Ada.Streams.Read (Stream.Back.all, Item, Last);
          149  +      end Read;
          150  +
          151  +      procedure Read is new ZLib.Read
          152  +         (Read       => Read,
          153  +          Buffer     => Stream.Buffer.all,
          154  +          Rest_First => Stream.Rest_First,
          155  +          Rest_Last  => Stream.Rest_Last);
          156  +
          157  +   begin
          158  +      Read (Stream.Reader, Item, Last);
          159  +   end Read;
          160  +
          161  +   -------------------
          162  +   -- Read_Total_In --
          163  +   -------------------
          164  +
          165  +   function Read_Total_In (Stream : in Stream_Type) return Count is
          166  +   begin
          167  +      return Total_In (Stream.Reader);
          168  +   end Read_Total_In;
          169  +
          170  +   --------------------
          171  +   -- Read_Total_Out --
          172  +   --------------------
          173  +
          174  +   function Read_Total_Out (Stream : in Stream_Type) return Count is
          175  +   begin
          176  +      return Total_Out (Stream.Reader);
          177  +   end Read_Total_Out;
          178  +
          179  +   -----------
          180  +   -- Write --
          181  +   -----------
          182  +
          183  +   procedure Write
          184  +     (Stream : in out Stream_Type;
          185  +      Item   : in     Stream_Element_Array)
          186  +   is
          187  +
          188  +      procedure Write (Item : in Stream_Element_Array);
          189  +
          190  +      -----------
          191  +      -- Write --
          192  +      -----------
          193  +
          194  +      procedure Write (Item : in Stream_Element_Array) is
          195  +      begin
          196  +         Ada.Streams.Write (Stream.Back.all, Item);
          197  +      end Write;
          198  +
          199  +      procedure Write is new ZLib.Write
          200  +         (Write       => Write,
          201  +          Buffer_Size => Stream.Buffer_Size);
          202  +
          203  +   begin
          204  +      Write (Stream.Writer, Item, No_Flush);
          205  +   end Write;
          206  +
          207  +   --------------------
          208  +   -- Write_Total_In --
          209  +   --------------------
          210  +
          211  +   function Write_Total_In (Stream : in Stream_Type) return Count is
          212  +   begin
          213  +      return Total_In (Stream.Writer);
          214  +   end Write_Total_In;
          215  +
          216  +   ---------------------
          217  +   -- Write_Total_Out --
          218  +   ---------------------
          219  +
          220  +   function Write_Total_Out (Stream : in Stream_Type) return Count is
          221  +   begin
          222  +      return Total_Out (Stream.Writer);
          223  +   end Write_Total_Out;
          224  +
          225  +end ZLib.Streams;

Added compat/zlib/contrib/ada/zlib-streams.ads.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: zlib-streams.ads,v 1.12 2004/05/31 10:53:40 vagul Exp $
           10  +
           11  +package ZLib.Streams is
           12  +
           13  +   type Stream_Mode is (In_Stream, Out_Stream, Duplex);
           14  +
           15  +   type Stream_Access is access all Ada.Streams.Root_Stream_Type'Class;
           16  +
           17  +   type Stream_Type is
           18  +      new Ada.Streams.Root_Stream_Type with private;
           19  +
           20  +   procedure Read
           21  +     (Stream : in out Stream_Type;
           22  +      Item   :    out Ada.Streams.Stream_Element_Array;
           23  +      Last   :    out Ada.Streams.Stream_Element_Offset);
           24  +
           25  +   procedure Write
           26  +     (Stream : in out Stream_Type;
           27  +      Item   : in     Ada.Streams.Stream_Element_Array);
           28  +
           29  +   procedure Flush
           30  +     (Stream : in out Stream_Type;
           31  +      Mode   : in     Flush_Mode := Sync_Flush);
           32  +   --  Flush the written data to the back stream,
           33  +   --  all data placed to the compressor is flushing to the Back stream.
           34  +   --  Should not be used untill necessary, becouse it is decreasing
           35  +   --  compression.
           36  +
           37  +   function Read_Total_In (Stream : in Stream_Type) return Count;
           38  +   pragma Inline (Read_Total_In);
           39  +   --  Return total number of bytes read from back stream so far.
           40  +
           41  +   function Read_Total_Out (Stream : in Stream_Type) return Count;
           42  +   pragma Inline (Read_Total_Out);
           43  +   --  Return total number of bytes read so far.
           44  +
           45  +   function Write_Total_In (Stream : in Stream_Type) return Count;
           46  +   pragma Inline (Write_Total_In);
           47  +   --  Return total number of bytes written so far.
           48  +
           49  +   function Write_Total_Out (Stream : in Stream_Type) return Count;
           50  +   pragma Inline (Write_Total_Out);
           51  +   --  Return total number of bytes written to the back stream.
           52  +
           53  +   procedure Create
           54  +     (Stream            :    out Stream_Type;
           55  +      Mode              : in     Stream_Mode;
           56  +      Back              : in     Stream_Access;
           57  +      Back_Compressed   : in     Boolean;
           58  +      Level             : in     Compression_Level := Default_Compression;
           59  +      Strategy          : in     Strategy_Type     := Default_Strategy;
           60  +      Header            : in     Header_Type       := Default;
           61  +      Read_Buffer_Size  : in     Ada.Streams.Stream_Element_Offset
           62  +                                    := Default_Buffer_Size;
           63  +      Write_Buffer_Size : in     Ada.Streams.Stream_Element_Offset
           64  +                                    := Default_Buffer_Size);
           65  +   --  Create the Comression/Decompression stream.
           66  +   --  If mode is In_Stream then Write operation is disabled.
           67  +   --  If mode is Out_Stream then Read operation is disabled.
           68  +
           69  +   --  If Back_Compressed is true then
           70  +   --  Data written to the Stream is compressing to the Back stream
           71  +   --  and data read from the Stream is decompressed data from the Back stream.
           72  +
           73  +   --  If Back_Compressed is false then
           74  +   --  Data written to the Stream is decompressing to the Back stream
           75  +   --  and data read from the Stream is compressed data from the Back stream.
           76  +
           77  +   --  !!! When the Need_Header is False ZLib-Ada is using undocumented
           78  +   --  ZLib 1.1.4 functionality to do not create/wait for ZLib headers.
           79  +
           80  +   function Is_Open (Stream : Stream_Type) return Boolean;
           81  +
           82  +   procedure Close (Stream : in out Stream_Type);
           83  +
           84  +private
           85  +
           86  +   use Ada.Streams;
           87  +
           88  +   type Buffer_Access is access all Stream_Element_Array;
           89  +
           90  +   type Stream_Type
           91  +     is new Root_Stream_Type with
           92  +   record
           93  +      Mode       : Stream_Mode;
           94  +
           95  +      Buffer     : Buffer_Access;
           96  +      Rest_First : Stream_Element_Offset;
           97  +      Rest_Last  : Stream_Element_Offset;
           98  +      --  Buffer for Read operation.
           99  +      --  We need to have this buffer in the record
          100  +      --  becouse not all read data from back stream
          101  +      --  could be processed during the read operation.
          102  +
          103  +      Buffer_Size : Stream_Element_Offset;
          104  +      --  Buffer size for write operation.
          105  +      --  We do not need to have this buffer
          106  +      --  in the record becouse all data could be
          107  +      --  processed in the write operation.
          108  +
          109  +      Back       : Stream_Access;
          110  +      Reader     : Filter_Type;
          111  +      Writer     : Filter_Type;
          112  +   end record;
          113  +
          114  +end ZLib.Streams;

Added compat/zlib/contrib/ada/zlib-thin.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: zlib-thin.adb,v 1.8 2003/12/14 18:27:31 vagul Exp $
           10  +
           11  +package body ZLib.Thin is
           12  +
           13  +   ZLIB_VERSION  : constant Chars_Ptr := zlibVersion;
           14  +
           15  +   Z_Stream_Size : constant Int := Z_Stream'Size / System.Storage_Unit;
           16  +
           17  +   --------------
           18  +   -- Avail_In --
           19  +   --------------
           20  +
           21  +   function Avail_In (Strm : in Z_Stream) return UInt is
           22  +   begin
           23  +      return Strm.Avail_In;
           24  +   end Avail_In;
           25  +
           26  +   ---------------
           27  +   -- Avail_Out --
           28  +   ---------------
           29  +
           30  +   function Avail_Out (Strm : in Z_Stream) return UInt is
           31  +   begin
           32  +      return Strm.Avail_Out;
           33  +   end Avail_Out;
           34  +
           35  +   ------------------
           36  +   -- Deflate_Init --
           37  +   ------------------
           38  +
           39  +   function Deflate_Init
           40  +     (strm       : Z_Streamp;
           41  +      level      : Int;
           42  +      method     : Int;
           43  +      windowBits : Int;
           44  +      memLevel   : Int;
           45  +      strategy   : Int)
           46  +      return       Int is
           47  +   begin
           48  +      return deflateInit2
           49  +               (strm,
           50  +                level,
           51  +                method,
           52  +                windowBits,
           53  +                memLevel,
           54  +                strategy,
           55  +                ZLIB_VERSION,
           56  +                Z_Stream_Size);
           57  +   end Deflate_Init;
           58  +
           59  +   ------------------
           60  +   -- Inflate_Init --
           61  +   ------------------
           62  +
           63  +   function Inflate_Init (strm : Z_Streamp; windowBits : Int) return Int is
           64  +   begin
           65  +      return inflateInit2 (strm, windowBits, ZLIB_VERSION, Z_Stream_Size);
           66  +   end Inflate_Init;
           67  +
           68  +   ------------------------
           69  +   -- Last_Error_Message --
           70  +   ------------------------
           71  +
           72  +   function Last_Error_Message (Strm : in Z_Stream) return String is
           73  +      use Interfaces.C.Strings;
           74  +   begin
           75  +      if Strm.msg = Null_Ptr then
           76  +         return "";
           77  +      else
           78  +         return Value (Strm.msg);
           79  +      end if;
           80  +   end Last_Error_Message;
           81  +
           82  +   ------------
           83  +   -- Set_In --
           84  +   ------------
           85  +
           86  +   procedure Set_In
           87  +     (Strm   : in out Z_Stream;
           88  +      Buffer : in     Voidp;
           89  +      Size   : in     UInt) is
           90  +   begin
           91  +      Strm.Next_In  := Buffer;
           92  +      Strm.Avail_In := Size;
           93  +   end Set_In;
           94  +
           95  +   ------------------
           96  +   -- Set_Mem_Func --
           97  +   ------------------
           98  +
           99  +   procedure Set_Mem_Func
          100  +     (Strm   : in out Z_Stream;
          101  +      Opaque : in     Voidp;
          102  +      Alloc  : in     alloc_func;
          103  +      Free   : in     free_func) is
          104  +   begin
          105  +      Strm.opaque := Opaque;
          106  +      Strm.zalloc := Alloc;
          107  +      Strm.zfree  := Free;
          108  +   end Set_Mem_Func;
          109  +
          110  +   -------------
          111  +   -- Set_Out --
          112  +   -------------
          113  +
          114  +   procedure Set_Out
          115  +     (Strm   : in out Z_Stream;
          116  +      Buffer : in     Voidp;
          117  +      Size   : in     UInt) is
          118  +   begin
          119  +      Strm.Next_Out  := Buffer;
          120  +      Strm.Avail_Out := Size;
          121  +   end Set_Out;
          122  +
          123  +   --------------
          124  +   -- Total_In --
          125  +   --------------
          126  +
          127  +   function Total_In (Strm : in Z_Stream) return ULong is
          128  +   begin
          129  +      return Strm.Total_In;
          130  +   end Total_In;
          131  +
          132  +   ---------------
          133  +   -- Total_Out --
          134  +   ---------------
          135  +
          136  +   function Total_Out (Strm : in Z_Stream) return ULong is
          137  +   begin
          138  +      return Strm.Total_Out;
          139  +   end Total_Out;
          140  +
          141  +end ZLib.Thin;

Added compat/zlib/contrib/ada/zlib-thin.ads.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: zlib-thin.ads,v 1.11 2004/07/23 06:33:11 vagul Exp $
           10  +
           11  +with Interfaces.C.Strings;
           12  +
           13  +with System;
           14  +
           15  +private package ZLib.Thin is
           16  +
           17  +   --  From zconf.h
           18  +
           19  +   MAX_MEM_LEVEL : constant := 9;         --  zconf.h:105
           20  +                                          --  zconf.h:105
           21  +   MAX_WBITS : constant := 15;      --  zconf.h:115
           22  +                                    --  32K LZ77 window
           23  +                                    --  zconf.h:115
           24  +   SEEK_SET : constant := 8#0000#;  --  zconf.h:244
           25  +                                    --  Seek from beginning of file.
           26  +                                    --  zconf.h:244
           27  +   SEEK_CUR : constant := 1;        --  zconf.h:245
           28  +                                    --  Seek from current position.
           29  +                                    --  zconf.h:245
           30  +   SEEK_END : constant := 2;        --  zconf.h:246
           31  +                                    --  Set file pointer to EOF plus "offset"
           32  +                                    --  zconf.h:246
           33  +
           34  +   type Byte is new Interfaces.C.unsigned_char; --  8 bits
           35  +                                                --  zconf.h:214
           36  +   type UInt is new Interfaces.C.unsigned;      --  16 bits or more
           37  +                                                --  zconf.h:216
           38  +   type Int is new Interfaces.C.int;
           39  +
           40  +   type ULong is new Interfaces.C.unsigned_long;     --  32 bits or more
           41  +                                                     --  zconf.h:217
           42  +   subtype Chars_Ptr is Interfaces.C.Strings.chars_ptr;
           43  +
           44  +   type ULong_Access is access ULong;
           45  +   type Int_Access is access Int;
           46  +
           47  +   subtype Voidp is System.Address;            --  zconf.h:232
           48  +
           49  +   subtype Byte_Access is Voidp;
           50  +
           51  +   Nul : constant Voidp := System.Null_Address;
           52  +   --  end from zconf
           53  +
           54  +   Z_NO_FLUSH : constant := 8#0000#;   --  zlib.h:125
           55  +                                       --  zlib.h:125
           56  +   Z_PARTIAL_FLUSH : constant := 1;       --  zlib.h:126
           57  +                                          --  will be removed, use
           58  +                                          --  Z_SYNC_FLUSH instead
           59  +                                          --  zlib.h:126
           60  +   Z_SYNC_FLUSH : constant := 2;       --  zlib.h:127
           61  +                                       --  zlib.h:127
           62  +   Z_FULL_FLUSH : constant := 3;       --  zlib.h:128
           63  +                                       --  zlib.h:128
           64  +   Z_FINISH : constant := 4;        --  zlib.h:129
           65  +                                    --  zlib.h:129
           66  +   Z_OK : constant := 8#0000#;   --  zlib.h:132
           67  +                                 --  zlib.h:132
           68  +   Z_STREAM_END : constant := 1;       --  zlib.h:133
           69  +                                       --  zlib.h:133
           70  +   Z_NEED_DICT : constant := 2;        --  zlib.h:134
           71  +                                       --  zlib.h:134
           72  +   Z_ERRNO : constant := -1;        --  zlib.h:135
           73  +                                    --  zlib.h:135
           74  +   Z_STREAM_ERROR : constant := -2;       --  zlib.h:136
           75  +                                          --  zlib.h:136
           76  +   Z_DATA_ERROR : constant := -3;      --  zlib.h:137
           77  +                                       --  zlib.h:137
           78  +   Z_MEM_ERROR : constant := -4;       --  zlib.h:138
           79  +                                       --  zlib.h:138
           80  +   Z_BUF_ERROR : constant := -5;       --  zlib.h:139
           81  +                                       --  zlib.h:139
           82  +   Z_VERSION_ERROR : constant := -6;      --  zlib.h:140
           83  +                                          --  zlib.h:140
           84  +   Z_NO_COMPRESSION : constant := 8#0000#;   --  zlib.h:145
           85  +                                             --  zlib.h:145
           86  +   Z_BEST_SPEED : constant := 1;       --  zlib.h:146
           87  +                                       --  zlib.h:146
           88  +   Z_BEST_COMPRESSION : constant := 9;       --  zlib.h:147
           89  +                                             --  zlib.h:147
           90  +   Z_DEFAULT_COMPRESSION : constant := -1;      --  zlib.h:148
           91  +                                                --  zlib.h:148
           92  +   Z_FILTERED : constant := 1;      --  zlib.h:151
           93  +                                    --  zlib.h:151
           94  +   Z_HUFFMAN_ONLY : constant := 2;        --  zlib.h:152
           95  +                                          --  zlib.h:152
           96  +   Z_DEFAULT_STRATEGY : constant := 8#0000#; --  zlib.h:153
           97  +                                             --  zlib.h:153
           98  +   Z_BINARY : constant := 8#0000#;  --  zlib.h:156
           99  +                                    --  zlib.h:156
          100  +   Z_ASCII : constant := 1;      --  zlib.h:157
          101  +                                 --  zlib.h:157
          102  +   Z_UNKNOWN : constant := 2;       --  zlib.h:158
          103  +                                    --  zlib.h:158
          104  +   Z_DEFLATED : constant := 8;      --  zlib.h:161
          105  +                                    --  zlib.h:161
          106  +   Z_NULL : constant := 8#0000#; --  zlib.h:164
          107  +                                 --  for initializing zalloc, zfree, opaque
          108  +                                 --  zlib.h:164
          109  +   type gzFile is new Voidp;                  --  zlib.h:646
          110  +
          111  +   type Z_Stream is private;
          112  +
          113  +   type Z_Streamp is access all Z_Stream;     --  zlib.h:89
          114  +
          115  +   type alloc_func is access function
          116  +     (Opaque : Voidp;
          117  +      Items  : UInt;
          118  +      Size   : UInt)
          119  +      return Voidp; --  zlib.h:63
          120  +
          121  +   type free_func is access procedure (opaque : Voidp; address : Voidp);
          122  +
          123  +   function zlibVersion return Chars_Ptr;
          124  +
          125  +   function Deflate (strm : Z_Streamp; flush : Int) return Int;
          126  +
          127  +   function DeflateEnd (strm : Z_Streamp) return Int;
          128  +
          129  +   function Inflate (strm : Z_Streamp; flush : Int) return Int;
          130  +
          131  +   function InflateEnd (strm : Z_Streamp) return Int;
          132  +
          133  +   function deflateSetDictionary
          134  +     (strm       : Z_Streamp;
          135  +      dictionary : Byte_Access;
          136  +      dictLength : UInt)
          137  +      return       Int;
          138  +
          139  +   function deflateCopy (dest : Z_Streamp; source : Z_Streamp) return Int;
          140  +   --  zlib.h:478
          141  +
          142  +   function deflateReset (strm : Z_Streamp) return Int; -- zlib.h:495
          143  +
          144  +   function deflateParams
          145  +     (strm     : Z_Streamp;
          146  +      level    : Int;
          147  +      strategy : Int)
          148  +      return     Int;       -- zlib.h:506
          149  +
          150  +   function inflateSetDictionary
          151  +     (strm       : Z_Streamp;
          152  +      dictionary : Byte_Access;
          153  +      dictLength : UInt)
          154  +      return       Int; --  zlib.h:548
          155  +
          156  +   function inflateSync (strm : Z_Streamp) return Int;  --  zlib.h:565
          157  +
          158  +   function inflateReset (strm : Z_Streamp) return Int; --  zlib.h:580
          159  +
          160  +   function compress
          161  +     (dest      : Byte_Access;
          162  +      destLen   : ULong_Access;
          163  +      source    : Byte_Access;
          164  +      sourceLen : ULong)
          165  +      return      Int;           -- zlib.h:601
          166  +
          167  +   function compress2
          168  +     (dest      : Byte_Access;
          169  +      destLen   : ULong_Access;
          170  +      source    : Byte_Access;
          171  +      sourceLen : ULong;
          172  +      level     : Int)
          173  +      return      Int;          -- zlib.h:615
          174  +
          175  +   function uncompress
          176  +     (dest      : Byte_Access;
          177  +      destLen   : ULong_Access;
          178  +      source    : Byte_Access;
          179  +      sourceLen : ULong)
          180  +      return      Int;
          181  +
          182  +   function gzopen (path : Chars_Ptr; mode : Chars_Ptr) return gzFile;
          183  +
          184  +   function gzdopen (fd : Int; mode : Chars_Ptr) return gzFile;
          185  +
          186  +   function gzsetparams
          187  +     (file     : gzFile;
          188  +      level    : Int;
          189  +      strategy : Int)
          190  +      return     Int;
          191  +
          192  +   function gzread
          193  +     (file : gzFile;
          194  +      buf  : Voidp;
          195  +      len  : UInt)
          196  +      return Int;
          197  +
          198  +   function gzwrite
          199  +     (file : in gzFile;
          200  +      buf  : in Voidp;
          201  +      len  : in UInt)
          202  +      return Int;
          203  +
          204  +   function gzprintf (file : in gzFile; format : in Chars_Ptr) return Int;
          205  +
          206  +   function gzputs (file : in gzFile; s : in Chars_Ptr) return Int;
          207  +
          208  +   function gzgets
          209  +     (file : gzFile;
          210  +      buf  : Chars_Ptr;
          211  +      len  : Int)
          212  +      return Chars_Ptr;
          213  +
          214  +   function gzputc (file : gzFile; char : Int) return Int;
          215  +
          216  +   function gzgetc (file : gzFile) return Int;
          217  +
          218  +   function gzflush (file : gzFile; flush : Int) return Int;
          219  +
          220  +   function gzseek
          221  +     (file   : gzFile;
          222  +      offset : Int;
          223  +      whence : Int)
          224  +      return   Int;
          225  +
          226  +   function gzrewind (file : gzFile) return Int;
          227  +
          228  +   function gztell (file : gzFile) return Int;
          229  +
          230  +   function gzeof (file : gzFile) return Int;
          231  +
          232  +   function gzclose (file : gzFile) return Int;
          233  +
          234  +   function gzerror (file : gzFile; errnum : Int_Access) return Chars_Ptr;
          235  +
          236  +   function adler32
          237  +     (adler : ULong;
          238  +      buf   : Byte_Access;
          239  +      len   : UInt)
          240  +      return  ULong;
          241  +
          242  +   function crc32
          243  +     (crc  : ULong;
          244  +      buf  : Byte_Access;
          245  +      len  : UInt)
          246  +      return ULong;
          247  +
          248  +   function deflateInit
          249  +     (strm        : Z_Streamp;
          250  +      level       : Int;
          251  +      version     : Chars_Ptr;
          252  +      stream_size : Int)
          253  +      return        Int;
          254  +
          255  +   function deflateInit2
          256  +     (strm        : Z_Streamp;
          257  +      level       : Int;
          258  +      method      : Int;
          259  +      windowBits  : Int;
          260  +      memLevel    : Int;
          261  +      strategy    : Int;
          262  +      version     : Chars_Ptr;
          263  +      stream_size : Int)
          264  +      return        Int;
          265  +
          266  +   function Deflate_Init
          267  +     (strm       : Z_Streamp;
          268  +      level      : Int;
          269  +      method     : Int;
          270  +      windowBits : Int;
          271  +      memLevel   : Int;
          272  +      strategy   : Int)
          273  +      return       Int;
          274  +   pragma Inline (Deflate_Init);
          275  +
          276  +   function inflateInit
          277  +     (strm        : Z_Streamp;
          278  +      version     : Chars_Ptr;
          279  +      stream_size : Int)
          280  +      return        Int;
          281  +
          282  +   function inflateInit2
          283  +     (strm        : in Z_Streamp;
          284  +      windowBits  : in Int;
          285  +      version     : in Chars_Ptr;
          286  +      stream_size : in Int)
          287  +      return      Int;
          288  +
          289  +   function inflateBackInit
          290  +     (strm        : in Z_Streamp;
          291  +      windowBits  : in Int;
          292  +      window      : in Byte_Access;
          293  +      version     : in Chars_Ptr;
          294  +      stream_size : in Int)
          295  +      return      Int;
          296  +   --  Size of window have to be 2**windowBits.
          297  +
          298  +   function Inflate_Init (strm : Z_Streamp; windowBits : Int) return Int;
          299  +   pragma Inline (Inflate_Init);
          300  +
          301  +   function zError (err : Int) return Chars_Ptr;
          302  +
          303  +   function inflateSyncPoint (z : Z_Streamp) return Int;
          304  +
          305  +   function get_crc_table return ULong_Access;
          306  +
          307  +   --  Interface to the available fields of the z_stream structure.
          308  +   --  The application must update next_in and avail_in when avail_in has
          309  +   --  dropped to zero. It must update next_out and avail_out when avail_out
          310  +   --  has dropped to zero. The application must initialize zalloc, zfree and
          311  +   --  opaque before calling the init function.
          312  +
          313  +   procedure Set_In
          314  +     (Strm   : in out Z_Stream;
          315  +      Buffer : in Voidp;
          316  +      Size   : in UInt);
          317  +   pragma Inline (Set_In);
          318  +
          319  +   procedure Set_Out
          320  +     (Strm   : in out Z_Stream;
          321  +      Buffer : in Voidp;
          322  +      Size   : in UInt);
          323  +   pragma Inline (Set_Out);
          324  +
          325  +   procedure Set_Mem_Func
          326  +     (Strm   : in out Z_Stream;
          327  +      Opaque : in Voidp;
          328  +      Alloc  : in alloc_func;
          329  +      Free   : in free_func);
          330  +   pragma Inline (Set_Mem_Func);
          331  +
          332  +   function Last_Error_Message (Strm : in Z_Stream) return String;
          333  +   pragma Inline (Last_Error_Message);
          334  +
          335  +   function Avail_Out (Strm : in Z_Stream) return UInt;
          336  +   pragma Inline (Avail_Out);
          337  +
          338  +   function Avail_In (Strm : in Z_Stream) return UInt;
          339  +   pragma Inline (Avail_In);
          340  +
          341  +   function Total_In (Strm : in Z_Stream) return ULong;
          342  +   pragma Inline (Total_In);
          343  +
          344  +   function Total_Out (Strm : in Z_Stream) return ULong;
          345  +   pragma Inline (Total_Out);
          346  +
          347  +   function inflateCopy
          348  +     (dest   : in Z_Streamp;
          349  +      Source : in Z_Streamp)
          350  +      return Int;
          351  +
          352  +   function compressBound (Source_Len : in ULong) return ULong;
          353  +
          354  +   function deflateBound
          355  +     (Strm       : in Z_Streamp;
          356  +      Source_Len : in ULong)
          357  +      return     ULong;
          358  +
          359  +   function gzungetc (C : in Int; File : in  gzFile) return Int;
          360  +
          361  +   function zlibCompileFlags return ULong;
          362  +
          363  +private
          364  +
          365  +   type Z_Stream is record            -- zlib.h:68
          366  +      Next_In   : Voidp      := Nul;  -- next input byte
          367  +      Avail_In  : UInt       := 0;    -- number of bytes available at next_in
          368  +      Total_In  : ULong      := 0;    -- total nb of input bytes read so far
          369  +      Next_Out  : Voidp      := Nul;  -- next output byte should be put there
          370  +      Avail_Out : UInt       := 0;    -- remaining free space at next_out
          371  +      Total_Out : ULong      := 0;    -- total nb of bytes output so far
          372  +      msg       : Chars_Ptr;          -- last error message, NULL if no error
          373  +      state     : Voidp;              -- not visible by applications
          374  +      zalloc    : alloc_func := null; -- used to allocate the internal state
          375  +      zfree     : free_func  := null; -- used to free the internal state
          376  +      opaque    : Voidp;              -- private data object passed to
          377  +                                      --  zalloc and zfree
          378  +      data_type : Int;                -- best guess about the data type:
          379  +                                      --  ascii or binary
          380  +      adler     : ULong;              -- adler32 value of the uncompressed
          381  +                                      --  data
          382  +      reserved  : ULong;              -- reserved for future use
          383  +   end record;
          384  +
          385  +   pragma Convention (C, Z_Stream);
          386  +
          387  +   pragma Import (C, zlibVersion, "zlibVersion");
          388  +   pragma Import (C, Deflate, "deflate");
          389  +   pragma Import (C, DeflateEnd, "deflateEnd");
          390  +   pragma Import (C, Inflate, "inflate");
          391  +   pragma Import (C, InflateEnd, "inflateEnd");
          392  +   pragma Import (C, deflateSetDictionary, "deflateSetDictionary");
          393  +   pragma Import (C, deflateCopy, "deflateCopy");
          394  +   pragma Import (C, deflateReset, "deflateReset");
          395  +   pragma Import (C, deflateParams, "deflateParams");
          396  +   pragma Import (C, inflateSetDictionary, "inflateSetDictionary");
          397  +   pragma Import (C, inflateSync, "inflateSync");
          398  +   pragma Import (C, inflateReset, "inflateReset");
          399  +   pragma Import (C, compress, "compress");
          400  +   pragma Import (C, compress2, "compress2");
          401  +   pragma Import (C, uncompress, "uncompress");
          402  +   pragma Import (C, gzopen, "gzopen");
          403  +   pragma Import (C, gzdopen, "gzdopen");
          404  +   pragma Import (C, gzsetparams, "gzsetparams");
          405  +   pragma Import (C, gzread, "gzread");
          406  +   pragma Import (C, gzwrite, "gzwrite");
          407  +   pragma Import (C, gzprintf, "gzprintf");
          408  +   pragma Import (C, gzputs, "gzputs");
          409  +   pragma Import (C, gzgets, "gzgets");
          410  +   pragma Import (C, gzputc, "gzputc");
          411  +   pragma Import (C, gzgetc, "gzgetc");
          412  +   pragma Import (C, gzflush, "gzflush");
          413  +   pragma Import (C, gzseek, "gzseek");
          414  +   pragma Import (C, gzrewind, "gzrewind");
          415  +   pragma Import (C, gztell, "gztell");
          416  +   pragma Import (C, gzeof, "gzeof");
          417  +   pragma Import (C, gzclose, "gzclose");
          418  +   pragma Import (C, gzerror, "gzerror");
          419  +   pragma Import (C, adler32, "adler32");
          420  +   pragma Import (C, crc32, "crc32");
          421  +   pragma Import (C, deflateInit, "deflateInit_");
          422  +   pragma Import (C, inflateInit, "inflateInit_");
          423  +   pragma Import (C, deflateInit2, "deflateInit2_");
          424  +   pragma Import (C, inflateInit2, "inflateInit2_");
          425  +   pragma Import (C, zError, "zError");
          426  +   pragma Import (C, inflateSyncPoint, "inflateSyncPoint");
          427  +   pragma Import (C, get_crc_table, "get_crc_table");
          428  +
          429  +   --  since zlib 1.2.0:
          430  +
          431  +   pragma Import (C, inflateCopy, "inflateCopy");
          432  +   pragma Import (C, compressBound, "compressBound");
          433  +   pragma Import (C, deflateBound, "deflateBound");
          434  +   pragma Import (C, gzungetc, "gzungetc");
          435  +   pragma Import (C, zlibCompileFlags, "zlibCompileFlags");
          436  +
          437  +   pragma Import (C, inflateBackInit, "inflateBackInit_");
          438  +
          439  +   --  I stopped binding the inflateBack routines, becouse realize that
          440  +   --  it does not support zlib and gzip headers for now, and have no
          441  +   --  symmetric deflateBack routines.
          442  +   --  ZLib-Ada is symmetric regarding deflate/inflate data transformation
          443  +   --  and has a similar generic callback interface for the
          444  +   --  deflate/inflate transformation based on the regular Deflate/Inflate
          445  +   --  routines.
          446  +
          447  +   --  pragma Import (C, inflateBack, "inflateBack");
          448  +   --  pragma Import (C, inflateBackEnd, "inflateBackEnd");
          449  +
          450  +end ZLib.Thin;

Added compat/zlib/contrib/ada/zlib.adb.

            1  +----------------------------------------------------------------
            2  +--  ZLib for Ada thick binding.                               --
            3  +--                                                            --
            4  +--  Copyright (C) 2002-2004 Dmitriy Anisimkov                 --
            5  +--                                                            --
            6  +--  Open source license information is in the zlib.ads file.  --
            7  +----------------------------------------------------------------
            8  +
            9  +--  $Id: zlib.adb,v 1.31 2004/09/06 06:53:19 vagul Exp $
           10  +
           11  +with Ada.Exceptions;
           12  +with Ada.Unchecked_Conversion;
           13  +with Ada.Unchecked_Deallocation;
           14  +
           15  +with Interfaces.C.Strings;
           16  +
           17  +with ZLib.Thin;
           18  +
           19  +package body ZLib is
           20  +
           21  +   use type Thin.Int;
           22  +
           23  +   type Z_Stream is new Thin.Z_Stream;
           24  +
           25  +   type Return_Code_Enum is
           26  +      (OK,
           27  +       STREAM_END,
           28  +       NEED_DICT,
           29  +       ERRNO,
           30  +       STREAM_ERROR,
           31  +       DATA_ERROR,
           32  +       MEM_ERROR,
           33  +       BUF_ERROR,
           34  +       VERSION_ERROR);
           35  +
           36  +   type Flate_Step_Function is access
           37  +     function (Strm : in Thin.Z_Streamp; Flush : in Thin.Int) return Thin.Int;
           38  +   pragma Convention (C, Flate_Step_Function);
           39  +
           40  +   type Flate_End_Function is access
           41  +      function (Ctrm : in Thin.Z_Streamp) return Thin.Int;
           42  +   pragma Convention (C, Flate_End_Function);
           43  +
           44  +   type Flate_Type is record
           45  +      Step : Flate_Step_Function;
           46  +      Done : Flate_End_Function;
           47  +   end record;
           48  +
           49  +   subtype Footer_Array is Stream_Element_Array (1 .. 8);
           50  +
           51  +   Simple_GZip_Header : constant Stream_Element_Array (1 .. 10)
           52  +     := (16#1f#, 16#8b#,                 --  Magic header
           53  +         16#08#,                         --  Z_DEFLATED
           54  +         16#00#,                         --  Flags
           55  +         16#00#, 16#00#, 16#00#, 16#00#, --  Time
           56  +         16#00#,                         --  XFlags
           57  +         16#03#                          --  OS code
           58  +        );
           59  +   --  The simplest gzip header is not for informational, but just for
           60  +   --  gzip format compatibility.
           61  +   --  Note that some code below is using assumption
           62  +   --  Simple_GZip_Header'Last > Footer_Array'Last, so do not make
           63  +   --  Simple_GZip_Header'Last <= Footer_Array'Last.
           64  +
           65  +   Return_Code : constant array (Thin.Int range <>) of Return_Code_Enum
           66  +     := (0 => OK,
           67  +         1 => STREAM_END,
           68  +         2 => NEED_DICT,
           69  +        -1 => ERRNO,
           70  +        -2 => STREAM_ERROR,
           71  +        -3 => DATA_ERROR,
           72  +        -4 => MEM_ERROR,
           73  +        -5 => BUF_ERROR,
           74  +        -6 => VERSION_ERROR);
           75  +
           76  +   Flate : constant array (Boolean) of Flate_Type
           77  +     := (True  => (Step => Thin.Deflate'Access,
           78  +                   Done => Thin.DeflateEnd'Access),
           79  +         False => (Step => Thin.Inflate'Access,
           80  +                   Done => Thin.InflateEnd'Access));
           81  +
           82  +   Flush_Finish : constant array (Boolean) of Flush_Mode
           83  +     := (True => Finish, False => No_Flush);
           84  +
           85  +   procedure Raise_Error (Stream : in Z_Stream);
           86  +   pragma Inline (Raise_Error);
           87  +
           88  +   procedure Raise_Error (Message : in String);
           89  +   pragma Inline (Raise_Error);
           90  +
           91  +   procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int);
           92  +
           93  +   procedure Free is new Ada.Unchecked_Deallocation
           94  +      (Z_Stream, Z_Stream_Access);
           95  +
           96  +   function To_Thin_Access is new Ada.Unchecked_Conversion
           97  +     (Z_Stream_Access, Thin.Z_Streamp);
           98  +
           99  +   procedure Translate_GZip
          100  +     (Filter    : in out Filter_Type;
          101  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          102  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          103  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          104  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          105  +      Flush     : in     Flush_Mode);
          106  +   --  Separate translate routine for make gzip header.
          107  +
          108  +   procedure Translate_Auto
          109  +     (Filter    : in out Filter_Type;
          110  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          111  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          112  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          113  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          114  +      Flush     : in     Flush_Mode);
          115  +   --  translate routine without additional headers.
          116  +
          117  +   -----------------
          118  +   -- Check_Error --
          119  +   -----------------
          120  +
          121  +   procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int) is
          122  +      use type Thin.Int;
          123  +   begin
          124  +      if Code /= Thin.Z_OK then
          125  +         Raise_Error
          126  +            (Return_Code_Enum'Image (Return_Code (Code))
          127  +              & ": " & Last_Error_Message (Stream));
          128  +      end if;
          129  +   end Check_Error;
          130  +
          131  +   -----------
          132  +   -- Close --
          133  +   -----------
          134  +
          135  +   procedure Close
          136  +     (Filter       : in out Filter_Type;
          137  +      Ignore_Error : in     Boolean := False)
          138  +   is
          139  +      Code : Thin.Int;
          140  +   begin
          141  +      if not Ignore_Error and then not Is_Open (Filter) then
          142  +         raise Status_Error;
          143  +      end if;
          144  +
          145  +      Code := Flate (Filter.Compression).Done (To_Thin_Access (Filter.Strm));
          146  +
          147  +      if Ignore_Error or else Code = Thin.Z_OK then
          148  +         Free (Filter.Strm);
          149  +      else
          150  +         declare
          151  +            Error_Message : constant String
          152  +              := Last_Error_Message (Filter.Strm.all);
          153  +         begin
          154  +            Free (Filter.Strm);
          155  +            Ada.Exceptions.Raise_Exception
          156  +               (ZLib_Error'Identity,
          157  +                Return_Code_Enum'Image (Return_Code (Code))
          158  +                  & ": " & Error_Message);
          159  +         end;
          160  +      end if;
          161  +   end Close;
          162  +
          163  +   -----------
          164  +   -- CRC32 --
          165  +   -----------
          166  +
          167  +   function CRC32
          168  +     (CRC  : in Unsigned_32;
          169  +      Data : in Ada.Streams.Stream_Element_Array)
          170  +      return Unsigned_32
          171  +   is
          172  +      use Thin;
          173  +   begin
          174  +      return Unsigned_32 (crc32 (ULong (CRC),
          175  +                                 Data'Address,
          176  +                                 Data'Length));
          177  +   end CRC32;
          178  +
          179  +   procedure CRC32
          180  +     (CRC  : in out Unsigned_32;
          181  +      Data : in     Ada.Streams.Stream_Element_Array) is
          182  +   begin
          183  +      CRC := CRC32 (CRC, Data);
          184  +   end CRC32;
          185  +
          186  +   ------------------
          187  +   -- Deflate_Init --
          188  +   ------------------
          189  +
          190  +   procedure Deflate_Init
          191  +     (Filter       : in out Filter_Type;
          192  +      Level        : in     Compression_Level  := Default_Compression;
          193  +      Strategy     : in     Strategy_Type      := Default_Strategy;
          194  +      Method       : in     Compression_Method := Deflated;
          195  +      Window_Bits  : in     Window_Bits_Type   := Default_Window_Bits;
          196  +      Memory_Level : in     Memory_Level_Type  := Default_Memory_Level;
          197  +      Header       : in     Header_Type        := Default)
          198  +   is
          199  +      use type Thin.Int;
          200  +      Win_Bits : Thin.Int := Thin.Int (Window_Bits);
          201  +   begin
          202  +      if Is_Open (Filter) then
          203  +         raise Status_Error;
          204  +      end if;
          205  +
          206  +      --  We allow ZLib to make header only in case of default header type.
          207  +      --  Otherwise we would either do header by ourselfs, or do not do
          208  +      --  header at all.
          209  +
          210  +      if Header = None or else Header = GZip then
          211  +         Win_Bits := -Win_Bits;
          212  +      end if;
          213  +
          214  +      --  For the GZip CRC calculation and make headers.
          215  +
          216  +      if Header = GZip then
          217  +         Filter.CRC    := 0;
          218  +         Filter.Offset := Simple_GZip_Header'First;
          219  +      else
          220  +         Filter.Offset := Simple_GZip_Header'Last + 1;
          221  +      end if;
          222  +
          223  +      Filter.Strm        := new Z_Stream;
          224  +      Filter.Compression := True;
          225  +      Filter.Stream_End  := False;
          226  +      Filter.Header      := Header;
          227  +
          228  +      if Thin.Deflate_Init
          229  +           (To_Thin_Access (Filter.Strm),
          230  +            Level      => Thin.Int (Level),
          231  +            method     => Thin.Int (Method),
          232  +            windowBits => Win_Bits,
          233  +            memLevel   => Thin.Int (Memory_Level),
          234  +            strategy   => Thin.Int (Strategy)) /= Thin.Z_OK
          235  +      then
          236  +         Raise_Error (Filter.Strm.all);
          237  +      end if;
          238  +   end Deflate_Init;
          239  +
          240  +   -----------
          241  +   -- Flush --
          242  +   -----------
          243  +
          244  +   procedure Flush
          245  +     (Filter    : in out Filter_Type;
          246  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          247  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          248  +      Flush     : in     Flush_Mode)
          249  +   is
          250  +      No_Data : Stream_Element_Array := (1 .. 0 => 0);
          251  +      Last    : Stream_Element_Offset;
          252  +   begin
          253  +      Translate (Filter, No_Data, Last, Out_Data, Out_Last, Flush);
          254  +   end Flush;
          255  +
          256  +   -----------------------
          257  +   -- Generic_Translate --
          258  +   -----------------------
          259  +
          260  +   procedure Generic_Translate
          261  +     (Filter          : in out ZLib.Filter_Type;
          262  +      In_Buffer_Size  : in     Integer := Default_Buffer_Size;
          263  +      Out_Buffer_Size : in     Integer := Default_Buffer_Size)
          264  +   is
          265  +      In_Buffer  : Stream_Element_Array
          266  +                     (1 .. Stream_Element_Offset (In_Buffer_Size));
          267  +      Out_Buffer : Stream_Element_Array
          268  +                     (1 .. Stream_Element_Offset (Out_Buffer_Size));
          269  +      Last       : Stream_Element_Offset;
          270  +      In_Last    : Stream_Element_Offset;
          271  +      In_First   : Stream_Element_Offset;
          272  +      Out_Last   : Stream_Element_Offset;
          273  +   begin
          274  +      Main : loop
          275  +         Data_In (In_Buffer, Last);
          276  +
          277  +         In_First := In_Buffer'First;
          278  +
          279  +         loop
          280  +            Translate
          281  +              (Filter   => Filter,
          282  +               In_Data  => In_Buffer (In_First .. Last),
          283  +               In_Last  => In_Last,
          284  +               Out_Data => Out_Buffer,
          285  +               Out_Last => Out_Last,
          286  +               Flush    => Flush_Finish (Last < In_Buffer'First));
          287  +
          288  +            if Out_Buffer'First <= Out_Last then
          289  +               Data_Out (Out_Buffer (Out_Buffer'First .. Out_Last));
          290  +            end if;
          291  +
          292  +            exit Main when Stream_End (Filter);
          293  +
          294  +            --  The end of in buffer.
          295  +
          296  +            exit when In_Last = Last;
          297  +
          298  +            In_First := In_Last + 1;
          299  +         end loop;
          300  +      end loop Main;
          301  +
          302  +   end Generic_Translate;
          303  +
          304  +   ------------------
          305  +   -- Inflate_Init --
          306  +   ------------------
          307  +
          308  +   procedure Inflate_Init
          309  +     (Filter      : in out Filter_Type;
          310  +      Window_Bits : in     Window_Bits_Type := Default_Window_Bits;
          311  +      Header      : in     Header_Type      := Default)
          312  +   is
          313  +      use type Thin.Int;
          314  +      Win_Bits : Thin.Int := Thin.Int (Window_Bits);
          315  +
          316  +      procedure Check_Version;
          317  +      --  Check the latest header types compatibility.
          318  +
          319  +      procedure Check_Version is
          320  +      begin
          321  +         if Version <= "1.1.4" then
          322  +            Raise_Error
          323  +              ("Inflate header type " & Header_Type'Image (Header)
          324  +               & " incompatible with ZLib version " & Version);
          325  +         end if;
          326  +      end Check_Version;
          327  +
          328  +   begin
          329  +      if Is_Open (Filter) then
          330  +         raise Status_Error;
          331  +      end if;
          332  +
          333  +      case Header is
          334  +         when None =>
          335  +            Check_Version;
          336  +
          337  +            --  Inflate data without headers determined
          338  +            --  by negative Win_Bits.
          339  +
          340  +            Win_Bits := -Win_Bits;
          341  +         when GZip =>
          342  +            Check_Version;
          343  +
          344  +            --  Inflate gzip data defined by flag 16.
          345  +
          346  +            Win_Bits := Win_Bits + 16;
          347  +         when Auto =>
          348  +            Check_Version;
          349  +
          350  +            --  Inflate with automatic detection
          351  +            --  of gzip or native header defined by flag 32.
          352  +
          353  +            Win_Bits := Win_Bits + 32;
          354  +         when Default => null;
          355  +      end case;
          356  +
          357  +      Filter.Strm        := new Z_Stream;
          358  +      Filter.Compression := False;
          359  +      Filter.Stream_End  := False;
          360  +      Filter.Header      := Header;
          361  +
          362  +      if Thin.Inflate_Init
          363  +         (To_Thin_Access (Filter.Strm), Win_Bits) /= Thin.Z_OK
          364  +      then
          365  +         Raise_Error (Filter.Strm.all);
          366  +      end if;
          367  +   end Inflate_Init;
          368  +
          369  +   -------------
          370  +   -- Is_Open --
          371  +   -------------
          372  +
          373  +   function Is_Open (Filter : in Filter_Type) return Boolean is
          374  +   begin
          375  +      return Filter.Strm /= null;
          376  +   end Is_Open;
          377  +
          378  +   -----------------
          379  +   -- Raise_Error --
          380  +   -----------------
          381  +
          382  +   procedure Raise_Error (Message : in String) is
          383  +   begin
          384  +      Ada.Exceptions.Raise_Exception (ZLib_Error'Identity, Message);
          385  +   end Raise_Error;
          386  +
          387  +   procedure Raise_Error (Stream : in Z_Stream) is
          388  +   begin
          389  +      Raise_Error (Last_Error_Message (Stream));
          390  +   end Raise_Error;
          391  +
          392  +   ----------
          393  +   -- Read --
          394  +   ----------
          395  +
          396  +   procedure Read
          397  +     (Filter : in out Filter_Type;
          398  +      Item   :    out Ada.Streams.Stream_Element_Array;
          399  +      Last   :    out Ada.Streams.Stream_Element_Offset;
          400  +      Flush  : in     Flush_Mode := No_Flush)
          401  +   is
          402  +      In_Last    : Stream_Element_Offset;
          403  +      Item_First : Ada.Streams.Stream_Element_Offset := Item'First;
          404  +      V_Flush    : Flush_Mode := Flush;
          405  +
          406  +   begin
          407  +      pragma Assert (Rest_First in Buffer'First .. Buffer'Last + 1);
          408  +      pragma Assert (Rest_Last in Buffer'First - 1 .. Buffer'Last);
          409  +
          410  +      loop
          411  +         if Rest_Last = Buffer'First - 1 then
          412  +            V_Flush := Finish;
          413  +
          414  +         elsif Rest_First > Rest_Last then
          415  +            Read (Buffer, Rest_Last);
          416  +            Rest_First := Buffer'First;
          417  +
          418  +            if Rest_Last < Buffer'First then
          419  +               V_Flush := Finish;
          420  +            end if;
          421  +         end if;
          422  +
          423  +         Translate
          424  +           (Filter   => Filter,
          425  +            In_Data  => Buffer (Rest_First .. Rest_Last),
          426  +            In_Last  => In_Last,
          427  +            Out_Data => Item (Item_First .. Item'Last),
          428  +            Out_Last => Last,
          429  +            Flush    => V_Flush);
          430  +
          431  +         Rest_First := In_Last + 1;
          432  +
          433  +         exit when Stream_End (Filter)
          434  +           or else Last = Item'Last
          435  +           or else (Last >= Item'First and then Allow_Read_Some);
          436  +
          437  +         Item_First := Last + 1;
          438  +      end loop;
          439  +   end Read;
          440  +
          441  +   ----------------
          442  +   -- Stream_End --
          443  +   ----------------
          444  +
          445  +   function Stream_End (Filter : in Filter_Type) return Boolean is
          446  +   begin
          447  +      if Filter.Header = GZip and Filter.Compression then
          448  +         return Filter.Stream_End
          449  +            and then Filter.Offset = Footer_Array'Last + 1;
          450  +      else
          451  +         return Filter.Stream_End;
          452  +      end if;
          453  +   end Stream_End;
          454  +
          455  +   --------------
          456  +   -- Total_In --
          457  +   --------------
          458  +
          459  +   function Total_In (Filter : in Filter_Type) return Count is
          460  +   begin
          461  +      return Count (Thin.Total_In (To_Thin_Access (Filter.Strm).all));
          462  +   end Total_In;
          463  +
          464  +   ---------------
          465  +   -- Total_Out --
          466  +   ---------------
          467  +
          468  +   function Total_Out (Filter : in Filter_Type) return Count is
          469  +   begin
          470  +      return Count (Thin.Total_Out (To_Thin_Access (Filter.Strm).all));
          471  +   end Total_Out;
          472  +
          473  +   ---------------
          474  +   -- Translate --
          475  +   ---------------
          476  +
          477  +   procedure Translate
          478  +     (Filter    : in out Filter_Type;
          479  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          480  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          481  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          482  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          483  +      Flush     : in     Flush_Mode) is
          484  +   begin
          485  +      if Filter.Header = GZip and then Filter.Compression then
          486  +         Translate_GZip
          487  +           (Filter   => Filter,
          488  +            In_Data  => In_Data,
          489  +            In_Last  => In_Last,
          490  +            Out_Data => Out_Data,
          491  +            Out_Last => Out_Last,
          492  +            Flush    => Flush);
          493  +      else
          494  +         Translate_Auto
          495  +           (Filter   => Filter,
          496  +            In_Data  => In_Data,
          497  +            In_Last  => In_Last,
          498  +            Out_Data => Out_Data,
          499  +            Out_Last => Out_Last,
          500  +            Flush    => Flush);
          501  +      end if;
          502  +   end Translate;
          503  +
          504  +   --------------------
          505  +   -- Translate_Auto --
          506  +   --------------------
          507  +
          508  +   procedure Translate_Auto
          509  +     (Filter    : in out Filter_Type;
          510  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          511  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          512  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          513  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          514  +      Flush     : in     Flush_Mode)
          515  +   is
          516  +      use type Thin.Int;
          517  +      Code : Thin.Int;
          518  +
          519  +   begin
          520  +      if not Is_Open (Filter) then
          521  +         raise Status_Error;
          522  +      end if;
          523  +
          524  +      if Out_Data'Length = 0 and then In_Data'Length = 0 then
          525  +         raise Constraint_Error;
          526  +      end if;
          527  +
          528  +      Set_Out (Filter.Strm.all, Out_Data'Address, Out_Data'Length);
          529  +      Set_In  (Filter.Strm.all, In_Data'Address, In_Data'Length);
          530  +
          531  +      Code := Flate (Filter.Compression).Step
          532  +        (To_Thin_Access (Filter.Strm),
          533  +         Thin.Int (Flush));
          534  +
          535  +      if Code = Thin.Z_STREAM_END then
          536  +         Filter.Stream_End := True;
          537  +      else
          538  +         Check_Error (Filter.Strm.all, Code);
          539  +      end if;
          540  +
          541  +      In_Last  := In_Data'Last
          542  +         - Stream_Element_Offset (Avail_In (Filter.Strm.all));
          543  +      Out_Last := Out_Data'Last
          544  +         - Stream_Element_Offset (Avail_Out (Filter.Strm.all));
          545  +   end Translate_Auto;
          546  +
          547  +   --------------------
          548  +   -- Translate_GZip --
          549  +   --------------------
          550  +
          551  +   procedure Translate_GZip
          552  +     (Filter    : in out Filter_Type;
          553  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          554  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          555  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          556  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          557  +      Flush     : in     Flush_Mode)
          558  +   is
          559  +      Out_First : Stream_Element_Offset;
          560  +
          561  +      procedure Add_Data (Data : in Stream_Element_Array);
          562  +      --  Add data to stream from the Filter.Offset till necessary,
          563  +      --  used for add gzip headr/footer.
          564  +
          565  +      procedure Put_32
          566  +        (Item : in out Stream_Element_Array;
          567  +         Data : in     Unsigned_32);
          568  +      pragma Inline (Put_32);
          569  +
          570  +      --------------
          571  +      -- Add_Data --
          572  +      --------------
          573  +
          574  +      procedure Add_Data (Data : in Stream_Element_Array) is
          575  +         Data_First : Stream_Element_Offset renames Filter.Offset;
          576  +         Data_Last  : Stream_Element_Offset;
          577  +         Data_Len   : Stream_Element_Offset; --  -1
          578  +         Out_Len    : Stream_Element_Offset; --  -1
          579  +      begin
          580  +         Out_First := Out_Last + 1;
          581  +
          582  +         if Data_First > Data'Last then
          583  +            return;
          584  +         end if;
          585  +
          586  +         Data_Len  := Data'Last     - Data_First;
          587  +         Out_Len   := Out_Data'Last - Out_First;
          588  +
          589  +         if Data_Len <= Out_Len then
          590  +            Out_Last  := Out_First  + Data_Len;
          591  +            Data_Last := Data'Last;
          592  +         else
          593  +            Out_Last  := Out_Data'Last;
          594  +            Data_Last := Data_First + Out_Len;
          595  +         end if;
          596  +
          597  +         Out_Data (Out_First .. Out_Last) := Data (Data_First .. Data_Last);
          598  +
          599  +         Data_First := Data_Last + 1;
          600  +         Out_First  := Out_Last + 1;
          601  +      end Add_Data;
          602  +
          603  +      ------------
          604  +      -- Put_32 --
          605  +      ------------
          606  +
          607  +      procedure Put_32
          608  +        (Item : in out Stream_Element_Array;
          609  +         Data : in     Unsigned_32)
          610  +      is
          611  +         D : Unsigned_32 := Data;
          612  +      begin
          613  +         for J in Item'First .. Item'First + 3 loop
          614  +            Item (J) := Stream_Element (D and 16#FF#);
          615  +            D := Shift_Right (D, 8);
          616  +         end loop;
          617  +      end Put_32;
          618  +
          619  +   begin
          620  +      Out_Last := Out_Data'First - 1;
          621  +
          622  +      if not Filter.Stream_End then
          623  +         Add_Data (Simple_GZip_Header);
          624  +
          625  +         Translate_Auto
          626  +           (Filter   => Filter,
          627  +            In_Data  => In_Data,
          628  +            In_Last  => In_Last,
          629  +            Out_Data => Out_Data (Out_First .. Out_Data'Last),
          630  +            Out_Last => Out_Last,
          631  +            Flush    => Flush);
          632  +
          633  +         CRC32 (Filter.CRC, In_Data (In_Data'First .. In_Last));
          634  +      end if;
          635  +
          636  +      if Filter.Stream_End and then Out_Last <= Out_Data'Last then
          637  +         --  This detection method would work only when
          638  +         --  Simple_GZip_Header'Last > Footer_Array'Last
          639  +
          640  +         if Filter.Offset = Simple_GZip_Header'Last + 1 then
          641  +            Filter.Offset := Footer_Array'First;
          642  +         end if;
          643  +
          644  +         declare
          645  +            Footer : Footer_Array;
          646  +         begin
          647  +            Put_32 (Footer, Filter.CRC);
          648  +            Put_32 (Footer (Footer'First + 4 .. Footer'Last),
          649  +                    Unsigned_32 (Total_In (Filter)));
          650  +            Add_Data (Footer);
          651  +         end;
          652  +      end if;
          653  +   end Translate_GZip;
          654  +
          655  +   -------------
          656  +   -- Version --
          657  +   -------------
          658  +
          659  +   function Version return String is
          660  +   begin
          661  +      return Interfaces.C.Strings.Value (Thin.zlibVersion);
          662  +   end Version;
          663  +
          664  +   -----------
          665  +   -- Write --
          666  +   -----------
          667  +
          668  +   procedure Write
          669  +     (Filter : in out Filter_Type;
          670  +      Item   : in     Ada.Streams.Stream_Element_Array;
          671  +      Flush  : in     Flush_Mode := No_Flush)
          672  +   is
          673  +      Buffer   : Stream_Element_Array (1 .. Buffer_Size);
          674  +      In_Last  : Stream_Element_Offset;
          675  +      Out_Last : Stream_Element_Offset;
          676  +      In_First : Stream_Element_Offset := Item'First;
          677  +   begin
          678  +      if Item'Length = 0 and Flush = No_Flush then
          679  +         return;
          680  +      end if;
          681  +
          682  +      loop
          683  +         Translate
          684  +           (Filter   => Filter,
          685  +            In_Data  => Item (In_First .. Item'Last),
          686  +            In_Last  => In_Last,
          687  +            Out_Data => Buffer,
          688  +            Out_Last => Out_Last,
          689  +            Flush    => Flush);
          690  +
          691  +         if Out_Last >= Buffer'First then
          692  +            Write (Buffer (1 .. Out_Last));
          693  +         end if;
          694  +
          695  +         exit when In_Last = Item'Last or Stream_End (Filter);
          696  +
          697  +         In_First := In_Last + 1;
          698  +      end loop;
          699  +   end Write;
          700  +
          701  +end ZLib;

Added compat/zlib/contrib/ada/zlib.ads.

            1  +------------------------------------------------------------------------------
            2  +--                      ZLib for Ada thick binding.                         --
            3  +--                                                                          --
            4  +--              Copyright (C) 2002-2004 Dmitriy Anisimkov                   --
            5  +--                                                                          --
            6  +--  This library is free software; you can redistribute it and/or modify    --
            7  +--  it under the terms of the GNU General Public License as published by    --
            8  +--  the Free Software Foundation; either version 2 of the License, or (at   --
            9  +--  your option) any later version.                                         --
           10  +--                                                                          --
           11  +--  This library is distributed in the hope that it will be useful, but     --
           12  +--  WITHOUT ANY WARRANTY; without even the implied warranty of              --
           13  +--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       --
           14  +--  General Public License for more details.                                --
           15  +--                                                                          --
           16  +--  You should have received a copy of the GNU General Public License       --
           17  +--  along with this library; if not, write to the Free Software Foundation, --
           18  +--  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.          --
           19  +--                                                                          --
           20  +--  As a special exception, if other files instantiate generics from this   --
           21  +--  unit, or you link this unit with other files to produce an executable,  --
           22  +--  this  unit  does not  by itself cause  the resulting executable to be   --
           23  +--  covered by the GNU General Public License. This exception does not      --
           24  +--  however invalidate any other reasons why the executable file  might be  --
           25  +--  covered by the  GNU Public License.                                     --
           26  +------------------------------------------------------------------------------
           27  +
           28  +--  $Id: zlib.ads,v 1.26 2004/09/06 06:53:19 vagul Exp $
           29  +
           30  +with Ada.Streams;
           31  +
           32  +with Interfaces;
           33  +
           34  +package ZLib is
           35  +
           36  +   ZLib_Error   : exception;
           37  +   Status_Error : exception;
           38  +
           39  +   type Compression_Level is new Integer range -1 .. 9;
           40  +
           41  +   type Flush_Mode is private;
           42  +
           43  +   type Compression_Method is private;
           44  +
           45  +   type Window_Bits_Type is new Integer range 8 .. 15;
           46  +
           47  +   type Memory_Level_Type is new Integer range 1 .. 9;
           48  +
           49  +   type Unsigned_32 is new Interfaces.Unsigned_32;
           50  +
           51  +   type Strategy_Type is private;
           52  +
           53  +   type Header_Type is (None, Auto, Default, GZip);
           54  +   --  Header type usage have a some limitation for inflate.
           55  +   --  See comment for Inflate_Init.
           56  +
           57  +   subtype Count is Ada.Streams.Stream_Element_Count;
           58  +
           59  +   Default_Memory_Level : constant Memory_Level_Type := 8;
           60  +   Default_Window_Bits  : constant Window_Bits_Type  := 15;
           61  +
           62  +   ----------------------------------
           63  +   -- Compression method constants --
           64  +   ----------------------------------
           65  +
           66  +   Deflated : constant Compression_Method;
           67  +   --  Only one method allowed in this ZLib version
           68  +
           69  +   ---------------------------------
           70  +   -- Compression level constants --
           71  +   ---------------------------------
           72  +
           73  +   No_Compression      : constant Compression_Level := 0;
           74  +   Best_Speed          : constant Compression_Level := 1;
           75  +   Best_Compression    : constant Compression_Level := 9;
           76  +   Default_Compression : constant Compression_Level := -1;
           77  +
           78  +   --------------------------
           79  +   -- Flush mode constants --
           80  +   --------------------------
           81  +
           82  +   No_Flush      : constant Flush_Mode;
           83  +   --  Regular way for compression, no flush
           84  +
           85  +   Partial_Flush : constant Flush_Mode;
           86  +   --  Will be removed, use Z_SYNC_FLUSH instead
           87  +
           88  +   Sync_Flush    : constant Flush_Mode;
           89  +   --  All pending output is flushed to the output buffer and the output
           90  +   --  is aligned on a byte boundary, so that the decompressor can get all
           91  +   --  input data available so far. (In particular avail_in is zero after the
           92  +   --  call if enough output space has been provided  before the call.)
           93  +   --  Flushing may degrade compression for some compression algorithms and so
           94  +   --  it should be used only when necessary.
           95  +
           96  +   Block_Flush   : constant Flush_Mode;
           97  +   --  Z_BLOCK requests that inflate() stop
           98  +   --  if and when it get to the next deflate block boundary. When decoding the
           99  +   --  zlib or gzip format, this will cause inflate() to return immediately
          100  +   --  after the header and before the first block. When doing a raw inflate,
          101  +   --  inflate() will go ahead and process the first block, and will return
          102  +   --  when it gets to the end of that block, or when it runs out of data.
          103  +
          104  +   Full_Flush    : constant Flush_Mode;
          105  +   --  All output is flushed as with SYNC_FLUSH, and the compression state
          106  +   --  is reset so that decompression can restart from this point if previous
          107  +   --  compressed data has been damaged or if random access is desired. Using
          108  +   --  Full_Flush too often can seriously degrade the compression.
          109  +
          110  +   Finish        : constant Flush_Mode;
          111  +   --  Just for tell the compressor that input data is complete.
          112  +
          113  +   ------------------------------------
          114  +   -- Compression strategy constants --
          115  +   ------------------------------------
          116  +
          117  +   --  RLE stategy could be used only in version 1.2.0 and later.
          118  +
          119  +   Filtered         : constant Strategy_Type;
          120  +   Huffman_Only     : constant Strategy_Type;
          121  +   RLE              : constant Strategy_Type;
          122  +   Default_Strategy : constant Strategy_Type;
          123  +
          124  +   Default_Buffer_Size : constant := 4096;
          125  +
          126  +   type Filter_Type is tagged limited private;
          127  +   --  The filter is for compression and for decompression.
          128  +   --  The usage of the type is depend of its initialization.
          129  +
          130  +   function Version return String;
          131  +   pragma Inline (Version);
          132  +   --  Return string representation of the ZLib version.
          133  +
          134  +   procedure Deflate_Init
          135  +     (Filter       : in out Filter_Type;
          136  +      Level        : in     Compression_Level  := Default_Compression;
          137  +      Strategy     : in     Strategy_Type      := Default_Strategy;
          138  +      Method       : in     Compression_Method := Deflated;
          139  +      Window_Bits  : in     Window_Bits_Type   := Default_Window_Bits;
          140  +      Memory_Level : in     Memory_Level_Type  := Default_Memory_Level;
          141  +      Header       : in     Header_Type        := Default);
          142  +   --  Compressor initialization.
          143  +   --  When Header parameter is Auto or Default, then default zlib header
          144  +   --  would be provided for compressed data.
          145  +   --  When Header is GZip, then gzip header would be set instead of
          146  +   --  default header.
          147  +   --  When Header is None, no header would be set for compressed data.
          148  +
          149  +   procedure Inflate_Init
          150  +     (Filter      : in out Filter_Type;
          151  +      Window_Bits : in     Window_Bits_Type := Default_Window_Bits;
          152  +      Header      : in     Header_Type      := Default);
          153  +   --  Decompressor initialization.
          154  +   --  Default header type mean that ZLib default header is expecting in the
          155  +   --  input compressed stream.
          156  +   --  Header type None mean that no header is expecting in the input stream.
          157  +   --  GZip header type mean that GZip header is expecting in the
          158  +   --  input compressed stream.
          159  +   --  Auto header type mean that header type (GZip or Native) would be
          160  +   --  detected automatically in the input stream.
          161  +   --  Note that header types parameter values None, GZip and Auto are
          162  +   --  supported for inflate routine only in ZLib versions 1.2.0.2 and later.
          163  +   --  Deflate_Init is supporting all header types.
          164  +
          165  +   function Is_Open (Filter : in Filter_Type) return Boolean;
          166  +   pragma Inline (Is_Open);
          167  +   --  Is the filter opened for compression or decompression.
          168  +
          169  +   procedure Close
          170  +     (Filter       : in out Filter_Type;
          171  +      Ignore_Error : in     Boolean := False);
          172  +   --  Closing the compression or decompressor.
          173  +   --  If stream is closing before the complete and Ignore_Error is False,
          174  +   --  The exception would be raised.
          175  +
          176  +   generic
          177  +      with procedure Data_In
          178  +        (Item : out Ada.Streams.Stream_Element_Array;
          179  +         Last : out Ada.Streams.Stream_Element_Offset);
          180  +      with procedure Data_Out
          181  +        (Item : in Ada.Streams.Stream_Element_Array);
          182  +   procedure Generic_Translate
          183  +     (Filter          : in out Filter_Type;
          184  +      In_Buffer_Size  : in     Integer := Default_Buffer_Size;
          185  +      Out_Buffer_Size : in     Integer := Default_Buffer_Size);
          186  +   --  Compress/decompress data fetch from Data_In routine and pass the result
          187  +   --  to the Data_Out routine. User should provide Data_In and Data_Out
          188  +   --  for compression/decompression data flow.
          189  +   --  Compression or decompression depend on Filter initialization.
          190  +
          191  +   function Total_In (Filter : in Filter_Type) return Count;
          192  +   pragma Inline (Total_In);
          193  +   --  Returns total number of input bytes read so far
          194  +
          195  +   function Total_Out (Filter : in Filter_Type) return Count;
          196  +   pragma Inline (Total_Out);
          197  +   --  Returns total number of bytes output so far
          198  +
          199  +   function CRC32
          200  +     (CRC    : in Unsigned_32;
          201  +      Data   : in Ada.Streams.Stream_Element_Array)
          202  +      return Unsigned_32;
          203  +   pragma Inline (CRC32);
          204  +   --  Compute CRC32, it could be necessary for make gzip format
          205  +
          206  +   procedure CRC32
          207  +     (CRC  : in out Unsigned_32;
          208  +      Data : in     Ada.Streams.Stream_Element_Array);
          209  +   pragma Inline (CRC32);
          210  +   --  Compute CRC32, it could be necessary for make gzip format
          211  +
          212  +   -------------------------------------------------
          213  +   --  Below is more complex low level routines.  --
          214  +   -------------------------------------------------
          215  +
          216  +   procedure Translate
          217  +     (Filter    : in out Filter_Type;
          218  +      In_Data   : in     Ada.Streams.Stream_Element_Array;
          219  +      In_Last   :    out Ada.Streams.Stream_Element_Offset;
          220  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          221  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          222  +      Flush     : in     Flush_Mode);
          223  +   --  Compress/decompress the In_Data buffer and place the result into
          224  +   --  Out_Data. In_Last is the index of last element from In_Data accepted by
          225  +   --  the Filter. Out_Last is the last element of the received data from
          226  +   --  Filter. To tell the filter that incoming data are complete put the
          227  +   --  Flush parameter to Finish.
          228  +
          229  +   function Stream_End (Filter : in Filter_Type) return Boolean;
          230  +   pragma Inline (Stream_End);
          231  +   --  Return the true when the stream is complete.
          232  +
          233  +   procedure Flush
          234  +     (Filter    : in out Filter_Type;
          235  +      Out_Data  :    out Ada.Streams.Stream_Element_Array;
          236  +      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
          237  +      Flush     : in     Flush_Mode);
          238  +   pragma Inline (Flush);
          239  +   --  Flushing the data from the compressor.
          240  +
          241  +   generic
          242  +      with procedure Write
          243  +        (Item : in Ada.Streams.Stream_Element_Array);
          244  +      --  User should provide this routine for accept
          245  +      --  compressed/decompressed data.
          246  +
          247  +      Buffer_Size : in Ada.Streams.Stream_Element_Offset
          248  +         := Default_Buffer_Size;
          249  +      --  Buffer size for Write user routine.
          250  +
          251  +   procedure Write
          252  +     (Filter  : in out Filter_Type;
          253  +      Item    : in     Ada.Streams.Stream_Element_Array;
          254  +      Flush   : in     Flush_Mode := No_Flush);
          255  +   --  Compress/Decompress data from Item to the generic parameter procedure
          256  +   --  Write. Output buffer size could be set in Buffer_Size generic parameter.
          257  +
          258  +   generic
          259  +      with procedure Read
          260  +        (Item : out Ada.Streams.Stream_Element_Array;
          261  +         Last : out Ada.Streams.Stream_Element_Offset);
          262  +      --  User should provide data for compression/decompression
          263  +      --  thru this routine.
          264  +
          265  +      Buffer : in out Ada.Streams.Stream_Element_Array;
          266  +      --  Buffer for keep remaining data from the previous
          267  +      --  back read.
          268  +
          269  +      Rest_First, Rest_Last : in out Ada.Streams.Stream_Element_Offset;
          270  +      --  Rest_First have to be initialized to Buffer'Last + 1
          271  +      --  Rest_Last have to be initialized to Buffer'Last
          272  +      --  before usage.
          273  +
          274  +      Allow_Read_Some : in Boolean := False;
          275  +      --  Is it allowed to return Last < Item'Last before end of data.
          276  +
          277  +   procedure Read
          278  +     (Filter : in out Filter_Type;
          279  +      Item   :    out Ada.Streams.Stream_Element_Array;
          280  +      Last   :    out Ada.Streams.Stream_Element_Offset;
          281  +      Flush  : in     Flush_Mode := No_Flush);
          282  +   --  Compress/Decompress data from generic parameter procedure Read to the
          283  +   --  Item. User should provide Buffer and initialized Rest_First, Rest_Last
          284  +   --  indicators. If Allow_Read_Some is True, Read routines could return
          285  +   --  Last < Item'Last only at end of stream.
          286  +
          287  +private
          288  +
          289  +   use Ada.Streams;
          290  +
          291  +   pragma Assert (Ada.Streams.Stream_Element'Size    =    8);
          292  +   pragma Assert (Ada.Streams.Stream_Element'Modulus = 2**8);
          293  +
          294  +   type Flush_Mode is new Integer range 0 .. 5;
          295  +
          296  +   type Compression_Method is new Integer range 8 .. 8;
          297  +
          298  +   type Strategy_Type is new Integer range 0 .. 3;
          299  +
          300  +   No_Flush      : constant Flush_Mode := 0;
          301  +   Partial_Flush : constant Flush_Mode := 1;
          302  +   Sync_Flush    : constant Flush_Mode := 2;
          303  +   Full_Flush    : constant Flush_Mode := 3;
          304  +   Finish        : constant Flush_Mode := 4;
          305  +   Block_Flush   : constant Flush_Mode := 5;
          306  +
          307  +   Filtered         : constant Strategy_Type := 1;
          308  +   Huffman_Only     : constant Strategy_Type := 2;
          309  +   RLE              : constant Strategy_Type := 3;
          310  +   Default_Strategy : constant Strategy_Type := 0;
          311  +
          312  +   Deflated : constant Compression_Method := 8;
          313  +
          314  +   type Z_Stream;
          315  +
          316  +   type Z_Stream_Access is access all Z_Stream;
          317  +
          318  +   type Filter_Type is tagged limited record
          319  +      Strm        : Z_Stream_Access;
          320  +      Compression : Boolean;
          321  +      Stream_End  : Boolean;
          322  +      Header      : Header_Type;
          323  +      CRC         : Unsigned_32;
          324  +      Offset      : Stream_Element_Offset;
          325  +      --  Offset for gzip header/footer output.
          326  +   end record;
          327  +
          328  +end ZLib;

Added compat/zlib/contrib/ada/zlib.gpr.

            1  +project Zlib is
            2  +
            3  +   for Languages use ("Ada");
            4  +   for Source_Dirs use (".");
            5  +   for Object_Dir use ".";
            6  +   for Main use ("test.adb", "mtest.adb", "read.adb", "buffer_demo");
            7  +
            8  +   package Compiler is
            9  +      for Default_Switches ("ada") use ("-gnatwcfilopru", "-gnatVcdfimorst", "-gnatyabcefhiklmnoprst");
           10  +   end Compiler;
           11  +
           12  +   package Linker is
           13  +      for Default_Switches ("ada") use ("-lz");
           14  +   end Linker;
           15  +
           16  +   package Builder is
           17  +      for Default_Switches ("ada") use ("-s", "-gnatQ");
           18  +   end Builder;
           19  +
           20  +end Zlib;

Added compat/zlib/contrib/amd64/amd64-match.S.

            1  +/*
            2  + * match.S -- optimized version of longest_match()
            3  + * based on the similar work by Gilles Vollant, and Brian Raiter, written 1998
            4  + *
            5  + * This is free software; you can redistribute it and/or modify it
            6  + * under the terms of the BSD License. Use by owners of Che Guevarra
            7  + * parafernalia is prohibited, where possible, and highly discouraged
            8  + * elsewhere.
            9  + */
           10  +
           11  +#ifndef NO_UNDERLINE
           12  +#	define	match_init	_match_init
           13  +#	define	longest_match	_longest_match
           14  +#endif
           15  +
           16  +#define	scanend		ebx
           17  +#define	scanendw	bx
           18  +#define	chainlenwmask	edx /* high word: current chain len low word: s->wmask */
           19  +#define	curmatch	rsi
           20  +#define	curmatchd	esi
           21  +#define	windowbestlen	r8
           22  +#define	scanalign	r9
           23  +#define	scanalignd	r9d
           24  +#define	window		r10
           25  +#define	bestlen		r11
           26  +#define	bestlend	r11d
           27  +#define	scanstart	r12d
           28  +#define	scanstartw	r12w
           29  +#define scan		r13
           30  +#define nicematch	r14d
           31  +#define	limit		r15
           32  +#define	limitd		r15d
           33  +#define prev		rcx
           34  +
           35  +/*
           36  + * The 258 is a "magic number, not a parameter -- changing it
           37  + * breaks the hell loose
           38  + */
           39  +#define	MAX_MATCH	(258)
           40  +#define	MIN_MATCH	(3)
           41  +#define	MIN_LOOKAHEAD	(MAX_MATCH + MIN_MATCH + 1)
           42  +#define	MAX_MATCH_8	((MAX_MATCH + 7) & ~7)
           43  +
           44  +/* stack frame offsets */
           45  +#define	LocalVarsSize	(112)
           46  +#define _chainlenwmask	( 8-LocalVarsSize)(%rsp)
           47  +#define _windowbestlen	(16-LocalVarsSize)(%rsp)
           48  +#define save_r14        (24-LocalVarsSize)(%rsp)
           49  +#define save_rsi        (32-LocalVarsSize)(%rsp)
           50  +#define save_rbx        (40-LocalVarsSize)(%rsp)
           51  +#define save_r12        (56-LocalVarsSize)(%rsp)
           52  +#define save_r13        (64-LocalVarsSize)(%rsp)
           53  +#define save_r15        (80-LocalVarsSize)(%rsp)
           54  +
           55  +
           56  +.globl	match_init, longest_match
           57  +
           58  +/*
           59  + * On AMD64 the first argument of a function (in our case -- the pointer to
           60  + * deflate_state structure) is passed in %rdi, hence our offsets below are
           61  + * all off of that.
           62  + */
           63  +
           64  +/* you can check the structure offset by running
           65  +
           66  +#include <stdlib.h>
           67  +#include <stdio.h>
           68  +#include "deflate.h"
           69  +
           70  +void print_depl()
           71  +{
           72  +deflate_state ds;
           73  +deflate_state *s=&ds;
           74  +printf("size pointer=%u\n",(int)sizeof(void*));
           75  +
           76  +printf("#define dsWSize         (%3u)(%%rdi)\n",(int)(((char*)&(s->w_size))-((char*)s)));
           77  +printf("#define dsWMask         (%3u)(%%rdi)\n",(int)(((char*)&(s->w_mask))-((char*)s)));
           78  +printf("#define dsWindow        (%3u)(%%rdi)\n",(int)(((char*)&(s->window))-((char*)s)));
           79  +printf("#define dsPrev          (%3u)(%%rdi)\n",(int)(((char*)&(s->prev))-((char*)s)));
           80  +printf("#define dsMatchLen      (%3u)(%%rdi)\n",(int)(((char*)&(s->match_length))-((char*)s)));
           81  +printf("#define dsPrevMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_match))-((char*)s)));
           82  +printf("#define dsStrStart      (%3u)(%%rdi)\n",(int)(((char*)&(s->strstart))-((char*)s)));
           83  +printf("#define dsMatchStart    (%3u)(%%rdi)\n",(int)(((char*)&(s->match_start))-((char*)s)));
           84  +printf("#define dsLookahead     (%3u)(%%rdi)\n",(int)(((char*)&(s->lookahead))-((char*)s)));
           85  +printf("#define dsPrevLen       (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_length))-((char*)s)));
           86  +printf("#define dsMaxChainLen   (%3u)(%%rdi)\n",(int)(((char*)&(s->max_chain_length))-((char*)s)));
           87  +printf("#define dsGoodMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->good_match))-((char*)s)));
           88  +printf("#define dsNiceMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->nice_match))-((char*)s)));
           89  +}
           90  +
           91  +*/
           92  +
           93  +
           94  +/*
           95  +  to compile for XCode 3.2 on MacOSX x86_64
           96  +  - run "gcc -g -c -DXCODE_MAC_X64_STRUCTURE amd64-match.S"
           97  + */
           98  +
           99  +
          100  +#ifndef CURRENT_LINX_XCODE_MAC_X64_STRUCTURE
          101  +#define dsWSize		( 68)(%rdi)
          102  +#define dsWMask		( 76)(%rdi)
          103  +#define dsWindow	( 80)(%rdi)
          104  +#define dsPrev		( 96)(%rdi)
          105  +#define dsMatchLen	(144)(%rdi)
          106  +#define dsPrevMatch	(148)(%rdi)
          107  +#define dsStrStart	(156)(%rdi)
          108  +#define dsMatchStart	(160)(%rdi)
          109  +#define dsLookahead	(164)(%rdi)
          110  +#define dsPrevLen	(168)(%rdi)
          111  +#define dsMaxChainLen	(172)(%rdi)
          112  +#define dsGoodMatch	(188)(%rdi)
          113  +#define dsNiceMatch	(192)(%rdi)
          114  +
          115  +#else 
          116  +
          117  +#ifndef STRUCT_OFFSET
          118  +#	define STRUCT_OFFSET	(0)
          119  +#endif
          120  +
          121  +
          122  +#define dsWSize		( 56 + STRUCT_OFFSET)(%rdi)
          123  +#define dsWMask		( 64 + STRUCT_OFFSET)(%rdi)
          124  +#define dsWindow	( 72 + STRUCT_OFFSET)(%rdi)
          125  +#define dsPrev		( 88 + STRUCT_OFFSET)(%rdi)
          126  +#define dsMatchLen	(136 + STRUCT_OFFSET)(%rdi)
          127  +#define dsPrevMatch	(140 + STRUCT_OFFSET)(%rdi)
          128  +#define dsStrStart	(148 + STRUCT_OFFSET)(%rdi)
          129  +#define dsMatchStart	(152 + STRUCT_OFFSET)(%rdi)
          130  +#define dsLookahead	(156 + STRUCT_OFFSET)(%rdi)
          131  +#define dsPrevLen	(160 + STRUCT_OFFSET)(%rdi)
          132  +#define dsMaxChainLen	(164 + STRUCT_OFFSET)(%rdi)
          133  +#define dsGoodMatch	(180 + STRUCT_OFFSET)(%rdi)
          134  +#define dsNiceMatch	(184 + STRUCT_OFFSET)(%rdi)
          135  +
          136  +#endif
          137  +
          138  +
          139  +
          140  +
          141  +.text
          142  +
          143  +/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
          144  +
          145  +longest_match:
          146  +/*
          147  + * Retrieve the function arguments. %curmatch will hold cur_match
          148  + * throughout the entire function (passed via rsi on amd64).
          149  + * rdi will hold the pointer to the deflate_state (first arg on amd64)
          150  + */
          151  +		mov     %rsi, save_rsi
          152  +		mov     %rbx, save_rbx
          153  +		mov	%r12, save_r12
          154  +		mov     %r13, save_r13
          155  +		mov     %r14, save_r14
          156  +		mov     %r15, save_r15
          157  +
          158  +/* uInt wmask = s->w_mask;						*/
          159  +/* unsigned chain_length = s->max_chain_length;				*/
          160  +/* if (s->prev_length >= s->good_match) {				*/
          161  +/*     chain_length >>= 2;						*/
          162  +/* }									*/
          163  +
          164  +		movl	dsPrevLen, %eax
          165  +		movl	dsGoodMatch, %ebx
          166  +		cmpl	%ebx, %eax
          167  +		movl	dsWMask, %eax
          168  +		movl	dsMaxChainLen, %chainlenwmask
          169  +		jl	LastMatchGood
          170  +		shrl	$2, %chainlenwmask
          171  +LastMatchGood:
          172  +
          173  +/* chainlen is decremented once beforehand so that the function can	*/
          174  +/* use the sign flag instead of the zero flag for the exit test.	*/
          175  +/* It is then shifted into the high word, to make room for the wmask	*/
          176  +/* value, which it will always accompany.				*/
          177  +
          178  +		decl	%chainlenwmask
          179  +		shll	$16, %chainlenwmask
          180  +		orl	%eax, %chainlenwmask
          181  +
          182  +/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;	*/
          183  +
          184  +		movl	dsNiceMatch, %eax
          185  +		movl	dsLookahead, %ebx
          186  +		cmpl	%eax, %ebx
          187  +		jl	LookaheadLess
          188  +		movl	%eax, %ebx
          189  +LookaheadLess:	movl	%ebx, %nicematch
          190  +
          191  +/* register Bytef *scan = s->window + s->strstart;			*/
          192  +
          193  +		mov	dsWindow, %window
          194  +		movl	dsStrStart, %limitd
          195  +		lea	(%limit, %window), %scan
          196  +
          197  +/* Determine how many bytes the scan ptr is off from being		*/
          198  +/* dword-aligned.							*/
          199  +
          200  +		mov	%scan, %scanalign
          201  +		negl	%scanalignd
          202  +		andl	$3, %scanalignd
          203  +
          204  +/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ?			*/
          205  +/*     s->strstart - (IPos)MAX_DIST(s) : NIL;				*/
          206  +
          207  +		movl	dsWSize, %eax
          208  +		subl	$MIN_LOOKAHEAD, %eax
          209  +		xorl	%ecx, %ecx
          210  +		subl	%eax, %limitd
          211  +		cmovng	%ecx, %limitd
          212  +
          213  +/* int best_len = s->prev_length;					*/
          214  +
          215  +		movl	dsPrevLen, %bestlend
          216  +
          217  +/* Store the sum of s->window + best_len in %windowbestlen locally, and in memory.	*/
          218  +
          219  +		lea	(%window, %bestlen), %windowbestlen
          220  +		mov	%windowbestlen, _windowbestlen
          221  +
          222  +/* register ush scan_start = *(ushf*)scan;				*/
          223  +/* register ush scan_end   = *(ushf*)(scan+best_len-1);			*/
          224  +/* Posf *prev = s->prev;						*/
          225  +
          226  +		movzwl	(%scan), %scanstart
          227  +		movzwl	-1(%scan, %bestlen), %scanend
          228  +		mov	dsPrev, %prev
          229  +
          230  +/* Jump into the main loop.						*/
          231  +
          232  +		movl	%chainlenwmask, _chainlenwmask
          233  +		jmp	LoopEntry
          234  +
          235  +.balign 16
          236  +
          237  +/* do {
          238  + *     match = s->window + cur_match;
          239  + *     if (*(ushf*)(match+best_len-1) != scan_end ||
          240  + *         *(ushf*)match != scan_start) continue;
          241  + *     [...]
          242  + * } while ((cur_match = prev[cur_match & wmask]) > limit
          243  + *          && --chain_length != 0);
          244  + *
          245  + * Here is the inner loop of the function. The function will spend the
          246  + * majority of its time in this loop, and majority of that time will
          247  + * be spent in the first ten instructions.
          248  + */
          249  +LookupLoop:
          250  +		andl	%chainlenwmask, %curmatchd
          251  +		movzwl	(%prev, %curmatch, 2), %curmatchd
          252  +		cmpl	%limitd, %curmatchd
          253  +		jbe	LeaveNow
          254  +		subl	$0x00010000, %chainlenwmask
          255  +		js	LeaveNow
          256  +LoopEntry:	cmpw	-1(%windowbestlen, %curmatch), %scanendw
          257  +		jne	LookupLoop
          258  +		cmpw	%scanstartw, (%window, %curmatch)
          259  +		jne	LookupLoop
          260  +
          261  +/* Store the current value of chainlen.					*/
          262  +		movl	%chainlenwmask, _chainlenwmask
          263  +
          264  +/* %scan is the string under scrutiny, and %prev to the string we	*/
          265  +/* are hoping to match it up with. In actuality, %esi and %edi are	*/
          266  +/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is	*/
          267  +/* initialized to -(MAX_MATCH_8 - scanalign).				*/
          268  +
          269  +		mov	$(-MAX_MATCH_8), %rdx
          270  +		lea	(%curmatch, %window), %windowbestlen
          271  +		lea	MAX_MATCH_8(%windowbestlen, %scanalign), %windowbestlen
          272  +		lea	MAX_MATCH_8(%scan, %scanalign), %prev
          273  +
          274  +/* the prefetching below makes very little difference... */
          275  +		prefetcht1	(%windowbestlen, %rdx)
          276  +		prefetcht1	(%prev, %rdx)
          277  +
          278  +/*
          279  + * Test the strings for equality, 8 bytes at a time. At the end,
          280  + * adjust %rdx so that it is offset to the exact byte that mismatched.
          281  + *
          282  + * It should be confessed that this loop usually does not represent
          283  + * much of the total running time. Replacing it with a more
          284  + * straightforward "rep cmpsb" would not drastically degrade
          285  + * performance -- unrolling it, for example, makes no difference.
          286  + */
          287  +
          288  +#undef USE_SSE	/* works, but is 6-7% slower, than non-SSE... */
          289  +
          290  +LoopCmps:
          291  +#ifdef USE_SSE
          292  +		/* Preload the SSE registers */
          293  +		movdqu	  (%windowbestlen, %rdx), %xmm1
          294  +		movdqu	  (%prev, %rdx), %xmm2
          295  +		pcmpeqb	%xmm2, %xmm1
          296  +		movdqu	16(%windowbestlen, %rdx), %xmm3
          297  +		movdqu	16(%prev, %rdx), %xmm4
          298  +		pcmpeqb	%xmm4, %xmm3
          299  +		movdqu	32(%windowbestlen, %rdx), %xmm5
          300  +		movdqu	32(%prev, %rdx), %xmm6
          301  +		pcmpeqb	%xmm6, %xmm5
          302  +		movdqu	48(%windowbestlen, %rdx), %xmm7
          303  +		movdqu	48(%prev, %rdx), %xmm8
          304  +		pcmpeqb	%xmm8, %xmm7
          305  +
          306  +		/* Check the comparisions' results */
          307  +		pmovmskb %xmm1, %rax
          308  +		notw	%ax
          309  +		bsfw	%ax, %ax
          310  +		jnz	LeaveLoopCmps
          311  +		
          312  +		/* this is the only iteration of the loop with a possibility of having
          313  +		   incremented rdx by 0x108 (each loop iteration add 16*4 = 0x40 
          314  +		   and (0x40*4)+8=0x108 */
          315  +		add	$8, %rdx
          316  +		jz LenMaximum
          317  +		add	$8, %rdx
          318  +
          319  +		
          320  +		pmovmskb %xmm3, %rax
          321  +		notw	%ax
          322  +		bsfw	%ax, %ax
          323  +		jnz	LeaveLoopCmps
          324  +		
          325  +		
          326  +		add	$16, %rdx
          327  +
          328  +
          329  +		pmovmskb %xmm5, %rax
          330  +		notw	%ax
          331  +		bsfw	%ax, %ax
          332  +		jnz	LeaveLoopCmps
          333  +		
          334  +		add	$16, %rdx
          335  +
          336  +
          337  +		pmovmskb %xmm7, %rax
          338  +		notw	%ax
          339  +		bsfw	%ax, %ax
          340  +		jnz	LeaveLoopCmps
          341  +		
          342  +		add	$16, %rdx
          343  +		
          344  +		jmp	LoopCmps
          345  +LeaveLoopCmps:	add	%rax, %rdx
          346  +#else
          347  +		mov	(%windowbestlen, %rdx), %rax
          348  +		xor	(%prev, %rdx), %rax
          349  +		jnz	LeaveLoopCmps
          350  +		
          351  +		mov	8(%windowbestlen, %rdx), %rax
          352  +		xor	8(%prev, %rdx), %rax
          353  +		jnz	LeaveLoopCmps8
          354  +
          355  +		mov	16(%windowbestlen, %rdx), %rax
          356  +		xor	16(%prev, %rdx), %rax
          357  +		jnz	LeaveLoopCmps16
          358  +				
          359  +		add	$24, %rdx
          360  +		jnz	LoopCmps
          361  +		jmp	LenMaximum
          362  +#	if 0
          363  +/*
          364  + * This three-liner is tantalizingly simple, but bsf is a slow instruction,
          365  + * and the complicated alternative down below is quite a bit faster. Sad...
          366  + */
          367  +
          368  +LeaveLoopCmps:	bsf	%rax, %rax /* find the first non-zero bit */
          369  +		shrl	$3, %eax /* divide by 8 to get the byte */
          370  +		add	%rax, %rdx
          371  +#	else
          372  +LeaveLoopCmps16:
          373  +		add	$8, %rdx
          374  +LeaveLoopCmps8:
          375  +		add	$8, %rdx
          376  +LeaveLoopCmps:	testl   $0xFFFFFFFF, %eax /* Check the first 4 bytes */
          377  +		jnz     Check16
          378  +		add     $4, %rdx
          379  +		shr     $32, %rax
          380  +Check16:        testw   $0xFFFF, %ax
          381  +		jnz     LenLower
          382  +		add	$2, %rdx
          383  +		shrl	$16, %eax
          384  +LenLower:	subb	$1, %al
          385  +		adc	$0, %rdx
          386  +#	endif
          387  +#endif
          388  +
          389  +/* Calculate the length of the match. If it is longer than MAX_MATCH,	*/
          390  +/* then automatically accept it as the best possible match and leave.	*/
          391  +
          392  +		lea	(%prev, %rdx), %rax
          393  +		sub	%scan, %rax
          394  +		cmpl	$MAX_MATCH, %eax
          395  +		jge	LenMaximum
          396  +
          397  +/* If the length of the match is not longer than the best match we	*/
          398  +/* have so far, then forget it and return to the lookup loop.		*/
          399  +
          400  +		cmpl	%bestlend, %eax
          401  +		jg	LongerMatch
          402  +		mov	_windowbestlen, %windowbestlen
          403  +		mov	dsPrev, %prev
          404  +		movl	_chainlenwmask, %edx
          405  +		jmp	LookupLoop
          406  +
          407  +/*         s->match_start = cur_match;					*/
          408  +/*         best_len = len;						*/
          409  +/*         if (len >= nice_match) break;				*/
          410  +/*         scan_end = *(ushf*)(scan+best_len-1);			*/
          411  +
          412  +LongerMatch:
          413  +		movl	%eax, %bestlend
          414  +		movl	%curmatchd, dsMatchStart
          415  +		cmpl	%nicematch, %eax
          416  +		jge	LeaveNow
          417  +
          418  +		lea	(%window, %bestlen), %windowbestlen
          419  +		mov	%windowbestlen, _windowbestlen
          420  +
          421  +		movzwl	-1(%scan, %rax), %scanend
          422  +		mov	dsPrev, %prev
          423  +		movl	_chainlenwmask, %chainlenwmask
          424  +		jmp	LookupLoop
          425  +
          426  +/* Accept the current string, with the maximum possible length.		*/
          427  +
          428  +LenMaximum:
          429  +		movl	$MAX_MATCH, %bestlend
          430  +		movl	%curmatchd, dsMatchStart
          431  +
          432  +/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len;		*/
          433  +/* return s->lookahead;							*/
          434  +
          435  +LeaveNow:
          436  +		movl	dsLookahead, %eax
          437  +		cmpl	%eax, %bestlend
          438  +		cmovngl	%bestlend, %eax
          439  +LookaheadRet:
          440  +
          441  +/* Restore the registers and return from whence we came.			*/
          442  +
          443  +	mov	save_rsi, %rsi
          444  +	mov	save_rbx, %rbx
          445  +	mov	save_r12, %r12
          446  +	mov	save_r13, %r13
          447  +	mov	save_r14, %r14
          448  +	mov	save_r15, %r15
          449  +
          450  +	ret
          451  +
          452  +match_init:	ret

Added compat/zlib/contrib/asm686/README.686.

            1  +This is a patched version of zlib, modified to use
            2  +Pentium-Pro-optimized assembly code in the deflation algorithm. The
            3  +files changed/added by this patch are:
            4  +
            5  +README.686
            6  +match.S
            7  +
            8  +The speedup that this patch provides varies, depending on whether the
            9  +compiler used to build the original version of zlib falls afoul of the
           10  +PPro's speed traps. My own tests show a speedup of around 10-20% at
           11  +the default compression level, and 20-30% using -9, against a version
           12  +compiled using gcc 2.7.2.3. Your mileage may vary.
           13  +
           14  +Note that this code has been tailored for the PPro/PII in particular,
           15  +and will not perform particuarly well on a Pentium.
           16  +
           17  +If you are using an assembler other than GNU as, you will have to
           18  +translate match.S to use your assembler's syntax. (Have fun.)
           19  +
           20  +Brian Raiter
           21  +breadbox@muppetlabs.com
           22  +April, 1998
           23  +
           24  +
           25  +Added for zlib 1.1.3:
           26  +
           27  +The patches come from
           28  +http://www.muppetlabs.com/~breadbox/software/assembly.html
           29  +
           30  +To compile zlib with this asm file, copy match.S to the zlib directory
           31  +then do:
           32  +
           33  +CFLAGS="-O3 -DASMV" ./configure
           34  +make OBJA=match.o
           35  +
           36  +
           37  +Update:
           38  +
           39  +I've been ignoring these assembly routines for years, believing that
           40  +gcc's generated code had caught up with it sometime around gcc 2.95
           41  +and the major rearchitecting of the Pentium 4. However, I recently
           42  +learned that, despite what I believed, this code still has some life
           43  +in it. On the Pentium 4 and AMD64 chips, it continues to run about 8%
           44  +faster than the code produced by gcc 4.1.
           45  +
           46  +In acknowledgement of its continuing usefulness, I've altered the
           47  +license to match that of the rest of zlib. Share and Enjoy!
           48  +
           49  +Brian Raiter
           50  +breadbox@muppetlabs.com
           51  +April, 2007

Added compat/zlib/contrib/asm686/match.S.

            1  +/* match.S -- x86 assembly version of the zlib longest_match() function.
            2  + * Optimized for the Intel 686 chips (PPro and later).
            3  + *
            4  + * Copyright (C) 1998, 2007 Brian Raiter <breadbox@muppetlabs.com>
            5  + *
            6  + * This software is provided 'as-is', without any express or implied
            7  + * warranty.  In no event will the author be held liable for any damages
            8  + * arising from the use of this software.
            9  + *
           10  + * Permission is granted to anyone to use this software for any purpose,
           11  + * including commercial applications, and to alter it and redistribute it
           12  + * freely, subject to the following restrictions:
           13  + *
           14  + * 1. The origin of this software must not be misrepresented; you must not
           15  + *    claim that you wrote the original software. If you use this software
           16  + *    in a product, an acknowledgment in the product documentation would be
           17  + *    appreciated but is not required.
           18  + * 2. Altered source versions must be plainly marked as such, and must not be
           19  + *    misrepresented as being the original software.
           20  + * 3. This notice may not be removed or altered from any source distribution.
           21  + */
           22  +
           23  +#ifndef NO_UNDERLINE
           24  +#define	match_init	_match_init
           25  +#define	longest_match	_longest_match
           26  +#endif
           27  +
           28  +#define	MAX_MATCH	(258)
           29  +#define	MIN_MATCH	(3)
           30  +#define	MIN_LOOKAHEAD	(MAX_MATCH + MIN_MATCH + 1)
           31  +#define	MAX_MATCH_8	((MAX_MATCH + 7) & ~7)
           32  +
           33  +/* stack frame offsets */
           34  +
           35  +#define	chainlenwmask		0	/* high word: current chain len	*/
           36  +					/* low word: s->wmask		*/
           37  +#define	window			4	/* local copy of s->window	*/
           38  +#define	windowbestlen		8	/* s->window + bestlen		*/
           39  +#define	scanstart		16	/* first two bytes of string	*/
           40  +#define	scanend			12	/* last two bytes of string	*/
           41  +#define	scanalign		20	/* dword-misalignment of string	*/
           42  +#define	nicematch		24	/* a good enough match size	*/
           43  +#define	bestlen			28	/* size of best match so far	*/
           44  +#define	scan			32	/* ptr to string wanting match	*/
           45  +
           46  +#define	LocalVarsSize		(36)
           47  +/*	saved ebx		36 */
           48  +/*	saved edi		40 */
           49  +/*	saved esi		44 */
           50  +/*	saved ebp		48 */
           51  +/*	return address		52 */
           52  +#define	deflatestate		56	/* the function arguments	*/
           53  +#define	curmatch		60
           54  +
           55  +/* All the +zlib1222add offsets are due to the addition of fields
           56  + *  in zlib in the deflate_state structure since the asm code was first written
           57  + * (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
           58  + * (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
           59  + * if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
           60  + */
           61  +
           62  +#define zlib1222add		(8)
           63  +
           64  +#define	dsWSize			(36+zlib1222add)
           65  +#define	dsWMask			(44+zlib1222add)
           66  +#define	dsWindow		(48+zlib1222add)
           67  +#define	dsPrev			(56+zlib1222add)
           68  +#define	dsMatchLen		(88+zlib1222add)
           69  +#define	dsPrevMatch		(92+zlib1222add)
           70  +#define	dsStrStart		(100+zlib1222add)
           71  +#define	dsMatchStart		(104+zlib1222add)
           72  +#define	dsLookahead		(108+zlib1222add)
           73  +#define	dsPrevLen		(112+zlib1222add)
           74  +#define	dsMaxChainLen		(116+zlib1222add)
           75  +#define	dsGoodMatch		(132+zlib1222add)
           76  +#define	dsNiceMatch		(136+zlib1222add)
           77  +
           78  +
           79  +.file "match.S"
           80  +
           81  +.globl	match_init, longest_match
           82  +
           83  +.text
           84  +
           85  +/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
           86  +.cfi_sections	.debug_frame
           87  +
           88  +longest_match:
           89  +
           90  +.cfi_startproc
           91  +/* Save registers that the compiler may be using, and adjust %esp to	*/
           92  +/* make room for our stack frame.					*/
           93  +
           94  +		pushl	%ebp
           95  +		.cfi_def_cfa_offset 8
           96  +		.cfi_offset ebp, -8
           97  +		pushl	%edi
           98  +		.cfi_def_cfa_offset 12
           99  +		pushl	%esi
          100  +		.cfi_def_cfa_offset 16
          101  +		pushl	%ebx
          102  +		.cfi_def_cfa_offset 20
          103  +		subl	$LocalVarsSize, %esp
          104  +		.cfi_def_cfa_offset LocalVarsSize+20
          105  +
          106  +/* Retrieve the function arguments. %ecx will hold cur_match		*/
          107  +/* throughout the entire function. %edx will hold the pointer to the	*/
          108  +/* deflate_state structure during the function's setup (before		*/
          109  +/* entering the main loop).						*/
          110  +
          111  +		movl	deflatestate(%esp), %edx
          112  +		movl	curmatch(%esp), %ecx
          113  +
          114  +/* uInt wmask = s->w_mask;						*/
          115  +/* unsigned chain_length = s->max_chain_length;				*/
          116  +/* if (s->prev_length >= s->good_match) {				*/
          117  +/*     chain_length >>= 2;						*/
          118  +/* }									*/
          119  + 
          120  +		movl	dsPrevLen(%edx), %eax
          121  +		movl	dsGoodMatch(%edx), %ebx
          122  +		cmpl	%ebx, %eax
          123  +		movl	dsWMask(%edx), %eax
          124  +		movl	dsMaxChainLen(%edx), %ebx
          125  +		jl	LastMatchGood
          126  +		shrl	$2, %ebx
          127  +LastMatchGood:
          128  +
          129  +/* chainlen is decremented once beforehand so that the function can	*/
          130  +/* use the sign flag instead of the zero flag for the exit test.	*/
          131  +/* It is then shifted into the high word, to make room for the wmask	*/
          132  +/* value, which it will always accompany.				*/
          133  +
          134  +		decl	%ebx
          135  +		shll	$16, %ebx
          136  +		orl	%eax, %ebx
          137  +		movl	%ebx, chainlenwmask(%esp)
          138  +
          139  +/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;	*/
          140  +
          141  +		movl	dsNiceMatch(%edx), %eax
          142  +		movl	dsLookahead(%edx), %ebx
          143  +		cmpl	%eax, %ebx
          144  +		jl	LookaheadLess
          145  +		movl	%eax, %ebx
          146  +LookaheadLess:	movl	%ebx, nicematch(%esp)
          147  +
          148  +/* register Bytef *scan = s->window + s->strstart;			*/
          149  +
          150  +		movl	dsWindow(%edx), %esi
          151  +		movl	%esi, window(%esp)
          152  +		movl	dsStrStart(%edx), %ebp
          153  +		lea	(%esi,%ebp), %edi
          154  +		movl	%edi, scan(%esp)
          155  +
          156  +/* Determine how many bytes the scan ptr is off from being		*/
          157  +/* dword-aligned.							*/
          158  +
          159  +		movl	%edi, %eax
          160  +		negl	%eax
          161  +		andl	$3, %eax
          162  +		movl	%eax, scanalign(%esp)
          163  +
          164  +/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ?			*/
          165  +/*     s->strstart - (IPos)MAX_DIST(s) : NIL;				*/
          166  +
          167  +		movl	dsWSize(%edx), %eax
          168  +		subl	$MIN_LOOKAHEAD, %eax
          169  +		subl	%eax, %ebp
          170  +		jg	LimitPositive
          171  +		xorl	%ebp, %ebp
          172  +LimitPositive:
          173  +
          174  +/* int best_len = s->prev_length;					*/
          175  +
          176  +		movl	dsPrevLen(%edx), %eax
          177  +		movl	%eax, bestlen(%esp)
          178  +
          179  +/* Store the sum of s->window + best_len in %esi locally, and in %esi.	*/
          180  +
          181  +		addl	%eax, %esi
          182  +		movl	%esi, windowbestlen(%esp)
          183  +
          184  +/* register ush scan_start = *(ushf*)scan;				*/
          185  +/* register ush scan_end   = *(ushf*)(scan+best_len-1);			*/
          186  +/* Posf *prev = s->prev;						*/
          187  +
          188  +		movzwl	(%edi), %ebx
          189  +		movl	%ebx, scanstart(%esp)
          190  +		movzwl	-1(%edi,%eax), %ebx
          191  +		movl	%ebx, scanend(%esp)
          192  +		movl	dsPrev(%edx), %edi
          193  +
          194  +/* Jump into the main loop.						*/
          195  +
          196  +		movl	chainlenwmask(%esp), %edx
          197  +		jmp	LoopEntry
          198  +
          199  +.balign 16
          200  +
          201  +/* do {
          202  + *     match = s->window + cur_match;
          203  + *     if (*(ushf*)(match+best_len-1) != scan_end ||
          204  + *         *(ushf*)match != scan_start) continue;
          205  + *     [...]
          206  + * } while ((cur_match = prev[cur_match & wmask]) > limit
          207  + *          && --chain_length != 0);
          208  + *
          209  + * Here is the inner loop of the function. The function will spend the
          210  + * majority of its time in this loop, and majority of that time will
          211  + * be spent in the first ten instructions.
          212  + *
          213  + * Within this loop:
          214  + * %ebx = scanend
          215  + * %ecx = curmatch
          216  + * %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
          217  + * %esi = windowbestlen - i.e., (window + bestlen)
          218  + * %edi = prev
          219  + * %ebp = limit
          220  + */
          221  +LookupLoop:
          222  +		andl	%edx, %ecx
          223  +		movzwl	(%edi,%ecx,2), %ecx
          224  +		cmpl	%ebp, %ecx
          225  +		jbe	LeaveNow
          226  +		subl	$0x00010000, %edx
          227  +		js	LeaveNow
          228  +LoopEntry:	movzwl	-1(%esi,%ecx), %eax
          229  +		cmpl	%ebx, %eax
          230  +		jnz	LookupLoop
          231  +		movl	window(%esp), %eax
          232  +		movzwl	(%eax,%ecx), %eax
          233  +		cmpl	scanstart(%esp), %eax
          234  +		jnz	LookupLoop
          235  +
          236  +/* Store the current value of chainlen.					*/
          237  +
          238  +		movl	%edx, chainlenwmask(%esp)
          239  +
          240  +/* Point %edi to the string under scrutiny, and %esi to the string we	*/
          241  +/* are hoping to match it up with. In actuality, %esi and %edi are	*/
          242  +/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is	*/
          243  +/* initialized to -(MAX_MATCH_8 - scanalign).				*/
          244  +
          245  +		movl	window(%esp), %esi
          246  +		movl	scan(%esp), %edi
          247  +		addl	%ecx, %esi
          248  +		movl	scanalign(%esp), %eax
          249  +		movl	$(-MAX_MATCH_8), %edx
          250  +		lea	MAX_MATCH_8(%edi,%eax), %edi
          251  +		lea	MAX_MATCH_8(%esi,%eax), %esi
          252  +
          253  +/* Test the strings for equality, 8 bytes at a time. At the end,
          254  + * adjust %edx so that it is offset to the exact byte that mismatched.
          255  + *
          256  + * We already know at this point that the first three bytes of the
          257  + * strings match each other, and they can be safely passed over before
          258  + * starting the compare loop. So what this code does is skip over 0-3
          259  + * bytes, as much as necessary in order to dword-align the %edi
          260  + * pointer. (%esi will still be misaligned three times out of four.)
          261  + *
          262  + * It should be confessed that this loop usually does not represent
          263  + * much of the total running time. Replacing it with a more
          264  + * straightforward "rep cmpsb" would not drastically degrade
          265  + * performance.
          266  + */
          267  +LoopCmps:
          268  +		movl	(%esi,%edx), %eax
          269  +		xorl	(%edi,%edx), %eax
          270  +		jnz	LeaveLoopCmps
          271  +		movl	4(%esi,%edx), %eax
          272  +		xorl	4(%edi,%edx), %eax
          273  +		jnz	LeaveLoopCmps4
          274  +		addl	$8, %edx
          275  +		jnz	LoopCmps
          276  +		jmp	LenMaximum
          277  +LeaveLoopCmps4:	addl	$4, %edx
          278  +LeaveLoopCmps:	testl	$0x0000FFFF, %eax
          279  +		jnz	LenLower
          280  +		addl	$2, %edx
          281  +		shrl	$16, %eax
          282  +LenLower:	subb	$1, %al
          283  +		adcl	$0, %edx
          284  +
          285  +/* Calculate the length of the match. If it is longer than MAX_MATCH,	*/
          286  +/* then automatically accept it as the best possible match and leave.	*/
          287  +
          288  +		lea	(%edi,%edx), %eax
          289  +		movl	scan(%esp), %edi
          290  +		subl	%edi, %eax
          291  +		cmpl	$MAX_MATCH, %eax
          292  +		jge	LenMaximum
          293  +
          294  +/* If the length of the match is not longer than the best match we	*/
          295  +/* have so far, then forget it and return to the lookup loop.		*/
          296  +
          297  +		movl	deflatestate(%esp), %edx
          298  +		movl	bestlen(%esp), %ebx
          299  +		cmpl	%ebx, %eax
          300  +		jg	LongerMatch
          301  +		movl	windowbestlen(%esp), %esi
          302  +		movl	dsPrev(%edx), %edi
          303  +		movl	scanend(%esp), %ebx
          304  +		movl	chainlenwmask(%esp), %edx
          305  +		jmp	LookupLoop
          306  +
          307  +/*         s->match_start = cur_match;					*/
          308  +/*         best_len = len;						*/
          309  +/*         if (len >= nice_match) break;				*/
          310  +/*         scan_end = *(ushf*)(scan+best_len-1);			*/
          311  +
          312  +LongerMatch:	movl	nicematch(%esp), %ebx
          313  +		movl	%eax, bestlen(%esp)
          314  +		movl	%ecx, dsMatchStart(%edx)
          315  +		cmpl	%ebx, %eax
          316  +		jge	LeaveNow
          317  +		movl	window(%esp), %esi
          318  +		addl	%eax, %esi
          319  +		movl	%esi, windowbestlen(%esp)
          320  +		movzwl	-1(%edi,%eax), %ebx
          321  +		movl	dsPrev(%edx), %edi
          322  +		movl	%ebx, scanend(%esp)
          323  +		movl	chainlenwmask(%esp), %edx
          324  +		jmp	LookupLoop
          325  +
          326  +/* Accept the current string, with the maximum possible length.		*/
          327  +
          328  +LenMaximum:	movl	deflatestate(%esp), %edx
          329  +		movl	$MAX_MATCH, bestlen(%esp)
          330  +		movl	%ecx, dsMatchStart(%edx)
          331  +
          332  +/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len;		*/
          333  +/* return s->lookahead;							*/
          334  +
          335  +LeaveNow:
          336  +		movl	deflatestate(%esp), %edx
          337  +		movl	bestlen(%esp), %ebx
          338  +		movl	dsLookahead(%edx), %eax
          339  +		cmpl	%eax, %ebx
          340  +		jg	LookaheadRet
          341  +		movl	%ebx, %eax
          342  +LookaheadRet:
          343  +
          344  +/* Restore the stack and return from whence we came.			*/
          345  +
          346  +		addl	$LocalVarsSize, %esp
          347  +		.cfi_def_cfa_offset 20
          348  +		popl	%ebx
          349  +		.cfi_def_cfa_offset 16
          350  +		popl	%esi
          351  +		.cfi_def_cfa_offset 12
          352  +		popl	%edi
          353  +		.cfi_def_cfa_offset 8
          354  +		popl	%ebp
          355  +		.cfi_def_cfa_offset 4
          356  +.cfi_endproc
          357  +match_init:	ret

Added compat/zlib/contrib/blast/Makefile.

            1  +blast: blast.c blast.h
            2  +	cc -DTEST -o blast blast.c
            3  +
            4  +test: blast
            5  +	blast < test.pk | cmp - test.txt
            6  +
            7  +clean:
            8  +	rm -f blast blast.o

Added compat/zlib/contrib/blast/README.

            1  +Read blast.h for purpose and usage.
            2  +
            3  +Mark Adler
            4  +madler@alumni.caltech.edu

Added compat/zlib/contrib/blast/blast.c.

            1  +/* blast.c
            2  + * Copyright (C) 2003 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in blast.h
            4  + * version 1.1, 16 Feb 2003
            5  + *
            6  + * blast.c decompresses data compressed by the PKWare Compression Library.
            7  + * This function provides functionality similar to the explode() function of
            8  + * the PKWare library, hence the name "blast".
            9  + *
           10  + * This decompressor is based on the excellent format description provided by
           11  + * Ben Rudiak-Gould in comp.compression on August 13, 2001.  Interestingly, the
           12  + * example Ben provided in the post is incorrect.  The distance 110001 should
           13  + * instead be 111000.  When corrected, the example byte stream becomes:
           14  + *
           15  + *    00 04 82 24 25 8f 80 7f
           16  + *
           17  + * which decompresses to "AIAIAIAIAIAIA" (without the quotes).
           18  + */
           19  +
           20  +/*
           21  + * Change history:
           22  + *
           23  + * 1.0  12 Feb 2003     - First version
           24  + * 1.1  16 Feb 2003     - Fixed distance check for > 4 GB uncompressed data
           25  + */
           26  +
           27  +#include <setjmp.h>             /* for setjmp(), longjmp(), and jmp_buf */
           28  +#include "blast.h"              /* prototype for blast() */
           29  +
           30  +#define local static            /* for local function definitions */
           31  +#define MAXBITS 13              /* maximum code length */
           32  +#define MAXWIN 4096             /* maximum window size */
           33  +
           34  +/* input and output state */
           35  +struct state {
           36  +    /* input state */
           37  +    blast_in infun;             /* input function provided by user */
           38  +    void *inhow;                /* opaque information passed to infun() */
           39  +    unsigned char *in;          /* next input location */
           40  +    unsigned left;              /* available input at in */
           41  +    int bitbuf;                 /* bit buffer */
           42  +    int bitcnt;                 /* number of bits in bit buffer */
           43  +
           44  +    /* input limit error return state for bits() and decode() */
           45  +    jmp_buf env;
           46  +
           47  +    /* output state */
           48  +    blast_out outfun;           /* output function provided by user */
           49  +    void *outhow;               /* opaque information passed to outfun() */
           50  +    unsigned next;              /* index of next write location in out[] */
           51  +    int first;                  /* true to check distances (for first 4K) */
           52  +    unsigned char out[MAXWIN];  /* output buffer and sliding window */
           53  +};
           54  +
           55  +/*
           56  + * Return need bits from the input stream.  This always leaves less than
           57  + * eight bits in the buffer.  bits() works properly for need == 0.
           58  + *
           59  + * Format notes:
           60  + *
           61  + * - Bits are stored in bytes from the least significant bit to the most
           62  + *   significant bit.  Therefore bits are dropped from the bottom of the bit
           63  + *   buffer, using shift right, and new bytes are appended to the top of the
           64  + *   bit buffer, using shift left.
           65  + */
           66  +local int bits(struct state *s, int need)
           67  +{
           68  +    int val;            /* bit accumulator */
           69  +
           70  +    /* load at least need bits into val */
           71  +    val = s->bitbuf;
           72  +    while (s->bitcnt < need) {
           73  +        if (s->left == 0) {
           74  +            s->left = s->infun(s->inhow, &(s->in));
           75  +            if (s->left == 0) longjmp(s->env, 1);       /* out of input */
           76  +        }
           77  +        val |= (int)(*(s->in)++) << s->bitcnt;          /* load eight bits */
           78  +        s->left--;
           79  +        s->bitcnt += 8;
           80  +    }
           81  +
           82  +    /* drop need bits and update buffer, always zero to seven bits left */
           83  +    s->bitbuf = val >> need;
           84  +    s->bitcnt -= need;
           85  +
           86  +    /* return need bits, zeroing the bits above that */
           87  +    return val & ((1 << need) - 1);
           88  +}
           89  +
           90  +/*
           91  + * Huffman code decoding tables.  count[1..MAXBITS] is the number of symbols of
           92  + * each length, which for a canonical code are stepped through in order.
           93  + * symbol[] are the symbol values in canonical order, where the number of
           94  + * entries is the sum of the counts in count[].  The decoding process can be
           95  + * seen in the function decode() below.
           96  + */
           97  +struct huffman {
           98  +    short *count;       /* number of symbols of each length */
           99  +    short *symbol;      /* canonically ordered symbols */
          100  +};
          101  +
          102  +/*
          103  + * Decode a code from the stream s using huffman table h.  Return the symbol or
          104  + * a negative value if there is an error.  If all of the lengths are zero, i.e.
          105  + * an empty code, or if the code is incomplete and an invalid code is received,
          106  + * then -9 is returned after reading MAXBITS bits.
          107  + *
          108  + * Format notes:
          109  + *
          110  + * - The codes as stored in the compressed data are bit-reversed relative to
          111  + *   a simple integer ordering of codes of the same lengths.  Hence below the
          112  + *   bits are pulled from the compressed data one at a time and used to
          113  + *   build the code value reversed from what is in the stream in order to
          114  + *   permit simple integer comparisons for decoding.
          115  + *
          116  + * - The first code for the shortest length is all ones.  Subsequent codes of
          117  + *   the same length are simply integer decrements of the previous code.  When
          118  + *   moving up a length, a one bit is appended to the code.  For a complete
          119  + *   code, the last code of the longest length will be all zeros.  To support
          120  + *   this ordering, the bits pulled during decoding are inverted to apply the
          121  + *   more "natural" ordering starting with all zeros and incrementing.
          122  + */
          123  +local int decode(struct state *s, struct huffman *h)
          124  +{
          125  +    int len;            /* current number of bits in code */
          126  +    int code;           /* len bits being decoded */
          127  +    int first;          /* first code of length len */
          128  +    int count;          /* number of codes of length len */
          129  +    int index;          /* index of first code of length len in symbol table */
          130  +    int bitbuf;         /* bits from stream */
          131  +    int left;           /* bits left in next or left to process */
          132  +    short *next;        /* next number of codes */
          133  +
          134  +    bitbuf = s->bitbuf;
          135  +    left = s->bitcnt;
          136  +    code = first = index = 0;
          137  +    len = 1;
          138  +    next = h->count + 1;
          139  +    while (1) {
          140  +        while (left--) {
          141  +            code |= (bitbuf & 1) ^ 1;   /* invert code */
          142  +            bitbuf >>= 1;
          143  +            count = *next++;
          144  +            if (code < first + count) { /* if length len, return symbol */
          145  +                s->bitbuf = bitbuf;
          146  +                s->bitcnt = (s->bitcnt - len) & 7;
          147  +                return h->symbol[index + (code - first)];
          148  +            }
          149  +            index += count;             /* else update for next length */
          150  +            first += count;
          151  +            first <<= 1;
          152  +            code <<= 1;
          153  +            len++;
          154  +        }
          155  +        left = (MAXBITS+1) - len;
          156  +        if (left == 0) break;
          157  +        if (s->left == 0) {
          158  +            s->left = s->infun(s->inhow, &(s->in));
          159  +            if (s->left == 0) longjmp(s->env, 1);       /* out of input */
          160  +        }
          161  +        bitbuf = *(s->in)++;
          162  +        s->left--;
          163  +        if (left > 8) left = 8;
          164  +    }
          165  +    return -9;                          /* ran out of codes */
          166  +}
          167  +
          168  +/*
          169  + * Given a list of repeated code lengths rep[0..n-1], where each byte is a
          170  + * count (high four bits + 1) and a code length (low four bits), generate the
          171  + * list of code lengths.  This compaction reduces the size of the object code.
          172  + * Then given the list of code lengths length[0..n-1] representing a canonical
          173  + * Huffman code for n symbols, construct the tables required to decode those
          174  + * codes.  Those tables are the number of codes of each length, and the symbols
          175  + * sorted by length, retaining their original order within each length.  The
          176  + * return value is zero for a complete code set, negative for an over-
          177  + * subscribed code set, and positive for an incomplete code set.  The tables
          178  + * can be used if the return value is zero or positive, but they cannot be used
          179  + * if the return value is negative.  If the return value is zero, it is not
          180  + * possible for decode() using that table to return an error--any stream of
          181  + * enough bits will resolve to a symbol.  If the return value is positive, then
          182  + * it is possible for decode() using that table to return an error for received
          183  + * codes past the end of the incomplete lengths.
          184  + */
          185  +local int construct(struct huffman *h, const unsigned char *rep, int n)
          186  +{
          187  +    int symbol;         /* current symbol when stepping through length[] */
          188  +    int len;            /* current length when stepping through h->count[] */
          189  +    int left;           /* number of possible codes left of current length */
          190  +    short offs[MAXBITS+1];      /* offsets in symbol table for each length */
          191  +    short length[256];  /* code lengths */
          192  +
          193  +    /* convert compact repeat counts into symbol bit length list */
          194  +    symbol = 0;
          195  +    do {
          196  +        len = *rep++;
          197  +        left = (len >> 4) + 1;
          198  +        len &= 15;
          199  +        do {
          200  +            length[symbol++] = len;
          201  +        } while (--left);
          202  +    } while (--n);
          203  +    n = symbol;
          204  +
          205  +    /* count number of codes of each length */
          206  +    for (len = 0; len <= MAXBITS; len++)
          207  +        h->count[len] = 0;
          208  +    for (symbol = 0; symbol < n; symbol++)
          209  +        (h->count[length[symbol]])++;   /* assumes lengths are within bounds */
          210  +    if (h->count[0] == n)               /* no codes! */
          211  +        return 0;                       /* complete, but decode() will fail */
          212  +
          213  +    /* check for an over-subscribed or incomplete set of lengths */
          214  +    left = 1;                           /* one possible code of zero length */
          215  +    for (len = 1; len <= MAXBITS; len++) {
          216  +        left <<= 1;                     /* one more bit, double codes left */
          217  +        left -= h->count[len];          /* deduct count from possible codes */
          218  +        if (left < 0) return left;      /* over-subscribed--return negative */
          219  +    }                                   /* left > 0 means incomplete */
          220  +
          221  +    /* generate offsets into symbol table for each length for sorting */
          222  +    offs[1] = 0;
          223  +    for (len = 1; len < MAXBITS; len++)
          224  +        offs[len + 1] = offs[len] + h->count[len];
          225  +
          226  +    /*
          227  +     * put symbols in table sorted by length, by symbol order within each
          228  +     * length
          229  +     */
          230  +    for (symbol = 0; symbol < n; symbol++)
          231  +        if (length[symbol] != 0)
          232  +            h->symbol[offs[length[symbol]]++] = symbol;
          233  +
          234  +    /* return zero for complete set, positive for incomplete set */
          235  +    return left;
          236  +}
          237  +
          238  +/*
          239  + * Decode PKWare Compression Library stream.
          240  + *
          241  + * Format notes:
          242  + *
          243  + * - First byte is 0 if literals are uncoded or 1 if they are coded.  Second
          244  + *   byte is 4, 5, or 6 for the number of extra bits in the distance code.
          245  + *   This is the base-2 logarithm of the dictionary size minus six.
          246  + *
          247  + * - Compressed data is a combination of literals and length/distance pairs
          248  + *   terminated by an end code.  Literals are either Huffman coded or
          249  + *   uncoded bytes.  A length/distance pair is a coded length followed by a
          250  + *   coded distance to represent a string that occurs earlier in the
          251  + *   uncompressed data that occurs again at the current location.
          252  + *
          253  + * - A bit preceding a literal or length/distance pair indicates which comes
          254  + *   next, 0 for literals, 1 for length/distance.
          255  + *
          256  + * - If literals are uncoded, then the next eight bits are the literal, in the
          257  + *   normal bit order in th stream, i.e. no bit-reversal is needed. Similarly,
          258  + *   no bit reversal is needed for either the length extra bits or the distance
          259  + *   extra bits.
          260  + *
          261  + * - Literal bytes are simply written to the output.  A length/distance pair is
          262  + *   an instruction to copy previously uncompressed bytes to the output.  The
          263  + *   copy is from distance bytes back in the output stream, copying for length
          264  + *   bytes.
          265  + *
          266  + * - Distances pointing before the beginning of the output data are not
          267  + *   permitted.
          268  + *
          269  + * - Overlapped copies, where the length is greater than the distance, are
          270  + *   allowed and common.  For example, a distance of one and a length of 518
          271  + *   simply copies the last byte 518 times.  A distance of four and a length of
          272  + *   twelve copies the last four bytes three times.  A simple forward copy
          273  + *   ignoring whether the length is greater than the distance or not implements
          274  + *   this correctly.
          275  + */
          276  +local int decomp(struct state *s)
          277  +{
          278  +    int lit;            /* true if literals are coded */
          279  +    int dict;           /* log2(dictionary size) - 6 */
          280  +    int symbol;         /* decoded symbol, extra bits for distance */
          281  +    int len;            /* length for copy */
          282  +    int dist;           /* distance for copy */
          283  +    int copy;           /* copy counter */
          284  +    unsigned char *from, *to;   /* copy pointers */
          285  +    static int virgin = 1;                              /* build tables once */
          286  +    static short litcnt[MAXBITS+1], litsym[256];        /* litcode memory */
          287  +    static short lencnt[MAXBITS+1], lensym[16];         /* lencode memory */
          288  +    static short distcnt[MAXBITS+1], distsym[64];       /* distcode memory */
          289  +    static struct huffman litcode = {litcnt, litsym};   /* length code */
          290  +    static struct huffman lencode = {lencnt, lensym};   /* length code */
          291  +    static struct huffman distcode = {distcnt, distsym};/* distance code */
          292  +        /* bit lengths of literal codes */
          293  +    static const unsigned char litlen[] = {
          294  +        11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,
          295  +        9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
          296  +        7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
          297  +        8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
          298  +        44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,
          299  +        44, 173};
          300  +        /* bit lengths of length codes 0..15 */
          301  +    static const unsigned char lenlen[] = {2, 35, 36, 53, 38, 23};
          302  +        /* bit lengths of distance codes 0..63 */
          303  +    static const unsigned char distlen[] = {2, 20, 53, 230, 247, 151, 248};
          304  +    static const short base[16] = {     /* base for length codes */
          305  +        3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264};
          306  +    static const char extra[16] = {     /* extra bits for length codes */
          307  +        0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8};
          308  +
          309  +    /* set up decoding tables (once--might not be thread-safe) */
          310  +    if (virgin) {
          311  +        construct(&litcode, litlen, sizeof(litlen));
          312  +        construct(&lencode, lenlen, sizeof(lenlen));
          313  +        construct(&distcode, distlen, sizeof(distlen));
          314  +        virgin = 0;
          315  +    }
          316  +
          317  +    /* read header */
          318  +    lit = bits(s, 8);
          319  +    if (lit > 1) return -1;
          320  +    dict = bits(s, 8);
          321  +    if (dict < 4 || dict > 6) return -2;
          322  +
          323  +    /* decode literals and length/distance pairs */
          324  +    do {
          325  +        if (bits(s, 1)) {
          326  +            /* get length */
          327  +            symbol = decode(s, &lencode);
          328  +            len = base[symbol] + bits(s, extra[symbol]);
          329  +            if (len == 519) break;              /* end code */
          330  +
          331  +            /* get distance */
          332  +            symbol = len == 2 ? 2 : dict;
          333  +            dist = decode(s, &distcode) << symbol;
          334  +            dist += bits(s, symbol);
          335  +            dist++;
          336  +            if (s->first && dist > s->next)
          337  +                return -3;              /* distance too far back */
          338  +
          339  +            /* copy length bytes from distance bytes back */
          340  +            do {
          341  +                to = s->out + s->next;
          342  +                from = to - dist;
          343  +                copy = MAXWIN;
          344  +                if (s->next < dist) {
          345  +                    from += copy;
          346  +                    copy = dist;
          347  +                }
          348  +                copy -= s->next;
          349  +                if (copy > len) copy = len;
          350  +                len -= copy;
          351  +                s->next += copy;
          352  +                do {
          353  +                    *to++ = *from++;
          354  +                } while (--copy);
          355  +                if (s->next == MAXWIN) {
          356  +                    if (s->outfun(s->outhow, s->out, s->next)) return 1;
          357  +                    s->next = 0;
          358  +                    s->first = 0;
          359  +                }
          360  +            } while (len != 0);
          361  +        }
          362  +        else {
          363  +            /* get literal and write it */
          364  +            symbol = lit ? decode(s, &litcode) : bits(s, 8);
          365  +            s->out[s->next++] = symbol;
          366  +            if (s->next == MAXWIN) {
          367  +                if (s->outfun(s->outhow, s->out, s->next)) return 1;
          368  +                s->next = 0;
          369  +                s->first = 0;
          370  +            }
          371  +        }
          372  +    } while (1);
          373  +    return 0;
          374  +}
          375  +
          376  +/* See comments in blast.h */
          377  +int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow)
          378  +{
          379  +    struct state s;             /* input/output state */
          380  +    int err;                    /* return value */
          381  +
          382  +    /* initialize input state */
          383  +    s.infun = infun;
          384  +    s.inhow = inhow;
          385  +    s.left = 0;
          386  +    s.bitbuf = 0;
          387  +    s.bitcnt = 0;
          388  +
          389  +    /* initialize output state */
          390  +    s.outfun = outfun;
          391  +    s.outhow = outhow;
          392  +    s.next = 0;
          393  +    s.first = 1;
          394  +
          395  +    /* return if bits() or decode() tries to read past available input */
          396  +    if (setjmp(s.env) != 0)             /* if came back here via longjmp(), */
          397  +        err = 2;                        /*  then skip decomp(), return error */
          398  +    else
          399  +        err = decomp(&s);               /* decompress */
          400  +
          401  +    /* write any leftover output and update the error code if needed */
          402  +    if (err != 1 && s.next && s.outfun(s.outhow, s.out, s.next) && err == 0)
          403  +        err = 1;
          404  +    return err;
          405  +}
          406  +
          407  +#ifdef TEST
          408  +/* Example of how to use blast() */
          409  +#include <stdio.h>
          410  +#include <stdlib.h>
          411  +
          412  +#define CHUNK 16384
          413  +
          414  +local unsigned inf(void *how, unsigned char **buf)
          415  +{
          416  +    static unsigned char hold[CHUNK];
          417  +
          418  +    *buf = hold;
          419  +    return fread(hold, 1, CHUNK, (FILE *)how);
          420  +}
          421  +
          422  +local int outf(void *how, unsigned char *buf, unsigned len)
          423  +{
          424  +    return fwrite(buf, 1, len, (FILE *)how) != len;
          425  +}
          426  +
          427  +/* Decompress a PKWare Compression Library stream from stdin to stdout */
          428  +int main(void)
          429  +{
          430  +    int ret, n;
          431  +
          432  +    /* decompress to stdout */
          433  +    ret = blast(inf, stdin, outf, stdout);
          434  +    if (ret != 0) fprintf(stderr, "blast error: %d\n", ret);
          435  +
          436  +    /* see if there are any leftover bytes */
          437  +    n = 0;
          438  +    while (getchar() != EOF) n++;
          439  +    if (n) fprintf(stderr, "blast warning: %d unused bytes of input\n", n);
          440  +
          441  +    /* return blast() error code */
          442  +    return ret;
          443  +}
          444  +#endif

Added compat/zlib/contrib/blast/blast.h.

            1  +/* blast.h -- interface for blast.c
            2  +  Copyright (C) 2003 Mark Adler
            3  +  version 1.1, 16 Feb 2003
            4  +
            5  +  This software is provided 'as-is', without any express or implied
            6  +  warranty.  In no event will the author be held liable for any damages
            7  +  arising from the use of this software.
            8  +
            9  +  Permission is granted to anyone to use this software for any purpose,
           10  +  including commercial applications, and to alter it and redistribute it
           11  +  freely, subject to the following restrictions:
           12  +
           13  +  1. The origin of this software must not be misrepresented; you must not
           14  +     claim that you wrote the original software. If you use this software
           15  +     in a product, an acknowledgment in the product documentation would be
           16  +     appreciated but is not required.
           17  +  2. Altered source versions must be plainly marked as such, and must not be
           18  +     misrepresented as being the original software.
           19  +  3. This notice may not be removed or altered from any source distribution.
           20  +
           21  +  Mark Adler    madler@alumni.caltech.edu
           22  + */
           23  +
           24  +
           25  +/*
           26  + * blast() decompresses the PKWare Data Compression Library (DCL) compressed
           27  + * format.  It provides the same functionality as the explode() function in
           28  + * that library.  (Note: PKWare overused the "implode" verb, and the format
           29  + * used by their library implode() function is completely different and
           30  + * incompatible with the implode compression method supported by PKZIP.)
           31  + */
           32  +
           33  +
           34  +typedef unsigned (*blast_in)(void *how, unsigned char **buf);
           35  +typedef int (*blast_out)(void *how, unsigned char *buf, unsigned len);
           36  +/* Definitions for input/output functions passed to blast().  See below for
           37  + * what the provided functions need to do.
           38  + */
           39  +
           40  +
           41  +int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow);
           42  +/* Decompress input to output using the provided infun() and outfun() calls.
           43  + * On success, the return value of blast() is zero.  If there is an error in
           44  + * the source data, i.e. it is not in the proper format, then a negative value
           45  + * is returned.  If there is not enough input available or there is not enough
           46  + * output space, then a positive error is returned.
           47  + *
           48  + * The input function is invoked: len = infun(how, &buf), where buf is set by
           49  + * infun() to point to the input buffer, and infun() returns the number of
           50  + * available bytes there.  If infun() returns zero, then blast() returns with
           51  + * an input error.  (blast() only asks for input if it needs it.)  inhow is for
           52  + * use by the application to pass an input descriptor to infun(), if desired.
           53  + *
           54  + * The output function is invoked: err = outfun(how, buf, len), where the bytes
           55  + * to be written are buf[0..len-1].  If err is not zero, then blast() returns
           56  + * with an output error.  outfun() is always called with len <= 4096.  outhow
           57  + * is for use by the application to pass an output descriptor to outfun(), if
           58  + * desired.
           59  + *
           60  + * The return codes are:
           61  + *
           62  + *   2:  ran out of input before completing decompression
           63  + *   1:  output error before completing decompression
           64  + *   0:  successful decompression
           65  + *  -1:  literal flag not zero or one
           66  + *  -2:  dictionary size not in 4..6
           67  + *  -3:  distance is too far back
           68  + *
           69  + * At the bottom of blast.c is an example program that uses blast() that can be
           70  + * compiled to produce a command-line decompression filter by defining TEST.
           71  + */

Added compat/zlib/contrib/blast/test.pk.

cannot compute difference between binary files

Added compat/zlib/contrib/blast/test.txt.

            1  +AIAIAIAIAIAIA

Added compat/zlib/contrib/delphi/ZLib.pas.

            1  +{*******************************************************}
            2  +{                                                       }
            3  +{       Borland Delphi Supplemental Components          }
            4  +{       ZLIB Data Compression Interface Unit            }
            5  +{                                                       }
            6  +{       Copyright (c) 1997,99 Borland Corporation       }
            7  +{                                                       }
            8  +{*******************************************************}
            9  +
           10  +{ Updated for zlib 1.2.x by Cosmin Truta <cosmint@cs.ubbcluj.ro> }
           11  +
           12  +unit ZLib;
           13  +
           14  +interface
           15  +
           16  +uses SysUtils, Classes;
           17  +
           18  +type
           19  +  TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
           20  +  TFree = procedure (AppData, Block: Pointer); cdecl;
           21  +
           22  +  // Internal structure.  Ignore.
           23  +  TZStreamRec = packed record
           24  +    next_in: PChar;       // next input byte
           25  +    avail_in: Integer;    // number of bytes available at next_in
           26  +    total_in: Longint;    // total nb of input bytes read so far
           27  +
           28  +    next_out: PChar;      // next output byte should be put here
           29  +    avail_out: Integer;   // remaining free space at next_out
           30  +    total_out: Longint;   // total nb of bytes output so far
           31  +
           32  +    msg: PChar;           // last error message, NULL if no error
           33  +    internal: Pointer;    // not visible by applications
           34  +
           35  +    zalloc: TAlloc;       // used to allocate the internal state
           36  +    zfree: TFree;         // used to free the internal state
           37  +    AppData: Pointer;     // private data object passed to zalloc and zfree
           38  +
           39  +    data_type: Integer;   // best guess about the data type: ascii or binary
           40  +    adler: Longint;       // adler32 value of the uncompressed data
           41  +    reserved: Longint;    // reserved for future use
           42  +  end;
           43  +
           44  +  // Abstract ancestor class
           45  +  TCustomZlibStream = class(TStream)
           46  +  private
           47  +    FStrm: TStream;
           48  +    FStrmPos: Integer;
           49  +    FOnProgress: TNotifyEvent;
           50  +    FZRec: TZStreamRec;
           51  +    FBuffer: array [Word] of Char;
           52  +  protected
           53  +    procedure Progress(Sender: TObject); dynamic;
           54  +    property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
           55  +    constructor Create(Strm: TStream);
           56  +  end;
           57  +
           58  +{ TCompressionStream compresses data on the fly as data is written to it, and
           59  +  stores the compressed data to another stream.
           60  +
           61  +  TCompressionStream is write-only and strictly sequential. Reading from the
           62  +  stream will raise an exception. Using Seek to move the stream pointer
           63  +  will raise an exception.
           64  +
           65  +  Output data is cached internally, written to the output stream only when
           66  +  the internal output buffer is full.  All pending output data is flushed
           67  +  when the stream is destroyed.
           68  +
           69  +  The Position property returns the number of uncompressed bytes of
           70  +  data that have been written to the stream so far.
           71  +
           72  +  CompressionRate returns the on-the-fly percentage by which the original
           73  +  data has been compressed:  (1 - (CompressedBytes / UncompressedBytes)) * 100
           74  +  If raw data size = 100 and compressed data size = 25, the CompressionRate
           75  +  is 75%
           76  +
           77  +  The OnProgress event is called each time the output buffer is filled and
           78  +  written to the output stream.  This is useful for updating a progress
           79  +  indicator when you are writing a large chunk of data to the compression
           80  +  stream in a single call.}
           81  +
           82  +
           83  +  TCompressionLevel = (clNone, clFastest, clDefault, clMax);
           84  +
           85  +  TCompressionStream = class(TCustomZlibStream)
           86  +  private
           87  +    function GetCompressionRate: Single;
           88  +  public
           89  +    constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
           90  +    destructor Destroy; override;
           91  +    function Read(var Buffer; Count: Longint): Longint; override;
           92  +    function Write(const Buffer; Count: Longint): Longint; override;
           93  +    function Seek(Offset: Longint; Origin: Word): Longint; override;
           94  +    property CompressionRate: Single read GetCompressionRate;
           95  +    property OnProgress;
           96  +  end;
           97  +
           98  +{ TDecompressionStream decompresses data on the fly as data is read from it.
           99  +
          100  +  Compressed data comes from a separate source stream.  TDecompressionStream
          101  +  is read-only and unidirectional; you can seek forward in the stream, but not
          102  +  backwards.  The special case of setting the stream position to zero is
          103  +  allowed.  Seeking forward decompresses data until the requested position in
          104  +  the uncompressed data has been reached.  Seeking backwards, seeking relative
          105  +  to the end of the stream, requesting the size of the stream, and writing to
          106  +  the stream will raise an exception.
          107  +
          108  +  The Position property returns the number of bytes of uncompressed data that
          109  +  have been read from the stream so far.
          110  +
          111  +  The OnProgress event is called each time the internal input buffer of
          112  +  compressed data is exhausted and the next block is read from the input stream.
          113  +  This is useful for updating a progress indicator when you are reading a
          114  +  large chunk of data from the decompression stream in a single call.}
          115  +
          116  +  TDecompressionStream = class(TCustomZlibStream)
          117  +  public
          118  +    constructor Create(Source: TStream);
          119  +    destructor Destroy; override;
          120  +    function Read(var Buffer; Count: Longint): Longint; override;
          121  +    function Write(const Buffer; Count: Longint): Longint; override;
          122  +    function Seek(Offset: Longint; Origin: Word): Longint; override;
          123  +    property OnProgress;
          124  +  end;
          125  +
          126  +
          127  +
          128  +{ CompressBuf compresses data, buffer to buffer, in one call.
          129  +   In: InBuf = ptr to compressed data
          130  +       InBytes = number of bytes in InBuf
          131  +  Out: OutBuf = ptr to newly allocated buffer containing decompressed data
          132  +       OutBytes = number of bytes in OutBuf   }
          133  +procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
          134  +                      out OutBuf: Pointer; out OutBytes: Integer);
          135  +
          136  +
          137  +{ DecompressBuf decompresses data, buffer to buffer, in one call.
          138  +   In: InBuf = ptr to compressed data
          139  +       InBytes = number of bytes in InBuf
          140  +       OutEstimate = zero, or est. size of the decompressed data
          141  +  Out: OutBuf = ptr to newly allocated buffer containing decompressed data
          142  +       OutBytes = number of bytes in OutBuf   }
          143  +procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
          144  + OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
          145  +
          146  +{ DecompressToUserBuf decompresses data, buffer to buffer, in one call.
          147  +   In: InBuf = ptr to compressed data
          148  +       InBytes = number of bytes in InBuf
          149  +  Out: OutBuf = ptr to user-allocated buffer to contain decompressed data
          150  +       BufSize = number of bytes in OutBuf   }
          151  +procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
          152  +  const OutBuf: Pointer; BufSize: Integer);
          153  +
          154  +const
          155  +  zlib_version = '1.2.7';
          156  +
          157  +type
          158  +  EZlibError = class(Exception);
          159  +  ECompressionError = class(EZlibError);
          160  +  EDecompressionError = class(EZlibError);
          161  +
          162  +implementation
          163  +
          164  +uses ZLibConst;
          165  +
          166  +const
          167  +  Z_NO_FLUSH      = 0;
          168  +  Z_PARTIAL_FLUSH = 1;
          169  +  Z_SYNC_FLUSH    = 2;
          170  +  Z_FULL_FLUSH    = 3;
          171  +  Z_FINISH        = 4;
          172  +
          173  +  Z_OK            = 0;
          174  +  Z_STREAM_END    = 1;
          175  +  Z_NEED_DICT     = 2;
          176  +  Z_ERRNO         = (-1);
          177  +  Z_STREAM_ERROR  = (-2);
          178  +  Z_DATA_ERROR    = (-3);
          179  +  Z_MEM_ERROR     = (-4);
          180  +  Z_BUF_ERROR     = (-5);
          181  +  Z_VERSION_ERROR = (-6);
          182  +
          183  +  Z_NO_COMPRESSION       =   0;
          184  +  Z_BEST_SPEED           =   1;
          185  +  Z_BEST_COMPRESSION     =   9;
          186  +  Z_DEFAULT_COMPRESSION  = (-1);
          187  +
          188  +  Z_FILTERED            = 1;
          189  +  Z_HUFFMAN_ONLY        = 2;
          190  +  Z_RLE                 = 3;
          191  +  Z_DEFAULT_STRATEGY    = 0;
          192  +
          193  +  Z_BINARY   = 0;
          194  +  Z_ASCII    = 1;
          195  +  Z_UNKNOWN  = 2;
          196  +
          197  +  Z_DEFLATED = 8;
          198  +
          199  +
          200  +{$L adler32.obj}
          201  +{$L compress.obj}
          202  +{$L crc32.obj}
          203  +{$L deflate.obj}
          204  +{$L infback.obj}
          205  +{$L inffast.obj}
          206  +{$L inflate.obj}
          207  +{$L inftrees.obj}
          208  +{$L trees.obj}
          209  +{$L uncompr.obj}
          210  +{$L zutil.obj}
          211  +
          212  +procedure adler32; external;
          213  +procedure compressBound; external;
          214  +procedure crc32; external;
          215  +procedure deflateInit2_; external;
          216  +procedure deflateParams; external;
          217  +
          218  +function _malloc(Size: Integer): Pointer; cdecl;
          219  +begin
          220  +  Result := AllocMem(Size);
          221  +end;
          222  +
          223  +procedure _free(Block: Pointer); cdecl;
          224  +begin
          225  +  FreeMem(Block);
          226  +end;
          227  +
          228  +procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
          229  +begin
          230  +  FillChar(P^, count, B);
          231  +end;
          232  +
          233  +procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
          234  +begin
          235  +  Move(source^, dest^, count);
          236  +end;
          237  +
          238  +
          239  +
          240  +// deflate compresses data
          241  +function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
          242  +  recsize: Integer): Integer; external;
          243  +function deflate(var strm: TZStreamRec; flush: Integer): Integer; external;
          244  +function deflateEnd(var strm: TZStreamRec): Integer; external;
          245  +
          246  +// inflate decompresses data
          247  +function inflateInit_(var strm: TZStreamRec; version: PChar;
          248  +  recsize: Integer): Integer; external;
          249  +function inflate(var strm: TZStreamRec; flush: Integer): Integer; external;
          250  +function inflateEnd(var strm: TZStreamRec): Integer; external;
          251  +function inflateReset(var strm: TZStreamRec): Integer; external;
          252  +
          253  +
          254  +function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
          255  +begin
          256  +//  GetMem(Result, Items*Size);
          257  +  Result := AllocMem(Items * Size);
          258  +end;
          259  +
          260  +procedure zlibFreeMem(AppData, Block: Pointer); cdecl;
          261  +begin
          262  +  FreeMem(Block);
          263  +end;
          264  +
          265  +{function zlibCheck(code: Integer): Integer;
          266  +begin
          267  +  Result := code;
          268  +  if code < 0 then
          269  +    raise EZlibError.Create('error');    //!!
          270  +end;}
          271  +
          272  +function CCheck(code: Integer): Integer;
          273  +begin
          274  +  Result := code;
          275  +  if code < 0 then
          276  +    raise ECompressionError.Create('error'); //!!
          277  +end;
          278  +
          279  +function DCheck(code: Integer): Integer;
          280  +begin
          281  +  Result := code;
          282  +  if code < 0 then
          283  +    raise EDecompressionError.Create('error');  //!!
          284  +end;
          285  +
          286  +procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
          287  +                      out OutBuf: Pointer; out OutBytes: Integer);
          288  +var
          289  +  strm: TZStreamRec;
          290  +  P: Pointer;
          291  +begin
          292  +  FillChar(strm, sizeof(strm), 0);
          293  +  strm.zalloc := zlibAllocMem;
          294  +  strm.zfree := zlibFreeMem;
          295  +  OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
          296  +  GetMem(OutBuf, OutBytes);
          297  +  try
          298  +    strm.next_in := InBuf;
          299  +    strm.avail_in := InBytes;
          300  +    strm.next_out := OutBuf;
          301  +    strm.avail_out := OutBytes;
          302  +    CCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlib_version, sizeof(strm)));
          303  +    try
          304  +      while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
          305  +      begin
          306  +        P := OutBuf;
          307  +        Inc(OutBytes, 256);
          308  +        ReallocMem(OutBuf, OutBytes);
          309  +        strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
          310  +        strm.avail_out := 256;
          311  +      end;
          312  +    finally
          313  +      CCheck(deflateEnd(strm));
          314  +    end;
          315  +    ReallocMem(OutBuf, strm.total_out);
          316  +    OutBytes := strm.total_out;
          317  +  except
          318  +    FreeMem(OutBuf);
          319  +    raise
          320  +  end;
          321  +end;
          322  +
          323  +
          324  +procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
          325  +  OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
          326  +var
          327  +  strm: TZStreamRec;
          328  +  P: Pointer;
          329  +  BufInc: Integer;
          330  +begin
          331  +  FillChar(strm, sizeof(strm), 0);
          332  +  strm.zalloc := zlibAllocMem;
          333  +  strm.zfree := zlibFreeMem;
          334  +  BufInc := (InBytes + 255) and not 255;
          335  +  if OutEstimate = 0 then
          336  +    OutBytes := BufInc
          337  +  else
          338  +    OutBytes := OutEstimate;
          339  +  GetMem(OutBuf, OutBytes);
          340  +  try
          341  +    strm.next_in := InBuf;
          342  +    strm.avail_in := InBytes;
          343  +    strm.next_out := OutBuf;
          344  +    strm.avail_out := OutBytes;
          345  +    DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
          346  +    try
          347  +      while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do
          348  +      begin
          349  +        P := OutBuf;
          350  +        Inc(OutBytes, BufInc);
          351  +        ReallocMem(OutBuf, OutBytes);
          352  +        strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
          353  +        strm.avail_out := BufInc;
          354  +      end;
          355  +    finally
          356  +      DCheck(inflateEnd(strm));
          357  +    end;
          358  +    ReallocMem(OutBuf, strm.total_out);
          359  +    OutBytes := strm.total_out;
          360  +  except
          361  +    FreeMem(OutBuf);
          362  +    raise
          363  +  end;
          364  +end;
          365  +
          366  +procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
          367  +  const OutBuf: Pointer; BufSize: Integer);
          368  +var
          369  +  strm: TZStreamRec;
          370  +begin
          371  +  FillChar(strm, sizeof(strm), 0);
          372  +  strm.zalloc := zlibAllocMem;
          373  +  strm.zfree := zlibFreeMem;
          374  +  strm.next_in := InBuf;
          375  +  strm.avail_in := InBytes;
          376  +  strm.next_out := OutBuf;
          377  +  strm.avail_out := BufSize;
          378  +  DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
          379  +  try
          380  +    if DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END then
          381  +      raise EZlibError.CreateRes(@sTargetBufferTooSmall);
          382  +  finally
          383  +    DCheck(inflateEnd(strm));
          384  +  end;
          385  +end;
          386  +
          387  +// TCustomZlibStream
          388  +
          389  +constructor TCustomZLibStream.Create(Strm: TStream);
          390  +begin
          391  +  inherited Create;
          392  +  FStrm := Strm;
          393  +  FStrmPos := Strm.Position;
          394  +  FZRec.zalloc := zlibAllocMem;
          395  +  FZRec.zfree := zlibFreeMem;
          396  +end;
          397  +
          398  +procedure TCustomZLibStream.Progress(Sender: TObject);
          399  +begin
          400  +  if Assigned(FOnProgress) then FOnProgress(Sender);
          401  +end;
          402  +
          403  +
          404  +// TCompressionStream
          405  +
          406  +constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
          407  +  Dest: TStream);
          408  +const
          409  +  Levels: array [TCompressionLevel] of ShortInt =
          410  +    (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
          411  +begin
          412  +  inherited Create(Dest);
          413  +  FZRec.next_out := FBuffer;
          414  +  FZRec.avail_out := sizeof(FBuffer);
          415  +  CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec)));
          416  +end;
          417  +
          418  +destructor TCompressionStream.Destroy;
          419  +begin
          420  +  FZRec.next_in := nil;
          421  +  FZRec.avail_in := 0;
          422  +  try
          423  +    if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
          424  +    while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
          425  +      and (FZRec.avail_out = 0) do
          426  +    begin
          427  +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
          428  +      FZRec.next_out := FBuffer;
          429  +      FZRec.avail_out := sizeof(FBuffer);
          430  +    end;
          431  +    if FZRec.avail_out < sizeof(FBuffer) then
          432  +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
          433  +  finally
          434  +    deflateEnd(FZRec);
          435  +  end;
          436  +  inherited Destroy;
          437  +end;
          438  +
          439  +function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
          440  +begin
          441  +  raise ECompressionError.CreateRes(@sInvalidStreamOp);
          442  +end;
          443  +
          444  +function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
          445  +begin
          446  +  FZRec.next_in := @Buffer;
          447  +  FZRec.avail_in := Count;
          448  +  if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
          449  +  while (FZRec.avail_in > 0) do
          450  +  begin
          451  +    CCheck(deflate(FZRec, 0));
          452  +    if FZRec.avail_out = 0 then
          453  +    begin
          454  +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
          455  +      FZRec.next_out := FBuffer;
          456  +      FZRec.avail_out := sizeof(FBuffer);
          457  +      FStrmPos := FStrm.Position;
          458  +      Progress(Self);
          459  +    end;
          460  +  end;
          461  +  Result := Count;
          462  +end;
          463  +
          464  +function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
          465  +begin
          466  +  if (Offset = 0) and (Origin = soFromCurrent) then
          467  +    Result := FZRec.total_in
          468  +  else
          469  +    raise ECompressionError.CreateRes(@sInvalidStreamOp);
          470  +end;
          471  +
          472  +function TCompressionStream.GetCompressionRate: Single;
          473  +begin
          474  +  if FZRec.total_in = 0 then
          475  +    Result := 0
          476  +  else
          477  +    Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
          478  +end;
          479  +
          480  +
          481  +// TDecompressionStream
          482  +
          483  +constructor TDecompressionStream.Create(Source: TStream);
          484  +begin
          485  +  inherited Create(Source);
          486  +  FZRec.next_in := FBuffer;
          487  +  FZRec.avail_in := 0;
          488  +  DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
          489  +end;
          490  +
          491  +destructor TDecompressionStream.Destroy;
          492  +begin
          493  +  FStrm.Seek(-FZRec.avail_in, 1);
          494  +  inflateEnd(FZRec);
          495  +  inherited Destroy;
          496  +end;
          497  +
          498  +function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
          499  +begin
          500  +  FZRec.next_out := @Buffer;
          501  +  FZRec.avail_out := Count;
          502  +  if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
          503  +  while (FZRec.avail_out > 0) do
          504  +  begin
          505  +    if FZRec.avail_in = 0 then
          506  +    begin
          507  +      FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
          508  +      if FZRec.avail_in = 0 then
          509  +      begin
          510  +        Result := Count - FZRec.avail_out;
          511  +        Exit;
          512  +      end;
          513  +      FZRec.next_in := FBuffer;
          514  +      FStrmPos := FStrm.Position;
          515  +      Progress(Self);
          516  +    end;
          517  +    CCheck(inflate(FZRec, 0));
          518  +  end;
          519  +  Result := Count;
          520  +end;
          521  +
          522  +function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
          523  +begin
          524  +  raise EDecompressionError.CreateRes(@sInvalidStreamOp);
          525  +end;
          526  +
          527  +function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
          528  +var
          529  +  I: Integer;
          530  +  Buf: array [0..4095] of Char;
          531  +begin
          532  +  if (Offset = 0) and (Origin = soFromBeginning) then
          533  +  begin
          534  +    DCheck(inflateReset(FZRec));
          535  +    FZRec.next_in := FBuffer;
          536  +    FZRec.avail_in := 0;
          537  +    FStrm.Position := 0;
          538  +    FStrmPos := 0;
          539  +  end
          540  +  else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
          541  +          ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
          542  +  begin
          543  +    if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
          544  +    if Offset > 0 then
          545  +    begin
          546  +      for I := 1 to Offset div sizeof(Buf) do
          547  +        ReadBuffer(Buf, sizeof(Buf));
          548  +      ReadBuffer(Buf, Offset mod sizeof(Buf));
          549  +    end;
          550  +  end
          551  +  else
          552  +    raise EDecompressionError.CreateRes(@sInvalidStreamOp);
          553  +  Result := FZRec.total_out;
          554  +end;
          555  +
          556  +
          557  +end.

Added compat/zlib/contrib/delphi/ZLibConst.pas.

            1  +unit ZLibConst;
            2  +
            3  +interface
            4  +
            5  +resourcestring
            6  +  sTargetBufferTooSmall = 'ZLib error: target buffer may be too small';
            7  +  sInvalidStreamOp = 'Invalid stream operation';
            8  +
            9  +implementation
           10  +
           11  +end.

Added compat/zlib/contrib/delphi/readme.txt.

            1  +
            2  +Overview
            3  +========
            4  +
            5  +This directory contains an update to the ZLib interface unit,
            6  +distributed by Borland as a Delphi supplemental component.
            7  +
            8  +The original ZLib unit is Copyright (c) 1997,99 Borland Corp.,
            9  +and is based on zlib version 1.0.4.  There are a series of bugs
           10  +and security problems associated with that old zlib version, and
           11  +we recommend the users to update their ZLib unit.
           12  +
           13  +
           14  +Summary of modifications
           15  +========================
           16  +
           17  +- Improved makefile, adapted to zlib version 1.2.1.
           18  +
           19  +- Some field types from TZStreamRec are changed from Integer to
           20  +  Longint, for consistency with the zlib.h header, and for 64-bit
           21  +  readiness.
           22  +
           23  +- The zlib_version constant is updated.
           24  +
           25  +- The new Z_RLE strategy has its corresponding symbolic constant.
           26  +
           27  +- The allocation and deallocation functions and function types
           28  +  (TAlloc, TFree, zlibAllocMem and zlibFreeMem) are now cdecl,
           29  +  and _malloc and _free are added as C RTL stubs.  As a result,
           30  +  the original C sources of zlib can be compiled out of the box,
           31  +  and linked to the ZLib unit.
           32  +
           33  +
           34  +Suggestions for improvements
           35  +============================
           36  +
           37  +Currently, the ZLib unit provides only a limited wrapper around
           38  +the zlib library, and much of the original zlib functionality is
           39  +missing.  Handling compressed file formats like ZIP/GZIP or PNG
           40  +cannot be implemented without having this functionality.
           41  +Applications that handle these formats are either using their own,
           42  +duplicated code, or not using the ZLib unit at all.
           43  +
           44  +Here are a few suggestions:
           45  +
           46  +- Checksum class wrappers around adler32() and crc32(), similar
           47  +  to the Java classes that implement the java.util.zip.Checksum
           48  +  interface.
           49  +
           50  +- The ability to read and write raw deflate streams, without the
           51  +  zlib stream header and trailer.  Raw deflate streams are used
           52  +  in the ZIP file format.
           53  +
           54  +- The ability to read and write gzip streams, used in the GZIP
           55  +  file format, and normally produced by the gzip program.
           56  +
           57  +- The ability to select a different compression strategy, useful
           58  +  to PNG and MNG image compression, and to multimedia compression
           59  +  in general.  Besides the compression level
           60  +
           61  +    TCompressionLevel = (clNone, clFastest, clDefault, clMax);
           62  +
           63  +  which, in fact, could have used the 'z' prefix and avoided
           64  +  TColor-like symbols
           65  +
           66  +    TCompressionLevel = (zcNone, zcFastest, zcDefault, zcMax);
           67  +
           68  +  there could be a compression strategy
           69  +
           70  +    TCompressionStrategy = (zsDefault, zsFiltered, zsHuffmanOnly, zsRle);
           71  +
           72  +- ZIP and GZIP stream handling via TStreams.
           73  +
           74  +
           75  +--
           76  +Cosmin Truta <cosmint@cs.ubbcluj.ro>

Added compat/zlib/contrib/delphi/zlibd32.mak.

            1  +# Makefile for zlib
            2  +# For use with Delphi and C++ Builder under Win32
            3  +# Updated for zlib 1.2.x by Cosmin Truta
            4  +
            5  +# ------------ Borland C++ ------------
            6  +
            7  +# This project uses the Delphi (fastcall/register) calling convention:
            8  +LOC = -DZEXPORT=__fastcall -DZEXPORTVA=__cdecl
            9  +
           10  +CC = bcc32
           11  +LD = bcc32
           12  +AR = tlib
           13  +# do not use "-pr" in CFLAGS
           14  +CFLAGS = -a -d -k- -O2 $(LOC)
           15  +LDFLAGS =
           16  +
           17  +
           18  +# variables
           19  +ZLIB_LIB = zlib.lib
           20  +
           21  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           22  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           23  +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj
           24  +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj
           25  +
           26  +
           27  +# targets
           28  +all: $(ZLIB_LIB) example.exe minigzip.exe
           29  +
           30  +.c.obj:
           31  +	$(CC) -c $(CFLAGS) $*.c
           32  +
           33  +adler32.obj: adler32.c zlib.h zconf.h
           34  +
           35  +compress.obj: compress.c zlib.h zconf.h
           36  +
           37  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           38  +
           39  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           40  +
           41  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           42  +
           43  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           44  +
           45  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           46  +
           47  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           48  +
           49  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           50  + inffast.h inffixed.h
           51  +
           52  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           53  + inffast.h
           54  +
           55  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           56  + inffast.h inffixed.h
           57  +
           58  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           59  +
           60  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           61  +
           62  +uncompr.obj: uncompr.c zlib.h zconf.h
           63  +
           64  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           65  +
           66  +example.obj: test/example.c zlib.h zconf.h
           67  +
           68  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           69  +
           70  +
           71  +# For the sake of the old Borland make,
           72  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           73  +$(ZLIB_LIB): $(OBJ1) $(OBJ2)
           74  +	-del $(ZLIB_LIB)
           75  +	$(AR) $(ZLIB_LIB) $(OBJP1)
           76  +	$(AR) $(ZLIB_LIB) $(OBJP2)
           77  +
           78  +
           79  +# testing
           80  +test: example.exe minigzip.exe
           81  +	example
           82  +	echo hello world | minigzip | minigzip -d
           83  +
           84  +example.exe: example.obj $(ZLIB_LIB)
           85  +	$(LD) $(LDFLAGS) example.obj $(ZLIB_LIB)
           86  +
           87  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
           88  +	$(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB)
           89  +
           90  +
           91  +# cleanup
           92  +clean:
           93  +	-del *.obj
           94  +	-del *.exe
           95  +	-del *.lib
           96  +	-del *.tds
           97  +	-del zlib.bak
           98  +	-del foo.gz
           99  +

Added compat/zlib/contrib/dotzlib/DotZLib.build.

            1  +<?xml version="1.0" encoding="utf-8" ?>
            2  +<project name="DotZLib" default="build" basedir="./DotZLib">
            3  +	<description>A .Net wrapper library around ZLib1.dll</description>
            4  +
            5  +	<property name="nunit.location" value="c:/program files/NUnit V2.1/bin" />
            6  +	<property name="build.root" value="bin" />
            7  +
            8  +	<property name="debug" value="true" />
            9  +	<property name="nunit" value="true" />
           10  +
           11  +	<property name="build.folder" value="${build.root}/debug/" if="${debug}" />
           12  +	<property name="build.folder" value="${build.root}/release/" unless="${debug}" />
           13  +
           14  +	<target name="clean" description="Remove all generated files">
           15  +		<delete dir="${build.root}" failonerror="false" />
           16  +	</target>
           17  +
           18  +	<target name="build" description="compiles the source code">
           19  +
           20  +		<mkdir dir="${build.folder}" />
           21  +		<csc target="library" output="${build.folder}DotZLib.dll" debug="${debug}">
           22  +			<references basedir="${nunit.location}">
           23  +				<includes if="${nunit}" name="nunit.framework.dll" />
           24  +			</references>
           25  +			<sources>
           26  +				<includes name="*.cs" />
           27  +				<excludes name="UnitTests.cs" unless="${nunit}" />
           28  +			</sources>
           29  +			<arg value="/d:nunit" if="${nunit}" />
           30  +		</csc>
           31  +	</target>
           32  +
           33  +</project>

Added compat/zlib/contrib/dotzlib/DotZLib.chm.

cannot compute difference between binary files

Added compat/zlib/contrib/dotzlib/DotZLib.sln.

            1  +Microsoft Visual Studio Solution File, Format Version 8.00
            2  +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotZLib", "DotZLib\DotZLib.csproj", "{BB1EE0B1-1808-46CB-B786-949D91117FC5}"
            3  +	ProjectSection(ProjectDependencies) = postProject
            4  +	EndProjectSection
            5  +EndProject
            6  +Global
            7  +	GlobalSection(SolutionConfiguration) = preSolution
            8  +		Debug = Debug
            9  +		Release = Release
           10  +	EndGlobalSection
           11  +	GlobalSection(ProjectConfiguration) = postSolution
           12  +		{BB1EE0B1-1808-46CB-B786-949D91117FC5}.Debug.ActiveCfg = Debug|.NET
           13  +		{BB1EE0B1-1808-46CB-B786-949D91117FC5}.Debug.Build.0 = Debug|.NET
           14  +		{BB1EE0B1-1808-46CB-B786-949D91117FC5}.Release.ActiveCfg = Release|.NET
           15  +		{BB1EE0B1-1808-46CB-B786-949D91117FC5}.Release.Build.0 = Release|.NET
           16  +	EndGlobalSection
           17  +	GlobalSection(ExtensibilityGlobals) = postSolution
           18  +	EndGlobalSection
           19  +	GlobalSection(ExtensibilityAddIns) = postSolution
           20  +	EndGlobalSection
           21  +EndGlobal

Added compat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs.

            1  +using System.Reflection;
            2  +using System.Runtime.CompilerServices;
            3  +
            4  +//
            5  +// General Information about an assembly is controlled through the following
            6  +// set of attributes. Change these attribute values to modify the information
            7  +// associated with an assembly.
            8  +//
            9  +[assembly: AssemblyTitle("DotZLib")]
           10  +[assembly: AssemblyDescription(".Net bindings for ZLib compression dll 1.2.x")]
           11  +[assembly: AssemblyConfiguration("")]
           12  +[assembly: AssemblyCompany("Henrik Ravn")]
           13  +[assembly: AssemblyProduct("")]
           14  +[assembly: AssemblyCopyright("(c) 2004 by Henrik Ravn")]
           15  +[assembly: AssemblyTrademark("")]
           16  +[assembly: AssemblyCulture("")]
           17  +
           18  +//
           19  +// Version information for an assembly consists of the following four values:
           20  +//
           21  +//      Major Version
           22  +//      Minor Version
           23  +//      Build Number
           24  +//      Revision
           25  +//
           26  +// You can specify all the values or you can default the Revision and Build Numbers
           27  +// by using the '*' as shown below:
           28  +
           29  +[assembly: AssemblyVersion("1.0.*")]
           30  +
           31  +//
           32  +// In order to sign your assembly you must specify a key to use. Refer to the
           33  +// Microsoft .NET Framework documentation for more information on assembly signing.
           34  +//
           35  +// Use the attributes below to control which key is used for signing.
           36  +//
           37  +// Notes:
           38  +//   (*) If no key is specified, the assembly is not signed.
           39  +//   (*) KeyName refers to a key that has been installed in the Crypto Service
           40  +//       Provider (CSP) on your machine. KeyFile refers to a file which contains
           41  +//       a key.
           42  +//   (*) If the KeyFile and the KeyName values are both specified, the
           43  +//       following processing occurs:
           44  +//       (1) If the KeyName can be found in the CSP, that key is used.
           45  +//       (2) If the KeyName does not exist and the KeyFile does exist, the key
           46  +//           in the KeyFile is installed into the CSP and used.
           47  +//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
           48  +//       When specifying the KeyFile, the location of the KeyFile should be
           49  +//       relative to the project output directory which is
           50  +//       %Project Directory%\obj\<configuration>. For example, if your KeyFile is
           51  +//       located in the project directory, you would specify the AssemblyKeyFile
           52  +//       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
           53  +//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
           54  +//       documentation for more information on this.
           55  +//
           56  +[assembly: AssemblyDelaySign(false)]
           57  +[assembly: AssemblyKeyFile("")]
           58  +[assembly: AssemblyKeyName("")]

Added compat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Runtime.InteropServices;
           10  +using System.Text;
           11  +
           12  +
           13  +namespace DotZLib
           14  +{
           15  +    #region ChecksumGeneratorBase
           16  +    /// <summary>
           17  +    /// Implements the common functionality needed for all <see cref="ChecksumGenerator"/>s
           18  +    /// </summary>
           19  +    /// <example></example>
           20  +    public abstract class ChecksumGeneratorBase : ChecksumGenerator
           21  +    {
           22  +        /// <summary>
           23  +        /// The value of the current checksum
           24  +        /// </summary>
           25  +        protected uint _current;
           26  +
           27  +        /// <summary>
           28  +        /// Initializes a new instance of the checksum generator base - the current checksum is
           29  +        /// set to zero
           30  +        /// </summary>
           31  +        public ChecksumGeneratorBase()
           32  +        {
           33  +            _current = 0;
           34  +        }
           35  +
           36  +        /// <summary>
           37  +        /// Initializes a new instance of the checksum generator basewith a specified value
           38  +        /// </summary>
           39  +        /// <param name="initialValue">The value to set the current checksum to</param>
           40  +        public ChecksumGeneratorBase(uint initialValue)
           41  +        {
           42  +            _current = initialValue;
           43  +        }
           44  +
           45  +        /// <summary>
           46  +        /// Resets the current checksum to zero
           47  +        /// </summary>
           48  +        public void Reset() { _current = 0; }
           49  +
           50  +        /// <summary>
           51  +        /// Gets the current checksum value
           52  +        /// </summary>
           53  +        public uint Value { get { return _current; } }
           54  +
           55  +        /// <summary>
           56  +        /// Updates the current checksum with part of an array of bytes
           57  +        /// </summary>
           58  +        /// <param name="data">The data to update the checksum with</param>
           59  +        /// <param name="offset">Where in <c>data</c> to start updating</param>
           60  +        /// <param name="count">The number of bytes from <c>data</c> to use</param>
           61  +        /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception>
           62  +        /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception>
           63  +        /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception>
           64  +        /// <remarks>All the other <c>Update</c> methods are implmeneted in terms of this one.
           65  +        /// This is therefore the only method a derived class has to implement</remarks>
           66  +        public abstract void Update(byte[] data, int offset, int count);
           67  +
           68  +        /// <summary>
           69  +        /// Updates the current checksum with an array of bytes.
           70  +        /// </summary>
           71  +        /// <param name="data">The data to update the checksum with</param>
           72  +        public void Update(byte[] data)
           73  +        {
           74  +            Update(data, 0, data.Length);
           75  +        }
           76  +
           77  +        /// <summary>
           78  +        /// Updates the current checksum with the data from a string
           79  +        /// </summary>
           80  +        /// <param name="data">The string to update the checksum with</param>
           81  +        /// <remarks>The characters in the string are converted by the UTF-8 encoding</remarks>
           82  +        public void Update(string data)
           83  +        {
           84  +			Update(Encoding.UTF8.GetBytes(data));
           85  +        }
           86  +
           87  +        /// <summary>
           88  +        /// Updates the current checksum with the data from a string, using a specific encoding
           89  +        /// </summary>
           90  +        /// <param name="data">The string to update the checksum with</param>
           91  +        /// <param name="encoding">The encoding to use</param>
           92  +        public void Update(string data, Encoding encoding)
           93  +        {
           94  +            Update(encoding.GetBytes(data));
           95  +        }
           96  +
           97  +    }
           98  +    #endregion
           99  +
          100  +    #region CRC32
          101  +    /// <summary>
          102  +    /// Implements a CRC32 checksum generator
          103  +    /// </summary>
          104  +    public sealed class CRC32Checksum : ChecksumGeneratorBase
          105  +    {
          106  +        #region DLL imports
          107  +
          108  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
          109  +        private static extern uint crc32(uint crc, int data, uint length);
          110  +
          111  +        #endregion
          112  +
          113  +        /// <summary>
          114  +        /// Initializes a new instance of the CRC32 checksum generator
          115  +        /// </summary>
          116  +        public CRC32Checksum() : base() {}
          117  +
          118  +        /// <summary>
          119  +        /// Initializes a new instance of the CRC32 checksum generator with a specified value
          120  +        /// </summary>
          121  +        /// <param name="initialValue">The value to set the current checksum to</param>
          122  +        public CRC32Checksum(uint initialValue) : base(initialValue) {}
          123  +
          124  +        /// <summary>
          125  +        /// Updates the current checksum with part of an array of bytes
          126  +        /// </summary>
          127  +        /// <param name="data">The data to update the checksum with</param>
          128  +        /// <param name="offset">Where in <c>data</c> to start updating</param>
          129  +        /// <param name="count">The number of bytes from <c>data</c> to use</param>
          130  +        /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception>
          131  +        /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception>
          132  +        /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception>
          133  +        public override void Update(byte[] data, int offset, int count)
          134  +        {
          135  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
          136  +            if ((offset+count) > data.Length) throw new ArgumentException();
          137  +            GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned);
          138  +            try
          139  +            {
          140  +                _current = crc32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count);
          141  +            }
          142  +            finally
          143  +            {
          144  +                hData.Free();
          145  +            }
          146  +        }
          147  +
          148  +    }
          149  +    #endregion
          150  +
          151  +    #region Adler
          152  +    /// <summary>
          153  +    /// Implements a checksum generator that computes the Adler checksum on data
          154  +    /// </summary>
          155  +    public sealed class AdlerChecksum : ChecksumGeneratorBase
          156  +    {
          157  +        #region DLL imports
          158  +
          159  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
          160  +        private static extern uint adler32(uint adler, int data, uint length);
          161  +
          162  +        #endregion
          163  +
          164  +        /// <summary>
          165  +        /// Initializes a new instance of the Adler checksum generator
          166  +        /// </summary>
          167  +        public AdlerChecksum() : base() {}
          168  +
          169  +        /// <summary>
          170  +        /// Initializes a new instance of the Adler checksum generator with a specified value
          171  +        /// </summary>
          172  +        /// <param name="initialValue">The value to set the current checksum to</param>
          173  +        public AdlerChecksum(uint initialValue) : base(initialValue) {}
          174  +
          175  +        /// <summary>
          176  +        /// Updates the current checksum with part of an array of bytes
          177  +        /// </summary>
          178  +        /// <param name="data">The data to update the checksum with</param>
          179  +        /// <param name="offset">Where in <c>data</c> to start updating</param>
          180  +        /// <param name="count">The number of bytes from <c>data</c> to use</param>
          181  +        /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception>
          182  +        /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception>
          183  +        /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception>
          184  +        public override void Update(byte[] data, int offset, int count)
          185  +        {
          186  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
          187  +            if ((offset+count) > data.Length) throw new ArgumentException();
          188  +            GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned);
          189  +            try
          190  +            {
          191  +                _current = adler32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count);
          192  +            }
          193  +            finally
          194  +            {
          195  +                hData.Free();
          196  +            }
          197  +        }
          198  +
          199  +    }
          200  +    #endregion
          201  +
          202  +}

Added compat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Diagnostics;
           10  +
           11  +namespace DotZLib
           12  +{
           13  +
           14  +	/// <summary>
           15  +	/// This class implements a circular buffer
           16  +	/// </summary>
           17  +	internal class CircularBuffer
           18  +	{
           19  +        #region Private data
           20  +        private int _capacity;
           21  +        private int _head;
           22  +        private int _tail;
           23  +        private int _size;
           24  +        private byte[] _buffer;
           25  +        #endregion
           26  +
           27  +        public CircularBuffer(int capacity)
           28  +        {
           29  +            Debug.Assert( capacity > 0 );
           30  +            _buffer = new byte[capacity];
           31  +            _capacity = capacity;
           32  +            _head = 0;
           33  +            _tail = 0;
           34  +            _size = 0;
           35  +        }
           36  +
           37  +        public int Size { get { return _size; } }
           38  +
           39  +        public int Put(byte[] source, int offset, int count)
           40  +        {
           41  +            Debug.Assert( count > 0 );
           42  +            int trueCount = Math.Min(count, _capacity - Size);
           43  +            for (int i = 0; i < trueCount; ++i)
           44  +                _buffer[(_tail+i) % _capacity] = source[offset+i];
           45  +            _tail += trueCount;
           46  +            _tail %= _capacity;
           47  +            _size += trueCount;
           48  +            return trueCount;
           49  +        }
           50  +
           51  +        public bool Put(byte b)
           52  +        {
           53  +            if (Size == _capacity) // no room
           54  +                return false;
           55  +            _buffer[_tail++] = b;
           56  +            _tail %= _capacity;
           57  +            ++_size;
           58  +            return true;
           59  +        }
           60  +
           61  +        public int Get(byte[] destination, int offset, int count)
           62  +        {
           63  +            int trueCount = Math.Min(count,Size);
           64  +            for (int i = 0; i < trueCount; ++i)
           65  +                destination[offset + i] = _buffer[(_head+i) % _capacity];
           66  +            _head += trueCount;
           67  +            _head %= _capacity;
           68  +            _size -= trueCount;
           69  +            return trueCount;
           70  +        }
           71  +
           72  +        public int Get()
           73  +        {
           74  +            if (Size == 0)
           75  +                return -1;
           76  +
           77  +            int result = (int)_buffer[_head++ % _capacity];
           78  +            --_size;
           79  +            return result;
           80  +        }
           81  +
           82  +    }
           83  +}

Added compat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Runtime.InteropServices;
           10  +
           11  +namespace DotZLib
           12  +{
           13  +	/// <summary>
           14  +	/// Implements the common functionality needed for all <see cref="Codec"/>s
           15  +	/// </summary>
           16  +	public abstract class CodecBase : Codec, IDisposable
           17  +	{
           18  +
           19  +        #region Data members
           20  +
           21  +        /// <summary>
           22  +        /// Instance of the internal zlib buffer structure that is
           23  +        /// passed to all functions in the zlib dll
           24  +        /// </summary>
           25  +        internal ZStream _ztream = new ZStream();
           26  +
           27  +        /// <summary>
           28  +        /// True if the object instance has been disposed, false otherwise
           29  +        /// </summary>
           30  +        protected bool _isDisposed = false;
           31  +
           32  +        /// <summary>
           33  +        /// The size of the internal buffers
           34  +        /// </summary>
           35  +        protected const int kBufferSize = 16384;
           36  +
           37  +        private byte[] _outBuffer = new byte[kBufferSize];
           38  +        private byte[] _inBuffer = new byte[kBufferSize];
           39  +
           40  +        private GCHandle _hInput;
           41  +        private GCHandle _hOutput;
           42  +
           43  +        private uint _checksum = 0;
           44  +
           45  +        #endregion
           46  +
           47  +        /// <summary>
           48  +        /// Initializes a new instance of the <c>CodeBase</c> class.
           49  +        /// </summary>
           50  +		public CodecBase()
           51  +		{
           52  +            try
           53  +            {
           54  +                _hInput = GCHandle.Alloc(_inBuffer, GCHandleType.Pinned);
           55  +                _hOutput = GCHandle.Alloc(_outBuffer, GCHandleType.Pinned);
           56  +            }
           57  +            catch (Exception)
           58  +            {
           59  +                CleanUp(false);
           60  +                throw;
           61  +            }
           62  +        }
           63  +
           64  +
           65  +        #region Codec Members
           66  +
           67  +        /// <summary>
           68  +        /// Occurs when more processed data are available.
           69  +        /// </summary>
           70  +        public event DataAvailableHandler DataAvailable;
           71  +
           72  +        /// <summary>
           73  +        /// Fires the <see cref="DataAvailable"/> event
           74  +        /// </summary>
           75  +        protected void OnDataAvailable()
           76  +        {
           77  +            if (_ztream.total_out > 0)
           78  +            {
           79  +                if (DataAvailable != null)
           80  +                    DataAvailable( _outBuffer, 0, (int)_ztream.total_out);
           81  +                resetOutput();
           82  +            }
           83  +        }
           84  +
           85  +        /// <summary>
           86  +        /// Adds more data to the codec to be processed.
           87  +        /// </summary>
           88  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
           89  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
           90  +        public void Add(byte[] data)
           91  +        {
           92  +            Add(data,0,data.Length);
           93  +        }
           94  +
           95  +        /// <summary>
           96  +        /// Adds more data to the codec to be processed.
           97  +        /// </summary>
           98  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
           99  +        /// <param name="offset">The index of the first byte to add from <c>data</c></param>
          100  +        /// <param name="count">The number of bytes to add</param>
          101  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
          102  +        /// <remarks>This must be implemented by a derived class</remarks>
          103  +        public abstract void Add(byte[] data, int offset, int count);
          104  +
          105  +        /// <summary>
          106  +        /// Finishes up any pending data that needs to be processed and handled.
          107  +        /// </summary>
          108  +        /// <remarks>This must be implemented by a derived class</remarks>
          109  +        public abstract void Finish();
          110  +
          111  +        /// <summary>
          112  +        /// Gets the checksum of the data that has been added so far
          113  +        /// </summary>
          114  +        public uint Checksum { get { return _checksum; } }
          115  +
          116  +        #endregion
          117  +
          118  +        #region Destructor & IDisposable stuff
          119  +
          120  +        /// <summary>
          121  +        /// Destroys this instance
          122  +        /// </summary>
          123  +        ~CodecBase()
          124  +        {
          125  +            CleanUp(false);
          126  +        }
          127  +
          128  +        /// <summary>
          129  +        /// Releases any unmanaged resources and calls the <see cref="CleanUp()"/> method of the derived class
          130  +        /// </summary>
          131  +        public void Dispose()
          132  +        {
          133  +            CleanUp(true);
          134  +        }
          135  +
          136  +        /// <summary>
          137  +        /// Performs any codec specific cleanup
          138  +        /// </summary>
          139  +        /// <remarks>This must be implemented by a derived class</remarks>
          140  +        protected abstract void CleanUp();
          141  +
          142  +        // performs the release of the handles and calls the dereived CleanUp()
          143  +        private void CleanUp(bool isDisposing)
          144  +        {
          145  +            if (!_isDisposed)
          146  +            {
          147  +                CleanUp();
          148  +                if (_hInput.IsAllocated)
          149  +                    _hInput.Free();
          150  +                if (_hOutput.IsAllocated)
          151  +                    _hOutput.Free();
          152  +
          153  +                _isDisposed = true;
          154  +            }
          155  +        }
          156  +
          157  +
          158  +        #endregion
          159  +
          160  +        #region Helper methods
          161  +
          162  +        /// <summary>
          163  +        /// Copies a number of bytes to the internal codec buffer - ready for proccesing
          164  +        /// </summary>
          165  +        /// <param name="data">The byte array that contains the data to copy</param>
          166  +        /// <param name="startIndex">The index of the first byte to copy</param>
          167  +        /// <param name="count">The number of bytes to copy from <c>data</c></param>
          168  +        protected void copyInput(byte[] data, int startIndex, int count)
          169  +        {
          170  +            Array.Copy(data, startIndex, _inBuffer,0, count);
          171  +            _ztream.next_in = _hInput.AddrOfPinnedObject();
          172  +            _ztream.total_in = 0;
          173  +            _ztream.avail_in = (uint)count;
          174  +
          175  +        }
          176  +
          177  +        /// <summary>
          178  +        /// Resets the internal output buffers to a known state - ready for processing
          179  +        /// </summary>
          180  +        protected void resetOutput()
          181  +        {
          182  +            _ztream.total_out = 0;
          183  +            _ztream.avail_out = kBufferSize;
          184  +            _ztream.next_out = _hOutput.AddrOfPinnedObject();
          185  +        }
          186  +
          187  +        /// <summary>
          188  +        /// Updates the running checksum property
          189  +        /// </summary>
          190  +        /// <param name="newSum">The new checksum value</param>
          191  +        protected void setChecksum(uint newSum)
          192  +        {
          193  +            _checksum = newSum;
          194  +        }
          195  +        #endregion
          196  +
          197  +    }
          198  +}

Added compat/zlib/contrib/dotzlib/DotZLib/Deflater.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Diagnostics;
           10  +using System.Runtime.InteropServices;
           11  +
           12  +namespace DotZLib
           13  +{
           14  +
           15  +    /// <summary>
           16  +    /// Implements a data compressor, using the deflate algorithm in the ZLib dll
           17  +    /// </summary>
           18  +	public sealed class Deflater : CodecBase
           19  +	{
           20  +        #region Dll imports
           21  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
           22  +        private static extern int deflateInit_(ref ZStream sz, int level, string vs, int size);
           23  +
           24  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           25  +        private static extern int deflate(ref ZStream sz, int flush);
           26  +
           27  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           28  +        private static extern int deflateReset(ref ZStream sz);
           29  +
           30  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           31  +        private static extern int deflateEnd(ref ZStream sz);
           32  +        #endregion
           33  +
           34  +        /// <summary>
           35  +        /// Constructs an new instance of the <c>Deflater</c>
           36  +        /// </summary>
           37  +        /// <param name="level">The compression level to use for this <c>Deflater</c></param>
           38  +		public Deflater(CompressLevel level) : base()
           39  +		{
           40  +            int retval = deflateInit_(ref _ztream, (int)level, Info.Version, Marshal.SizeOf(_ztream));
           41  +            if (retval != 0)
           42  +                throw new ZLibException(retval, "Could not initialize deflater");
           43  +
           44  +            resetOutput();
           45  +		}
           46  +
           47  +        /// <summary>
           48  +        /// Adds more data to the codec to be processed.
           49  +        /// </summary>
           50  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
           51  +        /// <param name="offset">The index of the first byte to add from <c>data</c></param>
           52  +        /// <param name="count">The number of bytes to add</param>
           53  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
           54  +        public override void Add(byte[] data, int offset, int count)
           55  +        {
           56  +            if (data == null) throw new ArgumentNullException();
           57  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
           58  +            if ((offset+count) > data.Length) throw new ArgumentException();
           59  +
           60  +            int total = count;
           61  +            int inputIndex = offset;
           62  +            int err = 0;
           63  +
           64  +            while (err >= 0 && inputIndex < total)
           65  +            {
           66  +                copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize));
           67  +                while (err >= 0 && _ztream.avail_in > 0)
           68  +                {
           69  +                    err = deflate(ref _ztream, (int)FlushTypes.None);
           70  +                    if (err == 0)
           71  +                        while (_ztream.avail_out == 0)
           72  +                        {
           73  +                            OnDataAvailable();
           74  +                            err = deflate(ref _ztream, (int)FlushTypes.None);
           75  +                        }
           76  +                    inputIndex += (int)_ztream.total_in;
           77  +                }
           78  +            }
           79  +            setChecksum( _ztream.adler );
           80  +        }
           81  +
           82  +
           83  +        /// <summary>
           84  +        /// Finishes up any pending data that needs to be processed and handled.
           85  +        /// </summary>
           86  +        public override void Finish()
           87  +        {
           88  +            int err;
           89  +            do
           90  +            {
           91  +                err = deflate(ref _ztream, (int)FlushTypes.Finish);
           92  +                OnDataAvailable();
           93  +            }
           94  +            while (err == 0);
           95  +            setChecksum( _ztream.adler );
           96  +            deflateReset(ref _ztream);
           97  +            resetOutput();
           98  +        }
           99  +
          100  +        /// <summary>
          101  +        /// Closes the internal zlib deflate stream
          102  +        /// </summary>
          103  +        protected override void CleanUp() { deflateEnd(ref _ztream); }
          104  +
          105  +    }
          106  +}

Added compat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.IO;
           10  +using System.Runtime.InteropServices;
           11  +using System.Text;
           12  +
           13  +
           14  +namespace DotZLib
           15  +{
           16  +
           17  +    #region Internal types
           18  +
           19  +    /// <summary>
           20  +    /// Defines constants for the various flush types used with zlib
           21  +    /// </summary>
           22  +    internal enum FlushTypes
           23  +    {
           24  +        None,  Partial,  Sync,  Full,  Finish,  Block
           25  +    }
           26  +
           27  +    #region ZStream structure
           28  +    // internal mapping of the zlib zstream structure for marshalling
           29  +    [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, CharSet=CharSet.Ansi)]
           30  +    internal struct ZStream
           31  +    {
           32  +        public IntPtr next_in;
           33  +        public uint avail_in;
           34  +        public uint total_in;
           35  +
           36  +        public IntPtr next_out;
           37  +        public uint avail_out;
           38  +        public uint total_out;
           39  +
           40  +        [MarshalAs(UnmanagedType.LPStr)]
           41  +        string msg;
           42  +        uint state;
           43  +
           44  +        uint zalloc;
           45  +        uint zfree;
           46  +        uint opaque;
           47  +
           48  +        int data_type;
           49  +        public uint adler;
           50  +        uint reserved;
           51  +    }
           52  +
           53  +    #endregion
           54  +
           55  +    #endregion
           56  +
           57  +    #region Public enums
           58  +    /// <summary>
           59  +    /// Defines constants for the available compression levels in zlib
           60  +    /// </summary>
           61  +    public enum CompressLevel : int
           62  +    {
           63  +        /// <summary>
           64  +        /// The default compression level with a reasonable compromise between compression and speed
           65  +        /// </summary>
           66  +        Default = -1,
           67  +        /// <summary>
           68  +        /// No compression at all. The data are passed straight through.
           69  +        /// </summary>
           70  +        None = 0,
           71  +        /// <summary>
           72  +        /// The maximum compression rate available.
           73  +        /// </summary>
           74  +        Best = 9,
           75  +        /// <summary>
           76  +        /// The fastest available compression level.
           77  +        /// </summary>
           78  +        Fastest = 1
           79  +    }
           80  +    #endregion
           81  +
           82  +    #region Exception classes
           83  +    /// <summary>
           84  +    /// The exception that is thrown when an error occurs on the zlib dll
           85  +    /// </summary>
           86  +    public class ZLibException : ApplicationException
           87  +    {
           88  +        /// <summary>
           89  +        /// Initializes a new instance of the <see cref="ZLibException"/> class with a specified
           90  +        /// error message and error code
           91  +        /// </summary>
           92  +        /// <param name="errorCode">The zlib error code that caused the exception</param>
           93  +        /// <param name="msg">A message that (hopefully) describes the error</param>
           94  +        public ZLibException(int errorCode, string msg) : base(String.Format("ZLib error {0} {1}", errorCode, msg))
           95  +        {
           96  +        }
           97  +
           98  +        /// <summary>
           99  +        /// Initializes a new instance of the <see cref="ZLibException"/> class with a specified
          100  +        /// error code
          101  +        /// </summary>
          102  +        /// <param name="errorCode">The zlib error code that caused the exception</param>
          103  +        public ZLibException(int errorCode) : base(String.Format("ZLib error {0}", errorCode))
          104  +        {
          105  +        }
          106  +    }
          107  +    #endregion
          108  +
          109  +    #region Interfaces
          110  +
          111  +    /// <summary>
          112  +    /// Declares methods and properties that enables a running checksum to be calculated
          113  +    /// </summary>
          114  +    public interface ChecksumGenerator
          115  +    {
          116  +        /// <summary>
          117  +        /// Gets the current value of the checksum
          118  +        /// </summary>
          119  +        uint Value { get; }
          120  +
          121  +        /// <summary>
          122  +        /// Clears the current checksum to 0
          123  +        /// </summary>
          124  +        void Reset();
          125  +
          126  +        /// <summary>
          127  +        /// Updates the current checksum with an array of bytes
          128  +        /// </summary>
          129  +        /// <param name="data">The data to update the checksum with</param>
          130  +        void Update(byte[] data);
          131  +
          132  +        /// <summary>
          133  +        /// Updates the current checksum with part of an array of bytes
          134  +        /// </summary>
          135  +        /// <param name="data">The data to update the checksum with</param>
          136  +        /// <param name="offset">Where in <c>data</c> to start updating</param>
          137  +        /// <param name="count">The number of bytes from <c>data</c> to use</param>
          138  +        /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception>
          139  +        /// <exception cref="ArgumentNullException"><c>data</c> is a null reference</exception>
          140  +        /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception>
          141  +        void Update(byte[] data, int offset, int count);
          142  +
          143  +        /// <summary>
          144  +        /// Updates the current checksum with the data from a string
          145  +        /// </summary>
          146  +        /// <param name="data">The string to update the checksum with</param>
          147  +        /// <remarks>The characters in the string are converted by the UTF-8 encoding</remarks>
          148  +        void Update(string data);
          149  +
          150  +        /// <summary>
          151  +        /// Updates the current checksum with the data from a string, using a specific encoding
          152  +        /// </summary>
          153  +        /// <param name="data">The string to update the checksum with</param>
          154  +        /// <param name="encoding">The encoding to use</param>
          155  +        void Update(string data, Encoding encoding);
          156  +    }
          157  +
          158  +
          159  +    /// <summary>
          160  +    /// Represents the method that will be called from a codec when new data
          161  +    /// are available.
          162  +    /// </summary>
          163  +    /// <paramref name="data">The byte array containing the processed data</paramref>
          164  +    /// <paramref name="startIndex">The index of the first processed byte in <c>data</c></paramref>
          165  +    /// <paramref name="count">The number of processed bytes available</paramref>
          166  +    /// <remarks>On return from this method, the data may be overwritten, so grab it while you can.
          167  +    /// You cannot assume that startIndex will be zero.
          168  +    /// </remarks>
          169  +    public delegate void DataAvailableHandler(byte[] data, int startIndex, int count);
          170  +
          171  +    /// <summary>
          172  +    /// Declares methods and events for implementing compressors/decompressors
          173  +    /// </summary>
          174  +    public interface Codec
          175  +    {
          176  +        /// <summary>
          177  +        /// Occurs when more processed data are available.
          178  +        /// </summary>
          179  +        event DataAvailableHandler DataAvailable;
          180  +
          181  +        /// <summary>
          182  +        /// Adds more data to the codec to be processed.
          183  +        /// </summary>
          184  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
          185  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
          186  +        void Add(byte[] data);
          187  +
          188  +        /// <summary>
          189  +        /// Adds more data to the codec to be processed.
          190  +        /// </summary>
          191  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
          192  +        /// <param name="offset">The index of the first byte to add from <c>data</c></param>
          193  +        /// <param name="count">The number of bytes to add</param>
          194  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
          195  +        void Add(byte[] data, int offset, int count);
          196  +
          197  +        /// <summary>
          198  +        /// Finishes up any pending data that needs to be processed and handled.
          199  +        /// </summary>
          200  +        void Finish();
          201  +
          202  +        /// <summary>
          203  +        /// Gets the checksum of the data that has been added so far
          204  +        /// </summary>
          205  +        uint Checksum { get; }
          206  +
          207  +
          208  +    }
          209  +
          210  +    #endregion
          211  +
          212  +    #region Classes
          213  +    /// <summary>
          214  +    /// Encapsulates general information about the ZLib library
          215  +    /// </summary>
          216  +    public class Info
          217  +    {
          218  +        #region DLL imports
          219  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
          220  +        private static extern uint zlibCompileFlags();
          221  +
          222  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
          223  +        private static extern string zlibVersion();
          224  +        #endregion
          225  +
          226  +        #region Private stuff
          227  +        private uint _flags;
          228  +
          229  +        // helper function that unpacks a bitsize mask
          230  +        private static int bitSize(uint bits)
          231  +        {
          232  +            switch (bits)
          233  +            {
          234  +                case 0: return 16;
          235  +                case 1: return 32;
          236  +                case 2: return 64;
          237  +            }
          238  +            return -1;
          239  +        }
          240  +        #endregion
          241  +
          242  +        /// <summary>
          243  +        /// Constructs an instance of the <c>Info</c> class.
          244  +        /// </summary>
          245  +        public Info()
          246  +        {
          247  +            _flags = zlibCompileFlags();
          248  +        }
          249  +
          250  +        /// <summary>
          251  +        /// True if the library is compiled with debug info
          252  +        /// </summary>
          253  +        public bool HasDebugInfo { get { return 0 != (_flags & 0x100); } }
          254  +
          255  +        /// <summary>
          256  +        /// True if the library is compiled with assembly optimizations
          257  +        /// </summary>
          258  +        public bool UsesAssemblyCode { get { return 0 != (_flags & 0x200); } }
          259  +
          260  +        /// <summary>
          261  +        /// Gets the size of the unsigned int that was compiled into Zlib
          262  +        /// </summary>
          263  +        public int SizeOfUInt { get { return bitSize(_flags & 3); } }
          264  +
          265  +        /// <summary>
          266  +        /// Gets the size of the unsigned long that was compiled into Zlib
          267  +        /// </summary>
          268  +        public int SizeOfULong { get { return bitSize((_flags >> 2) & 3); } }
          269  +
          270  +        /// <summary>
          271  +        /// Gets the size of the pointers that were compiled into Zlib
          272  +        /// </summary>
          273  +        public int SizeOfPointer { get { return bitSize((_flags >> 4) & 3); } }
          274  +
          275  +        /// <summary>
          276  +        /// Gets the size of the z_off_t type that was compiled into Zlib
          277  +        /// </summary>
          278  +        public int SizeOfOffset { get { return bitSize((_flags >> 6) & 3); } }
          279  +
          280  +        /// <summary>
          281  +        /// Gets the version of ZLib as a string, e.g. "1.2.1"
          282  +        /// </summary>
          283  +        public static string Version { get { return zlibVersion(); } }
          284  +    }
          285  +
          286  +    #endregion
          287  +
          288  +}

Added compat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj.

            1  +<VisualStudioProject>
            2  +    <CSHARP
            3  +        ProjectType = "Local"
            4  +        ProductVersion = "7.10.3077"
            5  +        SchemaVersion = "2.0"
            6  +        ProjectGuid = "{BB1EE0B1-1808-46CB-B786-949D91117FC5}"
            7  +    >
            8  +        <Build>
            9  +            <Settings
           10  +                ApplicationIcon = ""
           11  +                AssemblyKeyContainerName = ""
           12  +                AssemblyName = "DotZLib"
           13  +                AssemblyOriginatorKeyFile = ""
           14  +                DefaultClientScript = "JScript"
           15  +                DefaultHTMLPageLayout = "Grid"
           16  +                DefaultTargetSchema = "IE50"
           17  +                DelaySign = "false"
           18  +                OutputType = "Library"
           19  +                PreBuildEvent = ""
           20  +                PostBuildEvent = ""
           21  +                RootNamespace = "DotZLib"
           22  +                RunPostBuildEvent = "OnBuildSuccess"
           23  +                StartupObject = ""
           24  +            >
           25  +                <Config
           26  +                    Name = "Debug"
           27  +                    AllowUnsafeBlocks = "false"
           28  +                    BaseAddress = "285212672"
           29  +                    CheckForOverflowUnderflow = "false"
           30  +                    ConfigurationOverrideFile = ""
           31  +                    DefineConstants = "DEBUG;TRACE"
           32  +                    DocumentationFile = "docs\DotZLib.xml"
           33  +                    DebugSymbols = "true"
           34  +                    FileAlignment = "4096"
           35  +                    IncrementalBuild = "false"
           36  +                    NoStdLib = "false"
           37  +                    NoWarn = "1591"
           38  +                    Optimize = "false"
           39  +                    OutputPath = "bin\Debug\"
           40  +                    RegisterForComInterop = "false"
           41  +                    RemoveIntegerChecks = "false"
           42  +                    TreatWarningsAsErrors = "false"
           43  +                    WarningLevel = "4"
           44  +                />
           45  +                <Config
           46  +                    Name = "Release"
           47  +                    AllowUnsafeBlocks = "false"
           48  +                    BaseAddress = "285212672"
           49  +                    CheckForOverflowUnderflow = "false"
           50  +                    ConfigurationOverrideFile = ""
           51  +                    DefineConstants = "TRACE"
           52  +                    DocumentationFile = "docs\DotZLib.xml"
           53  +                    DebugSymbols = "false"
           54  +                    FileAlignment = "4096"
           55  +                    IncrementalBuild = "false"
           56  +                    NoStdLib = "false"
           57  +                    NoWarn = ""
           58  +                    Optimize = "true"
           59  +                    OutputPath = "bin\Release\"
           60  +                    RegisterForComInterop = "false"
           61  +                    RemoveIntegerChecks = "false"
           62  +                    TreatWarningsAsErrors = "false"
           63  +                    WarningLevel = "4"
           64  +                />
           65  +            </Settings>
           66  +            <References>
           67  +                <Reference
           68  +                    Name = "System"
           69  +                    AssemblyName = "System"
           70  +                    HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.dll"
           71  +                />
           72  +                <Reference
           73  +                    Name = "System.Data"
           74  +                    AssemblyName = "System.Data"
           75  +                    HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.Data.dll"
           76  +                />
           77  +                <Reference
           78  +                    Name = "System.XML"
           79  +                    AssemblyName = "System.Xml"
           80  +                    HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.XML.dll"
           81  +                />
           82  +                <Reference
           83  +                    Name = "nunit.framework"
           84  +                    AssemblyName = "nunit.framework"
           85  +                    HintPath = "E:\apps\NUnit V2.1\\bin\nunit.framework.dll"
           86  +                    AssemblyFolderKey = "hklm\dn\nunit.framework"
           87  +                />
           88  +            </References>
           89  +        </Build>
           90  +        <Files>
           91  +            <Include>
           92  +                <File
           93  +                    RelPath = "AssemblyInfo.cs"
           94  +                    SubType = "Code"
           95  +                    BuildAction = "Compile"
           96  +                />
           97  +                <File
           98  +                    RelPath = "ChecksumImpl.cs"
           99  +                    SubType = "Code"
          100  +                    BuildAction = "Compile"
          101  +                />
          102  +                <File
          103  +                    RelPath = "CircularBuffer.cs"
          104  +                    SubType = "Code"
          105  +                    BuildAction = "Compile"
          106  +                />
          107  +                <File
          108  +                    RelPath = "CodecBase.cs"
          109  +                    SubType = "Code"
          110  +                    BuildAction = "Compile"
          111  +                />
          112  +                <File
          113  +                    RelPath = "Deflater.cs"
          114  +                    SubType = "Code"
          115  +                    BuildAction = "Compile"
          116  +                />
          117  +                <File
          118  +                    RelPath = "DotZLib.cs"
          119  +                    SubType = "Code"
          120  +                    BuildAction = "Compile"
          121  +                />
          122  +                <File
          123  +                    RelPath = "GZipStream.cs"
          124  +                    SubType = "Code"
          125  +                    BuildAction = "Compile"
          126  +                />
          127  +                <File
          128  +                    RelPath = "Inflater.cs"
          129  +                    SubType = "Code"
          130  +                    BuildAction = "Compile"
          131  +                />
          132  +                <File
          133  +                    RelPath = "UnitTests.cs"
          134  +                    SubType = "Code"
          135  +                    BuildAction = "Compile"
          136  +                />
          137  +            </Include>
          138  +        </Files>
          139  +    </CSHARP>
          140  +</VisualStudioProject>
          141  +

Added compat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.IO;
           10  +using System.Runtime.InteropServices;
           11  +
           12  +namespace DotZLib
           13  +{
           14  +	/// <summary>
           15  +	/// Implements a compressed <see cref="Stream"/>, in GZip (.gz) format.
           16  +	/// </summary>
           17  +	public class GZipStream : Stream, IDisposable
           18  +	{
           19  +        #region Dll Imports
           20  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
           21  +        private static extern IntPtr gzopen(string name, string mode);
           22  +
           23  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           24  +        private static extern int gzclose(IntPtr gzFile);
           25  +
           26  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           27  +        private static extern int gzwrite(IntPtr gzFile, int data, int length);
           28  +
           29  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           30  +        private static extern int gzread(IntPtr gzFile, int data, int length);
           31  +
           32  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           33  +        private static extern int gzgetc(IntPtr gzFile);
           34  +
           35  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           36  +        private static extern int gzputc(IntPtr gzFile, int c);
           37  +
           38  +        #endregion
           39  +
           40  +        #region Private data
           41  +        private IntPtr _gzFile;
           42  +        private bool _isDisposed = false;
           43  +        private bool _isWriting;
           44  +        #endregion
           45  +
           46  +        #region Constructors
           47  +        /// <summary>
           48  +        /// Creates a new file as a writeable GZipStream
           49  +        /// </summary>
           50  +        /// <param name="fileName">The name of the compressed file to create</param>
           51  +        /// <param name="level">The compression level to use when adding data</param>
           52  +        /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception>
           53  +		public GZipStream(string fileName, CompressLevel level)
           54  +		{
           55  +            _isWriting = true;
           56  +            _gzFile = gzopen(fileName, String.Format("wb{0}", (int)level));
           57  +            if (_gzFile == IntPtr.Zero)
           58  +                throw new ZLibException(-1, "Could not open " + fileName);
           59  +		}
           60  +
           61  +        /// <summary>
           62  +        /// Opens an existing file as a readable GZipStream
           63  +        /// </summary>
           64  +        /// <param name="fileName">The name of the file to open</param>
           65  +        /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception>
           66  +        public GZipStream(string fileName)
           67  +        {
           68  +            _isWriting = false;
           69  +            _gzFile = gzopen(fileName, "rb");
           70  +            if (_gzFile == IntPtr.Zero)
           71  +                throw new ZLibException(-1, "Could not open " + fileName);
           72  +
           73  +        }
           74  +        #endregion
           75  +
           76  +        #region Access properties
           77  +        /// <summary>
           78  +        /// Returns true of this stream can be read from, false otherwise
           79  +        /// </summary>
           80  +        public override bool CanRead
           81  +        {
           82  +            get
           83  +            {
           84  +                return !_isWriting;
           85  +            }
           86  +        }
           87  +
           88  +
           89  +        /// <summary>
           90  +        /// Returns false.
           91  +        /// </summary>
           92  +        public override bool CanSeek
           93  +        {
           94  +            get
           95  +            {
           96  +                return false;
           97  +            }
           98  +        }
           99  +
          100  +        /// <summary>
          101  +        /// Returns true if this tsream is writeable, false otherwise
          102  +        /// </summary>
          103  +        public override bool CanWrite
          104  +        {
          105  +            get
          106  +            {
          107  +                return _isWriting;
          108  +            }
          109  +        }
          110  +        #endregion
          111  +
          112  +        #region Destructor & IDispose stuff
          113  +
          114  +        /// <summary>
          115  +        /// Destroys this instance
          116  +        /// </summary>
          117  +        ~GZipStream()
          118  +        {
          119  +            cleanUp(false);
          120  +        }
          121  +
          122  +        /// <summary>
          123  +        /// Closes the external file handle
          124  +        /// </summary>
          125  +        public void Dispose()
          126  +        {
          127  +            cleanUp(true);
          128  +        }
          129  +
          130  +        // Does the actual closing of the file handle.
          131  +        private void cleanUp(bool isDisposing)
          132  +        {
          133  +            if (!_isDisposed)
          134  +            {
          135  +                gzclose(_gzFile);
          136  +                _isDisposed = true;
          137  +            }
          138  +        }
          139  +        #endregion
          140  +
          141  +        #region Basic reading and writing
          142  +        /// <summary>
          143  +        /// Attempts to read a number of bytes from the stream.
          144  +        /// </summary>
          145  +        /// <param name="buffer">The destination data buffer</param>
          146  +        /// <param name="offset">The index of the first destination byte in <c>buffer</c></param>
          147  +        /// <param name="count">The number of bytes requested</param>
          148  +        /// <returns>The number of bytes read</returns>
          149  +        /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception>
          150  +        /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception>
          151  +        /// <exception cref="ArgumentException">If <c>offset</c>  + <c>count</c> is &gt; buffer.Length</exception>
          152  +        /// <exception cref="NotSupportedException">If this stream is not readable.</exception>
          153  +        /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception>
          154  +        public override int Read(byte[] buffer, int offset, int count)
          155  +        {
          156  +            if (!CanRead) throw new NotSupportedException();
          157  +            if (buffer == null) throw new ArgumentNullException();
          158  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
          159  +            if ((offset+count) > buffer.Length) throw new ArgumentException();
          160  +            if (_isDisposed) throw new ObjectDisposedException("GZipStream");
          161  +
          162  +            GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned);
          163  +            int result;
          164  +            try
          165  +            {
          166  +                result = gzread(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count);
          167  +                if (result < 0)
          168  +                    throw new IOException();
          169  +            }
          170  +            finally
          171  +            {
          172  +                h.Free();
          173  +            }
          174  +            return result;
          175  +        }
          176  +
          177  +        /// <summary>
          178  +        /// Attempts to read a single byte from the stream.
          179  +        /// </summary>
          180  +        /// <returns>The byte that was read, or -1 in case of error or End-Of-File</returns>
          181  +        public override int ReadByte()
          182  +        {
          183  +            if (!CanRead) throw new NotSupportedException();
          184  +            if (_isDisposed) throw new ObjectDisposedException("GZipStream");
          185  +            return gzgetc(_gzFile);
          186  +        }
          187  +
          188  +        /// <summary>
          189  +        /// Writes a number of bytes to the stream
          190  +        /// </summary>
          191  +        /// <param name="buffer"></param>
          192  +        /// <param name="offset"></param>
          193  +        /// <param name="count"></param>
          194  +        /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception>
          195  +        /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception>
          196  +        /// <exception cref="ArgumentException">If <c>offset</c>  + <c>count</c> is &gt; buffer.Length</exception>
          197  +        /// <exception cref="NotSupportedException">If this stream is not writeable.</exception>
          198  +        /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception>
          199  +        public override void Write(byte[] buffer, int offset, int count)
          200  +        {
          201  +            if (!CanWrite) throw new NotSupportedException();
          202  +            if (buffer == null) throw new ArgumentNullException();
          203  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
          204  +            if ((offset+count) > buffer.Length) throw new ArgumentException();
          205  +            if (_isDisposed) throw new ObjectDisposedException("GZipStream");
          206  +
          207  +            GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned);
          208  +            try
          209  +            {
          210  +                int result = gzwrite(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count);
          211  +                if (result < 0)
          212  +                    throw new IOException();
          213  +            }
          214  +            finally
          215  +            {
          216  +                h.Free();
          217  +            }
          218  +        }
          219  +
          220  +        /// <summary>
          221  +        /// Writes a single byte to the stream
          222  +        /// </summary>
          223  +        /// <param name="value">The byte to add to the stream.</param>
          224  +        /// <exception cref="NotSupportedException">If this stream is not writeable.</exception>
          225  +        /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception>
          226  +        public override void WriteByte(byte value)
          227  +        {
          228  +            if (!CanWrite) throw new NotSupportedException();
          229  +            if (_isDisposed) throw new ObjectDisposedException("GZipStream");
          230  +
          231  +            int result = gzputc(_gzFile, (int)value);
          232  +            if (result < 0)
          233  +                throw new IOException();
          234  +        }
          235  +        #endregion
          236  +
          237  +        #region Position & length stuff
          238  +        /// <summary>
          239  +        /// Not supported.
          240  +        /// </summary>
          241  +        /// <param name="value"></param>
          242  +        /// <exception cref="NotSupportedException">Always thrown</exception>
          243  +        public override void SetLength(long value)
          244  +        {
          245  +            throw new NotSupportedException();
          246  +        }
          247  +
          248  +        /// <summary>
          249  +        ///  Not suppported.
          250  +        /// </summary>
          251  +        /// <param name="offset"></param>
          252  +        /// <param name="origin"></param>
          253  +        /// <returns></returns>
          254  +        /// <exception cref="NotSupportedException">Always thrown</exception>
          255  +        public override long Seek(long offset, SeekOrigin origin)
          256  +        {
          257  +            throw new NotSupportedException();
          258  +        }
          259  +
          260  +        /// <summary>
          261  +        /// Flushes the <c>GZipStream</c>.
          262  +        /// </summary>
          263  +        /// <remarks>In this implementation, this method does nothing. This is because excessive
          264  +        /// flushing may degrade the achievable compression rates.</remarks>
          265  +        public override void Flush()
          266  +        {
          267  +            // left empty on purpose
          268  +        }
          269  +
          270  +        /// <summary>
          271  +        /// Gets/sets the current position in the <c>GZipStream</c>. Not suppported.
          272  +        /// </summary>
          273  +        /// <remarks>In this implementation this property is not supported</remarks>
          274  +        /// <exception cref="NotSupportedException">Always thrown</exception>
          275  +        public override long Position
          276  +        {
          277  +            get
          278  +            {
          279  +                throw new NotSupportedException();
          280  +            }
          281  +            set
          282  +            {
          283  +                throw new NotSupportedException();
          284  +            }
          285  +        }
          286  +
          287  +        /// <summary>
          288  +        /// Gets the size of the stream. Not suppported.
          289  +        /// </summary>
          290  +        /// <remarks>In this implementation this property is not supported</remarks>
          291  +        /// <exception cref="NotSupportedException">Always thrown</exception>
          292  +        public override long Length
          293  +        {
          294  +            get
          295  +            {
          296  +                throw new NotSupportedException();
          297  +            }
          298  +        }
          299  +        #endregion
          300  +    }
          301  +}

Added compat/zlib/contrib/dotzlib/DotZLib/Inflater.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Diagnostics;
           10  +using System.Runtime.InteropServices;
           11  +
           12  +namespace DotZLib
           13  +{
           14  +
           15  +    /// <summary>
           16  +    /// Implements a data decompressor, using the inflate algorithm in the ZLib dll
           17  +    /// </summary>
           18  +    public class Inflater : CodecBase
           19  +	{
           20  +        #region Dll imports
           21  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
           22  +        private static extern int inflateInit_(ref ZStream sz, string vs, int size);
           23  +
           24  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           25  +        private static extern int inflate(ref ZStream sz, int flush);
           26  +
           27  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           28  +        private static extern int inflateReset(ref ZStream sz);
           29  +
           30  +        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
           31  +        private static extern int inflateEnd(ref ZStream sz);
           32  +        #endregion
           33  +
           34  +        /// <summary>
           35  +        /// Constructs an new instance of the <c>Inflater</c>
           36  +        /// </summary>
           37  +        public Inflater() : base()
           38  +		{
           39  +            int retval = inflateInit_(ref _ztream, Info.Version, Marshal.SizeOf(_ztream));
           40  +            if (retval != 0)
           41  +                throw new ZLibException(retval, "Could not initialize inflater");
           42  +
           43  +            resetOutput();
           44  +        }
           45  +
           46  +
           47  +        /// <summary>
           48  +        /// Adds more data to the codec to be processed.
           49  +        /// </summary>
           50  +        /// <param name="data">Byte array containing the data to be added to the codec</param>
           51  +        /// <param name="offset">The index of the first byte to add from <c>data</c></param>
           52  +        /// <param name="count">The number of bytes to add</param>
           53  +        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>
           54  +        public override void Add(byte[] data, int offset, int count)
           55  +        {
           56  +            if (data == null) throw new ArgumentNullException();
           57  +            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
           58  +            if ((offset+count) > data.Length) throw new ArgumentException();
           59  +
           60  +            int total = count;
           61  +            int inputIndex = offset;
           62  +            int err = 0;
           63  +
           64  +            while (err >= 0 && inputIndex < total)
           65  +            {
           66  +                copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize));
           67  +                err = inflate(ref _ztream, (int)FlushTypes.None);
           68  +                if (err == 0)
           69  +                    while (_ztream.avail_out == 0)
           70  +                    {
           71  +                        OnDataAvailable();
           72  +                        err = inflate(ref _ztream, (int)FlushTypes.None);
           73  +                    }
           74  +
           75  +                inputIndex += (int)_ztream.total_in;
           76  +            }
           77  +            setChecksum( _ztream.adler );
           78  +        }
           79  +
           80  +
           81  +        /// <summary>
           82  +        /// Finishes up any pending data that needs to be processed and handled.
           83  +        /// </summary>
           84  +        public override void Finish()
           85  +        {
           86  +            int err;
           87  +            do
           88  +            {
           89  +                err = inflate(ref _ztream, (int)FlushTypes.Finish);
           90  +                OnDataAvailable();
           91  +            }
           92  +            while (err == 0);
           93  +            setChecksum( _ztream.adler );
           94  +            inflateReset(ref _ztream);
           95  +            resetOutput();
           96  +        }
           97  +
           98  +        /// <summary>
           99  +        /// Closes the internal zlib inflate stream
          100  +        /// </summary>
          101  +        protected override void CleanUp() { inflateEnd(ref _ztream); }
          102  +
          103  +
          104  +	}
          105  +}

Added compat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs.

            1  +//
            2  +// © Copyright Henrik Ravn 2004
            3  +//
            4  +// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
            5  +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
            6  +//
            7  +
            8  +using System;
            9  +using System.Collections;
           10  +using System.IO;
           11  +
           12  +// uncomment the define below to include unit tests
           13  +//#define nunit
           14  +#if nunit
           15  +using NUnit.Framework;
           16  +
           17  +// Unit tests for the DotZLib class library
           18  +// ----------------------------------------
           19  +//
           20  +// Use this with NUnit 2 from http://www.nunit.org
           21  +//
           22  +
           23  +namespace DotZLibTests
           24  +{
           25  +    using DotZLib;
           26  +
           27  +    // helper methods
           28  +    internal class Utils
           29  +    {
           30  +        public static bool byteArrEqual( byte[] lhs, byte[] rhs )
           31  +        {
           32  +            if (lhs.Length != rhs.Length)
           33  +                return false;
           34  +            for (int i = lhs.Length-1; i >= 0; --i)
           35  +                if (lhs[i] != rhs[i])
           36  +                    return false;
           37  +            return true;
           38  +        }
           39  +
           40  +    }
           41  +
           42  +
           43  +    [TestFixture]
           44  +    public class CircBufferTests
           45  +    {
           46  +        #region Circular buffer tests
           47  +        [Test]
           48  +        public void SinglePutGet()
           49  +        {
           50  +            CircularBuffer buf = new CircularBuffer(10);
           51  +            Assert.AreEqual( 0, buf.Size );
           52  +            Assert.AreEqual( -1, buf.Get() );
           53  +
           54  +            Assert.IsTrue(buf.Put( 1 ));
           55  +            Assert.AreEqual( 1, buf.Size );
           56  +            Assert.AreEqual( 1, buf.Get() );
           57  +            Assert.AreEqual( 0, buf.Size );
           58  +            Assert.AreEqual( -1, buf.Get() );
           59  +        }
           60  +
           61  +        [Test]
           62  +        public void BlockPutGet()
           63  +        {
           64  +            CircularBuffer buf = new CircularBuffer(10);
           65  +            byte[] arr = {1,2,3,4,5,6,7,8,9,10};
           66  +            Assert.AreEqual( 10, buf.Put(arr,0,10) );
           67  +            Assert.AreEqual( 10, buf.Size );
           68  +            Assert.IsFalse( buf.Put(11) );
           69  +            Assert.AreEqual( 1, buf.Get() );
           70  +            Assert.IsTrue( buf.Put(11) );
           71  +
           72  +            byte[] arr2 = (byte[])arr.Clone();
           73  +            Assert.AreEqual( 9, buf.Get(arr2,1,9) );
           74  +            Assert.IsTrue( Utils.byteArrEqual(arr,arr2) );
           75  +        }
           76  +
           77  +        #endregion
           78  +    }
           79  +
           80  +    [TestFixture]
           81  +    public class ChecksumTests
           82  +    {
           83  +        #region CRC32 Tests
           84  +        [Test]
           85  +        public void CRC32_Null()
           86  +        {
           87  +            CRC32Checksum crc32 = new CRC32Checksum();
           88  +            Assert.AreEqual( 0, crc32.Value );
           89  +
           90  +            crc32 = new CRC32Checksum(1);
           91  +            Assert.AreEqual( 1, crc32.Value );
           92  +
           93  +            crc32 = new CRC32Checksum(556);
           94  +            Assert.AreEqual( 556, crc32.Value );
           95  +        }
           96  +
           97  +        [Test]
           98  +        public void CRC32_Data()
           99  +        {
          100  +            CRC32Checksum crc32 = new CRC32Checksum();
          101  +            byte[] data = { 1,2,3,4,5,6,7 };
          102  +            crc32.Update(data);
          103  +            Assert.AreEqual( 0x70e46888, crc32.Value  );
          104  +
          105  +            crc32 = new CRC32Checksum();
          106  +            crc32.Update("penguin");
          107  +            Assert.AreEqual( 0x0e5c1a120, crc32.Value );
          108  +
          109  +            crc32 = new CRC32Checksum(1);
          110  +            crc32.Update("penguin");
          111  +            Assert.AreEqual(0x43b6aa94, crc32.Value);
          112  +
          113  +        }
          114  +        #endregion
          115  +
          116  +        #region Adler tests
          117  +
          118  +        [Test]
          119  +        public void Adler_Null()
          120  +        {
          121  +            AdlerChecksum adler = new AdlerChecksum();
          122  +            Assert.AreEqual(0, adler.Value);
          123  +
          124  +            adler = new AdlerChecksum(1);
          125  +            Assert.AreEqual( 1, adler.Value );
          126  +
          127  +            adler = new AdlerChecksum(556);
          128  +            Assert.AreEqual( 556, adler.Value );
          129  +        }
          130  +
          131  +        [Test]
          132  +        public void Adler_Data()
          133  +        {
          134  +            AdlerChecksum adler = new AdlerChecksum(1);
          135  +            byte[] data = { 1,2,3,4,5,6,7 };
          136  +            adler.Update(data);
          137  +            Assert.AreEqual( 0x5b001d, adler.Value  );
          138  +
          139  +            adler = new AdlerChecksum();
          140  +            adler.Update("penguin");
          141  +            Assert.AreEqual(0x0bcf02f6, adler.Value );
          142  +
          143  +            adler = new AdlerChecksum(1);
          144  +            adler.Update("penguin");
          145  +            Assert.AreEqual(0x0bd602f7, adler.Value);
          146  +
          147  +        }
          148  +        #endregion
          149  +    }
          150  +
          151  +    [TestFixture]
          152  +    public class InfoTests
          153  +    {
          154  +        #region Info tests
          155  +        [Test]
          156  +        public void Info_Version()
          157  +        {
          158  +            Info info = new Info();
          159  +            Assert.AreEqual("1.2.7", Info.Version);
          160  +            Assert.AreEqual(32, info.SizeOfUInt);
          161  +            Assert.AreEqual(32, info.SizeOfULong);
          162  +            Assert.AreEqual(32, info.SizeOfPointer);
          163  +            Assert.AreEqual(32, info.SizeOfOffset);
          164  +        }
          165  +        #endregion
          166  +    }
          167  +
          168  +    [TestFixture]
          169  +    public class DeflateInflateTests
          170  +    {
          171  +        #region Deflate tests
          172  +        [Test]
          173  +        public void Deflate_Init()
          174  +        {
          175  +            using (Deflater def = new Deflater(CompressLevel.Default))
          176  +            {
          177  +            }
          178  +        }
          179  +
          180  +        private ArrayList compressedData = new ArrayList();
          181  +        private uint adler1;
          182  +
          183  +        private ArrayList uncompressedData = new ArrayList();
          184  +        private uint adler2;
          185  +
          186  +        public void CDataAvail(byte[] data, int startIndex, int count)
          187  +        {
          188  +            for (int i = 0; i < count; ++i)
          189  +                compressedData.Add(data[i+startIndex]);
          190  +        }
          191  +
          192  +        [Test]
          193  +        public void Deflate_Compress()
          194  +        {
          195  +            compressedData.Clear();
          196  +
          197  +            byte[] testData = new byte[35000];
          198  +            for (int i = 0; i < testData.Length; ++i)
          199  +                testData[i] = 5;
          200  +
          201  +            using (Deflater def = new Deflater((CompressLevel)5))
          202  +            {
          203  +                def.DataAvailable += new DataAvailableHandler(CDataAvail);
          204  +                def.Add(testData);
          205  +                def.Finish();
          206  +                adler1 = def.Checksum;
          207  +            }
          208  +        }
          209  +        #endregion
          210  +
          211  +        #region Inflate tests
          212  +        [Test]
          213  +        public void Inflate_Init()
          214  +        {
          215  +            using (Inflater inf = new Inflater())
          216  +            {
          217  +            }
          218  +        }
          219  +
          220  +        private void DDataAvail(byte[] data, int startIndex, int count)
          221  +        {
          222  +            for (int i = 0; i < count; ++i)
          223  +                uncompressedData.Add(data[i+startIndex]);
          224  +        }
          225  +
          226  +        [Test]
          227  +        public void Inflate_Expand()
          228  +        {
          229  +            uncompressedData.Clear();
          230  +
          231  +            using (Inflater inf = new Inflater())
          232  +            {
          233  +                inf.DataAvailable += new DataAvailableHandler(DDataAvail);
          234  +                inf.Add((byte[])compressedData.ToArray(typeof(byte)));
          235  +                inf.Finish();
          236  +                adler2 = inf.Checksum;
          237  +            }
          238  +            Assert.AreEqual( adler1, adler2 );
          239  +        }
          240  +        #endregion
          241  +    }
          242  +
          243  +    [TestFixture]
          244  +    public class GZipStreamTests
          245  +    {
          246  +        #region GZipStream test
          247  +        [Test]
          248  +        public void GZipStream_WriteRead()
          249  +        {
          250  +            using (GZipStream gzOut = new GZipStream("gzstream.gz", CompressLevel.Best))
          251  +            {
          252  +                BinaryWriter writer = new BinaryWriter(gzOut);
          253  +                writer.Write("hi there");
          254  +                writer.Write(Math.PI);
          255  +                writer.Write(42);
          256  +            }
          257  +
          258  +            using (GZipStream gzIn = new GZipStream("gzstream.gz"))
          259  +            {
          260  +                BinaryReader reader = new BinaryReader(gzIn);
          261  +                string s = reader.ReadString();
          262  +                Assert.AreEqual("hi there",s);
          263  +                double d = reader.ReadDouble();
          264  +                Assert.AreEqual(Math.PI, d);
          265  +                int i = reader.ReadInt32();
          266  +                Assert.AreEqual(42,i);
          267  +            }
          268  +
          269  +        }
          270  +        #endregion
          271  +	}
          272  +}
          273  +
          274  +#endif

Added compat/zlib/contrib/dotzlib/LICENSE_1_0.txt.

            1  +Boost Software License - Version 1.0 - August 17th, 2003
            2  +
            3  +Permission is hereby granted, free of charge, to any person or organization
            4  +obtaining a copy of the software and accompanying documentation covered by
            5  +this license (the "Software") to use, reproduce, display, distribute,
            6  +execute, and transmit the Software, and to prepare derivative works of the
            7  +Software, and to permit third-parties to whom the Software is furnished to
            8  +do so, all subject to the following:
            9  +
           10  +The copyright notices in the Software and this entire statement, including
           11  +the above license grant, this restriction and the following disclaimer,
           12  +must be included in all copies of the Software, in whole or in part, and
           13  +all derivative works of the Software, unless such copies or derivative
           14  +works are solely in the form of machine-executable object code generated by
           15  +a source language processor.
           16  +
           17  +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           18  +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           19  +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
           20  +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
           21  +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
           22  +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
           23  +DEALINGS IN THE SOFTWARE.

Added compat/zlib/contrib/dotzlib/readme.txt.

            1  +This directory contains a .Net wrapper class library for the ZLib1.dll
            2  +
            3  +The wrapper includes support for inflating/deflating memory buffers,
            4  +.Net streaming wrappers for the gz streams part of zlib, and wrappers
            5  +for the checksum parts of zlib. See DotZLib/UnitTests.cs for examples.
            6  +
            7  +Directory structure:
            8  +--------------------
            9  +
           10  +LICENSE_1_0.txt       - License file.
           11  +readme.txt            - This file.
           12  +DotZLib.chm           - Class library documentation
           13  +DotZLib.build         - NAnt build file
           14  +DotZLib.sln           - Microsoft Visual Studio 2003 solution file
           15  +
           16  +DotZLib\*.cs          - Source files for the class library
           17  +
           18  +Unit tests:
           19  +-----------
           20  +The file DotZLib/UnitTests.cs contains unit tests for use with NUnit 2.1 or higher.
           21  +To include unit tests in the build, define nunit before building.
           22  +
           23  +
           24  +Build instructions:
           25  +-------------------
           26  +
           27  +1. Using Visual Studio.Net 2003:
           28  +   Open DotZLib.sln in VS.Net and build from there. Output file (DotZLib.dll)
           29  +   will be found ./DotZLib/bin/release or ./DotZLib/bin/debug, depending on
           30  +   you are building the release or debug version of the library. Check
           31  +   DotZLib/UnitTests.cs for instructions on how to include unit tests in the
           32  +   build.
           33  +
           34  +2. Using NAnt:
           35  +   Open a command prompt with access to the build environment and run nant
           36  +   in the same directory as the DotZLib.build file.
           37  +   You can define 2 properties on the nant command-line to control the build:
           38  +   debug={true|false} to toggle between release/debug builds (default=true).
           39  +   nunit={true|false} to include or esclude unit tests (default=true).
           40  +   Also the target clean will remove binaries.
           41  +   Output file (DotZLib.dll) will be found in either ./DotZLib/bin/release
           42  +   or ./DotZLib/bin/debug, depending on whether you are building the release
           43  +   or debug version of the library.
           44  +
           45  +   Examples:
           46  +     nant -D:debug=false -D:nunit=false
           47  +       will build a release mode version of the library without unit tests.
           48  +     nant
           49  +       will build a debug version of the library with unit tests
           50  +     nant clean
           51  +       will remove all previously built files.
           52  +
           53  +
           54  +---------------------------------
           55  +Copyright (c) Henrik Ravn 2004
           56  +
           57  +Use, modification and distribution are subject to the Boost Software License, Version 1.0.
           58  +(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

Added compat/zlib/contrib/gcc_gvmat64/gvmat64.S.

            1  +/*
            2  +;uInt longest_match_x64(
            3  +;    deflate_state *s,
            4  +;    IPos cur_match);                             // current match 
            5  +
            6  +; gvmat64.S -- Asm portion of the optimized longest_match for 32 bits x86_64
            7  +;  (AMD64 on Athlon 64, Opteron, Phenom
            8  +;     and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7)
            9  +; this file is translation from gvmat64.asm to GCC 4.x (for Linux, Mac XCode)
           10  +; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant.
           11  +;
           12  +; File written by Gilles Vollant, by converting to assembly the longest_match
           13  +;  from Jean-loup Gailly in deflate.c of zLib and infoZip zip.
           14  +;  and by taking inspiration on asm686 with masm, optimised assembly code
           15  +;        from Brian Raiter, written 1998
           16  +;
           17  +;  This software is provided 'as-is', without any express or implied
           18  +;  warranty.  In no event will the authors be held liable for any damages
           19  +;  arising from the use of this software.
           20  +;
           21  +;  Permission is granted to anyone to use this software for any purpose,
           22  +;  including commercial applications, and to alter it and redistribute it
           23  +;  freely, subject to the following restrictions:
           24  +;
           25  +;  1. The origin of this software must not be misrepresented; you must not
           26  +;     claim that you wrote the original software. If you use this software
           27  +;     in a product, an acknowledgment in the product documentation would be
           28  +;     appreciated but is not required.
           29  +;  2. Altered source versions must be plainly marked as such, and must not be
           30  +;     misrepresented as being the original software
           31  +;  3. This notice may not be removed or altered from any source distribution.
           32  +;
           33  +;         http://www.zlib.net
           34  +;         http://www.winimage.com/zLibDll
           35  +;         http://www.muppetlabs.com/~breadbox/software/assembly.html
           36  +;
           37  +; to compile this file for zLib, I use option:
           38  +;   gcc -c -arch x86_64 gvmat64.S
           39  +
           40  +
           41  +;uInt longest_match(s, cur_match)
           42  +;    deflate_state *s;
           43  +;    IPos cur_match;                             // current match /
           44  +;
           45  +; with XCode for Mac, I had strange error with some jump on intel syntax
           46  +; this is why BEFORE_JMP and AFTER_JMP are used
           47  + */
           48  +
           49  +
           50  +#define BEFORE_JMP .att_syntax
           51  +#define AFTER_JMP .intel_syntax noprefix
           52  +
           53  +#ifndef NO_UNDERLINE
           54  +#	define	match_init	_match_init
           55  +#	define	longest_match	_longest_match
           56  +#endif
           57  +
           58  +.intel_syntax noprefix
           59  +
           60  +.globl	match_init, longest_match
           61  +.text
           62  +longest_match:
           63  +
           64  +
           65  +
           66  +#define LocalVarsSize 96
           67  +/*
           68  +; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12
           69  +; free register :  r14,r15
           70  +; register can be saved : rsp
           71  +*/
           72  +
           73  +#define chainlenwmask     (rsp + 8 - LocalVarsSize)
           74  +#define nicematch         (rsp + 16 - LocalVarsSize)
           75  +
           76  +#define save_rdi        (rsp + 24 - LocalVarsSize)
           77  +#define save_rsi        (rsp + 32 - LocalVarsSize)
           78  +#define save_rbx        (rsp + 40 - LocalVarsSize)
           79  +#define save_rbp        (rsp + 48 - LocalVarsSize)
           80  +#define save_r12        (rsp + 56 - LocalVarsSize)
           81  +#define save_r13        (rsp + 64 - LocalVarsSize)
           82  +#define save_r14        (rsp + 72 - LocalVarsSize)
           83  +#define save_r15        (rsp + 80 - LocalVarsSize)
           84  +
           85  +
           86  +/*
           87  +;  all the +4 offsets are due to the addition of pending_buf_size (in zlib
           88  +;  in the deflate_state structure since the asm code was first written
           89  +;  (if you compile with zlib 1.0.4 or older, remove the +4).
           90  +;  Note : these value are good with a 8 bytes boundary pack structure
           91  +*/
           92  +
           93  +#define    MAX_MATCH              258
           94  +#define    MIN_MATCH              3
           95  +#define    MIN_LOOKAHEAD          (MAX_MATCH+MIN_MATCH+1)
           96  +
           97  +/*
           98  +;;; Offsets for fields in the deflate_state structure. These numbers
           99  +;;; are calculated from the definition of deflate_state, with the
          100  +;;; assumption that the compiler will dword-align the fields. (Thus,
          101  +;;; changing the definition of deflate_state could easily cause this
          102  +;;; program to crash horribly, without so much as a warning at
          103  +;;; compile time. Sigh.)
          104  +
          105  +;  all the +zlib1222add offsets are due to the addition of fields
          106  +;  in zlib in the deflate_state structure since the asm code was first written
          107  +;  (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
          108  +;  (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
          109  +;  if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
          110  +*/
          111  +
          112  +
          113  +
          114  +/* you can check the structure offset by running
          115  +
          116  +#include <stdlib.h>
          117  +#include <stdio.h>
          118  +#include "deflate.h"
          119  +
          120  +void print_depl()
          121  +{
          122  +deflate_state ds;
          123  +deflate_state *s=&ds;
          124  +printf("size pointer=%u\n",(int)sizeof(void*));
          125  +
          126  +printf("#define dsWSize         %u\n",(int)(((char*)&(s->w_size))-((char*)s)));
          127  +printf("#define dsWMask         %u\n",(int)(((char*)&(s->w_mask))-((char*)s)));
          128  +printf("#define dsWindow        %u\n",(int)(((char*)&(s->window))-((char*)s)));
          129  +printf("#define dsPrev          %u\n",(int)(((char*)&(s->prev))-((char*)s)));
          130  +printf("#define dsMatchLen      %u\n",(int)(((char*)&(s->match_length))-((char*)s)));
          131  +printf("#define dsPrevMatch     %u\n",(int)(((char*)&(s->prev_match))-((char*)s)));
          132  +printf("#define dsStrStart      %u\n",(int)(((char*)&(s->strstart))-((char*)s)));
          133  +printf("#define dsMatchStart    %u\n",(int)(((char*)&(s->match_start))-((char*)s)));
          134  +printf("#define dsLookahead     %u\n",(int)(((char*)&(s->lookahead))-((char*)s)));
          135  +printf("#define dsPrevLen       %u\n",(int)(((char*)&(s->prev_length))-((char*)s)));
          136  +printf("#define dsMaxChainLen   %u\n",(int)(((char*)&(s->max_chain_length))-((char*)s)));
          137  +printf("#define dsGoodMatch     %u\n",(int)(((char*)&(s->good_match))-((char*)s)));
          138  +printf("#define dsNiceMatch     %u\n",(int)(((char*)&(s->nice_match))-((char*)s)));
          139  +}
          140  +*/
          141  +
          142  +#define dsWSize          68
          143  +#define dsWMask          76
          144  +#define dsWindow         80
          145  +#define dsPrev           96
          146  +#define dsMatchLen       144
          147  +#define dsPrevMatch      148
          148  +#define dsStrStart       156
          149  +#define dsMatchStart     160
          150  +#define dsLookahead      164
          151  +#define dsPrevLen        168
          152  +#define dsMaxChainLen    172
          153  +#define dsGoodMatch      188
          154  +#define dsNiceMatch      192
          155  +
          156  +#define window_size      [ rcx + dsWSize]
          157  +#define WMask            [ rcx + dsWMask]
          158  +#define window_ad        [ rcx + dsWindow]
          159  +#define prev_ad          [ rcx + dsPrev]
          160  +#define strstart         [ rcx + dsStrStart]
          161  +#define match_start      [ rcx + dsMatchStart]
          162  +#define Lookahead        [ rcx + dsLookahead] //; 0ffffffffh on infozip
          163  +#define prev_length      [ rcx + dsPrevLen]
          164  +#define max_chain_length [ rcx + dsMaxChainLen]
          165  +#define good_match       [ rcx + dsGoodMatch]
          166  +#define nice_match       [ rcx + dsNiceMatch]
          167  +
          168  +/*
          169  +; windows:
          170  +; parameter 1 in rcx(deflate state s), param 2 in rdx (cur match)
          171  +
          172  +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and
          173  +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp
          174  +;
          175  +; All registers must be preserved across the call, except for
          176  +;   rax, rcx, rdx, r8, r9, r10, and r11, which are scratch.
          177  +
          178  +;
          179  +; gcc on macosx-linux:
          180  +; see http://www.x86-64.org/documentation/abi-0.99.pdf
          181  +; param 1 in rdi, param 2 in rsi
          182  +; rbx, rsp, rbp, r12 to r15 must be preserved
          183  +
          184  +;;; Save registers that the compiler may be using, and adjust esp to
          185  +;;; make room for our stack frame.
          186  +
          187  +
          188  +;;; Retrieve the function arguments. r8d will hold cur_match
          189  +;;; throughout the entire function. edx will hold the pointer to the
          190  +;;; deflate_state structure during the function's setup (before
          191  +;;; entering the main loop.
          192  +
          193  +; ms: parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match)
          194  +; mac: param 1 in rdi, param 2 rsi
          195  +; this clear high 32 bits of r8, which can be garbage in both r8 and rdx
          196  +*/
          197  +        mov [save_rbx],rbx
          198  +        mov [save_rbp],rbp
          199  +
          200  +
          201  +        mov rcx,rdi
          202  +
          203  +        mov r8d,esi
          204  +
          205  +
          206  +        mov [save_r12],r12
          207  +        mov [save_r13],r13
          208  +        mov [save_r14],r14
          209  +        mov [save_r15],r15
          210  +
          211  +
          212  +//;;; uInt wmask = s->w_mask;
          213  +//;;; unsigned chain_length = s->max_chain_length;
          214  +//;;; if (s->prev_length >= s->good_match) {
          215  +//;;;     chain_length >>= 2;
          216  +//;;; }
          217  +
          218  +
          219  +        mov edi, prev_length
          220  +        mov esi, good_match
          221  +        mov eax, WMask
          222  +        mov ebx, max_chain_length
          223  +        cmp edi, esi
          224  +        jl  LastMatchGood
          225  +        shr ebx, 2
          226  +LastMatchGood:
          227  +
          228  +//;;; chainlen is decremented once beforehand so that the function can
          229  +//;;; use the sign flag instead of the zero flag for the exit test.
          230  +//;;; It is then shifted into the high word, to make room for the wmask
          231  +//;;; value, which it will always accompany.
          232  +
          233  +        dec ebx
          234  +        shl ebx, 16
          235  +        or  ebx, eax
          236  +
          237  +//;;; on zlib only
          238  +//;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
          239  +
          240  +
          241  +
          242  +        mov eax, nice_match
          243  +        mov [chainlenwmask], ebx
          244  +        mov r10d, Lookahead
          245  +        cmp r10d, eax
          246  +        cmovnl r10d, eax
          247  +        mov [nicematch],r10d
          248  +
          249  +
          250  +
          251  +//;;; register Bytef *scan = s->window + s->strstart;
          252  +        mov r10, window_ad
          253  +        mov ebp, strstart
          254  +        lea r13, [r10 + rbp]
          255  +
          256  +//;;; Determine how many bytes the scan ptr is off from being
          257  +//;;; dword-aligned.
          258  +
          259  +         mov r9,r13
          260  +         neg r13
          261  +         and r13,3
          262  +
          263  +//;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
          264  +//;;;     s->strstart - (IPos)MAX_DIST(s) : NIL;
          265  +
          266  +
          267  +        mov eax, window_size
          268  +        sub eax, MIN_LOOKAHEAD
          269  +
          270  +
          271  +        xor edi,edi
          272  +        sub ebp, eax
          273  +
          274  +        mov r11d, prev_length
          275  +
          276  +        cmovng ebp,edi
          277  +
          278  +//;;; int best_len = s->prev_length;
          279  +
          280  +
          281  +//;;; Store the sum of s->window + best_len in esi locally, and in esi.
          282  +
          283  +       lea  rsi,[r10+r11]
          284  +
          285  +//;;; register ush scan_start = *(ushf*)scan;
          286  +//;;; register ush scan_end   = *(ushf*)(scan+best_len-1);
          287  +//;;; Posf *prev = s->prev;
          288  +
          289  +        movzx r12d,word ptr [r9]
          290  +        movzx ebx, word ptr [r9 + r11 - 1]
          291  +
          292  +        mov rdi, prev_ad
          293  +
          294  +//;;; Jump into the main loop.
          295  +
          296  +        mov edx, [chainlenwmask]
          297  +
          298  +        cmp bx,word ptr [rsi + r8 - 1]
          299  +        jz  LookupLoopIsZero
          300  +				
          301  +						
          302  +						
          303  +LookupLoop1:
          304  +        and r8d, edx
          305  +
          306  +        movzx   r8d, word ptr [rdi + r8*2]
          307  +        cmp r8d, ebp
          308  +        jbe LeaveNow
          309  +		
          310  +		
          311  +		
          312  +        sub edx, 0x00010000
          313  +		BEFORE_JMP
          314  +        js  LeaveNow
          315  +		AFTER_JMP
          316  +
          317  +LoopEntry1:
          318  +        cmp bx,word ptr [rsi + r8 - 1]
          319  +		BEFORE_JMP
          320  +        jz  LookupLoopIsZero
          321  +		AFTER_JMP
          322  +
          323  +LookupLoop2:
          324  +        and r8d, edx
          325  +
          326  +        movzx   r8d, word ptr [rdi + r8*2]
          327  +        cmp r8d, ebp
          328  +		BEFORE_JMP
          329  +        jbe LeaveNow
          330  +		AFTER_JMP
          331  +        sub edx, 0x00010000
          332  +		BEFORE_JMP
          333  +        js  LeaveNow
          334  +		AFTER_JMP
          335  +
          336  +LoopEntry2:
          337  +        cmp bx,word ptr [rsi + r8 - 1]
          338  +		BEFORE_JMP
          339  +        jz  LookupLoopIsZero
          340  +		AFTER_JMP
          341  +
          342  +LookupLoop4:
          343  +        and r8d, edx
          344  +
          345  +        movzx   r8d, word ptr [rdi + r8*2]
          346  +        cmp r8d, ebp
          347  +		BEFORE_JMP
          348  +        jbe LeaveNow
          349  +		AFTER_JMP
          350  +        sub edx, 0x00010000
          351  +		BEFORE_JMP
          352  +        js  LeaveNow
          353  +		AFTER_JMP
          354  +
          355  +LoopEntry4:
          356  +
          357  +        cmp bx,word ptr [rsi + r8 - 1]
          358  +		BEFORE_JMP
          359  +        jnz LookupLoop1
          360  +        jmp LookupLoopIsZero
          361  +		AFTER_JMP
          362  +/*
          363  +;;; do {
          364  +;;;     match = s->window + cur_match;
          365  +;;;     if (*(ushf*)(match+best_len-1) != scan_end ||
          366  +;;;         *(ushf*)match != scan_start) continue;
          367  +;;;     [...]
          368  +;;; } while ((cur_match = prev[cur_match & wmask]) > limit
          369  +;;;          && --chain_length != 0);
          370  +;;;
          371  +;;; Here is the inner loop of the function. The function will spend the
          372  +;;; majority of its time in this loop, and majority of that time will
          373  +;;; be spent in the first ten instructions.
          374  +;;;
          375  +;;; Within this loop:
          376  +;;; ebx = scanend
          377  +;;; r8d = curmatch
          378  +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
          379  +;;; esi = windowbestlen - i.e., (window + bestlen)
          380  +;;; edi = prev
          381  +;;; ebp = limit
          382  +*/
          383  +.balign 16
          384  +LookupLoop:
          385  +        and r8d, edx
          386  +
          387  +        movzx   r8d, word ptr [rdi + r8*2]
          388  +        cmp r8d, ebp
          389  +		BEFORE_JMP
          390  +        jbe LeaveNow
          391  +		AFTER_JMP
          392  +        sub edx, 0x00010000
          393  +		BEFORE_JMP
          394  +        js  LeaveNow
          395  +		AFTER_JMP
          396  +
          397  +LoopEntry:
          398  +
          399  +        cmp bx,word ptr [rsi + r8 - 1]
          400  +		BEFORE_JMP
          401  +        jnz LookupLoop1
          402  +		AFTER_JMP
          403  +LookupLoopIsZero:
          404  +        cmp     r12w, word ptr [r10 + r8]
          405  +		BEFORE_JMP
          406  +        jnz LookupLoop1
          407  +		AFTER_JMP
          408  +
          409  +
          410  +//;;; Store the current value of chainlen.
          411  +        mov [chainlenwmask], edx
          412  +/*
          413  +;;; Point edi to the string under scrutiny, and esi to the string we
          414  +;;; are hoping to match it up with. In actuality, esi and edi are
          415  +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is
          416  +;;; initialized to -(MAX_MATCH_8 - scanalign).
          417  +*/
          418  +        lea rsi,[r8+r10]
          419  +        mov rdx, 0xfffffffffffffef8 //; -(MAX_MATCH_8)
          420  +        lea rsi, [rsi + r13 + 0x0108] //;MAX_MATCH_8]
          421  +        lea rdi, [r9 + r13 + 0x0108] //;MAX_MATCH_8]
          422  +
          423  +        prefetcht1 [rsi+rdx]
          424  +        prefetcht1 [rdi+rdx]
          425  +
          426  +/*
          427  +;;; Test the strings for equality, 8 bytes at a time. At the end,
          428  +;;; adjust rdx so that it is offset to the exact byte that mismatched.
          429  +;;;
          430  +;;; We already know at this point that the first three bytes of the
          431  +;;; strings match each other, and they can be safely passed over before
          432  +;;; starting the compare loop. So what this code does is skip over 0-3
          433  +;;; bytes, as much as necessary in order to dword-align the edi
          434  +;;; pointer. (rsi will still be misaligned three times out of four.)
          435  +;;;
          436  +;;; It should be confessed that this loop usually does not represent
          437  +;;; much of the total running time. Replacing it with a more
          438  +;;; straightforward "rep cmpsb" would not drastically degrade
          439  +;;; performance.
          440  +*/
          441  +
          442  +LoopCmps:
          443  +        mov rax, [rsi + rdx]
          444  +        xor rax, [rdi + rdx]
          445  +        jnz LeaveLoopCmps
          446  +
          447  +        mov rax, [rsi + rdx + 8]
          448  +        xor rax, [rdi + rdx + 8]
          449  +        jnz LeaveLoopCmps8
          450  +
          451  +
          452  +        mov rax, [rsi + rdx + 8+8]
          453  +        xor rax, [rdi + rdx + 8+8]
          454  +        jnz LeaveLoopCmps16
          455  +
          456  +        add rdx,8+8+8
          457  +
          458  +		BEFORE_JMP
          459  +        jnz  LoopCmps
          460  +        jmp  LenMaximum
          461  +		AFTER_JMP
          462  +		
          463  +LeaveLoopCmps16: add rdx,8
          464  +LeaveLoopCmps8: add rdx,8
          465  +LeaveLoopCmps:
          466  +
          467  +        test    eax, 0x0000FFFF
          468  +        jnz LenLower
          469  +
          470  +        test eax,0xffffffff
          471  +
          472  +        jnz LenLower32
          473  +
          474  +        add rdx,4
          475  +        shr rax,32
          476  +        or ax,ax
          477  +		BEFORE_JMP
          478  +        jnz LenLower
          479  +		AFTER_JMP
          480  +
          481  +LenLower32:
          482  +        shr eax,16
          483  +        add rdx,2
          484  +		
          485  +LenLower:		
          486  +        sub al, 1
          487  +        adc rdx, 0
          488  +//;;; Calculate the length of the match. If it is longer than MAX_MATCH,
          489  +//;;; then automatically accept it as the best possible match and leave.
          490  +
          491  +        lea rax, [rdi + rdx]
          492  +        sub rax, r9
          493  +        cmp eax, MAX_MATCH
          494  +		BEFORE_JMP
          495  +        jge LenMaximum
          496  +		AFTER_JMP
          497  +/*
          498  +;;; If the length of the match is not longer than the best match we
          499  +;;; have so far, then forget it and return to the lookup loop.
          500  +;///////////////////////////////////
          501  +*/
          502  +        cmp eax, r11d
          503  +        jg  LongerMatch
          504  +
          505  +        lea rsi,[r10+r11]
          506  +
          507  +        mov rdi, prev_ad
          508  +        mov edx, [chainlenwmask]
          509  +		BEFORE_JMP
          510  +        jmp LookupLoop
          511  +		AFTER_JMP
          512  +/*
          513  +;;;         s->match_start = cur_match;
          514  +;;;         best_len = len;
          515  +;;;         if (len >= nice_match) break;
          516  +;;;         scan_end = *(ushf*)(scan+best_len-1);
          517  +*/
          518  +LongerMatch:
          519  +        mov r11d, eax
          520  +        mov match_start, r8d
          521  +        cmp eax, [nicematch]
          522  +		BEFORE_JMP
          523  +        jge LeaveNow
          524  +		AFTER_JMP
          525  +
          526  +        lea rsi,[r10+rax]
          527  +
          528  +        movzx   ebx, word ptr [r9 + rax - 1]
          529  +        mov rdi, prev_ad
          530  +        mov edx, [chainlenwmask]
          531  +		BEFORE_JMP
          532  +        jmp LookupLoop
          533  +		AFTER_JMP
          534  +
          535  +//;;; Accept the current string, with the maximum possible length.
          536  +
          537  +LenMaximum:
          538  +        mov r11d,MAX_MATCH
          539  +        mov match_start, r8d
          540  +
          541  +//;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
          542  +//;;; return s->lookahead;
          543  +
          544  +LeaveNow:
          545  +        mov eax, Lookahead
          546  +        cmp r11d, eax
          547  +        cmovng eax, r11d
          548  +
          549  +
          550  +
          551  +//;;; Restore the stack and return from whence we came.
          552  +
          553  +
          554  +//        mov rsi,[save_rsi]
          555  +//        mov rdi,[save_rdi]
          556  +        mov rbx,[save_rbx]
          557  +        mov rbp,[save_rbp]
          558  +        mov r12,[save_r12]
          559  +        mov r13,[save_r13]
          560  +        mov r14,[save_r14]
          561  +        mov r15,[save_r15]
          562  +
          563  +
          564  +        ret 0
          565  +//; please don't remove this string !
          566  +//; Your can freely use gvmat64 in any free or commercial app
          567  +//; but it is far better don't remove the string in the binary!
          568  + //   db     0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0
          569  +
          570  +
          571  +match_init:
          572  +  ret 0
          573  +
          574  +

Added compat/zlib/contrib/infback9/README.

            1  +See infback9.h for what this is and how to use it.

Added compat/zlib/contrib/infback9/infback9.c.

            1  +/* infback9.c -- inflate deflate64 data using a call-back interface
            2  + * Copyright (C) 1995-2008 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "zutil.h"
            7  +#include "infback9.h"
            8  +#include "inftree9.h"
            9  +#include "inflate9.h"
           10  +
           11  +#define WSIZE 65536UL
           12  +
           13  +/*
           14  +   strm provides memory allocation functions in zalloc and zfree, or
           15  +   Z_NULL to use the library memory allocation functions.
           16  +
           17  +   window is a user-supplied window and output buffer that is 64K bytes.
           18  + */
           19  +int ZEXPORT inflateBack9Init_(strm, window, version, stream_size)
           20  +z_stream FAR *strm;
           21  +unsigned char FAR *window;
           22  +const char *version;
           23  +int stream_size;
           24  +{
           25  +    struct inflate_state FAR *state;
           26  +
           27  +    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
           28  +        stream_size != (int)(sizeof(z_stream)))
           29  +        return Z_VERSION_ERROR;
           30  +    if (strm == Z_NULL || window == Z_NULL)
           31  +        return Z_STREAM_ERROR;
           32  +    strm->msg = Z_NULL;                 /* in case we return an error */
           33  +    if (strm->zalloc == (alloc_func)0) {
           34  +        strm->zalloc = zcalloc;
           35  +        strm->opaque = (voidpf)0;
           36  +    }
           37  +    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
           38  +    state = (struct inflate_state FAR *)ZALLOC(strm, 1,
           39  +                                               sizeof(struct inflate_state));
           40  +    if (state == Z_NULL) return Z_MEM_ERROR;
           41  +    Tracev((stderr, "inflate: allocated\n"));
           42  +    strm->state = (voidpf)state;
           43  +    state->window = window;
           44  +    return Z_OK;
           45  +}
           46  +
           47  +/*
           48  +   Build and output length and distance decoding tables for fixed code
           49  +   decoding.
           50  + */
           51  +#ifdef MAKEFIXED
           52  +#include <stdio.h>
           53  +
           54  +void makefixed9(void)
           55  +{
           56  +    unsigned sym, bits, low, size;
           57  +    code *next, *lenfix, *distfix;
           58  +    struct inflate_state state;
           59  +    code fixed[544];
           60  +
           61  +    /* literal/length table */
           62  +    sym = 0;
           63  +    while (sym < 144) state.lens[sym++] = 8;
           64  +    while (sym < 256) state.lens[sym++] = 9;
           65  +    while (sym < 280) state.lens[sym++] = 7;
           66  +    while (sym < 288) state.lens[sym++] = 8;
           67  +    next = fixed;
           68  +    lenfix = next;
           69  +    bits = 9;
           70  +    inflate_table9(LENS, state.lens, 288, &(next), &(bits), state.work);
           71  +
           72  +    /* distance table */
           73  +    sym = 0;
           74  +    while (sym < 32) state.lens[sym++] = 5;
           75  +    distfix = next;
           76  +    bits = 5;
           77  +    inflate_table9(DISTS, state.lens, 32, &(next), &(bits), state.work);
           78  +
           79  +    /* write tables */
           80  +    puts("    /* inffix9.h -- table for decoding deflate64 fixed codes");
           81  +    puts("     * Generated automatically by makefixed9().");
           82  +    puts("     */");
           83  +    puts("");
           84  +    puts("    /* WARNING: this file should *not* be used by applications.");
           85  +    puts("       It is part of the implementation of this library and is");
           86  +    puts("       subject to change. Applications should only use zlib.h.");
           87  +    puts("     */");
           88  +    puts("");
           89  +    size = 1U << 9;
           90  +    printf("    static const code lenfix[%u] = {", size);
           91  +    low = 0;
           92  +    for (;;) {
           93  +        if ((low % 6) == 0) printf("\n        ");
           94  +        printf("{%u,%u,%d}", lenfix[low].op, lenfix[low].bits,
           95  +               lenfix[low].val);
           96  +        if (++low == size) break;
           97  +        putchar(',');
           98  +    }
           99  +    puts("\n    };");
          100  +    size = 1U << 5;
          101  +    printf("\n    static const code distfix[%u] = {", size);
          102  +    low = 0;
          103  +    for (;;) {
          104  +        if ((low % 5) == 0) printf("\n        ");
          105  +        printf("{%u,%u,%d}", distfix[low].op, distfix[low].bits,
          106  +               distfix[low].val);
          107  +        if (++low == size) break;
          108  +        putchar(',');
          109  +    }
          110  +    puts("\n    };");
          111  +}
          112  +#endif /* MAKEFIXED */
          113  +
          114  +/* Macros for inflateBack(): */
          115  +
          116  +/* Clear the input bit accumulator */
          117  +#define INITBITS() \
          118  +    do { \
          119  +        hold = 0; \
          120  +        bits = 0; \
          121  +    } while (0)
          122  +
          123  +/* Assure that some input is available.  If input is requested, but denied,
          124  +   then return a Z_BUF_ERROR from inflateBack(). */
          125  +#define PULL() \
          126  +    do { \
          127  +        if (have == 0) { \
          128  +            have = in(in_desc, &next); \
          129  +            if (have == 0) { \
          130  +                next = Z_NULL; \
          131  +                ret = Z_BUF_ERROR; \
          132  +                goto inf_leave; \
          133  +            } \
          134  +        } \
          135  +    } while (0)
          136  +
          137  +/* Get a byte of input into the bit accumulator, or return from inflateBack()
          138  +   with an error if there is no input available. */
          139  +#define PULLBYTE() \
          140  +    do { \
          141  +        PULL(); \
          142  +        have--; \
          143  +        hold += (unsigned long)(*next++) << bits; \
          144  +        bits += 8; \
          145  +    } while (0)
          146  +
          147  +/* Assure that there are at least n bits in the bit accumulator.  If there is
          148  +   not enough available input to do that, then return from inflateBack() with
          149  +   an error. */
          150  +#define NEEDBITS(n) \
          151  +    do { \
          152  +        while (bits < (unsigned)(n)) \
          153  +            PULLBYTE(); \
          154  +    } while (0)
          155  +
          156  +/* Return the low n bits of the bit accumulator (n <= 16) */
          157  +#define BITS(n) \
          158  +    ((unsigned)hold & ((1U << (n)) - 1))
          159  +
          160  +/* Remove n bits from the bit accumulator */
          161  +#define DROPBITS(n) \
          162  +    do { \
          163  +        hold >>= (n); \
          164  +        bits -= (unsigned)(n); \
          165  +    } while (0)
          166  +
          167  +/* Remove zero to seven bits as needed to go to a byte boundary */
          168  +#define BYTEBITS() \
          169  +    do { \
          170  +        hold >>= bits & 7; \
          171  +        bits -= bits & 7; \
          172  +    } while (0)
          173  +
          174  +/* Assure that some output space is available, by writing out the window
          175  +   if it's full.  If the write fails, return from inflateBack() with a
          176  +   Z_BUF_ERROR. */
          177  +#define ROOM() \
          178  +    do { \
          179  +        if (left == 0) { \
          180  +            put = window; \
          181  +            left = WSIZE; \
          182  +            wrap = 1; \
          183  +            if (out(out_desc, put, (unsigned)left)) { \
          184  +                ret = Z_BUF_ERROR; \
          185  +                goto inf_leave; \
          186  +            } \
          187  +        } \
          188  +    } while (0)
          189  +
          190  +/*
          191  +   strm provides the memory allocation functions and window buffer on input,
          192  +   and provides information on the unused input on return.  For Z_DATA_ERROR
          193  +   returns, strm will also provide an error message.
          194  +
          195  +   in() and out() are the call-back input and output functions.  When
          196  +   inflateBack() needs more input, it calls in().  When inflateBack() has
          197  +   filled the window with output, or when it completes with data in the
          198  +   window, it calls out() to write out the data.  The application must not
          199  +   change the provided input until in() is called again or inflateBack()
          200  +   returns.  The application must not change the window/output buffer until
          201  +   inflateBack() returns.
          202  +
          203  +   in() and out() are called with a descriptor parameter provided in the
          204  +   inflateBack() call.  This parameter can be a structure that provides the
          205  +   information required to do the read or write, as well as accumulated
          206  +   information on the input and output such as totals and check values.
          207  +
          208  +   in() should return zero on failure.  out() should return non-zero on
          209  +   failure.  If either in() or out() fails, than inflateBack() returns a
          210  +   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it
          211  +   was in() or out() that caused in the error.  Otherwise,  inflateBack()
          212  +   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
          213  +   error, or Z_MEM_ERROR if it could not allocate memory for the state.
          214  +   inflateBack() can also return Z_STREAM_ERROR if the input parameters
          215  +   are not correct, i.e. strm is Z_NULL or the state was not initialized.
          216  + */
          217  +int ZEXPORT inflateBack9(strm, in, in_desc, out, out_desc)
          218  +z_stream FAR *strm;
          219  +in_func in;
          220  +void FAR *in_desc;
          221  +out_func out;
          222  +void FAR *out_desc;
          223  +{
          224  +    struct inflate_state FAR *state;
          225  +    unsigned char FAR *next;    /* next input */
          226  +    unsigned char FAR *put;     /* next output */
          227  +    unsigned have;              /* available input */
          228  +    unsigned long left;         /* available output */
          229  +    inflate_mode mode;          /* current inflate mode */
          230  +    int lastblock;              /* true if processing last block */
          231  +    int wrap;                   /* true if the window has wrapped */
          232  +    unsigned long write;        /* window write index */
          233  +    unsigned char FAR *window;  /* allocated sliding window, if needed */
          234  +    unsigned long hold;         /* bit buffer */
          235  +    unsigned bits;              /* bits in bit buffer */
          236  +    unsigned extra;             /* extra bits needed */
          237  +    unsigned long length;       /* literal or length of data to copy */
          238  +    unsigned long offset;       /* distance back to copy string from */
          239  +    unsigned long copy;         /* number of stored or match bytes to copy */
          240  +    unsigned char FAR *from;    /* where to copy match bytes from */
          241  +    code const FAR *lencode;    /* starting table for length/literal codes */
          242  +    code const FAR *distcode;   /* starting table for distance codes */
          243  +    unsigned lenbits;           /* index bits for lencode */
          244  +    unsigned distbits;          /* index bits for distcode */
          245  +    code here;                  /* current decoding table entry */
          246  +    code last;                  /* parent table entry */
          247  +    unsigned len;               /* length to copy for repeats, bits to drop */
          248  +    int ret;                    /* return code */
          249  +    static const unsigned short order[19] = /* permutation of code lengths */
          250  +        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
          251  +#include "inffix9.h"
          252  +
          253  +    /* Check that the strm exists and that the state was initialized */
          254  +    if (strm == Z_NULL || strm->state == Z_NULL)
          255  +        return Z_STREAM_ERROR;
          256  +    state = (struct inflate_state FAR *)strm->state;
          257  +
          258  +    /* Reset the state */
          259  +    strm->msg = Z_NULL;
          260  +    mode = TYPE;
          261  +    lastblock = 0;
          262  +    write = 0;
          263  +    wrap = 0;
          264  +    window = state->window;
          265  +    next = strm->next_in;
          266  +    have = next != Z_NULL ? strm->avail_in : 0;
          267  +    hold = 0;
          268  +    bits = 0;
          269  +    put = window;
          270  +    left = WSIZE;
          271  +    lencode = Z_NULL;
          272  +    distcode = Z_NULL;
          273  +
          274  +    /* Inflate until end of block marked as last */
          275  +    for (;;)
          276  +        switch (mode) {
          277  +        case TYPE:
          278  +            /* determine and dispatch block type */
          279  +            if (lastblock) {
          280  +                BYTEBITS();
          281  +                mode = DONE;
          282  +                break;
          283  +            }
          284  +            NEEDBITS(3);
          285  +            lastblock = BITS(1);
          286  +            DROPBITS(1);
          287  +            switch (BITS(2)) {
          288  +            case 0:                             /* stored block */
          289  +                Tracev((stderr, "inflate:     stored block%s\n",
          290  +                        lastblock ? " (last)" : ""));
          291  +                mode = STORED;
          292  +                break;
          293  +            case 1:                             /* fixed block */
          294  +                lencode = lenfix;
          295  +                lenbits = 9;
          296  +                distcode = distfix;
          297  +                distbits = 5;
          298  +                Tracev((stderr, "inflate:     fixed codes block%s\n",
          299  +                        lastblock ? " (last)" : ""));
          300  +                mode = LEN;                     /* decode codes */
          301  +                break;
          302  +            case 2:                             /* dynamic block */
          303  +                Tracev((stderr, "inflate:     dynamic codes block%s\n",
          304  +                        lastblock ? " (last)" : ""));
          305  +                mode = TABLE;
          306  +                break;
          307  +            case 3:
          308  +                strm->msg = (char *)"invalid block type";
          309  +                mode = BAD;
          310  +            }
          311  +            DROPBITS(2);
          312  +            break;
          313  +
          314  +        case STORED:
          315  +            /* get and verify stored block length */
          316  +            BYTEBITS();                         /* go to byte boundary */
          317  +            NEEDBITS(32);
          318  +            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
          319  +                strm->msg = (char *)"invalid stored block lengths";
          320  +                mode = BAD;
          321  +                break;
          322  +            }
          323  +            length = (unsigned)hold & 0xffff;
          324  +            Tracev((stderr, "inflate:       stored length %lu\n",
          325  +                    length));
          326  +            INITBITS();
          327  +
          328  +            /* copy stored block from input to output */
          329  +            while (length != 0) {
          330  +                copy = length;
          331  +                PULL();
          332  +                ROOM();
          333  +                if (copy > have) copy = have;
          334  +                if (copy > left) copy = left;
          335  +                zmemcpy(put, next, copy);
          336  +                have -= copy;
          337  +                next += copy;
          338  +                left -= copy;
          339  +                put += copy;
          340  +                length -= copy;
          341  +            }
          342  +            Tracev((stderr, "inflate:       stored end\n"));
          343  +            mode = TYPE;
          344  +            break;
          345  +
          346  +        case TABLE:
          347  +            /* get dynamic table entries descriptor */
          348  +            NEEDBITS(14);
          349  +            state->nlen = BITS(5) + 257;
          350  +            DROPBITS(5);
          351  +            state->ndist = BITS(5) + 1;
          352  +            DROPBITS(5);
          353  +            state->ncode = BITS(4) + 4;
          354  +            DROPBITS(4);
          355  +            if (state->nlen > 286) {
          356  +                strm->msg = (char *)"too many length symbols";
          357  +                mode = BAD;
          358  +                break;
          359  +            }
          360  +            Tracev((stderr, "inflate:       table sizes ok\n"));
          361  +
          362  +            /* get code length code lengths (not a typo) */
          363  +            state->have = 0;
          364  +            while (state->have < state->ncode) {
          365  +                NEEDBITS(3);
          366  +                state->lens[order[state->have++]] = (unsigned short)BITS(3);
          367  +                DROPBITS(3);
          368  +            }
          369  +            while (state->have < 19)
          370  +                state->lens[order[state->have++]] = 0;
          371  +            state->next = state->codes;
          372  +            lencode = (code const FAR *)(state->next);
          373  +            lenbits = 7;
          374  +            ret = inflate_table9(CODES, state->lens, 19, &(state->next),
          375  +                                &(lenbits), state->work);
          376  +            if (ret) {
          377  +                strm->msg = (char *)"invalid code lengths set";
          378  +                mode = BAD;
          379  +                break;
          380  +            }
          381  +            Tracev((stderr, "inflate:       code lengths ok\n"));
          382  +
          383  +            /* get length and distance code code lengths */
          384  +            state->have = 0;
          385  +            while (state->have < state->nlen + state->ndist) {
          386  +                for (;;) {
          387  +                    here = lencode[BITS(lenbits)];
          388  +                    if ((unsigned)(here.bits) <= bits) break;
          389  +                    PULLBYTE();
          390  +                }
          391  +                if (here.val < 16) {
          392  +                    NEEDBITS(here.bits);
          393  +                    DROPBITS(here.bits);
          394  +                    state->lens[state->have++] = here.val;
          395  +                }
          396  +                else {
          397  +                    if (here.val == 16) {
          398  +                        NEEDBITS(here.bits + 2);
          399  +                        DROPBITS(here.bits);
          400  +                        if (state->have == 0) {
          401  +                            strm->msg = (char *)"invalid bit length repeat";
          402  +                            mode = BAD;
          403  +                            break;
          404  +                        }
          405  +                        len = (unsigned)(state->lens[state->have - 1]);
          406  +                        copy = 3 + BITS(2);
          407  +                        DROPBITS(2);
          408  +                    }
          409  +                    else if (here.val == 17) {
          410  +                        NEEDBITS(here.bits + 3);
          411  +                        DROPBITS(here.bits);
          412  +                        len = 0;
          413  +                        copy = 3 + BITS(3);
          414  +                        DROPBITS(3);
          415  +                    }
          416  +                    else {
          417  +                        NEEDBITS(here.bits + 7);
          418  +                        DROPBITS(here.bits);
          419  +                        len = 0;
          420  +                        copy = 11 + BITS(7);
          421  +                        DROPBITS(7);
          422  +                    }
          423  +                    if (state->have + copy > state->nlen + state->ndist) {
          424  +                        strm->msg = (char *)"invalid bit length repeat";
          425  +                        mode = BAD;
          426  +                        break;
          427  +                    }
          428  +                    while (copy--)
          429  +                        state->lens[state->have++] = (unsigned short)len;
          430  +                }
          431  +            }
          432  +
          433  +            /* handle error breaks in while */
          434  +            if (mode == BAD) break;
          435  +
          436  +            /* check for end-of-block code (better have one) */
          437  +            if (state->lens[256] == 0) {
          438  +                strm->msg = (char *)"invalid code -- missing end-of-block";
          439  +                mode = BAD;
          440  +                break;
          441  +            }
          442  +
          443  +            /* build code tables -- note: do not change the lenbits or distbits
          444  +               values here (9 and 6) without reading the comments in inftree9.h
          445  +               concerning the ENOUGH constants, which depend on those values */
          446  +            state->next = state->codes;
          447  +            lencode = (code const FAR *)(state->next);
          448  +            lenbits = 9;
          449  +            ret = inflate_table9(LENS, state->lens, state->nlen,
          450  +                            &(state->next), &(lenbits), state->work);
          451  +            if (ret) {
          452  +                strm->msg = (char *)"invalid literal/lengths set";
          453  +                mode = BAD;
          454  +                break;
          455  +            }
          456  +            distcode = (code const FAR *)(state->next);
          457  +            distbits = 6;
          458  +            ret = inflate_table9(DISTS, state->lens + state->nlen,
          459  +                            state->ndist, &(state->next), &(distbits),
          460  +                            state->work);
          461  +            if (ret) {
          462  +                strm->msg = (char *)"invalid distances set";
          463  +                mode = BAD;
          464  +                break;
          465  +            }
          466  +            Tracev((stderr, "inflate:       codes ok\n"));
          467  +            mode = LEN;
          468  +
          469  +        case LEN:
          470  +            /* get a literal, length, or end-of-block code */
          471  +            for (;;) {
          472  +                here = lencode[BITS(lenbits)];
          473  +                if ((unsigned)(here.bits) <= bits) break;
          474  +                PULLBYTE();
          475  +            }
          476  +            if (here.op && (here.op & 0xf0) == 0) {
          477  +                last = here;
          478  +                for (;;) {
          479  +                    here = lencode[last.val +
          480  +                            (BITS(last.bits + last.op) >> last.bits)];
          481  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
          482  +                    PULLBYTE();
          483  +                }
          484  +                DROPBITS(last.bits);
          485  +            }
          486  +            DROPBITS(here.bits);
          487  +            length = (unsigned)here.val;
          488  +
          489  +            /* process literal */
          490  +            if (here.op == 0) {
          491  +                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
          492  +                        "inflate:         literal '%c'\n" :
          493  +                        "inflate:         literal 0x%02x\n", here.val));
          494  +                ROOM();
          495  +                *put++ = (unsigned char)(length);
          496  +                left--;
          497  +                mode = LEN;
          498  +                break;
          499  +            }
          500  +
          501  +            /* process end of block */
          502  +            if (here.op & 32) {
          503  +                Tracevv((stderr, "inflate:         end of block\n"));
          504  +                mode = TYPE;
          505  +                break;
          506  +            }
          507  +
          508  +            /* invalid code */
          509  +            if (here.op & 64) {
          510  +                strm->msg = (char *)"invalid literal/length code";
          511  +                mode = BAD;
          512  +                break;
          513  +            }
          514  +
          515  +            /* length code -- get extra bits, if any */
          516  +            extra = (unsigned)(here.op) & 31;
          517  +            if (extra != 0) {
          518  +                NEEDBITS(extra);
          519  +                length += BITS(extra);
          520  +                DROPBITS(extra);
          521  +            }
          522  +            Tracevv((stderr, "inflate:         length %lu\n", length));
          523  +
          524  +            /* get distance code */
          525  +            for (;;) {
          526  +                here = distcode[BITS(distbits)];
          527  +                if ((unsigned)(here.bits) <= bits) break;
          528  +                PULLBYTE();
          529  +            }
          530  +            if ((here.op & 0xf0) == 0) {
          531  +                last = here;
          532  +                for (;;) {
          533  +                    here = distcode[last.val +
          534  +                            (BITS(last.bits + last.op) >> last.bits)];
          535  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
          536  +                    PULLBYTE();
          537  +                }
          538  +                DROPBITS(last.bits);
          539  +            }
          540  +            DROPBITS(here.bits);
          541  +            if (here.op & 64) {
          542  +                strm->msg = (char *)"invalid distance code";
          543  +                mode = BAD;
          544  +                break;
          545  +            }
          546  +            offset = (unsigned)here.val;
          547  +
          548  +            /* get distance extra bits, if any */
          549  +            extra = (unsigned)(here.op) & 15;
          550  +            if (extra != 0) {
          551  +                NEEDBITS(extra);
          552  +                offset += BITS(extra);
          553  +                DROPBITS(extra);
          554  +            }
          555  +            if (offset > WSIZE - (wrap ? 0: left)) {
          556  +                strm->msg = (char *)"invalid distance too far back";
          557  +                mode = BAD;
          558  +                break;
          559  +            }
          560  +            Tracevv((stderr, "inflate:         distance %lu\n", offset));
          561  +
          562  +            /* copy match from window to output */
          563  +            do {
          564  +                ROOM();
          565  +                copy = WSIZE - offset;
          566  +                if (copy < left) {
          567  +                    from = put + copy;
          568  +                    copy = left - copy;
          569  +                }
          570  +                else {
          571  +                    from = put - offset;
          572  +                    copy = left;
          573  +                }
          574  +                if (copy > length) copy = length;
          575  +                length -= copy;
          576  +                left -= copy;
          577  +                do {
          578  +                    *put++ = *from++;
          579  +                } while (--copy);
          580  +            } while (length != 0);
          581  +            break;
          582  +
          583  +        case DONE:
          584  +            /* inflate stream terminated properly -- write leftover output */
          585  +            ret = Z_STREAM_END;
          586  +            if (left < WSIZE) {
          587  +                if (out(out_desc, window, (unsigned)(WSIZE - left)))
          588  +                    ret = Z_BUF_ERROR;
          589  +            }
          590  +            goto inf_leave;
          591  +
          592  +        case BAD:
          593  +            ret = Z_DATA_ERROR;
          594  +            goto inf_leave;
          595  +
          596  +        default:                /* can't happen, but makes compilers happy */
          597  +            ret = Z_STREAM_ERROR;
          598  +            goto inf_leave;
          599  +        }
          600  +
          601  +    /* Return unused input */
          602  +  inf_leave:
          603  +    strm->next_in = next;
          604  +    strm->avail_in = have;
          605  +    return ret;
          606  +}
          607  +
          608  +int ZEXPORT inflateBack9End(strm)
          609  +z_stream FAR *strm;
          610  +{
          611  +    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
          612  +        return Z_STREAM_ERROR;
          613  +    ZFREE(strm, strm->state);
          614  +    strm->state = Z_NULL;
          615  +    Tracev((stderr, "inflate: end\n"));
          616  +    return Z_OK;
          617  +}

Added compat/zlib/contrib/infback9/infback9.h.

            1  +/* infback9.h -- header for using inflateBack9 functions
            2  + * Copyright (C) 2003 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/*
            7  + * This header file and associated patches provide a decoder for PKWare's
            8  + * undocumented deflate64 compression method (method 9).  Use with infback9.c,
            9  + * inftree9.h, inftree9.c, and inffix9.h.  These patches are not supported.
           10  + * This should be compiled with zlib, since it uses zutil.h and zutil.o.
           11  + * This code has not yet been tested on 16-bit architectures.  See the
           12  + * comments in zlib.h for inflateBack() usage.  These functions are used
           13  + * identically, except that there is no windowBits parameter, and a 64K
           14  + * window must be provided.  Also if int's are 16 bits, then a zero for
           15  + * the third parameter of the "out" function actually means 65536UL.
           16  + * zlib.h must be included before this header file.
           17  + */
           18  +
           19  +#ifdef __cplusplus
           20  +extern "C" {
           21  +#endif
           22  +
           23  +ZEXTERN int ZEXPORT inflateBack9 OF((z_stream FAR *strm,
           24  +                                    in_func in, void FAR *in_desc,
           25  +                                    out_func out, void FAR *out_desc));
           26  +ZEXTERN int ZEXPORT inflateBack9End OF((z_stream FAR *strm));
           27  +ZEXTERN int ZEXPORT inflateBack9Init_ OF((z_stream FAR *strm,
           28  +                                         unsigned char FAR *window,
           29  +                                         const char *version,
           30  +                                         int stream_size));
           31  +#define inflateBack9Init(strm, window) \
           32  +        inflateBack9Init_((strm), (window), \
           33  +        ZLIB_VERSION, sizeof(z_stream))
           34  +
           35  +#ifdef __cplusplus
           36  +}
           37  +#endif

Added compat/zlib/contrib/infback9/inffix9.h.

            1  +    /* inffix9.h -- table for decoding deflate64 fixed codes
            2  +     * Generated automatically by makefixed9().
            3  +     */
            4  +
            5  +    /* WARNING: this file should *not* be used by applications.
            6  +       It is part of the implementation of this library and is
            7  +       subject to change. Applications should only use zlib.h.
            8  +     */
            9  +
           10  +    static const code lenfix[512] = {
           11  +        {96,7,0},{0,8,80},{0,8,16},{132,8,115},{130,7,31},{0,8,112},
           12  +        {0,8,48},{0,9,192},{128,7,10},{0,8,96},{0,8,32},{0,9,160},
           13  +        {0,8,0},{0,8,128},{0,8,64},{0,9,224},{128,7,6},{0,8,88},
           14  +        {0,8,24},{0,9,144},{131,7,59},{0,8,120},{0,8,56},{0,9,208},
           15  +        {129,7,17},{0,8,104},{0,8,40},{0,9,176},{0,8,8},{0,8,136},
           16  +        {0,8,72},{0,9,240},{128,7,4},{0,8,84},{0,8,20},{133,8,227},
           17  +        {131,7,43},{0,8,116},{0,8,52},{0,9,200},{129,7,13},{0,8,100},
           18  +        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},
           19  +        {128,7,8},{0,8,92},{0,8,28},{0,9,152},{132,7,83},{0,8,124},
           20  +        {0,8,60},{0,9,216},{130,7,23},{0,8,108},{0,8,44},{0,9,184},
           21  +        {0,8,12},{0,8,140},{0,8,76},{0,9,248},{128,7,3},{0,8,82},
           22  +        {0,8,18},{133,8,163},{131,7,35},{0,8,114},{0,8,50},{0,9,196},
           23  +        {129,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},{0,8,130},
           24  +        {0,8,66},{0,9,228},{128,7,7},{0,8,90},{0,8,26},{0,9,148},
           25  +        {132,7,67},{0,8,122},{0,8,58},{0,9,212},{130,7,19},{0,8,106},
           26  +        {0,8,42},{0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},
           27  +        {128,7,5},{0,8,86},{0,8,22},{65,8,0},{131,7,51},{0,8,118},
           28  +        {0,8,54},{0,9,204},{129,7,15},{0,8,102},{0,8,38},{0,9,172},
           29  +        {0,8,6},{0,8,134},{0,8,70},{0,9,236},{128,7,9},{0,8,94},
           30  +        {0,8,30},{0,9,156},{132,7,99},{0,8,126},{0,8,62},{0,9,220},
           31  +        {130,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
           32  +        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{133,8,131},
           33  +        {130,7,31},{0,8,113},{0,8,49},{0,9,194},{128,7,10},{0,8,97},
           34  +        {0,8,33},{0,9,162},{0,8,1},{0,8,129},{0,8,65},{0,9,226},
           35  +        {128,7,6},{0,8,89},{0,8,25},{0,9,146},{131,7,59},{0,8,121},
           36  +        {0,8,57},{0,9,210},{129,7,17},{0,8,105},{0,8,41},{0,9,178},
           37  +        {0,8,9},{0,8,137},{0,8,73},{0,9,242},{128,7,4},{0,8,85},
           38  +        {0,8,21},{144,8,3},{131,7,43},{0,8,117},{0,8,53},{0,9,202},
           39  +        {129,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},
           40  +        {0,8,69},{0,9,234},{128,7,8},{0,8,93},{0,8,29},{0,9,154},
           41  +        {132,7,83},{0,8,125},{0,8,61},{0,9,218},{130,7,23},{0,8,109},
           42  +        {0,8,45},{0,9,186},{0,8,13},{0,8,141},{0,8,77},{0,9,250},
           43  +        {128,7,3},{0,8,83},{0,8,19},{133,8,195},{131,7,35},{0,8,115},
           44  +        {0,8,51},{0,9,198},{129,7,11},{0,8,99},{0,8,35},{0,9,166},
           45  +        {0,8,3},{0,8,131},{0,8,67},{0,9,230},{128,7,7},{0,8,91},
           46  +        {0,8,27},{0,9,150},{132,7,67},{0,8,123},{0,8,59},{0,9,214},
           47  +        {130,7,19},{0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},
           48  +        {0,8,75},{0,9,246},{128,7,5},{0,8,87},{0,8,23},{77,8,0},
           49  +        {131,7,51},{0,8,119},{0,8,55},{0,9,206},{129,7,15},{0,8,103},
           50  +        {0,8,39},{0,9,174},{0,8,7},{0,8,135},{0,8,71},{0,9,238},
           51  +        {128,7,9},{0,8,95},{0,8,31},{0,9,158},{132,7,99},{0,8,127},
           52  +        {0,8,63},{0,9,222},{130,7,27},{0,8,111},{0,8,47},{0,9,190},
           53  +        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},
           54  +        {0,8,16},{132,8,115},{130,7,31},{0,8,112},{0,8,48},{0,9,193},
           55  +        {128,7,10},{0,8,96},{0,8,32},{0,9,161},{0,8,0},{0,8,128},
           56  +        {0,8,64},{0,9,225},{128,7,6},{0,8,88},{0,8,24},{0,9,145},
           57  +        {131,7,59},{0,8,120},{0,8,56},{0,9,209},{129,7,17},{0,8,104},
           58  +        {0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},{0,9,241},
           59  +        {128,7,4},{0,8,84},{0,8,20},{133,8,227},{131,7,43},{0,8,116},
           60  +        {0,8,52},{0,9,201},{129,7,13},{0,8,100},{0,8,36},{0,9,169},
           61  +        {0,8,4},{0,8,132},{0,8,68},{0,9,233},{128,7,8},{0,8,92},
           62  +        {0,8,28},{0,9,153},{132,7,83},{0,8,124},{0,8,60},{0,9,217},
           63  +        {130,7,23},{0,8,108},{0,8,44},{0,9,185},{0,8,12},{0,8,140},
           64  +        {0,8,76},{0,9,249},{128,7,3},{0,8,82},{0,8,18},{133,8,163},
           65  +        {131,7,35},{0,8,114},{0,8,50},{0,9,197},{129,7,11},{0,8,98},
           66  +        {0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
           67  +        {128,7,7},{0,8,90},{0,8,26},{0,9,149},{132,7,67},{0,8,122},
           68  +        {0,8,58},{0,9,213},{130,7,19},{0,8,106},{0,8,42},{0,9,181},
           69  +        {0,8,10},{0,8,138},{0,8,74},{0,9,245},{128,7,5},{0,8,86},
           70  +        {0,8,22},{65,8,0},{131,7,51},{0,8,118},{0,8,54},{0,9,205},
           71  +        {129,7,15},{0,8,102},{0,8,38},{0,9,173},{0,8,6},{0,8,134},
           72  +        {0,8,70},{0,9,237},{128,7,9},{0,8,94},{0,8,30},{0,9,157},
           73  +        {132,7,99},{0,8,126},{0,8,62},{0,9,221},{130,7,27},{0,8,110},
           74  +        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},
           75  +        {96,7,0},{0,8,81},{0,8,17},{133,8,131},{130,7,31},{0,8,113},
           76  +        {0,8,49},{0,9,195},{128,7,10},{0,8,97},{0,8,33},{0,9,163},
           77  +        {0,8,1},{0,8,129},{0,8,65},{0,9,227},{128,7,6},{0,8,89},
           78  +        {0,8,25},{0,9,147},{131,7,59},{0,8,121},{0,8,57},{0,9,211},
           79  +        {129,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},{0,8,137},
           80  +        {0,8,73},{0,9,243},{128,7,4},{0,8,85},{0,8,21},{144,8,3},
           81  +        {131,7,43},{0,8,117},{0,8,53},{0,9,203},{129,7,13},{0,8,101},
           82  +        {0,8,37},{0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},
           83  +        {128,7,8},{0,8,93},{0,8,29},{0,9,155},{132,7,83},{0,8,125},
           84  +        {0,8,61},{0,9,219},{130,7,23},{0,8,109},{0,8,45},{0,9,187},
           85  +        {0,8,13},{0,8,141},{0,8,77},{0,9,251},{128,7,3},{0,8,83},
           86  +        {0,8,19},{133,8,195},{131,7,35},{0,8,115},{0,8,51},{0,9,199},
           87  +        {129,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
           88  +        {0,8,67},{0,9,231},{128,7,7},{0,8,91},{0,8,27},{0,9,151},
           89  +        {132,7,67},{0,8,123},{0,8,59},{0,9,215},{130,7,19},{0,8,107},
           90  +        {0,8,43},{0,9,183},{0,8,11},{0,8,139},{0,8,75},{0,9,247},
           91  +        {128,7,5},{0,8,87},{0,8,23},{77,8,0},{131,7,51},{0,8,119},
           92  +        {0,8,55},{0,9,207},{129,7,15},{0,8,103},{0,8,39},{0,9,175},
           93  +        {0,8,7},{0,8,135},{0,8,71},{0,9,239},{128,7,9},{0,8,95},
           94  +        {0,8,31},{0,9,159},{132,7,99},{0,8,127},{0,8,63},{0,9,223},
           95  +        {130,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},
           96  +        {0,8,79},{0,9,255}
           97  +    };
           98  +
           99  +    static const code distfix[32] = {
          100  +        {128,5,1},{135,5,257},{131,5,17},{139,5,4097},{129,5,5},
          101  +        {137,5,1025},{133,5,65},{141,5,16385},{128,5,3},{136,5,513},
          102  +        {132,5,33},{140,5,8193},{130,5,9},{138,5,2049},{134,5,129},
          103  +        {142,5,32769},{128,5,2},{135,5,385},{131,5,25},{139,5,6145},
          104  +        {129,5,7},{137,5,1537},{133,5,97},{141,5,24577},{128,5,4},
          105  +        {136,5,769},{132,5,49},{140,5,12289},{130,5,13},{138,5,3073},
          106  +        {134,5,193},{142,5,49153}
          107  +    };

Added compat/zlib/contrib/infback9/inflate9.h.

            1  +/* inflate9.h -- internal inflate state definition
            2  + * Copyright (C) 1995-2003 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* Possible inflate modes between inflate() calls */
           12  +typedef enum {
           13  +        TYPE,       /* i: waiting for type bits, including last-flag bit */
           14  +        STORED,     /* i: waiting for stored size (length and complement) */
           15  +        TABLE,      /* i: waiting for dynamic block table lengths */
           16  +            LEN,        /* i: waiting for length/lit code */
           17  +    DONE,       /* finished check, done -- remain here until reset */
           18  +    BAD         /* got a data error -- remain here until reset */
           19  +} inflate_mode;
           20  +
           21  +/*
           22  +    State transitions between above modes -
           23  +
           24  +    (most modes can go to the BAD mode -- not shown for clarity)
           25  +
           26  +    Read deflate blocks:
           27  +            TYPE -> STORED or TABLE or LEN or DONE
           28  +            STORED -> TYPE
           29  +            TABLE -> LENLENS -> CODELENS -> LEN
           30  +    Read deflate codes:
           31  +                LEN -> LEN or TYPE
           32  + */
           33  +
           34  +/* state maintained between inflate() calls.  Approximately 7K bytes. */
           35  +struct inflate_state {
           36  +        /* sliding window */
           37  +    unsigned char FAR *window;  /* allocated sliding window, if needed */
           38  +        /* dynamic table building */
           39  +    unsigned ncode;             /* number of code length code lengths */
           40  +    unsigned nlen;              /* number of length code lengths */
           41  +    unsigned ndist;             /* number of distance code lengths */
           42  +    unsigned have;              /* number of code lengths in lens[] */
           43  +    code FAR *next;             /* next available space in codes[] */
           44  +    unsigned short lens[320];   /* temporary storage for code lengths */
           45  +    unsigned short work[288];   /* work area for code table building */
           46  +    code codes[ENOUGH];         /* space for code tables */
           47  +};

Added compat/zlib/contrib/infback9/inftree9.c.

            1  +/* inftree9.c -- generate Huffman trees for efficient decoding
            2  + * Copyright (C) 1995-2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "zutil.h"
            7  +#include "inftree9.h"
            8  +
            9  +#define MAXBITS 15
           10  +
           11  +const char inflate9_copyright[] =
           12  +   " inflate9 1.2.7 Copyright 1995-2012 Mark Adler ";
           13  +/*
           14  +  If you use the zlib library in a product, an acknowledgment is welcome
           15  +  in the documentation of your product. If for some reason you cannot
           16  +  include such an acknowledgment, I would appreciate that you keep this
           17  +  copyright string in the executable of your product.
           18  + */
           19  +
           20  +/*
           21  +   Build a set of tables to decode the provided canonical Huffman code.
           22  +   The code lengths are lens[0..codes-1].  The result starts at *table,
           23  +   whose indices are 0..2^bits-1.  work is a writable array of at least
           24  +   lens shorts, which is used as a work area.  type is the type of code
           25  +   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
           26  +   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
           27  +   on return points to the next available entry's address.  bits is the
           28  +   requested root table index bits, and on return it is the actual root
           29  +   table index bits.  It will differ if the request is greater than the
           30  +   longest code or if it is less than the shortest code.
           31  + */
           32  +int inflate_table9(type, lens, codes, table, bits, work)
           33  +codetype type;
           34  +unsigned short FAR *lens;
           35  +unsigned codes;
           36  +code FAR * FAR *table;
           37  +unsigned FAR *bits;
           38  +unsigned short FAR *work;
           39  +{
           40  +    unsigned len;               /* a code's length in bits */
           41  +    unsigned sym;               /* index of code symbols */
           42  +    unsigned min, max;          /* minimum and maximum code lengths */
           43  +    unsigned root;              /* number of index bits for root table */
           44  +    unsigned curr;              /* number of index bits for current table */
           45  +    unsigned drop;              /* code bits to drop for sub-table */
           46  +    int left;                   /* number of prefix codes available */
           47  +    unsigned used;              /* code entries in table used */
           48  +    unsigned huff;              /* Huffman code */
           49  +    unsigned incr;              /* for incrementing code, index */
           50  +    unsigned fill;              /* index for replicating entries */
           51  +    unsigned low;               /* low bits for current root entry */
           52  +    unsigned mask;              /* mask for low root bits */
           53  +    code this;                  /* table entry for duplication */
           54  +    code FAR *next;             /* next available space in table */
           55  +    const unsigned short FAR *base;     /* base value table to use */
           56  +    const unsigned short FAR *extra;    /* extra bits table to use */
           57  +    int end;                    /* use base and extra for symbol > end */
           58  +    unsigned short count[MAXBITS+1];    /* number of codes of each length */
           59  +    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
           60  +    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
           61  +        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17,
           62  +        19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115,
           63  +        131, 163, 195, 227, 3, 0, 0};
           64  +    static const unsigned short lext[31] = { /* Length codes 257..285 extra */
           65  +        128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
           66  +        130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132,
           67  +        133, 133, 133, 133, 144, 78, 68};
           68  +    static const unsigned short dbase[32] = { /* Distance codes 0..31 base */
           69  +        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49,
           70  +        65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073,
           71  +        4097, 6145, 8193, 12289, 16385, 24577, 32769, 49153};
           72  +    static const unsigned short dext[32] = { /* Distance codes 0..31 extra */
           73  +        128, 128, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132,
           74  +        133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138,
           75  +        139, 139, 140, 140, 141, 141, 142, 142};
           76  +
           77  +    /*
           78  +       Process a set of code lengths to create a canonical Huffman code.  The
           79  +       code lengths are lens[0..codes-1].  Each length corresponds to the
           80  +       symbols 0..codes-1.  The Huffman code is generated by first sorting the
           81  +       symbols by length from short to long, and retaining the symbol order
           82  +       for codes with equal lengths.  Then the code starts with all zero bits
           83  +       for the first code of the shortest length, and the codes are integer
           84  +       increments for the same length, and zeros are appended as the length
           85  +       increases.  For the deflate format, these bits are stored backwards
           86  +       from their more natural integer increment ordering, and so when the
           87  +       decoding tables are built in the large loop below, the integer codes
           88  +       are incremented backwards.
           89  +
           90  +       This routine assumes, but does not check, that all of the entries in
           91  +       lens[] are in the range 0..MAXBITS.  The caller must assure this.
           92  +       1..MAXBITS is interpreted as that code length.  zero means that that
           93  +       symbol does not occur in this code.
           94  +
           95  +       The codes are sorted by computing a count of codes for each length,
           96  +       creating from that a table of starting indices for each length in the
           97  +       sorted table, and then entering the symbols in order in the sorted
           98  +       table.  The sorted table is work[], with that space being provided by
           99  +       the caller.
          100  +
          101  +       The length counts are used for other purposes as well, i.e. finding
          102  +       the minimum and maximum length codes, determining if there are any
          103  +       codes at all, checking for a valid set of lengths, and looking ahead
          104  +       at length counts to determine sub-table sizes when building the
          105  +       decoding tables.
          106  +     */
          107  +
          108  +    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
          109  +    for (len = 0; len <= MAXBITS; len++)
          110  +        count[len] = 0;
          111  +    for (sym = 0; sym < codes; sym++)
          112  +        count[lens[sym]]++;
          113  +
          114  +    /* bound code lengths, force root to be within code lengths */
          115  +    root = *bits;
          116  +    for (max = MAXBITS; max >= 1; max--)
          117  +        if (count[max] != 0) break;
          118  +    if (root > max) root = max;
          119  +    if (max == 0) return -1;            /* no codes! */
          120  +    for (min = 1; min <= MAXBITS; min++)
          121  +        if (count[min] != 0) break;
          122  +    if (root < min) root = min;
          123  +
          124  +    /* check for an over-subscribed or incomplete set of lengths */
          125  +    left = 1;
          126  +    for (len = 1; len <= MAXBITS; len++) {
          127  +        left <<= 1;
          128  +        left -= count[len];
          129  +        if (left < 0) return -1;        /* over-subscribed */
          130  +    }
          131  +    if (left > 0 && (type == CODES || max != 1))
          132  +        return -1;                      /* incomplete set */
          133  +
          134  +    /* generate offsets into symbol table for each length for sorting */
          135  +    offs[1] = 0;
          136  +    for (len = 1; len < MAXBITS; len++)
          137  +        offs[len + 1] = offs[len] + count[len];
          138  +
          139  +    /* sort symbols by length, by symbol order within each length */
          140  +    for (sym = 0; sym < codes; sym++)
          141  +        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
          142  +
          143  +    /*
          144  +       Create and fill in decoding tables.  In this loop, the table being
          145  +       filled is at next and has curr index bits.  The code being used is huff
          146  +       with length len.  That code is converted to an index by dropping drop
          147  +       bits off of the bottom.  For codes where len is less than drop + curr,
          148  +       those top drop + curr - len bits are incremented through all values to
          149  +       fill the table with replicated entries.
          150  +
          151  +       root is the number of index bits for the root table.  When len exceeds
          152  +       root, sub-tables are created pointed to by the root entry with an index
          153  +       of the low root bits of huff.  This is saved in low to check for when a
          154  +       new sub-table should be started.  drop is zero when the root table is
          155  +       being filled, and drop is root when sub-tables are being filled.
          156  +
          157  +       When a new sub-table is needed, it is necessary to look ahead in the
          158  +       code lengths to determine what size sub-table is needed.  The length
          159  +       counts are used for this, and so count[] is decremented as codes are
          160  +       entered in the tables.
          161  +
          162  +       used keeps track of how many table entries have been allocated from the
          163  +       provided *table space.  It is checked for LENS and DIST tables against
          164  +       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
          165  +       the initial root table size constants.  See the comments in inftree9.h
          166  +       for more information.
          167  +
          168  +       sym increments through all symbols, and the loop terminates when
          169  +       all codes of length max, i.e. all codes, have been processed.  This
          170  +       routine permits incomplete codes, so another loop after this one fills
          171  +       in the rest of the decoding tables with invalid code markers.
          172  +     */
          173  +
          174  +    /* set up for code type */
          175  +    switch (type) {
          176  +    case CODES:
          177  +        base = extra = work;    /* dummy value--not used */
          178  +        end = 19;
          179  +        break;
          180  +    case LENS:
          181  +        base = lbase;
          182  +        base -= 257;
          183  +        extra = lext;
          184  +        extra -= 257;
          185  +        end = 256;
          186  +        break;
          187  +    default:            /* DISTS */
          188  +        base = dbase;
          189  +        extra = dext;
          190  +        end = -1;
          191  +    }
          192  +
          193  +    /* initialize state for loop */
          194  +    huff = 0;                   /* starting code */
          195  +    sym = 0;                    /* starting code symbol */
          196  +    len = min;                  /* starting code length */
          197  +    next = *table;              /* current table to fill in */
          198  +    curr = root;                /* current table index bits */
          199  +    drop = 0;                   /* current bits to drop from code for index */
          200  +    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
          201  +    used = 1U << root;          /* use root table entries */
          202  +    mask = used - 1;            /* mask for comparing low */
          203  +
          204  +    /* check available table space */
          205  +    if ((type == LENS && used >= ENOUGH_LENS) ||
          206  +        (type == DISTS && used >= ENOUGH_DISTS))
          207  +        return 1;
          208  +
          209  +    /* process all codes and make table entries */
          210  +    for (;;) {
          211  +        /* create table entry */
          212  +        this.bits = (unsigned char)(len - drop);
          213  +        if ((int)(work[sym]) < end) {
          214  +            this.op = (unsigned char)0;
          215  +            this.val = work[sym];
          216  +        }
          217  +        else if ((int)(work[sym]) > end) {
          218  +            this.op = (unsigned char)(extra[work[sym]]);
          219  +            this.val = base[work[sym]];
          220  +        }
          221  +        else {
          222  +            this.op = (unsigned char)(32 + 64);         /* end of block */
          223  +            this.val = 0;
          224  +        }
          225  +
          226  +        /* replicate for those indices with low len bits equal to huff */
          227  +        incr = 1U << (len - drop);
          228  +        fill = 1U << curr;
          229  +        do {
          230  +            fill -= incr;
          231  +            next[(huff >> drop) + fill] = this;
          232  +        } while (fill != 0);
          233  +
          234  +        /* backwards increment the len-bit code huff */
          235  +        incr = 1U << (len - 1);
          236  +        while (huff & incr)
          237  +            incr >>= 1;
          238  +        if (incr != 0) {
          239  +            huff &= incr - 1;
          240  +            huff += incr;
          241  +        }
          242  +        else
          243  +            huff = 0;
          244  +
          245  +        /* go to next symbol, update count, len */
          246  +        sym++;
          247  +        if (--(count[len]) == 0) {
          248  +            if (len == max) break;
          249  +            len = lens[work[sym]];
          250  +        }
          251  +
          252  +        /* create new sub-table if needed */
          253  +        if (len > root && (huff & mask) != low) {
          254  +            /* if first time, transition to sub-tables */
          255  +            if (drop == 0)
          256  +                drop = root;
          257  +
          258  +            /* increment past last table */
          259  +            next += 1U << curr;
          260  +
          261  +            /* determine length of next table */
          262  +            curr = len - drop;
          263  +            left = (int)(1 << curr);
          264  +            while (curr + drop < max) {
          265  +                left -= count[curr + drop];
          266  +                if (left <= 0) break;
          267  +                curr++;
          268  +                left <<= 1;
          269  +            }
          270  +
          271  +            /* check for enough space */
          272  +            used += 1U << curr;
          273  +            if ((type == LENS && used >= ENOUGH_LENS) ||
          274  +                (type == DISTS && used >= ENOUGH_DISTS))
          275  +                return 1;
          276  +
          277  +            /* point entry in root table to sub-table */
          278  +            low = huff & mask;
          279  +            (*table)[low].op = (unsigned char)curr;
          280  +            (*table)[low].bits = (unsigned char)root;
          281  +            (*table)[low].val = (unsigned short)(next - *table);
          282  +        }
          283  +    }
          284  +
          285  +    /*
          286  +       Fill in rest of table for incomplete codes.  This loop is similar to the
          287  +       loop above in incrementing huff for table indices.  It is assumed that
          288  +       len is equal to curr + drop, so there is no loop needed to increment
          289  +       through high index bits.  When the current sub-table is filled, the loop
          290  +       drops back to the root table to fill in any remaining entries there.
          291  +     */
          292  +    this.op = (unsigned char)64;                /* invalid code marker */
          293  +    this.bits = (unsigned char)(len - drop);
          294  +    this.val = (unsigned short)0;
          295  +    while (huff != 0) {
          296  +        /* when done with sub-table, drop back to root table */
          297  +        if (drop != 0 && (huff & mask) != low) {
          298  +            drop = 0;
          299  +            len = root;
          300  +            next = *table;
          301  +            curr = root;
          302  +            this.bits = (unsigned char)len;
          303  +        }
          304  +
          305  +        /* put invalid code marker in table */
          306  +        next[huff >> drop] = this;
          307  +
          308  +        /* backwards increment the len-bit code huff */
          309  +        incr = 1U << (len - 1);
          310  +        while (huff & incr)
          311  +            incr >>= 1;
          312  +        if (incr != 0) {
          313  +            huff &= incr - 1;
          314  +            huff += incr;
          315  +        }
          316  +        else
          317  +            huff = 0;
          318  +    }
          319  +
          320  +    /* set return parameters */
          321  +    *table += used;
          322  +    *bits = root;
          323  +    return 0;
          324  +}

Added compat/zlib/contrib/infback9/inftree9.h.

            1  +/* inftree9.h -- header to use inftree9.c
            2  + * Copyright (C) 1995-2008 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* Structure for decoding tables.  Each entry provides either the
           12  +   information needed to do the operation requested by the code that
           13  +   indexed that table entry, or it provides a pointer to another
           14  +   table that indexes more bits of the code.  op indicates whether
           15  +   the entry is a pointer to another table, a literal, a length or
           16  +   distance, an end-of-block, or an invalid code.  For a table
           17  +   pointer, the low four bits of op is the number of index bits of
           18  +   that table.  For a length or distance, the low four bits of op
           19  +   is the number of extra bits to get after the code.  bits is
           20  +   the number of bits in this code or part of the code to drop off
           21  +   of the bit buffer.  val is the actual byte to output in the case
           22  +   of a literal, the base length or distance, or the offset from
           23  +   the current table to the next table.  Each entry is four bytes. */
           24  +typedef struct {
           25  +    unsigned char op;           /* operation, extra bits, table bits */
           26  +    unsigned char bits;         /* bits in this part of the code */
           27  +    unsigned short val;         /* offset in table or code value */
           28  +} code;
           29  +
           30  +/* op values as set by inflate_table():
           31  +    00000000 - literal
           32  +    0000tttt - table link, tttt != 0 is the number of table index bits
           33  +    100eeeee - length or distance, eeee is the number of extra bits
           34  +    01100000 - end of block
           35  +    01000000 - invalid code
           36  + */
           37  +
           38  +/* Maximum size of the dynamic table.  The maximum number of code structures is
           39  +   1446, which is the sum of 852 for literal/length codes and 594 for distance
           40  +   codes.  These values were found by exhaustive searches using the program
           41  +   examples/enough.c found in the zlib distribtution.  The arguments to that
           42  +   program are the number of symbols, the initial root table size, and the
           43  +   maximum bit length of a code.  "enough 286 9 15" for literal/length codes
           44  +   returns returns 852, and "enough 32 6 15" for distance codes returns 594.
           45  +   The initial root table size (9 or 6) is found in the fifth argument of the
           46  +   inflate_table() calls in infback9.c.  If the root table size is changed,
           47  +   then these maximum sizes would be need to be recalculated and updated. */
           48  +#define ENOUGH_LENS 852
           49  +#define ENOUGH_DISTS 594
           50  +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
           51  +
           52  +/* Type of code to build for inflate_table9() */
           53  +typedef enum {
           54  +    CODES,
           55  +    LENS,
           56  +    DISTS
           57  +} codetype;
           58  +
           59  +extern int inflate_table9 OF((codetype type, unsigned short FAR *lens,
           60  +                             unsigned codes, code FAR * FAR *table,
           61  +                             unsigned FAR *bits, unsigned short FAR *work));

Added compat/zlib/contrib/inflate86/inffas86.c.

            1  +/* inffas86.c is a hand tuned assembler version of
            2  + *
            3  + * inffast.c -- fast decoding
            4  + * Copyright (C) 1995-2003 Mark Adler
            5  + * For conditions of distribution and use, see copyright notice in zlib.h
            6  + *
            7  + * Copyright (C) 2003 Chris Anderson <christop@charm.net>
            8  + * Please use the copyright conditions above.
            9  + *
           10  + * Dec-29-2003 -- I added AMD64 inflate asm support.  This version is also
           11  + * slightly quicker on x86 systems because, instead of using rep movsb to copy
           12  + * data, it uses rep movsw, which moves data in 2-byte chunks instead of single
           13  + * bytes.  I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates
           14  + * from http://fedora.linux.duke.edu/fc1_x86_64
           15  + * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with
           16  + * 1GB ram.  The 64-bit version is about 4% faster than the 32-bit version,
           17  + * when decompressing mozilla-source-1.3.tar.gz.
           18  + *
           19  + * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
           20  + * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
           21  + * the moment.  I have successfully compiled and tested this code with gcc2.96,
           22  + * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
           23  + * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
           24  + * enabled.  I will attempt to merge the MMX code into this version.  Newer
           25  + * versions of this and inffast.S can be found at
           26  + * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
           27  + */
           28  +
           29  +#include "zutil.h"
           30  +#include "inftrees.h"
           31  +#include "inflate.h"
           32  +#include "inffast.h"
           33  +
           34  +/* Mark Adler's comments from inffast.c: */
           35  +
           36  +/*
           37  +   Decode literal, length, and distance codes and write out the resulting
           38  +   literal and match bytes until either not enough input or output is
           39  +   available, an end-of-block is encountered, or a data error is encountered.
           40  +   When large enough input and output buffers are supplied to inflate(), for
           41  +   example, a 16K input buffer and a 64K output buffer, more than 95% of the
           42  +   inflate execution time is spent in this routine.
           43  +
           44  +   Entry assumptions:
           45  +
           46  +        state->mode == LEN
           47  +        strm->avail_in >= 6
           48  +        strm->avail_out >= 258
           49  +        start >= strm->avail_out
           50  +        state->bits < 8
           51  +
           52  +   On return, state->mode is one of:
           53  +
           54  +        LEN -- ran out of enough output space or enough available input
           55  +        TYPE -- reached end of block code, inflate() to interpret next block
           56  +        BAD -- error in block data
           57  +
           58  +   Notes:
           59  +
           60  +    - The maximum input bits used by a length/distance pair is 15 bits for the
           61  +      length code, 5 bits for the length extra, 15 bits for the distance code,
           62  +      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
           63  +      Therefore if strm->avail_in >= 6, then there is enough input to avoid
           64  +      checking for available input while decoding.
           65  +
           66  +    - The maximum bytes that a single length/distance pair can output is 258
           67  +      bytes, which is the maximum length that can be coded.  inflate_fast()
           68  +      requires strm->avail_out >= 258 for each loop to avoid checking for
           69  +      output space.
           70  + */
           71  +void inflate_fast(strm, start)
           72  +z_streamp strm;
           73  +unsigned start;         /* inflate()'s starting value for strm->avail_out */
           74  +{
           75  +    struct inflate_state FAR *state;
           76  +    struct inffast_ar {
           77  +/* 64   32                               x86  x86_64 */
           78  +/* ar offset                              register */
           79  +/*  0    0 */ void *esp;                /* esp save */
           80  +/*  8    4 */ void *ebp;                /* ebp save */
           81  +/* 16    8 */ unsigned char FAR *in;    /* esi rsi  local strm->next_in */
           82  +/* 24   12 */ unsigned char FAR *last;  /*     r9   while in < last */
           83  +/* 32   16 */ unsigned char FAR *out;   /* edi rdi  local strm->next_out */
           84  +/* 40   20 */ unsigned char FAR *beg;   /*          inflate()'s init next_out */
           85  +/* 48   24 */ unsigned char FAR *end;   /*     r10  while out < end */
           86  +/* 56   28 */ unsigned char FAR *window;/*          size of window, wsize!=0 */
           87  +/* 64   32 */ code const FAR *lcode;    /* ebp rbp  local strm->lencode */
           88  +/* 72   36 */ code const FAR *dcode;    /*     r11  local strm->distcode */
           89  +/* 80   40 */ unsigned long hold;       /* edx rdx  local strm->hold */
           90  +/* 88   44 */ unsigned bits;            /* ebx rbx  local strm->bits */
           91  +/* 92   48 */ unsigned wsize;           /*          window size */
           92  +/* 96   52 */ unsigned write;           /*          window write index */
           93  +/*100   56 */ unsigned lmask;           /*     r12  mask for lcode */
           94  +/*104   60 */ unsigned dmask;           /*     r13  mask for dcode */
           95  +/*108   64 */ unsigned len;             /*     r14  match length */
           96  +/*112   68 */ unsigned dist;            /*     r15  match distance */
           97  +/*116   72 */ unsigned status;          /*          set when state chng*/
           98  +    } ar;
           99  +
          100  +#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )
          101  +#define PAD_AVAIL_IN 6
          102  +#define PAD_AVAIL_OUT 258
          103  +#else
          104  +#define PAD_AVAIL_IN 5
          105  +#define PAD_AVAIL_OUT 257
          106  +#endif
          107  +
          108  +    /* copy state to local variables */
          109  +    state = (struct inflate_state FAR *)strm->state;
          110  +    ar.in = strm->next_in;
          111  +    ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN);
          112  +    ar.out = strm->next_out;
          113  +    ar.beg = ar.out - (start - strm->avail_out);
          114  +    ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT);
          115  +    ar.wsize = state->wsize;
          116  +    ar.write = state->wnext;
          117  +    ar.window = state->window;
          118  +    ar.hold = state->hold;
          119  +    ar.bits = state->bits;
          120  +    ar.lcode = state->lencode;
          121  +    ar.dcode = state->distcode;
          122  +    ar.lmask = (1U << state->lenbits) - 1;
          123  +    ar.dmask = (1U << state->distbits) - 1;
          124  +
          125  +    /* decode literals and length/distances until end-of-block or not enough
          126  +       input data or output space */
          127  +
          128  +    /* align in on 1/2 hold size boundary */
          129  +    while (((unsigned long)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) {
          130  +        ar.hold += (unsigned long)*ar.in++ << ar.bits;
          131  +        ar.bits += 8;
          132  +    }
          133  +
          134  +#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )
          135  +    __asm__ __volatile__ (
          136  +"        leaq    %0, %%rax\n"
          137  +"        movq    %%rbp, 8(%%rax)\n"       /* save regs rbp and rsp */
          138  +"        movq    %%rsp, (%%rax)\n"
          139  +"        movq    %%rax, %%rsp\n"          /* make rsp point to &ar */
          140  +"        movq    16(%%rsp), %%rsi\n"      /* rsi  = in */
          141  +"        movq    32(%%rsp), %%rdi\n"      /* rdi  = out */
          142  +"        movq    24(%%rsp), %%r9\n"       /* r9   = last */
          143  +"        movq    48(%%rsp), %%r10\n"      /* r10  = end */
          144  +"        movq    64(%%rsp), %%rbp\n"      /* rbp  = lcode */
          145  +"        movq    72(%%rsp), %%r11\n"      /* r11  = dcode */
          146  +"        movq    80(%%rsp), %%rdx\n"      /* rdx  = hold */
          147  +"        movl    88(%%rsp), %%ebx\n"      /* ebx  = bits */
          148  +"        movl    100(%%rsp), %%r12d\n"    /* r12d = lmask */
          149  +"        movl    104(%%rsp), %%r13d\n"    /* r13d = dmask */
          150  +                                          /* r14d = len */
          151  +                                          /* r15d = dist */
          152  +"        cld\n"
          153  +"        cmpq    %%rdi, %%r10\n"
          154  +"        je      .L_one_time\n"           /* if only one decode left */
          155  +"        cmpq    %%rsi, %%r9\n"
          156  +"        je      .L_one_time\n"
          157  +"        jmp     .L_do_loop\n"
          158  +
          159  +".L_one_time:\n"
          160  +"        movq    %%r12, %%r8\n"           /* r8 = lmask */
          161  +"        cmpb    $32, %%bl\n"
          162  +"        ja      .L_get_length_code_one_time\n"
          163  +
          164  +"        lodsl\n"                         /* eax = *(uint *)in++ */
          165  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          166  +"        addb    $32, %%bl\n"             /* bits += 32 */
          167  +"        shlq    %%cl, %%rax\n"
          168  +"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */
          169  +"        jmp     .L_get_length_code_one_time\n"
          170  +
          171  +".align 32,0x90\n"
          172  +".L_while_test:\n"
          173  +"        cmpq    %%rdi, %%r10\n"
          174  +"        jbe     .L_break_loop\n"
          175  +"        cmpq    %%rsi, %%r9\n"
          176  +"        jbe     .L_break_loop\n"
          177  +
          178  +".L_do_loop:\n"
          179  +"        movq    %%r12, %%r8\n"           /* r8 = lmask */
          180  +"        cmpb    $32, %%bl\n"
          181  +"        ja      .L_get_length_code\n"    /* if (32 < bits) */
          182  +
          183  +"        lodsl\n"                         /* eax = *(uint *)in++ */
          184  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          185  +"        addb    $32, %%bl\n"             /* bits += 32 */
          186  +"        shlq    %%cl, %%rax\n"
          187  +"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */
          188  +
          189  +".L_get_length_code:\n"
          190  +"        andq    %%rdx, %%r8\n"            /* r8 &= hold */
          191  +"        movl    (%%rbp,%%r8,4), %%eax\n"  /* eax = lcode[hold & lmask] */
          192  +
          193  +"        movb    %%ah, %%cl\n"            /* cl = this.bits */
          194  +"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
          195  +"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */
          196  +
          197  +"        testb   %%al, %%al\n"
          198  +"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */
          199  +
          200  +"        movq    %%r12, %%r8\n"            /* r8 = lmask */
          201  +"        shrl    $16, %%eax\n"            /* output this.val char */
          202  +"        stosb\n"
          203  +
          204  +".L_get_length_code_one_time:\n"
          205  +"        andq    %%rdx, %%r8\n"            /* r8 &= hold */
          206  +"        movl    (%%rbp,%%r8,4), %%eax\n" /* eax = lcode[hold & lmask] */
          207  +
          208  +".L_dolen:\n"
          209  +"        movb    %%ah, %%cl\n"            /* cl = this.bits */
          210  +"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
          211  +"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */
          212  +
          213  +"        testb   %%al, %%al\n"
          214  +"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */
          215  +
          216  +"        shrl    $16, %%eax\n"            /* output this.val char */
          217  +"        stosb\n"
          218  +"        jmp     .L_while_test\n"
          219  +
          220  +".align 32,0x90\n"
          221  +".L_test_for_length_base:\n"
          222  +"        movl    %%eax, %%r14d\n"         /* len = this */
          223  +"        shrl    $16, %%r14d\n"           /* len = this.val */
          224  +"        movb    %%al, %%cl\n"
          225  +
          226  +"        testb   $16, %%al\n"
          227  +"        jz      .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */
          228  +"        andb    $15, %%cl\n"             /* op &= 15 */
          229  +"        jz      .L_decode_distance\n"    /* if (!op) */
          230  +
          231  +".L_add_bits_to_len:\n"
          232  +"        subb    %%cl, %%bl\n"
          233  +"        xorl    %%eax, %%eax\n"
          234  +"        incl    %%eax\n"
          235  +"        shll    %%cl, %%eax\n"
          236  +"        decl    %%eax\n"
          237  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          238  +"        shrq    %%cl, %%rdx\n"
          239  +"        addl    %%eax, %%r14d\n"         /* len += hold & mask[op] */
          240  +
          241  +".L_decode_distance:\n"
          242  +"        movq    %%r13, %%r8\n"           /* r8 = dmask */
          243  +"        cmpb    $32, %%bl\n"
          244  +"        ja      .L_get_distance_code\n"  /* if (32 < bits) */
          245  +
          246  +"        lodsl\n"                         /* eax = *(uint *)in++ */
          247  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          248  +"        addb    $32, %%bl\n"             /* bits += 32 */
          249  +"        shlq    %%cl, %%rax\n"
          250  +"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */
          251  +
          252  +".L_get_distance_code:\n"
          253  +"        andq    %%rdx, %%r8\n"           /* r8 &= hold */
          254  +"        movl    (%%r11,%%r8,4), %%eax\n" /* eax = dcode[hold & dmask] */
          255  +
          256  +".L_dodist:\n"
          257  +"        movl    %%eax, %%r15d\n"         /* dist = this */
          258  +"        shrl    $16, %%r15d\n"           /* dist = this.val */
          259  +"        movb    %%ah, %%cl\n"
          260  +"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
          261  +"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */
          262  +"        movb    %%al, %%cl\n"            /* cl = this.op */
          263  +
          264  +"        testb   $16, %%al\n"             /* if ((op & 16) == 0) */
          265  +"        jz      .L_test_for_second_level_dist\n"
          266  +"        andb    $15, %%cl\n"             /* op &= 15 */
          267  +"        jz      .L_check_dist_one\n"
          268  +
          269  +".L_add_bits_to_dist:\n"
          270  +"        subb    %%cl, %%bl\n"
          271  +"        xorl    %%eax, %%eax\n"
          272  +"        incl    %%eax\n"
          273  +"        shll    %%cl, %%eax\n"
          274  +"        decl    %%eax\n"                 /* (1 << op) - 1 */
          275  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          276  +"        shrq    %%cl, %%rdx\n"
          277  +"        addl    %%eax, %%r15d\n"         /* dist += hold & ((1 << op) - 1) */
          278  +
          279  +".L_check_window:\n"
          280  +"        movq    %%rsi, %%r8\n"           /* save in so from can use it's reg */
          281  +"        movq    %%rdi, %%rax\n"
          282  +"        subq    40(%%rsp), %%rax\n"      /* nbytes = out - beg */
          283  +
          284  +"        cmpl    %%r15d, %%eax\n"
          285  +"        jb      .L_clip_window\n"        /* if (dist > nbytes) 4.2% */
          286  +
          287  +"        movl    %%r14d, %%ecx\n"         /* ecx = len */
          288  +"        movq    %%rdi, %%rsi\n"
          289  +"        subq    %%r15, %%rsi\n"          /* from = out - dist */
          290  +
          291  +"        sarl    %%ecx\n"
          292  +"        jnc     .L_copy_two\n"           /* if len % 2 == 0 */
          293  +
          294  +"        rep     movsw\n"
          295  +"        movb    (%%rsi), %%al\n"
          296  +"        movb    %%al, (%%rdi)\n"
          297  +"        incq    %%rdi\n"
          298  +
          299  +"        movq    %%r8, %%rsi\n"           /* move in back to %rsi, toss from */
          300  +"        jmp     .L_while_test\n"
          301  +
          302  +".L_copy_two:\n"
          303  +"        rep     movsw\n"
          304  +"        movq    %%r8, %%rsi\n"           /* move in back to %rsi, toss from */
          305  +"        jmp     .L_while_test\n"
          306  +
          307  +".align 32,0x90\n"
          308  +".L_check_dist_one:\n"
          309  +"        cmpl    $1, %%r15d\n"            /* if dist 1, is a memset */
          310  +"        jne     .L_check_window\n"
          311  +"        cmpq    %%rdi, 40(%%rsp)\n"      /* if out == beg, outside window */
          312  +"        je      .L_check_window\n"
          313  +
          314  +"        movl    %%r14d, %%ecx\n"         /* ecx = len */
          315  +"        movb    -1(%%rdi), %%al\n"
          316  +"        movb    %%al, %%ah\n"
          317  +
          318  +"        sarl    %%ecx\n"
          319  +"        jnc     .L_set_two\n"
          320  +"        movb    %%al, (%%rdi)\n"
          321  +"        incq    %%rdi\n"
          322  +
          323  +".L_set_two:\n"
          324  +"        rep     stosw\n"
          325  +"        jmp     .L_while_test\n"
          326  +
          327  +".align 32,0x90\n"
          328  +".L_test_for_second_level_length:\n"
          329  +"        testb   $64, %%al\n"
          330  +"        jnz     .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */
          331  +
          332  +"        xorl    %%eax, %%eax\n"
          333  +"        incl    %%eax\n"
          334  +"        shll    %%cl, %%eax\n"
          335  +"        decl    %%eax\n"
          336  +"        andl    %%edx, %%eax\n"         /* eax &= hold */
          337  +"        addl    %%r14d, %%eax\n"        /* eax += len */
          338  +"        movl    (%%rbp,%%rax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/
          339  +"        jmp     .L_dolen\n"
          340  +
          341  +".align 32,0x90\n"
          342  +".L_test_for_second_level_dist:\n"
          343  +"        testb   $64, %%al\n"
          344  +"        jnz     .L_invalid_distance_code\n" /* if ((op & 64) != 0) */
          345  +
          346  +"        xorl    %%eax, %%eax\n"
          347  +"        incl    %%eax\n"
          348  +"        shll    %%cl, %%eax\n"
          349  +"        decl    %%eax\n"
          350  +"        andl    %%edx, %%eax\n"         /* eax &= hold */
          351  +"        addl    %%r15d, %%eax\n"        /* eax += dist */
          352  +"        movl    (%%r11,%%rax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/
          353  +"        jmp     .L_dodist\n"
          354  +
          355  +".align 32,0x90\n"
          356  +".L_clip_window:\n"
          357  +"        movl    %%eax, %%ecx\n"         /* ecx = nbytes */
          358  +"        movl    92(%%rsp), %%eax\n"     /* eax = wsize, prepare for dist cmp */
          359  +"        negl    %%ecx\n"                /* nbytes = -nbytes */
          360  +
          361  +"        cmpl    %%r15d, %%eax\n"
          362  +"        jb      .L_invalid_distance_too_far\n" /* if (dist > wsize) */
          363  +
          364  +"        addl    %%r15d, %%ecx\n"         /* nbytes = dist - nbytes */
          365  +"        cmpl    $0, 96(%%rsp)\n"
          366  +"        jne     .L_wrap_around_window\n" /* if (write != 0) */
          367  +
          368  +"        movq    56(%%rsp), %%rsi\n"     /* from  = window */
          369  +"        subl    %%ecx, %%eax\n"         /* eax  -= nbytes */
          370  +"        addq    %%rax, %%rsi\n"         /* from += wsize - nbytes */
          371  +
          372  +"        movl    %%r14d, %%eax\n"        /* eax = len */
          373  +"        cmpl    %%ecx, %%r14d\n"
          374  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          375  +
          376  +"        subl    %%ecx, %%eax\n"         /* eax -= nbytes */
          377  +"        rep     movsb\n"
          378  +"        movq    %%rdi, %%rsi\n"
          379  +"        subq    %%r15, %%rsi\n"         /* from = &out[ -dist ] */
          380  +"        jmp     .L_do_copy\n"
          381  +
          382  +".align 32,0x90\n"
          383  +".L_wrap_around_window:\n"
          384  +"        movl    96(%%rsp), %%eax\n"     /* eax = write */
          385  +"        cmpl    %%eax, %%ecx\n"
          386  +"        jbe     .L_contiguous_in_window\n" /* if (write >= nbytes) */
          387  +
          388  +"        movl    92(%%rsp), %%esi\n"     /* from  = wsize */
          389  +"        addq    56(%%rsp), %%rsi\n"     /* from += window */
          390  +"        addq    %%rax, %%rsi\n"         /* from += write */
          391  +"        subq    %%rcx, %%rsi\n"         /* from -= nbytes */
          392  +"        subl    %%eax, %%ecx\n"         /* nbytes -= write */
          393  +
          394  +"        movl    %%r14d, %%eax\n"        /* eax = len */
          395  +"        cmpl    %%ecx, %%eax\n"
          396  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          397  +
          398  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          399  +"        rep     movsb\n"
          400  +"        movq    56(%%rsp), %%rsi\n"     /* from = window */
          401  +"        movl    96(%%rsp), %%ecx\n"     /* nbytes = write */
          402  +"        cmpl    %%ecx, %%eax\n"
          403  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          404  +
          405  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          406  +"        rep     movsb\n"
          407  +"        movq    %%rdi, %%rsi\n"
          408  +"        subq    %%r15, %%rsi\n"         /* from = out - dist */
          409  +"        jmp     .L_do_copy\n"
          410  +
          411  +".align 32,0x90\n"
          412  +".L_contiguous_in_window:\n"
          413  +"        movq    56(%%rsp), %%rsi\n"     /* rsi = window */
          414  +"        addq    %%rax, %%rsi\n"
          415  +"        subq    %%rcx, %%rsi\n"         /* from += write - nbytes */
          416  +
          417  +"        movl    %%r14d, %%eax\n"        /* eax = len */
          418  +"        cmpl    %%ecx, %%eax\n"
          419  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          420  +
          421  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          422  +"        rep     movsb\n"
          423  +"        movq    %%rdi, %%rsi\n"
          424  +"        subq    %%r15, %%rsi\n"         /* from = out - dist */
          425  +"        jmp     .L_do_copy\n"           /* if (nbytes >= len) */
          426  +
          427  +".align 32,0x90\n"
          428  +".L_do_copy:\n"
          429  +"        movl    %%eax, %%ecx\n"         /* ecx = len */
          430  +"        rep     movsb\n"
          431  +
          432  +"        movq    %%r8, %%rsi\n"          /* move in back to %esi, toss from */
          433  +"        jmp     .L_while_test\n"
          434  +
          435  +".L_test_for_end_of_block:\n"
          436  +"        testb   $32, %%al\n"
          437  +"        jz      .L_invalid_literal_length_code\n"
          438  +"        movl    $1, 116(%%rsp)\n"
          439  +"        jmp     .L_break_loop_with_status\n"
          440  +
          441  +".L_invalid_literal_length_code:\n"
          442  +"        movl    $2, 116(%%rsp)\n"
          443  +"        jmp     .L_break_loop_with_status\n"
          444  +
          445  +".L_invalid_distance_code:\n"
          446  +"        movl    $3, 116(%%rsp)\n"
          447  +"        jmp     .L_break_loop_with_status\n"
          448  +
          449  +".L_invalid_distance_too_far:\n"
          450  +"        movl    $4, 116(%%rsp)\n"
          451  +"        jmp     .L_break_loop_with_status\n"
          452  +
          453  +".L_break_loop:\n"
          454  +"        movl    $0, 116(%%rsp)\n"
          455  +
          456  +".L_break_loop_with_status:\n"
          457  +/* put in, out, bits, and hold back into ar and pop esp */
          458  +"        movq    %%rsi, 16(%%rsp)\n"     /* in */
          459  +"        movq    %%rdi, 32(%%rsp)\n"     /* out */
          460  +"        movl    %%ebx, 88(%%rsp)\n"     /* bits */
          461  +"        movq    %%rdx, 80(%%rsp)\n"     /* hold */
          462  +"        movq    (%%rsp), %%rax\n"       /* restore rbp and rsp */
          463  +"        movq    8(%%rsp), %%rbp\n"
          464  +"        movq    %%rax, %%rsp\n"
          465  +          :
          466  +          : "m" (ar)
          467  +          : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi",
          468  +            "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
          469  +    );
          470  +#elif ( defined( __GNUC__ ) || defined( __ICC ) ) && defined( __i386 )
          471  +    __asm__ __volatile__ (
          472  +"        leal    %0, %%eax\n"
          473  +"        movl    %%esp, (%%eax)\n"        /* save esp, ebp */
          474  +"        movl    %%ebp, 4(%%eax)\n"
          475  +"        movl    %%eax, %%esp\n"
          476  +"        movl    8(%%esp), %%esi\n"       /* esi = in */
          477  +"        movl    16(%%esp), %%edi\n"      /* edi = out */
          478  +"        movl    40(%%esp), %%edx\n"      /* edx = hold */
          479  +"        movl    44(%%esp), %%ebx\n"      /* ebx = bits */
          480  +"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
          481  +
          482  +"        cld\n"
          483  +"        jmp     .L_do_loop\n"
          484  +
          485  +".align 32,0x90\n"
          486  +".L_while_test:\n"
          487  +"        cmpl    %%edi, 24(%%esp)\n"      /* out < end */
          488  +"        jbe     .L_break_loop\n"
          489  +"        cmpl    %%esi, 12(%%esp)\n"      /* in < last */
          490  +"        jbe     .L_break_loop\n"
          491  +
          492  +".L_do_loop:\n"
          493  +"        cmpb    $15, %%bl\n"
          494  +"        ja      .L_get_length_code\n"    /* if (15 < bits) */
          495  +
          496  +"        xorl    %%eax, %%eax\n"
          497  +"        lodsw\n"                         /* al = *(ushort *)in++ */
          498  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          499  +"        addb    $16, %%bl\n"             /* bits += 16 */
          500  +"        shll    %%cl, %%eax\n"
          501  +"        orl     %%eax, %%edx\n"        /* hold |= *((ushort *)in)++ << bits */
          502  +
          503  +".L_get_length_code:\n"
          504  +"        movl    56(%%esp), %%eax\n"      /* eax = lmask */
          505  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          506  +"        movl    (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[hold & lmask] */
          507  +
          508  +".L_dolen:\n"
          509  +"        movb    %%ah, %%cl\n"            /* cl = this.bits */
          510  +"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
          511  +"        shrl    %%cl, %%edx\n"           /* hold >>= this.bits */
          512  +
          513  +"        testb   %%al, %%al\n"
          514  +"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */
          515  +
          516  +"        shrl    $16, %%eax\n"            /* output this.val char */
          517  +"        stosb\n"
          518  +"        jmp     .L_while_test\n"
          519  +
          520  +".align 32,0x90\n"
          521  +".L_test_for_length_base:\n"
          522  +"        movl    %%eax, %%ecx\n"          /* len = this */
          523  +"        shrl    $16, %%ecx\n"            /* len = this.val */
          524  +"        movl    %%ecx, 64(%%esp)\n"      /* save len */
          525  +"        movb    %%al, %%cl\n"
          526  +
          527  +"        testb   $16, %%al\n"
          528  +"        jz      .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */
          529  +"        andb    $15, %%cl\n"             /* op &= 15 */
          530  +"        jz      .L_decode_distance\n"    /* if (!op) */
          531  +"        cmpb    %%cl, %%bl\n"
          532  +"        jae     .L_add_bits_to_len\n"    /* if (op <= bits) */
          533  +
          534  +"        movb    %%cl, %%ch\n"            /* stash op in ch, freeing cl */
          535  +"        xorl    %%eax, %%eax\n"
          536  +"        lodsw\n"                         /* al = *(ushort *)in++ */
          537  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          538  +"        addb    $16, %%bl\n"             /* bits += 16 */
          539  +"        shll    %%cl, %%eax\n"
          540  +"        orl     %%eax, %%edx\n"         /* hold |= *((ushort *)in)++ << bits */
          541  +"        movb    %%ch, %%cl\n"            /* move op back to ecx */
          542  +
          543  +".L_add_bits_to_len:\n"
          544  +"        subb    %%cl, %%bl\n"
          545  +"        xorl    %%eax, %%eax\n"
          546  +"        incl    %%eax\n"
          547  +"        shll    %%cl, %%eax\n"
          548  +"        decl    %%eax\n"
          549  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          550  +"        shrl    %%cl, %%edx\n"
          551  +"        addl    %%eax, 64(%%esp)\n"      /* len += hold & mask[op] */
          552  +
          553  +".L_decode_distance:\n"
          554  +"        cmpb    $15, %%bl\n"
          555  +"        ja      .L_get_distance_code\n"  /* if (15 < bits) */
          556  +
          557  +"        xorl    %%eax, %%eax\n"
          558  +"        lodsw\n"                         /* al = *(ushort *)in++ */
          559  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          560  +"        addb    $16, %%bl\n"             /* bits += 16 */
          561  +"        shll    %%cl, %%eax\n"
          562  +"        orl     %%eax, %%edx\n"         /* hold |= *((ushort *)in)++ << bits */
          563  +
          564  +".L_get_distance_code:\n"
          565  +"        movl    60(%%esp), %%eax\n"      /* eax = dmask */
          566  +"        movl    36(%%esp), %%ecx\n"      /* ecx = dcode */
          567  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          568  +"        movl    (%%ecx,%%eax,4), %%eax\n"/* eax = dcode[hold & dmask] */
          569  +
          570  +".L_dodist:\n"
          571  +"        movl    %%eax, %%ebp\n"          /* dist = this */
          572  +"        shrl    $16, %%ebp\n"            /* dist = this.val */
          573  +"        movb    %%ah, %%cl\n"
          574  +"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
          575  +"        shrl    %%cl, %%edx\n"           /* hold >>= this.bits */
          576  +"        movb    %%al, %%cl\n"            /* cl = this.op */
          577  +
          578  +"        testb   $16, %%al\n"             /* if ((op & 16) == 0) */
          579  +"        jz      .L_test_for_second_level_dist\n"
          580  +"        andb    $15, %%cl\n"             /* op &= 15 */
          581  +"        jz      .L_check_dist_one\n"
          582  +"        cmpb    %%cl, %%bl\n"
          583  +"        jae     .L_add_bits_to_dist\n"   /* if (op <= bits) 97.6% */
          584  +
          585  +"        movb    %%cl, %%ch\n"            /* stash op in ch, freeing cl */
          586  +"        xorl    %%eax, %%eax\n"
          587  +"        lodsw\n"                         /* al = *(ushort *)in++ */
          588  +"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
          589  +"        addb    $16, %%bl\n"             /* bits += 16 */
          590  +"        shll    %%cl, %%eax\n"
          591  +"        orl     %%eax, %%edx\n"        /* hold |= *((ushort *)in)++ << bits */
          592  +"        movb    %%ch, %%cl\n"            /* move op back to ecx */
          593  +
          594  +".L_add_bits_to_dist:\n"
          595  +"        subb    %%cl, %%bl\n"
          596  +"        xorl    %%eax, %%eax\n"
          597  +"        incl    %%eax\n"
          598  +"        shll    %%cl, %%eax\n"
          599  +"        decl    %%eax\n"                 /* (1 << op) - 1 */
          600  +"        andl    %%edx, %%eax\n"          /* eax &= hold */
          601  +"        shrl    %%cl, %%edx\n"
          602  +"        addl    %%eax, %%ebp\n"          /* dist += hold & ((1 << op) - 1) */
          603  +
          604  +".L_check_window:\n"
          605  +"        movl    %%esi, 8(%%esp)\n"       /* save in so from can use it's reg */
          606  +"        movl    %%edi, %%eax\n"
          607  +"        subl    20(%%esp), %%eax\n"      /* nbytes = out - beg */
          608  +
          609  +"        cmpl    %%ebp, %%eax\n"
          610  +"        jb      .L_clip_window\n"        /* if (dist > nbytes) 4.2% */
          611  +
          612  +"        movl    64(%%esp), %%ecx\n"      /* ecx = len */
          613  +"        movl    %%edi, %%esi\n"
          614  +"        subl    %%ebp, %%esi\n"          /* from = out - dist */
          615  +
          616  +"        sarl    %%ecx\n"
          617  +"        jnc     .L_copy_two\n"           /* if len % 2 == 0 */
          618  +
          619  +"        rep     movsw\n"
          620  +"        movb    (%%esi), %%al\n"
          621  +"        movb    %%al, (%%edi)\n"
          622  +"        incl    %%edi\n"
          623  +
          624  +"        movl    8(%%esp), %%esi\n"       /* move in back to %esi, toss from */
          625  +"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
          626  +"        jmp     .L_while_test\n"
          627  +
          628  +".L_copy_two:\n"
          629  +"        rep     movsw\n"
          630  +"        movl    8(%%esp), %%esi\n"       /* move in back to %esi, toss from */
          631  +"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
          632  +"        jmp     .L_while_test\n"
          633  +
          634  +".align 32,0x90\n"
          635  +".L_check_dist_one:\n"
          636  +"        cmpl    $1, %%ebp\n"            /* if dist 1, is a memset */
          637  +"        jne     .L_check_window\n"
          638  +"        cmpl    %%edi, 20(%%esp)\n"
          639  +"        je      .L_check_window\n"      /* out == beg, if outside window */
          640  +
          641  +"        movl    64(%%esp), %%ecx\n"      /* ecx = len */
          642  +"        movb    -1(%%edi), %%al\n"
          643  +"        movb    %%al, %%ah\n"
          644  +
          645  +"        sarl    %%ecx\n"
          646  +"        jnc     .L_set_two\n"
          647  +"        movb    %%al, (%%edi)\n"
          648  +"        incl    %%edi\n"
          649  +
          650  +".L_set_two:\n"
          651  +"        rep     stosw\n"
          652  +"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
          653  +"        jmp     .L_while_test\n"
          654  +
          655  +".align 32,0x90\n"
          656  +".L_test_for_second_level_length:\n"
          657  +"        testb   $64, %%al\n"
          658  +"        jnz     .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */
          659  +
          660  +"        xorl    %%eax, %%eax\n"
          661  +"        incl    %%eax\n"
          662  +"        shll    %%cl, %%eax\n"
          663  +"        decl    %%eax\n"
          664  +"        andl    %%edx, %%eax\n"         /* eax &= hold */
          665  +"        addl    64(%%esp), %%eax\n"     /* eax += len */
          666  +"        movl    (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/
          667  +"        jmp     .L_dolen\n"
          668  +
          669  +".align 32,0x90\n"
          670  +".L_test_for_second_level_dist:\n"
          671  +"        testb   $64, %%al\n"
          672  +"        jnz     .L_invalid_distance_code\n" /* if ((op & 64) != 0) */
          673  +
          674  +"        xorl    %%eax, %%eax\n"
          675  +"        incl    %%eax\n"
          676  +"        shll    %%cl, %%eax\n"
          677  +"        decl    %%eax\n"
          678  +"        andl    %%edx, %%eax\n"         /* eax &= hold */
          679  +"        addl    %%ebp, %%eax\n"         /* eax += dist */
          680  +"        movl    36(%%esp), %%ecx\n"     /* ecx = dcode */
          681  +"        movl    (%%ecx,%%eax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/
          682  +"        jmp     .L_dodist\n"
          683  +
          684  +".align 32,0x90\n"
          685  +".L_clip_window:\n"
          686  +"        movl    %%eax, %%ecx\n"
          687  +"        movl    48(%%esp), %%eax\n"     /* eax = wsize */
          688  +"        negl    %%ecx\n"                /* nbytes = -nbytes */
          689  +"        movl    28(%%esp), %%esi\n"     /* from = window */
          690  +
          691  +"        cmpl    %%ebp, %%eax\n"
          692  +"        jb      .L_invalid_distance_too_far\n" /* if (dist > wsize) */
          693  +
          694  +"        addl    %%ebp, %%ecx\n"         /* nbytes = dist - nbytes */
          695  +"        cmpl    $0, 52(%%esp)\n"
          696  +"        jne     .L_wrap_around_window\n" /* if (write != 0) */
          697  +
          698  +"        subl    %%ecx, %%eax\n"
          699  +"        addl    %%eax, %%esi\n"         /* from += wsize - nbytes */
          700  +
          701  +"        movl    64(%%esp), %%eax\n"     /* eax = len */
          702  +"        cmpl    %%ecx, %%eax\n"
          703  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          704  +
          705  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          706  +"        rep     movsb\n"
          707  +"        movl    %%edi, %%esi\n"
          708  +"        subl    %%ebp, %%esi\n"         /* from = out - dist */
          709  +"        jmp     .L_do_copy\n"
          710  +
          711  +".align 32,0x90\n"
          712  +".L_wrap_around_window:\n"
          713  +"        movl    52(%%esp), %%eax\n"     /* eax = write */
          714  +"        cmpl    %%eax, %%ecx\n"
          715  +"        jbe     .L_contiguous_in_window\n" /* if (write >= nbytes) */
          716  +
          717  +"        addl    48(%%esp), %%esi\n"     /* from += wsize */
          718  +"        addl    %%eax, %%esi\n"         /* from += write */
          719  +"        subl    %%ecx, %%esi\n"         /* from -= nbytes */
          720  +"        subl    %%eax, %%ecx\n"         /* nbytes -= write */
          721  +
          722  +"        movl    64(%%esp), %%eax\n"     /* eax = len */
          723  +"        cmpl    %%ecx, %%eax\n"
          724  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          725  +
          726  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          727  +"        rep     movsb\n"
          728  +"        movl    28(%%esp), %%esi\n"     /* from = window */
          729  +"        movl    52(%%esp), %%ecx\n"     /* nbytes = write */
          730  +"        cmpl    %%ecx, %%eax\n"
          731  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          732  +
          733  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          734  +"        rep     movsb\n"
          735  +"        movl    %%edi, %%esi\n"
          736  +"        subl    %%ebp, %%esi\n"         /* from = out - dist */
          737  +"        jmp     .L_do_copy\n"
          738  +
          739  +".align 32,0x90\n"
          740  +".L_contiguous_in_window:\n"
          741  +"        addl    %%eax, %%esi\n"
          742  +"        subl    %%ecx, %%esi\n"         /* from += write - nbytes */
          743  +
          744  +"        movl    64(%%esp), %%eax\n"     /* eax = len */
          745  +"        cmpl    %%ecx, %%eax\n"
          746  +"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */
          747  +
          748  +"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
          749  +"        rep     movsb\n"
          750  +"        movl    %%edi, %%esi\n"
          751  +"        subl    %%ebp, %%esi\n"         /* from = out - dist */
          752  +"        jmp     .L_do_copy\n"           /* if (nbytes >= len) */
          753  +
          754  +".align 32,0x90\n"
          755  +".L_do_copy:\n"
          756  +"        movl    %%eax, %%ecx\n"
          757  +"        rep     movsb\n"
          758  +
          759  +"        movl    8(%%esp), %%esi\n"      /* move in back to %esi, toss from */
          760  +"        movl    32(%%esp), %%ebp\n"     /* ebp = lcode */
          761  +"        jmp     .L_while_test\n"
          762  +
          763  +".L_test_for_end_of_block:\n"
          764  +"        testb   $32, %%al\n"
          765  +"        jz      .L_invalid_literal_length_code\n"
          766  +"        movl    $1, 72(%%esp)\n"
          767  +"        jmp     .L_break_loop_with_status\n"
          768  +
          769  +".L_invalid_literal_length_code:\n"
          770  +"        movl    $2, 72(%%esp)\n"
          771  +"        jmp     .L_break_loop_with_status\n"
          772  +
          773  +".L_invalid_distance_code:\n"
          774  +"        movl    $3, 72(%%esp)\n"
          775  +"        jmp     .L_break_loop_with_status\n"
          776  +
          777  +".L_invalid_distance_too_far:\n"
          778  +"        movl    8(%%esp), %%esi\n"
          779  +"        movl    $4, 72(%%esp)\n"
          780  +"        jmp     .L_break_loop_with_status\n"
          781  +
          782  +".L_break_loop:\n"
          783  +"        movl    $0, 72(%%esp)\n"
          784  +
          785  +".L_break_loop_with_status:\n"
          786  +/* put in, out, bits, and hold back into ar and pop esp */
          787  +"        movl    %%esi, 8(%%esp)\n"      /* save in */
          788  +"        movl    %%edi, 16(%%esp)\n"     /* save out */
          789  +"        movl    %%ebx, 44(%%esp)\n"     /* save bits */
          790  +"        movl    %%edx, 40(%%esp)\n"     /* save hold */
          791  +"        movl    4(%%esp), %%ebp\n"      /* restore esp, ebp */
          792  +"        movl    (%%esp), %%esp\n"
          793  +          :
          794  +          : "m" (ar)
          795  +          : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"
          796  +    );
          797  +#elif defined( _MSC_VER ) && ! defined( _M_AMD64 )
          798  +    __asm {
          799  +	lea	eax, ar
          800  +	mov	[eax], esp         /* save esp, ebp */
          801  +	mov	[eax+4], ebp
          802  +	mov	esp, eax
          803  +	mov	esi, [esp+8]       /* esi = in */
          804  +	mov	edi, [esp+16]      /* edi = out */
          805  +	mov	edx, [esp+40]      /* edx = hold */
          806  +	mov	ebx, [esp+44]      /* ebx = bits */
          807  +	mov	ebp, [esp+32]      /* ebp = lcode */
          808  +
          809  +	cld
          810  +	jmp	L_do_loop
          811  +
          812  +ALIGN 4
          813  +L_while_test:
          814  +	cmp	[esp+24], edi
          815  +	jbe	L_break_loop
          816  +	cmp	[esp+12], esi
          817  +	jbe	L_break_loop
          818  +
          819  +L_do_loop:
          820  +	cmp	bl, 15
          821  +	ja	L_get_length_code    /* if (15 < bits) */
          822  +
          823  +	xor	eax, eax
          824  +	lodsw                         /* al = *(ushort *)in++ */
          825  +	mov	cl, bl            /* cl = bits, needs it for shifting */
          826  +	add	bl, 16             /* bits += 16 */
          827  +	shl	eax, cl
          828  +	or	edx, eax        /* hold |= *((ushort *)in)++ << bits */
          829  +
          830  +L_get_length_code:
          831  +	mov	eax, [esp+56]      /* eax = lmask */
          832  +	and	eax, edx          /* eax &= hold */
          833  +	mov	eax, [ebp+eax*4] /* eax = lcode[hold & lmask] */
          834  +
          835  +L_dolen:
          836  +	mov	cl, ah            /* cl = this.bits */
          837  +	sub	bl, ah            /* bits -= this.bits */
          838  +	shr	edx, cl           /* hold >>= this.bits */
          839  +
          840  +	test	al, al
          841  +	jnz	L_test_for_length_base /* if (op != 0) 45.7% */
          842  +
          843  +	shr	eax, 16            /* output this.val char */
          844  +	stosb
          845  +	jmp	L_while_test
          846  +
          847  +ALIGN 4
          848  +L_test_for_length_base:
          849  +	mov	ecx, eax          /* len = this */
          850  +	shr	ecx, 16            /* len = this.val */
          851  +	mov	[esp+64], ecx      /* save len */
          852  +	mov	cl, al
          853  +
          854  +	test	al, 16
          855  +	jz	L_test_for_second_level_length /* if ((op & 16) == 0) 8% */
          856  +	and	cl, 15             /* op &= 15 */
          857  +	jz	L_decode_distance    /* if (!op) */
          858  +	cmp	bl, cl
          859  +	jae	L_add_bits_to_len    /* if (op <= bits) */
          860  +
          861  +	mov	ch, cl            /* stash op in ch, freeing cl */
          862  +	xor	eax, eax
          863  +	lodsw                         /* al = *(ushort *)in++ */
          864  +	mov	cl, bl            /* cl = bits, needs it for shifting */
          865  +	add	bl, 16             /* bits += 16 */
          866  +	shl	eax, cl
          867  +	or	edx, eax         /* hold |= *((ushort *)in)++ << bits */
          868  +	mov	cl, ch            /* move op back to ecx */
          869  +
          870  +L_add_bits_to_len:
          871  +	sub	bl, cl
          872  +	xor	eax, eax
          873  +	inc	eax
          874  +	shl	eax, cl
          875  +	dec	eax
          876  +	and	eax, edx          /* eax &= hold */
          877  +	shr	edx, cl
          878  +	add	[esp+64], eax      /* len += hold & mask[op] */
          879  +
          880  +L_decode_distance:
          881  +	cmp	bl, 15
          882  +	ja	L_get_distance_code  /* if (15 < bits) */
          883  +
          884  +	xor	eax, eax
          885  +	lodsw                         /* al = *(ushort *)in++ */
          886  +	mov	cl, bl            /* cl = bits, needs it for shifting */
          887  +	add	bl, 16             /* bits += 16 */
          888  +	shl	eax, cl
          889  +	or	edx, eax         /* hold |= *((ushort *)in)++ << bits */
          890  +
          891  +L_get_distance_code:
          892  +	mov	eax, [esp+60]      /* eax = dmask */
          893  +	mov	ecx, [esp+36]      /* ecx = dcode */
          894  +	and	eax, edx          /* eax &= hold */
          895  +	mov	eax, [ecx+eax*4]/* eax = dcode[hold & dmask] */
          896  +
          897  +L_dodist:
          898  +	mov	ebp, eax          /* dist = this */
          899  +	shr	ebp, 16            /* dist = this.val */
          900  +	mov	cl, ah
          901  +	sub	bl, ah            /* bits -= this.bits */
          902  +	shr	edx, cl           /* hold >>= this.bits */
          903  +	mov	cl, al            /* cl = this.op */
          904  +
          905  +	test	al, 16             /* if ((op & 16) == 0) */
          906  +	jz	L_test_for_second_level_dist
          907  +	and	cl, 15             /* op &= 15 */
          908  +	jz	L_check_dist_one
          909  +	cmp	bl, cl
          910  +	jae	L_add_bits_to_dist   /* if (op <= bits) 97.6% */
          911  +
          912  +	mov	ch, cl            /* stash op in ch, freeing cl */
          913  +	xor	eax, eax
          914  +	lodsw                         /* al = *(ushort *)in++ */
          915  +	mov	cl, bl            /* cl = bits, needs it for shifting */
          916  +	add	bl, 16             /* bits += 16 */
          917  +	shl	eax, cl
          918  +	or	edx, eax        /* hold |= *((ushort *)in)++ << bits */
          919  +	mov	cl, ch            /* move op back to ecx */
          920  +
          921  +L_add_bits_to_dist:
          922  +	sub	bl, cl
          923  +	xor	eax, eax
          924  +	inc	eax
          925  +	shl	eax, cl
          926  +	dec	eax                 /* (1 << op) - 1 */
          927  +	and	eax, edx          /* eax &= hold */
          928  +	shr	edx, cl
          929  +	add	ebp, eax          /* dist += hold & ((1 << op) - 1) */
          930  +
          931  +L_check_window:
          932  +	mov	[esp+8], esi       /* save in so from can use it's reg */
          933  +	mov	eax, edi
          934  +	sub	eax, [esp+20]      /* nbytes = out - beg */
          935  +
          936  +	cmp	eax, ebp
          937  +	jb	L_clip_window        /* if (dist > nbytes) 4.2% */
          938  +
          939  +	mov	ecx, [esp+64]      /* ecx = len */
          940  +	mov	esi, edi
          941  +	sub	esi, ebp          /* from = out - dist */
          942  +
          943  +	sar	ecx, 1
          944  +	jnc	L_copy_two
          945  +
          946  +	rep     movsw
          947  +	mov	al, [esi]
          948  +	mov	[edi], al
          949  +	inc	edi
          950  +
          951  +	mov	esi, [esp+8]      /* move in back to %esi, toss from */
          952  +	mov	ebp, [esp+32]     /* ebp = lcode */
          953  +	jmp	L_while_test
          954  +
          955  +L_copy_two:
          956  +	rep     movsw
          957  +	mov	esi, [esp+8]      /* move in back to %esi, toss from */
          958  +	mov	ebp, [esp+32]     /* ebp = lcode */
          959  +	jmp	L_while_test
          960  +
          961  +ALIGN 4
          962  +L_check_dist_one:
          963  +	cmp	ebp, 1            /* if dist 1, is a memset */
          964  +	jne	L_check_window
          965  +	cmp	[esp+20], edi
          966  +	je	L_check_window    /* out == beg, if outside window */
          967  +
          968  +	mov	ecx, [esp+64]     /* ecx = len */
          969  +	mov	al, [edi-1]
          970  +	mov	ah, al
          971  +
          972  +	sar	ecx, 1
          973  +	jnc	L_set_two
          974  +	mov	[edi], al         /* memset out with from[-1] */
          975  +	inc	edi
          976  +
          977  +L_set_two:
          978  +	rep     stosw
          979  +	mov	ebp, [esp+32]     /* ebp = lcode */
          980  +	jmp	L_while_test
          981  +
          982  +ALIGN 4
          983  +L_test_for_second_level_length:
          984  +	test	al, 64
          985  +	jnz	L_test_for_end_of_block /* if ((op & 64) != 0) */
          986  +
          987  +	xor	eax, eax
          988  +	inc	eax
          989  +	shl	eax, cl
          990  +	dec	eax
          991  +	and	eax, edx         /* eax &= hold */
          992  +	add	eax, [esp+64]     /* eax += len */
          993  +	mov	eax, [ebp+eax*4] /* eax = lcode[val+(hold&mask[op])]*/
          994  +	jmp	L_dolen
          995  +
          996  +ALIGN 4
          997  +L_test_for_second_level_dist:
          998  +	test	al, 64
          999  +	jnz	L_invalid_distance_code /* if ((op & 64) != 0) */
         1000  +
         1001  +	xor	eax, eax
         1002  +	inc	eax
         1003  +	shl	eax, cl
         1004  +	dec	eax
         1005  +	and	eax, edx         /* eax &= hold */
         1006  +	add	eax, ebp         /* eax += dist */
         1007  +	mov	ecx, [esp+36]     /* ecx = dcode */
         1008  +	mov	eax, [ecx+eax*4] /* eax = dcode[val+(hold&mask[op])]*/
         1009  +	jmp	L_dodist
         1010  +
         1011  +ALIGN 4
         1012  +L_clip_window:
         1013  +	mov	ecx, eax
         1014  +	mov	eax, [esp+48]     /* eax = wsize */
         1015  +	neg	ecx                /* nbytes = -nbytes */
         1016  +	mov	esi, [esp+28]     /* from = window */
         1017  +
         1018  +	cmp	eax, ebp
         1019  +	jb	L_invalid_distance_too_far /* if (dist > wsize) */
         1020  +
         1021  +	add	ecx, ebp         /* nbytes = dist - nbytes */
         1022  +	cmp	dword ptr [esp+52], 0
         1023  +	jne	L_wrap_around_window /* if (write != 0) */
         1024  +
         1025  +	sub	eax, ecx
         1026  +	add	esi, eax         /* from += wsize - nbytes */
         1027  +
         1028  +	mov	eax, [esp+64]    /* eax = len */
         1029  +	cmp	eax, ecx
         1030  +	jbe	L_do_copy          /* if (nbytes >= len) */
         1031  +
         1032  +	sub	eax, ecx         /* len -= nbytes */
         1033  +	rep     movsb
         1034  +	mov	esi, edi
         1035  +	sub	esi, ebp         /* from = out - dist */
         1036  +	jmp	L_do_copy
         1037  +
         1038  +ALIGN 4
         1039  +L_wrap_around_window:
         1040  +	mov	eax, [esp+52]    /* eax = write */
         1041  +	cmp	ecx, eax
         1042  +	jbe	L_contiguous_in_window /* if (write >= nbytes) */
         1043  +
         1044  +	add	esi, [esp+48]    /* from += wsize */
         1045  +	add	esi, eax         /* from += write */
         1046  +	sub	esi, ecx         /* from -= nbytes */
         1047  +	sub	ecx, eax         /* nbytes -= write */
         1048  +
         1049  +	mov	eax, [esp+64]    /* eax = len */
         1050  +	cmp	eax, ecx
         1051  +	jbe	L_do_copy          /* if (nbytes >= len) */
         1052  +
         1053  +	sub	eax, ecx         /* len -= nbytes */
         1054  +	rep     movsb
         1055  +	mov	esi, [esp+28]     /* from = window */
         1056  +	mov	ecx, [esp+52]     /* nbytes = write */
         1057  +	cmp	eax, ecx
         1058  +	jbe	L_do_copy          /* if (nbytes >= len) */
         1059  +
         1060  +	sub	eax, ecx         /* len -= nbytes */
         1061  +	rep     movsb
         1062  +	mov	esi, edi
         1063  +	sub	esi, ebp         /* from = out - dist */
         1064  +	jmp	L_do_copy
         1065  +
         1066  +ALIGN 4
         1067  +L_contiguous_in_window:
         1068  +	add	esi, eax
         1069  +	sub	esi, ecx         /* from += write - nbytes */
         1070  +
         1071  +	mov	eax, [esp+64]    /* eax = len */
         1072  +	cmp	eax, ecx
         1073  +	jbe	L_do_copy          /* if (nbytes >= len) */
         1074  +
         1075  +	sub	eax, ecx         /* len -= nbytes */
         1076  +	rep     movsb
         1077  +	mov	esi, edi
         1078  +	sub	esi, ebp         /* from = out - dist */
         1079  +	jmp	L_do_copy
         1080  +
         1081  +ALIGN 4
         1082  +L_do_copy:
         1083  +	mov	ecx, eax
         1084  +	rep     movsb
         1085  +
         1086  +	mov	esi, [esp+8]      /* move in back to %esi, toss from */
         1087  +	mov	ebp, [esp+32]     /* ebp = lcode */
         1088  +	jmp	L_while_test
         1089  +
         1090  +L_test_for_end_of_block:
         1091  +	test	al, 32
         1092  +	jz	L_invalid_literal_length_code
         1093  +	mov	dword ptr [esp+72], 1
         1094  +	jmp	L_break_loop_with_status
         1095  +
         1096  +L_invalid_literal_length_code:
         1097  +	mov	dword ptr [esp+72], 2
         1098  +	jmp	L_break_loop_with_status
         1099  +
         1100  +L_invalid_distance_code:
         1101  +	mov	dword ptr [esp+72], 3
         1102  +	jmp	L_break_loop_with_status
         1103  +
         1104  +L_invalid_distance_too_far:
         1105  +	mov	esi, [esp+4]
         1106  +	mov	dword ptr [esp+72], 4
         1107  +	jmp	L_break_loop_with_status
         1108  +
         1109  +L_break_loop:
         1110  +	mov	dword ptr [esp+72], 0
         1111  +
         1112  +L_break_loop_with_status:
         1113  +/* put in, out, bits, and hold back into ar and pop esp */
         1114  +	mov	[esp+8], esi     /* save in */
         1115  +	mov	[esp+16], edi    /* save out */
         1116  +	mov	[esp+44], ebx    /* save bits */
         1117  +	mov	[esp+40], edx    /* save hold */
         1118  +	mov	ebp, [esp+4]     /* restore esp, ebp */
         1119  +	mov	esp, [esp]
         1120  +    }
         1121  +#else
         1122  +#error "x86 architecture not defined"
         1123  +#endif
         1124  +
         1125  +    if (ar.status > 1) {
         1126  +        if (ar.status == 2)
         1127  +            strm->msg = "invalid literal/length code";
         1128  +        else if (ar.status == 3)
         1129  +            strm->msg = "invalid distance code";
         1130  +        else
         1131  +            strm->msg = "invalid distance too far back";
         1132  +        state->mode = BAD;
         1133  +    }
         1134  +    else if ( ar.status == 1 ) {
         1135  +        state->mode = TYPE;
         1136  +    }
         1137  +
         1138  +    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
         1139  +    ar.len = ar.bits >> 3;
         1140  +    ar.in -= ar.len;
         1141  +    ar.bits -= ar.len << 3;
         1142  +    ar.hold &= (1U << ar.bits) - 1;
         1143  +
         1144  +    /* update state and return */
         1145  +    strm->next_in = ar.in;
         1146  +    strm->next_out = ar.out;
         1147  +    strm->avail_in = (unsigned)(ar.in < ar.last ?
         1148  +                                PAD_AVAIL_IN + (ar.last - ar.in) :
         1149  +                                PAD_AVAIL_IN - (ar.in - ar.last));
         1150  +    strm->avail_out = (unsigned)(ar.out < ar.end ?
         1151  +                                 PAD_AVAIL_OUT + (ar.end - ar.out) :
         1152  +                                 PAD_AVAIL_OUT - (ar.out - ar.end));
         1153  +    state->hold = ar.hold;
         1154  +    state->bits = ar.bits;
         1155  +    return;
         1156  +}
         1157  +

Added compat/zlib/contrib/inflate86/inffast.S.

            1  +/*
            2  + * inffast.S is a hand tuned assembler version of:
            3  + *
            4  + * inffast.c -- fast decoding
            5  + * Copyright (C) 1995-2003 Mark Adler
            6  + * For conditions of distribution and use, see copyright notice in zlib.h
            7  + *
            8  + * Copyright (C) 2003 Chris Anderson <christop@charm.net>
            9  + * Please use the copyright conditions above.
           10  + *
           11  + * This version (Jan-23-2003) of inflate_fast was coded and tested under
           12  + * GNU/Linux on a pentium 3, using the gcc-3.2 compiler distribution.  On that
           13  + * machine, I found that gzip style archives decompressed about 20% faster than
           14  + * the gcc-3.2 -O3 -fomit-frame-pointer compiled version.  Your results will
           15  + * depend on how large of a buffer is used for z_stream.next_in & next_out
           16  + * (8K-32K worked best for my 256K cpu cache) and how much overhead there is in
           17  + * stream processing I/O and crc32/addler32.  In my case, this routine used
           18  + * 70% of the cpu time and crc32 used 20%.
           19  + *
           20  + * I am confident that this version will work in the general case, but I have
           21  + * not tested a wide variety of datasets or a wide variety of platforms.
           22  + *
           23  + * Jan-24-2003 -- Added -DUSE_MMX define for slightly faster inflating.
           24  + * It should be a runtime flag instead of compile time flag...
           25  + *
           26  + * Jan-26-2003 -- Added runtime check for MMX support with cpuid instruction.
           27  + * With -DUSE_MMX, only MMX code is compiled.  With -DNO_MMX, only non-MMX code
           28  + * is compiled.  Without either option, runtime detection is enabled.  Runtime
           29  + * detection should work on all modern cpus and the recomended algorithm (flip
           30  + * ID bit on eflags and then use the cpuid instruction) is used in many
           31  + * multimedia applications.  Tested under win2k with gcc-2.95 and gas-2.12
           32  + * distributed with cygwin3.  Compiling with gcc-2.95 -c inffast.S -o
           33  + * inffast.obj generates a COFF object which can then be linked with MSVC++
           34  + * compiled code.  Tested under FreeBSD 4.7 with gcc-2.95.
           35  + *
           36  + * Jan-28-2003 -- Tested Athlon XP... MMX mode is slower than no MMX (and
           37  + * slower than compiler generated code).  Adjusted cpuid check to use the MMX
           38  + * code only for Pentiums < P4 until I have more data on the P4.  Speed
           39  + * improvment is only about 15% on the Athlon when compared with code generated
           40  + * with MSVC++.  Not sure yet, but I think the P4 will also be slower using the
           41  + * MMX mode because many of it's x86 ALU instructions execute in .5 cycles and
           42  + * have less latency than MMX ops.  Added code to buffer the last 11 bytes of
           43  + * the input stream since the MMX code grabs bits in chunks of 32, which
           44  + * differs from the inffast.c algorithm.  I don't think there would have been
           45  + * read overruns where a page boundary was crossed (a segfault), but there
           46  + * could have been overruns when next_in ends on unaligned memory (unintialized
           47  + * memory read).
           48  + *
           49  + * Mar-13-2003 -- P4 MMX is slightly slower than P4 NO_MMX.  I created a C
           50  + * version of the non-MMX code so that it doesn't depend on zstrm and zstate
           51  + * structure offsets which are hard coded in this file.  This was last tested
           52  + * with zlib-1.2.0 which is currently in beta testing, newer versions of this
           53  + * and inffas86.c can be found at http://www.eetbeetee.com/zlib/ and
           54  + * http://www.charm.net/~christop/zlib/
           55  + */
           56  +
           57  +
           58  +/*
           59  + * if you have underscore linking problems (_inflate_fast undefined), try
           60  + * using -DGAS_COFF
           61  + */
           62  +#if ! defined( GAS_COFF ) && ! defined( GAS_ELF )
           63  +
           64  +#if defined( WIN32 ) || defined( __CYGWIN__ )
           65  +#define GAS_COFF /* windows object format */
           66  +#else
           67  +#define GAS_ELF
           68  +#endif
           69  +
           70  +#endif /* ! GAS_COFF && ! GAS_ELF */
           71  +
           72  +
           73  +#if defined( GAS_COFF )
           74  +
           75  +/* coff externals have underscores */
           76  +#define inflate_fast _inflate_fast
           77  +#define inflate_fast_use_mmx _inflate_fast_use_mmx
           78  +
           79  +#endif /* GAS_COFF */
           80  +
           81  +
           82  +.file "inffast.S"
           83  +
           84  +.globl inflate_fast
           85  +
           86  +.text
           87  +.align 4,0
           88  +.L_invalid_literal_length_code_msg:
           89  +.string "invalid literal/length code"
           90  +
           91  +.align 4,0
           92  +.L_invalid_distance_code_msg:
           93  +.string "invalid distance code"
           94  +
           95  +.align 4,0
           96  +.L_invalid_distance_too_far_msg:
           97  +.string "invalid distance too far back"
           98  +
           99  +#if ! defined( NO_MMX )
          100  +.align 4,0
          101  +.L_mask: /* mask[N] = ( 1 << N ) - 1 */
          102  +.long 0
          103  +.long 1
          104  +.long 3
          105  +.long 7
          106  +.long 15
          107  +.long 31
          108  +.long 63
          109  +.long 127
          110  +.long 255
          111  +.long 511
          112  +.long 1023
          113  +.long 2047
          114  +.long 4095
          115  +.long 8191
          116  +.long 16383
          117  +.long 32767
          118  +.long 65535
          119  +.long 131071
          120  +.long 262143
          121  +.long 524287
          122  +.long 1048575
          123  +.long 2097151
          124  +.long 4194303
          125  +.long 8388607
          126  +.long 16777215
          127  +.long 33554431
          128  +.long 67108863
          129  +.long 134217727
          130  +.long 268435455
          131  +.long 536870911
          132  +.long 1073741823
          133  +.long 2147483647
          134  +.long 4294967295
          135  +#endif /* NO_MMX */
          136  +
          137  +.text
          138  +
          139  +/*
          140  + * struct z_stream offsets, in zlib.h
          141  + */
          142  +#define next_in_strm   0   /* strm->next_in */
          143  +#define avail_in_strm  4   /* strm->avail_in */
          144  +#define next_out_strm  12  /* strm->next_out */
          145  +#define avail_out_strm 16  /* strm->avail_out */
          146  +#define msg_strm       24  /* strm->msg */
          147  +#define state_strm     28  /* strm->state */
          148  +
          149  +/*
          150  + * struct inflate_state offsets, in inflate.h
          151  + */
          152  +#define mode_state     0   /* state->mode */
          153  +#define wsize_state    32  /* state->wsize */
          154  +#define write_state    40  /* state->write */
          155  +#define window_state   44  /* state->window */
          156  +#define hold_state     48  /* state->hold */
          157  +#define bits_state     52  /* state->bits */
          158  +#define lencode_state  68  /* state->lencode */
          159  +#define distcode_state 72  /* state->distcode */
          160  +#define lenbits_state  76  /* state->lenbits */
          161  +#define distbits_state 80  /* state->distbits */
          162  +
          163  +/*
          164  + * inflate_fast's activation record
          165  + */
          166  +#define local_var_size 64 /* how much local space for vars */
          167  +#define strm_sp        88 /* first arg: z_stream * (local_var_size + 24) */
          168  +#define start_sp       92 /* second arg: unsigned int (local_var_size + 28) */
          169  +
          170  +/*
          171  + * offsets for local vars on stack
          172  + */
          173  +#define out            60  /* unsigned char* */
          174  +#define window         56  /* unsigned char* */
          175  +#define wsize          52  /* unsigned int */
          176  +#define write          48  /* unsigned int */
          177  +#define in             44  /* unsigned char* */
          178  +#define beg            40  /* unsigned char* */
          179  +#define buf            28  /* char[ 12 ] */
          180  +#define len            24  /* unsigned int */
          181  +#define last           20  /* unsigned char* */
          182  +#define end            16  /* unsigned char* */
          183  +#define dcode          12  /* code* */
          184  +#define lcode           8  /* code* */
          185  +#define dmask           4  /* unsigned int */
          186  +#define lmask           0  /* unsigned int */
          187  +
          188  +/*
          189  + * typedef enum inflate_mode consts, in inflate.h
          190  + */
          191  +#define INFLATE_MODE_TYPE 11  /* state->mode flags enum-ed in inflate.h */
          192  +#define INFLATE_MODE_BAD  26
          193  +
          194  +
          195  +#if ! defined( USE_MMX ) && ! defined( NO_MMX )
          196  +
          197  +#define RUN_TIME_MMX
          198  +
          199  +#define CHECK_MMX    1
          200  +#define DO_USE_MMX   2
          201  +#define DONT_USE_MMX 3
          202  +
          203  +.globl inflate_fast_use_mmx
          204  +
          205  +.data
          206  +
          207  +.align 4,0
          208  +inflate_fast_use_mmx: /* integer flag for run time control 1=check,2=mmx,3=no */
          209  +.long CHECK_MMX
          210  +
          211  +#if defined( GAS_ELF )
          212  +/* elf info */
          213  +.type   inflate_fast_use_mmx,@object
          214  +.size   inflate_fast_use_mmx,4
          215  +#endif
          216  +
          217  +#endif /* RUN_TIME_MMX */
          218  +
          219  +#if defined( GAS_COFF )
          220  +/* coff info: scl 2 = extern, type 32 = function */
          221  +.def inflate_fast; .scl 2; .type 32; .endef
          222  +#endif
          223  +
          224  +.text
          225  +
          226  +.align 32,0x90
          227  +inflate_fast:
          228  +        pushl   %edi
          229  +        pushl   %esi
          230  +        pushl   %ebp
          231  +        pushl   %ebx
          232  +        pushf   /* save eflags (strm_sp, state_sp assumes this is 32 bits) */
          233  +        subl    $local_var_size, %esp
          234  +        cld
          235  +
          236  +#define strm_r  %esi
          237  +#define state_r %edi
          238  +
          239  +        movl    strm_sp(%esp), strm_r
          240  +        movl    state_strm(strm_r), state_r
          241  +
          242  +        /* in = strm->next_in;
          243  +         * out = strm->next_out;
          244  +         * last = in + strm->avail_in - 11;
          245  +         * beg = out - (start - strm->avail_out);
          246  +         * end = out + (strm->avail_out - 257);
          247  +         */
          248  +        movl    avail_in_strm(strm_r), %edx
          249  +        movl    next_in_strm(strm_r), %eax
          250  +
          251  +        addl    %eax, %edx      /* avail_in += next_in */
          252  +        subl    $11, %edx       /* avail_in -= 11 */
          253  +
          254  +        movl    %eax, in(%esp)
          255  +        movl    %edx, last(%esp)
          256  +
          257  +        movl    start_sp(%esp), %ebp
          258  +        movl    avail_out_strm(strm_r), %ecx
          259  +        movl    next_out_strm(strm_r), %ebx
          260  +
          261  +        subl    %ecx, %ebp      /* start -= avail_out */
          262  +        negl    %ebp            /* start = -start */
          263  +        addl    %ebx, %ebp      /* start += next_out */
          264  +
          265  +        subl    $257, %ecx      /* avail_out -= 257 */
          266  +        addl    %ebx, %ecx      /* avail_out += out */
          267  +
          268  +        movl    %ebx, out(%esp)
          269  +        movl    %ebp, beg(%esp)
          270  +        movl    %ecx, end(%esp)
          271  +
          272  +        /* wsize = state->wsize;
          273  +         * write = state->write;
          274  +         * window = state->window;
          275  +         * hold = state->hold;
          276  +         * bits = state->bits;
          277  +         * lcode = state->lencode;
          278  +         * dcode = state->distcode;
          279  +         * lmask = ( 1 << state->lenbits ) - 1;
          280  +         * dmask = ( 1 << state->distbits ) - 1;
          281  +         */
          282  +
          283  +        movl    lencode_state(state_r), %eax
          284  +        movl    distcode_state(state_r), %ecx
          285  +
          286  +        movl    %eax, lcode(%esp)
          287  +        movl    %ecx, dcode(%esp)
          288  +
          289  +        movl    $1, %eax
          290  +        movl    lenbits_state(state_r), %ecx
          291  +        shll    %cl, %eax
          292  +        decl    %eax
          293  +        movl    %eax, lmask(%esp)
          294  +
          295  +        movl    $1, %eax
          296  +        movl    distbits_state(state_r), %ecx
          297  +        shll    %cl, %eax
          298  +        decl    %eax
          299  +        movl    %eax, dmask(%esp)
          300  +
          301  +        movl    wsize_state(state_r), %eax
          302  +        movl    write_state(state_r), %ecx
          303  +        movl    window_state(state_r), %edx
          304  +
          305  +        movl    %eax, wsize(%esp)
          306  +        movl    %ecx, write(%esp)
          307  +        movl    %edx, window(%esp)
          308  +
          309  +        movl    hold_state(state_r), %ebp
          310  +        movl    bits_state(state_r), %ebx
          311  +
          312  +#undef strm_r
          313  +#undef state_r
          314  +
          315  +#define in_r       %esi
          316  +#define from_r     %esi
          317  +#define out_r      %edi
          318  +
          319  +        movl    in(%esp), in_r
          320  +        movl    last(%esp), %ecx
          321  +        cmpl    in_r, %ecx
          322  +        ja      .L_align_long           /* if in < last */
          323  +
          324  +        addl    $11, %ecx               /* ecx = &in[ avail_in ] */
          325  +        subl    in_r, %ecx              /* ecx = avail_in */
          326  +        movl    $12, %eax
          327  +        subl    %ecx, %eax              /* eax = 12 - avail_in */
          328  +        leal    buf(%esp), %edi
          329  +        rep     movsb                   /* memcpy( buf, in, avail_in ) */
          330  +        movl    %eax, %ecx
          331  +        xorl    %eax, %eax
          332  +        rep     stosb         /* memset( &buf[ avail_in ], 0, 12 - avail_in ) */
          333  +        leal    buf(%esp), in_r         /* in = buf */
          334  +        movl    in_r, last(%esp)        /* last = in, do just one iteration */
          335  +        jmp     .L_is_aligned
          336  +
          337  +        /* align in_r on long boundary */
          338  +.L_align_long:
          339  +        testl   $3, in_r
          340  +        jz      .L_is_aligned
          341  +        xorl    %eax, %eax
          342  +        movb    (in_r), %al
          343  +        incl    in_r
          344  +        movl    %ebx, %ecx
          345  +        addl    $8, %ebx
          346  +        shll    %cl, %eax
          347  +        orl     %eax, %ebp
          348  +        jmp     .L_align_long
          349  +
          350  +.L_is_aligned:
          351  +        movl    out(%esp), out_r
          352  +
          353  +#if defined( NO_MMX )
          354  +        jmp     .L_do_loop
          355  +#endif
          356  +
          357  +#if defined( USE_MMX )
          358  +        jmp     .L_init_mmx
          359  +#endif
          360  +
          361  +/*** Runtime MMX check ***/
          362  +
          363  +#if defined( RUN_TIME_MMX )
          364  +.L_check_mmx:
          365  +        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
          366  +        je      .L_init_mmx
          367  +        ja      .L_do_loop /* > 2 */
          368  +
          369  +        pushl   %eax
          370  +        pushl   %ebx
          371  +        pushl   %ecx
          372  +        pushl   %edx
          373  +        pushf
          374  +        movl    (%esp), %eax      /* copy eflags to eax */
          375  +        xorl    $0x200000, (%esp) /* try toggling ID bit of eflags (bit 21)
          376  +                                   * to see if cpu supports cpuid...
          377  +                                   * ID bit method not supported by NexGen but
          378  +                                   * bios may load a cpuid instruction and
          379  +                                   * cpuid may be disabled on Cyrix 5-6x86 */
          380  +        popf
          381  +        pushf
          382  +        popl    %edx              /* copy new eflags to edx */
          383  +        xorl    %eax, %edx        /* test if ID bit is flipped */
          384  +        jz      .L_dont_use_mmx   /* not flipped if zero */
          385  +        xorl    %eax, %eax
          386  +        cpuid
          387  +        cmpl    $0x756e6547, %ebx /* check for GenuineIntel in ebx,ecx,edx */
          388  +        jne     .L_dont_use_mmx
          389  +        cmpl    $0x6c65746e, %ecx
          390  +        jne     .L_dont_use_mmx
          391  +        cmpl    $0x49656e69, %edx
          392  +        jne     .L_dont_use_mmx
          393  +        movl    $1, %eax
          394  +        cpuid                     /* get cpu features */
          395  +        shrl    $8, %eax
          396  +        andl    $15, %eax
          397  +        cmpl    $6, %eax          /* check for Pentium family, is 0xf for P4 */
          398  +        jne     .L_dont_use_mmx
          399  +        testl   $0x800000, %edx   /* test if MMX feature is set (bit 23) */
          400  +        jnz     .L_use_mmx
          401  +        jmp     .L_dont_use_mmx
          402  +.L_use_mmx:
          403  +        movl    $DO_USE_MMX, inflate_fast_use_mmx
          404  +        jmp     .L_check_mmx_pop
          405  +.L_dont_use_mmx:
          406  +        movl    $DONT_USE_MMX, inflate_fast_use_mmx
          407  +.L_check_mmx_pop:
          408  +        popl    %edx
          409  +        popl    %ecx
          410  +        popl    %ebx
          411  +        popl    %eax
          412  +        jmp     .L_check_mmx
          413  +#endif
          414  +
          415  +
          416  +/*** Non-MMX code ***/
          417  +
          418  +#if defined ( NO_MMX ) || defined( RUN_TIME_MMX )
          419  +
          420  +#define hold_r     %ebp
          421  +#define bits_r     %bl
          422  +#define bitslong_r %ebx
          423  +
          424  +.align 32,0x90
          425  +.L_while_test:
          426  +        /* while (in < last && out < end)
          427  +         */
          428  +        cmpl    out_r, end(%esp)
          429  +        jbe     .L_break_loop           /* if (out >= end) */
          430  +
          431  +        cmpl    in_r, last(%esp)
          432  +        jbe     .L_break_loop
          433  +
          434  +.L_do_loop:
          435  +        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
          436  +         *
          437  +         * do {
          438  +         *   if (bits < 15) {
          439  +         *     hold |= *((unsigned short *)in)++ << bits;
          440  +         *     bits += 16
          441  +         *   }
          442  +         *   this = lcode[hold & lmask]
          443  +         */
          444  +        cmpb    $15, bits_r
          445  +        ja      .L_get_length_code      /* if (15 < bits) */
          446  +
          447  +        xorl    %eax, %eax
          448  +        lodsw                           /* al = *(ushort *)in++ */
          449  +        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
          450  +        addb    $16, bits_r             /* bits += 16 */
          451  +        shll    %cl, %eax
          452  +        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
          453  +
          454  +.L_get_length_code:
          455  +        movl    lmask(%esp), %edx       /* edx = lmask */
          456  +        movl    lcode(%esp), %ecx       /* ecx = lcode */
          457  +        andl    hold_r, %edx            /* edx &= hold */
          458  +        movl    (%ecx,%edx,4), %eax     /* eax = lcode[hold & lmask] */
          459  +
          460  +.L_dolen:
          461  +        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
          462  +         *
          463  +         * dolen:
          464  +         *    bits -= this.bits;
          465  +         *    hold >>= this.bits
          466  +         */
          467  +        movb    %ah, %cl                /* cl = this.bits */
          468  +        subb    %ah, bits_r             /* bits -= this.bits */
          469  +        shrl    %cl, hold_r             /* hold >>= this.bits */
          470  +
          471  +        /* check if op is a literal
          472  +         * if (op == 0) {
          473  +         *    PUP(out) = this.val;
          474  +         *  }
          475  +         */
          476  +        testb   %al, %al
          477  +        jnz     .L_test_for_length_base /* if (op != 0) 45.7% */
          478  +
          479  +        shrl    $16, %eax               /* output this.val char */
          480  +        stosb
          481  +        jmp     .L_while_test
          482  +
          483  +.L_test_for_length_base:
          484  +        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = len
          485  +         *
          486  +         * else if (op & 16) {
          487  +         *   len = this.val
          488  +         *   op &= 15
          489  +         *   if (op) {
          490  +         *     if (op > bits) {
          491  +         *       hold |= *((unsigned short *)in)++ << bits;
          492  +         *       bits += 16
          493  +         *     }
          494  +         *     len += hold & mask[op];
          495  +         *     bits -= op;
          496  +         *     hold >>= op;
          497  +         *   }
          498  +         */
          499  +#define len_r %edx
          500  +        movl    %eax, len_r             /* len = this */
          501  +        shrl    $16, len_r              /* len = this.val */
          502  +        movb    %al, %cl
          503  +
          504  +        testb   $16, %al
          505  +        jz      .L_test_for_second_level_length /* if ((op & 16) == 0) 8% */
          506  +        andb    $15, %cl                /* op &= 15 */
          507  +        jz      .L_save_len             /* if (!op) */
          508  +        cmpb    %cl, bits_r
          509  +        jae     .L_add_bits_to_len      /* if (op <= bits) */
          510  +
          511  +        movb    %cl, %ch                /* stash op in ch, freeing cl */
          512  +        xorl    %eax, %eax
          513  +        lodsw                           /* al = *(ushort *)in++ */
          514  +        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
          515  +        addb    $16, bits_r             /* bits += 16 */
          516  +        shll    %cl, %eax
          517  +        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
          518  +        movb    %ch, %cl                /* move op back to ecx */
          519  +
          520  +.L_add_bits_to_len:
          521  +        movl    $1, %eax
          522  +        shll    %cl, %eax
          523  +        decl    %eax
          524  +        subb    %cl, bits_r
          525  +        andl    hold_r, %eax            /* eax &= hold */
          526  +        shrl    %cl, hold_r
          527  +        addl    %eax, len_r             /* len += hold & mask[op] */
          528  +
          529  +.L_save_len:
          530  +        movl    len_r, len(%esp)        /* save len */
          531  +#undef  len_r
          532  +
          533  +.L_decode_distance:
          534  +        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          535  +         *
          536  +         *   if (bits < 15) {
          537  +         *     hold |= *((unsigned short *)in)++ << bits;
          538  +         *     bits += 16
          539  +         *   }
          540  +         *   this = dcode[hold & dmask];
          541  +         * dodist:
          542  +         *   bits -= this.bits;
          543  +         *   hold >>= this.bits;
          544  +         *   op = this.op;
          545  +         */
          546  +
          547  +        cmpb    $15, bits_r
          548  +        ja      .L_get_distance_code    /* if (15 < bits) */
          549  +
          550  +        xorl    %eax, %eax
          551  +        lodsw                           /* al = *(ushort *)in++ */
          552  +        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
          553  +        addb    $16, bits_r             /* bits += 16 */
          554  +        shll    %cl, %eax
          555  +        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
          556  +
          557  +.L_get_distance_code:
          558  +        movl    dmask(%esp), %edx       /* edx = dmask */
          559  +        movl    dcode(%esp), %ecx       /* ecx = dcode */
          560  +        andl    hold_r, %edx            /* edx &= hold */
          561  +        movl    (%ecx,%edx,4), %eax     /* eax = dcode[hold & dmask] */
          562  +
          563  +#define dist_r %edx
          564  +.L_dodist:
          565  +        movl    %eax, dist_r            /* dist = this */
          566  +        shrl    $16, dist_r             /* dist = this.val */
          567  +        movb    %ah, %cl
          568  +        subb    %ah, bits_r             /* bits -= this.bits */
          569  +        shrl    %cl, hold_r             /* hold >>= this.bits */
          570  +
          571  +        /* if (op & 16) {
          572  +         *   dist = this.val
          573  +         *   op &= 15
          574  +         *   if (op > bits) {
          575  +         *     hold |= *((unsigned short *)in)++ << bits;
          576  +         *     bits += 16
          577  +         *   }
          578  +         *   dist += hold & mask[op];
          579  +         *   bits -= op;
          580  +         *   hold >>= op;
          581  +         */
          582  +        movb    %al, %cl                /* cl = this.op */
          583  +
          584  +        testb   $16, %al                /* if ((op & 16) == 0) */
          585  +        jz      .L_test_for_second_level_dist
          586  +        andb    $15, %cl                /* op &= 15 */
          587  +        jz      .L_check_dist_one
          588  +        cmpb    %cl, bits_r
          589  +        jae     .L_add_bits_to_dist     /* if (op <= bits) 97.6% */
          590  +
          591  +        movb    %cl, %ch                /* stash op in ch, freeing cl */
          592  +        xorl    %eax, %eax
          593  +        lodsw                           /* al = *(ushort *)in++ */
          594  +        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
          595  +        addb    $16, bits_r             /* bits += 16 */
          596  +        shll    %cl, %eax
          597  +        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
          598  +        movb    %ch, %cl                /* move op back to ecx */
          599  +
          600  +.L_add_bits_to_dist:
          601  +        movl    $1, %eax
          602  +        shll    %cl, %eax
          603  +        decl    %eax                    /* (1 << op) - 1 */
          604  +        subb    %cl, bits_r
          605  +        andl    hold_r, %eax            /* eax &= hold */
          606  +        shrl    %cl, hold_r
          607  +        addl    %eax, dist_r            /* dist += hold & ((1 << op) - 1) */
          608  +        jmp     .L_check_window
          609  +
          610  +.L_check_window:
          611  +        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          612  +         *       %ecx = nbytes
          613  +         *
          614  +         * nbytes = out - beg;
          615  +         * if (dist <= nbytes) {
          616  +         *   from = out - dist;
          617  +         *   do {
          618  +         *     PUP(out) = PUP(from);
          619  +         *   } while (--len > 0) {
          620  +         * }
          621  +         */
          622  +
          623  +        movl    in_r, in(%esp)          /* save in so from can use it's reg */
          624  +        movl    out_r, %eax
          625  +        subl    beg(%esp), %eax         /* nbytes = out - beg */
          626  +
          627  +        cmpl    dist_r, %eax
          628  +        jb      .L_clip_window          /* if (dist > nbytes) 4.2% */
          629  +
          630  +        movl    len(%esp), %ecx
          631  +        movl    out_r, from_r
          632  +        subl    dist_r, from_r          /* from = out - dist */
          633  +
          634  +        subl    $3, %ecx
          635  +        movb    (from_r), %al
          636  +        movb    %al, (out_r)
          637  +        movb    1(from_r), %al
          638  +        movb    2(from_r), %dl
          639  +        addl    $3, from_r
          640  +        movb    %al, 1(out_r)
          641  +        movb    %dl, 2(out_r)
          642  +        addl    $3, out_r
          643  +        rep     movsb
          644  +
          645  +        movl    in(%esp), in_r          /* move in back to %esi, toss from */
          646  +        jmp     .L_while_test
          647  +
          648  +.align 16,0x90
          649  +.L_check_dist_one:
          650  +        cmpl    $1, dist_r
          651  +        jne     .L_check_window
          652  +        cmpl    out_r, beg(%esp)
          653  +        je      .L_check_window
          654  +
          655  +        decl    out_r
          656  +        movl    len(%esp), %ecx
          657  +        movb    (out_r), %al
          658  +        subl    $3, %ecx
          659  +
          660  +        movb    %al, 1(out_r)
          661  +        movb    %al, 2(out_r)
          662  +        movb    %al, 3(out_r)
          663  +        addl    $4, out_r
          664  +        rep     stosb
          665  +
          666  +        jmp     .L_while_test
          667  +
          668  +.align 16,0x90
          669  +.L_test_for_second_level_length:
          670  +        /* else if ((op & 64) == 0) {
          671  +         *   this = lcode[this.val + (hold & mask[op])];
          672  +         * }
          673  +         */
          674  +        testb   $64, %al
          675  +        jnz     .L_test_for_end_of_block  /* if ((op & 64) != 0) */
          676  +
          677  +        movl    $1, %eax
          678  +        shll    %cl, %eax
          679  +        decl    %eax
          680  +        andl    hold_r, %eax            /* eax &= hold */
          681  +        addl    %edx, %eax              /* eax += this.val */
          682  +        movl    lcode(%esp), %edx       /* edx = lcode */
          683  +        movl    (%edx,%eax,4), %eax     /* eax = lcode[val + (hold&mask[op])] */
          684  +        jmp     .L_dolen
          685  +
          686  +.align 16,0x90
          687  +.L_test_for_second_level_dist:
          688  +        /* else if ((op & 64) == 0) {
          689  +         *   this = dcode[this.val + (hold & mask[op])];
          690  +         * }
          691  +         */
          692  +        testb   $64, %al
          693  +        jnz     .L_invalid_distance_code  /* if ((op & 64) != 0) */
          694  +
          695  +        movl    $1, %eax
          696  +        shll    %cl, %eax
          697  +        decl    %eax
          698  +        andl    hold_r, %eax            /* eax &= hold */
          699  +        addl    %edx, %eax              /* eax += this.val */
          700  +        movl    dcode(%esp), %edx       /* edx = dcode */
          701  +        movl    (%edx,%eax,4), %eax     /* eax = dcode[val + (hold&mask[op])] */
          702  +        jmp     .L_dodist
          703  +
          704  +.align 16,0x90
          705  +.L_clip_window:
          706  +        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          707  +         *       %ecx = nbytes
          708  +         *
          709  +         * else {
          710  +         *   if (dist > wsize) {
          711  +         *     invalid distance
          712  +         *   }
          713  +         *   from = window;
          714  +         *   nbytes = dist - nbytes;
          715  +         *   if (write == 0) {
          716  +         *     from += wsize - nbytes;
          717  +         */
          718  +#define nbytes_r %ecx
          719  +        movl    %eax, nbytes_r
          720  +        movl    wsize(%esp), %eax       /* prepare for dist compare */
          721  +        negl    nbytes_r                /* nbytes = -nbytes */
          722  +        movl    window(%esp), from_r    /* from = window */
          723  +
          724  +        cmpl    dist_r, %eax
          725  +        jb      .L_invalid_distance_too_far /* if (dist > wsize) */
          726  +
          727  +        addl    dist_r, nbytes_r        /* nbytes = dist - nbytes */
          728  +        cmpl    $0, write(%esp)
          729  +        jne     .L_wrap_around_window   /* if (write != 0) */
          730  +
          731  +        subl    nbytes_r, %eax
          732  +        addl    %eax, from_r            /* from += wsize - nbytes */
          733  +
          734  +        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          735  +         *       %ecx = nbytes, %eax = len
          736  +         *
          737  +         *     if (nbytes < len) {
          738  +         *       len -= nbytes;
          739  +         *       do {
          740  +         *         PUP(out) = PUP(from);
          741  +         *       } while (--nbytes);
          742  +         *       from = out - dist;
          743  +         *     }
          744  +         *   }
          745  +         */
          746  +#define len_r %eax
          747  +        movl    len(%esp), len_r
          748  +        cmpl    nbytes_r, len_r
          749  +        jbe     .L_do_copy1             /* if (nbytes >= len) */
          750  +
          751  +        subl    nbytes_r, len_r         /* len -= nbytes */
          752  +        rep     movsb
          753  +        movl    out_r, from_r
          754  +        subl    dist_r, from_r          /* from = out - dist */
          755  +        jmp     .L_do_copy1
          756  +
          757  +        cmpl    nbytes_r, len_r
          758  +        jbe     .L_do_copy1             /* if (nbytes >= len) */
          759  +
          760  +        subl    nbytes_r, len_r         /* len -= nbytes */
          761  +        rep     movsb
          762  +        movl    out_r, from_r
          763  +        subl    dist_r, from_r          /* from = out - dist */
          764  +        jmp     .L_do_copy1
          765  +
          766  +.L_wrap_around_window:
          767  +        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          768  +         *       %ecx = nbytes, %eax = write, %eax = len
          769  +         *
          770  +         *   else if (write < nbytes) {
          771  +         *     from += wsize + write - nbytes;
          772  +         *     nbytes -= write;
          773  +         *     if (nbytes < len) {
          774  +         *       len -= nbytes;
          775  +         *       do {
          776  +         *         PUP(out) = PUP(from);
          777  +         *       } while (--nbytes);
          778  +         *       from = window;
          779  +         *       nbytes = write;
          780  +         *       if (nbytes < len) {
          781  +         *         len -= nbytes;
          782  +         *         do {
          783  +         *           PUP(out) = PUP(from);
          784  +         *         } while(--nbytes);
          785  +         *         from = out - dist;
          786  +         *       }
          787  +         *     }
          788  +         *   }
          789  +         */
          790  +#define write_r %eax
          791  +        movl    write(%esp), write_r
          792  +        cmpl    write_r, nbytes_r
          793  +        jbe     .L_contiguous_in_window /* if (write >= nbytes) */
          794  +
          795  +        addl    wsize(%esp), from_r
          796  +        addl    write_r, from_r
          797  +        subl    nbytes_r, from_r        /* from += wsize + write - nbytes */
          798  +        subl    write_r, nbytes_r       /* nbytes -= write */
          799  +#undef write_r
          800  +
          801  +        movl    len(%esp), len_r
          802  +        cmpl    nbytes_r, len_r
          803  +        jbe     .L_do_copy1             /* if (nbytes >= len) */
          804  +
          805  +        subl    nbytes_r, len_r         /* len -= nbytes */
          806  +        rep     movsb
          807  +        movl    window(%esp), from_r    /* from = window */
          808  +        movl    write(%esp), nbytes_r   /* nbytes = write */
          809  +        cmpl    nbytes_r, len_r
          810  +        jbe     .L_do_copy1             /* if (nbytes >= len) */
          811  +
          812  +        subl    nbytes_r, len_r         /* len -= nbytes */
          813  +        rep     movsb
          814  +        movl    out_r, from_r
          815  +        subl    dist_r, from_r          /* from = out - dist */
          816  +        jmp     .L_do_copy1
          817  +
          818  +.L_contiguous_in_window:
          819  +        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
          820  +         *       %ecx = nbytes, %eax = write, %eax = len
          821  +         *
          822  +         *   else {
          823  +         *     from += write - nbytes;
          824  +         *     if (nbytes < len) {
          825  +         *       len -= nbytes;
          826  +         *       do {
          827  +         *         PUP(out) = PUP(from);
          828  +         *       } while (--nbytes);
          829  +         *       from = out - dist;
          830  +         *     }
          831  +         *   }
          832  +         */
          833  +#define write_r %eax
          834  +        addl    write_r, from_r
          835  +        subl    nbytes_r, from_r        /* from += write - nbytes */
          836  +#undef write_r
          837  +
          838  +        movl    len(%esp), len_r
          839  +        cmpl    nbytes_r, len_r
          840  +        jbe     .L_do_copy1             /* if (nbytes >= len) */
          841  +
          842  +        subl    nbytes_r, len_r         /* len -= nbytes */
          843  +        rep     movsb
          844  +        movl    out_r, from_r
          845  +        subl    dist_r, from_r          /* from = out - dist */
          846  +
          847  +.L_do_copy1:
          848  +        /* regs: %esi = from, %esi = in, %ebp = hold, %bl = bits, %edi = out
          849  +         *       %eax = len
          850  +         *
          851  +         *     while (len > 0) {
          852  +         *       PUP(out) = PUP(from);
          853  +         *       len--;
          854  +         *     }
          855  +         *   }
          856  +         * } while (in < last && out < end);
          857  +         */
          858  +#undef nbytes_r
          859  +#define in_r %esi
          860  +        movl    len_r, %ecx
          861  +        rep     movsb
          862  +
          863  +        movl    in(%esp), in_r          /* move in back to %esi, toss from */
          864  +        jmp     .L_while_test
          865  +
          866  +#undef len_r
          867  +#undef dist_r
          868  +
          869  +#endif /* NO_MMX || RUN_TIME_MMX */
          870  +
          871  +
          872  +/*** MMX code ***/
          873  +
          874  +#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
          875  +
          876  +.align 32,0x90
          877  +.L_init_mmx:
          878  +        emms
          879  +
          880  +#undef  bits_r
          881  +#undef  bitslong_r
          882  +#define bitslong_r %ebp
          883  +#define hold_mm    %mm0
          884  +        movd    %ebp, hold_mm
          885  +        movl    %ebx, bitslong_r
          886  +
          887  +#define used_mm   %mm1
          888  +#define dmask2_mm %mm2
          889  +#define lmask2_mm %mm3
          890  +#define lmask_mm  %mm4
          891  +#define dmask_mm  %mm5
          892  +#define tmp_mm    %mm6
          893  +
          894  +        movd    lmask(%esp), lmask_mm
          895  +        movq    lmask_mm, lmask2_mm
          896  +        movd    dmask(%esp), dmask_mm
          897  +        movq    dmask_mm, dmask2_mm
          898  +        pxor    used_mm, used_mm
          899  +        movl    lcode(%esp), %ebx       /* ebx = lcode */
          900  +        jmp     .L_do_loop_mmx
          901  +
          902  +.align 32,0x90
          903  +.L_while_test_mmx:
          904  +        /* while (in < last && out < end)
          905  +         */
          906  +        cmpl    out_r, end(%esp)
          907  +        jbe     .L_break_loop           /* if (out >= end) */
          908  +
          909  +        cmpl    in_r, last(%esp)
          910  +        jbe     .L_break_loop
          911  +
          912  +.L_do_loop_mmx:
          913  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
          914  +
          915  +        cmpl    $32, bitslong_r
          916  +        ja      .L_get_length_code_mmx  /* if (32 < bits) */
          917  +
          918  +        movd    bitslong_r, tmp_mm
          919  +        movd    (in_r), %mm7
          920  +        addl    $4, in_r
          921  +        psllq   tmp_mm, %mm7
          922  +        addl    $32, bitslong_r
          923  +        por     %mm7, hold_mm           /* hold_mm |= *((uint *)in)++ << bits */
          924  +
          925  +.L_get_length_code_mmx:
          926  +        pand    hold_mm, lmask_mm
          927  +        movd    lmask_mm, %eax
          928  +        movq    lmask2_mm, lmask_mm
          929  +        movl    (%ebx,%eax,4), %eax     /* eax = lcode[hold & lmask] */
          930  +
          931  +.L_dolen_mmx:
          932  +        movzbl  %ah, %ecx               /* ecx = this.bits */
          933  +        movd    %ecx, used_mm
          934  +        subl    %ecx, bitslong_r        /* bits -= this.bits */
          935  +
          936  +        testb   %al, %al
          937  +        jnz     .L_test_for_length_base_mmx /* if (op != 0) 45.7% */
          938  +
          939  +        shrl    $16, %eax               /* output this.val char */
          940  +        stosb
          941  +        jmp     .L_while_test_mmx
          942  +
          943  +.L_test_for_length_base_mmx:
          944  +#define len_r  %edx
          945  +        movl    %eax, len_r             /* len = this */
          946  +        shrl    $16, len_r              /* len = this.val */
          947  +
          948  +        testb   $16, %al
          949  +        jz      .L_test_for_second_level_length_mmx /* if ((op & 16) == 0) 8% */
          950  +        andl    $15, %eax               /* op &= 15 */
          951  +        jz      .L_decode_distance_mmx  /* if (!op) */
          952  +
          953  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
          954  +        movd    %eax, used_mm
          955  +        movd    hold_mm, %ecx
          956  +        subl    %eax, bitslong_r
          957  +        andl    .L_mask(,%eax,4), %ecx
          958  +        addl    %ecx, len_r             /* len += hold & mask[op] */
          959  +
          960  +.L_decode_distance_mmx:
          961  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
          962  +
          963  +        cmpl    $32, bitslong_r
          964  +        ja      .L_get_dist_code_mmx    /* if (32 < bits) */
          965  +
          966  +        movd    bitslong_r, tmp_mm
          967  +        movd    (in_r), %mm7
          968  +        addl    $4, in_r
          969  +        psllq   tmp_mm, %mm7
          970  +        addl    $32, bitslong_r
          971  +        por     %mm7, hold_mm           /* hold_mm |= *((uint *)in)++ << bits */
          972  +
          973  +.L_get_dist_code_mmx:
          974  +        movl    dcode(%esp), %ebx       /* ebx = dcode */
          975  +        pand    hold_mm, dmask_mm
          976  +        movd    dmask_mm, %eax
          977  +        movq    dmask2_mm, dmask_mm
          978  +        movl    (%ebx,%eax,4), %eax     /* eax = dcode[hold & lmask] */
          979  +
          980  +.L_dodist_mmx:
          981  +#define dist_r %ebx
          982  +        movzbl  %ah, %ecx               /* ecx = this.bits */
          983  +        movl    %eax, dist_r
          984  +        shrl    $16, dist_r             /* dist  = this.val */
          985  +        subl    %ecx, bitslong_r        /* bits -= this.bits */
          986  +        movd    %ecx, used_mm
          987  +
          988  +        testb   $16, %al                /* if ((op & 16) == 0) */
          989  +        jz      .L_test_for_second_level_dist_mmx
          990  +        andl    $15, %eax               /* op &= 15 */
          991  +        jz      .L_check_dist_one_mmx
          992  +
          993  +.L_add_bits_to_dist_mmx:
          994  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
          995  +        movd    %eax, used_mm           /* save bit length of current op */
          996  +        movd    hold_mm, %ecx           /* get the next bits on input stream */
          997  +        subl    %eax, bitslong_r        /* bits -= op bits */
          998  +        andl    .L_mask(,%eax,4), %ecx  /* ecx   = hold & mask[op] */
          999  +        addl    %ecx, dist_r            /* dist += hold & mask[op] */
         1000  +
         1001  +.L_check_window_mmx:
         1002  +        movl    in_r, in(%esp)          /* save in so from can use it's reg */
         1003  +        movl    out_r, %eax
         1004  +        subl    beg(%esp), %eax         /* nbytes = out - beg */
         1005  +
         1006  +        cmpl    dist_r, %eax
         1007  +        jb      .L_clip_window_mmx      /* if (dist > nbytes) 4.2% */
         1008  +
         1009  +        movl    len_r, %ecx
         1010  +        movl    out_r, from_r
         1011  +        subl    dist_r, from_r          /* from = out - dist */
         1012  +
         1013  +        subl    $3, %ecx
         1014  +        movb    (from_r), %al
         1015  +        movb    %al, (out_r)
         1016  +        movb    1(from_r), %al
         1017  +        movb    2(from_r), %dl
         1018  +        addl    $3, from_r
         1019  +        movb    %al, 1(out_r)
         1020  +        movb    %dl, 2(out_r)
         1021  +        addl    $3, out_r
         1022  +        rep     movsb
         1023  +
         1024  +        movl    in(%esp), in_r          /* move in back to %esi, toss from */
         1025  +        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
         1026  +        jmp     .L_while_test_mmx
         1027  +
         1028  +.align 16,0x90
         1029  +.L_check_dist_one_mmx:
         1030  +        cmpl    $1, dist_r
         1031  +        jne     .L_check_window_mmx
         1032  +        cmpl    out_r, beg(%esp)
         1033  +        je      .L_check_window_mmx
         1034  +
         1035  +        decl    out_r
         1036  +        movl    len_r, %ecx
         1037  +        movb    (out_r), %al
         1038  +        subl    $3, %ecx
         1039  +
         1040  +        movb    %al, 1(out_r)
         1041  +        movb    %al, 2(out_r)
         1042  +        movb    %al, 3(out_r)
         1043  +        addl    $4, out_r
         1044  +        rep     stosb
         1045  +
         1046  +        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
         1047  +        jmp     .L_while_test_mmx
         1048  +
         1049  +.align 16,0x90
         1050  +.L_test_for_second_level_length_mmx:
         1051  +        testb   $64, %al
         1052  +        jnz     .L_test_for_end_of_block  /* if ((op & 64) != 0) */
         1053  +
         1054  +        andl    $15, %eax
         1055  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
         1056  +        movd    hold_mm, %ecx
         1057  +        andl    .L_mask(,%eax,4), %ecx
         1058  +        addl    len_r, %ecx
         1059  +        movl    (%ebx,%ecx,4), %eax     /* eax = lcode[hold & lmask] */
         1060  +        jmp     .L_dolen_mmx
         1061  +
         1062  +.align 16,0x90
         1063  +.L_test_for_second_level_dist_mmx:
         1064  +        testb   $64, %al
         1065  +        jnz     .L_invalid_distance_code  /* if ((op & 64) != 0) */
         1066  +
         1067  +        andl    $15, %eax
         1068  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
         1069  +        movd    hold_mm, %ecx
         1070  +        andl    .L_mask(,%eax,4), %ecx
         1071  +        movl    dcode(%esp), %eax       /* ecx = dcode */
         1072  +        addl    dist_r, %ecx
         1073  +        movl    (%eax,%ecx,4), %eax     /* eax = lcode[hold & lmask] */
         1074  +        jmp     .L_dodist_mmx
         1075  +
         1076  +.align 16,0x90
         1077  +.L_clip_window_mmx:
         1078  +#define nbytes_r %ecx
         1079  +        movl    %eax, nbytes_r
         1080  +        movl    wsize(%esp), %eax       /* prepare for dist compare */
         1081  +        negl    nbytes_r                /* nbytes = -nbytes */
         1082  +        movl    window(%esp), from_r    /* from = window */
         1083  +
         1084  +        cmpl    dist_r, %eax
         1085  +        jb      .L_invalid_distance_too_far /* if (dist > wsize) */
         1086  +
         1087  +        addl    dist_r, nbytes_r        /* nbytes = dist - nbytes */
         1088  +        cmpl    $0, write(%esp)
         1089  +        jne     .L_wrap_around_window_mmx /* if (write != 0) */
         1090  +
         1091  +        subl    nbytes_r, %eax
         1092  +        addl    %eax, from_r            /* from += wsize - nbytes */
         1093  +
         1094  +        cmpl    nbytes_r, len_r
         1095  +        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */
         1096  +
         1097  +        subl    nbytes_r, len_r         /* len -= nbytes */
         1098  +        rep     movsb
         1099  +        movl    out_r, from_r
         1100  +        subl    dist_r, from_r          /* from = out - dist */
         1101  +        jmp     .L_do_copy1_mmx
         1102  +
         1103  +        cmpl    nbytes_r, len_r
         1104  +        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */
         1105  +
         1106  +        subl    nbytes_r, len_r         /* len -= nbytes */
         1107  +        rep     movsb
         1108  +        movl    out_r, from_r
         1109  +        subl    dist_r, from_r          /* from = out - dist */
         1110  +        jmp     .L_do_copy1_mmx
         1111  +
         1112  +.L_wrap_around_window_mmx:
         1113  +#define write_r %eax
         1114  +        movl    write(%esp), write_r
         1115  +        cmpl    write_r, nbytes_r
         1116  +        jbe     .L_contiguous_in_window_mmx /* if (write >= nbytes) */
         1117  +
         1118  +        addl    wsize(%esp), from_r
         1119  +        addl    write_r, from_r
         1120  +        subl    nbytes_r, from_r        /* from += wsize + write - nbytes */
         1121  +        subl    write_r, nbytes_r       /* nbytes -= write */
         1122  +#undef write_r
         1123  +
         1124  +        cmpl    nbytes_r, len_r
         1125  +        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */
         1126  +
         1127  +        subl    nbytes_r, len_r         /* len -= nbytes */
         1128  +        rep     movsb
         1129  +        movl    window(%esp), from_r    /* from = window */
         1130  +        movl    write(%esp), nbytes_r   /* nbytes = write */
         1131  +        cmpl    nbytes_r, len_r
         1132  +        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */
         1133  +
         1134  +        subl    nbytes_r, len_r         /* len -= nbytes */
         1135  +        rep     movsb
         1136  +        movl    out_r, from_r
         1137  +        subl    dist_r, from_r          /* from = out - dist */
         1138  +        jmp     .L_do_copy1_mmx
         1139  +
         1140  +.L_contiguous_in_window_mmx:
         1141  +#define write_r %eax
         1142  +        addl    write_r, from_r
         1143  +        subl    nbytes_r, from_r        /* from += write - nbytes */
         1144  +#undef write_r
         1145  +
         1146  +        cmpl    nbytes_r, len_r
         1147  +        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */
         1148  +
         1149  +        subl    nbytes_r, len_r         /* len -= nbytes */
         1150  +        rep     movsb
         1151  +        movl    out_r, from_r
         1152  +        subl    dist_r, from_r          /* from = out - dist */
         1153  +
         1154  +.L_do_copy1_mmx:
         1155  +#undef nbytes_r
         1156  +#define in_r %esi
         1157  +        movl    len_r, %ecx
         1158  +        rep     movsb
         1159  +
         1160  +        movl    in(%esp), in_r          /* move in back to %esi, toss from */
         1161  +        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
         1162  +        jmp     .L_while_test_mmx
         1163  +
         1164  +#undef hold_r
         1165  +#undef bitslong_r
         1166  +
         1167  +#endif /* USE_MMX || RUN_TIME_MMX */
         1168  +
         1169  +
         1170  +/*** USE_MMX, NO_MMX, and RUNTIME_MMX from here on ***/
         1171  +
         1172  +.L_invalid_distance_code:
         1173  +        /* else {
         1174  +         *   strm->msg = "invalid distance code";
         1175  +         *   state->mode = BAD;
         1176  +         * }
         1177  +         */
         1178  +        movl    $.L_invalid_distance_code_msg, %ecx
         1179  +        movl    $INFLATE_MODE_BAD, %edx
         1180  +        jmp     .L_update_stream_state
         1181  +
         1182  +.L_test_for_end_of_block:
         1183  +        /* else if (op & 32) {
         1184  +         *   state->mode = TYPE;
         1185  +         *   break;
         1186  +         * }
         1187  +         */
         1188  +        testb   $32, %al
         1189  +        jz      .L_invalid_literal_length_code  /* if ((op & 32) == 0) */
         1190  +
         1191  +        movl    $0, %ecx
         1192  +        movl    $INFLATE_MODE_TYPE, %edx
         1193  +        jmp     .L_update_stream_state
         1194  +
         1195  +.L_invalid_literal_length_code:
         1196  +        /* else {
         1197  +         *   strm->msg = "invalid literal/length code";
         1198  +         *   state->mode = BAD;
         1199  +         * }
         1200  +         */
         1201  +        movl    $.L_invalid_literal_length_code_msg, %ecx
         1202  +        movl    $INFLATE_MODE_BAD, %edx
         1203  +        jmp     .L_update_stream_state
         1204  +
         1205  +.L_invalid_distance_too_far:
         1206  +        /* strm->msg = "invalid distance too far back";
         1207  +         * state->mode = BAD;
         1208  +         */
         1209  +        movl    in(%esp), in_r          /* from_r has in's reg, put in back */
         1210  +        movl    $.L_invalid_distance_too_far_msg, %ecx
         1211  +        movl    $INFLATE_MODE_BAD, %edx
         1212  +        jmp     .L_update_stream_state
         1213  +
         1214  +.L_update_stream_state:
         1215  +        /* set strm->msg = %ecx, strm->state->mode = %edx */
         1216  +        movl    strm_sp(%esp), %eax
         1217  +        testl   %ecx, %ecx              /* if (msg != NULL) */
         1218  +        jz      .L_skip_msg
         1219  +        movl    %ecx, msg_strm(%eax)    /* strm->msg = msg */
         1220  +.L_skip_msg:
         1221  +        movl    state_strm(%eax), %eax  /* state = strm->state */
         1222  +        movl    %edx, mode_state(%eax)  /* state->mode = edx (BAD | TYPE) */
         1223  +        jmp     .L_break_loop
         1224  +
         1225  +.align 32,0x90
         1226  +.L_break_loop:
         1227  +
         1228  +/*
         1229  + * Regs:
         1230  + *
         1231  + * bits = %ebp when mmx, and in %ebx when non-mmx
         1232  + * hold = %hold_mm when mmx, and in %ebp when non-mmx
         1233  + * in   = %esi
         1234  + * out  = %edi
         1235  + */
         1236  +
         1237  +#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
         1238  +
         1239  +#if defined( RUN_TIME_MMX )
         1240  +
         1241  +        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
         1242  +        jne     .L_update_next_in
         1243  +
         1244  +#endif /* RUN_TIME_MMX */
         1245  +
         1246  +        movl    %ebp, %ebx
         1247  +
         1248  +.L_update_next_in:
         1249  +
         1250  +#endif
         1251  +
         1252  +#define strm_r  %eax
         1253  +#define state_r %edx
         1254  +
         1255  +        /* len = bits >> 3;
         1256  +         * in -= len;
         1257  +         * bits -= len << 3;
         1258  +         * hold &= (1U << bits) - 1;
         1259  +         * state->hold = hold;
         1260  +         * state->bits = bits;
         1261  +         * strm->next_in = in;
         1262  +         * strm->next_out = out;
         1263  +         */
         1264  +        movl    strm_sp(%esp), strm_r
         1265  +        movl    %ebx, %ecx
         1266  +        movl    state_strm(strm_r), state_r
         1267  +        shrl    $3, %ecx
         1268  +        subl    %ecx, in_r
         1269  +        shll    $3, %ecx
         1270  +        subl    %ecx, %ebx
         1271  +        movl    out_r, next_out_strm(strm_r)
         1272  +        movl    %ebx, bits_state(state_r)
         1273  +        movl    %ebx, %ecx
         1274  +
         1275  +        leal    buf(%esp), %ebx
         1276  +        cmpl    %ebx, last(%esp)
         1277  +        jne     .L_buf_not_used         /* if buf != last */
         1278  +
         1279  +        subl    %ebx, in_r              /* in -= buf */
         1280  +        movl    next_in_strm(strm_r), %ebx
         1281  +        movl    %ebx, last(%esp)        /* last = strm->next_in */
         1282  +        addl    %ebx, in_r              /* in += strm->next_in */
         1283  +        movl    avail_in_strm(strm_r), %ebx
         1284  +        subl    $11, %ebx
         1285  +        addl    %ebx, last(%esp)    /* last = &strm->next_in[ avail_in - 11 ] */
         1286  +
         1287  +.L_buf_not_used:
         1288  +        movl    in_r, next_in_strm(strm_r)
         1289  +
         1290  +        movl    $1, %ebx
         1291  +        shll    %cl, %ebx
         1292  +        decl    %ebx
         1293  +
         1294  +#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
         1295  +
         1296  +#if defined( RUN_TIME_MMX )
         1297  +
         1298  +        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
         1299  +        jne     .L_update_hold
         1300  +
         1301  +#endif /* RUN_TIME_MMX */
         1302  +
         1303  +        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
         1304  +        movd    hold_mm, %ebp
         1305  +
         1306  +        emms
         1307  +
         1308  +.L_update_hold:
         1309  +
         1310  +#endif /* USE_MMX || RUN_TIME_MMX */
         1311  +
         1312  +        andl    %ebx, %ebp
         1313  +        movl    %ebp, hold_state(state_r)
         1314  +
         1315  +#define last_r %ebx
         1316  +
         1317  +        /* strm->avail_in = in < last ? 11 + (last - in) : 11 - (in - last) */
         1318  +        movl    last(%esp), last_r
         1319  +        cmpl    in_r, last_r
         1320  +        jbe     .L_last_is_smaller     /* if (in >= last) */
         1321  +
         1322  +        subl    in_r, last_r           /* last -= in */
         1323  +        addl    $11, last_r            /* last += 11 */
         1324  +        movl    last_r, avail_in_strm(strm_r)
         1325  +        jmp     .L_fixup_out
         1326  +.L_last_is_smaller:
         1327  +        subl    last_r, in_r           /* in -= last */
         1328  +        negl    in_r                   /* in = -in */
         1329  +        addl    $11, in_r              /* in += 11 */
         1330  +        movl    in_r, avail_in_strm(strm_r)
         1331  +
         1332  +#undef last_r
         1333  +#define end_r %ebx
         1334  +
         1335  +.L_fixup_out:
         1336  +        /* strm->avail_out = out < end ? 257 + (end - out) : 257 - (out - end)*/
         1337  +        movl    end(%esp), end_r
         1338  +        cmpl    out_r, end_r
         1339  +        jbe     .L_end_is_smaller      /* if (out >= end) */
         1340  +
         1341  +        subl    out_r, end_r           /* end -= out */
         1342  +        addl    $257, end_r            /* end += 257 */
         1343  +        movl    end_r, avail_out_strm(strm_r)
         1344  +        jmp     .L_done
         1345  +.L_end_is_smaller:
         1346  +        subl    end_r, out_r           /* out -= end */
         1347  +        negl    out_r                  /* out = -out */
         1348  +        addl    $257, out_r            /* out += 257 */
         1349  +        movl    out_r, avail_out_strm(strm_r)
         1350  +
         1351  +#undef end_r
         1352  +#undef strm_r
         1353  +#undef state_r
         1354  +
         1355  +.L_done:
         1356  +        addl    $local_var_size, %esp
         1357  +        popf
         1358  +        popl    %ebx
         1359  +        popl    %ebp
         1360  +        popl    %esi
         1361  +        popl    %edi
         1362  +        ret
         1363  +
         1364  +#if defined( GAS_ELF )
         1365  +/* elf info */
         1366  +.type inflate_fast,@function
         1367  +.size inflate_fast,.-inflate_fast
         1368  +#endif

Added compat/zlib/contrib/iostream/test.cpp.

            1  +
            2  +#include "zfstream.h"
            3  +
            4  +int main() {
            5  +
            6  +  // Construct a stream object with this filebuffer.  Anything sent
            7  +  // to this stream will go to standard out.
            8  +  gzofstream os( 1, ios::out );
            9  +
           10  +  // This text is getting compressed and sent to stdout.
           11  +  // To prove this, run 'test | zcat'.
           12  +  os << "Hello, Mommy" << endl;
           13  +
           14  +  os << setcompressionlevel( Z_NO_COMPRESSION );
           15  +  os << "hello, hello, hi, ho!" << endl;
           16  +
           17  +  setcompressionlevel( os, Z_DEFAULT_COMPRESSION )
           18  +    << "I'm compressing again" << endl;
           19  +
           20  +  os.close();
           21  +
           22  +  return 0;
           23  +
           24  +}

Added compat/zlib/contrib/iostream/zfstream.cpp.

            1  +
            2  +#include "zfstream.h"
            3  +
            4  +gzfilebuf::gzfilebuf() :
            5  +  file(NULL),
            6  +  mode(0),
            7  +  own_file_descriptor(0)
            8  +{ }
            9  +
           10  +gzfilebuf::~gzfilebuf() {
           11  +
           12  +  sync();
           13  +  if ( own_file_descriptor )
           14  +    close();
           15  +
           16  +}
           17  +
           18  +gzfilebuf *gzfilebuf::open( const char *name,
           19  +                            int io_mode ) {
           20  +
           21  +  if ( is_open() )
           22  +    return NULL;
           23  +
           24  +  char char_mode[10];
           25  +  char *p = char_mode;
           26  +
           27  +  if ( io_mode & ios::in ) {
           28  +    mode = ios::in;
           29  +    *p++ = 'r';
           30  +  } else if ( io_mode & ios::app ) {
           31  +    mode = ios::app;
           32  +    *p++ = 'a';
           33  +  } else {
           34  +    mode = ios::out;
           35  +    *p++ = 'w';
           36  +  }
           37  +
           38  +  if ( io_mode & ios::binary ) {
           39  +    mode |= ios::binary;
           40  +    *p++ = 'b';
           41  +  }
           42  +
           43  +  // Hard code the compression level
           44  +  if ( io_mode & (ios::out|ios::app )) {
           45  +    *p++ = '9';
           46  +  }
           47  +
           48  +  // Put the end-of-string indicator
           49  +  *p = '\0';
           50  +
           51  +  if ( (file = gzopen(name, char_mode)) == NULL )
           52  +    return NULL;
           53  +
           54  +  own_file_descriptor = 1;
           55  +
           56  +  return this;
           57  +
           58  +}
           59  +
           60  +gzfilebuf *gzfilebuf::attach( int file_descriptor,
           61  +                              int io_mode ) {
           62  +
           63  +  if ( is_open() )
           64  +    return NULL;
           65  +
           66  +  char char_mode[10];
           67  +  char *p = char_mode;
           68  +
           69  +  if ( io_mode & ios::in ) {
           70  +    mode = ios::in;
           71  +    *p++ = 'r';
           72  +  } else if ( io_mode & ios::app ) {
           73  +    mode = ios::app;
           74  +    *p++ = 'a';
           75  +  } else {
           76  +    mode = ios::out;
           77  +    *p++ = 'w';
           78  +  }
           79  +
           80  +  if ( io_mode & ios::binary ) {
           81  +    mode |= ios::binary;
           82  +    *p++ = 'b';
           83  +  }
           84  +
           85  +  // Hard code the compression level
           86  +  if ( io_mode & (ios::out|ios::app )) {
           87  +    *p++ = '9';
           88  +  }
           89  +
           90  +  // Put the end-of-string indicator
           91  +  *p = '\0';
           92  +
           93  +  if ( (file = gzdopen(file_descriptor, char_mode)) == NULL )
           94  +    return NULL;
           95  +
           96  +  own_file_descriptor = 0;
           97  +
           98  +  return this;
           99  +
          100  +}
          101  +
          102  +gzfilebuf *gzfilebuf::close() {
          103  +
          104  +  if ( is_open() ) {
          105  +
          106  +    sync();
          107  +    gzclose( file );
          108  +    file = NULL;
          109  +
          110  +  }
          111  +
          112  +  return this;
          113  +
          114  +}
          115  +
          116  +int gzfilebuf::setcompressionlevel( int comp_level ) {
          117  +
          118  +  return gzsetparams(file, comp_level, -2);
          119  +
          120  +}
          121  +
          122  +int gzfilebuf::setcompressionstrategy( int comp_strategy ) {
          123  +
          124  +  return gzsetparams(file, -2, comp_strategy);
          125  +
          126  +}
          127  +
          128  +
          129  +streampos gzfilebuf::seekoff( streamoff off, ios::seek_dir dir, int which ) {
          130  +
          131  +  return streampos(EOF);
          132  +
          133  +}
          134  +
          135  +int gzfilebuf::underflow() {
          136  +
          137  +  // If the file hasn't been opened for reading, error.
          138  +  if ( !is_open() || !(mode & ios::in) )
          139  +    return EOF;
          140  +
          141  +  // if a buffer doesn't exists, allocate one.
          142  +  if ( !base() ) {
          143  +
          144  +    if ( (allocate()) == EOF )
          145  +      return EOF;
          146  +    setp(0,0);
          147  +
          148  +  } else {
          149  +
          150  +    if ( in_avail() )
          151  +      return (unsigned char) *gptr();
          152  +
          153  +    if ( out_waiting() ) {
          154  +      if ( flushbuf() == EOF )
          155  +        return EOF;
          156  +    }
          157  +
          158  +  }
          159  +
          160  +  // Attempt to fill the buffer.
          161  +
          162  +  int result = fillbuf();
          163  +  if ( result == EOF ) {
          164  +    // disable get area
          165  +    setg(0,0,0);
          166  +    return EOF;
          167  +  }
          168  +
          169  +  return (unsigned char) *gptr();
          170  +
          171  +}
          172  +
          173  +int gzfilebuf::overflow( int c ) {
          174  +
          175  +  if ( !is_open() || !(mode & ios::out) )
          176  +    return EOF;
          177  +
          178  +  if ( !base() ) {
          179  +    if ( allocate() == EOF )
          180  +      return EOF;
          181  +    setg(0,0,0);
          182  +  } else {
          183  +    if (in_avail()) {
          184  +        return EOF;
          185  +    }
          186  +    if (out_waiting()) {
          187  +      if (flushbuf() == EOF)
          188  +        return EOF;
          189  +    }
          190  +  }
          191  +
          192  +  int bl = blen();
          193  +  setp( base(), base() + bl);
          194  +
          195  +  if ( c != EOF ) {
          196  +
          197  +    *pptr() = c;
          198  +    pbump(1);
          199  +
          200  +  }
          201  +
          202  +  return 0;
          203  +
          204  +}
          205  +
          206  +int gzfilebuf::sync() {
          207  +
          208  +  if ( !is_open() )
          209  +    return EOF;
          210  +
          211  +  if ( out_waiting() )
          212  +    return flushbuf();
          213  +
          214  +  return 0;
          215  +
          216  +}
          217  +
          218  +int gzfilebuf::flushbuf() {
          219  +
          220  +  int n;
          221  +  char *q;
          222  +
          223  +  q = pbase();
          224  +  n = pptr() - q;
          225  +
          226  +  if ( gzwrite( file, q, n) < n )
          227  +    return EOF;
          228  +
          229  +  setp(0,0);
          230  +
          231  +  return 0;
          232  +
          233  +}
          234  +
          235  +int gzfilebuf::fillbuf() {
          236  +
          237  +  int required;
          238  +  char *p;
          239  +
          240  +  p = base();
          241  +
          242  +  required = blen();
          243  +
          244  +  int t = gzread( file, p, required );
          245  +
          246  +  if ( t <= 0) return EOF;
          247  +
          248  +  setg( base(), base(), base()+t);
          249  +
          250  +  return t;
          251  +
          252  +}
          253  +
          254  +gzfilestream_common::gzfilestream_common() :
          255  +  ios( gzfilestream_common::rdbuf() )
          256  +{ }
          257  +
          258  +gzfilestream_common::~gzfilestream_common()
          259  +{ }
          260  +
          261  +void gzfilestream_common::attach( int fd, int io_mode ) {
          262  +
          263  +  if ( !buffer.attach( fd, io_mode) )
          264  +    clear( ios::failbit | ios::badbit );
          265  +  else
          266  +    clear();
          267  +
          268  +}
          269  +
          270  +void gzfilestream_common::open( const char *name, int io_mode ) {
          271  +
          272  +  if ( !buffer.open( name, io_mode ) )
          273  +    clear( ios::failbit | ios::badbit );
          274  +  else
          275  +    clear();
          276  +
          277  +}
          278  +
          279  +void gzfilestream_common::close() {
          280  +
          281  +  if ( !buffer.close() )
          282  +    clear( ios::failbit | ios::badbit );
          283  +
          284  +}
          285  +
          286  +gzfilebuf *gzfilestream_common::rdbuf()
          287  +{
          288  +  return &buffer;
          289  +}
          290  +
          291  +gzifstream::gzifstream() :
          292  +  ios( gzfilestream_common::rdbuf() )
          293  +{
          294  +  clear( ios::badbit );
          295  +}
          296  +
          297  +gzifstream::gzifstream( const char *name, int io_mode ) :
          298  +  ios( gzfilestream_common::rdbuf() )
          299  +{
          300  +  gzfilestream_common::open( name, io_mode );
          301  +}
          302  +
          303  +gzifstream::gzifstream( int fd, int io_mode ) :
          304  +  ios( gzfilestream_common::rdbuf() )
          305  +{
          306  +  gzfilestream_common::attach( fd, io_mode );
          307  +}
          308  +
          309  +gzifstream::~gzifstream() { }
          310  +
          311  +gzofstream::gzofstream() :
          312  +  ios( gzfilestream_common::rdbuf() )
          313  +{
          314  +  clear( ios::badbit );
          315  +}
          316  +
          317  +gzofstream::gzofstream( const char *name, int io_mode ) :
          318  +  ios( gzfilestream_common::rdbuf() )
          319  +{
          320  +  gzfilestream_common::open( name, io_mode );
          321  +}
          322  +
          323  +gzofstream::gzofstream( int fd, int io_mode ) :
          324  +  ios( gzfilestream_common::rdbuf() )
          325  +{
          326  +  gzfilestream_common::attach( fd, io_mode );
          327  +}
          328  +
          329  +gzofstream::~gzofstream() { }

Added compat/zlib/contrib/iostream/zfstream.h.

            1  +
            2  +#ifndef zfstream_h
            3  +#define zfstream_h
            4  +
            5  +#include <fstream.h>
            6  +#include "zlib.h"
            7  +
            8  +class gzfilebuf : public streambuf {
            9  +
           10  +public:
           11  +
           12  +  gzfilebuf( );
           13  +  virtual ~gzfilebuf();
           14  +
           15  +  gzfilebuf *open( const char *name, int io_mode );
           16  +  gzfilebuf *attach( int file_descriptor, int io_mode );
           17  +  gzfilebuf *close();
           18  +
           19  +  int setcompressionlevel( int comp_level );
           20  +  int setcompressionstrategy( int comp_strategy );
           21  +
           22  +  inline int is_open() const { return (file !=NULL); }
           23  +
           24  +  virtual streampos seekoff( streamoff, ios::seek_dir, int );
           25  +
           26  +  virtual int sync();
           27  +
           28  +protected:
           29  +
           30  +  virtual int underflow();
           31  +  virtual int overflow( int = EOF );
           32  +
           33  +private:
           34  +
           35  +  gzFile file;
           36  +  short mode;
           37  +  short own_file_descriptor;
           38  +
           39  +  int flushbuf();
           40  +  int fillbuf();
           41  +
           42  +};
           43  +
           44  +class gzfilestream_common : virtual public ios {
           45  +
           46  +  friend class gzifstream;
           47  +  friend class gzofstream;
           48  +  friend gzofstream &setcompressionlevel( gzofstream &, int );
           49  +  friend gzofstream &setcompressionstrategy( gzofstream &, int );
           50  +
           51  +public:
           52  +  virtual ~gzfilestream_common();
           53  +
           54  +  void attach( int fd, int io_mode );
           55  +  void open( const char *name, int io_mode );
           56  +  void close();
           57  +
           58  +protected:
           59  +  gzfilestream_common();
           60  +
           61  +private:
           62  +  gzfilebuf *rdbuf();
           63  +
           64  +  gzfilebuf buffer;
           65  +
           66  +};
           67  +
           68  +class gzifstream : public gzfilestream_common, public istream {
           69  +
           70  +public:
           71  +
           72  +  gzifstream();
           73  +  gzifstream( const char *name, int io_mode = ios::in );
           74  +  gzifstream( int fd, int io_mode = ios::in );
           75  +
           76  +  virtual ~gzifstream();
           77  +
           78  +};
           79  +
           80  +class gzofstream : public gzfilestream_common, public ostream {
           81  +
           82  +public:
           83  +
           84  +  gzofstream();
           85  +  gzofstream( const char *name, int io_mode = ios::out );
           86  +  gzofstream( int fd, int io_mode = ios::out );
           87  +
           88  +  virtual ~gzofstream();
           89  +
           90  +};
           91  +
           92  +template<class T> class gzomanip {
           93  +  friend gzofstream &operator<<(gzofstream &, const gzomanip<T> &);
           94  +public:
           95  +  gzomanip(gzofstream &(*f)(gzofstream &, T), T v) : func(f), val(v) { }
           96  +private:
           97  +  gzofstream &(*func)(gzofstream &, T);
           98  +  T val;
           99  +};
          100  +
          101  +template<class T> gzofstream &operator<<(gzofstream &s, const gzomanip<T> &m)
          102  +{
          103  +  return (*m.func)(s, m.val);
          104  +}
          105  +
          106  +inline gzofstream &setcompressionlevel( gzofstream &s, int l )
          107  +{
          108  +  (s.rdbuf())->setcompressionlevel(l);
          109  +  return s;
          110  +}
          111  +
          112  +inline gzofstream &setcompressionstrategy( gzofstream &s, int l )
          113  +{
          114  +  (s.rdbuf())->setcompressionstrategy(l);
          115  +  return s;
          116  +}
          117  +
          118  +inline gzomanip<int> setcompressionlevel(int l)
          119  +{
          120  +  return gzomanip<int>(&setcompressionlevel,l);
          121  +}
          122  +
          123  +inline gzomanip<int> setcompressionstrategy(int l)
          124  +{
          125  +  return gzomanip<int>(&setcompressionstrategy,l);
          126  +}
          127  +
          128  +#endif

Added compat/zlib/contrib/iostream2/zstream.h.

            1  +/*
            2  + *
            3  + * Copyright (c) 1997
            4  + * Christian Michelsen Research AS
            5  + * Advanced Computing
            6  + * Fantoftvegen 38, 5036 BERGEN, Norway
            7  + * http://www.cmr.no
            8  + *
            9  + * Permission to use, copy, modify, distribute and sell this software
           10  + * and its documentation for any purpose is hereby granted without fee,
           11  + * provided that the above copyright notice appear in all copies and
           12  + * that both that copyright notice and this permission notice appear
           13  + * in supporting documentation.  Christian Michelsen Research AS makes no
           14  + * representations about the suitability of this software for any
           15  + * purpose.  It is provided "as is" without express or implied warranty.
           16  + *
           17  + */
           18  +
           19  +#ifndef ZSTREAM__H
           20  +#define ZSTREAM__H
           21  +
           22  +/*
           23  + * zstream.h - C++ interface to the 'zlib' general purpose compression library
           24  + * $Id: zstream.h 1.1 1997-06-25 12:00:56+02 tyge Exp tyge $
           25  + */
           26  +
           27  +#include <strstream.h>
           28  +#include <string.h>
           29  +#include <stdio.h>
           30  +#include "zlib.h"
           31  +
           32  +#if defined(_WIN32)
           33  +#   include <fcntl.h>
           34  +#   include <io.h>
           35  +#   define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
           36  +#else
           37  +#   define SET_BINARY_MODE(file)
           38  +#endif
           39  +
           40  +class zstringlen {
           41  +public:
           42  +    zstringlen(class izstream&);
           43  +    zstringlen(class ozstream&, const char*);
           44  +    size_t value() const { return val.word; }
           45  +private:
           46  +    struct Val { unsigned char byte; size_t word; } val;
           47  +};
           48  +
           49  +//  ----------------------------- izstream -----------------------------
           50  +
           51  +class izstream
           52  +{
           53  +    public:
           54  +        izstream() : m_fp(0) {}
           55  +        izstream(FILE* fp) : m_fp(0) { open(fp); }
           56  +        izstream(const char* name) : m_fp(0) { open(name); }
           57  +        ~izstream() { close(); }
           58  +
           59  +        /* Opens a gzip (.gz) file for reading.
           60  +         * open() can be used to read a file which is not in gzip format;
           61  +         * in this case read() will directly read from the file without
           62  +         * decompression. errno can be checked to distinguish two error
           63  +         * cases (if errno is zero, the zlib error is Z_MEM_ERROR).
           64  +         */
           65  +        void open(const char* name) {
           66  +            if (m_fp) close();
           67  +            m_fp = ::gzopen(name, "rb");
           68  +        }
           69  +
           70  +        void open(FILE* fp) {
           71  +            SET_BINARY_MODE(fp);
           72  +            if (m_fp) close();
           73  +            m_fp = ::gzdopen(fileno(fp), "rb");
           74  +        }
           75  +
           76  +        /* Flushes all pending input if necessary, closes the compressed file
           77  +         * and deallocates all the (de)compression state. The return value is
           78  +         * the zlib error number (see function error() below).
           79  +         */
           80  +        int close() {
           81  +            int r = ::gzclose(m_fp);
           82  +            m_fp = 0; return r;
           83  +        }
           84  +
           85  +        /* Binary read the given number of bytes from the compressed file.
           86  +         */
           87  +        int read(void* buf, size_t len) {
           88  +            return ::gzread(m_fp, buf, len);
           89  +        }
           90  +
           91  +        /* Returns the error message for the last error which occurred on the
           92  +         * given compressed file. errnum is set to zlib error number. If an
           93  +         * error occurred in the file system and not in the compression library,
           94  +         * errnum is set to Z_ERRNO and the application may consult errno
           95  +         * to get the exact error code.
           96  +         */
           97  +        const char* error(int* errnum) {
           98  +            return ::gzerror(m_fp, errnum);
           99  +        }
          100  +
          101  +        gzFile fp() { return m_fp; }
          102  +
          103  +    private:
          104  +        gzFile m_fp;
          105  +};
          106  +
          107  +/*
          108  + * Binary read the given (array of) object(s) from the compressed file.
          109  + * If the input file was not in gzip format, read() copies the objects number
          110  + * of bytes into the buffer.
          111  + * returns the number of uncompressed bytes actually read
          112  + * (0 for end of file, -1 for error).
          113  + */
          114  +template <class T, class Items>
          115  +inline int read(izstream& zs, T* x, Items items) {
          116  +    return ::gzread(zs.fp(), x, items*sizeof(T));
          117  +}
          118  +
          119  +/*
          120  + * Binary input with the '>' operator.
          121  + */
          122  +template <class T>
          123  +inline izstream& operator>(izstream& zs, T& x) {
          124  +    ::gzread(zs.fp(), &x, sizeof(T));
          125  +    return zs;
          126  +}
          127  +
          128  +
          129  +inline zstringlen::zstringlen(izstream& zs) {
          130  +    zs > val.byte;
          131  +    if (val.byte == 255) zs > val.word;
          132  +    else val.word = val.byte;
          133  +}
          134  +
          135  +/*
          136  + * Read length of string + the string with the '>' operator.
          137  + */
          138  +inline izstream& operator>(izstream& zs, char* x) {
          139  +    zstringlen len(zs);
          140  +    ::gzread(zs.fp(), x, len.value());
          141  +    x[len.value()] = '\0';
          142  +    return zs;
          143  +}
          144  +
          145  +inline char* read_string(izstream& zs) {
          146  +    zstringlen len(zs);
          147  +    char* x = new char[len.value()+1];
          148  +    ::gzread(zs.fp(), x, len.value());
          149  +    x[len.value()] = '\0';
          150  +    return x;
          151  +}
          152  +
          153  +// ----------------------------- ozstream -----------------------------
          154  +
          155  +class ozstream
          156  +{
          157  +    public:
          158  +        ozstream() : m_fp(0), m_os(0) {
          159  +        }
          160  +        ozstream(FILE* fp, int level = Z_DEFAULT_COMPRESSION)
          161  +            : m_fp(0), m_os(0) {
          162  +            open(fp, level);
          163  +        }
          164  +        ozstream(const char* name, int level = Z_DEFAULT_COMPRESSION)
          165  +            : m_fp(0), m_os(0) {
          166  +            open(name, level);
          167  +        }
          168  +        ~ozstream() {
          169  +            close();
          170  +        }
          171  +
          172  +        /* Opens a gzip (.gz) file for writing.
          173  +         * The compression level parameter should be in 0..9
          174  +         * errno can be checked to distinguish two error cases
          175  +         * (if errno is zero, the zlib error is Z_MEM_ERROR).
          176  +         */
          177  +        void open(const char* name, int level = Z_DEFAULT_COMPRESSION) {
          178  +            char mode[4] = "wb\0";
          179  +            if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level;
          180  +            if (m_fp) close();
          181  +            m_fp = ::gzopen(name, mode);
          182  +        }
          183  +
          184  +        /* open from a FILE pointer.
          185  +         */
          186  +        void open(FILE* fp, int level = Z_DEFAULT_COMPRESSION) {
          187  +            SET_BINARY_MODE(fp);
          188  +            char mode[4] = "wb\0";
          189  +            if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level;
          190  +            if (m_fp) close();
          191  +            m_fp = ::gzdopen(fileno(fp), mode);
          192  +        }
          193  +
          194  +        /* Flushes all pending output if necessary, closes the compressed file
          195  +         * and deallocates all the (de)compression state. The return value is
          196  +         * the zlib error number (see function error() below).
          197  +         */
          198  +        int close() {
          199  +            if (m_os) {
          200  +                ::gzwrite(m_fp, m_os->str(), m_os->pcount());
          201  +                delete[] m_os->str(); delete m_os; m_os = 0;
          202  +            }
          203  +            int r = ::gzclose(m_fp); m_fp = 0; return r;
          204  +        }
          205  +
          206  +        /* Binary write the given number of bytes into the compressed file.
          207  +         */
          208  +        int write(const void* buf, size_t len) {
          209  +            return ::gzwrite(m_fp, (voidp) buf, len);
          210  +        }
          211  +
          212  +        /* Flushes all pending output into the compressed file. The parameter
          213  +         * _flush is as in the deflate() function. The return value is the zlib
          214  +         * error number (see function gzerror below). flush() returns Z_OK if
          215  +         * the flush_ parameter is Z_FINISH and all output could be flushed.
          216  +         * flush() should be called only when strictly necessary because it can
          217  +         * degrade compression.
          218  +         */
          219  +        int flush(int _flush) {
          220  +            os_flush();
          221  +            return ::gzflush(m_fp, _flush);
          222  +        }
          223  +
          224  +        /* Returns the error message for the last error which occurred on the
          225  +         * given compressed file. errnum is set to zlib error number. If an
          226  +         * error occurred in the file system and not in the compression library,
          227  +         * errnum is set to Z_ERRNO and the application may consult errno
          228  +         * to get the exact error code.
          229  +         */
          230  +        const char* error(int* errnum) {
          231  +            return ::gzerror(m_fp, errnum);
          232  +        }
          233  +
          234  +        gzFile fp() { return m_fp; }
          235  +
          236  +        ostream& os() {
          237  +            if (m_os == 0) m_os = new ostrstream;
          238  +            return *m_os;
          239  +        }
          240  +
          241  +        void os_flush() {
          242  +            if (m_os && m_os->pcount()>0) {
          243  +                ostrstream* oss = new ostrstream;
          244  +                oss->fill(m_os->fill());
          245  +                oss->flags(m_os->flags());
          246  +                oss->precision(m_os->precision());
          247  +                oss->width(m_os->width());
          248  +                ::gzwrite(m_fp, m_os->str(), m_os->pcount());
          249  +                delete[] m_os->str(); delete m_os; m_os = oss;
          250  +            }
          251  +        }
          252  +
          253  +    private:
          254  +        gzFile m_fp;
          255  +        ostrstream* m_os;
          256  +};
          257  +
          258  +/*
          259  + * Binary write the given (array of) object(s) into the compressed file.
          260  + * returns the number of uncompressed bytes actually written
          261  + * (0 in case of error).
          262  + */
          263  +template <class T, class Items>
          264  +inline int write(ozstream& zs, const T* x, Items items) {
          265  +    return ::gzwrite(zs.fp(), (voidp) x, items*sizeof(T));
          266  +}
          267  +
          268  +/*
          269  + * Binary output with the '<' operator.
          270  + */
          271  +template <class T>
          272  +inline ozstream& operator<(ozstream& zs, const T& x) {
          273  +    ::gzwrite(zs.fp(), (voidp) &x, sizeof(T));
          274  +    return zs;
          275  +}
          276  +
          277  +inline zstringlen::zstringlen(ozstream& zs, const char* x) {
          278  +    val.byte = 255;  val.word = ::strlen(x);
          279  +    if (val.word < 255) zs < (val.byte = val.word);
          280  +    else zs < val;
          281  +}
          282  +
          283  +/*
          284  + * Write length of string + the string with the '<' operator.
          285  + */
          286  +inline ozstream& operator<(ozstream& zs, const char* x) {
          287  +    zstringlen len(zs, x);
          288  +    ::gzwrite(zs.fp(), (voidp) x, len.value());
          289  +    return zs;
          290  +}
          291  +
          292  +#ifdef _MSC_VER
          293  +inline ozstream& operator<(ozstream& zs, char* const& x) {
          294  +    return zs < (const char*) x;
          295  +}
          296  +#endif
          297  +
          298  +/*
          299  + * Ascii write with the << operator;
          300  + */
          301  +template <class T>
          302  +inline ostream& operator<<(ozstream& zs, const T& x) {
          303  +    zs.os_flush();
          304  +    return zs.os() << x;
          305  +}
          306  +
          307  +#endif

Added compat/zlib/contrib/iostream2/zstream_test.cpp.

            1  +#include "zstream.h"
            2  +#include <math.h>
            3  +#include <stdlib.h>
            4  +#include <iomanip.h>
            5  +
            6  +void main() {
            7  +    char h[256] = "Hello";
            8  +    char* g = "Goodbye";
            9  +    ozstream out("temp.gz");
           10  +    out < "This works well" < h < g;
           11  +    out.close();
           12  +
           13  +    izstream in("temp.gz"); // read it back
           14  +    char *x = read_string(in), *y = new char[256], z[256];
           15  +    in > y > z;
           16  +    in.close();
           17  +    cout << x << endl << y << endl << z << endl;
           18  +
           19  +    out.open("temp.gz"); // try ascii output; zcat temp.gz to see the results
           20  +    out << setw(50) << setfill('#') << setprecision(20) << x << endl << y << endl << z << endl;
           21  +    out << z << endl << y << endl << x << endl;
           22  +    out << 1.1234567890123456789 << endl;
           23  +
           24  +    delete[] x; delete[] y;
           25  +}

Added compat/zlib/contrib/iostream3/README.

            1  +These classes provide a C++ stream interface to the zlib library. It allows you
            2  +to do things like:
            3  +
            4  +  gzofstream outf("blah.gz");
            5  +  outf << "These go into the gzip file " << 123 << endl;
            6  +
            7  +It does this by deriving a specialized stream buffer for gzipped files, which is
            8  +the way Stroustrup would have done it. :->
            9  +
           10  +The gzifstream and gzofstream classes were originally written by Kevin Ruland
           11  +and made available in the zlib contrib/iostream directory. The older version still
           12  +compiles under gcc 2.xx, but not under gcc 3.xx, which sparked the development of
           13  +this version.
           14  +
           15  +The new classes are as standard-compliant as possible, closely following the
           16  +approach of the standard library's fstream classes. It compiles under gcc versions
           17  +3.2 and 3.3, but not under gcc 2.xx. This is mainly due to changes in the standard
           18  +library naming scheme. The new version of gzifstream/gzofstream/gzfilebuf differs
           19  +from the previous one in the following respects:
           20  +- added showmanyc
           21  +- added setbuf, with support for unbuffered output via setbuf(0,0)
           22  +- a few bug fixes of stream behavior
           23  +- gzipped output file opened with default compression level instead of maximum level
           24  +- setcompressionlevel()/strategy() members replaced by single setcompression()
           25  +
           26  +The code is provided "as is", with the permission to use, copy, modify, distribute
           27  +and sell it for any purpose without fee.
           28  +
           29  +Ludwig Schwardt
           30  +<schwardt@sun.ac.za>
           31  +
           32  +DSP Lab
           33  +Electrical & Electronic Engineering Department
           34  +University of Stellenbosch
           35  +South Africa

Added compat/zlib/contrib/iostream3/TODO.

            1  +Possible upgrades to gzfilebuf:
            2  +
            3  +- The ability to do putback (e.g. putbackfail)
            4  +
            5  +- The ability to seek (zlib supports this, but could be slow/tricky)
            6  +
            7  +- Simultaneous read/write access (does it make sense?)
            8  +
            9  +- Support for ios_base::ate open mode
           10  +
           11  +- Locale support?
           12  +
           13  +- Check public interface to see which calls give problems
           14  +  (due to dependence on library internals)
           15  +
           16  +- Override operator<<(ostream&, gzfilebuf*) to allow direct copying
           17  +  of stream buffer to stream ( i.e. os << is.rdbuf(); )

Added compat/zlib/contrib/iostream3/test.cc.

            1  +/*
            2  + * Test program for gzifstream and gzofstream
            3  + *
            4  + * by Ludwig Schwardt <schwardt@sun.ac.za>
            5  + * original version by Kevin Ruland <kevin@rodin.wustl.edu>
            6  + */
            7  +
            8  +#include "zfstream.h"
            9  +#include <iostream>      // for cout
           10  +
           11  +int main() {
           12  +
           13  +  gzofstream outf;
           14  +  gzifstream inf;
           15  +  char buf[80];
           16  +
           17  +  outf.open("test1.txt.gz");
           18  +  outf << "The quick brown fox sidestepped the lazy canine\n"
           19  +       << 1.3 << "\nPlan " << 9 << std::endl;
           20  +  outf.close();
           21  +  std::cout << "Wrote the following message to 'test1.txt.gz' (check with zcat or zless):\n"
           22  +            << "The quick brown fox sidestepped the lazy canine\n"
           23  +            << 1.3 << "\nPlan " << 9 << std::endl;
           24  +
           25  +  std::cout << "\nReading 'test1.txt.gz' (buffered) produces:\n";
           26  +  inf.open("test1.txt.gz");
           27  +  while (inf.getline(buf,80,'\n')) {
           28  +    std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n";
           29  +  }
           30  +  inf.close();
           31  +
           32  +  outf.rdbuf()->pubsetbuf(0,0);
           33  +  outf.open("test2.txt.gz");
           34  +  outf << setcompression(Z_NO_COMPRESSION)
           35  +       << "The quick brown fox sidestepped the lazy canine\n"
           36  +       << 1.3 << "\nPlan " << 9 << std::endl;
           37  +  outf.close();
           38  +  std::cout << "\nWrote the same message to 'test2.txt.gz' in uncompressed form";
           39  +
           40  +  std::cout << "\nReading 'test2.txt.gz' (unbuffered) produces:\n";
           41  +  inf.rdbuf()->pubsetbuf(0,0);
           42  +  inf.open("test2.txt.gz");
           43  +  while (inf.getline(buf,80,'\n')) {
           44  +    std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n";
           45  +  }
           46  +  inf.close();
           47  +
           48  +  return 0;
           49  +
           50  +}

Added compat/zlib/contrib/iostream3/zfstream.cc.

            1  +/*
            2  + * A C++ I/O streams interface to the zlib gz* functions
            3  + *
            4  + * by Ludwig Schwardt <schwardt@sun.ac.za>
            5  + * original version by Kevin Ruland <kevin@rodin.wustl.edu>
            6  + *
            7  + * This version is standard-compliant and compatible with gcc 3.x.
            8  + */
            9  +
           10  +#include "zfstream.h"
           11  +#include <cstring>          // for strcpy, strcat, strlen (mode strings)
           12  +#include <cstdio>           // for BUFSIZ
           13  +
           14  +// Internal buffer sizes (default and "unbuffered" versions)
           15  +#define BIGBUFSIZE BUFSIZ
           16  +#define SMALLBUFSIZE 1
           17  +
           18  +/*****************************************************************************/
           19  +
           20  +// Default constructor
           21  +gzfilebuf::gzfilebuf()
           22  +: file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false),
           23  +  buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true)
           24  +{
           25  +  // No buffers to start with
           26  +  this->disable_buffer();
           27  +}
           28  +
           29  +// Destructor
           30  +gzfilebuf::~gzfilebuf()
           31  +{
           32  +  // Sync output buffer and close only if responsible for file
           33  +  // (i.e. attached streams should be left open at this stage)
           34  +  this->sync();
           35  +  if (own_fd)
           36  +    this->close();
           37  +  // Make sure internal buffer is deallocated
           38  +  this->disable_buffer();
           39  +}
           40  +
           41  +// Set compression level and strategy
           42  +int
           43  +gzfilebuf::setcompression(int comp_level,
           44  +                          int comp_strategy)
           45  +{
           46  +  return gzsetparams(file, comp_level, comp_strategy);
           47  +}
           48  +
           49  +// Open gzipped file
           50  +gzfilebuf*
           51  +gzfilebuf::open(const char *name,
           52  +                std::ios_base::openmode mode)
           53  +{
           54  +  // Fail if file already open
           55  +  if (this->is_open())
           56  +    return NULL;
           57  +  // Don't support simultaneous read/write access (yet)
           58  +  if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
           59  +    return NULL;
           60  +
           61  +  // Build mode string for gzopen and check it [27.8.1.3.2]
           62  +  char char_mode[6] = "\0\0\0\0\0";
           63  +  if (!this->open_mode(mode, char_mode))
           64  +    return NULL;
           65  +
           66  +  // Attempt to open file
           67  +  if ((file = gzopen(name, char_mode)) == NULL)
           68  +    return NULL;
           69  +
           70  +  // On success, allocate internal buffer and set flags
           71  +  this->enable_buffer();
           72  +  io_mode = mode;
           73  +  own_fd = true;
           74  +  return this;
           75  +}
           76  +
           77  +// Attach to gzipped file
           78  +gzfilebuf*
           79  +gzfilebuf::attach(int fd,
           80  +                  std::ios_base::openmode mode)
           81  +{
           82  +  // Fail if file already open
           83  +  if (this->is_open())
           84  +    return NULL;
           85  +  // Don't support simultaneous read/write access (yet)
           86  +  if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
           87  +    return NULL;
           88  +
           89  +  // Build mode string for gzdopen and check it [27.8.1.3.2]
           90  +  char char_mode[6] = "\0\0\0\0\0";
           91  +  if (!this->open_mode(mode, char_mode))
           92  +    return NULL;
           93  +
           94  +  // Attempt to attach to file
           95  +  if ((file = gzdopen(fd, char_mode)) == NULL)
           96  +    return NULL;
           97  +
           98  +  // On success, allocate internal buffer and set flags
           99  +  this->enable_buffer();
          100  +  io_mode = mode;
          101  +  own_fd = false;
          102  +  return this;
          103  +}
          104  +
          105  +// Close gzipped file
          106  +gzfilebuf*
          107  +gzfilebuf::close()
          108  +{
          109  +  // Fail immediately if no file is open
          110  +  if (!this->is_open())
          111  +    return NULL;
          112  +  // Assume success
          113  +  gzfilebuf* retval = this;
          114  +  // Attempt to sync and close gzipped file
          115  +  if (this->sync() == -1)
          116  +    retval = NULL;
          117  +  if (gzclose(file) < 0)
          118  +    retval = NULL;
          119  +  // File is now gone anyway (postcondition [27.8.1.3.8])
          120  +  file = NULL;
          121  +  own_fd = false;
          122  +  // Destroy internal buffer if it exists
          123  +  this->disable_buffer();
          124  +  return retval;
          125  +}
          126  +
          127  +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
          128  +
          129  +// Convert int open mode to mode string
          130  +bool
          131  +gzfilebuf::open_mode(std::ios_base::openmode mode,
          132  +                     char* c_mode) const
          133  +{
          134  +  bool testb = mode & std::ios_base::binary;
          135  +  bool testi = mode & std::ios_base::in;
          136  +  bool testo = mode & std::ios_base::out;
          137  +  bool testt = mode & std::ios_base::trunc;
          138  +  bool testa = mode & std::ios_base::app;
          139  +
          140  +  // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
          141  +  // Original zfstream hardcoded the compression level to maximum here...
          142  +  // Double the time for less than 1% size improvement seems
          143  +  // excessive though - keeping it at the default level
          144  +  // To change back, just append "9" to the next three mode strings
          145  +  if (!testi && testo && !testt && !testa)
          146  +    strcpy(c_mode, "w");
          147  +  if (!testi && testo && !testt && testa)
          148  +    strcpy(c_mode, "a");
          149  +  if (!testi && testo && testt && !testa)
          150  +    strcpy(c_mode, "w");
          151  +  if (testi && !testo && !testt && !testa)
          152  +    strcpy(c_mode, "r");
          153  +  // No read/write mode yet
          154  +//  if (testi && testo && !testt && !testa)
          155  +//    strcpy(c_mode, "r+");
          156  +//  if (testi && testo && testt && !testa)
          157  +//    strcpy(c_mode, "w+");
          158  +
          159  +  // Mode string should be empty for invalid combination of flags
          160  +  if (strlen(c_mode) == 0)
          161  +    return false;
          162  +  if (testb)
          163  +    strcat(c_mode, "b");
          164  +  return true;
          165  +}
          166  +
          167  +// Determine number of characters in internal get buffer
          168  +std::streamsize
          169  +gzfilebuf::showmanyc()
          170  +{
          171  +  // Calls to underflow will fail if file not opened for reading
          172  +  if (!this->is_open() || !(io_mode & std::ios_base::in))
          173  +    return -1;
          174  +  // Make sure get area is in use
          175  +  if (this->gptr() && (this->gptr() < this->egptr()))
          176  +    return std::streamsize(this->egptr() - this->gptr());
          177  +  else
          178  +    return 0;
          179  +}
          180  +
          181  +// Fill get area from gzipped file
          182  +gzfilebuf::int_type
          183  +gzfilebuf::underflow()
          184  +{
          185  +  // If something is left in the get area by chance, return it
          186  +  // (this shouldn't normally happen, as underflow is only supposed
          187  +  // to be called when gptr >= egptr, but it serves as error check)
          188  +  if (this->gptr() && (this->gptr() < this->egptr()))
          189  +    return traits_type::to_int_type(*(this->gptr()));
          190  +
          191  +  // If the file hasn't been opened for reading, produce error
          192  +  if (!this->is_open() || !(io_mode & std::ios_base::in))
          193  +    return traits_type::eof();
          194  +
          195  +  // Attempt to fill internal buffer from gzipped file
          196  +  // (buffer must be guaranteed to exist...)
          197  +  int bytes_read = gzread(file, buffer, buffer_size);
          198  +  // Indicates error or EOF
          199  +  if (bytes_read <= 0)
          200  +  {
          201  +    // Reset get area
          202  +    this->setg(buffer, buffer, buffer);
          203  +    return traits_type::eof();
          204  +  }
          205  +  // Make all bytes read from file available as get area
          206  +  this->setg(buffer, buffer, buffer + bytes_read);
          207  +
          208  +  // Return next character in get area
          209  +  return traits_type::to_int_type(*(this->gptr()));
          210  +}
          211  +
          212  +// Write put area to gzipped file
          213  +gzfilebuf::int_type
          214  +gzfilebuf::overflow(int_type c)
          215  +{
          216  +  // Determine whether put area is in use
          217  +  if (this->pbase())
          218  +  {
          219  +    // Double-check pointer range
          220  +    if (this->pptr() > this->epptr() || this->pptr() < this->pbase())
          221  +      return traits_type::eof();
          222  +    // Add extra character to buffer if not EOF
          223  +    if (!traits_type::eq_int_type(c, traits_type::eof()))
          224  +    {
          225  +      *(this->pptr()) = traits_type::to_char_type(c);
          226  +      this->pbump(1);
          227  +    }
          228  +    // Number of characters to write to file
          229  +    int bytes_to_write = this->pptr() - this->pbase();
          230  +    // Overflow doesn't fail if nothing is to be written
          231  +    if (bytes_to_write > 0)
          232  +    {
          233  +      // If the file hasn't been opened for writing, produce error
          234  +      if (!this->is_open() || !(io_mode & std::ios_base::out))
          235  +        return traits_type::eof();
          236  +      // If gzipped file won't accept all bytes written to it, fail
          237  +      if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write)
          238  +        return traits_type::eof();
          239  +      // Reset next pointer to point to pbase on success
          240  +      this->pbump(-bytes_to_write);
          241  +    }
          242  +  }
          243  +  // Write extra character to file if not EOF
          244  +  else if (!traits_type::eq_int_type(c, traits_type::eof()))
          245  +  {
          246  +    // If the file hasn't been opened for writing, produce error
          247  +    if (!this->is_open() || !(io_mode & std::ios_base::out))
          248  +      return traits_type::eof();
          249  +    // Impromptu char buffer (allows "unbuffered" output)
          250  +    char_type last_char = traits_type::to_char_type(c);
          251  +    // If gzipped file won't accept this character, fail
          252  +    if (gzwrite(file, &last_char, 1) != 1)
          253  +      return traits_type::eof();
          254  +  }
          255  +
          256  +  // If you got here, you have succeeded (even if c was EOF)
          257  +  // The return value should therefore be non-EOF
          258  +  if (traits_type::eq_int_type(c, traits_type::eof()))
          259  +    return traits_type::not_eof(c);
          260  +  else
          261  +    return c;
          262  +}
          263  +
          264  +// Assign new buffer
          265  +std::streambuf*
          266  +gzfilebuf::setbuf(char_type* p,
          267  +                  std::streamsize n)
          268  +{
          269  +  // First make sure stuff is sync'ed, for safety
          270  +  if (this->sync() == -1)
          271  +    return NULL;
          272  +  // If buffering is turned off on purpose via setbuf(0,0), still allocate one...
          273  +  // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
          274  +  // least a buffer of size 1 (very inefficient though, therefore make it bigger?)
          275  +  // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems)
          276  +  if (!p || !n)
          277  +  {
          278  +    // Replace existing buffer (if any) with small internal buffer
          279  +    this->disable_buffer();
          280  +    buffer = NULL;
          281  +    buffer_size = 0;
          282  +    own_buffer = true;
          283  +    this->enable_buffer();
          284  +  }
          285  +  else
          286  +  {
          287  +    // Replace existing buffer (if any) with external buffer
          288  +    this->disable_buffer();
          289  +    buffer = p;
          290  +    buffer_size = n;
          291  +    own_buffer = false;
          292  +    this->enable_buffer();
          293  +  }
          294  +  return this;
          295  +}
          296  +
          297  +// Write put area to gzipped file (i.e. ensures that put area is empty)
          298  +int
          299  +gzfilebuf::sync()
          300  +{
          301  +  return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
          302  +}
          303  +
          304  +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
          305  +
          306  +// Allocate internal buffer
          307  +void
          308  +gzfilebuf::enable_buffer()
          309  +{
          310  +  // If internal buffer required, allocate one
          311  +  if (own_buffer && !buffer)
          312  +  {
          313  +    // Check for buffered vs. "unbuffered"
          314  +    if (buffer_size > 0)
          315  +    {
          316  +      // Allocate internal buffer
          317  +      buffer = new char_type[buffer_size];
          318  +      // Get area starts empty and will be expanded by underflow as need arises
          319  +      this->setg(buffer, buffer, buffer);
          320  +      // Setup entire internal buffer as put area.
          321  +      // The one-past-end pointer actually points to the last element of the buffer,
          322  +      // so that overflow(c) can safely add the extra character c to the sequence.
          323  +      // These pointers remain in place for the duration of the buffer
          324  +      this->setp(buffer, buffer + buffer_size - 1);
          325  +    }
          326  +    else
          327  +    {
          328  +      // Even in "unbuffered" case, (small?) get buffer is still required
          329  +      buffer_size = SMALLBUFSIZE;
          330  +      buffer = new char_type[buffer_size];
          331  +      this->setg(buffer, buffer, buffer);
          332  +      // "Unbuffered" means no put buffer
          333  +      this->setp(0, 0);
          334  +    }
          335  +  }
          336  +  else
          337  +  {
          338  +    // If buffer already allocated, reset buffer pointers just to make sure no
          339  +    // stale chars are lying around
          340  +    this->setg(buffer, buffer, buffer);
          341  +    this->setp(buffer, buffer + buffer_size - 1);
          342  +  }
          343  +}
          344  +
          345  +// Destroy internal buffer
          346  +void
          347  +gzfilebuf::disable_buffer()
          348  +{
          349  +  // If internal buffer exists, deallocate it
          350  +  if (own_buffer && buffer)
          351  +  {
          352  +    // Preserve unbuffered status by zeroing size
          353  +    if (!this->pbase())
          354  +      buffer_size = 0;
          355  +    delete[] buffer;
          356  +    buffer = NULL;
          357  +    this->setg(0, 0, 0);
          358  +    this->setp(0, 0);
          359  +  }
          360  +  else
          361  +  {
          362  +    // Reset buffer pointers to initial state if external buffer exists
          363  +    this->setg(buffer, buffer, buffer);
          364  +    if (buffer)
          365  +      this->setp(buffer, buffer + buffer_size - 1);
          366  +    else
          367  +      this->setp(0, 0);
          368  +  }
          369  +}
          370  +
          371  +/*****************************************************************************/
          372  +
          373  +// Default constructor initializes stream buffer
          374  +gzifstream::gzifstream()
          375  +: std::istream(NULL), sb()
          376  +{ this->init(&sb); }
          377  +
          378  +// Initialize stream buffer and open file
          379  +gzifstream::gzifstream(const char* name,
          380  +                       std::ios_base::openmode mode)
          381  +: std::istream(NULL), sb()
          382  +{
          383  +  this->init(&sb);
          384  +  this->open(name, mode);
          385  +}
          386  +
          387  +// Initialize stream buffer and attach to file
          388  +gzifstream::gzifstream(int fd,
          389  +                       std::ios_base::openmode mode)
          390  +: std::istream(NULL), sb()
          391  +{
          392  +  this->init(&sb);
          393  +  this->attach(fd, mode);
          394  +}
          395  +
          396  +// Open file and go into fail() state if unsuccessful
          397  +void
          398  +gzifstream::open(const char* name,
          399  +                 std::ios_base::openmode mode)
          400  +{
          401  +  if (!sb.open(name, mode | std::ios_base::in))
          402  +    this->setstate(std::ios_base::failbit);
          403  +  else
          404  +    this->clear();
          405  +}
          406  +
          407  +// Attach to file and go into fail() state if unsuccessful
          408  +void
          409  +gzifstream::attach(int fd,
          410  +                   std::ios_base::openmode mode)
          411  +{
          412  +  if (!sb.attach(fd, mode | std::ios_base::in))
          413  +    this->setstate(std::ios_base::failbit);
          414  +  else
          415  +    this->clear();
          416  +}
          417  +
          418  +// Close file
          419  +void
          420  +gzifstream::close()
          421  +{
          422  +  if (!sb.close())
          423  +    this->setstate(std::ios_base::failbit);
          424  +}
          425  +
          426  +/*****************************************************************************/
          427  +
          428  +// Default constructor initializes stream buffer
          429  +gzofstream::gzofstream()
          430  +: std::ostream(NULL), sb()
          431  +{ this->init(&sb); }
          432  +
          433  +// Initialize stream buffer and open file
          434  +gzofstream::gzofstream(const char* name,
          435  +                       std::ios_base::openmode mode)
          436  +: std::ostream(NULL), sb()
          437  +{
          438  +  this->init(&sb);
          439  +  this->open(name, mode);
          440  +}
          441  +
          442  +// Initialize stream buffer and attach to file
          443  +gzofstream::gzofstream(int fd,
          444  +                       std::ios_base::openmode mode)
          445  +: std::ostream(NULL), sb()
          446  +{
          447  +  this->init(&sb);
          448  +  this->attach(fd, mode);
          449  +}
          450  +
          451  +// Open file and go into fail() state if unsuccessful
          452  +void
          453  +gzofstream::open(const char* name,
          454  +                 std::ios_base::openmode mode)
          455  +{
          456  +  if (!sb.open(name, mode | std::ios_base::out))
          457  +    this->setstate(std::ios_base::failbit);
          458  +  else
          459  +    this->clear();
          460  +}
          461  +
          462  +// Attach to file and go into fail() state if unsuccessful
          463  +void
          464  +gzofstream::attach(int fd,
          465  +                   std::ios_base::openmode mode)
          466  +{
          467  +  if (!sb.attach(fd, mode | std::ios_base::out))
          468  +    this->setstate(std::ios_base::failbit);
          469  +  else
          470  +    this->clear();
          471  +}
          472  +
          473  +// Close file
          474  +void
          475  +gzofstream::close()
          476  +{
          477  +  if (!sb.close())
          478  +    this->setstate(std::ios_base::failbit);
          479  +}

Added compat/zlib/contrib/iostream3/zfstream.h.

            1  +/*
            2  + * A C++ I/O streams interface to the zlib gz* functions
            3  + *
            4  + * by Ludwig Schwardt <schwardt@sun.ac.za>
            5  + * original version by Kevin Ruland <kevin@rodin.wustl.edu>
            6  + *
            7  + * This version is standard-compliant and compatible with gcc 3.x.
            8  + */
            9  +
           10  +#ifndef ZFSTREAM_H
           11  +#define ZFSTREAM_H
           12  +
           13  +#include <istream>  // not iostream, since we don't need cin/cout
           14  +#include <ostream>
           15  +#include "zlib.h"
           16  +
           17  +/*****************************************************************************/
           18  +
           19  +/**
           20  + *  @brief  Gzipped file stream buffer class.
           21  + *
           22  + *  This class implements basic_filebuf for gzipped files. It doesn't yet support
           23  + *  seeking (allowed by zlib but slow/limited), putback and read/write access
           24  + *  (tricky). Otherwise, it attempts to be a drop-in replacement for the standard
           25  + *  file streambuf.
           26  +*/
           27  +class gzfilebuf : public std::streambuf
           28  +{
           29  +public:
           30  +  //  Default constructor.
           31  +  gzfilebuf();
           32  +
           33  +  //  Destructor.
           34  +  virtual
           35  +  ~gzfilebuf();
           36  +
           37  +  /**
           38  +   *  @brief  Set compression level and strategy on the fly.
           39  +   *  @param  comp_level  Compression level (see zlib.h for allowed values)
           40  +   *  @param  comp_strategy  Compression strategy (see zlib.h for allowed values)
           41  +   *  @return  Z_OK on success, Z_STREAM_ERROR otherwise.
           42  +   *
           43  +   *  Unfortunately, these parameters cannot be modified separately, as the
           44  +   *  previous zfstream version assumed. Since the strategy is seldom changed,
           45  +   *  it can default and setcompression(level) then becomes like the old
           46  +   *  setcompressionlevel(level).
           47  +  */
           48  +  int
           49  +  setcompression(int comp_level,
           50  +                 int comp_strategy = Z_DEFAULT_STRATEGY);
           51  +
           52  +  /**
           53  +   *  @brief  Check if file is open.
           54  +   *  @return  True if file is open.
           55  +  */
           56  +  bool
           57  +  is_open() const { return (file != NULL); }
           58  +
           59  +  /**
           60  +   *  @brief  Open gzipped file.
           61  +   *  @param  name  File name.
           62  +   *  @param  mode  Open mode flags.
           63  +   *  @return  @c this on success, NULL on failure.
           64  +  */
           65  +  gzfilebuf*
           66  +  open(const char* name,
           67  +       std::ios_base::openmode mode);
           68  +
           69  +  /**
           70  +   *  @brief  Attach to already open gzipped file.
           71  +   *  @param  fd  File descriptor.
           72  +   *  @param  mode  Open mode flags.
           73  +   *  @return  @c this on success, NULL on failure.
           74  +  */
           75  +  gzfilebuf*
           76  +  attach(int fd,
           77  +         std::ios_base::openmode mode);
           78  +
           79  +  /**
           80  +   *  @brief  Close gzipped file.
           81  +   *  @return  @c this on success, NULL on failure.
           82  +  */
           83  +  gzfilebuf*
           84  +  close();
           85  +
           86  +protected:
           87  +  /**
           88  +   *  @brief  Convert ios open mode int to mode string used by zlib.
           89  +   *  @return  True if valid mode flag combination.
           90  +  */
           91  +  bool
           92  +  open_mode(std::ios_base::openmode mode,
           93  +            char* c_mode) const;
           94  +
           95  +  /**
           96  +   *  @brief  Number of characters available in stream buffer.
           97  +   *  @return  Number of characters.
           98  +   *
           99  +   *  This indicates number of characters in get area of stream buffer.
          100  +   *  These characters can be read without accessing the gzipped file.
          101  +  */
          102  +  virtual std::streamsize
          103  +  showmanyc();
          104  +
          105  +  /**
          106  +   *  @brief  Fill get area from gzipped file.
          107  +   *  @return  First character in get area on success, EOF on error.
          108  +   *
          109  +   *  This actually reads characters from gzipped file to stream
          110  +   *  buffer. Always buffered.
          111  +  */
          112  +  virtual int_type
          113  +  underflow();
          114  +
          115  +  /**
          116  +   *  @brief  Write put area to gzipped file.
          117  +   *  @param  c  Extra character to add to buffer contents.
          118  +   *  @return  Non-EOF on success, EOF on error.
          119  +   *
          120  +   *  This actually writes characters in stream buffer to
          121  +   *  gzipped file. With unbuffered output this is done one
          122  +   *  character at a time.
          123  +  */
          124  +  virtual int_type
          125  +  overflow(int_type c = traits_type::eof());
          126  +
          127  +  /**
          128  +   *  @brief  Installs external stream buffer.
          129  +   *  @param  p  Pointer to char buffer.
          130  +   *  @param  n  Size of external buffer.
          131  +   *  @return  @c this on success, NULL on failure.
          132  +   *
          133  +   *  Call setbuf(0,0) to enable unbuffered output.
          134  +  */
          135  +  virtual std::streambuf*
          136  +  setbuf(char_type* p,
          137  +         std::streamsize n);
          138  +
          139  +  /**
          140  +   *  @brief  Flush stream buffer to file.
          141  +   *  @return  0 on success, -1 on error.
          142  +   *
          143  +   *  This calls underflow(EOF) to do the job.
          144  +  */
          145  +  virtual int
          146  +  sync();
          147  +
          148  +//
          149  +// Some future enhancements
          150  +//
          151  +//  virtual int_type uflow();
          152  +//  virtual int_type pbackfail(int_type c = traits_type::eof());
          153  +//  virtual pos_type
          154  +//  seekoff(off_type off,
          155  +//          std::ios_base::seekdir way,
          156  +//          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
          157  +//  virtual pos_type
          158  +//  seekpos(pos_type sp,
          159  +//          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
          160  +
          161  +private:
          162  +  /**
          163  +   *  @brief  Allocate internal buffer.
          164  +   *
          165  +   *  This function is safe to call multiple times. It will ensure
          166  +   *  that a proper internal buffer exists if it is required. If the
          167  +   *  buffer already exists or is external, the buffer pointers will be
          168  +   *  reset to their original state.
          169  +  */
          170  +  void
          171  +  enable_buffer();
          172  +
          173  +  /**
          174  +   *  @brief  Destroy internal buffer.
          175  +   *
          176  +   *  This function is safe to call multiple times. It will ensure
          177  +   *  that the internal buffer is deallocated if it exists. In any
          178  +   *  case, it will also reset the buffer pointers.
          179  +  */
          180  +  void
          181  +  disable_buffer();
          182  +
          183  +  /**
          184  +   *  Underlying file pointer.
          185  +  */
          186  +  gzFile file;
          187  +
          188  +  /**
          189  +   *  Mode in which file was opened.
          190  +  */
          191  +  std::ios_base::openmode io_mode;
          192  +
          193  +  /**
          194  +   *  @brief  True if this object owns file descriptor.
          195  +   *
          196  +   *  This makes the class responsible for closing the file
          197  +   *  upon destruction.
          198  +  */
          199  +  bool own_fd;
          200  +
          201  +  /**
          202  +   *  @brief  Stream buffer.
          203  +   *
          204  +   *  For simplicity this remains allocated on the free store for the
          205  +   *  entire life span of the gzfilebuf object, unless replaced by setbuf.
          206  +  */
          207  +  char_type* buffer;
          208  +
          209  +  /**
          210  +   *  @brief  Stream buffer size.
          211  +   *
          212  +   *  Defaults to system default buffer size (typically 8192 bytes).
          213  +   *  Modified by setbuf.
          214  +  */
          215  +  std::streamsize buffer_size;
          216  +
          217  +  /**
          218  +   *  @brief  True if this object owns stream buffer.
          219  +   *
          220  +   *  This makes the class responsible for deleting the buffer
          221  +   *  upon destruction.
          222  +  */
          223  +  bool own_buffer;
          224  +};
          225  +
          226  +/*****************************************************************************/
          227  +
          228  +/**
          229  + *  @brief  Gzipped file input stream class.
          230  + *
          231  + *  This class implements ifstream for gzipped files. Seeking and putback
          232  + *  is not supported yet.
          233  +*/
          234  +class gzifstream : public std::istream
          235  +{
          236  +public:
          237  +  //  Default constructor
          238  +  gzifstream();
          239  +
          240  +  /**
          241  +   *  @brief  Construct stream on gzipped file to be opened.
          242  +   *  @param  name  File name.
          243  +   *  @param  mode  Open mode flags (forced to contain ios::in).
          244  +  */
          245  +  explicit
          246  +  gzifstream(const char* name,
          247  +             std::ios_base::openmode mode = std::ios_base::in);
          248  +
          249  +  /**
          250  +   *  @brief  Construct stream on already open gzipped file.
          251  +   *  @param  fd    File descriptor.
          252  +   *  @param  mode  Open mode flags (forced to contain ios::in).
          253  +  */
          254  +  explicit
          255  +  gzifstream(int fd,
          256  +             std::ios_base::openmode mode = std::ios_base::in);
          257  +
          258  +  /**
          259  +   *  Obtain underlying stream buffer.
          260  +  */
          261  +  gzfilebuf*
          262  +  rdbuf() const
          263  +  { return const_cast<gzfilebuf*>(&sb); }
          264  +
          265  +  /**
          266  +   *  @brief  Check if file is open.
          267  +   *  @return  True if file is open.
          268  +  */
          269  +  bool
          270  +  is_open() { return sb.is_open(); }
          271  +
          272  +  /**
          273  +   *  @brief  Open gzipped file.
          274  +   *  @param  name  File name.
          275  +   *  @param  mode  Open mode flags (forced to contain ios::in).
          276  +   *
          277  +   *  Stream will be in state good() if file opens successfully;
          278  +   *  otherwise in state fail(). This differs from the behavior of
          279  +   *  ifstream, which never sets the state to good() and therefore
          280  +   *  won't allow you to reuse the stream for a second file unless
          281  +   *  you manually clear() the state. The choice is a matter of
          282  +   *  convenience.
          283  +  */
          284  +  void
          285  +  open(const char* name,
          286  +       std::ios_base::openmode mode = std::ios_base::in);
          287  +
          288  +  /**
          289  +   *  @brief  Attach to already open gzipped file.
          290  +   *  @param  fd  File descriptor.
          291  +   *  @param  mode  Open mode flags (forced to contain ios::in).
          292  +   *
          293  +   *  Stream will be in state good() if attach succeeded; otherwise
          294  +   *  in state fail().
          295  +  */
          296  +  void
          297  +  attach(int fd,
          298  +         std::ios_base::openmode mode = std::ios_base::in);
          299  +
          300  +  /**
          301  +   *  @brief  Close gzipped file.
          302  +   *
          303  +   *  Stream will be in state fail() if close failed.
          304  +  */
          305  +  void
          306  +  close();
          307  +
          308  +private:
          309  +  /**
          310  +   *  Underlying stream buffer.
          311  +  */
          312  +  gzfilebuf sb;
          313  +};
          314  +
          315  +/*****************************************************************************/
          316  +
          317  +/**
          318  + *  @brief  Gzipped file output stream class.
          319  + *
          320  + *  This class implements ofstream for gzipped files. Seeking and putback
          321  + *  is not supported yet.
          322  +*/
          323  +class gzofstream : public std::ostream
          324  +{
          325  +public:
          326  +  //  Default constructor
          327  +  gzofstream();
          328  +
          329  +  /**
          330  +   *  @brief  Construct stream on gzipped file to be opened.
          331  +   *  @param  name  File name.
          332  +   *  @param  mode  Open mode flags (forced to contain ios::out).
          333  +  */
          334  +  explicit
          335  +  gzofstream(const char* name,
          336  +             std::ios_base::openmode mode = std::ios_base::out);
          337  +
          338  +  /**
          339  +   *  @brief  Construct stream on already open gzipped file.
          340  +   *  @param  fd    File descriptor.
          341  +   *  @param  mode  Open mode flags (forced to contain ios::out).
          342  +  */
          343  +  explicit
          344  +  gzofstream(int fd,
          345  +             std::ios_base::openmode mode = std::ios_base::out);
          346  +
          347  +  /**
          348  +   *  Obtain underlying stream buffer.
          349  +  */
          350  +  gzfilebuf*
          351  +  rdbuf() const
          352  +  { return const_cast<gzfilebuf*>(&sb); }
          353  +
          354  +  /**
          355  +   *  @brief  Check if file is open.
          356  +   *  @return  True if file is open.
          357  +  */
          358  +  bool
          359  +  is_open() { return sb.is_open(); }
          360  +
          361  +  /**
          362  +   *  @brief  Open gzipped file.
          363  +   *  @param  name  File name.
          364  +   *  @param  mode  Open mode flags (forced to contain ios::out).
          365  +   *
          366  +   *  Stream will be in state good() if file opens successfully;
          367  +   *  otherwise in state fail(). This differs from the behavior of
          368  +   *  ofstream, which never sets the state to good() and therefore
          369  +   *  won't allow you to reuse the stream for a second file unless
          370  +   *  you manually clear() the state. The choice is a matter of
          371  +   *  convenience.
          372  +  */
          373  +  void
          374  +  open(const char* name,
          375  +       std::ios_base::openmode mode = std::ios_base::out);
          376  +
          377  +  /**
          378  +   *  @brief  Attach to already open gzipped file.
          379  +   *  @param  fd  File descriptor.
          380  +   *  @param  mode  Open mode flags (forced to contain ios::out).
          381  +   *
          382  +   *  Stream will be in state good() if attach succeeded; otherwise
          383  +   *  in state fail().
          384  +  */
          385  +  void
          386  +  attach(int fd,
          387  +         std::ios_base::openmode mode = std::ios_base::out);
          388  +
          389  +  /**
          390  +   *  @brief  Close gzipped file.
          391  +   *
          392  +   *  Stream will be in state fail() if close failed.
          393  +  */
          394  +  void
          395  +  close();
          396  +
          397  +private:
          398  +  /**
          399  +   *  Underlying stream buffer.
          400  +  */
          401  +  gzfilebuf sb;
          402  +};
          403  +
          404  +/*****************************************************************************/
          405  +
          406  +/**
          407  + *  @brief  Gzipped file output stream manipulator class.
          408  + *
          409  + *  This class defines a two-argument manipulator for gzofstream. It is used
          410  + *  as base for the setcompression(int,int) manipulator.
          411  +*/
          412  +template<typename T1, typename T2>
          413  +  class gzomanip2
          414  +  {
          415  +  public:
          416  +    // Allows insertor to peek at internals
          417  +    template <typename Ta, typename Tb>
          418  +      friend gzofstream&
          419  +      operator<<(gzofstream&,
          420  +                 const gzomanip2<Ta,Tb>&);
          421  +
          422  +    // Constructor
          423  +    gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2),
          424  +              T1 v1,
          425  +              T2 v2);
          426  +  private:
          427  +    // Underlying manipulator function
          428  +    gzofstream&
          429  +    (*func)(gzofstream&, T1, T2);
          430  +
          431  +    // Arguments for manipulator function
          432  +    T1 val1;
          433  +    T2 val2;
          434  +  };
          435  +
          436  +/*****************************************************************************/
          437  +
          438  +// Manipulator function thunks through to stream buffer
          439  +inline gzofstream&
          440  +setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY)
          441  +{
          442  +  (gzs.rdbuf())->setcompression(l, s);
          443  +  return gzs;
          444  +}
          445  +
          446  +// Manipulator constructor stores arguments
          447  +template<typename T1, typename T2>
          448  +  inline
          449  +  gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
          450  +                              T1 v1,
          451  +                              T2 v2)
          452  +  : func(f), val1(v1), val2(v2)
          453  +  { }
          454  +
          455  +// Insertor applies underlying manipulator function to stream
          456  +template<typename T1, typename T2>
          457  +  inline gzofstream&
          458  +  operator<<(gzofstream& s, const gzomanip2<T1,T2>& m)
          459  +  { return (*m.func)(s, m.val1, m.val2); }
          460  +
          461  +// Insert this onto stream to simplify setting of compression level
          462  +inline gzomanip2<int,int>
          463  +setcompression(int l, int s = Z_DEFAULT_STRATEGY)
          464  +{ return gzomanip2<int,int>(&setcompression, l, s); }
          465  +
          466  +#endif // ZFSTREAM_H

Added compat/zlib/contrib/masmx64/bld_ml64.bat.

            1  +ml64.exe /Flinffasx64 /c /Zi inffasx64.asm
            2  +ml64.exe /Flgvmat64   /c /Zi gvmat64.asm

Added compat/zlib/contrib/masmx64/gvmat64.asm.

            1  +;uInt longest_match_x64(
            2  +;    deflate_state *s,
            3  +;    IPos cur_match);                             /* current match */
            4  +
            5  +; gvmat64.asm -- Asm portion of the optimized longest_match for 32 bits x86_64
            6  +;  (AMD64 on Athlon 64, Opteron, Phenom
            7  +;     and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7)
            8  +; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant.
            9  +;
           10  +; File written by Gilles Vollant, by converting to assembly the longest_match
           11  +;  from Jean-loup Gailly in deflate.c of zLib and infoZip zip.
           12  +;
           13  +;  and by taking inspiration on asm686 with masm, optimised assembly code
           14  +;        from Brian Raiter, written 1998
           15  +;
           16  +;  This software is provided 'as-is', without any express or implied
           17  +;  warranty.  In no event will the authors be held liable for any damages
           18  +;  arising from the use of this software.
           19  +;
           20  +;  Permission is granted to anyone to use this software for any purpose,
           21  +;  including commercial applications, and to alter it and redistribute it
           22  +;  freely, subject to the following restrictions:
           23  +;
           24  +;  1. The origin of this software must not be misrepresented; you must not
           25  +;     claim that you wrote the original software. If you use this software
           26  +;     in a product, an acknowledgment in the product documentation would be
           27  +;     appreciated but is not required.
           28  +;  2. Altered source versions must be plainly marked as such, and must not be
           29  +;     misrepresented as being the original software
           30  +;  3. This notice may not be removed or altered from any source distribution.
           31  +;
           32  +;
           33  +;
           34  +;         http://www.zlib.net
           35  +;         http://www.winimage.com/zLibDll
           36  +;         http://www.muppetlabs.com/~breadbox/software/assembly.html
           37  +;
           38  +; to compile this file for infozip Zip, I use option:
           39  +;   ml64.exe /Flgvmat64 /c /Zi /DINFOZIP gvmat64.asm
           40  +;
           41  +; to compile this file for zLib, I use option:
           42  +;   ml64.exe /Flgvmat64 /c /Zi gvmat64.asm
           43  +; Be carrefull to adapt zlib1222add below to your version of zLib
           44  +;   (if you use a version of zLib before 1.0.4 or after 1.2.2.2, change
           45  +;    value of zlib1222add later)
           46  +;
           47  +; This file compile with Microsoft Macro Assembler (x64) for AMD64
           48  +;
           49  +;   ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK
           50  +;
           51  +;   (you can get Windows WDK with ml64 for AMD64 from
           52  +;      http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price)
           53  +;
           54  +
           55  +
           56  +;uInt longest_match(s, cur_match)
           57  +;    deflate_state *s;
           58  +;    IPos cur_match;                             /* current match */
           59  +.code
           60  +longest_match PROC
           61  +
           62  +
           63  +;LocalVarsSize   equ 88
           64  + LocalVarsSize   equ 72
           65  +
           66  +; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12
           67  +; free register :  r14,r15
           68  +; register can be saved : rsp
           69  +
           70  + chainlenwmask   equ  rsp + 8 - LocalVarsSize    ; high word: current chain len
           71  +                                                 ; low word: s->wmask
           72  +;window          equ  rsp + xx - LocalVarsSize   ; local copy of s->window ; stored in r10
           73  +;windowbestlen   equ  rsp + xx - LocalVarsSize   ; s->window + bestlen , use r10+r11
           74  +;scanstart       equ  rsp + xx - LocalVarsSize   ; first two bytes of string ; stored in r12w
           75  +;scanend         equ  rsp + xx - LocalVarsSize   ; last two bytes of string use ebx
           76  +;scanalign       equ  rsp + xx - LocalVarsSize   ; dword-misalignment of string r13
           77  +;bestlen         equ  rsp + xx - LocalVarsSize   ; size of best match so far -> r11d
           78  +;scan            equ  rsp + xx - LocalVarsSize   ; ptr to string wanting match -> r9
           79  +IFDEF INFOZIP
           80  +ELSE
           81  + nicematch       equ  (rsp + 16 - LocalVarsSize) ; a good enough match size
           82  +ENDIF
           83  +
           84  +save_rdi        equ  rsp + 24 - LocalVarsSize
           85  +save_rsi        equ  rsp + 32 - LocalVarsSize
           86  +save_rbx        equ  rsp + 40 - LocalVarsSize
           87  +save_rbp        equ  rsp + 48 - LocalVarsSize
           88  +save_r12        equ  rsp + 56 - LocalVarsSize
           89  +save_r13        equ  rsp + 64 - LocalVarsSize
           90  +;save_r14        equ  rsp + 72 - LocalVarsSize
           91  +;save_r15        equ  rsp + 80 - LocalVarsSize
           92  +
           93  +
           94  +; summary of register usage
           95  +; scanend     ebx
           96  +; scanendw    bx
           97  +; chainlenwmask   edx
           98  +; curmatch    rsi
           99  +; curmatchd   esi
          100  +; windowbestlen   r8
          101  +; scanalign   r9
          102  +; scanalignd  r9d
          103  +; window      r10
          104  +; bestlen     r11
          105  +; bestlend    r11d
          106  +; scanstart   r12d
          107  +; scanstartw  r12w
          108  +; scan        r13
          109  +; nicematch   r14d
          110  +; limit       r15
          111  +; limitd      r15d
          112  +; prev        rcx
          113  +
          114  +;  all the +4 offsets are due to the addition of pending_buf_size (in zlib
          115  +;  in the deflate_state structure since the asm code was first written
          116  +;  (if you compile with zlib 1.0.4 or older, remove the +4).
          117  +;  Note : these value are good with a 8 bytes boundary pack structure
          118  +
          119  +
          120  +    MAX_MATCH           equ     258
          121  +    MIN_MATCH           equ     3
          122  +    MIN_LOOKAHEAD       equ     (MAX_MATCH+MIN_MATCH+1)
          123  +
          124  +
          125  +;;; Offsets for fields in the deflate_state structure. These numbers
          126  +;;; are calculated from the definition of deflate_state, with the
          127  +;;; assumption that the compiler will dword-align the fields. (Thus,
          128  +;;; changing the definition of deflate_state could easily cause this
          129  +;;; program to crash horribly, without so much as a warning at
          130  +;;; compile time. Sigh.)
          131  +
          132  +;  all the +zlib1222add offsets are due to the addition of fields
          133  +;  in zlib in the deflate_state structure since the asm code was first written
          134  +;  (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
          135  +;  (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
          136  +;  if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
          137  +
          138  +
          139  +IFDEF INFOZIP
          140  +
          141  +_DATA   SEGMENT
          142  +COMM    window_size:DWORD
          143  +; WMask ; 7fff
          144  +COMM    window:BYTE:010040H
          145  +COMM    prev:WORD:08000H
          146  +; MatchLen : unused
          147  +; PrevMatch : unused
          148  +COMM    strstart:DWORD
          149  +COMM    match_start:DWORD
          150  +; Lookahead : ignore
          151  +COMM    prev_length:DWORD ; PrevLen
          152  +COMM    max_chain_length:DWORD
          153  +COMM    good_match:DWORD
          154  +COMM    nice_match:DWORD
          155  +prev_ad equ OFFSET prev
          156  +window_ad equ OFFSET window
          157  +nicematch equ nice_match
          158  +_DATA ENDS
          159  +WMask equ 07fffh
          160  +
          161  +ELSE
          162  +
          163  +  IFNDEF zlib1222add
          164  +    zlib1222add equ 8
          165  +  ENDIF
          166  +dsWSize         equ 56+zlib1222add+(zlib1222add/2)
          167  +dsWMask         equ 64+zlib1222add+(zlib1222add/2)
          168  +dsWindow        equ 72+zlib1222add
          169  +dsPrev          equ 88+zlib1222add
          170  +dsMatchLen      equ 128+zlib1222add
          171  +dsPrevMatch     equ 132+zlib1222add
          172  +dsStrStart      equ 140+zlib1222add
          173  +dsMatchStart    equ 144+zlib1222add
          174  +dsLookahead     equ 148+zlib1222add
          175  +dsPrevLen       equ 152+zlib1222add
          176  +dsMaxChainLen   equ 156+zlib1222add
          177  +dsGoodMatch     equ 172+zlib1222add
          178  +dsNiceMatch     equ 176+zlib1222add
          179  +
          180  +window_size     equ [ rcx + dsWSize]
          181  +WMask           equ [ rcx + dsWMask]
          182  +window_ad       equ [ rcx + dsWindow]
          183  +prev_ad         equ [ rcx + dsPrev]
          184  +strstart        equ [ rcx + dsStrStart]
          185  +match_start     equ [ rcx + dsMatchStart]
          186  +Lookahead       equ [ rcx + dsLookahead] ; 0ffffffffh on infozip
          187  +prev_length     equ [ rcx + dsPrevLen]
          188  +max_chain_length equ [ rcx + dsMaxChainLen]
          189  +good_match      equ [ rcx + dsGoodMatch]
          190  +nice_match      equ [ rcx + dsNiceMatch]
          191  +ENDIF
          192  +
          193  +; parameter 1 in r8(deflate state s), param 2 in rdx (cur match)
          194  +
          195  +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and
          196  +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp
          197  +;
          198  +; All registers must be preserved across the call, except for
          199  +;   rax, rcx, rdx, r8, r9, r10, and r11, which are scratch.
          200  +
          201  +
          202  +
          203  +;;; Save registers that the compiler may be using, and adjust esp to
          204  +;;; make room for our stack frame.
          205  +
          206  +
          207  +;;; Retrieve the function arguments. r8d will hold cur_match
          208  +;;; throughout the entire function. edx will hold the pointer to the
          209  +;;; deflate_state structure during the function's setup (before
          210  +;;; entering the main loop.
          211  +
          212  +; parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match)
          213  +
          214  +; this clear high 32 bits of r8, which can be garbage in both r8 and rdx
          215  +
          216  +        mov [save_rdi],rdi
          217  +        mov [save_rsi],rsi
          218  +        mov [save_rbx],rbx
          219  +        mov [save_rbp],rbp
          220  +IFDEF INFOZIP
          221  +        mov r8d,ecx
          222  +ELSE
          223  +        mov r8d,edx
          224  +ENDIF
          225  +        mov [save_r12],r12
          226  +        mov [save_r13],r13
          227  +;        mov [save_r14],r14
          228  +;        mov [save_r15],r15
          229  +
          230  +
          231  +;;; uInt wmask = s->w_mask;
          232  +;;; unsigned chain_length = s->max_chain_length;
          233  +;;; if (s->prev_length >= s->good_match) {
          234  +;;;     chain_length >>= 2;
          235  +;;; }
          236  +
          237  +        mov edi, prev_length
          238  +        mov esi, good_match
          239  +        mov eax, WMask
          240  +        mov ebx, max_chain_length
          241  +        cmp edi, esi
          242  +        jl  LastMatchGood
          243  +        shr ebx, 2
          244  +LastMatchGood:
          245  +
          246  +;;; chainlen is decremented once beforehand so that the function can
          247  +;;; use the sign flag instead of the zero flag for the exit test.
          248  +;;; It is then shifted into the high word, to make room for the wmask
          249  +;;; value, which it will always accompany.
          250  +
          251  +        dec ebx
          252  +        shl ebx, 16
          253  +        or  ebx, eax
          254  +
          255  +;;; on zlib only
          256  +;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
          257  +
          258  +IFDEF INFOZIP
          259  +        mov [chainlenwmask], ebx
          260  +; on infozip nice_match = [nice_match]
          261  +ELSE
          262  +        mov eax, nice_match
          263  +        mov [chainlenwmask], ebx
          264  +        mov r10d, Lookahead
          265  +        cmp r10d, eax
          266  +        cmovnl r10d, eax
          267  +        mov [nicematch],r10d
          268  +ENDIF
          269  +
          270  +;;; register Bytef *scan = s->window + s->strstart;
          271  +        mov r10, window_ad
          272  +        mov ebp, strstart
          273  +        lea r13, [r10 + rbp]
          274  +
          275  +;;; Determine how many bytes the scan ptr is off from being
          276  +;;; dword-aligned.
          277  +
          278  +         mov r9,r13
          279  +         neg r13
          280  +         and r13,3
          281  +
          282  +;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
          283  +;;;     s->strstart - (IPos)MAX_DIST(s) : NIL;
          284  +IFDEF INFOZIP
          285  +        mov eax,07efah ; MAX_DIST = (WSIZE-MIN_LOOKAHEAD) (0x8000-(3+8+1))
          286  +ELSE
          287  +        mov eax, window_size
          288  +        sub eax, MIN_LOOKAHEAD
          289  +ENDIF
          290  +        xor edi,edi
          291  +        sub ebp, eax
          292  +
          293  +        mov r11d, prev_length
          294  +
          295  +        cmovng ebp,edi
          296  +
          297  +;;; int best_len = s->prev_length;
          298  +
          299  +
          300  +;;; Store the sum of s->window + best_len in esi locally, and in esi.
          301  +
          302  +       lea  rsi,[r10+r11]
          303  +
          304  +;;; register ush scan_start = *(ushf*)scan;
          305  +;;; register ush scan_end   = *(ushf*)(scan+best_len-1);
          306  +;;; Posf *prev = s->prev;
          307  +
          308  +        movzx r12d,word ptr [r9]
          309  +        movzx ebx, word ptr [r9 + r11 - 1]
          310  +
          311  +        mov rdi, prev_ad
          312  +
          313  +;;; Jump into the main loop.
          314  +
          315  +        mov edx, [chainlenwmask]
          316  +
          317  +        cmp bx,word ptr [rsi + r8 - 1]
          318  +        jz  LookupLoopIsZero
          319  +
          320  +LookupLoop1:
          321  +        and r8d, edx
          322  +
          323  +        movzx   r8d, word ptr [rdi + r8*2]
          324  +        cmp r8d, ebp
          325  +        jbe LeaveNow
          326  +        sub edx, 00010000h
          327  +        js  LeaveNow
          328  +
          329  +LoopEntry1:
          330  +        cmp bx,word ptr [rsi + r8 - 1]
          331  +        jz  LookupLoopIsZero
          332  +
          333  +LookupLoop2:
          334  +        and r8d, edx
          335  +
          336  +        movzx   r8d, word ptr [rdi + r8*2]
          337  +        cmp r8d, ebp
          338  +        jbe LeaveNow
          339  +        sub edx, 00010000h
          340  +        js  LeaveNow
          341  +
          342  +LoopEntry2:
          343  +        cmp bx,word ptr [rsi + r8 - 1]
          344  +        jz  LookupLoopIsZero
          345  +
          346  +LookupLoop4:
          347  +        and r8d, edx
          348  +
          349  +        movzx   r8d, word ptr [rdi + r8*2]
          350  +        cmp r8d, ebp
          351  +        jbe LeaveNow
          352  +        sub edx, 00010000h
          353  +        js  LeaveNow
          354  +
          355  +LoopEntry4:
          356  +
          357  +        cmp bx,word ptr [rsi + r8 - 1]
          358  +        jnz LookupLoop1
          359  +        jmp LookupLoopIsZero
          360  +
          361  +
          362  +;;; do {
          363  +;;;     match = s->window + cur_match;
          364  +;;;     if (*(ushf*)(match+best_len-1) != scan_end ||
          365  +;;;         *(ushf*)match != scan_start) continue;
          366  +;;;     [...]
          367  +;;; } while ((cur_match = prev[cur_match & wmask]) > limit
          368  +;;;          && --chain_length != 0);
          369  +;;;
          370  +;;; Here is the inner loop of the function. The function will spend the
          371  +;;; majority of its time in this loop, and majority of that time will
          372  +;;; be spent in the first ten instructions.
          373  +;;;
          374  +;;; Within this loop:
          375  +;;; ebx = scanend
          376  +;;; r8d = curmatch
          377  +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
          378  +;;; esi = windowbestlen - i.e., (window + bestlen)
          379  +;;; edi = prev
          380  +;;; ebp = limit
          381  +
          382  +LookupLoop:
          383  +        and r8d, edx
          384  +
          385  +        movzx   r8d, word ptr [rdi + r8*2]
          386  +        cmp r8d, ebp
          387  +        jbe LeaveNow
          388  +        sub edx, 00010000h
          389  +        js  LeaveNow
          390  +
          391  +LoopEntry:
          392  +
          393  +        cmp bx,word ptr [rsi + r8 - 1]
          394  +        jnz LookupLoop1
          395  +LookupLoopIsZero:
          396  +        cmp     r12w, word ptr [r10 + r8]
          397  +        jnz LookupLoop1
          398  +
          399  +
          400  +;;; Store the current value of chainlen.
          401  +        mov [chainlenwmask], edx
          402  +
          403  +;;; Point edi to the string under scrutiny, and esi to the string we
          404  +;;; are hoping to match it up with. In actuality, esi and edi are
          405  +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is
          406  +;;; initialized to -(MAX_MATCH_8 - scanalign).
          407  +
          408  +        lea rsi,[r8+r10]
          409  +        mov rdx, 0fffffffffffffef8h; -(MAX_MATCH_8)
          410  +        lea rsi, [rsi + r13 + 0108h] ;MAX_MATCH_8]
          411  +        lea rdi, [r9 + r13 + 0108h] ;MAX_MATCH_8]
          412  +
          413  +        prefetcht1 [rsi+rdx]
          414  +        prefetcht1 [rdi+rdx]
          415  +
          416  +
          417  +;;; Test the strings for equality, 8 bytes at a time. At the end,
          418  +;;; adjust rdx so that it is offset to the exact byte that mismatched.
          419  +;;;
          420  +;;; We already know at this point that the first three bytes of the
          421  +;;; strings match each other, and they can be safely passed over before
          422  +;;; starting the compare loop. So what this code does is skip over 0-3
          423  +;;; bytes, as much as necessary in order to dword-align the edi
          424  +;;; pointer. (rsi will still be misaligned three times out of four.)
          425  +;;;
          426  +;;; It should be confessed that this loop usually does not represent
          427  +;;; much of the total running time. Replacing it with a more
          428  +;;; straightforward "rep cmpsb" would not drastically degrade
          429  +;;; performance.
          430  +
          431  +
          432  +LoopCmps:
          433  +        mov rax, [rsi + rdx]
          434  +        xor rax, [rdi + rdx]
          435  +        jnz LeaveLoopCmps
          436  +
          437  +        mov rax, [rsi + rdx + 8]
          438  +        xor rax, [rdi + rdx + 8]
          439  +        jnz LeaveLoopCmps8
          440  +
          441  +
          442  +        mov rax, [rsi + rdx + 8+8]
          443  +        xor rax, [rdi + rdx + 8+8]
          444  +        jnz LeaveLoopCmps16
          445  +
          446  +        add rdx,8+8+8
          447  +
          448  +        jnz short LoopCmps
          449  +        jmp short LenMaximum
          450  +LeaveLoopCmps16: add rdx,8
          451  +LeaveLoopCmps8: add rdx,8
          452  +LeaveLoopCmps:
          453  +
          454  +        test    eax, 0000FFFFh
          455  +        jnz LenLower
          456  +
          457  +        test eax,0ffffffffh
          458  +
          459  +        jnz LenLower32
          460  +
          461  +        add rdx,4
          462  +        shr rax,32
          463  +        or ax,ax
          464  +        jnz LenLower
          465  +
          466  +LenLower32:
          467  +        shr eax,16
          468  +        add rdx,2
          469  +LenLower:   sub al, 1
          470  +        adc rdx, 0
          471  +;;; Calculate the length of the match. If it is longer than MAX_MATCH,
          472  +;;; then automatically accept it as the best possible match and leave.
          473  +
          474  +        lea rax, [rdi + rdx]
          475  +        sub rax, r9
          476  +        cmp eax, MAX_MATCH
          477  +        jge LenMaximum
          478  +
          479  +;;; If the length of the match is not longer than the best match we
          480  +;;; have so far, then forget it and return to the lookup loop.
          481  +;///////////////////////////////////
          482  +
          483  +        cmp eax, r11d
          484  +        jg  LongerMatch
          485  +
          486  +        lea rsi,[r10+r11]
          487  +
          488  +        mov rdi, prev_ad
          489  +        mov edx, [chainlenwmask]
          490  +        jmp LookupLoop
          491  +
          492  +;;;         s->match_start = cur_match;
          493  +;;;         best_len = len;
          494  +;;;         if (len >= nice_match) break;
          495  +;;;         scan_end = *(ushf*)(scan+best_len-1);
          496  +
          497  +LongerMatch:
          498  +        mov r11d, eax
          499  +        mov match_start, r8d
          500  +        cmp eax, [nicematch]
          501  +        jge LeaveNow
          502  +
          503  +        lea rsi,[r10+rax]
          504  +
          505  +        movzx   ebx, word ptr [r9 + rax - 1]
          506  +        mov rdi, prev_ad
          507  +        mov edx, [chainlenwmask]
          508  +        jmp LookupLoop
          509  +
          510  +;;; Accept the current string, with the maximum possible length.
          511  +
          512  +LenMaximum:
          513  +        mov r11d,MAX_MATCH
          514  +        mov match_start, r8d
          515  +
          516  +;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
          517  +;;; return s->lookahead;
          518  +
          519  +LeaveNow:
          520  +IFDEF INFOZIP
          521  +        mov eax,r11d
          522  +ELSE
          523  +        mov eax, Lookahead
          524  +        cmp r11d, eax
          525  +        cmovng eax, r11d
          526  +ENDIF
          527  +
          528  +;;; Restore the stack and return from whence we came.
          529  +
          530  +
          531  +        mov rsi,[save_rsi]
          532  +        mov rdi,[save_rdi]
          533  +        mov rbx,[save_rbx]
          534  +        mov rbp,[save_rbp]
          535  +        mov r12,[save_r12]
          536  +        mov r13,[save_r13]
          537  +;        mov r14,[save_r14]
          538  +;        mov r15,[save_r15]
          539  +
          540  +
          541  +        ret 0
          542  +; please don't remove this string !
          543  +; Your can freely use gvmat64 in any free or commercial app
          544  +; but it is far better don't remove the string in the binary!
          545  +    db     0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0
          546  +longest_match   ENDP
          547  +
          548  +match_init PROC
          549  +  ret 0
          550  +match_init ENDP
          551  +
          552  +
          553  +END

Added compat/zlib/contrib/masmx64/inffas8664.c.

            1  +/* inffas8664.c is a hand tuned assembler version of inffast.c - fast decoding
            2  + * version for AMD64 on Windows using Microsoft C compiler
            3  + *
            4  + * Copyright (C) 1995-2003 Mark Adler
            5  + * For conditions of distribution and use, see copyright notice in zlib.h
            6  + *
            7  + * Copyright (C) 2003 Chris Anderson <christop@charm.net>
            8  + * Please use the copyright conditions above.
            9  + *
           10  + * 2005 - Adaptation to Microsoft C Compiler for AMD64 by Gilles Vollant
           11  + *
           12  + * inffas8664.c call function inffas8664fnc in inffasx64.asm
           13  + *  inffasx64.asm is automatically convert from AMD64 portion of inffas86.c
           14  + *
           15  + * Dec-29-2003 -- I added AMD64 inflate asm support.  This version is also
           16  + * slightly quicker on x86 systems because, instead of using rep movsb to copy
           17  + * data, it uses rep movsw, which moves data in 2-byte chunks instead of single
           18  + * bytes.  I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates
           19  + * from http://fedora.linux.duke.edu/fc1_x86_64
           20  + * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with
           21  + * 1GB ram.  The 64-bit version is about 4% faster than the 32-bit version,
           22  + * when decompressing mozilla-source-1.3.tar.gz.
           23  + *
           24  + * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
           25  + * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
           26  + * the moment.  I have successfully compiled and tested this code with gcc2.96,
           27  + * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
           28  + * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
           29  + * enabled.  I will attempt to merge the MMX code into this version.  Newer
           30  + * versions of this and inffast.S can be found at
           31  + * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
           32  + *
           33  + */
           34  +
           35  +#include <stdio.h>
           36  +#include "zutil.h"
           37  +#include "inftrees.h"
           38  +#include "inflate.h"
           39  +#include "inffast.h"
           40  +
           41  +/* Mark Adler's comments from inffast.c: */
           42  +
           43  +/*
           44  +   Decode literal, length, and distance codes and write out the resulting
           45  +   literal and match bytes until either not enough input or output is
           46  +   available, an end-of-block is encountered, or a data error is encountered.
           47  +   When large enough input and output buffers are supplied to inflate(), for
           48  +   example, a 16K input buffer and a 64K output buffer, more than 95% of the
           49  +   inflate execution time is spent in this routine.
           50  +
           51  +   Entry assumptions:
           52  +
           53  +        state->mode == LEN
           54  +        strm->avail_in >= 6
           55  +        strm->avail_out >= 258
           56  +        start >= strm->avail_out
           57  +        state->bits < 8
           58  +
           59  +   On return, state->mode is one of:
           60  +
           61  +        LEN -- ran out of enough output space or enough available input
           62  +        TYPE -- reached end of block code, inflate() to interpret next block
           63  +        BAD -- error in block data
           64  +
           65  +   Notes:
           66  +
           67  +    - The maximum input bits used by a length/distance pair is 15 bits for the
           68  +      length code, 5 bits for the length extra, 15 bits for the distance code,
           69  +      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
           70  +      Therefore if strm->avail_in >= 6, then there is enough input to avoid
           71  +      checking for available input while decoding.
           72  +
           73  +    - The maximum bytes that a single length/distance pair can output is 258
           74  +      bytes, which is the maximum length that can be coded.  inflate_fast()
           75  +      requires strm->avail_out >= 258 for each loop to avoid checking for
           76  +      output space.
           77  + */
           78  +
           79  +
           80  +
           81  +    typedef struct inffast_ar {
           82  +/* 64   32                               x86  x86_64 */
           83  +/* ar offset                              register */
           84  +/*  0    0 */ void *esp;                /* esp save */
           85  +/*  8    4 */ void *ebp;                /* ebp save */
           86  +/* 16    8 */ unsigned char FAR *in;    /* esi rsi  local strm->next_in */
           87  +/* 24   12 */ unsigned char FAR *last;  /*     r9   while in < last */
           88  +/* 32   16 */ unsigned char FAR *out;   /* edi rdi  local strm->next_out */
           89  +/* 40   20 */ unsigned char FAR *beg;   /*          inflate()'s init next_out */
           90  +/* 48   24 */ unsigned char FAR *end;   /*     r10  while out < end */
           91  +/* 56   28 */ unsigned char FAR *window;/*          size of window, wsize!=0 */
           92  +/* 64   32 */ code const FAR *lcode;    /* ebp rbp  local strm->lencode */
           93  +/* 72   36 */ code const FAR *dcode;    /*     r11  local strm->distcode */
           94  +/* 80   40 */ size_t /*unsigned long */hold;       /* edx rdx  local strm->hold */
           95  +/* 88   44 */ unsigned bits;            /* ebx rbx  local strm->bits */
           96  +/* 92   48 */ unsigned wsize;           /*          window size */
           97  +/* 96   52 */ unsigned write;           /*          window write index */
           98  +/*100   56 */ unsigned lmask;           /*     r12  mask for lcode */
           99  +/*104   60 */ unsigned dmask;           /*     r13  mask for dcode */
          100  +/*108   64 */ unsigned len;             /*     r14  match length */
          101  +/*112   68 */ unsigned dist;            /*     r15  match distance */
          102  +/*116   72 */ unsigned status;          /*          set when state chng*/
          103  +    } type_ar;
          104  +#ifdef ASMINF
          105  +
          106  +void inflate_fast(strm, start)
          107  +z_streamp strm;
          108  +unsigned start;         /* inflate()'s starting value for strm->avail_out */
          109  +{
          110  +    struct inflate_state FAR *state;
          111  +    type_ar ar;
          112  +    void inffas8664fnc(struct inffast_ar * par);
          113  +
          114  +
          115  +
          116  +#if (defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )) || (defined(_MSC_VER) && defined(_M_AMD64))
          117  +#define PAD_AVAIL_IN 6
          118  +#define PAD_AVAIL_OUT 258
          119  +#else
          120  +#define PAD_AVAIL_IN 5
          121  +#define PAD_AVAIL_OUT 257
          122  +#endif
          123  +
          124  +    /* copy state to local variables */
          125  +    state = (struct inflate_state FAR *)strm->state;
          126  +
          127  +    ar.in = strm->next_in;
          128  +    ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN);
          129  +    ar.out = strm->next_out;
          130  +    ar.beg = ar.out - (start - strm->avail_out);
          131  +    ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT);
          132  +    ar.wsize = state->wsize;
          133  +    ar.write = state->wnext;
          134  +    ar.window = state->window;
          135  +    ar.hold = state->hold;
          136  +    ar.bits = state->bits;
          137  +    ar.lcode = state->lencode;
          138  +    ar.dcode = state->distcode;
          139  +    ar.lmask = (1U << state->lenbits) - 1;
          140  +    ar.dmask = (1U << state->distbits) - 1;
          141  +
          142  +    /* decode literals and length/distances until end-of-block or not enough
          143  +       input data or output space */
          144  +
          145  +    /* align in on 1/2 hold size boundary */
          146  +    while (((size_t)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) {
          147  +        ar.hold += (unsigned long)*ar.in++ << ar.bits;
          148  +        ar.bits += 8;
          149  +    }
          150  +
          151  +    inffas8664fnc(&ar);
          152  +
          153  +    if (ar.status > 1) {
          154  +        if (ar.status == 2)
          155  +            strm->msg = "invalid literal/length code";
          156  +        else if (ar.status == 3)
          157  +            strm->msg = "invalid distance code";
          158  +        else
          159  +            strm->msg = "invalid distance too far back";
          160  +        state->mode = BAD;
          161  +    }
          162  +    else if ( ar.status == 1 ) {
          163  +        state->mode = TYPE;
          164  +    }
          165  +
          166  +    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
          167  +    ar.len = ar.bits >> 3;
          168  +    ar.in -= ar.len;
          169  +    ar.bits -= ar.len << 3;
          170  +    ar.hold &= (1U << ar.bits) - 1;
          171  +
          172  +    /* update state and return */
          173  +    strm->next_in = ar.in;
          174  +    strm->next_out = ar.out;
          175  +    strm->avail_in = (unsigned)(ar.in < ar.last ?
          176  +                                PAD_AVAIL_IN + (ar.last - ar.in) :
          177  +                                PAD_AVAIL_IN - (ar.in - ar.last));
          178  +    strm->avail_out = (unsigned)(ar.out < ar.end ?
          179  +                                 PAD_AVAIL_OUT + (ar.end - ar.out) :
          180  +                                 PAD_AVAIL_OUT - (ar.out - ar.end));
          181  +    state->hold = (unsigned long)ar.hold;
          182  +    state->bits = ar.bits;
          183  +    return;
          184  +}
          185  +
          186  +#endif

Added compat/zlib/contrib/masmx64/inffasx64.asm.

            1  +; inffasx64.asm is a hand tuned assembler version of inffast.c - fast decoding
            2  +; version for AMD64 on Windows using Microsoft C compiler
            3  +;
            4  +; inffasx64.asm is automatically convert from AMD64 portion of inffas86.c
            5  +; inffasx64.asm is called by inffas8664.c, which contain more info.
            6  +
            7  +
            8  +; to compile this file, I use option
            9  +;   ml64.exe /Flinffasx64 /c /Zi inffasx64.asm
           10  +;   with Microsoft Macro Assembler (x64) for AMD64
           11  +;
           12  +
           13  +; This file compile with Microsoft Macro Assembler (x64) for AMD64
           14  +;
           15  +;   ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK
           16  +;
           17  +;   (you can get Windows WDK with ml64 for AMD64 from
           18  +;      http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price)
           19  +;
           20  +
           21  +
           22  +.code
           23  +inffas8664fnc PROC
           24  +
           25  +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and
           26  +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp
           27  +;
           28  +; All registers must be preserved across the call, except for
           29  +;   rax, rcx, rdx, r8, r-9, r10, and r11, which are scratch.
           30  +
           31  +
           32  +	mov [rsp-8],rsi
           33  +	mov [rsp-16],rdi
           34  +	mov [rsp-24],r12
           35  +	mov [rsp-32],r13
           36  +	mov [rsp-40],r14
           37  +	mov [rsp-48],r15
           38  +	mov [rsp-56],rbx
           39  +
           40  +	mov rax,rcx
           41  +
           42  +	mov	[rax+8], rbp       ; /* save regs rbp and rsp */
           43  +	mov	[rax], rsp
           44  +
           45  +	mov	rsp, rax          ; /* make rsp point to &ar */
           46  +
           47  +	mov	rsi, [rsp+16]      ; /* rsi  = in */
           48  +	mov	rdi, [rsp+32]      ; /* rdi  = out */
           49  +	mov	r9, [rsp+24]       ; /* r9   = last */
           50  +	mov	r10, [rsp+48]      ; /* r10  = end */
           51  +	mov	rbp, [rsp+64]      ; /* rbp  = lcode */
           52  +	mov	r11, [rsp+72]      ; /* r11  = dcode */
           53  +	mov	rdx, [rsp+80]      ; /* rdx  = hold */
           54  +	mov	ebx, [rsp+88]      ; /* ebx  = bits */
           55  +	mov	r12d, [rsp+100]    ; /* r12d = lmask */
           56  +	mov	r13d, [rsp+104]    ; /* r13d = dmask */
           57  +                                          ; /* r14d = len */
           58  +                                          ; /* r15d = dist */
           59  +
           60  +
           61  +	cld
           62  +	cmp	r10, rdi
           63  +	je	L_one_time           ; /* if only one decode left */
           64  +	cmp	r9, rsi
           65  +
           66  +    jne L_do_loop
           67  +
           68  +
           69  +L_one_time:
           70  +	mov	r8, r12           ; /* r8 = lmask */
           71  +	cmp	bl, 32
           72  +	ja	L_get_length_code_one_time
           73  +
           74  +	lodsd                         ; /* eax = *(uint *)in++ */
           75  +	mov	cl, bl            ; /* cl = bits, needs it for shifting */
           76  +	add	bl, 32             ; /* bits += 32 */
           77  +	shl	rax, cl
           78  +	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */
           79  +	jmp	L_get_length_code_one_time
           80  +
           81  +ALIGN 4
           82  +L_while_test:
           83  +	cmp	r10, rdi
           84  +	jbe	L_break_loop
           85  +	cmp	r9, rsi
           86  +	jbe	L_break_loop
           87  +
           88  +L_do_loop:
           89  +	mov	r8, r12           ; /* r8 = lmask */
           90  +	cmp	bl, 32
           91  +	ja	L_get_length_code    ; /* if (32 < bits) */
           92  +
           93  +	lodsd                         ; /* eax = *(uint *)in++ */
           94  +	mov	cl, bl            ; /* cl = bits, needs it for shifting */
           95  +	add	bl, 32             ; /* bits += 32 */
           96  +	shl	rax, cl
           97  +	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */
           98  +
           99  +L_get_length_code:
          100  +	and	r8, rdx            ; /* r8 &= hold */
          101  +	mov	eax, [rbp+r8*4]  ; /* eax = lcode[hold & lmask] */
          102  +
          103  +	mov	cl, ah            ; /* cl = this.bits */
          104  +	sub	bl, ah            ; /* bits -= this.bits */
          105  +	shr	rdx, cl           ; /* hold >>= this.bits */
          106  +
          107  +	test	al, al
          108  +	jnz	L_test_for_length_base ; /* if (op != 0) 45.7% */
          109  +
          110  +	mov	r8, r12            ; /* r8 = lmask */
          111  +	shr	eax, 16            ; /* output this.val char */
          112  +	stosb
          113  +
          114  +L_get_length_code_one_time:
          115  +	and	r8, rdx            ; /* r8 &= hold */
          116  +	mov	eax, [rbp+r8*4] ; /* eax = lcode[hold & lmask] */
          117  +
          118  +L_dolen:
          119  +	mov	cl, ah            ; /* cl = this.bits */
          120  +	sub	bl, ah            ; /* bits -= this.bits */
          121  +	shr	rdx, cl           ; /* hold >>= this.bits */
          122  +
          123  +	test	al, al
          124  +	jnz	L_test_for_length_base ; /* if (op != 0) 45.7% */
          125  +
          126  +	shr	eax, 16            ; /* output this.val char */
          127  +	stosb
          128  +	jmp	L_while_test
          129  +
          130  +ALIGN 4
          131  +L_test_for_length_base:
          132  +	mov	r14d, eax         ; /* len = this */
          133  +	shr	r14d, 16           ; /* len = this.val */
          134  +	mov	cl, al
          135  +
          136  +	test	al, 16
          137  +	jz	L_test_for_second_level_length ; /* if ((op & 16) == 0) 8% */
          138  +	and	cl, 15             ; /* op &= 15 */
          139  +	jz	L_decode_distance    ; /* if (!op) */
          140  +
          141  +L_add_bits_to_len:
          142  +	sub	bl, cl
          143  +	xor	eax, eax
          144  +	inc	eax
          145  +	shl	eax, cl
          146  +	dec	eax
          147  +	and	eax, edx          ; /* eax &= hold */
          148  +	shr	rdx, cl
          149  +	add	r14d, eax         ; /* len += hold & mask[op] */
          150  +
          151  +L_decode_distance:
          152  +	mov	r8, r13           ; /* r8 = dmask */
          153  +	cmp	bl, 32
          154  +	ja	L_get_distance_code  ; /* if (32 < bits) */
          155  +
          156  +	lodsd                         ; /* eax = *(uint *)in++ */
          157  +	mov	cl, bl            ; /* cl = bits, needs it for shifting */
          158  +	add	bl, 32             ; /* bits += 32 */
          159  +	shl	rax, cl
          160  +	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */
          161  +
          162  +L_get_distance_code:
          163  +	and	r8, rdx           ; /* r8 &= hold */
          164  +	mov	eax, [r11+r8*4] ; /* eax = dcode[hold & dmask] */
          165  +
          166  +L_dodist:
          167  +	mov	r15d, eax         ; /* dist = this */
          168  +	shr	r15d, 16           ; /* dist = this.val */
          169  +	mov	cl, ah
          170  +	sub	bl, ah            ; /* bits -= this.bits */
          171  +	shr	rdx, cl           ; /* hold >>= this.bits */
          172  +	mov	cl, al            ; /* cl = this.op */
          173  +
          174  +	test	al, 16             ; /* if ((op & 16) == 0) */
          175  +	jz	L_test_for_second_level_dist
          176  +	and	cl, 15             ; /* op &= 15 */
          177  +	jz	L_check_dist_one
          178  +
          179  +L_add_bits_to_dist:
          180  +	sub	bl, cl
          181  +	xor	eax, eax
          182  +	inc	eax
          183  +	shl	eax, cl
          184  +	dec	eax                 ; /* (1 << op) - 1 */
          185  +	and	eax, edx          ; /* eax &= hold */
          186  +	shr	rdx, cl
          187  +	add	r15d, eax         ; /* dist += hold & ((1 << op) - 1) */
          188  +
          189  +L_check_window:
          190  +	mov	r8, rsi           ; /* save in so from can use it's reg */
          191  +	mov	rax, rdi
          192  +	sub	rax, [rsp+40]      ; /* nbytes = out - beg */
          193  +
          194  +	cmp	eax, r15d
          195  +	jb	L_clip_window        ; /* if (dist > nbytes) 4.2% */
          196  +
          197  +	mov	ecx, r14d         ; /* ecx = len */
          198  +	mov	rsi, rdi
          199  +	sub	rsi, r15          ; /* from = out - dist */
          200  +
          201  +	sar	ecx, 1
          202  +	jnc	L_copy_two           ; /* if len % 2 == 0 */
          203  +
          204  +	rep     movsw
          205  +	mov	al, [rsi]
          206  +	mov	[rdi], al
          207  +	inc	rdi
          208  +
          209  +	mov	rsi, r8           ; /* move in back to %rsi, toss from */
          210  +	jmp	L_while_test
          211  +
          212  +L_copy_two:
          213  +	rep     movsw
          214  +	mov	rsi, r8           ; /* move in back to %rsi, toss from */
          215  +	jmp	L_while_test
          216  +
          217  +ALIGN 4
          218  +L_check_dist_one:
          219  +	cmp	r15d, 1            ; /* if dist 1, is a memset */
          220  +	jne	L_check_window
          221  +	cmp	[rsp+40], rdi      ; /* if out == beg, outside window */
          222  +	je	L_check_window
          223  +
          224  +	mov	ecx, r14d         ; /* ecx = len */
          225  +	mov	al, [rdi-1]
          226  +	mov	ah, al
          227  +
          228  +	sar	ecx, 1
          229  +	jnc	L_set_two
          230  +	mov	[rdi], al
          231  +	inc	rdi
          232  +
          233  +L_set_two:
          234  +	rep     stosw
          235  +	jmp	L_while_test
          236  +
          237  +ALIGN 4
          238  +L_test_for_second_level_length:
          239  +	test	al, 64
          240  +	jnz	L_test_for_end_of_block ; /* if ((op & 64) != 0) */
          241  +
          242  +	xor	eax, eax
          243  +	inc	eax
          244  +	shl	eax, cl
          245  +	dec	eax
          246  +	and	eax, edx         ; /* eax &= hold */
          247  +	add	eax, r14d        ; /* eax += len */
          248  +	mov	eax, [rbp+rax*4] ; /* eax = lcode[val+(hold&mask[op])]*/
          249  +	jmp	L_dolen
          250  +
          251  +ALIGN 4
          252  +L_test_for_second_level_dist:
          253  +	test	al, 64
          254  +	jnz	L_invalid_distance_code ; /* if ((op & 64) != 0) */
          255  +
          256  +	xor	eax, eax
          257  +	inc	eax
          258  +	shl	eax, cl
          259  +	dec	eax
          260  +	and	eax, edx         ; /* eax &= hold */
          261  +	add	eax, r15d        ; /* eax += dist */
          262  +	mov	eax, [r11+rax*4] ; /* eax = dcode[val+(hold&mask[op])]*/
          263  +	jmp	L_dodist
          264  +
          265  +ALIGN 4
          266  +L_clip_window:
          267  +	mov	ecx, eax         ; /* ecx = nbytes */
          268  +	mov	eax, [rsp+92]     ; /* eax = wsize, prepare for dist cmp */
          269  +	neg	ecx                ; /* nbytes = -nbytes */
          270  +
          271  +	cmp	eax, r15d
          272  +	jb	L_invalid_distance_too_far ; /* if (dist > wsize) */
          273  +
          274  +	add	ecx, r15d         ; /* nbytes = dist - nbytes */
          275  +	cmp	dword ptr [rsp+96], 0
          276  +	jne	L_wrap_around_window ; /* if (write != 0) */
          277  +
          278  +	mov	rsi, [rsp+56]     ; /* from  = window */
          279  +	sub	eax, ecx         ; /* eax  -= nbytes */
          280  +	add	rsi, rax         ; /* from += wsize - nbytes */
          281  +
          282  +	mov	eax, r14d        ; /* eax = len */
          283  +	cmp	r14d, ecx
          284  +	jbe	L_do_copy           ; /* if (nbytes >= len) */
          285  +
          286  +	sub	eax, ecx         ; /* eax -= nbytes */
          287  +	rep     movsb
          288  +	mov	rsi, rdi
          289  +	sub	rsi, r15         ; /* from = &out[ -dist ] */
          290  +	jmp	L_do_copy
          291  +
          292  +ALIGN 4
          293  +L_wrap_around_window:
          294  +	mov	eax, [rsp+96]     ; /* eax = write */
          295  +	cmp	ecx, eax
          296  +	jbe	L_contiguous_in_window ; /* if (write >= nbytes) */
          297  +
          298  +	mov	esi, [rsp+92]     ; /* from  = wsize */
          299  +	add	rsi, [rsp+56]     ; /* from += window */
          300  +	add	rsi, rax         ; /* from += write */
          301  +	sub	rsi, rcx         ; /* from -= nbytes */
          302  +	sub	ecx, eax         ; /* nbytes -= write */
          303  +
          304  +	mov	eax, r14d        ; /* eax = len */
          305  +	cmp	eax, ecx
          306  +	jbe	L_do_copy           ; /* if (nbytes >= len) */
          307  +
          308  +	sub	eax, ecx         ; /* len -= nbytes */
          309  +	rep     movsb
          310  +	mov	rsi, [rsp+56]     ; /* from = window */
          311  +	mov	ecx, [rsp+96]     ; /* nbytes = write */
          312  +	cmp	eax, ecx
          313  +	jbe	L_do_copy           ; /* if (nbytes >= len) */
          314  +
          315  +	sub	eax, ecx         ; /* len -= nbytes */
          316  +	rep     movsb
          317  +	mov	rsi, rdi
          318  +	sub	rsi, r15         ; /* from = out - dist */
          319  +	jmp	L_do_copy
          320  +
          321  +ALIGN 4
          322  +L_contiguous_in_window:
          323  +	mov	rsi, [rsp+56]     ; /* rsi = window */
          324  +	add	rsi, rax
          325  +	sub	rsi, rcx         ; /* from += write - nbytes */
          326  +
          327  +	mov	eax, r14d        ; /* eax = len */
          328  +	cmp	eax, ecx
          329  +	jbe	L_do_copy           ; /* if (nbytes >= len) */
          330  +
          331  +	sub	eax, ecx         ; /* len -= nbytes */
          332  +	rep     movsb
          333  +	mov	rsi, rdi
          334  +	sub	rsi, r15         ; /* from = out - dist */
          335  +	jmp	L_do_copy           ; /* if (nbytes >= len) */
          336  +
          337  +ALIGN 4
          338  +L_do_copy:
          339  +	mov	ecx, eax         ; /* ecx = len */
          340  +	rep     movsb
          341  +
          342  +	mov	rsi, r8          ; /* move in back to %esi, toss from */
          343  +	jmp	L_while_test
          344  +
          345  +L_test_for_end_of_block:
          346  +	test	al, 32
          347  +	jz	L_invalid_literal_length_code
          348  +	mov	dword ptr [rsp+116], 1
          349  +	jmp	L_break_loop_with_status
          350  +
          351  +L_invalid_literal_length_code:
          352  +	mov	dword ptr [rsp+116], 2
          353  +	jmp	L_break_loop_with_status
          354  +
          355  +L_invalid_distance_code:
          356  +	mov	dword ptr [rsp+116], 3
          357  +	jmp	L_break_loop_with_status
          358  +
          359  +L_invalid_distance_too_far:
          360  +	mov	dword ptr [rsp+116], 4
          361  +	jmp	L_break_loop_with_status
          362  +
          363  +L_break_loop:
          364  +	mov	dword ptr [rsp+116], 0
          365  +
          366  +L_break_loop_with_status:
          367  +; /* put in, out, bits, and hold back into ar and pop esp */
          368  +	mov	[rsp+16], rsi     ; /* in */
          369  +	mov	[rsp+32], rdi     ; /* out */
          370  +	mov	[rsp+88], ebx     ; /* bits */
          371  +	mov	[rsp+80], rdx     ; /* hold */
          372  +
          373  +	mov	rax, [rsp]       ; /* restore rbp and rsp */
          374  +	mov	rbp, [rsp+8]
          375  +	mov	rsp, rax
          376  +
          377  +
          378  +
          379  +	mov rsi,[rsp-8]
          380  +	mov rdi,[rsp-16]
          381  +	mov r12,[rsp-24]
          382  +	mov r13,[rsp-32]
          383  +	mov r14,[rsp-40]
          384  +	mov r15,[rsp-48]
          385  +	mov rbx,[rsp-56]
          386  +
          387  +    ret 0
          388  +;          :
          389  +;          : "m" (ar)
          390  +;          : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi",
          391  +;            "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
          392  +;    );
          393  +
          394  +inffas8664fnc 	ENDP
          395  +;_TEXT	ENDS
          396  +END

Added compat/zlib/contrib/masmx64/readme.txt.

            1  +Summary
            2  +-------
            3  +This directory contains ASM implementations of the functions
            4  +longest_match() and inflate_fast(), for 64 bits x86 (both AMD64 and Intel EM64t),
            5  +for use with Microsoft Macro Assembler (x64) for AMD64 and Microsoft C++ 64 bits.
            6  +
            7  +gvmat64.asm is written by Gilles Vollant (2005), by using Brian Raiter 686/32 bits
            8  +   assembly optimized version from Jean-loup Gailly original longest_match function
            9  +
           10  +inffasx64.asm and inffas8664.c were written by Chris Anderson, by optimizing
           11  +   original function from Mark Adler
           12  +
           13  +Use instructions
           14  +----------------
           15  +Assemble the .asm files using MASM and put the object files into the zlib source
           16  +directory.  You can also get object files here:
           17  +
           18  +     http://www.winimage.com/zLibDll/zlib124_masm_obj.zip
           19  +
           20  +define ASMV and ASMINF in your project. Include inffas8664.c in your source tree,
           21  +and inffasx64.obj and gvmat64.obj as object to link.
           22  +
           23  +
           24  +Build instructions
           25  +------------------
           26  +run bld_64.bat with Microsoft Macro Assembler (x64) for AMD64 (ml64.exe)
           27  +
           28  +ml64.exe is given with Visual Studio 2005, Windows 2003 server DDK
           29  +
           30  +You can get Windows 2003 server DDK with ml64 and cl for AMD64 from
           31  +  http://www.microsoft.com/whdc/devtools/ddk/default.mspx for low price)

Added compat/zlib/contrib/masmx86/bld_ml32.bat.

            1  +ml /coff /Zi /c /Flmatch686.lst match686.asm
            2  +ml /coff /Zi /c /Flinffas32.lst inffas32.asm

Added compat/zlib/contrib/masmx86/inffas32.asm.

            1  +;/* inffas32.asm is a hand tuned assembler version of inffast.c -- fast decoding
            2  +; *
            3  +; * inffas32.asm is derivated from inffas86.c, with translation of assembly code
            4  +; *
            5  +; * Copyright (C) 1995-2003 Mark Adler
            6  +; * For conditions of distribution and use, see copyright notice in zlib.h
            7  +; *
            8  +; * Copyright (C) 2003 Chris Anderson <christop@charm.net>
            9  +; * Please use the copyright conditions above.
           10  +; *
           11  +; * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
           12  +; * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
           13  +; * the moment.  I have successfully compiled and tested this code with gcc2.96,
           14  +; * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
           15  +; * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
           16  +; * enabled.  I will attempt to merge the MMX code into this version.  Newer
           17  +; * versions of this and inffast.S can be found at
           18  +; * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
           19  +; *
           20  +; * 2005 : modification by Gilles Vollant
           21  +; */
           22  +; For Visual C++ 4.x and higher and ML 6.x and higher
           23  +;   ml.exe is in directory \MASM611C of Win95 DDK
           24  +;   ml.exe is also distributed in http://www.masm32.com/masmdl.htm
           25  +;    and in VC++2003 toolkit at http://msdn.microsoft.com/visualc/vctoolkit2003/
           26  +;
           27  +;
           28  +;   compile with command line option
           29  +;   ml  /coff /Zi /c /Flinffas32.lst inffas32.asm
           30  +
           31  +;   if you define NO_GZIP (see inflate.h), compile with
           32  +;   ml  /coff /Zi /c /Flinffas32.lst /DNO_GUNZIP inffas32.asm
           33  +
           34  +
           35  +; zlib122sup is 0 fort zlib 1.2.2.1 and lower
           36  +; zlib122sup is 8 fort zlib 1.2.2.2 and more (with addition of dmax and head
           37  +;        in inflate_state in inflate.h)
           38  +zlib1222sup      equ    8
           39  +
           40  +
           41  +IFDEF GUNZIP
           42  +  INFLATE_MODE_TYPE    equ 11
           43  +  INFLATE_MODE_BAD     equ 26
           44  +ELSE
           45  +  IFNDEF NO_GUNZIP
           46  +    INFLATE_MODE_TYPE    equ 11
           47  +    INFLATE_MODE_BAD     equ 26
           48  +  ELSE
           49  +    INFLATE_MODE_TYPE    equ 3
           50  +    INFLATE_MODE_BAD     equ 17
           51  +  ENDIF
           52  +ENDIF
           53  +
           54  +
           55  +; 75 "inffast.S"
           56  +;FILE "inffast.S"
           57  +
           58  +;;;GLOBAL _inflate_fast
           59  +
           60  +;;;SECTION .text
           61  +
           62  +
           63  +
           64  +	.586p
           65  +	.mmx
           66  +
           67  +	name	inflate_fast_x86
           68  +	.MODEL	FLAT
           69  +
           70  +_DATA			segment
           71  +inflate_fast_use_mmx:
           72  +	dd	1
           73  +
           74  +
           75  +_TEXT			segment
           76  +
           77  +
           78  +
           79  +ALIGN 4
           80  +	db	'Fast decoding Code from Chris Anderson'
           81  +	db	0
           82  +
           83  +ALIGN 4
           84  +invalid_literal_length_code_msg:
           85  +	db	'invalid literal/length code'
           86  +	db	0
           87  +
           88  +ALIGN 4
           89  +invalid_distance_code_msg:
           90  +	db	'invalid distance code'
           91  +	db	0
           92  +
           93  +ALIGN 4
           94  +invalid_distance_too_far_msg:
           95  +	db	'invalid distance too far back'
           96  +	db	0
           97  +
           98  +
           99  +ALIGN 4
          100  +inflate_fast_mask:
          101  +dd	0
          102  +dd	1
          103  +dd	3
          104  +dd	7
          105  +dd	15
          106  +dd	31
          107  +dd	63
          108  +dd	127
          109  +dd	255
          110  +dd	511
          111  +dd	1023
          112  +dd	2047
          113  +dd	4095
          114  +dd	8191
          115  +dd	16383
          116  +dd	32767
          117  +dd	65535
          118  +dd	131071
          119  +dd	262143
          120  +dd	524287
          121  +dd	1048575
          122  +dd	2097151
          123  +dd	4194303
          124  +dd	8388607
          125  +dd	16777215
          126  +dd	33554431
          127  +dd	67108863
          128  +dd	134217727
          129  +dd	268435455
          130  +dd	536870911
          131  +dd	1073741823
          132  +dd	2147483647
          133  +dd	4294967295
          134  +
          135  +
          136  +mode_state	 equ	0	;/* state->mode	*/
          137  +wsize_state	 equ	(32+zlib1222sup)	;/* state->wsize */
          138  +write_state	 equ	(36+4+zlib1222sup)	;/* state->write */
          139  +window_state	 equ	(40+4+zlib1222sup)	;/* state->window */
          140  +hold_state	 equ	(44+4+zlib1222sup)	;/* state->hold	*/
          141  +bits_state	 equ	(48+4+zlib1222sup)	;/* state->bits	*/
          142  +lencode_state	 equ	(64+4+zlib1222sup)	;/* state->lencode */
          143  +distcode_state	 equ	(68+4+zlib1222sup)	;/* state->distcode */
          144  +lenbits_state	 equ	(72+4+zlib1222sup)	;/* state->lenbits */
          145  +distbits_state	 equ	(76+4+zlib1222sup)	;/* state->distbits */
          146  +
          147  +
          148  +;;SECTION .text
          149  +; 205 "inffast.S"
          150  +;GLOBAL	inflate_fast_use_mmx
          151  +
          152  +;SECTION .data
          153  +
          154  +
          155  +; GLOBAL inflate_fast_use_mmx:object
          156  +;.size inflate_fast_use_mmx, 4
          157  +; 226 "inffast.S"
          158  +;SECTION .text
          159  +
          160  +ALIGN 4
          161  +_inflate_fast proc near
          162  +.FPO (16, 4, 0, 0, 1, 0)
          163  +	push  edi
          164  +	push  esi
          165  +	push  ebp
          166  +	push  ebx
          167  +	pushfd
          168  +	sub  esp,64
          169  +	cld
          170  +
          171  +
          172  +
          173  +
          174  +	mov  esi, [esp+88]
          175  +	mov  edi, [esi+28]
          176  +
          177  +
          178  +
          179  +
          180  +
          181  +
          182  +
          183  +	mov  edx, [esi+4]
          184  +	mov  eax, [esi+0]
          185  +
          186  +	add  edx,eax
          187  +	sub  edx,11
          188  +
          189  +	mov  [esp+44],eax
          190  +	mov  [esp+20],edx
          191  +
          192  +	mov  ebp, [esp+92]
          193  +	mov  ecx, [esi+16]
          194  +	mov  ebx, [esi+12]
          195  +
          196  +	sub  ebp,ecx
          197  +	neg  ebp
          198  +	add  ebp,ebx
          199  +
          200  +	sub  ecx,257
          201  +	add  ecx,ebx
          202  +
          203  +	mov  [esp+60],ebx
          204  +	mov  [esp+40],ebp
          205  +	mov  [esp+16],ecx
          206  +; 285 "inffast.S"
          207  +	mov  eax, [edi+lencode_state]
          208  +	mov  ecx, [edi+distcode_state]
          209  +
          210  +	mov  [esp+8],eax
          211  +	mov  [esp+12],ecx
          212  +
          213  +	mov  eax,1
          214  +	mov  ecx, [edi+lenbits_state]
          215  +	shl  eax,cl
          216  +	dec  eax
          217  +	mov  [esp+0],eax
          218  +
          219  +	mov  eax,1
          220  +	mov  ecx, [edi+distbits_state]
          221  +	shl  eax,cl
          222  +	dec  eax
          223  +	mov  [esp+4],eax
          224  +
          225  +	mov  eax, [edi+wsize_state]
          226  +	mov  ecx, [edi+write_state]
          227  +	mov  edx, [edi+window_state]
          228  +
          229  +	mov  [esp+52],eax
          230  +	mov  [esp+48],ecx
          231  +	mov  [esp+56],edx
          232  +
          233  +	mov  ebp, [edi+hold_state]
          234  +	mov  ebx, [edi+bits_state]
          235  +; 321 "inffast.S"
          236  +	mov  esi, [esp+44]
          237  +	mov  ecx, [esp+20]
          238  +	cmp  ecx,esi
          239  +	ja   L_align_long
          240  +
          241  +	add  ecx,11
          242  +	sub  ecx,esi
          243  +	mov  eax,12
          244  +	sub  eax,ecx
          245  +	lea  edi, [esp+28]
          246  +	rep movsb
          247  +	mov  ecx,eax
          248  +	xor  eax,eax
          249  +	rep stosb
          250  +	lea  esi, [esp+28]
          251  +	mov  [esp+20],esi
          252  +	jmp  L_is_aligned
          253  +
          254  +
          255  +L_align_long:
          256  +	test  esi,3
          257  +	jz   L_is_aligned
          258  +	xor  eax,eax
          259  +	mov  al, [esi]
          260  +	inc  esi
          261  +	mov  ecx,ebx
          262  +	add  ebx,8
          263  +	shl  eax,cl
          264  +	or  ebp,eax
          265  +	jmp L_align_long
          266  +
          267  +L_is_aligned:
          268  +	mov  edi, [esp+60]
          269  +; 366 "inffast.S"
          270  +L_check_mmx:
          271  +	cmp  dword ptr [inflate_fast_use_mmx],2
          272  +	je   L_init_mmx
          273  +	ja   L_do_loop
          274  +
          275  +	push  eax
          276  +	push  ebx
          277  +	push  ecx
          278  +	push  edx
          279  +	pushfd
          280  +	mov  eax, [esp]
          281  +	xor  dword ptr [esp],0200000h
          282  +
          283  +
          284  +
          285  +
          286  +	popfd
          287  +	pushfd
          288  +	pop  edx
          289  +	xor  edx,eax
          290  +	jz   L_dont_use_mmx
          291  +	xor  eax,eax
          292  +	cpuid
          293  +	cmp  ebx,0756e6547h
          294  +	jne  L_dont_use_mmx
          295  +	cmp  ecx,06c65746eh
          296  +	jne  L_dont_use_mmx
          297  +	cmp  edx,049656e69h
          298  +	jne  L_dont_use_mmx
          299  +	mov  eax,1
          300  +	cpuid
          301  +	shr  eax,8
          302  +	and  eax,15
          303  +	cmp  eax,6
          304  +	jne  L_dont_use_mmx
          305  +	test  edx,0800000h
          306  +	jnz  L_use_mmx
          307  +	jmp  L_dont_use_mmx
          308  +L_use_mmx:
          309  +	mov  dword ptr [inflate_fast_use_mmx],2
          310  +	jmp  L_check_mmx_pop
          311  +L_dont_use_mmx:
          312  +	mov  dword ptr [inflate_fast_use_mmx],3
          313  +L_check_mmx_pop:
          314  +	pop  edx
          315  +	pop  ecx
          316  +	pop  ebx
          317  +	pop  eax
          318  +	jmp  L_check_mmx
          319  +; 426 "inffast.S"
          320  +ALIGN 4
          321  +L_do_loop:
          322  +; 437 "inffast.S"
          323  +	cmp  bl,15
          324  +	ja   L_get_length_code
          325  +
          326  +	xor  eax,eax
          327  +	lodsw
          328  +	mov  cl,bl
          329  +	add  bl,16
          330  +	shl  eax,cl
          331  +	or  ebp,eax
          332  +
          333  +L_get_length_code:
          334  +	mov  edx, [esp+0]
          335  +	mov  ecx, [esp+8]
          336  +	and  edx,ebp
          337  +	mov  eax, [ecx+edx*4]
          338  +
          339  +L_dolen:
          340  +
          341  +
          342  +
          343  +
          344  +
          345  +
          346  +	mov  cl,ah
          347  +	sub  bl,ah
          348  +	shr  ebp,cl
          349  +
          350  +
          351  +
          352  +
          353  +
          354  +
          355  +	test  al,al
          356  +	jnz   L_test_for_length_base
          357  +
          358  +	shr  eax,16
          359  +	stosb
          360  +
          361  +L_while_test:
          362  +
          363  +
          364  +	cmp  [esp+16],edi
          365  +	jbe  L_break_loop
          366  +
          367  +	cmp  [esp+20],esi
          368  +	ja   L_do_loop
          369  +	jmp  L_break_loop
          370  +
          371  +L_test_for_length_base:
          372  +; 502 "inffast.S"
          373  +	mov  edx,eax
          374  +	shr  edx,16
          375  +	mov  cl,al
          376  +
          377  +	test  al,16
          378  +	jz   L_test_for_second_level_length
          379  +	and  cl,15
          380  +	jz   L_save_len
          381  +	cmp  bl,cl
          382  +	jae  L_add_bits_to_len
          383  +
          384  +	mov  ch,cl
          385  +	xor  eax,eax
          386  +	lodsw
          387  +	mov  cl,bl
          388  +	add  bl,16
          389  +	shl  eax,cl
          390  +	or  ebp,eax
          391  +	mov  cl,ch
          392  +
          393  +L_add_bits_to_len:
          394  +	mov  eax,1
          395  +	shl  eax,cl
          396  +	dec  eax
          397  +	sub  bl,cl
          398  +	and  eax,ebp
          399  +	shr  ebp,cl
          400  +	add  edx,eax
          401  +
          402  +L_save_len:
          403  +	mov  [esp+24],edx
          404  +
          405  +
          406  +L_decode_distance:
          407  +; 549 "inffast.S"
          408  +	cmp  bl,15
          409  +	ja   L_get_distance_code
          410  +
          411  +	xor  eax,eax
          412  +	lodsw
          413  +	mov  cl,bl
          414  +	add  bl,16
          415  +	shl  eax,cl
          416  +	or  ebp,eax
          417  +
          418  +L_get_distance_code:
          419  +	mov  edx, [esp+4]
          420  +	mov  ecx, [esp+12]
          421  +	and  edx,ebp
          422  +	mov  eax, [ecx+edx*4]
          423  +
          424  +
          425  +L_dodist:
          426  +	mov  edx,eax
          427  +	shr  edx,16
          428  +	mov  cl,ah
          429  +	sub  bl,ah
          430  +	shr  ebp,cl
          431  +; 584 "inffast.S"
          432  +	mov  cl,al
          433  +
          434  +	test  al,16
          435  +	jz  L_test_for_second_level_dist
          436  +	and  cl,15
          437  +	jz  L_check_dist_one
          438  +	cmp  bl,cl
          439  +	jae  L_add_bits_to_dist
          440  +
          441  +	mov  ch,cl
          442  +	xor  eax,eax
          443  +	lodsw
          444  +	mov  cl,bl
          445  +	add  bl,16
          446  +	shl  eax,cl
          447  +	or  ebp,eax
          448  +	mov  cl,ch
          449  +
          450  +L_add_bits_to_dist:
          451  +	mov  eax,1
          452  +	shl  eax,cl
          453  +	dec  eax
          454  +	sub  bl,cl
          455  +	and  eax,ebp
          456  +	shr  ebp,cl
          457  +	add  edx,eax
          458  +	jmp  L_check_window
          459  +
          460  +L_check_window:
          461  +; 625 "inffast.S"
          462  +	mov  [esp+44],esi
          463  +	mov  eax,edi
          464  +	sub  eax, [esp+40]
          465  +
          466  +	cmp  eax,edx
          467  +	jb   L_clip_window
          468  +
          469  +	mov  ecx, [esp+24]
          470  +	mov  esi,edi
          471  +	sub  esi,edx
          472  +
          473  +	sub  ecx,3
          474  +	mov  al, [esi]
          475  +	mov  [edi],al
          476  +	mov  al, [esi+1]
          477  +	mov  dl, [esi+2]
          478  +	add  esi,3
          479  +	mov  [edi+1],al
          480  +	mov  [edi+2],dl
          481  +	add  edi,3
          482  +	rep movsb
          483  +
          484  +	mov  esi, [esp+44]
          485  +	jmp  L_while_test
          486  +
          487  +ALIGN 4
          488  +L_check_dist_one:
          489  +	cmp  edx,1
          490  +	jne  L_check_window
          491  +	cmp  [esp+40],edi
          492  +	je  L_check_window
          493  +
          494  +	dec  edi
          495  +	mov  ecx, [esp+24]
          496  +	mov  al, [edi]
          497  +	sub  ecx,3
          498  +
          499  +	mov  [edi+1],al
          500  +	mov  [edi+2],al
          501  +	mov  [edi+3],al
          502  +	add  edi,4
          503  +	rep stosb
          504  +
          505  +	jmp  L_while_test
          506  +
          507  +ALIGN 4
          508  +L_test_for_second_level_length:
          509  +
          510  +
          511  +
          512  +
          513  +	test  al,64
          514  +	jnz   L_test_for_end_of_block
          515  +
          516  +	mov  eax,1
          517  +	shl  eax,cl
          518  +	dec  eax
          519  +	and  eax,ebp
          520  +	add  eax,edx
          521  +	mov  edx, [esp+8]
          522  +	mov  eax, [edx+eax*4]
          523  +	jmp  L_dolen
          524  +
          525  +ALIGN 4
          526  +L_test_for_second_level_dist:
          527  +
          528  +
          529  +
          530  +
          531  +	test  al,64
          532  +	jnz   L_invalid_distance_code
          533  +
          534  +	mov  eax,1
          535  +	shl  eax,cl
          536  +	dec  eax
          537  +	and  eax,ebp
          538  +	add  eax,edx
          539  +	mov  edx, [esp+12]
          540  +	mov  eax, [edx+eax*4]
          541  +	jmp  L_dodist
          542  +
          543  +ALIGN 4
          544  +L_clip_window:
          545  +; 721 "inffast.S"
          546  +	mov  ecx,eax
          547  +	mov  eax, [esp+52]
          548  +	neg  ecx
          549  +	mov  esi, [esp+56]
          550  +
          551  +	cmp  eax,edx
          552  +	jb   L_invalid_distance_too_far
          553  +
          554  +	add  ecx,edx
          555  +	cmp  dword ptr [esp+48],0
          556  +	jne  L_wrap_around_window
          557  +
          558  +	sub  eax,ecx
          559  +	add  esi,eax
          560  +; 749 "inffast.S"
          561  +	mov  eax, [esp+24]
          562  +	cmp  eax,ecx
          563  +	jbe  L_do_copy1
          564  +
          565  +	sub  eax,ecx
          566  +	rep movsb
          567  +	mov  esi,edi
          568  +	sub  esi,edx
          569  +	jmp  L_do_copy1
          570  +
          571  +	cmp  eax,ecx
          572  +	jbe  L_do_copy1
          573  +
          574  +	sub  eax,ecx
          575  +	rep movsb
          576  +	mov  esi,edi
          577  +	sub  esi,edx
          578  +	jmp  L_do_copy1
          579  +
          580  +L_wrap_around_window:
          581  +; 793 "inffast.S"
          582  +	mov  eax, [esp+48]
          583  +	cmp  ecx,eax
          584  +	jbe  L_contiguous_in_window
          585  +
          586  +	add  esi, [esp+52]
          587  +	add  esi,eax
          588  +	sub  esi,ecx
          589  +	sub  ecx,eax
          590  +
          591  +
          592  +	mov  eax, [esp+24]
          593  +	cmp  eax,ecx
          594  +	jbe  L_do_copy1
          595  +
          596  +	sub  eax,ecx
          597  +	rep movsb
          598  +	mov  esi, [esp+56]
          599  +	mov  ecx, [esp+48]
          600  +	cmp  eax,ecx
          601  +	jbe  L_do_copy1
          602  +
          603  +	sub  eax,ecx
          604  +	rep movsb
          605  +	mov  esi,edi
          606  +	sub  esi,edx
          607  +	jmp  L_do_copy1
          608  +
          609  +L_contiguous_in_window:
          610  +; 836 "inffast.S"
          611  +	add  esi,eax
          612  +	sub  esi,ecx
          613  +
          614  +
          615  +	mov  eax, [esp+24]
          616  +	cmp  eax,ecx
          617  +	jbe  L_do_copy1
          618  +
          619  +	sub  eax,ecx
          620  +	rep movsb
          621  +	mov  esi,edi
          622  +	sub  esi,edx
          623  +
          624  +L_do_copy1:
          625  +; 862 "inffast.S"
          626  +	mov  ecx,eax
          627  +	rep movsb
          628  +
          629  +	mov  esi, [esp+44]
          630  +	jmp  L_while_test
          631  +; 878 "inffast.S"
          632  +ALIGN 4
          633  +L_init_mmx:
          634  +	emms
          635  +
          636  +
          637  +
          638  +
          639  +
          640  +	movd mm0,ebp
          641  +	mov  ebp,ebx
          642  +; 896 "inffast.S"
          643  +	movd mm4,dword ptr [esp+0]
          644  +	movq mm3,mm4
          645  +	movd mm5,dword ptr [esp+4]
          646  +	movq mm2,mm5
          647  +	pxor mm1,mm1
          648  +	mov  ebx, [esp+8]
          649  +	jmp  L_do_loop_mmx
          650  +
          651  +ALIGN 4
          652  +L_do_loop_mmx:
          653  +	psrlq mm0,mm1
          654  +
          655  +	cmp  ebp,32
          656  +	ja  L_get_length_code_mmx
          657  +
          658  +	movd mm6,ebp
          659  +	movd mm7,dword ptr [esi]
          660  +	add  esi,4
          661  +	psllq mm7,mm6
          662  +	add  ebp,32
          663  +	por mm0,mm7
          664  +
          665  +L_get_length_code_mmx:
          666  +	pand mm4,mm0
          667  +	movd eax,mm4
          668  +	movq mm4,mm3
          669  +	mov  eax, [ebx+eax*4]
          670  +
          671  +L_dolen_mmx:
          672  +	movzx  ecx,ah
          673  +	movd mm1,ecx
          674  +	sub  ebp,ecx
          675  +
          676  +	test  al,al
          677  +	jnz L_test_for_length_base_mmx
          678  +
          679  +	shr  eax,16
          680  +	stosb
          681  +
          682  +L_while_test_mmx:
          683  +
          684  +
          685  +	cmp  [esp+16],edi
          686  +	jbe L_break_loop
          687  +
          688  +	cmp  [esp+20],esi
          689  +	ja L_do_loop_mmx
          690  +	jmp L_break_loop
          691  +
          692  +L_test_for_length_base_mmx:
          693  +
          694  +	mov  edx,eax
          695  +	shr  edx,16
          696  +
          697  +	test  al,16
          698  +	jz  L_test_for_second_level_length_mmx
          699  +	and  eax,15
          700  +	jz L_decode_distance_mmx
          701  +
          702  +	psrlq mm0,mm1
          703  +	movd mm1,eax
          704  +	movd ecx,mm0
          705  +	sub  ebp,eax
          706  +	and  ecx, [inflate_fast_mask+eax*4]
          707  +	add  edx,ecx
          708  +
          709  +L_decode_distance_mmx:
          710  +	psrlq mm0,mm1
          711  +
          712  +	cmp  ebp,32
          713  +	ja L_get_dist_code_mmx
          714  +
          715  +	movd mm6,ebp
          716  +	movd mm7,dword ptr [esi]
          717  +	add  esi,4
          718  +	psllq mm7,mm6
          719  +	add  ebp,32
          720  +	por mm0,mm7
          721  +
          722  +L_get_dist_code_mmx:
          723  +	mov  ebx, [esp+12]
          724  +	pand mm5,mm0
          725  +	movd eax,mm5
          726  +	movq mm5,mm2
          727  +	mov  eax, [ebx+eax*4]
          728  +
          729  +L_dodist_mmx:
          730  +
          731  +	movzx  ecx,ah
          732  +	mov  ebx,eax
          733  +	shr  ebx,16
          734  +	sub  ebp,ecx
          735  +	movd mm1,ecx
          736  +
          737  +	test  al,16
          738  +	jz L_test_for_second_level_dist_mmx
          739  +	and  eax,15
          740  +	jz L_check_dist_one_mmx
          741  +
          742  +L_add_bits_to_dist_mmx:
          743  +	psrlq mm0,mm1
          744  +	movd mm1,eax
          745  +	movd ecx,mm0
          746  +	sub  ebp,eax
          747  +	and  ecx, [inflate_fast_mask+eax*4]
          748  +	add  ebx,ecx
          749  +
          750  +L_check_window_mmx:
          751  +	mov  [esp+44],esi
          752  +	mov  eax,edi
          753  +	sub  eax, [esp+40]
          754  +
          755  +	cmp  eax,ebx
          756  +	jb L_clip_window_mmx
          757  +
          758  +	mov  ecx,edx
          759  +	mov  esi,edi
          760  +	sub  esi,ebx
          761  +
          762  +	sub  ecx,3
          763  +	mov  al, [esi]
          764  +	mov  [edi],al
          765  +	mov  al, [esi+1]
          766  +	mov  dl, [esi+2]
          767  +	add  esi,3
          768  +	mov  [edi+1],al
          769  +	mov  [edi+2],dl
          770  +	add  edi,3
          771  +	rep movsb
          772  +
          773  +	mov  esi, [esp+44]
          774  +	mov  ebx, [esp+8]
          775  +	jmp  L_while_test_mmx
          776  +
          777  +ALIGN 4
          778  +L_check_dist_one_mmx:
          779  +	cmp  ebx,1
          780  +	jne  L_check_window_mmx
          781  +	cmp  [esp+40],edi
          782  +	je   L_check_window_mmx
          783  +
          784  +	dec  edi
          785  +	mov  ecx,edx
          786  +	mov  al, [edi]
          787  +	sub  ecx,3
          788  +
          789  +	mov  [edi+1],al
          790  +	mov  [edi+2],al
          791  +	mov  [edi+3],al
          792  +	add  edi,4
          793  +	rep stosb
          794  +
          795  +	mov  ebx, [esp+8]
          796  +	jmp  L_while_test_mmx
          797  +
          798  +ALIGN 4
          799  +L_test_for_second_level_length_mmx:
          800  +	test  al,64
          801  +	jnz L_test_for_end_of_block
          802  +
          803  +	and  eax,15
          804  +	psrlq mm0,mm1
          805  +	movd ecx,mm0
          806  +	and  ecx, [inflate_fast_mask+eax*4]
          807  +	add  ecx,edx
          808  +	mov  eax, [ebx+ecx*4]
          809  +	jmp L_dolen_mmx
          810  +
          811  +ALIGN 4
          812  +L_test_for_second_level_dist_mmx:
          813  +	test  al,64
          814  +	jnz L_invalid_distance_code
          815  +
          816  +	and  eax,15
          817  +	psrlq mm0,mm1
          818  +	movd ecx,mm0
          819  +	and  ecx, [inflate_fast_mask+eax*4]
          820  +	mov  eax, [esp+12]
          821  +	add  ecx,ebx
          822  +	mov  eax, [eax+ecx*4]
          823  +	jmp  L_dodist_mmx
          824  +
          825  +ALIGN 4
          826  +L_clip_window_mmx:
          827  +
          828  +	mov  ecx,eax
          829  +	mov  eax, [esp+52]
          830  +	neg  ecx
          831  +	mov  esi, [esp+56]
          832  +
          833  +	cmp  eax,ebx
          834  +	jb  L_invalid_distance_too_far
          835  +
          836  +	add  ecx,ebx
          837  +	cmp  dword ptr [esp+48],0
          838  +	jne  L_wrap_around_window_mmx
          839  +
          840  +	sub  eax,ecx
          841  +	add  esi,eax
          842  +
          843  +	cmp  edx,ecx
          844  +	jbe  L_do_copy1_mmx
          845  +
          846  +	sub  edx,ecx
          847  +	rep movsb
          848  +	mov  esi,edi
          849  +	sub  esi,ebx
          850  +	jmp  L_do_copy1_mmx
          851  +
          852  +	cmp  edx,ecx
          853  +	jbe  L_do_copy1_mmx
          854  +
          855  +	sub  edx,ecx
          856  +	rep movsb
          857  +	mov  esi,edi
          858  +	sub  esi,ebx
          859  +	jmp  L_do_copy1_mmx
          860  +
          861  +L_wrap_around_window_mmx:
          862  +
          863  +	mov  eax, [esp+48]
          864  +	cmp  ecx,eax
          865  +	jbe  L_contiguous_in_window_mmx
          866  +
          867  +	add  esi, [esp+52]
          868  +	add  esi,eax
          869  +	sub  esi,ecx
          870  +	sub  ecx,eax
          871  +
          872  +
          873  +	cmp  edx,ecx
          874  +	jbe  L_do_copy1_mmx
          875  +
          876  +	sub  edx,ecx
          877  +	rep movsb
          878  +	mov  esi, [esp+56]
          879  +	mov  ecx, [esp+48]
          880  +	cmp  edx,ecx
          881  +	jbe  L_do_copy1_mmx
          882  +
          883  +	sub  edx,ecx
          884  +	rep movsb
          885  +	mov  esi,edi
          886  +	sub  esi,ebx
          887  +	jmp  L_do_copy1_mmx
          888  +
          889  +L_contiguous_in_window_mmx:
          890  +
          891  +	add  esi,eax
          892  +	sub  esi,ecx
          893  +
          894  +
          895  +	cmp  edx,ecx
          896  +	jbe  L_do_copy1_mmx
          897  +
          898  +	sub  edx,ecx
          899  +	rep movsb
          900  +	mov  esi,edi
          901  +	sub  esi,ebx
          902  +
          903  +L_do_copy1_mmx:
          904  +
          905  +
          906  +	mov  ecx,edx
          907  +	rep movsb
          908  +
          909  +	mov  esi, [esp+44]
          910  +	mov  ebx, [esp+8]
          911  +	jmp  L_while_test_mmx
          912  +; 1174 "inffast.S"
          913  +L_invalid_distance_code:
          914  +
          915  +
          916  +
          917  +
          918  +
          919  +	mov  ecx, invalid_distance_code_msg
          920  +	mov  edx,INFLATE_MODE_BAD
          921  +	jmp  L_update_stream_state
          922  +
          923  +L_test_for_end_of_block:
          924  +
          925  +
          926  +
          927  +
          928  +
          929  +	test  al,32
          930  +	jz  L_invalid_literal_length_code
          931  +
          932  +	mov  ecx,0
          933  +	mov  edx,INFLATE_MODE_TYPE
          934  +	jmp  L_update_stream_state
          935  +
          936  +L_invalid_literal_length_code:
          937  +
          938  +
          939  +
          940  +
          941  +
          942  +	mov  ecx, invalid_literal_length_code_msg
          943  +	mov  edx,INFLATE_MODE_BAD
          944  +	jmp  L_update_stream_state
          945  +
          946  +L_invalid_distance_too_far:
          947  +
          948  +
          949  +
          950  +	mov  esi, [esp+44]
          951  +	mov  ecx, invalid_distance_too_far_msg
          952  +	mov  edx,INFLATE_MODE_BAD
          953  +	jmp  L_update_stream_state
          954  +
          955  +L_update_stream_state:
          956  +
          957  +	mov  eax, [esp+88]
          958  +	test  ecx,ecx
          959  +	jz  L_skip_msg
          960  +	mov  [eax+24],ecx
          961  +L_skip_msg:
          962  +	mov  eax, [eax+28]
          963  +	mov  [eax+mode_state],edx
          964  +	jmp  L_break_loop
          965  +
          966  +ALIGN 4
          967  +L_break_loop:
          968  +; 1243 "inffast.S"
          969  +	cmp  dword ptr [inflate_fast_use_mmx],2
          970  +	jne  L_update_next_in
          971  +
          972  +
          973  +
          974  +	mov  ebx,ebp
          975  +
          976  +L_update_next_in:
          977  +; 1266 "inffast.S"
          978  +	mov  eax, [esp+88]
          979  +	mov  ecx,ebx
          980  +	mov  edx, [eax+28]
          981  +	shr  ecx,3
          982  +	sub  esi,ecx
          983  +	shl  ecx,3
          984  +	sub  ebx,ecx
          985  +	mov  [eax+12],edi
          986  +	mov  [edx+bits_state],ebx
          987  +	mov  ecx,ebx
          988  +
          989  +	lea  ebx, [esp+28]
          990  +	cmp  [esp+20],ebx
          991  +	jne  L_buf_not_used
          992  +
          993  +	sub  esi,ebx
          994  +	mov  ebx, [eax+0]
          995  +	mov  [esp+20],ebx
          996  +	add  esi,ebx
          997  +	mov  ebx, [eax+4]
          998  +	sub  ebx,11
          999  +	add  [esp+20],ebx
         1000  +
         1001  +L_buf_not_used:
         1002  +	mov  [eax+0],esi
         1003  +
         1004  +	mov  ebx,1
         1005  +	shl  ebx,cl
         1006  +	dec  ebx
         1007  +
         1008  +
         1009  +
         1010  +
         1011  +
         1012  +	cmp  dword ptr [inflate_fast_use_mmx],2
         1013  +	jne  L_update_hold
         1014  +
         1015  +
         1016  +
         1017  +	psrlq mm0,mm1
         1018  +	movd ebp,mm0
         1019  +
         1020  +	emms
         1021  +
         1022  +L_update_hold:
         1023  +
         1024  +
         1025  +
         1026  +	and  ebp,ebx
         1027  +	mov  [edx+hold_state],ebp
         1028  +
         1029  +
         1030  +
         1031  +
         1032  +	mov  ebx, [esp+20]
         1033  +	cmp  ebx,esi
         1034  +	jbe  L_last_is_smaller
         1035  +
         1036  +	sub  ebx,esi
         1037  +	add  ebx,11
         1038  +	mov  [eax+4],ebx
         1039  +	jmp  L_fixup_out
         1040  +L_last_is_smaller:
         1041  +	sub  esi,ebx
         1042  +	neg  esi
         1043  +	add  esi,11
         1044  +	mov  [eax+4],esi
         1045  +
         1046  +
         1047  +
         1048  +
         1049  +L_fixup_out:
         1050  +
         1051  +	mov  ebx, [esp+16]
         1052  +	cmp  ebx,edi
         1053  +	jbe  L_end_is_smaller
         1054  +
         1055  +	sub  ebx,edi
         1056  +	add  ebx,257
         1057  +	mov  [eax+16],ebx
         1058  +	jmp  L_done
         1059  +L_end_is_smaller:
         1060  +	sub  edi,ebx
         1061  +	neg  edi
         1062  +	add  edi,257
         1063  +	mov  [eax+16],edi
         1064  +
         1065  +
         1066  +
         1067  +
         1068  +
         1069  +L_done:
         1070  +	add  esp,64
         1071  +	popfd
         1072  +	pop  ebx
         1073  +	pop  ebp
         1074  +	pop  esi
         1075  +	pop  edi
         1076  +	ret
         1077  +_inflate_fast endp
         1078  +
         1079  +_TEXT	ends
         1080  +end

Added compat/zlib/contrib/masmx86/match686.asm.

            1  +; match686.asm -- Asm portion of the optimized longest_match for 32 bits x86
            2  +; Copyright (C) 1995-1996 Jean-loup Gailly, Brian Raiter and Gilles Vollant.
            3  +; File written by Gilles Vollant, by converting match686.S from Brian Raiter
            4  +; for MASM. This is as assembly version of longest_match
            5  +;  from Jean-loup Gailly in deflate.c
            6  +;
            7  +;         http://www.zlib.net
            8  +;         http://www.winimage.com/zLibDll
            9  +;         http://www.muppetlabs.com/~breadbox/software/assembly.html
           10  +;
           11  +; For Visual C++ 4.x and higher and ML 6.x and higher
           12  +;   ml.exe is distributed in
           13  +;  http://www.microsoft.com/downloads/details.aspx?FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64
           14  +;
           15  +; this file contain two implementation of longest_match
           16  +;
           17  +;  this longest_match was written by Brian raiter (1998), optimized for Pentium Pro
           18  +;   (and the faster known version of match_init on modern Core 2 Duo and AMD Phenom)
           19  +;
           20  +;  for using an assembly version of longest_match, you need define ASMV in project
           21  +;
           22  +;    compile the asm file running
           23  +;           ml /coff /Zi /c /Flmatch686.lst match686.asm
           24  +;    and do not include match686.obj in your project
           25  +;
           26  +; note: contrib of zLib 1.2.3 and earlier contained both a deprecated version for
           27  +;  Pentium (prior Pentium Pro) and this version for Pentium Pro and modern processor
           28  +;  with autoselect (with cpu detection code)
           29  +;  if you want support the old pentium optimization, you can still use these version
           30  +;
           31  +; this file is not optimized for old pentium, but it compatible with all x86 32 bits
           32  +; processor (starting 80386)
           33  +;
           34  +;
           35  +; see below : zlib1222add must be adjuster if you use a zlib version < 1.2.2.2
           36  +
           37  +;uInt longest_match(s, cur_match)
           38  +;    deflate_state *s;
           39  +;    IPos cur_match;                             /* current match */
           40  +
           41  +    NbStack         equ     76
           42  +    cur_match       equ     dword ptr[esp+NbStack-0]
           43  +    str_s           equ     dword ptr[esp+NbStack-4]
           44  +; 5 dword on top (ret,ebp,esi,edi,ebx)
           45  +    adrret          equ     dword ptr[esp+NbStack-8]
           46  +    pushebp         equ     dword ptr[esp+NbStack-12]
           47  +    pushedi         equ     dword ptr[esp+NbStack-16]
           48  +    pushesi         equ     dword ptr[esp+NbStack-20]
           49  +    pushebx         equ     dword ptr[esp+NbStack-24]
           50  +
           51  +    chain_length    equ     dword ptr [esp+NbStack-28]
           52  +    limit           equ     dword ptr [esp+NbStack-32]
           53  +    best_len        equ     dword ptr [esp+NbStack-36]
           54  +    window          equ     dword ptr [esp+NbStack-40]
           55  +    prev            equ     dword ptr [esp+NbStack-44]
           56  +    scan_start      equ      word ptr [esp+NbStack-48]
           57  +    wmask           equ     dword ptr [esp+NbStack-52]
           58  +    match_start_ptr equ     dword ptr [esp+NbStack-56]
           59  +    nice_match      equ     dword ptr [esp+NbStack-60]
           60  +    scan            equ     dword ptr [esp+NbStack-64]
           61  +
           62  +    windowlen       equ     dword ptr [esp+NbStack-68]
           63  +    match_start     equ     dword ptr [esp+NbStack-72]
           64  +    strend          equ     dword ptr [esp+NbStack-76]
           65  +    NbStackAdd      equ     (NbStack-24)
           66  +
           67  +    .386p
           68  +
           69  +    name    gvmatch
           70  +    .MODEL  FLAT
           71  +
           72  +
           73  +
           74  +;  all the +zlib1222add offsets are due to the addition of fields
           75  +;  in zlib in the deflate_state structure since the asm code was first written
           76  +;  (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
           77  +;  (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
           78  +;  if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
           79  +
           80  +    zlib1222add         equ     8
           81  +
           82  +;  Note : these value are good with a 8 bytes boundary pack structure
           83  +    dep_chain_length    equ     74h+zlib1222add
           84  +    dep_window          equ     30h+zlib1222add
           85  +    dep_strstart        equ     64h+zlib1222add
           86  +    dep_prev_length     equ     70h+zlib1222add
           87  +    dep_nice_match      equ     88h+zlib1222add
           88  +    dep_w_size          equ     24h+zlib1222add
           89  +    dep_prev            equ     38h+zlib1222add
           90  +    dep_w_mask          equ     2ch+zlib1222add
           91  +    dep_good_match      equ     84h+zlib1222add
           92  +    dep_match_start     equ     68h+zlib1222add
           93  +    dep_lookahead       equ     6ch+zlib1222add
           94  +
           95  +
           96  +_TEXT                   segment
           97  +
           98  +IFDEF NOUNDERLINE
           99  +            public  longest_match
          100  +            public  match_init
          101  +ELSE
          102  +            public  _longest_match
          103  +            public  _match_init
          104  +ENDIF
          105  +
          106  +    MAX_MATCH           equ     258
          107  +    MIN_MATCH           equ     3
          108  +    MIN_LOOKAHEAD       equ     (MAX_MATCH+MIN_MATCH+1)
          109  +
          110  +
          111  +
          112  +MAX_MATCH       equ     258
          113  +MIN_MATCH       equ     3
          114  +MIN_LOOKAHEAD   equ     (MAX_MATCH + MIN_MATCH + 1)
          115  +MAX_MATCH_8_     equ     ((MAX_MATCH + 7) AND 0FFF0h)
          116  +
          117  +
          118  +;;; stack frame offsets
          119  +
          120  +chainlenwmask   equ  esp + 0    ; high word: current chain len
          121  +                    ; low word: s->wmask
          122  +window      equ  esp + 4    ; local copy of s->window
          123  +windowbestlen   equ  esp + 8    ; s->window + bestlen
          124  +scanstart   equ  esp + 16   ; first two bytes of string
          125  +scanend     equ  esp + 12   ; last two bytes of string
          126  +scanalign   equ  esp + 20   ; dword-misalignment of string
          127  +nicematch   equ  esp + 24   ; a good enough match size
          128  +bestlen     equ  esp + 28   ; size of best match so far
          129  +scan        equ  esp + 32   ; ptr to string wanting match
          130  +
          131  +LocalVarsSize   equ 36
          132  +;   saved ebx   byte esp + 36
          133  +;   saved edi   byte esp + 40
          134  +;   saved esi   byte esp + 44
          135  +;   saved ebp   byte esp + 48
          136  +;   return address  byte esp + 52
          137  +deflatestate    equ  esp + 56   ; the function arguments
          138  +curmatch    equ  esp + 60
          139  +
          140  +;;; Offsets for fields in the deflate_state structure. These numbers
          141  +;;; are calculated from the definition of deflate_state, with the
          142  +;;; assumption that the compiler will dword-align the fields. (Thus,
          143  +;;; changing the definition of deflate_state could easily cause this
          144  +;;; program to crash horribly, without so much as a warning at
          145  +;;; compile time. Sigh.)
          146  +
          147  +dsWSize     equ 36+zlib1222add
          148  +dsWMask     equ 44+zlib1222add
          149  +dsWindow    equ 48+zlib1222add
          150  +dsPrev      equ 56+zlib1222add
          151  +dsMatchLen  equ 88+zlib1222add
          152  +dsPrevMatch equ 92+zlib1222add
          153  +dsStrStart  equ 100+zlib1222add
          154  +dsMatchStart    equ 104+zlib1222add
          155  +dsLookahead equ 108+zlib1222add
          156  +dsPrevLen   equ 112+zlib1222add
          157  +dsMaxChainLen   equ 116+zlib1222add
          158  +dsGoodMatch equ 132+zlib1222add
          159  +dsNiceMatch equ 136+zlib1222add
          160  +
          161  +
          162  +;;; match686.asm -- Pentium-Pro-optimized version of longest_match()
          163  +;;; Written for zlib 1.1.2
          164  +;;; Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
          165  +;;; You can look at http://www.muppetlabs.com/~breadbox/software/assembly.html
          166  +;;;
          167  +;;
          168  +;;  This software is provided 'as-is', without any express or implied
          169  +;;  warranty.  In no event will the authors be held liable for any damages
          170  +;;  arising from the use of this software.
          171  +;;
          172  +;;  Permission is granted to anyone to use this software for any purpose,
          173  +;;  including commercial applications, and to alter it and redistribute it
          174  +;;  freely, subject to the following restrictions:
          175  +;;
          176  +;;  1. The origin of this software must not be misrepresented; you must not
          177  +;;     claim that you wrote the original software. If you use this software
          178  +;;     in a product, an acknowledgment in the product documentation would be
          179  +;;     appreciated but is not required.
          180  +;;  2. Altered source versions must be plainly marked as such, and must not be
          181  +;;     misrepresented as being the original software
          182  +;;  3. This notice may not be removed or altered from any source distribution.
          183  +;;
          184  +
          185  +;GLOBAL _longest_match, _match_init
          186  +
          187  +
          188  +;SECTION    .text
          189  +
          190  +;;; uInt longest_match(deflate_state *deflatestate, IPos curmatch)
          191  +
          192  +;_longest_match:
          193  +    IFDEF NOUNDERLINE
          194  +    longest_match       proc near
          195  +    ELSE
          196  +    _longest_match      proc near
          197  +    ENDIF
          198  +.FPO (9, 4, 0, 0, 1, 0)
          199  +
          200  +;;; Save registers that the compiler may be using, and adjust esp to
          201  +;;; make room for our stack frame.
          202  +
          203  +        push    ebp
          204  +        push    edi
          205  +        push    esi
          206  +        push    ebx
          207  +        sub esp, LocalVarsSize
          208  +
          209  +;;; Retrieve the function arguments. ecx will hold cur_match
          210  +;;; throughout the entire function. edx will hold the pointer to the
          211  +;;; deflate_state structure during the function's setup (before
          212  +;;; entering the main loop.
          213  +
          214  +        mov edx, [deflatestate]
          215  +        mov ecx, [curmatch]
          216  +
          217  +;;; uInt wmask = s->w_mask;
          218  +;;; unsigned chain_length = s->max_chain_length;
          219  +;;; if (s->prev_length >= s->good_match) {
          220  +;;;     chain_length >>= 2;
          221  +;;; }
          222  +
          223  +        mov eax, [edx + dsPrevLen]
          224  +        mov ebx, [edx + dsGoodMatch]
          225  +        cmp eax, ebx
          226  +        mov eax, [edx + dsWMask]
          227  +        mov ebx, [edx + dsMaxChainLen]
          228  +        jl  LastMatchGood
          229  +        shr ebx, 2
          230  +LastMatchGood:
          231  +
          232  +;;; chainlen is decremented once beforehand so that the function can
          233  +;;; use the sign flag instead of the zero flag for the exit test.
          234  +;;; It is then shifted into the high word, to make room for the wmask
          235  +;;; value, which it will always accompany.
          236  +
          237  +        dec ebx
          238  +        shl ebx, 16
          239  +        or  ebx, eax
          240  +        mov [chainlenwmask], ebx
          241  +
          242  +;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
          243  +
          244  +        mov eax, [edx + dsNiceMatch]
          245  +        mov ebx, [edx + dsLookahead]
          246  +        cmp ebx, eax
          247  +        jl  LookaheadLess
          248  +        mov ebx, eax
          249  +LookaheadLess:  mov [nicematch], ebx
          250  +
          251  +;;; register Bytef *scan = s->window + s->strstart;
          252  +
          253  +        mov esi, [edx + dsWindow]
          254  +        mov [window], esi
          255  +        mov ebp, [edx + dsStrStart]
          256  +        lea edi, [esi + ebp]
          257  +        mov [scan], edi
          258  +
          259  +;;; Determine how many bytes the scan ptr is off from being
          260  +;;; dword-aligned.
          261  +
          262  +        mov eax, edi
          263  +        neg eax
          264  +        and eax, 3
          265  +        mov [scanalign], eax
          266  +
          267  +;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
          268  +;;;     s->strstart - (IPos)MAX_DIST(s) : NIL;
          269  +
          270  +        mov eax, [edx + dsWSize]
          271  +        sub eax, MIN_LOOKAHEAD
          272  +        sub ebp, eax
          273  +        jg  LimitPositive
          274  +        xor ebp, ebp
          275  +LimitPositive:
          276  +
          277  +;;; int best_len = s->prev_length;
          278  +
          279  +        mov eax, [edx + dsPrevLen]
          280  +        mov [bestlen], eax
          281  +
          282  +;;; Store the sum of s->window + best_len in esi locally, and in esi.
          283  +
          284  +        add esi, eax
          285  +        mov [windowbestlen], esi
          286  +
          287  +;;; register ush scan_start = *(ushf*)scan;
          288  +;;; register ush scan_end   = *(ushf*)(scan+best_len-1);
          289  +;;; Posf *prev = s->prev;
          290  +
          291  +        movzx   ebx, word ptr [edi]
          292  +        mov [scanstart], ebx
          293  +        movzx   ebx, word ptr [edi + eax - 1]
          294  +        mov [scanend], ebx
          295  +        mov edi, [edx + dsPrev]
          296  +
          297  +;;; Jump into the main loop.
          298  +
          299  +        mov edx, [chainlenwmask]
          300  +        jmp short LoopEntry
          301  +
          302  +align 4
          303  +
          304  +;;; do {
          305  +;;;     match = s->window + cur_match;
          306  +;;;     if (*(ushf*)(match+best_len-1) != scan_end ||
          307  +;;;         *(ushf*)match != scan_start) continue;
          308  +;;;     [...]
          309  +;;; } while ((cur_match = prev[cur_match & wmask]) > limit
          310  +;;;          && --chain_length != 0);
          311  +;;;
          312  +;;; Here is the inner loop of the function. The function will spend the
          313  +;;; majority of its time in this loop, and majority of that time will
          314  +;;; be spent in the first ten instructions.
          315  +;;;
          316  +;;; Within this loop:
          317  +;;; ebx = scanend
          318  +;;; ecx = curmatch
          319  +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
          320  +;;; esi = windowbestlen - i.e., (window + bestlen)
          321  +;;; edi = prev
          322  +;;; ebp = limit
          323  +
          324  +LookupLoop:
          325  +        and ecx, edx
          326  +        movzx   ecx, word ptr [edi + ecx*2]
          327  +        cmp ecx, ebp
          328  +        jbe LeaveNow
          329  +        sub edx, 00010000h
          330  +        js  LeaveNow
          331  +LoopEntry:  movzx   eax, word ptr [esi + ecx - 1]
          332  +        cmp eax, ebx
          333  +        jnz LookupLoop
          334  +        mov eax, [window]
          335  +        movzx   eax, word ptr [eax + ecx]
          336  +        cmp eax, [scanstart]
          337  +        jnz LookupLoop
          338  +
          339  +;;; Store the current value of chainlen.
          340  +
          341  +        mov [chainlenwmask], edx
          342  +
          343  +;;; Point edi to the string under scrutiny, and esi to the string we
          344  +;;; are hoping to match it up with. In actuality, esi and edi are
          345  +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is
          346  +;;; initialized to -(MAX_MATCH_8 - scanalign).
          347  +
          348  +        mov esi, [window]
          349  +        mov edi, [scan]
          350  +        add esi, ecx
          351  +        mov eax, [scanalign]
          352  +        mov edx, 0fffffef8h; -(MAX_MATCH_8)
          353  +        lea edi, [edi + eax + 0108h] ;MAX_MATCH_8]
          354  +        lea esi, [esi + eax + 0108h] ;MAX_MATCH_8]
          355  +
          356  +;;; Test the strings for equality, 8 bytes at a time. At the end,
          357  +;;; adjust edx so that it is offset to the exact byte that mismatched.
          358  +;;;
          359  +;;; We already know at this point that the first three bytes of the
          360  +;;; strings match each other, and they can be safely passed over before
          361  +;;; starting the compare loop. So what this code does is skip over 0-3
          362  +;;; bytes, as much as necessary in order to dword-align the edi
          363  +;;; pointer. (esi will still be misaligned three times out of four.)
          364  +;;;
          365  +;;; It should be confessed that this loop usually does not represent
          366  +;;; much of the total running time. Replacing it with a more
          367  +;;; straightforward "rep cmpsb" would not drastically degrade
          368  +;;; performance.
          369  +
          370  +LoopCmps:
          371  +        mov eax, [esi + edx]
          372  +        xor eax, [edi + edx]
          373  +        jnz LeaveLoopCmps
          374  +        mov eax, [esi + edx + 4]
          375  +        xor eax, [edi + edx + 4]
          376  +        jnz LeaveLoopCmps4
          377  +        add edx, 8
          378  +        jnz LoopCmps
          379  +        jmp short LenMaximum
          380  +LeaveLoopCmps4: add edx, 4
          381  +LeaveLoopCmps:  test    eax, 0000FFFFh
          382  +        jnz LenLower
          383  +        add edx,  2
          384  +        shr eax, 16
          385  +LenLower:   sub al, 1
          386  +        adc edx, 0
          387  +
          388  +;;; Calculate the length of the match. If it is longer than MAX_MATCH,
          389  +;;; then automatically accept it as the best possible match and leave.
          390  +
          391  +        lea eax, [edi + edx]
          392  +        mov edi, [scan]
          393  +        sub eax, edi
          394  +        cmp eax, MAX_MATCH
          395  +        jge LenMaximum
          396  +
          397  +;;; If the length of the match is not longer than the best match we
          398  +;;; have so far, then forget it and return to the lookup loop.
          399  +
          400  +        mov edx, [deflatestate]
          401  +        mov ebx, [bestlen]
          402  +        cmp eax, ebx
          403  +        jg  LongerMatch
          404  +        mov esi, [windowbestlen]
          405  +        mov edi, [edx + dsPrev]
          406  +        mov ebx, [scanend]
          407  +        mov edx, [chainlenwmask]
          408  +        jmp LookupLoop
          409  +
          410  +;;;         s->match_start = cur_match;
          411  +;;;         best_len = len;
          412  +;;;         if (len >= nice_match) break;
          413  +;;;         scan_end = *(ushf*)(scan+best_len-1);
          414  +
          415  +LongerMatch:    mov ebx, [nicematch]
          416  +        mov [bestlen], eax
          417  +        mov [edx + dsMatchStart], ecx
          418  +        cmp eax, ebx
          419  +        jge LeaveNow
          420  +        mov esi, [window]
          421  +        add esi, eax
          422  +        mov [windowbestlen], esi
          423  +        movzx   ebx, word ptr [edi + eax - 1]
          424  +        mov edi, [edx + dsPrev]
          425  +        mov [scanend], ebx
          426  +        mov edx, [chainlenwmask]
          427  +        jmp LookupLoop
          428  +
          429  +;;; Accept the current string, with the maximum possible length.
          430  +
          431  +LenMaximum: mov edx, [deflatestate]
          432  +        mov dword ptr [bestlen], MAX_MATCH
          433  +        mov [edx + dsMatchStart], ecx
          434  +
          435  +;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
          436  +;;; return s->lookahead;
          437  +
          438  +LeaveNow:
          439  +        mov edx, [deflatestate]
          440  +        mov ebx, [bestlen]
          441  +        mov eax, [edx + dsLookahead]
          442  +        cmp ebx, eax
          443  +        jg  LookaheadRet
          444  +        mov eax, ebx
          445  +LookaheadRet:
          446  +
          447  +;;; Restore the stack and return from whence we came.
          448  +
          449  +        add esp, LocalVarsSize
          450  +        pop ebx
          451  +        pop esi
          452  +        pop edi
          453  +        pop ebp
          454  +
          455  +        ret
          456  +; please don't remove this string !
          457  +; Your can freely use match686 in any free or commercial app if you don't remove the string in the binary!
          458  +    db     0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998",0dh,0ah
          459  +
          460  +
          461  +    IFDEF NOUNDERLINE
          462  +    longest_match       endp
          463  +    ELSE
          464  +    _longest_match      endp
          465  +    ENDIF
          466  +
          467  +    IFDEF NOUNDERLINE
          468  +    match_init      proc near
          469  +                    ret
          470  +    match_init      endp
          471  +    ELSE
          472  +    _match_init     proc near
          473  +                    ret
          474  +    _match_init     endp
          475  +    ENDIF
          476  +
          477  +
          478  +_TEXT   ends
          479  +end

Added compat/zlib/contrib/masmx86/readme.txt.

            1  +
            2  +Summary
            3  +-------
            4  +This directory contains ASM implementations of the functions
            5  +longest_match() and inflate_fast().
            6  +
            7  +
            8  +Use instructions
            9  +----------------
           10  +Assemble using MASM, and copy the object files into the zlib source
           11  +directory, then run the appropriate makefile, as suggested below.  You can
           12  +donwload MASM from here:
           13  +
           14  +    http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64
           15  +
           16  +You can also get objects files here:
           17  +
           18  +    http://www.winimage.com/zLibDll/zlib124_masm_obj.zip
           19  +
           20  +Build instructions
           21  +------------------
           22  +* With Microsoft C and MASM:
           23  +nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj"
           24  +
           25  +* With Borland C and TASM:
           26  +make -f win32/Makefile.bor LOCAL_ZLIB="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj" OBJPA="+match686c.obj+match686.obj+inffas32.obj"
           27  +

Added compat/zlib/contrib/minizip/Makefile.

            1  +CC=cc
            2  +CFLAGS=-O -I../..
            3  +
            4  +UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a
            5  +ZIP_OBJS = minizip.o zip.o   ioapi.o ../../libz.a
            6  +
            7  +.c.o:
            8  +	$(CC) -c $(CFLAGS) $*.c
            9  +
           10  +all: miniunz minizip
           11  +
           12  +miniunz:  $(UNZ_OBJS)
           13  +	$(CC) $(CFLAGS) -o $@ $(UNZ_OBJS)
           14  +
           15  +minizip:  $(ZIP_OBJS)
           16  +	$(CC) $(CFLAGS) -o $@ $(ZIP_OBJS)
           17  +
           18  +test:	miniunz minizip
           19  +	./minizip test readme.txt
           20  +	./miniunz -l test.zip
           21  +	mv readme.txt readme.old
           22  +	./miniunz test.zip
           23  +
           24  +clean:
           25  +	/bin/rm -f *.o *~ minizip miniunz

Added compat/zlib/contrib/minizip/Makefile.am.

            1  +lib_LTLIBRARIES = libminizip.la
            2  +
            3  +if COND_DEMOS
            4  +bin_PROGRAMS = miniunzip minizip
            5  +endif
            6  +
            7  +zlib_top_srcdir = $(top_srcdir)/../..
            8  +zlib_top_builddir = $(top_builddir)/../..
            9  +
           10  +AM_CPPFLAGS = -I$(zlib_top_srcdir)
           11  +AM_LDFLAGS = -L$(zlib_top_builddir)
           12  +
           13  +if WIN32
           14  +iowin32_src = iowin32.c
           15  +iowin32_h = iowin32.h
           16  +endif
           17  +
           18  +libminizip_la_SOURCES = \
           19  +	ioapi.c \
           20  +	mztools.c \
           21  +	unzip.c \
           22  +	zip.c \
           23  +	${iowin32_src}
           24  +
           25  +libminizip_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0 -lz
           26  +
           27  +minizip_includedir = $(includedir)/minizip
           28  +minizip_include_HEADERS = \
           29  +	crypt.h \
           30  +	ioapi.h \
           31  +	mztools.h \
           32  +	unzip.h \
           33  +	zip.h \
           34  +	${iowin32_h}
           35  +
           36  +pkgconfigdir = $(libdir)/pkgconfig
           37  +pkgconfig_DATA = minizip.pc
           38  +
           39  +EXTRA_PROGRAMS = miniunzip minizip
           40  +
           41  +miniunzip_SOURCES = miniunz.c
           42  +miniunzip_LDADD = libminizip.la
           43  +
           44  +minizip_SOURCES = minizip.c
           45  +minizip_LDADD = libminizip.la -lz

Added compat/zlib/contrib/minizip/MiniZip64_Changes.txt.

            1  +
            2  +MiniZip 1.1 was derrived from MiniZip at version 1.01f
            3  +
            4  +Change in 1.0 (Okt 2009)
            5  + - **TODO - Add history**
            6  +

Added compat/zlib/contrib/minizip/MiniZip64_info.txt.

            1  +MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson
            2  +
            3  +Introduction
            4  +---------------------
            5  +MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0.
            8  +All possible work was done for compatibility.
            9  +
           10  +
           11  +Background
           12  +---------------------
           13  +When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64 
           14  +support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ )
           15  +
           16  +That was used as a starting point. And after that ZIP64 support was added to zip.c
           17  +some refactoring and code cleanup was also done.
           18  +
           19  +
           20  +Changed from MiniZip 1.0 to MiniZip 1.1
           21  +---------------------------------------
           22  +* Added ZIP64 support for unzip ( by Even Rouault )
           23  +* Added ZIP64 support for zip ( by Mathias Svensson )
           24  +* Reverted some changed that Even Rouault did.
           25  +* Bunch of patches received from Gulles Vollant that he received for MiniZip from various users.
           26  +* Added unzip patch for BZIP Compression method (patch create by Daniel Borca)
           27  +* Added BZIP Compress method for zip
           28  +* Did some refactoring and code cleanup
           29  +
           30  +
           31  +Credits
           32  +
           33  + Gilles Vollant    - Original MiniZip author
           34  + Even Rouault      - ZIP64 unzip Support
           35  + Daniel Borca      - BZip Compression method support in unzip
           36  + Mathias Svensson  - ZIP64 zip support
           37  + Mathias Svensson  - BZip Compression method support in zip
           38  +
           39  + Resources
           40  +
           41  + ZipLayout   http://result42.com/projects/ZipFileLayout
           42  +             Command line tool for Windows that shows the layout and information of the headers in a zip archive.
           43  +             Used when debugging and validating the creation of zip files using MiniZip64
           44  +
           45  +
           46  + ZIP App Note  http://www.pkware.com/documents/casestudies/APPNOTE.TXT
           47  +               Zip File specification
           48  +
           49  +
           50  +Notes.
           51  + * To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined.
           52  +
           53  +License
           54  +----------------------------------------------------------
           55  +   Condition of use and distribution are the same than zlib :
           56  +
           57  +  This software is provided 'as-is', without any express or implied
           58  +  warranty.  In no event will the authors be held liable for any damages
           59  +  arising from the use of this software.
           60  +
           61  +  Permission is granted to anyone to use this software for any purpose,
           62  +  including commercial applications, and to alter it and redistribute it
           63  +  freely, subject to the following restrictions:
           64  +
           65  +  1. The origin of this software must not be misrepresented; you must not
           66  +     claim that you wrote the original software. If you use this software
           67  +     in a product, an acknowledgment in the product documentation would be
           68  +     appreciated but is not required.
           69  +  2. Altered source versions must be plainly marked as such, and must not be
           70  +     misrepresented as being the original software.
           71  +  3. This notice may not be removed or altered from any source distribution.
           72  +
           73  +----------------------------------------------------------
           74  +

Added compat/zlib/contrib/minizip/configure.ac.

            1  +#                                               -*- Autoconf -*-
            2  +# Process this file with autoconf to produce a configure script.
            3  +
            4  +AC_INIT([minizip], [1.2.7], [bugzilla.redhat.com])
            5  +AC_CONFIG_SRCDIR([minizip.c])
            6  +AM_INIT_AUTOMAKE([foreign])
            7  +LT_INIT
            8  +
            9  +AC_MSG_CHECKING([whether to build example programs])
           10  +AC_ARG_ENABLE([demos], AC_HELP_STRING([--enable-demos], [build example programs]))
           11  +AM_CONDITIONAL([COND_DEMOS], [test "$enable_demos" = yes])
           12  +if test "$enable_demos" = yes
           13  +then
           14  +	AC_MSG_RESULT([yes])
           15  +else
           16  +	AC_MSG_RESULT([no])
           17  +fi
           18  +
           19  +case "${host}" in
           20  +	*-mingw* | mingw*)
           21  +		WIN32="yes"
           22  +		;;
           23  +	*)
           24  +		;;
           25  +esac
           26  +AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
           27  +
           28  +
           29  +AC_SUBST([HAVE_UNISTD_H], [0])
           30  +AC_CHECK_HEADER([unistd.h], [HAVE_UNISTD_H=1], [])
           31  +AC_CONFIG_FILES([Makefile minizip.pc])
           32  +AC_OUTPUT

Added compat/zlib/contrib/minizip/crypt.h.

            1  +/* crypt.h -- base code for crypt/uncrypt ZIPfile
            2  +
            3  +
            4  +   Version 1.01e, February 12th, 2005
            5  +
            6  +   Copyright (C) 1998-2005 Gilles Vollant
            7  +
            8  +   This code is a modified version of crypting code in Infozip distribution
            9  +
           10  +   The encryption/decryption parts of this source code (as opposed to the
           11  +   non-echoing password parts) were originally written in Europe.  The
           12  +   whole source package can be freely distributed, including from the USA.
           13  +   (Prior to January 2000, re-export from the US was a violation of US law.)
           14  +
           15  +   This encryption code is a direct transcription of the algorithm from
           16  +   Roger Schlafly, described by Phil Katz in the file appnote.txt.  This
           17  +   file (appnote.txt) is distributed with the PKZIP program (even in the
           18  +   version without encryption capabilities).
           19  +
           20  +   If you don't need crypting in your application, just define symbols
           21  +   NOCRYPT and NOUNCRYPT.
           22  +
           23  +   This code support the "Traditional PKWARE Encryption".
           24  +
           25  +   The new AES encryption added on Zip format by Winzip (see the page
           26  +   http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
           27  +   Encryption is not supported.
           28  +*/
           29  +
           30  +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
           31  +
           32  +/***********************************************************************
           33  + * Return the next byte in the pseudo-random sequence
           34  + */
           35  +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
           36  +{
           37  +    unsigned temp;  /* POTENTIAL BUG:  temp*(temp^1) may overflow in an
           38  +                     * unpredictable manner on 16-bit systems; not a problem
           39  +                     * with any known compiler so far, though */
           40  +
           41  +    temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
           42  +    return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
           43  +}
           44  +
           45  +/***********************************************************************
           46  + * Update the encryption keys with the next byte of plain text
           47  + */
           48  +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
           49  +{
           50  +    (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
           51  +    (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
           52  +    (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
           53  +    {
           54  +      register int keyshift = (int)((*(pkeys+1)) >> 24);
           55  +      (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
           56  +    }
           57  +    return c;
           58  +}
           59  +
           60  +
           61  +/***********************************************************************
           62  + * Initialize the encryption keys and the random header according to
           63  + * the given password.
           64  + */
           65  +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
           66  +{
           67  +    *(pkeys+0) = 305419896L;
           68  +    *(pkeys+1) = 591751049L;
           69  +    *(pkeys+2) = 878082192L;
           70  +    while (*passwd != '\0') {
           71  +        update_keys(pkeys,pcrc_32_tab,(int)*passwd);
           72  +        passwd++;
           73  +    }
           74  +}
           75  +
           76  +#define zdecode(pkeys,pcrc_32_tab,c) \
           77  +    (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
           78  +
           79  +#define zencode(pkeys,pcrc_32_tab,c,t) \
           80  +    (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
           81  +
           82  +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
           83  +
           84  +#define RAND_HEAD_LEN  12
           85  +   /* "last resort" source for second part of crypt seed pattern */
           86  +#  ifndef ZCR_SEED2
           87  +#    define ZCR_SEED2 3141592654UL     /* use PI as default pattern */
           88  +#  endif
           89  +
           90  +static int crypthead(const char* passwd,      /* password string */
           91  +                     unsigned char* buf,      /* where to write header */
           92  +                     int bufSize,
           93  +                     unsigned long* pkeys,
           94  +                     const unsigned long* pcrc_32_tab,
           95  +                     unsigned long crcForCrypting)
           96  +{
           97  +    int n;                       /* index in random header */
           98  +    int t;                       /* temporary */
           99  +    int c;                       /* random byte */
          100  +    unsigned char header[RAND_HEAD_LEN-2]; /* random header */
          101  +    static unsigned calls = 0;   /* ensure different random header each time */
          102  +
          103  +    if (bufSize<RAND_HEAD_LEN)
          104  +      return 0;
          105  +
          106  +    /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
          107  +     * output of rand() to get less predictability, since rand() is
          108  +     * often poorly implemented.
          109  +     */
          110  +    if (++calls == 1)
          111  +    {
          112  +        srand((unsigned)(time(NULL) ^ ZCR_SEED2));
          113  +    }
          114  +    init_keys(passwd, pkeys, pcrc_32_tab);
          115  +    for (n = 0; n < RAND_HEAD_LEN-2; n++)
          116  +    {
          117  +        c = (rand() >> 7) & 0xff;
          118  +        header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
          119  +    }
          120  +    /* Encrypt random header (last two bytes is high word of crc) */
          121  +    init_keys(passwd, pkeys, pcrc_32_tab);
          122  +    for (n = 0; n < RAND_HEAD_LEN-2; n++)
          123  +    {
          124  +        buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
          125  +    }
          126  +    buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
          127  +    buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
          128  +    return n;
          129  +}
          130  +
          131  +#endif

Added compat/zlib/contrib/minizip/ioapi.c.

            1  +/* ioapi.h -- IO base function header for compress/uncompress .zip
            2  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            3  +
            4  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            5  +
            6  +         Modifications for Zip64 support
            7  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            8  +
            9  +         For more info read MiniZip_info.txt
           10  +
           11  +*/
           12  +
           13  +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS)))
           14  +        #define _CRT_SECURE_NO_WARNINGS
           15  +#endif
           16  +
           17  +#if defined(__APPLE__) || defined(IOAPI_NO_64)
           18  +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
           19  +#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
           20  +#define FTELLO_FUNC(stream) ftello(stream)
           21  +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
           22  +#else
           23  +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
           24  +#define FTELLO_FUNC(stream) ftello64(stream)
           25  +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
           26  +#endif
           27  +
           28  +
           29  +#include "ioapi.h"
           30  +
           31  +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
           32  +{
           33  +    if (pfilefunc->zfile_func64.zopen64_file != NULL)
           34  +        return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode);
           35  +    else
           36  +    {
           37  +        return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode);
           38  +    }
           39  +}
           40  +
           41  +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)
           42  +{
           43  +    if (pfilefunc->zfile_func64.zseek64_file != NULL)
           44  +        return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
           45  +    else
           46  +    {
           47  +        uLong offsetTruncated = (uLong)offset;
           48  +        if (offsetTruncated != offset)
           49  +            return -1;
           50  +        else
           51  +            return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin);
           52  +    }
           53  +}
           54  +
           55  +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)
           56  +{
           57  +    if (pfilefunc->zfile_func64.zseek64_file != NULL)
           58  +        return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream);
           59  +    else
           60  +    {
           61  +        uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
           62  +        if ((tell_uLong) == MAXU32)
           63  +            return (ZPOS64_T)-1;
           64  +        else
           65  +            return tell_uLong;
           66  +    }
           67  +}
           68  +
           69  +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32)
           70  +{
           71  +    p_filefunc64_32->zfile_func64.zopen64_file = NULL;
           72  +    p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
           73  +    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
           74  +    p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
           75  +    p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
           76  +    p_filefunc64_32->zfile_func64.ztell64_file = NULL;
           77  +    p_filefunc64_32->zfile_func64.zseek64_file = NULL;
           78  +    p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
           79  +    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
           80  +    p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
           81  +    p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
           82  +    p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
           83  +}
           84  +
           85  +
           86  +
           87  +static voidpf  ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
           88  +static uLong   ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
           89  +static uLong   ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
           90  +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
           91  +static long    ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
           92  +static int     ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
           93  +static int     ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
           94  +
           95  +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
           96  +{
           97  +    FILE* file = NULL;
           98  +    const char* mode_fopen = NULL;
           99  +    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
          100  +        mode_fopen = "rb";
          101  +    else
          102  +    if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
          103  +        mode_fopen = "r+b";
          104  +    else
          105  +    if (mode & ZLIB_FILEFUNC_MODE_CREATE)
          106  +        mode_fopen = "wb";
          107  +
          108  +    if ((filename!=NULL) && (mode_fopen != NULL))
          109  +        file = fopen(filename, mode_fopen);
          110  +    return file;
          111  +}
          112  +
          113  +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode)
          114  +{
          115  +    FILE* file = NULL;
          116  +    const char* mode_fopen = NULL;
          117  +    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
          118  +        mode_fopen = "rb";
          119  +    else
          120  +    if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
          121  +        mode_fopen = "r+b";
          122  +    else
          123  +    if (mode & ZLIB_FILEFUNC_MODE_CREATE)
          124  +        mode_fopen = "wb";
          125  +
          126  +    if ((filename!=NULL) && (mode_fopen != NULL))
          127  +        file = FOPEN_FUNC((const char*)filename, mode_fopen);
          128  +    return file;
          129  +}
          130  +
          131  +
          132  +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
          133  +{
          134  +    uLong ret;
          135  +    ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
          136  +    return ret;
          137  +}
          138  +
          139  +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
          140  +{
          141  +    uLong ret;
          142  +    ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
          143  +    return ret;
          144  +}
          145  +
          146  +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
          147  +{
          148  +    long ret;
          149  +    ret = ftell((FILE *)stream);
          150  +    return ret;
          151  +}
          152  +
          153  +
          154  +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
          155  +{
          156  +    ZPOS64_T ret;
          157  +    ret = FTELLO_FUNC((FILE *)stream);
          158  +    return ret;
          159  +}
          160  +
          161  +static long ZCALLBACK fseek_file_func (voidpf  opaque, voidpf stream, uLong offset, int origin)
          162  +{
          163  +    int fseek_origin=0;
          164  +    long ret;
          165  +    switch (origin)
          166  +    {
          167  +    case ZLIB_FILEFUNC_SEEK_CUR :
          168  +        fseek_origin = SEEK_CUR;
          169  +        break;
          170  +    case ZLIB_FILEFUNC_SEEK_END :
          171  +        fseek_origin = SEEK_END;
          172  +        break;
          173  +    case ZLIB_FILEFUNC_SEEK_SET :
          174  +        fseek_origin = SEEK_SET;
          175  +        break;
          176  +    default: return -1;
          177  +    }
          178  +    ret = 0;
          179  +    if (fseek((FILE *)stream, offset, fseek_origin) != 0)
          180  +        ret = -1;
          181  +    return ret;
          182  +}
          183  +
          184  +static long ZCALLBACK fseek64_file_func (voidpf  opaque, voidpf stream, ZPOS64_T offset, int origin)
          185  +{
          186  +    int fseek_origin=0;
          187  +    long ret;
          188  +    switch (origin)
          189  +    {
          190  +    case ZLIB_FILEFUNC_SEEK_CUR :
          191  +        fseek_origin = SEEK_CUR;
          192  +        break;
          193  +    case ZLIB_FILEFUNC_SEEK_END :
          194  +        fseek_origin = SEEK_END;
          195  +        break;
          196  +    case ZLIB_FILEFUNC_SEEK_SET :
          197  +        fseek_origin = SEEK_SET;
          198  +        break;
          199  +    default: return -1;
          200  +    }
          201  +    ret = 0;
          202  +
          203  +    if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0)
          204  +                        ret = -1;
          205  +
          206  +    return ret;
          207  +}
          208  +
          209  +
          210  +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
          211  +{
          212  +    int ret;
          213  +    ret = fclose((FILE *)stream);
          214  +    return ret;
          215  +}
          216  +
          217  +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
          218  +{
          219  +    int ret;
          220  +    ret = ferror((FILE *)stream);
          221  +    return ret;
          222  +}
          223  +
          224  +void fill_fopen_filefunc (pzlib_filefunc_def)
          225  +  zlib_filefunc_def* pzlib_filefunc_def;
          226  +{
          227  +    pzlib_filefunc_def->zopen_file = fopen_file_func;
          228  +    pzlib_filefunc_def->zread_file = fread_file_func;
          229  +    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
          230  +    pzlib_filefunc_def->ztell_file = ftell_file_func;
          231  +    pzlib_filefunc_def->zseek_file = fseek_file_func;
          232  +    pzlib_filefunc_def->zclose_file = fclose_file_func;
          233  +    pzlib_filefunc_def->zerror_file = ferror_file_func;
          234  +    pzlib_filefunc_def->opaque = NULL;
          235  +}
          236  +
          237  +void fill_fopen64_filefunc (zlib_filefunc64_def*  pzlib_filefunc_def)
          238  +{
          239  +    pzlib_filefunc_def->zopen64_file = fopen64_file_func;
          240  +    pzlib_filefunc_def->zread_file = fread_file_func;
          241  +    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
          242  +    pzlib_filefunc_def->ztell64_file = ftell64_file_func;
          243  +    pzlib_filefunc_def->zseek64_file = fseek64_file_func;
          244  +    pzlib_filefunc_def->zclose_file = fclose_file_func;
          245  +    pzlib_filefunc_def->zerror_file = ferror_file_func;
          246  +    pzlib_filefunc_def->opaque = NULL;
          247  +}

Added compat/zlib/contrib/minizip/ioapi.h.

            1  +/* ioapi.h -- IO base function header for compress/uncompress .zip
            2  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            3  +
            4  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            5  +
            6  +         Modifications for Zip64 support
            7  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            8  +
            9  +         For more info read MiniZip_info.txt
           10  +
           11  +         Changes
           12  +
           13  +    Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this)
           14  +    Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux.
           15  +               More if/def section may be needed to support other platforms
           16  +    Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows.
           17  +                          (but you should use iowin32.c for windows instead)
           18  +
           19  +*/
           20  +
           21  +#ifndef _ZLIBIOAPI64_H
           22  +#define _ZLIBIOAPI64_H
           23  +
           24  +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
           25  +
           26  +  // Linux needs this to support file operation on files larger then 4+GB
           27  +  // But might need better if/def to select just the platforms that needs them.
           28  +
           29  +        #ifndef __USE_FILE_OFFSET64
           30  +                #define __USE_FILE_OFFSET64
           31  +        #endif
           32  +        #ifndef __USE_LARGEFILE64
           33  +                #define __USE_LARGEFILE64
           34  +        #endif
           35  +        #ifndef _LARGEFILE64_SOURCE
           36  +                #define _LARGEFILE64_SOURCE
           37  +        #endif
           38  +        #ifndef _FILE_OFFSET_BIT
           39  +                #define _FILE_OFFSET_BIT 64
           40  +        #endif
           41  +
           42  +#endif
           43  +
           44  +#include <stdio.h>
           45  +#include <stdlib.h>
           46  +#include "zlib.h"
           47  +
           48  +#if defined(USE_FILE32API)
           49  +#define fopen64 fopen
           50  +#define ftello64 ftell
           51  +#define fseeko64 fseek
           52  +#else
           53  +#ifdef __FreeBSD__
           54  +#define fopen64 fopen
           55  +#define ftello64 ftello
           56  +#define fseeko64 fseeko
           57  +#endif
           58  +#ifdef _MSC_VER
           59  + #define fopen64 fopen
           60  + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
           61  +  #define ftello64 _ftelli64
           62  +  #define fseeko64 _fseeki64
           63  + #else // old MSC
           64  +  #define ftello64 ftell
           65  +  #define fseeko64 fseek
           66  + #endif
           67  +#endif
           68  +#endif
           69  +
           70  +/*
           71  +#ifndef ZPOS64_T
           72  +  #ifdef _WIN32
           73  +                #define ZPOS64_T fpos_t
           74  +  #else
           75  +    #include <stdint.h>
           76  +    #define ZPOS64_T uint64_t
           77  +  #endif
           78  +#endif
           79  +*/
           80  +
           81  +#ifdef HAVE_MINIZIP64_CONF_H
           82  +#include "mz64conf.h"
           83  +#endif
           84  +
           85  +/* a type choosen by DEFINE */
           86  +#ifdef HAVE_64BIT_INT_CUSTOM
           87  +typedef  64BIT_INT_CUSTOM_TYPE ZPOS64_T;
           88  +#else
           89  +#ifdef HAS_STDINT_H
           90  +#include "stdint.h"
           91  +typedef uint64_t ZPOS64_T;
           92  +#else
           93  +
           94  +/* Maximum unsigned 32-bit value used as placeholder for zip64 */
           95  +#define MAXU32 0xffffffff
           96  +
           97  +#if defined(_MSC_VER) || defined(__BORLANDC__)
           98  +typedef unsigned __int64 ZPOS64_T;
           99  +#else
          100  +typedef unsigned long long int ZPOS64_T;
          101  +#endif
          102  +#endif
          103  +#endif
          104  +
          105  +
          106  +
          107  +#ifdef __cplusplus
          108  +extern "C" {
          109  +#endif
          110  +
          111  +
          112  +#define ZLIB_FILEFUNC_SEEK_CUR (1)
          113  +#define ZLIB_FILEFUNC_SEEK_END (2)
          114  +#define ZLIB_FILEFUNC_SEEK_SET (0)
          115  +
          116  +#define ZLIB_FILEFUNC_MODE_READ      (1)
          117  +#define ZLIB_FILEFUNC_MODE_WRITE     (2)
          118  +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
          119  +
          120  +#define ZLIB_FILEFUNC_MODE_EXISTING (4)
          121  +#define ZLIB_FILEFUNC_MODE_CREATE   (8)
          122  +
          123  +
          124  +#ifndef ZCALLBACK
          125  + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
          126  +   #define ZCALLBACK CALLBACK
          127  + #else
          128  +   #define ZCALLBACK
          129  + #endif
          130  +#endif
          131  +
          132  +
          133  +
          134  +
          135  +typedef voidpf   (ZCALLBACK *open_file_func)      OF((voidpf opaque, const char* filename, int mode));
          136  +typedef uLong    (ZCALLBACK *read_file_func)      OF((voidpf opaque, voidpf stream, void* buf, uLong size));
          137  +typedef uLong    (ZCALLBACK *write_file_func)     OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
          138  +typedef int      (ZCALLBACK *close_file_func)     OF((voidpf opaque, voidpf stream));
          139  +typedef int      (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
          140  +
          141  +typedef long     (ZCALLBACK *tell_file_func)      OF((voidpf opaque, voidpf stream));
          142  +typedef long     (ZCALLBACK *seek_file_func)      OF((voidpf opaque, voidpf stream, uLong offset, int origin));
          143  +
          144  +
          145  +/* here is the "old" 32 bits structure structure */
          146  +typedef struct zlib_filefunc_def_s
          147  +{
          148  +    open_file_func      zopen_file;
          149  +    read_file_func      zread_file;
          150  +    write_file_func     zwrite_file;
          151  +    tell_file_func      ztell_file;
          152  +    seek_file_func      zseek_file;
          153  +    close_file_func     zclose_file;
          154  +    testerror_file_func zerror_file;
          155  +    voidpf              opaque;
          156  +} zlib_filefunc_def;
          157  +
          158  +typedef ZPOS64_T (ZCALLBACK *tell64_file_func)    OF((voidpf opaque, voidpf stream));
          159  +typedef long     (ZCALLBACK *seek64_file_func)    OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
          160  +typedef voidpf   (ZCALLBACK *open64_file_func)    OF((voidpf opaque, const void* filename, int mode));
          161  +
          162  +typedef struct zlib_filefunc64_def_s
          163  +{
          164  +    open64_file_func    zopen64_file;
          165  +    read_file_func      zread_file;
          166  +    write_file_func     zwrite_file;
          167  +    tell64_file_func    ztell64_file;
          168  +    seek64_file_func    zseek64_file;
          169  +    close_file_func     zclose_file;
          170  +    testerror_file_func zerror_file;
          171  +    voidpf              opaque;
          172  +} zlib_filefunc64_def;
          173  +
          174  +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def));
          175  +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
          176  +
          177  +/* now internal definition, only for zip.c and unzip.h */
          178  +typedef struct zlib_filefunc64_32_def_s
          179  +{
          180  +    zlib_filefunc64_def zfile_func64;
          181  +    open_file_func      zopen32_file;
          182  +    tell_file_func      ztell32_file;
          183  +    seek_file_func      zseek32_file;
          184  +} zlib_filefunc64_32_def;
          185  +
          186  +
          187  +#define ZREAD64(filefunc,filestream,buf,size)     ((*((filefunc).zfile_func64.zread_file))   ((filefunc).zfile_func64.opaque,filestream,buf,size))
          188  +#define ZWRITE64(filefunc,filestream,buf,size)    ((*((filefunc).zfile_func64.zwrite_file))  ((filefunc).zfile_func64.opaque,filestream,buf,size))
          189  +//#define ZTELL64(filefunc,filestream)            ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))
          190  +//#define ZSEEK64(filefunc,filestream,pos,mode)   ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))
          191  +#define ZCLOSE64(filefunc,filestream)             ((*((filefunc).zfile_func64.zclose_file))  ((filefunc).zfile_func64.opaque,filestream))
          192  +#define ZERROR64(filefunc,filestream)             ((*((filefunc).zfile_func64.zerror_file))  ((filefunc).zfile_func64.opaque,filestream))
          193  +
          194  +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
          195  +long    call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
          196  +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream));
          197  +
          198  +void    fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32);
          199  +
          200  +#define ZOPEN64(filefunc,filename,mode)         (call_zopen64((&(filefunc)),(filename),(mode)))
          201  +#define ZTELL64(filefunc,filestream)            (call_ztell64((&(filefunc)),(filestream)))
          202  +#define ZSEEK64(filefunc,filestream,pos,mode)   (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
          203  +
          204  +#ifdef __cplusplus
          205  +}
          206  +#endif
          207  +
          208  +#endif

Added compat/zlib/contrib/minizip/iowin32.c.

            1  +/* iowin32.c -- IO base function header for compress/uncompress .zip
            2  +     Version 1.1, February 14h, 2010
            3  +     part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications for Zip64 support
            8  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            9  +
           10  +     For more info read MiniZip_info.txt
           11  +
           12  +*/
           13  +
           14  +#include <stdlib.h>
           15  +
           16  +#include "zlib.h"
           17  +#include "ioapi.h"
           18  +#include "iowin32.h"
           19  +
           20  +#ifndef INVALID_HANDLE_VALUE
           21  +#define INVALID_HANDLE_VALUE (0xFFFFFFFF)
           22  +#endif
           23  +
           24  +#ifndef INVALID_SET_FILE_POINTER
           25  +#define INVALID_SET_FILE_POINTER ((DWORD)-1)
           26  +#endif
           27  +
           28  +voidpf  ZCALLBACK win32_open_file_func  OF((voidpf opaque, const char* filename, int mode));
           29  +uLong   ZCALLBACK win32_read_file_func  OF((voidpf opaque, voidpf stream, void* buf, uLong size));
           30  +uLong   ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
           31  +ZPOS64_T ZCALLBACK win32_tell64_file_func  OF((voidpf opaque, voidpf stream));
           32  +long    ZCALLBACK win32_seek64_file_func  OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
           33  +int     ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream));
           34  +int     ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream));
           35  +
           36  +typedef struct
           37  +{
           38  +    HANDLE hf;
           39  +    int error;
           40  +} WIN32FILE_IOWIN;
           41  +
           42  +
           43  +static void win32_translate_open_mode(int mode,
           44  +                                      DWORD* lpdwDesiredAccess,
           45  +                                      DWORD* lpdwCreationDisposition,
           46  +                                      DWORD* lpdwShareMode,
           47  +                                      DWORD* lpdwFlagsAndAttributes)
           48  +{
           49  +    *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0;
           50  +
           51  +    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
           52  +    {
           53  +        *lpdwDesiredAccess = GENERIC_READ;
           54  +        *lpdwCreationDisposition = OPEN_EXISTING;
           55  +        *lpdwShareMode = FILE_SHARE_READ;
           56  +    }
           57  +    else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
           58  +    {
           59  +        *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
           60  +        *lpdwCreationDisposition = OPEN_EXISTING;
           61  +    }
           62  +    else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
           63  +    {
           64  +        *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
           65  +        *lpdwCreationDisposition = CREATE_ALWAYS;
           66  +    }
           67  +}
           68  +
           69  +static voidpf win32_build_iowin(HANDLE hFile)
           70  +{
           71  +    voidpf ret=NULL;
           72  +
           73  +    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
           74  +    {
           75  +        WIN32FILE_IOWIN w32fiow;
           76  +        w32fiow.hf = hFile;
           77  +        w32fiow.error = 0;
           78  +        ret = malloc(sizeof(WIN32FILE_IOWIN));
           79  +
           80  +        if (ret==NULL)
           81  +            CloseHandle(hFile);
           82  +        else
           83  +            *((WIN32FILE_IOWIN*)ret) = w32fiow;
           84  +    }
           85  +    return ret;
           86  +}
           87  +
           88  +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode)
           89  +{
           90  +    const char* mode_fopen = NULL;
           91  +    DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
           92  +    HANDLE hFile = NULL;
           93  +
           94  +    win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
           95  +
           96  +    if ((filename!=NULL) && (dwDesiredAccess != 0))
           97  +        hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
           98  +
           99  +    return win32_build_iowin(hFile);
          100  +}
          101  +
          102  +
          103  +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode)
          104  +{
          105  +    const char* mode_fopen = NULL;
          106  +    DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
          107  +    HANDLE hFile = NULL;
          108  +
          109  +    win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
          110  +
          111  +    if ((filename!=NULL) && (dwDesiredAccess != 0))
          112  +        hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
          113  +
          114  +    return win32_build_iowin(hFile);
          115  +}
          116  +
          117  +
          118  +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode)
          119  +{
          120  +    const char* mode_fopen = NULL;
          121  +    DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
          122  +    HANDLE hFile = NULL;
          123  +
          124  +    win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
          125  +
          126  +    if ((filename!=NULL) && (dwDesiredAccess != 0))
          127  +        hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
          128  +
          129  +    return win32_build_iowin(hFile);
          130  +}
          131  +
          132  +
          133  +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode)
          134  +{
          135  +    const char* mode_fopen = NULL;
          136  +    DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
          137  +    HANDLE hFile = NULL;
          138  +
          139  +    win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes);
          140  +
          141  +    if ((filename!=NULL) && (dwDesiredAccess != 0))
          142  +        hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL);
          143  +
          144  +    return win32_build_iowin(hFile);
          145  +}
          146  +
          147  +
          148  +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size)
          149  +{
          150  +    uLong ret=0;
          151  +    HANDLE hFile = NULL;
          152  +    if (stream!=NULL)
          153  +        hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
          154  +
          155  +    if (hFile != NULL)
          156  +    {
          157  +        if (!ReadFile(hFile, buf, size, &ret, NULL))
          158  +        {
          159  +            DWORD dwErr = GetLastError();
          160  +            if (dwErr == ERROR_HANDLE_EOF)
          161  +                dwErr = 0;
          162  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          163  +        }
          164  +    }
          165  +
          166  +    return ret;
          167  +}
          168  +
          169  +
          170  +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size)
          171  +{
          172  +    uLong ret=0;
          173  +    HANDLE hFile = NULL;
          174  +    if (stream!=NULL)
          175  +        hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
          176  +
          177  +    if (hFile != NULL)
          178  +    {
          179  +        if (!WriteFile(hFile, buf, size, &ret, NULL))
          180  +        {
          181  +            DWORD dwErr = GetLastError();
          182  +            if (dwErr == ERROR_HANDLE_EOF)
          183  +                dwErr = 0;
          184  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          185  +        }
          186  +    }
          187  +
          188  +    return ret;
          189  +}
          190  +
          191  +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream)
          192  +{
          193  +    long ret=-1;
          194  +    HANDLE hFile = NULL;
          195  +    if (stream!=NULL)
          196  +        hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
          197  +    if (hFile != NULL)
          198  +    {
          199  +        DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
          200  +        if (dwSet == INVALID_SET_FILE_POINTER)
          201  +        {
          202  +            DWORD dwErr = GetLastError();
          203  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          204  +            ret = -1;
          205  +        }
          206  +        else
          207  +            ret=(long)dwSet;
          208  +    }
          209  +    return ret;
          210  +}
          211  +
          212  +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream)
          213  +{
          214  +    ZPOS64_T ret= (ZPOS64_T)-1;
          215  +    HANDLE hFile = NULL;
          216  +    if (stream!=NULL)
          217  +        hFile = ((WIN32FILE_IOWIN*)stream)->hf;
          218  +
          219  +    if (hFile)
          220  +    {
          221  +        LARGE_INTEGER li;
          222  +        li.QuadPart = 0;
          223  +        li.u.LowPart = SetFilePointer(hFile, li.u.LowPart, &li.u.HighPart, FILE_CURRENT);
          224  +        if ( (li.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
          225  +        {
          226  +            DWORD dwErr = GetLastError();
          227  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          228  +            ret = (ZPOS64_T)-1;
          229  +        }
          230  +        else
          231  +            ret=li.QuadPart;
          232  +    }
          233  +    return ret;
          234  +}
          235  +
          236  +
          237  +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin)
          238  +{
          239  +    DWORD dwMoveMethod=0xFFFFFFFF;
          240  +    HANDLE hFile = NULL;
          241  +
          242  +    long ret=-1;
          243  +    if (stream!=NULL)
          244  +        hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
          245  +    switch (origin)
          246  +    {
          247  +    case ZLIB_FILEFUNC_SEEK_CUR :
          248  +        dwMoveMethod = FILE_CURRENT;
          249  +        break;
          250  +    case ZLIB_FILEFUNC_SEEK_END :
          251  +        dwMoveMethod = FILE_END;
          252  +        break;
          253  +    case ZLIB_FILEFUNC_SEEK_SET :
          254  +        dwMoveMethod = FILE_BEGIN;
          255  +        break;
          256  +    default: return -1;
          257  +    }
          258  +
          259  +    if (hFile != NULL)
          260  +    {
          261  +        DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod);
          262  +        if (dwSet == INVALID_SET_FILE_POINTER)
          263  +        {
          264  +            DWORD dwErr = GetLastError();
          265  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          266  +            ret = -1;
          267  +        }
          268  +        else
          269  +            ret=0;
          270  +    }
          271  +    return ret;
          272  +}
          273  +
          274  +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin)
          275  +{
          276  +    DWORD dwMoveMethod=0xFFFFFFFF;
          277  +    HANDLE hFile = NULL;
          278  +    long ret=-1;
          279  +
          280  +    if (stream!=NULL)
          281  +        hFile = ((WIN32FILE_IOWIN*)stream)->hf;
          282  +
          283  +    switch (origin)
          284  +    {
          285  +        case ZLIB_FILEFUNC_SEEK_CUR :
          286  +            dwMoveMethod = FILE_CURRENT;
          287  +            break;
          288  +        case ZLIB_FILEFUNC_SEEK_END :
          289  +            dwMoveMethod = FILE_END;
          290  +            break;
          291  +        case ZLIB_FILEFUNC_SEEK_SET :
          292  +            dwMoveMethod = FILE_BEGIN;
          293  +            break;
          294  +        default: return -1;
          295  +    }
          296  +
          297  +    if (hFile)
          298  +    {
          299  +        LARGE_INTEGER* li = (LARGE_INTEGER*)&offset;
          300  +        DWORD dwSet = SetFilePointer(hFile, li->u.LowPart, &li->u.HighPart, dwMoveMethod);
          301  +        if (dwSet == INVALID_SET_FILE_POINTER)
          302  +        {
          303  +            DWORD dwErr = GetLastError();
          304  +            ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
          305  +            ret = -1;
          306  +        }
          307  +        else
          308  +            ret=0;
          309  +    }
          310  +    return ret;
          311  +}
          312  +
          313  +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream)
          314  +{
          315  +    int ret=-1;
          316  +
          317  +    if (stream!=NULL)
          318  +    {
          319  +        HANDLE hFile;
          320  +        hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
          321  +        if (hFile != NULL)
          322  +        {
          323  +            CloseHandle(hFile);
          324  +            ret=0;
          325  +        }
          326  +        free(stream);
          327  +    }
          328  +    return ret;
          329  +}
          330  +
          331  +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream)
          332  +{
          333  +    int ret=-1;
          334  +    if (stream!=NULL)
          335  +    {
          336  +        ret = ((WIN32FILE_IOWIN*)stream) -> error;
          337  +    }
          338  +    return ret;
          339  +}
          340  +
          341  +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def)
          342  +{
          343  +    pzlib_filefunc_def->zopen_file = win32_open_file_func;
          344  +    pzlib_filefunc_def->zread_file = win32_read_file_func;
          345  +    pzlib_filefunc_def->zwrite_file = win32_write_file_func;
          346  +    pzlib_filefunc_def->ztell_file = win32_tell_file_func;
          347  +    pzlib_filefunc_def->zseek_file = win32_seek_file_func;
          348  +    pzlib_filefunc_def->zclose_file = win32_close_file_func;
          349  +    pzlib_filefunc_def->zerror_file = win32_error_file_func;
          350  +    pzlib_filefunc_def->opaque = NULL;
          351  +}
          352  +
          353  +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def)
          354  +{
          355  +    pzlib_filefunc_def->zopen64_file = win32_open64_file_func;
          356  +    pzlib_filefunc_def->zread_file = win32_read_file_func;
          357  +    pzlib_filefunc_def->zwrite_file = win32_write_file_func;
          358  +    pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
          359  +    pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
          360  +    pzlib_filefunc_def->zclose_file = win32_close_file_func;
          361  +    pzlib_filefunc_def->zerror_file = win32_error_file_func;
          362  +    pzlib_filefunc_def->opaque = NULL;
          363  +}
          364  +
          365  +
          366  +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def)
          367  +{
          368  +    pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA;
          369  +    pzlib_filefunc_def->zread_file = win32_read_file_func;
          370  +    pzlib_filefunc_def->zwrite_file = win32_write_file_func;
          371  +    pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
          372  +    pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
          373  +    pzlib_filefunc_def->zclose_file = win32_close_file_func;
          374  +    pzlib_filefunc_def->zerror_file = win32_error_file_func;
          375  +    pzlib_filefunc_def->opaque = NULL;
          376  +}
          377  +
          378  +
          379  +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def)
          380  +{
          381  +    pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW;
          382  +    pzlib_filefunc_def->zread_file = win32_read_file_func;
          383  +    pzlib_filefunc_def->zwrite_file = win32_write_file_func;
          384  +    pzlib_filefunc_def->ztell64_file = win32_tell64_file_func;
          385  +    pzlib_filefunc_def->zseek64_file = win32_seek64_file_func;
          386  +    pzlib_filefunc_def->zclose_file = win32_close_file_func;
          387  +    pzlib_filefunc_def->zerror_file = win32_error_file_func;
          388  +    pzlib_filefunc_def->opaque = NULL;
          389  +}

Added compat/zlib/contrib/minizip/iowin32.h.

            1  +/* iowin32.h -- IO base function header for compress/uncompress .zip
            2  +     Version 1.1, February 14h, 2010
            3  +     part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications for Zip64 support
            8  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            9  +
           10  +         For more info read MiniZip_info.txt
           11  +
           12  +*/
           13  +
           14  +#include <windows.h>
           15  +
           16  +
           17  +#ifdef __cplusplus
           18  +extern "C" {
           19  +#endif
           20  +
           21  +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
           22  +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def));
           23  +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def));
           24  +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def));
           25  +
           26  +#ifdef __cplusplus
           27  +}
           28  +#endif

Added compat/zlib/contrib/minizip/make_vms.com.

            1  +$ if f$search("ioapi.h_orig") .eqs. "" then copy ioapi.h ioapi.h_orig
            2  +$ open/write zdef vmsdefs.h
            3  +$ copy sys$input: zdef
            4  +$ deck
            5  +#define unix
            6  +#define fill_zlib_filefunc64_32_def_from_filefunc32 fillzffunc64from
            7  +#define Write_Zip64EndOfCentralDirectoryLocator Write_Zip64EoDLocator
            8  +#define Write_Zip64EndOfCentralDirectoryRecord Write_Zip64EoDRecord
            9  +#define Write_EndOfCentralDirectoryRecord Write_EoDRecord
           10  +$ eod
           11  +$ close zdef
           12  +$ copy vmsdefs.h,ioapi.h_orig ioapi.h
           13  +$ cc/include=[--]/prefix=all ioapi.c
           14  +$ cc/include=[--]/prefix=all miniunz.c
           15  +$ cc/include=[--]/prefix=all unzip.c
           16  +$ cc/include=[--]/prefix=all minizip.c
           17  +$ cc/include=[--]/prefix=all zip.c
           18  +$ link miniunz,unzip,ioapi,[--]libz.olb/lib
           19  +$ link minizip,zip,ioapi,[--]libz.olb/lib
           20  +$ mcr []minizip test minizip_info.txt
           21  +$ mcr []miniunz -l test.zip
           22  +$ rename minizip_info.txt; minizip_info.txt_old
           23  +$ mcr []miniunz test.zip
           24  +$ delete test.zip;*
           25  +$exit

Added compat/zlib/contrib/minizip/miniunz.c.

            1  +/*
            2  +   miniunz.c
            3  +   Version 1.1, February 14h, 2010
            4  +   sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            5  +
            6  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            7  +
            8  +         Modifications of Unzip for Zip64
            9  +         Copyright (C) 2007-2008 Even Rouault
           10  +
           11  +         Modifications for Zip64 support on both zip and unzip
           12  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
           13  +*/
           14  +
           15  +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
           16  +        #ifndef __USE_FILE_OFFSET64
           17  +                #define __USE_FILE_OFFSET64
           18  +        #endif
           19  +        #ifndef __USE_LARGEFILE64
           20  +                #define __USE_LARGEFILE64
           21  +        #endif
           22  +        #ifndef _LARGEFILE64_SOURCE
           23  +                #define _LARGEFILE64_SOURCE
           24  +        #endif
           25  +        #ifndef _FILE_OFFSET_BIT
           26  +                #define _FILE_OFFSET_BIT 64
           27  +        #endif
           28  +#endif
           29  +
           30  +#ifdef __APPLE__
           31  +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
           32  +#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
           33  +#define FTELLO_FUNC(stream) ftello(stream)
           34  +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
           35  +#else
           36  +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
           37  +#define FTELLO_FUNC(stream) ftello64(stream)
           38  +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
           39  +#endif
           40  +
           41  +
           42  +#include <stdio.h>
           43  +#include <stdlib.h>
           44  +#include <string.h>
           45  +#include <time.h>
           46  +#include <errno.h>
           47  +#include <fcntl.h>
           48  +
           49  +#ifdef _WIN32
           50  +# include <direct.h>
           51  +# include <io.h>
           52  +#else
           53  +# include <unistd.h>
           54  +# include <utime.h>
           55  +#endif
           56  +
           57  +
           58  +#include "unzip.h"
           59  +
           60  +#define CASESENSITIVITY (0)
           61  +#define WRITEBUFFERSIZE (8192)
           62  +#define MAXFILENAME (256)
           63  +
           64  +#ifdef _WIN32
           65  +#define USEWIN32IOAPI
           66  +#include "iowin32.h"
           67  +#endif
           68  +/*
           69  +  mini unzip, demo of unzip package
           70  +
           71  +  usage :
           72  +  Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]
           73  +
           74  +  list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
           75  +    if it exists
           76  +*/
           77  +
           78  +
           79  +/* change_file_date : change the date/time of a file
           80  +    filename : the filename of the file where date/time must be modified
           81  +    dosdate : the new date at the MSDos format (4 bytes)
           82  +    tmu_date : the SAME new date at the tm_unz format */
           83  +void change_file_date(filename,dosdate,tmu_date)
           84  +    const char *filename;
           85  +    uLong dosdate;
           86  +    tm_unz tmu_date;
           87  +{
           88  +#ifdef _WIN32
           89  +  HANDLE hFile;
           90  +  FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
           91  +
           92  +  hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
           93  +                      0,NULL,OPEN_EXISTING,0,NULL);
           94  +  GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
           95  +  DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
           96  +  LocalFileTimeToFileTime(&ftLocal,&ftm);
           97  +  SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
           98  +  CloseHandle(hFile);
           99  +#else
          100  +#ifdef unix || __APPLE__
          101  +  struct utimbuf ut;
          102  +  struct tm newdate;
          103  +  newdate.tm_sec = tmu_date.tm_sec;
          104  +  newdate.tm_min=tmu_date.tm_min;
          105  +  newdate.tm_hour=tmu_date.tm_hour;
          106  +  newdate.tm_mday=tmu_date.tm_mday;
          107  +  newdate.tm_mon=tmu_date.tm_mon;
          108  +  if (tmu_date.tm_year > 1900)
          109  +      newdate.tm_year=tmu_date.tm_year - 1900;
          110  +  else
          111  +      newdate.tm_year=tmu_date.tm_year ;
          112  +  newdate.tm_isdst=-1;
          113  +
          114  +  ut.actime=ut.modtime=mktime(&newdate);
          115  +  utime(filename,&ut);
          116  +#endif
          117  +#endif
          118  +}
          119  +
          120  +
          121  +/* mymkdir and change_file_date are not 100 % portable
          122  +   As I don't know well Unix, I wait feedback for the unix portion */
          123  +
          124  +int mymkdir(dirname)
          125  +    const char* dirname;
          126  +{
          127  +    int ret=0;
          128  +#ifdef _WIN32
          129  +    ret = _mkdir(dirname);
          130  +#elif unix
          131  +    ret = mkdir (dirname,0775);
          132  +#elif __APPLE__
          133  +    ret = mkdir (dirname,0775);
          134  +#endif
          135  +    return ret;
          136  +}
          137  +
          138  +int makedir (newdir)
          139  +    char *newdir;
          140  +{
          141  +  char *buffer ;
          142  +  char *p;
          143  +  int  len = (int)strlen(newdir);
          144  +
          145  +  if (len <= 0)
          146  +    return 0;
          147  +
          148  +  buffer = (char*)malloc(len+1);
          149  +        if (buffer==NULL)
          150  +        {
          151  +                printf("Error allocating memory\n");
          152  +                return UNZ_INTERNALERROR;
          153  +        }
          154  +  strcpy(buffer,newdir);
          155  +
          156  +  if (buffer[len-1] == '/') {
          157  +    buffer[len-1] = '\0';
          158  +  }
          159  +  if (mymkdir(buffer) == 0)
          160  +    {
          161  +      free(buffer);
          162  +      return 1;
          163  +    }
          164  +
          165  +  p = buffer+1;
          166  +  while (1)
          167  +    {
          168  +      char hold;
          169  +
          170  +      while(*p && *p != '\\' && *p != '/')
          171  +        p++;
          172  +      hold = *p;
          173  +      *p = 0;
          174  +      if ((mymkdir(buffer) == -1) && (errno == ENOENT))
          175  +        {
          176  +          printf("couldn't create directory %s\n",buffer);
          177  +          free(buffer);
          178  +          return 0;
          179  +        }
          180  +      if (hold == 0)
          181  +        break;
          182  +      *p++ = hold;
          183  +    }
          184  +  free(buffer);
          185  +  return 1;
          186  +}
          187  +
          188  +void do_banner()
          189  +{
          190  +    printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
          191  +    printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
          192  +}
          193  +
          194  +void do_help()
          195  +{
          196  +    printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
          197  +           "  -e  Extract without pathname (junk paths)\n" \
          198  +           "  -x  Extract with pathname\n" \
          199  +           "  -v  list files\n" \
          200  +           "  -l  list files\n" \
          201  +           "  -d  directory to extract into\n" \
          202  +           "  -o  overwrite files without prompting\n" \
          203  +           "  -p  extract crypted file using password\n\n");
          204  +}
          205  +
          206  +void Display64BitsSize(ZPOS64_T n, int size_char)
          207  +{
          208  +  /* to avoid compatibility problem , we do here the conversion */
          209  +  char number[21];
          210  +  int offset=19;
          211  +  int pos_string = 19;
          212  +  number[20]=0;
          213  +  for (;;) {
          214  +      number[offset]=(char)((n%10)+'0');
          215  +      if (number[offset] != '0')
          216  +          pos_string=offset;
          217  +      n/=10;
          218  +      if (offset==0)
          219  +          break;
          220  +      offset--;
          221  +  }
          222  +  {
          223  +      int size_display_string = 19-pos_string;
          224  +      while (size_char > size_display_string)
          225  +      {
          226  +          size_char--;
          227  +          printf(" ");
          228  +      }
          229  +  }
          230  +
          231  +  printf("%s",&number[pos_string]);
          232  +}
          233  +
          234  +int do_list(uf)
          235  +    unzFile uf;
          236  +{
          237  +    uLong i;
          238  +    unz_global_info64 gi;
          239  +    int err;
          240  +
          241  +    err = unzGetGlobalInfo64(uf,&gi);
          242  +    if (err!=UNZ_OK)
          243  +        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
          244  +    printf("  Length  Method     Size Ratio   Date    Time   CRC-32     Name\n");
          245  +    printf("  ------  ------     ---- -----   ----    ----   ------     ----\n");
          246  +    for (i=0;i<gi.number_entry;i++)
          247  +    {
          248  +        char filename_inzip[256];
          249  +        unz_file_info64 file_info;
          250  +        uLong ratio=0;
          251  +        const char *string_method;
          252  +        char charCrypt=' ';
          253  +        err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
          254  +        if (err!=UNZ_OK)
          255  +        {
          256  +            printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
          257  +            break;
          258  +        }
          259  +        if (file_info.uncompressed_size>0)
          260  +            ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size);
          261  +
          262  +        /* display a '*' if the file is crypted */
          263  +        if ((file_info.flag & 1) != 0)
          264  +            charCrypt='*';
          265  +
          266  +        if (file_info.compression_method==0)
          267  +            string_method="Stored";
          268  +        else
          269  +        if (file_info.compression_method==Z_DEFLATED)
          270  +        {
          271  +            uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
          272  +            if (iLevel==0)
          273  +              string_method="Defl:N";
          274  +            else if (iLevel==1)
          275  +              string_method="Defl:X";
          276  +            else if ((iLevel==2) || (iLevel==3))
          277  +              string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
          278  +        }
          279  +        else
          280  +        if (file_info.compression_method==Z_BZIP2ED)
          281  +        {
          282  +              string_method="BZip2 ";
          283  +        }
          284  +        else
          285  +            string_method="Unkn. ";
          286  +
          287  +        Display64BitsSize(file_info.uncompressed_size,7);
          288  +        printf("  %6s%c",string_method,charCrypt);
          289  +        Display64BitsSize(file_info.compressed_size,7);
          290  +        printf(" %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s\n",
          291  +                ratio,
          292  +                (uLong)file_info.tmu_date.tm_mon + 1,
          293  +                (uLong)file_info.tmu_date.tm_mday,
          294  +                (uLong)file_info.tmu_date.tm_year % 100,
          295  +                (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
          296  +                (uLong)file_info.crc,filename_inzip);
          297  +        if ((i+1)<gi.number_entry)
          298  +        {
          299  +            err = unzGoToNextFile(uf);
          300  +            if (err!=UNZ_OK)
          301  +            {
          302  +                printf("error %d with zipfile in unzGoToNextFile\n",err);
          303  +                break;
          304  +            }
          305  +        }
          306  +    }
          307  +
          308  +    return 0;
          309  +}
          310  +
          311  +
          312  +int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
          313  +    unzFile uf;
          314  +    const int* popt_extract_without_path;
          315  +    int* popt_overwrite;
          316  +    const char* password;
          317  +{
          318  +    char filename_inzip[256];
          319  +    char* filename_withoutpath;
          320  +    char* p;
          321  +    int err=UNZ_OK;
          322  +    FILE *fout=NULL;
          323  +    void* buf;
          324  +    uInt size_buf;
          325  +
          326  +    unz_file_info64 file_info;
          327  +    uLong ratio=0;
          328  +    err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
          329  +
          330  +    if (err!=UNZ_OK)
          331  +    {
          332  +        printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
          333  +        return err;
          334  +    }
          335  +
          336  +    size_buf = WRITEBUFFERSIZE;
          337  +    buf = (void*)malloc(size_buf);
          338  +    if (buf==NULL)
          339  +    {
          340  +        printf("Error allocating memory\n");
          341  +        return UNZ_INTERNALERROR;
          342  +    }
          343  +
          344  +    p = filename_withoutpath = filename_inzip;
          345  +    while ((*p) != '\0')
          346  +    {
          347  +        if (((*p)=='/') || ((*p)=='\\'))
          348  +            filename_withoutpath = p+1;
          349  +        p++;
          350  +    }
          351  +
          352  +    if ((*filename_withoutpath)=='\0')
          353  +    {
          354  +        if ((*popt_extract_without_path)==0)
          355  +        {
          356  +            printf("creating directory: %s\n",filename_inzip);
          357  +            mymkdir(filename_inzip);
          358  +        }
          359  +    }
          360  +    else
          361  +    {
          362  +        const char* write_filename;
          363  +        int skip=0;
          364  +
          365  +        if ((*popt_extract_without_path)==0)
          366  +            write_filename = filename_inzip;
          367  +        else
          368  +            write_filename = filename_withoutpath;
          369  +
          370  +        err = unzOpenCurrentFilePassword(uf,password);
          371  +        if (err!=UNZ_OK)
          372  +        {
          373  +            printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
          374  +        }
          375  +
          376  +        if (((*popt_overwrite)==0) && (err==UNZ_OK))
          377  +        {
          378  +            char rep=0;
          379  +            FILE* ftestexist;
          380  +            ftestexist = FOPEN_FUNC(write_filename,"rb");
          381  +            if (ftestexist!=NULL)
          382  +            {
          383  +                fclose(ftestexist);
          384  +                do
          385  +                {
          386  +                    char answer[128];
          387  +                    int ret;
          388  +
          389  +                    printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
          390  +                    ret = scanf("%1s",answer);
          391  +                    if (ret != 1)
          392  +                    {
          393  +                       exit(EXIT_FAILURE);
          394  +                    }
          395  +                    rep = answer[0] ;
          396  +                    if ((rep>='a') && (rep<='z'))
          397  +                        rep -= 0x20;
          398  +                }
          399  +                while ((rep!='Y') && (rep!='N') && (rep!='A'));
          400  +            }
          401  +
          402  +            if (rep == 'N')
          403  +                skip = 1;
          404  +
          405  +            if (rep == 'A')
          406  +                *popt_overwrite=1;
          407  +        }
          408  +
          409  +        if ((skip==0) && (err==UNZ_OK))
          410  +        {
          411  +            fout=FOPEN_FUNC(write_filename,"wb");
          412  +            /* some zipfile don't contain directory alone before file */
          413  +            if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
          414  +                                (filename_withoutpath!=(char*)filename_inzip))
          415  +            {
          416  +                char c=*(filename_withoutpath-1);
          417  +                *(filename_withoutpath-1)='\0';
          418  +                makedir(write_filename);
          419  +                *(filename_withoutpath-1)=c;
          420  +                fout=FOPEN_FUNC(write_filename,"wb");
          421  +            }
          422  +
          423  +            if (fout==NULL)
          424  +            {
          425  +                printf("error opening %s\n",write_filename);
          426  +            }
          427  +        }
          428  +
          429  +        if (fout!=NULL)
          430  +        {
          431  +            printf(" extracting: %s\n",write_filename);
          432  +
          433  +            do
          434  +            {
          435  +                err = unzReadCurrentFile(uf,buf,size_buf);
          436  +                if (err<0)
          437  +                {
          438  +                    printf("error %d with zipfile in unzReadCurrentFile\n",err);
          439  +                    break;
          440  +                }
          441  +                if (err>0)
          442  +                    if (fwrite(buf,err,1,fout)!=1)
          443  +                    {
          444  +                        printf("error in writing extracted file\n");
          445  +                        err=UNZ_ERRNO;
          446  +                        break;
          447  +                    }
          448  +            }
          449  +            while (err>0);
          450  +            if (fout)
          451  +                    fclose(fout);
          452  +
          453  +            if (err==0)
          454  +                change_file_date(write_filename,file_info.dosDate,
          455  +                                 file_info.tmu_date);
          456  +        }
          457  +
          458  +        if (err==UNZ_OK)
          459  +        {
          460  +            err = unzCloseCurrentFile (uf);
          461  +            if (err!=UNZ_OK)
          462  +            {
          463  +                printf("error %d with zipfile in unzCloseCurrentFile\n",err);
          464  +            }
          465  +        }
          466  +        else
          467  +            unzCloseCurrentFile(uf); /* don't lose the error */
          468  +    }
          469  +
          470  +    free(buf);
          471  +    return err;
          472  +}
          473  +
          474  +
          475  +int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
          476  +    unzFile uf;
          477  +    int opt_extract_without_path;
          478  +    int opt_overwrite;
          479  +    const char* password;
          480  +{
          481  +    uLong i;
          482  +    unz_global_info64 gi;
          483  +    int err;
          484  +    FILE* fout=NULL;
          485  +
          486  +    err = unzGetGlobalInfo64(uf,&gi);
          487  +    if (err!=UNZ_OK)
          488  +        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
          489  +
          490  +    for (i=0;i<gi.number_entry;i++)
          491  +    {
          492  +        if (do_extract_currentfile(uf,&opt_extract_without_path,
          493  +                                      &opt_overwrite,
          494  +                                      password) != UNZ_OK)
          495  +            break;
          496  +
          497  +        if ((i+1)<gi.number_entry)
          498  +        {
          499  +            err = unzGoToNextFile(uf);
          500  +            if (err!=UNZ_OK)
          501  +            {
          502  +                printf("error %d with zipfile in unzGoToNextFile\n",err);
          503  +                break;
          504  +            }
          505  +        }
          506  +    }
          507  +
          508  +    return 0;
          509  +}
          510  +
          511  +int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
          512  +    unzFile uf;
          513  +    const char* filename;
          514  +    int opt_extract_without_path;
          515  +    int opt_overwrite;
          516  +    const char* password;
          517  +{
          518  +    int err = UNZ_OK;
          519  +    if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
          520  +    {
          521  +        printf("file %s not found in the zipfile\n",filename);
          522  +        return 2;
          523  +    }
          524  +
          525  +    if (do_extract_currentfile(uf,&opt_extract_without_path,
          526  +                                      &opt_overwrite,
          527  +                                      password) == UNZ_OK)
          528  +        return 0;
          529  +    else
          530  +        return 1;
          531  +}
          532  +
          533  +
          534  +int main(argc,argv)
          535  +    int argc;
          536  +    char *argv[];
          537  +{
          538  +    const char *zipfilename=NULL;
          539  +    const char *filename_to_extract=NULL;
          540  +    const char *password=NULL;
          541  +    char filename_try[MAXFILENAME+16] = "";
          542  +    int i;
          543  +    int ret_value=0;
          544  +    int opt_do_list=0;
          545  +    int opt_do_extract=1;
          546  +    int opt_do_extract_withoutpath=0;
          547  +    int opt_overwrite=0;
          548  +    int opt_extractdir=0;
          549  +    const char *dirname=NULL;
          550  +    unzFile uf=NULL;
          551  +
          552  +    do_banner();
          553  +    if (argc==1)
          554  +    {
          555  +        do_help();
          556  +        return 0;
          557  +    }
          558  +    else
          559  +    {
          560  +        for (i=1;i<argc;i++)
          561  +        {
          562  +            if ((*argv[i])=='-')
          563  +            {
          564  +                const char *p=argv[i]+1;
          565  +
          566  +                while ((*p)!='\0')
          567  +                {
          568  +                    char c=*(p++);;
          569  +                    if ((c=='l') || (c=='L'))
          570  +                        opt_do_list = 1;
          571  +                    if ((c=='v') || (c=='V'))
          572  +                        opt_do_list = 1;
          573  +                    if ((c=='x') || (c=='X'))
          574  +                        opt_do_extract = 1;
          575  +                    if ((c=='e') || (c=='E'))
          576  +                        opt_do_extract = opt_do_extract_withoutpath = 1;
          577  +                    if ((c=='o') || (c=='O'))
          578  +                        opt_overwrite=1;
          579  +                    if ((c=='d') || (c=='D'))
          580  +                    {
          581  +                        opt_extractdir=1;
          582  +                        dirname=argv[i+1];
          583  +                    }
          584  +
          585  +                    if (((c=='p') || (c=='P')) && (i+1<argc))
          586  +                    {
          587  +                        password=argv[i+1];
          588  +                        i++;
          589  +                    }
          590  +                }
          591  +            }
          592  +            else
          593  +            {
          594  +                if (zipfilename == NULL)
          595  +                    zipfilename = argv[i];
          596  +                else if ((filename_to_extract==NULL) && (!opt_extractdir))
          597  +                        filename_to_extract = argv[i] ;
          598  +            }
          599  +        }
          600  +    }
          601  +
          602  +    if (zipfilename!=NULL)
          603  +    {
          604  +
          605  +#        ifdef USEWIN32IOAPI
          606  +        zlib_filefunc64_def ffunc;
          607  +#        endif
          608  +
          609  +        strncpy(filename_try, zipfilename,MAXFILENAME-1);
          610  +        /* strncpy doesnt append the trailing NULL, of the string is too long. */
          611  +        filename_try[ MAXFILENAME ] = '\0';
          612  +
          613  +#        ifdef USEWIN32IOAPI
          614  +        fill_win32_filefunc64A(&ffunc);
          615  +        uf = unzOpen2_64(zipfilename,&ffunc);
          616  +#        else
          617  +        uf = unzOpen64(zipfilename);
          618  +#        endif
          619  +        if (uf==NULL)
          620  +        {
          621  +            strcat(filename_try,".zip");
          622  +#            ifdef USEWIN32IOAPI
          623  +            uf = unzOpen2_64(filename_try,&ffunc);
          624  +#            else
          625  +            uf = unzOpen64(filename_try);
          626  +#            endif
          627  +        }
          628  +    }
          629  +
          630  +    if (uf==NULL)
          631  +    {
          632  +        printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
          633  +        return 1;
          634  +    }
          635  +    printf("%s opened\n",filename_try);
          636  +
          637  +    if (opt_do_list==1)
          638  +        ret_value = do_list(uf);
          639  +    else if (opt_do_extract==1)
          640  +    {
          641  +#ifdef _WIN32
          642  +        if (opt_extractdir && _chdir(dirname))
          643  +#else
          644  +        if (opt_extractdir && chdir(dirname))
          645  +#endif
          646  +        {
          647  +          printf("Error changing into %s, aborting\n", dirname);
          648  +          exit(-1);
          649  +        }
          650  +
          651  +        if (filename_to_extract == NULL)
          652  +            ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
          653  +        else
          654  +            ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);
          655  +    }
          656  +
          657  +    unzClose(uf);
          658  +
          659  +    return ret_value;
          660  +}

Added compat/zlib/contrib/minizip/minizip.c.

            1  +/*
            2  +   minizip.c
            3  +   Version 1.1, February 14h, 2010
            4  +   sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            5  +
            6  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            7  +
            8  +         Modifications of Unzip for Zip64
            9  +         Copyright (C) 2007-2008 Even Rouault
           10  +
           11  +         Modifications for Zip64 support on both zip and unzip
           12  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
           13  +*/
           14  +
           15  +
           16  +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
           17  +        #ifndef __USE_FILE_OFFSET64
           18  +                #define __USE_FILE_OFFSET64
           19  +        #endif
           20  +        #ifndef __USE_LARGEFILE64
           21  +                #define __USE_LARGEFILE64
           22  +        #endif
           23  +        #ifndef _LARGEFILE64_SOURCE
           24  +                #define _LARGEFILE64_SOURCE
           25  +        #endif
           26  +        #ifndef _FILE_OFFSET_BIT
           27  +                #define _FILE_OFFSET_BIT 64
           28  +        #endif
           29  +#endif
           30  +
           31  +#ifdef __APPLE__
           32  +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
           33  +#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
           34  +#define FTELLO_FUNC(stream) ftello(stream)
           35  +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
           36  +#else
           37  +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
           38  +#define FTELLO_FUNC(stream) ftello64(stream)
           39  +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
           40  +#endif
           41  +
           42  +
           43  +
           44  +#include <stdio.h>
           45  +#include <stdlib.h>
           46  +#include <string.h>
           47  +#include <time.h>
           48  +#include <errno.h>
           49  +#include <fcntl.h>
           50  +
           51  +#ifdef _WIN32
           52  +# include <direct.h>
           53  +# include <io.h>
           54  +#else
           55  +# include <unistd.h>
           56  +# include <utime.h>
           57  +# include <sys/types.h>
           58  +# include <sys/stat.h>
           59  +#endif
           60  +
           61  +#include "zip.h"
           62  +
           63  +#ifdef _WIN32
           64  +        #define USEWIN32IOAPI
           65  +        #include "iowin32.h"
           66  +#endif
           67  +
           68  +
           69  +
           70  +#define WRITEBUFFERSIZE (16384)
           71  +#define MAXFILENAME (256)
           72  +
           73  +#ifdef _WIN32
           74  +uLong filetime(f, tmzip, dt)
           75  +    char *f;                /* name of file to get info on */
           76  +    tm_zip *tmzip;             /* return value: access, modific. and creation times */
           77  +    uLong *dt;             /* dostime */
           78  +{
           79  +  int ret = 0;
           80  +  {
           81  +      FILETIME ftLocal;
           82  +      HANDLE hFind;
           83  +      WIN32_FIND_DATAA ff32;
           84  +
           85  +      hFind = FindFirstFileA(f,&ff32);
           86  +      if (hFind != INVALID_HANDLE_VALUE)
           87  +      {
           88  +        FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
           89  +        FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
           90  +        FindClose(hFind);
           91  +        ret = 1;
           92  +      }
           93  +  }
           94  +  return ret;
           95  +}
           96  +#else
           97  +#ifdef unix || __APPLE__
           98  +uLong filetime(f, tmzip, dt)
           99  +    char *f;               /* name of file to get info on */
          100  +    tm_zip *tmzip;         /* return value: access, modific. and creation times */
          101  +    uLong *dt;             /* dostime */
          102  +{
          103  +  int ret=0;
          104  +  struct stat s;        /* results of stat() */
          105  +  struct tm* filedate;
          106  +  time_t tm_t=0;
          107  +
          108  +  if (strcmp(f,"-")!=0)
          109  +  {
          110  +    char name[MAXFILENAME+1];
          111  +    int len = strlen(f);
          112  +    if (len > MAXFILENAME)
          113  +      len = MAXFILENAME;
          114  +
          115  +    strncpy(name, f,MAXFILENAME-1);
          116  +    /* strncpy doesnt append the trailing NULL, of the string is too long. */
          117  +    name[ MAXFILENAME ] = '\0';
          118  +
          119  +    if (name[len - 1] == '/')
          120  +      name[len - 1] = '\0';
          121  +    /* not all systems allow stat'ing a file with / appended */
          122  +    if (stat(name,&s)==0)
          123  +    {
          124  +      tm_t = s.st_mtime;
          125  +      ret = 1;
          126  +    }
          127  +  }
          128  +  filedate = localtime(&tm_t);
          129  +
          130  +  tmzip->tm_sec  = filedate->tm_sec;
          131  +  tmzip->tm_min  = filedate->tm_min;
          132  +  tmzip->tm_hour = filedate->tm_hour;
          133  +  tmzip->tm_mday = filedate->tm_mday;
          134  +  tmzip->tm_mon  = filedate->tm_mon ;
          135  +  tmzip->tm_year = filedate->tm_year;
          136  +
          137  +  return ret;
          138  +}
          139  +#else
          140  +uLong filetime(f, tmzip, dt)
          141  +    char *f;                /* name of file to get info on */
          142  +    tm_zip *tmzip;             /* return value: access, modific. and creation times */
          143  +    uLong *dt;             /* dostime */
          144  +{
          145  +    return 0;
          146  +}
          147  +#endif
          148  +#endif
          149  +
          150  +
          151  +
          152  +
          153  +int check_exist_file(filename)
          154  +    const char* filename;
          155  +{
          156  +    FILE* ftestexist;
          157  +    int ret = 1;
          158  +    ftestexist = FOPEN_FUNC(filename,"rb");
          159  +    if (ftestexist==NULL)
          160  +        ret = 0;
          161  +    else
          162  +        fclose(ftestexist);
          163  +    return ret;
          164  +}
          165  +
          166  +void do_banner()
          167  +{
          168  +    printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n");
          169  +    printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n");
          170  +}
          171  +
          172  +void do_help()
          173  +{
          174  +    printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \
          175  +           "  -o  Overwrite existing file.zip\n" \
          176  +           "  -a  Append to existing file.zip\n" \
          177  +           "  -0  Store only\n" \
          178  +           "  -1  Compress faster\n" \
          179  +           "  -9  Compress better\n\n" \
          180  +           "  -j  exclude path. store only the file name.\n\n");
          181  +}
          182  +
          183  +/* calculate the CRC32 of a file,
          184  +   because to encrypt a file, we need known the CRC32 of the file before */
          185  +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
          186  +{
          187  +   unsigned long calculate_crc=0;
          188  +   int err=ZIP_OK;
          189  +   FILE * fin = FOPEN_FUNC(filenameinzip,"rb");
          190  +
          191  +   unsigned long size_read = 0;
          192  +   unsigned long total_read = 0;
          193  +   if (fin==NULL)
          194  +   {
          195  +       err = ZIP_ERRNO;
          196  +   }
          197  +
          198  +    if (err == ZIP_OK)
          199  +        do
          200  +        {
          201  +            err = ZIP_OK;
          202  +            size_read = (int)fread(buf,1,size_buf,fin);
          203  +            if (size_read < size_buf)
          204  +                if (feof(fin)==0)
          205  +            {
          206  +                printf("error in reading %s\n",filenameinzip);
          207  +                err = ZIP_ERRNO;
          208  +            }
          209  +
          210  +            if (size_read>0)
          211  +                calculate_crc = crc32(calculate_crc,buf,size_read);
          212  +            total_read += size_read;
          213  +
          214  +        } while ((err == ZIP_OK) && (size_read>0));
          215  +
          216  +    if (fin)
          217  +        fclose(fin);
          218  +
          219  +    *result_crc=calculate_crc;
          220  +    printf("file %s crc %lx\n", filenameinzip, calculate_crc);
          221  +    return err;
          222  +}
          223  +
          224  +int isLargeFile(const char* filename)
          225  +{
          226  +  int largeFile = 0;
          227  +  ZPOS64_T pos = 0;
          228  +  FILE* pFile = FOPEN_FUNC(filename, "rb");
          229  +
          230  +  if(pFile != NULL)
          231  +  {
          232  +    int n = FSEEKO_FUNC(pFile, 0, SEEK_END);
          233  +    pos = FTELLO_FUNC(pFile);
          234  +
          235  +                printf("File : %s is %lld bytes\n", filename, pos);
          236  +
          237  +    if(pos >= 0xffffffff)
          238  +     largeFile = 1;
          239  +
          240  +                fclose(pFile);
          241  +  }
          242  +
          243  + return largeFile;
          244  +}
          245  +
          246  +int main(argc,argv)
          247  +    int argc;
          248  +    char *argv[];
          249  +{
          250  +    int i;
          251  +    int opt_overwrite=0;
          252  +    int opt_compress_level=Z_DEFAULT_COMPRESSION;
          253  +    int opt_exclude_path=0;
          254  +    int zipfilenamearg = 0;
          255  +    char filename_try[MAXFILENAME+16];
          256  +    int zipok;
          257  +    int err=0;
          258  +    int size_buf=0;
          259  +    void* buf=NULL;
          260  +    const char* password=NULL;
          261  +
          262  +
          263  +    do_banner();
          264  +    if (argc==1)
          265  +    {
          266  +        do_help();
          267  +        return 0;
          268  +    }
          269  +    else
          270  +    {
          271  +        for (i=1;i<argc;i++)
          272  +        {
          273  +            if ((*argv[i])=='-')
          274  +            {
          275  +                const char *p=argv[i]+1;
          276  +
          277  +                while ((*p)!='\0')
          278  +                {
          279  +                    char c=*(p++);;
          280  +                    if ((c=='o') || (c=='O'))
          281  +                        opt_overwrite = 1;
          282  +                    if ((c=='a') || (c=='A'))
          283  +                        opt_overwrite = 2;
          284  +                    if ((c>='0') && (c<='9'))
          285  +                        opt_compress_level = c-'0';
          286  +                    if ((c=='j') || (c=='J'))
          287  +                        opt_exclude_path = 1;
          288  +
          289  +                    if (((c=='p') || (c=='P')) && (i+1<argc))
          290  +                    {
          291  +                        password=argv[i+1];
          292  +                        i++;
          293  +                    }
          294  +                }
          295  +            }
          296  +            else
          297  +            {
          298  +                if (zipfilenamearg == 0)
          299  +                {
          300  +                    zipfilenamearg = i ;
          301  +                }
          302  +            }
          303  +        }
          304  +    }
          305  +
          306  +    size_buf = WRITEBUFFERSIZE;
          307  +    buf = (void*)malloc(size_buf);
          308  +    if (buf==NULL)
          309  +    {
          310  +        printf("Error allocating memory\n");
          311  +        return ZIP_INTERNALERROR;
          312  +    }
          313  +
          314  +    if (zipfilenamearg==0)
          315  +    {
          316  +        zipok=0;
          317  +    }
          318  +    else
          319  +    {
          320  +        int i,len;
          321  +        int dot_found=0;
          322  +
          323  +        zipok = 1 ;
          324  +        strncpy(filename_try, argv[zipfilenamearg],MAXFILENAME-1);
          325  +        /* strncpy doesnt append the trailing NULL, of the string is too long. */
          326  +        filename_try[ MAXFILENAME ] = '\0';
          327  +
          328  +        len=(int)strlen(filename_try);
          329  +        for (i=0;i<len;i++)
          330  +            if (filename_try[i]=='.')
          331  +                dot_found=1;
          332  +
          333  +        if (dot_found==0)
          334  +            strcat(filename_try,".zip");
          335  +
          336  +        if (opt_overwrite==2)
          337  +        {
          338  +            /* if the file don't exist, we not append file */
          339  +            if (check_exist_file(filename_try)==0)
          340  +                opt_overwrite=1;
          341  +        }
          342  +        else
          343  +        if (opt_overwrite==0)
          344  +            if (check_exist_file(filename_try)!=0)
          345  +            {
          346  +                char rep=0;
          347  +                do
          348  +                {
          349  +                    char answer[128];
          350  +                    int ret;
          351  +                    printf("The file %s exists. Overwrite ? [y]es, [n]o, [a]ppend : ",filename_try);
          352  +                    ret = scanf("%1s",answer);
          353  +                    if (ret != 1)
          354  +                    {
          355  +                       exit(EXIT_FAILURE);
          356  +                    }
          357  +                    rep = answer[0] ;
          358  +                    if ((rep>='a') && (rep<='z'))
          359  +                        rep -= 0x20;
          360  +                }
          361  +                while ((rep!='Y') && (rep!='N') && (rep!='A'));
          362  +                if (rep=='N')
          363  +                    zipok = 0;
          364  +                if (rep=='A')
          365  +                    opt_overwrite = 2;
          366  +            }
          367  +    }
          368  +
          369  +    if (zipok==1)
          370  +    {
          371  +        zipFile zf;
          372  +        int errclose;
          373  +#        ifdef USEWIN32IOAPI
          374  +        zlib_filefunc64_def ffunc;
          375  +        fill_win32_filefunc64A(&ffunc);
          376  +        zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc);
          377  +#        else
          378  +        zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0);
          379  +#        endif
          380  +
          381  +        if (zf == NULL)
          382  +        {
          383  +            printf("error opening %s\n",filename_try);
          384  +            err= ZIP_ERRNO;
          385  +        }
          386  +        else
          387  +            printf("creating %s\n",filename_try);
          388  +
          389  +        for (i=zipfilenamearg+1;(i<argc) && (err==ZIP_OK);i++)
          390  +        {
          391  +            if (!((((*(argv[i]))=='-') || ((*(argv[i]))=='/')) &&
          392  +                  ((argv[i][1]=='o') || (argv[i][1]=='O') ||
          393  +                   (argv[i][1]=='a') || (argv[i][1]=='A') ||
          394  +                   (argv[i][1]=='p') || (argv[i][1]=='P') ||
          395  +                   ((argv[i][1]>='0') || (argv[i][1]<='9'))) &&
          396  +                  (strlen(argv[i]) == 2)))
          397  +            {
          398  +                FILE * fin;
          399  +                int size_read;
          400  +                const char* filenameinzip = argv[i];
          401  +                const char *savefilenameinzip;
          402  +                zip_fileinfo zi;
          403  +                unsigned long crcFile=0;
          404  +                int zip64 = 0;
          405  +
          406  +                zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
          407  +                zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
          408  +                zi.dosDate = 0;
          409  +                zi.internal_fa = 0;
          410  +                zi.external_fa = 0;
          411  +                filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
          412  +
          413  +/*
          414  +                err = zipOpenNewFileInZip(zf,filenameinzip,&zi,
          415  +                                 NULL,0,NULL,0,NULL / * comment * /,
          416  +                                 (opt_compress_level != 0) ? Z_DEFLATED : 0,
          417  +                                 opt_compress_level);
          418  +*/
          419  +                if ((password != NULL) && (err==ZIP_OK))
          420  +                    err = getFileCrc(filenameinzip,buf,size_buf,&crcFile);
          421  +
          422  +                zip64 = isLargeFile(filenameinzip);
          423  +
          424  +                                                         /* The path name saved, should not include a leading slash. */
          425  +               /*if it did, windows/xp and dynazip couldn't read the zip file. */
          426  +                 savefilenameinzip = filenameinzip;
          427  +                 while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' )
          428  +                 {
          429  +                     savefilenameinzip++;
          430  +                 }
          431  +
          432  +                 /*should the zip file contain any path at all?*/
          433  +                 if( opt_exclude_path )
          434  +                 {
          435  +                     const char *tmpptr;
          436  +                     const char *lastslash = 0;
          437  +                     for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++)
          438  +                     {
          439  +                         if( *tmpptr == '\\' || *tmpptr == '/')
          440  +                         {
          441  +                             lastslash = tmpptr;
          442  +                         }
          443  +                     }
          444  +                     if( lastslash != NULL )
          445  +                     {
          446  +                         savefilenameinzip = lastslash+1; // base filename follows last slash.
          447  +                     }
          448  +                 }
          449  +
          450  +                 /**/
          451  +                err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
          452  +                                 NULL,0,NULL,0,NULL /* comment*/,
          453  +                                 (opt_compress_level != 0) ? Z_DEFLATED : 0,
          454  +                                 opt_compress_level,0,
          455  +                                 /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
          456  +                                 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
          457  +                                 password,crcFile, zip64);
          458  +
          459  +                if (err != ZIP_OK)
          460  +                    printf("error in opening %s in zipfile\n",filenameinzip);
          461  +                else
          462  +                {
          463  +                    fin = FOPEN_FUNC(filenameinzip,"rb");
          464  +                    if (fin==NULL)
          465  +                    {
          466  +                        err=ZIP_ERRNO;
          467  +                        printf("error in opening %s for reading\n",filenameinzip);
          468  +                    }
          469  +                }
          470  +
          471  +                if (err == ZIP_OK)
          472  +                    do
          473  +                    {
          474  +                        err = ZIP_OK;
          475  +                        size_read = (int)fread(buf,1,size_buf,fin);
          476  +                        if (size_read < size_buf)
          477  +                            if (feof(fin)==0)
          478  +                        {
          479  +                            printf("error in reading %s\n",filenameinzip);
          480  +                            err = ZIP_ERRNO;
          481  +                        }
          482  +
          483  +                        if (size_read>0)
          484  +                        {
          485  +                            err = zipWriteInFileInZip (zf,buf,size_read);
          486  +                            if (err<0)
          487  +                            {
          488  +                                printf("error in writing %s in the zipfile\n",
          489  +                                                 filenameinzip);
          490  +                            }
          491  +
          492  +                        }
          493  +                    } while ((err == ZIP_OK) && (size_read>0));
          494  +
          495  +                if (fin)
          496  +                    fclose(fin);
          497  +
          498  +                if (err<0)
          499  +                    err=ZIP_ERRNO;
          500  +                else
          501  +                {
          502  +                    err = zipCloseFileInZip(zf);
          503  +                    if (err!=ZIP_OK)
          504  +                        printf("error in closing %s in the zipfile\n",
          505  +                                    filenameinzip);
          506  +                }
          507  +            }
          508  +        }
          509  +        errclose = zipClose(zf,NULL);
          510  +        if (errclose != ZIP_OK)
          511  +            printf("error in closing %s\n",filename_try);
          512  +    }
          513  +    else
          514  +    {
          515  +       do_help();
          516  +    }
          517  +
          518  +    free(buf);
          519  +    return 0;
          520  +}

Added compat/zlib/contrib/minizip/minizip.pc.in.

            1  +prefix=@prefix@
            2  +exec_prefix=@exec_prefix@
            3  +libdir=@libdir@
            4  +includedir=@includedir@/minizip
            5  +
            6  +Name: minizip
            7  +Description: Minizip zip file manipulation library
            8  +Requires:
            9  +Version: @PACKAGE_VERSION@
           10  +Libs: -L${libdir} -lminizip
           11  +Libs.private: -lz
           12  +Cflags: -I${includedir}

Added compat/zlib/contrib/minizip/mztools.c.

            1  +/*
            2  +  Additional tools for Minizip
            3  +  Code: Xavier Roche '2004
            4  +  License: Same as ZLIB (www.gzip.org)
            5  +*/
            6  +
            7  +/* Code */
            8  +#include <stdio.h>
            9  +#include <stdlib.h>
           10  +#include <string.h>
           11  +#include "zlib.h"
           12  +#include "unzip.h"
           13  +
           14  +#define READ_8(adr)  ((unsigned char)*(adr))
           15  +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
           16  +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
           17  +
           18  +#define WRITE_8(buff, n) do { \
           19  +  *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
           20  +} while(0)
           21  +#define WRITE_16(buff, n) do { \
           22  +  WRITE_8((unsigned char*)(buff), n); \
           23  +  WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
           24  +} while(0)
           25  +#define WRITE_32(buff, n) do { \
           26  +  WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
           27  +  WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
           28  +} while(0)
           29  +
           30  +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
           31  +const char* file;
           32  +const char* fileOut;
           33  +const char* fileOutTmp;
           34  +uLong* nRecovered;
           35  +uLong* bytesRecovered;
           36  +{
           37  +  int err = Z_OK;
           38  +  FILE* fpZip = fopen(file, "rb");
           39  +  FILE* fpOut = fopen(fileOut, "wb");
           40  +  FILE* fpOutCD = fopen(fileOutTmp, "wb");
           41  +  if (fpZip != NULL &&  fpOut != NULL) {
           42  +    int entries = 0;
           43  +    uLong totalBytes = 0;
           44  +    char header[30];
           45  +    char filename[1024];
           46  +    char extra[1024];
           47  +    int offset = 0;
           48  +    int offsetCD = 0;
           49  +    while ( fread(header, 1, 30, fpZip) == 30 ) {
           50  +      int currentOffset = offset;
           51  +
           52  +      /* File entry */
           53  +      if (READ_32(header) == 0x04034b50) {
           54  +        unsigned int version = READ_16(header + 4);
           55  +        unsigned int gpflag = READ_16(header + 6);
           56  +        unsigned int method = READ_16(header + 8);
           57  +        unsigned int filetime = READ_16(header + 10);
           58  +        unsigned int filedate = READ_16(header + 12);
           59  +        unsigned int crc = READ_32(header + 14); /* crc */
           60  +        unsigned int cpsize = READ_32(header + 18); /* compressed size */
           61  +        unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
           62  +        unsigned int fnsize = READ_16(header + 26); /* file name length */
           63  +        unsigned int extsize = READ_16(header + 28); /* extra field length */
           64  +        filename[0] = extra[0] = '\0';
           65  +
           66  +        /* Header */
           67  +        if (fwrite(header, 1, 30, fpOut) == 30) {
           68  +          offset += 30;
           69  +        } else {
           70  +          err = Z_ERRNO;
           71  +          break;
           72  +        }
           73  +
           74  +        /* Filename */
           75  +        if (fnsize > 0) {
           76  +          if (fnsize < sizeof(filename)) {
           77  +            if (fread(filename, 1, fnsize, fpZip) == fnsize) {
           78  +                if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
           79  +                offset += fnsize;
           80  +              } else {
           81  +                err = Z_ERRNO;
           82  +                break;
           83  +              }
           84  +            } else {
           85  +              err = Z_ERRNO;
           86  +              break;
           87  +            }
           88  +          } else {
           89  +            err = Z_ERRNO;
           90  +            break;
           91  +          }
           92  +        } else {
           93  +          err = Z_STREAM_ERROR;
           94  +          break;
           95  +        }
           96  +
           97  +        /* Extra field */
           98  +        if (extsize > 0) {
           99  +          if (extsize < sizeof(extra)) {
          100  +            if (fread(extra, 1, extsize, fpZip) == extsize) {
          101  +              if (fwrite(extra, 1, extsize, fpOut) == extsize) {
          102  +                offset += extsize;
          103  +                } else {
          104  +                err = Z_ERRNO;
          105  +                break;
          106  +              }
          107  +            } else {
          108  +              err = Z_ERRNO;
          109  +              break;
          110  +            }
          111  +          } else {
          112  +            err = Z_ERRNO;
          113  +            break;
          114  +          }
          115  +        }
          116  +
          117  +        /* Data */
          118  +        {
          119  +          int dataSize = cpsize;
          120  +          if (dataSize == 0) {
          121  +            dataSize = uncpsize;
          122  +          }
          123  +          if (dataSize > 0) {
          124  +            char* data = malloc(dataSize);
          125  +            if (data != NULL) {
          126  +              if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
          127  +                if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
          128  +                  offset += dataSize;
          129  +                  totalBytes += dataSize;
          130  +                } else {
          131  +                  err = Z_ERRNO;
          132  +                }
          133  +              } else {
          134  +                err = Z_ERRNO;
          135  +              }
          136  +              free(data);
          137  +              if (err != Z_OK) {
          138  +                break;
          139  +              }
          140  +            } else {
          141  +              err = Z_MEM_ERROR;
          142  +              break;
          143  +            }
          144  +          }
          145  +        }
          146  +
          147  +        /* Central directory entry */
          148  +        {
          149  +          char header[46];
          150  +          char* comment = "";
          151  +          int comsize = (int) strlen(comment);
          152  +          WRITE_32(header, 0x02014b50);
          153  +          WRITE_16(header + 4, version);
          154  +          WRITE_16(header + 6, version);
          155  +          WRITE_16(header + 8, gpflag);
          156  +          WRITE_16(header + 10, method);
          157  +          WRITE_16(header + 12, filetime);
          158  +          WRITE_16(header + 14, filedate);
          159  +          WRITE_32(header + 16, crc);
          160  +          WRITE_32(header + 20, cpsize);
          161  +          WRITE_32(header + 24, uncpsize);
          162  +          WRITE_16(header + 28, fnsize);
          163  +          WRITE_16(header + 30, extsize);
          164  +          WRITE_16(header + 32, comsize);
          165  +          WRITE_16(header + 34, 0);     /* disk # */
          166  +          WRITE_16(header + 36, 0);     /* int attrb */
          167  +          WRITE_32(header + 38, 0);     /* ext attrb */
          168  +          WRITE_32(header + 42, currentOffset);
          169  +          /* Header */
          170  +          if (fwrite(header, 1, 46, fpOutCD) == 46) {
          171  +            offsetCD += 46;
          172  +
          173  +            /* Filename */
          174  +            if (fnsize > 0) {
          175  +              if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
          176  +                offsetCD += fnsize;
          177  +              } else {
          178  +                err = Z_ERRNO;
          179  +                break;
          180  +              }
          181  +            } else {
          182  +              err = Z_STREAM_ERROR;
          183  +              break;
          184  +            }
          185  +
          186  +            /* Extra field */
          187  +            if (extsize > 0) {
          188  +              if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
          189  +                offsetCD += extsize;
          190  +              } else {
          191  +                err = Z_ERRNO;
          192  +                break;
          193  +              }
          194  +            }
          195  +
          196  +            /* Comment field */
          197  +            if (comsize > 0) {
          198  +              if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
          199  +                offsetCD += comsize;
          200  +              } else {
          201  +                err = Z_ERRNO;
          202  +                break;
          203  +              }
          204  +            }
          205  +
          206  +
          207  +          } else {
          208  +            err = Z_ERRNO;
          209  +            break;
          210  +          }
          211  +        }
          212  +
          213  +        /* Success */
          214  +        entries++;
          215  +
          216  +      } else {
          217  +        break;
          218  +      }
          219  +    }
          220  +
          221  +    /* Final central directory  */
          222  +    {
          223  +      int entriesZip = entries;
          224  +      char header[22];
          225  +      char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
          226  +      int comsize = (int) strlen(comment);
          227  +      if (entriesZip > 0xffff) {
          228  +        entriesZip = 0xffff;
          229  +      }
          230  +      WRITE_32(header, 0x06054b50);
          231  +      WRITE_16(header + 4, 0);    /* disk # */
          232  +      WRITE_16(header + 6, 0);    /* disk # */
          233  +      WRITE_16(header + 8, entriesZip);   /* hack */
          234  +      WRITE_16(header + 10, entriesZip);  /* hack */
          235  +      WRITE_32(header + 12, offsetCD);    /* size of CD */
          236  +      WRITE_32(header + 16, offset);      /* offset to CD */
          237  +      WRITE_16(header + 20, comsize);     /* comment */
          238  +
          239  +      /* Header */
          240  +      if (fwrite(header, 1, 22, fpOutCD) == 22) {
          241  +
          242  +        /* Comment field */
          243  +        if (comsize > 0) {
          244  +          if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
          245  +            err = Z_ERRNO;
          246  +          }
          247  +        }
          248  +
          249  +      } else {
          250  +        err = Z_ERRNO;
          251  +      }
          252  +    }
          253  +
          254  +    /* Final merge (file + central directory) */
          255  +    fclose(fpOutCD);
          256  +    if (err == Z_OK) {
          257  +      fpOutCD = fopen(fileOutTmp, "rb");
          258  +      if (fpOutCD != NULL) {
          259  +        int nRead;
          260  +        char buffer[8192];
          261  +        while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
          262  +          if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
          263  +            err = Z_ERRNO;
          264  +            break;
          265  +          }
          266  +        }
          267  +        fclose(fpOutCD);
          268  +      }
          269  +    }
          270  +
          271  +    /* Close */
          272  +    fclose(fpZip);
          273  +    fclose(fpOut);
          274  +
          275  +    /* Wipe temporary file */
          276  +    (void)remove(fileOutTmp);
          277  +
          278  +    /* Number of recovered entries */
          279  +    if (err == Z_OK) {
          280  +      if (nRecovered != NULL) {
          281  +        *nRecovered = entries;
          282  +      }
          283  +      if (bytesRecovered != NULL) {
          284  +        *bytesRecovered = totalBytes;
          285  +      }
          286  +    }
          287  +  } else {
          288  +    err = Z_STREAM_ERROR;
          289  +  }
          290  +  return err;
          291  +}

Added compat/zlib/contrib/minizip/mztools.h.

            1  +/*
            2  +  Additional tools for Minizip
            3  +  Code: Xavier Roche '2004
            4  +  License: Same as ZLIB (www.gzip.org)
            5  +*/
            6  +
            7  +#ifndef _zip_tools_H
            8  +#define _zip_tools_H
            9  +
           10  +#ifdef __cplusplus
           11  +extern "C" {
           12  +#endif
           13  +
           14  +#ifndef _ZLIB_H
           15  +#include "zlib.h"
           16  +#endif
           17  +
           18  +#include "unzip.h"
           19  +
           20  +/* Repair a ZIP file (missing central directory)
           21  +   file: file to recover
           22  +   fileOut: output file after recovery
           23  +   fileOutTmp: temporary file name used for recovery
           24  +*/
           25  +extern int ZEXPORT unzRepair(const char* file,
           26  +                             const char* fileOut,
           27  +                             const char* fileOutTmp,
           28  +                             uLong* nRecovered,
           29  +                             uLong* bytesRecovered);
           30  +
           31  +
           32  +#ifdef __cplusplus
           33  +}
           34  +#endif
           35  +
           36  +
           37  +#endif

Added compat/zlib/contrib/minizip/unzip.c.

            1  +/* unzip.c -- IO for uncompress .zip files using zlib
            2  +   Version 1.1, February 14h, 2010
            3  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications of Unzip for Zip64
            8  +         Copyright (C) 2007-2008 Even Rouault
            9  +
           10  +         Modifications for Zip64 support on both zip and unzip
           11  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
           12  +
           13  +         For more info read MiniZip_info.txt
           14  +
           15  +
           16  +  ------------------------------------------------------------------------------------
           17  +  Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
           18  +  compatibility with older software. The following is from the original crypt.c.
           19  +  Code woven in by Terry Thorsen 1/2003.
           20  +
           21  +  Copyright (c) 1990-2000 Info-ZIP.  All rights reserved.
           22  +
           23  +  See the accompanying file LICENSE, version 2000-Apr-09 or later
           24  +  (the contents of which are also included in zip.h) for terms of use.
           25  +  If, for some reason, all these files are missing, the Info-ZIP license
           26  +  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
           27  +
           28  +        crypt.c (full version) by Info-ZIP.      Last revised:  [see crypt.h]
           29  +
           30  +  The encryption/decryption parts of this source code (as opposed to the
           31  +  non-echoing password parts) were originally written in Europe.  The
           32  +  whole source package can be freely distributed, including from the USA.
           33  +  (Prior to January 2000, re-export from the US was a violation of US law.)
           34  +
           35  +        This encryption code is a direct transcription of the algorithm from
           36  +  Roger Schlafly, described by Phil Katz in the file appnote.txt.  This
           37  +  file (appnote.txt) is distributed with the PKZIP program (even in the
           38  +  version without encryption capabilities).
           39  +
           40  +        ------------------------------------------------------------------------------------
           41  +
           42  +        Changes in unzip.c
           43  +
           44  +        2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos
           45  +  2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz*
           46  +  2007-2008 - Even Rouault - Remove old C style function prototypes
           47  +  2007-2008 - Even Rouault - Add unzip support for ZIP64
           48  +
           49  +        Copyright (C) 2007-2008 Even Rouault
           50  +
           51  +
           52  +        Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again).
           53  +  Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G
           54  +                                should only read the compressed/uncompressed size from the Zip64 format if
           55  +                                the size from normal header was 0xFFFFFFFF
           56  +  Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant
           57  +        Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required)
           58  +                                Patch created by Daniel Borca
           59  +
           60  +  Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer
           61  +
           62  +  Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson
           63  +
           64  +*/
           65  +
           66  +
           67  +#include <stdio.h>
           68  +#include <stdlib.h>
           69  +#include <string.h>
           70  +
           71  +#ifndef NOUNCRYPT
           72  +        #define NOUNCRYPT
           73  +#endif
           74  +
           75  +#include "zlib.h"
           76  +#include "unzip.h"
           77  +
           78  +#ifdef STDC
           79  +#  include <stddef.h>
           80  +#  include <string.h>
           81  +#  include <stdlib.h>
           82  +#endif
           83  +#ifdef NO_ERRNO_H
           84  +    extern int errno;
           85  +#else
           86  +#   include <errno.h>
           87  +#endif
           88  +
           89  +
           90  +#ifndef local
           91  +#  define local static
           92  +#endif
           93  +/* compile with -Dlocal if your debugger can't find static symbols */
           94  +
           95  +
           96  +#ifndef CASESENSITIVITYDEFAULT_NO
           97  +#  if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
           98  +#    define CASESENSITIVITYDEFAULT_NO
           99  +#  endif
          100  +#endif
          101  +
          102  +
          103  +#ifndef UNZ_BUFSIZE
          104  +#define UNZ_BUFSIZE (16384)
          105  +#endif
          106  +
          107  +#ifndef UNZ_MAXFILENAMEINZIP
          108  +#define UNZ_MAXFILENAMEINZIP (256)
          109  +#endif
          110  +
          111  +#ifndef ALLOC
          112  +# define ALLOC(size) (malloc(size))
          113  +#endif
          114  +#ifndef TRYFREE
          115  +# define TRYFREE(p) {if (p) free(p);}
          116  +#endif
          117  +
          118  +#define SIZECENTRALDIRITEM (0x2e)
          119  +#define SIZEZIPLOCALHEADER (0x1e)
          120  +
          121  +
          122  +const char unz_copyright[] =
          123  +   " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
          124  +
          125  +/* unz_file_info_interntal contain internal info about a file in zipfile*/
          126  +typedef struct unz_file_info64_internal_s
          127  +{
          128  +    ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */
          129  +} unz_file_info64_internal;
          130  +
          131  +
          132  +/* file_in_zip_read_info_s contain internal information about a file in zipfile,
          133  +    when reading and decompress it */
          134  +typedef struct
          135  +{
          136  +    char  *read_buffer;         /* internal buffer for compressed data */
          137  +    z_stream stream;            /* zLib stream structure for inflate */
          138  +
          139  +#ifdef HAVE_BZIP2
          140  +    bz_stream bstream;          /* bzLib stream structure for bziped */
          141  +#endif
          142  +
          143  +    ZPOS64_T pos_in_zipfile;       /* position in byte on the zipfile, for fseek*/
          144  +    uLong stream_initialised;   /* flag set if stream structure is initialised*/
          145  +
          146  +    ZPOS64_T offset_local_extrafield;/* offset of the local extra field */
          147  +    uInt  size_local_extrafield;/* size of the local extra field */
          148  +    ZPOS64_T pos_local_extrafield;   /* position in the local extra field in read*/
          149  +    ZPOS64_T total_out_64;
          150  +
          151  +    uLong crc32;                /* crc32 of all data uncompressed */
          152  +    uLong crc32_wait;           /* crc32 we must obtain after decompress all */
          153  +    ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */
          154  +    ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/
          155  +    zlib_filefunc64_32_def z_filefunc;
          156  +    voidpf filestream;        /* io structore of the zipfile */
          157  +    uLong compression_method;   /* compression method (0==store) */
          158  +    ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
          159  +    int   raw;
          160  +} file_in_zip64_read_info_s;
          161  +
          162  +
          163  +/* unz64_s contain internal information about the zipfile
          164  +*/
          165  +typedef struct
          166  +{
          167  +    zlib_filefunc64_32_def z_filefunc;
          168  +    int is64bitOpenFunction;
          169  +    voidpf filestream;        /* io structore of the zipfile */
          170  +    unz_global_info64 gi;       /* public global information */
          171  +    ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
          172  +    ZPOS64_T num_file;             /* number of the current file in the zipfile*/
          173  +    ZPOS64_T pos_in_central_dir;   /* pos of the current file in the central dir*/
          174  +    ZPOS64_T current_file_ok;      /* flag about the usability of the current file*/
          175  +    ZPOS64_T central_pos;          /* position of the beginning of the central dir*/
          176  +
          177  +    ZPOS64_T size_central_dir;     /* size of the central directory  */
          178  +    ZPOS64_T offset_central_dir;   /* offset of start of central directory with
          179  +                                   respect to the starting disk number */
          180  +
          181  +    unz_file_info64 cur_file_info; /* public info about the current file in zip*/
          182  +    unz_file_info64_internal cur_file_info_internal; /* private info about it*/
          183  +    file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current
          184  +                                        file if we are decompressing it */
          185  +    int encrypted;
          186  +
          187  +    int isZip64;
          188  +
          189  +#    ifndef NOUNCRYPT
          190  +    unsigned long keys[3];     /* keys defining the pseudo-random sequence */
          191  +    const unsigned long* pcrc_32_tab;
          192  +#    endif
          193  +} unz64_s;
          194  +
          195  +
          196  +#ifndef NOUNCRYPT
          197  +#include "crypt.h"
          198  +#endif
          199  +
          200  +/* ===========================================================================
          201  +     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
          202  +   for end of file.
          203  +   IN assertion: the stream s has been sucessfully opened for reading.
          204  +*/
          205  +
          206  +
          207  +local int unz64local_getByte OF((
          208  +    const zlib_filefunc64_32_def* pzlib_filefunc_def,
          209  +    voidpf filestream,
          210  +    int *pi));
          211  +
          212  +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)
          213  +{
          214  +    unsigned char c;
          215  +    int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1);
          216  +    if (err==1)
          217  +    {
          218  +        *pi = (int)c;
          219  +        return UNZ_OK;
          220  +    }
          221  +    else
          222  +    {
          223  +        if (ZERROR64(*pzlib_filefunc_def,filestream))
          224  +            return UNZ_ERRNO;
          225  +        else
          226  +            return UNZ_EOF;
          227  +    }
          228  +}
          229  +
          230  +
          231  +/* ===========================================================================
          232  +   Reads a long in LSB order from the given gz_stream. Sets
          233  +*/
          234  +local int unz64local_getShort OF((
          235  +    const zlib_filefunc64_32_def* pzlib_filefunc_def,
          236  +    voidpf filestream,
          237  +    uLong *pX));
          238  +
          239  +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def,
          240  +                             voidpf filestream,
          241  +                             uLong *pX)
          242  +{
          243  +    uLong x ;
          244  +    int i = 0;
          245  +    int err;
          246  +
          247  +    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          248  +    x = (uLong)i;
          249  +
          250  +    if (err==UNZ_OK)
          251  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          252  +    x |= ((uLong)i)<<8;
          253  +
          254  +    if (err==UNZ_OK)
          255  +        *pX = x;
          256  +    else
          257  +        *pX = 0;
          258  +    return err;
          259  +}
          260  +
          261  +local int unz64local_getLong OF((
          262  +    const zlib_filefunc64_32_def* pzlib_filefunc_def,
          263  +    voidpf filestream,
          264  +    uLong *pX));
          265  +
          266  +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def,
          267  +                            voidpf filestream,
          268  +                            uLong *pX)
          269  +{
          270  +    uLong x ;
          271  +    int i = 0;
          272  +    int err;
          273  +
          274  +    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          275  +    x = (uLong)i;
          276  +
          277  +    if (err==UNZ_OK)
          278  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          279  +    x |= ((uLong)i)<<8;
          280  +
          281  +    if (err==UNZ_OK)
          282  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          283  +    x |= ((uLong)i)<<16;
          284  +
          285  +    if (err==UNZ_OK)
          286  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          287  +    x += ((uLong)i)<<24;
          288  +
          289  +    if (err==UNZ_OK)
          290  +        *pX = x;
          291  +    else
          292  +        *pX = 0;
          293  +    return err;
          294  +}
          295  +
          296  +local int unz64local_getLong64 OF((
          297  +    const zlib_filefunc64_32_def* pzlib_filefunc_def,
          298  +    voidpf filestream,
          299  +    ZPOS64_T *pX));
          300  +
          301  +
          302  +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def,
          303  +                            voidpf filestream,
          304  +                            ZPOS64_T *pX)
          305  +{
          306  +    ZPOS64_T x ;
          307  +    int i = 0;
          308  +    int err;
          309  +
          310  +    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          311  +    x = (ZPOS64_T)i;
          312  +
          313  +    if (err==UNZ_OK)
          314  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          315  +    x |= ((ZPOS64_T)i)<<8;
          316  +
          317  +    if (err==UNZ_OK)
          318  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          319  +    x |= ((ZPOS64_T)i)<<16;
          320  +
          321  +    if (err==UNZ_OK)
          322  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          323  +    x |= ((ZPOS64_T)i)<<24;
          324  +
          325  +    if (err==UNZ_OK)
          326  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          327  +    x |= ((ZPOS64_T)i)<<32;
          328  +
          329  +    if (err==UNZ_OK)
          330  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          331  +    x |= ((ZPOS64_T)i)<<40;
          332  +
          333  +    if (err==UNZ_OK)
          334  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          335  +    x |= ((ZPOS64_T)i)<<48;
          336  +
          337  +    if (err==UNZ_OK)
          338  +        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
          339  +    x |= ((ZPOS64_T)i)<<56;
          340  +
          341  +    if (err==UNZ_OK)
          342  +        *pX = x;
          343  +    else
          344  +        *pX = 0;
          345  +    return err;
          346  +}
          347  +
          348  +/* My own strcmpi / strcasecmp */
          349  +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2)
          350  +{
          351  +    for (;;)
          352  +    {
          353  +        char c1=*(fileName1++);
          354  +        char c2=*(fileName2++);
          355  +        if ((c1>='a') && (c1<='z'))
          356  +            c1 -= 0x20;
          357  +        if ((c2>='a') && (c2<='z'))
          358  +            c2 -= 0x20;
          359  +        if (c1=='\0')
          360  +            return ((c2=='\0') ? 0 : -1);
          361  +        if (c2=='\0')
          362  +            return 1;
          363  +        if (c1<c2)
          364  +            return -1;
          365  +        if (c1>c2)
          366  +            return 1;
          367  +    }
          368  +}
          369  +
          370  +
          371  +#ifdef  CASESENSITIVITYDEFAULT_NO
          372  +#define CASESENSITIVITYDEFAULTVALUE 2
          373  +#else
          374  +#define CASESENSITIVITYDEFAULTVALUE 1
          375  +#endif
          376  +
          377  +#ifndef STRCMPCASENOSENTIVEFUNCTION
          378  +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
          379  +#endif
          380  +
          381  +/*
          382  +   Compare two filename (fileName1,fileName2).
          383  +   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
          384  +   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
          385  +                                                                or strcasecmp)
          386  +   If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
          387  +        (like 1 on Unix, 2 on Windows)
          388  +
          389  +*/
          390  +extern int ZEXPORT unzStringFileNameCompare (const char*  fileName1,
          391  +                                                 const char*  fileName2,
          392  +                                                 int iCaseSensitivity)
          393  +
          394  +{
          395  +    if (iCaseSensitivity==0)
          396  +        iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
          397  +
          398  +    if (iCaseSensitivity==1)
          399  +        return strcmp(fileName1,fileName2);
          400  +
          401  +    return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
          402  +}
          403  +
          404  +#ifndef BUFREADCOMMENT
          405  +#define BUFREADCOMMENT (0x400)
          406  +#endif
          407  +
          408  +/*
          409  +  Locate the Central directory of a zipfile (at the end, just before
          410  +    the global comment)
          411  +*/
          412  +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
          413  +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
          414  +{
          415  +    unsigned char* buf;
          416  +    ZPOS64_T uSizeFile;
          417  +    ZPOS64_T uBackRead;
          418  +    ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
          419  +    ZPOS64_T uPosFound=0;
          420  +
          421  +    if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
          422  +        return 0;
          423  +
          424  +
          425  +    uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
          426  +
          427  +    if (uMaxBack>uSizeFile)
          428  +        uMaxBack = uSizeFile;
          429  +
          430  +    buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
          431  +    if (buf==NULL)
          432  +        return 0;
          433  +
          434  +    uBackRead = 4;
          435  +    while (uBackRead<uMaxBack)
          436  +    {
          437  +        uLong uReadSize;
          438  +        ZPOS64_T uReadPos ;
          439  +        int i;
          440  +        if (uBackRead+BUFREADCOMMENT>uMaxBack)
          441  +            uBackRead = uMaxBack;
          442  +        else
          443  +            uBackRead+=BUFREADCOMMENT;
          444  +        uReadPos = uSizeFile-uBackRead ;
          445  +
          446  +        uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
          447  +                     (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
          448  +        if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          449  +            break;
          450  +
          451  +        if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
          452  +            break;
          453  +
          454  +        for (i=(int)uReadSize-3; (i--)>0;)
          455  +            if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
          456  +                ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
          457  +            {
          458  +                uPosFound = uReadPos+i;
          459  +                break;
          460  +            }
          461  +
          462  +        if (uPosFound!=0)
          463  +            break;
          464  +    }
          465  +    TRYFREE(buf);
          466  +    return uPosFound;
          467  +}
          468  +
          469  +
          470  +/*
          471  +  Locate the Central directory 64 of a zipfile (at the end, just before
          472  +    the global comment)
          473  +*/
          474  +local ZPOS64_T unz64local_SearchCentralDir64 OF((
          475  +    const zlib_filefunc64_32_def* pzlib_filefunc_def,
          476  +    voidpf filestream));
          477  +
          478  +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def,
          479  +                                      voidpf filestream)
          480  +{
          481  +    unsigned char* buf;
          482  +    ZPOS64_T uSizeFile;
          483  +    ZPOS64_T uBackRead;
          484  +    ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
          485  +    ZPOS64_T uPosFound=0;
          486  +    uLong uL;
          487  +                ZPOS64_T relativeOffset;
          488  +
          489  +    if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
          490  +        return 0;
          491  +
          492  +
          493  +    uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
          494  +
          495  +    if (uMaxBack>uSizeFile)
          496  +        uMaxBack = uSizeFile;
          497  +
          498  +    buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
          499  +    if (buf==NULL)
          500  +        return 0;
          501  +
          502  +    uBackRead = 4;
          503  +    while (uBackRead<uMaxBack)
          504  +    {
          505  +        uLong uReadSize;
          506  +        ZPOS64_T uReadPos;
          507  +        int i;
          508  +        if (uBackRead+BUFREADCOMMENT>uMaxBack)
          509  +            uBackRead = uMaxBack;
          510  +        else
          511  +            uBackRead+=BUFREADCOMMENT;
          512  +        uReadPos = uSizeFile-uBackRead ;
          513  +
          514  +        uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
          515  +                     (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
          516  +        if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          517  +            break;
          518  +
          519  +        if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
          520  +            break;
          521  +
          522  +        for (i=(int)uReadSize-3; (i--)>0;)
          523  +            if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
          524  +                ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07))
          525  +            {
          526  +                uPosFound = uReadPos+i;
          527  +                break;
          528  +            }
          529  +
          530  +        if (uPosFound!=0)
          531  +            break;
          532  +    }
          533  +    TRYFREE(buf);
          534  +    if (uPosFound == 0)
          535  +        return 0;
          536  +
          537  +    /* Zip64 end of central directory locator */
          538  +    if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
          539  +        return 0;
          540  +
          541  +    /* the signature, already checked */
          542  +    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
          543  +        return 0;
          544  +
          545  +    /* number of the disk with the start of the zip64 end of  central directory */
          546  +    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
          547  +        return 0;
          548  +    if (uL != 0)
          549  +        return 0;
          550  +
          551  +    /* relative offset of the zip64 end of central directory record */
          552  +    if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK)
          553  +        return 0;
          554  +
          555  +    /* total number of disks */
          556  +    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
          557  +        return 0;
          558  +    if (uL != 1)
          559  +        return 0;
          560  +
          561  +    /* Goto end of central directory record */
          562  +    if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
          563  +        return 0;
          564  +
          565  +     /* the signature */
          566  +    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
          567  +        return 0;
          568  +
          569  +    if (uL != 0x06064b50)
          570  +        return 0;
          571  +
          572  +    return relativeOffset;
          573  +}
          574  +
          575  +/*
          576  +  Open a Zip file. path contain the full pathname (by example,
          577  +     on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
          578  +     "zlib/zlib114.zip".
          579  +     If the zipfile cannot be opened (file doesn't exist or in not valid), the
          580  +       return value is NULL.
          581  +     Else, the return value is a unzFile Handle, usable with other function
          582  +       of this unzip package.
          583  +*/
          584  +local unzFile unzOpenInternal (const void *path,
          585  +                               zlib_filefunc64_32_def* pzlib_filefunc64_32_def,
          586  +                               int is64bitOpenFunction)
          587  +{
          588  +    unz64_s us;
          589  +    unz64_s *s;
          590  +    ZPOS64_T central_pos;
          591  +    uLong   uL;
          592  +
          593  +    uLong number_disk;          /* number of the current dist, used for
          594  +                                   spaning ZIP, unsupported, always 0*/
          595  +    uLong number_disk_with_CD;  /* number the the disk with central dir, used
          596  +                                   for spaning ZIP, unsupported, always 0*/
          597  +    ZPOS64_T number_entry_CD;      /* total number of entries in
          598  +                                   the central dir
          599  +                                   (same than number_entry on nospan) */
          600  +
          601  +    int err=UNZ_OK;
          602  +
          603  +    if (unz_copyright[0]!=' ')
          604  +        return NULL;
          605  +
          606  +    us.z_filefunc.zseek32_file = NULL;
          607  +    us.z_filefunc.ztell32_file = NULL;
          608  +    if (pzlib_filefunc64_32_def==NULL)
          609  +        fill_fopen64_filefunc(&us.z_filefunc.zfile_func64);
          610  +    else
          611  +        us.z_filefunc = *pzlib_filefunc64_32_def;
          612  +    us.is64bitOpenFunction = is64bitOpenFunction;
          613  +
          614  +
          615  +
          616  +    us.filestream = ZOPEN64(us.z_filefunc,
          617  +                                                 path,
          618  +                                                 ZLIB_FILEFUNC_MODE_READ |
          619  +                                                 ZLIB_FILEFUNC_MODE_EXISTING);
          620  +    if (us.filestream==NULL)
          621  +        return NULL;
          622  +
          623  +    central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream);
          624  +    if (central_pos)
          625  +    {
          626  +        uLong uS;
          627  +        ZPOS64_T uL64;
          628  +
          629  +        us.isZip64 = 1;
          630  +
          631  +        if (ZSEEK64(us.z_filefunc, us.filestream,
          632  +                                      central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          633  +        err=UNZ_ERRNO;
          634  +
          635  +        /* the signature, already checked */
          636  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          637  +            err=UNZ_ERRNO;
          638  +
          639  +        /* size of zip64 end of central directory record */
          640  +        if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK)
          641  +            err=UNZ_ERRNO;
          642  +
          643  +        /* version made by */
          644  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK)
          645  +            err=UNZ_ERRNO;
          646  +
          647  +        /* version needed to extract */
          648  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK)
          649  +            err=UNZ_ERRNO;
          650  +
          651  +        /* number of this disk */
          652  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
          653  +            err=UNZ_ERRNO;
          654  +
          655  +        /* number of the disk with the start of the central directory */
          656  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
          657  +            err=UNZ_ERRNO;
          658  +
          659  +        /* total number of entries in the central directory on this disk */
          660  +        if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
          661  +            err=UNZ_ERRNO;
          662  +
          663  +        /* total number of entries in the central directory */
          664  +        if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
          665  +            err=UNZ_ERRNO;
          666  +
          667  +        if ((number_entry_CD!=us.gi.number_entry) ||
          668  +            (number_disk_with_CD!=0) ||
          669  +            (number_disk!=0))
          670  +            err=UNZ_BADZIPFILE;
          671  +
          672  +        /* size of the central directory */
          673  +        if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
          674  +            err=UNZ_ERRNO;
          675  +
          676  +        /* offset of start of central directory with respect to the
          677  +          starting disk number */
          678  +        if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
          679  +            err=UNZ_ERRNO;
          680  +
          681  +        us.gi.size_comment = 0;
          682  +    }
          683  +    else
          684  +    {
          685  +        central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream);
          686  +        if (central_pos==0)
          687  +            err=UNZ_ERRNO;
          688  +
          689  +        us.isZip64 = 0;
          690  +
          691  +        if (ZSEEK64(us.z_filefunc, us.filestream,
          692  +                                        central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          693  +            err=UNZ_ERRNO;
          694  +
          695  +        /* the signature, already checked */
          696  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          697  +            err=UNZ_ERRNO;
          698  +
          699  +        /* number of this disk */
          700  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
          701  +            err=UNZ_ERRNO;
          702  +
          703  +        /* number of the disk with the start of the central directory */
          704  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
          705  +            err=UNZ_ERRNO;
          706  +
          707  +        /* total number of entries in the central dir on this disk */
          708  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          709  +            err=UNZ_ERRNO;
          710  +        us.gi.number_entry = uL;
          711  +
          712  +        /* total number of entries in the central dir */
          713  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          714  +            err=UNZ_ERRNO;
          715  +        number_entry_CD = uL;
          716  +
          717  +        if ((number_entry_CD!=us.gi.number_entry) ||
          718  +            (number_disk_with_CD!=0) ||
          719  +            (number_disk!=0))
          720  +            err=UNZ_BADZIPFILE;
          721  +
          722  +        /* size of the central directory */
          723  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          724  +            err=UNZ_ERRNO;
          725  +        us.size_central_dir = uL;
          726  +
          727  +        /* offset of start of central directory with respect to the
          728  +            starting disk number */
          729  +        if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
          730  +            err=UNZ_ERRNO;
          731  +        us.offset_central_dir = uL;
          732  +
          733  +        /* zipfile comment length */
          734  +        if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
          735  +            err=UNZ_ERRNO;
          736  +    }
          737  +
          738  +    if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
          739  +        (err==UNZ_OK))
          740  +        err=UNZ_BADZIPFILE;
          741  +
          742  +    if (err!=UNZ_OK)
          743  +    {
          744  +        ZCLOSE64(us.z_filefunc, us.filestream);
          745  +        return NULL;
          746  +    }
          747  +
          748  +    us.byte_before_the_zipfile = central_pos -
          749  +                            (us.offset_central_dir+us.size_central_dir);
          750  +    us.central_pos = central_pos;
          751  +    us.pfile_in_zip_read = NULL;
          752  +    us.encrypted = 0;
          753  +
          754  +
          755  +    s=(unz64_s*)ALLOC(sizeof(unz64_s));
          756  +    if( s != NULL)
          757  +    {
          758  +        *s=us;
          759  +        unzGoToFirstFile((unzFile)s);
          760  +    }
          761  +    return (unzFile)s;
          762  +}
          763  +
          764  +
          765  +extern unzFile ZEXPORT unzOpen2 (const char *path,
          766  +                                        zlib_filefunc_def* pzlib_filefunc32_def)
          767  +{
          768  +    if (pzlib_filefunc32_def != NULL)
          769  +    {
          770  +        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
          771  +        fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def);
          772  +        return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 0);
          773  +    }
          774  +    else
          775  +        return unzOpenInternal(path, NULL, 0);
          776  +}
          777  +
          778  +extern unzFile ZEXPORT unzOpen2_64 (const void *path,
          779  +                                     zlib_filefunc64_def* pzlib_filefunc_def)
          780  +{
          781  +    if (pzlib_filefunc_def != NULL)
          782  +    {
          783  +        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
          784  +        zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
          785  +        zlib_filefunc64_32_def_fill.ztell32_file = NULL;
          786  +        zlib_filefunc64_32_def_fill.zseek32_file = NULL;
          787  +        return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 1);
          788  +    }
          789  +    else
          790  +        return unzOpenInternal(path, NULL, 1);
          791  +}
          792  +
          793  +extern unzFile ZEXPORT unzOpen (const char *path)
          794  +{
          795  +    return unzOpenInternal(path, NULL, 0);
          796  +}
          797  +
          798  +extern unzFile ZEXPORT unzOpen64 (const void *path)
          799  +{
          800  +    return unzOpenInternal(path, NULL, 1);
          801  +}
          802  +
          803  +/*
          804  +  Close a ZipFile opened with unzipOpen.
          805  +  If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
          806  +    these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
          807  +  return UNZ_OK if there is no problem. */
          808  +extern int ZEXPORT unzClose (unzFile file)
          809  +{
          810  +    unz64_s* s;
          811  +    if (file==NULL)
          812  +        return UNZ_PARAMERROR;
          813  +    s=(unz64_s*)file;
          814  +
          815  +    if (s->pfile_in_zip_read!=NULL)
          816  +        unzCloseCurrentFile(file);
          817  +
          818  +    ZCLOSE64(s->z_filefunc, s->filestream);
          819  +    TRYFREE(s);
          820  +    return UNZ_OK;
          821  +}
          822  +
          823  +
          824  +/*
          825  +  Write info about the ZipFile in the *pglobal_info structure.
          826  +  No preparation of the structure is needed
          827  +  return UNZ_OK if there is no problem. */
          828  +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info)
          829  +{
          830  +    unz64_s* s;
          831  +    if (file==NULL)
          832  +        return UNZ_PARAMERROR;
          833  +    s=(unz64_s*)file;
          834  +    *pglobal_info=s->gi;
          835  +    return UNZ_OK;
          836  +}
          837  +
          838  +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32)
          839  +{
          840  +    unz64_s* s;
          841  +    if (file==NULL)
          842  +        return UNZ_PARAMERROR;
          843  +    s=(unz64_s*)file;
          844  +    /* to do : check if number_entry is not truncated */
          845  +    pglobal_info32->number_entry = (uLong)s->gi.number_entry;
          846  +    pglobal_info32->size_comment = s->gi.size_comment;
          847  +    return UNZ_OK;
          848  +}
          849  +/*
          850  +   Translate date/time from Dos format to tm_unz (readable more easilty)
          851  +*/
          852  +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm)
          853  +{
          854  +    ZPOS64_T uDate;
          855  +    uDate = (ZPOS64_T)(ulDosDate>>16);
          856  +    ptm->tm_mday = (uInt)(uDate&0x1f) ;
          857  +    ptm->tm_mon =  (uInt)((((uDate)&0x1E0)/0x20)-1) ;
          858  +    ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
          859  +
          860  +    ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
          861  +    ptm->tm_min =  (uInt) ((ulDosDate&0x7E0)/0x20) ;
          862  +    ptm->tm_sec =  (uInt) (2*(ulDosDate&0x1f)) ;
          863  +}
          864  +
          865  +/*
          866  +  Get Info about the current file in the zipfile, with internal only info
          867  +*/
          868  +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file,
          869  +                                                  unz_file_info64 *pfile_info,
          870  +                                                  unz_file_info64_internal
          871  +                                                  *pfile_info_internal,
          872  +                                                  char *szFileName,
          873  +                                                  uLong fileNameBufferSize,
          874  +                                                  void *extraField,
          875  +                                                  uLong extraFieldBufferSize,
          876  +                                                  char *szComment,
          877  +                                                  uLong commentBufferSize));
          878  +
          879  +local int unz64local_GetCurrentFileInfoInternal (unzFile file,
          880  +                                                  unz_file_info64 *pfile_info,
          881  +                                                  unz_file_info64_internal
          882  +                                                  *pfile_info_internal,
          883  +                                                  char *szFileName,
          884  +                                                  uLong fileNameBufferSize,
          885  +                                                  void *extraField,
          886  +                                                  uLong extraFieldBufferSize,
          887  +                                                  char *szComment,
          888  +                                                  uLong commentBufferSize)
          889  +{
          890  +    unz64_s* s;
          891  +    unz_file_info64 file_info;
          892  +    unz_file_info64_internal file_info_internal;
          893  +    int err=UNZ_OK;
          894  +    uLong uMagic;
          895  +    long lSeek=0;
          896  +    uLong uL;
          897  +
          898  +    if (file==NULL)
          899  +        return UNZ_PARAMERROR;
          900  +    s=(unz64_s*)file;
          901  +    if (ZSEEK64(s->z_filefunc, s->filestream,
          902  +              s->pos_in_central_dir+s->byte_before_the_zipfile,
          903  +              ZLIB_FILEFUNC_SEEK_SET)!=0)
          904  +        err=UNZ_ERRNO;
          905  +
          906  +
          907  +    /* we check the magic */
          908  +    if (err==UNZ_OK)
          909  +    {
          910  +        if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
          911  +            err=UNZ_ERRNO;
          912  +        else if (uMagic!=0x02014b50)
          913  +            err=UNZ_BADZIPFILE;
          914  +    }
          915  +
          916  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
          917  +        err=UNZ_ERRNO;
          918  +
          919  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
          920  +        err=UNZ_ERRNO;
          921  +
          922  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
          923  +        err=UNZ_ERRNO;
          924  +
          925  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
          926  +        err=UNZ_ERRNO;
          927  +
          928  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
          929  +        err=UNZ_ERRNO;
          930  +
          931  +    unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
          932  +
          933  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
          934  +        err=UNZ_ERRNO;
          935  +
          936  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
          937  +        err=UNZ_ERRNO;
          938  +    file_info.compressed_size = uL;
          939  +
          940  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
          941  +        err=UNZ_ERRNO;
          942  +    file_info.uncompressed_size = uL;
          943  +
          944  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
          945  +        err=UNZ_ERRNO;
          946  +
          947  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
          948  +        err=UNZ_ERRNO;
          949  +
          950  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
          951  +        err=UNZ_ERRNO;
          952  +
          953  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
          954  +        err=UNZ_ERRNO;
          955  +
          956  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
          957  +        err=UNZ_ERRNO;
          958  +
          959  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
          960  +        err=UNZ_ERRNO;
          961  +
          962  +                // relative offset of local header
          963  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
          964  +        err=UNZ_ERRNO;
          965  +    file_info_internal.offset_curfile = uL;
          966  +
          967  +    lSeek+=file_info.size_filename;
          968  +    if ((err==UNZ_OK) && (szFileName!=NULL))
          969  +    {
          970  +        uLong uSizeRead ;
          971  +        if (file_info.size_filename<fileNameBufferSize)
          972  +        {
          973  +            *(szFileName+file_info.size_filename)='\0';
          974  +            uSizeRead = file_info.size_filename;
          975  +        }
          976  +        else
          977  +            uSizeRead = fileNameBufferSize;
          978  +
          979  +        if ((file_info.size_filename>0) && (fileNameBufferSize>0))
          980  +            if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
          981  +                err=UNZ_ERRNO;
          982  +        lSeek -= uSizeRead;
          983  +    }
          984  +
          985  +    // Read extrafield
          986  +    if ((err==UNZ_OK) && (extraField!=NULL))
          987  +    {
          988  +        ZPOS64_T uSizeRead ;
          989  +        if (file_info.size_file_extra<extraFieldBufferSize)
          990  +            uSizeRead = file_info.size_file_extra;
          991  +        else
          992  +            uSizeRead = extraFieldBufferSize;
          993  +
          994  +        if (lSeek!=0)
          995  +        {
          996  +            if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
          997  +                lSeek=0;
          998  +            else
          999  +                err=UNZ_ERRNO;
         1000  +        }
         1001  +
         1002  +        if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
         1003  +            if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead)
         1004  +                err=UNZ_ERRNO;
         1005  +
         1006  +        lSeek += file_info.size_file_extra - (uLong)uSizeRead;
         1007  +    }
         1008  +    else
         1009  +        lSeek += file_info.size_file_extra;
         1010  +
         1011  +
         1012  +    if ((err==UNZ_OK) && (file_info.size_file_extra != 0))
         1013  +    {
         1014  +                                uLong acc = 0;
         1015  +
         1016  +        // since lSeek now points to after the extra field we need to move back
         1017  +        lSeek -= file_info.size_file_extra;
         1018  +
         1019  +        if (lSeek!=0)
         1020  +        {
         1021  +            if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
         1022  +                lSeek=0;
         1023  +            else
         1024  +                err=UNZ_ERRNO;
         1025  +        }
         1026  +
         1027  +        while(acc < file_info.size_file_extra)
         1028  +        {
         1029  +            uLong headerId;
         1030  +                                                uLong dataSize;
         1031  +
         1032  +            if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK)
         1033  +                err=UNZ_ERRNO;
         1034  +
         1035  +            if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK)
         1036  +                err=UNZ_ERRNO;
         1037  +
         1038  +            /* ZIP64 extra fields */
         1039  +            if (headerId == 0x0001)
         1040  +            {
         1041  +                                                        uLong uL;
         1042  +
         1043  +                                                                if(file_info.uncompressed_size == MAXU32)
         1044  +                                                                {
         1045  +                                                                        if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
         1046  +                                                                                        err=UNZ_ERRNO;
         1047  +                                                                }
         1048  +
         1049  +                                                                if(file_info.compressed_size == MAXU32)
         1050  +                                                                {
         1051  +                                                                        if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
         1052  +                                                                                  err=UNZ_ERRNO;
         1053  +                                                                }
         1054  +
         1055  +                                                                if(file_info_internal.offset_curfile == MAXU32)
         1056  +                                                                {
         1057  +                                                                        /* Relative Header offset */
         1058  +                                                                        if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
         1059  +                                                                                err=UNZ_ERRNO;
         1060  +                                                                }
         1061  +
         1062  +                                                                if(file_info.disk_num_start == MAXU32)
         1063  +                                                                {
         1064  +                                                                        /* Disk Start Number */
         1065  +                                                                        if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
         1066  +                                                                                err=UNZ_ERRNO;
         1067  +                                                                }
         1068  +
         1069  +            }
         1070  +            else
         1071  +            {
         1072  +                if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0)
         1073  +                    err=UNZ_ERRNO;
         1074  +            }
         1075  +
         1076  +            acc += 2 + 2 + dataSize;
         1077  +        }
         1078  +    }
         1079  +
         1080  +    if ((err==UNZ_OK) && (szComment!=NULL))
         1081  +    {
         1082  +        uLong uSizeRead ;
         1083  +        if (file_info.size_file_comment<commentBufferSize)
         1084  +        {
         1085  +            *(szComment+file_info.size_file_comment)='\0';
         1086  +            uSizeRead = file_info.size_file_comment;
         1087  +        }
         1088  +        else
         1089  +            uSizeRead = commentBufferSize;
         1090  +
         1091  +        if (lSeek!=0)
         1092  +        {
         1093  +            if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
         1094  +                lSeek=0;
         1095  +            else
         1096  +                err=UNZ_ERRNO;
         1097  +        }
         1098  +
         1099  +        if ((file_info.size_file_comment>0) && (commentBufferSize>0))
         1100  +            if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
         1101  +                err=UNZ_ERRNO;
         1102  +        lSeek+=file_info.size_file_comment - uSizeRead;
         1103  +    }
         1104  +    else
         1105  +        lSeek+=file_info.size_file_comment;
         1106  +
         1107  +
         1108  +    if ((err==UNZ_OK) && (pfile_info!=NULL))
         1109  +        *pfile_info=file_info;
         1110  +
         1111  +    if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
         1112  +        *pfile_info_internal=file_info_internal;
         1113  +
         1114  +    return err;
         1115  +}
         1116  +
         1117  +
         1118  +
         1119  +/*
         1120  +  Write info about the ZipFile in the *pglobal_info structure.
         1121  +  No preparation of the structure is needed
         1122  +  return UNZ_OK if there is no problem.
         1123  +*/
         1124  +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file,
         1125  +                                          unz_file_info64 * pfile_info,
         1126  +                                          char * szFileName, uLong fileNameBufferSize,
         1127  +                                          void *extraField, uLong extraFieldBufferSize,
         1128  +                                          char* szComment,  uLong commentBufferSize)
         1129  +{
         1130  +    return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL,
         1131  +                                                szFileName,fileNameBufferSize,
         1132  +                                                extraField,extraFieldBufferSize,
         1133  +                                                szComment,commentBufferSize);
         1134  +}
         1135  +
         1136  +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file,
         1137  +                                          unz_file_info * pfile_info,
         1138  +                                          char * szFileName, uLong fileNameBufferSize,
         1139  +                                          void *extraField, uLong extraFieldBufferSize,
         1140  +                                          char* szComment,  uLong commentBufferSize)
         1141  +{
         1142  +    int err;
         1143  +    unz_file_info64 file_info64;
         1144  +    err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL,
         1145  +                                                szFileName,fileNameBufferSize,
         1146  +                                                extraField,extraFieldBufferSize,
         1147  +                                                szComment,commentBufferSize);
         1148  +    if ((err==UNZ_OK) && (pfile_info != NULL))
         1149  +    {
         1150  +        pfile_info->version = file_info64.version;
         1151  +        pfile_info->version_needed = file_info64.version_needed;
         1152  +        pfile_info->flag = file_info64.flag;
         1153  +        pfile_info->compression_method = file_info64.compression_method;
         1154  +        pfile_info->dosDate = file_info64.dosDate;
         1155  +        pfile_info->crc = file_info64.crc;
         1156  +
         1157  +        pfile_info->size_filename = file_info64.size_filename;
         1158  +        pfile_info->size_file_extra = file_info64.size_file_extra;
         1159  +        pfile_info->size_file_comment = file_info64.size_file_comment;
         1160  +
         1161  +        pfile_info->disk_num_start = file_info64.disk_num_start;
         1162  +        pfile_info->internal_fa = file_info64.internal_fa;
         1163  +        pfile_info->external_fa = file_info64.external_fa;
         1164  +
         1165  +        pfile_info->tmu_date = file_info64.tmu_date,
         1166  +
         1167  +
         1168  +        pfile_info->compressed_size = (uLong)file_info64.compressed_size;
         1169  +        pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size;
         1170  +
         1171  +    }
         1172  +    return err;
         1173  +}
         1174  +/*
         1175  +  Set the current file of the zipfile to the first file.
         1176  +  return UNZ_OK if there is no problem
         1177  +*/
         1178  +extern int ZEXPORT unzGoToFirstFile (unzFile file)
         1179  +{
         1180  +    int err=UNZ_OK;
         1181  +    unz64_s* s;
         1182  +    if (file==NULL)
         1183  +        return UNZ_PARAMERROR;
         1184  +    s=(unz64_s*)file;
         1185  +    s->pos_in_central_dir=s->offset_central_dir;
         1186  +    s->num_file=0;
         1187  +    err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info,
         1188  +                                             &s->cur_file_info_internal,
         1189  +                                             NULL,0,NULL,0,NULL,0);
         1190  +    s->current_file_ok = (err == UNZ_OK);
         1191  +    return err;
         1192  +}
         1193  +
         1194  +/*
         1195  +  Set the current file of the zipfile to the next file.
         1196  +  return UNZ_OK if there is no problem
         1197  +  return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
         1198  +*/
         1199  +extern int ZEXPORT unzGoToNextFile (unzFile  file)
         1200  +{
         1201  +    unz64_s* s;
         1202  +    int err;
         1203  +
         1204  +    if (file==NULL)
         1205  +        return UNZ_PARAMERROR;
         1206  +    s=(unz64_s*)file;
         1207  +    if (!s->current_file_ok)
         1208  +        return UNZ_END_OF_LIST_OF_FILE;
         1209  +    if (s->gi.number_entry != 0xffff)    /* 2^16 files overflow hack */
         1210  +      if (s->num_file+1==s->gi.number_entry)
         1211  +        return UNZ_END_OF_LIST_OF_FILE;
         1212  +
         1213  +    s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
         1214  +            s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
         1215  +    s->num_file++;
         1216  +    err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info,
         1217  +                                               &s->cur_file_info_internal,
         1218  +                                               NULL,0,NULL,0,NULL,0);
         1219  +    s->current_file_ok = (err == UNZ_OK);
         1220  +    return err;
         1221  +}
         1222  +
         1223  +
         1224  +/*
         1225  +  Try locate the file szFileName in the zipfile.
         1226  +  For the iCaseSensitivity signification, see unzipStringFileNameCompare
         1227  +
         1228  +  return value :
         1229  +  UNZ_OK if the file is found. It becomes the current file.
         1230  +  UNZ_END_OF_LIST_OF_FILE if the file is not found
         1231  +*/
         1232  +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity)
         1233  +{
         1234  +    unz64_s* s;
         1235  +    int err;
         1236  +
         1237  +    /* We remember the 'current' position in the file so that we can jump
         1238  +     * back there if we fail.
         1239  +     */
         1240  +    unz_file_info64 cur_file_infoSaved;
         1241  +    unz_file_info64_internal cur_file_info_internalSaved;
         1242  +    ZPOS64_T num_fileSaved;
         1243  +    ZPOS64_T pos_in_central_dirSaved;
         1244  +
         1245  +
         1246  +    if (file==NULL)
         1247  +        return UNZ_PARAMERROR;
         1248  +
         1249  +    if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
         1250  +        return UNZ_PARAMERROR;
         1251  +
         1252  +    s=(unz64_s*)file;
         1253  +    if (!s->current_file_ok)
         1254  +        return UNZ_END_OF_LIST_OF_FILE;
         1255  +
         1256  +    /* Save the current state */
         1257  +    num_fileSaved = s->num_file;
         1258  +    pos_in_central_dirSaved = s->pos_in_central_dir;
         1259  +    cur_file_infoSaved = s->cur_file_info;
         1260  +    cur_file_info_internalSaved = s->cur_file_info_internal;
         1261  +
         1262  +    err = unzGoToFirstFile(file);
         1263  +
         1264  +    while (err == UNZ_OK)
         1265  +    {
         1266  +        char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
         1267  +        err = unzGetCurrentFileInfo64(file,NULL,
         1268  +                                    szCurrentFileName,sizeof(szCurrentFileName)-1,
         1269  +                                    NULL,0,NULL,0);
         1270  +        if (err == UNZ_OK)
         1271  +        {
         1272  +            if (unzStringFileNameCompare(szCurrentFileName,
         1273  +                                            szFileName,iCaseSensitivity)==0)
         1274  +                return UNZ_OK;
         1275  +            err = unzGoToNextFile(file);
         1276  +        }
         1277  +    }
         1278  +
         1279  +    /* We failed, so restore the state of the 'current file' to where we
         1280  +     * were.
         1281  +     */
         1282  +    s->num_file = num_fileSaved ;
         1283  +    s->pos_in_central_dir = pos_in_central_dirSaved ;
         1284  +    s->cur_file_info = cur_file_infoSaved;
         1285  +    s->cur_file_info_internal = cur_file_info_internalSaved;
         1286  +    return err;
         1287  +}
         1288  +
         1289  +
         1290  +/*
         1291  +///////////////////////////////////////////
         1292  +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
         1293  +// I need random access
         1294  +//
         1295  +// Further optimization could be realized by adding an ability
         1296  +// to cache the directory in memory. The goal being a single
         1297  +// comprehensive file read to put the file I need in a memory.
         1298  +*/
         1299  +
         1300  +/*
         1301  +typedef struct unz_file_pos_s
         1302  +{
         1303  +    ZPOS64_T pos_in_zip_directory;   // offset in file
         1304  +    ZPOS64_T num_of_file;            // # of file
         1305  +} unz_file_pos;
         1306  +*/
         1307  +
         1308  +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos*  file_pos)
         1309  +{
         1310  +    unz64_s* s;
         1311  +
         1312  +    if (file==NULL || file_pos==NULL)
         1313  +        return UNZ_PARAMERROR;
         1314  +    s=(unz64_s*)file;
         1315  +    if (!s->current_file_ok)
         1316  +        return UNZ_END_OF_LIST_OF_FILE;
         1317  +
         1318  +    file_pos->pos_in_zip_directory  = s->pos_in_central_dir;
         1319  +    file_pos->num_of_file           = s->num_file;
         1320  +
         1321  +    return UNZ_OK;
         1322  +}
         1323  +
         1324  +extern int ZEXPORT unzGetFilePos(
         1325  +    unzFile file,
         1326  +    unz_file_pos* file_pos)
         1327  +{
         1328  +    unz64_file_pos file_pos64;
         1329  +    int err = unzGetFilePos64(file,&file_pos64);
         1330  +    if (err==UNZ_OK)
         1331  +    {
         1332  +        file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory;
         1333  +        file_pos->num_of_file = (uLong)file_pos64.num_of_file;
         1334  +    }
         1335  +    return err;
         1336  +}
         1337  +
         1338  +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos)
         1339  +{
         1340  +    unz64_s* s;
         1341  +    int err;
         1342  +
         1343  +    if (file==NULL || file_pos==NULL)
         1344  +        return UNZ_PARAMERROR;
         1345  +    s=(unz64_s*)file;
         1346  +
         1347  +    /* jump to the right spot */
         1348  +    s->pos_in_central_dir = file_pos->pos_in_zip_directory;
         1349  +    s->num_file           = file_pos->num_of_file;
         1350  +
         1351  +    /* set the current file */
         1352  +    err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info,
         1353  +                                               &s->cur_file_info_internal,
         1354  +                                               NULL,0,NULL,0,NULL,0);
         1355  +    /* return results */
         1356  +    s->current_file_ok = (err == UNZ_OK);
         1357  +    return err;
         1358  +}
         1359  +
         1360  +extern int ZEXPORT unzGoToFilePos(
         1361  +    unzFile file,
         1362  +    unz_file_pos* file_pos)
         1363  +{
         1364  +    unz64_file_pos file_pos64;
         1365  +    if (file_pos == NULL)
         1366  +        return UNZ_PARAMERROR;
         1367  +
         1368  +    file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory;
         1369  +    file_pos64.num_of_file = file_pos->num_of_file;
         1370  +    return unzGoToFilePos64(file,&file_pos64);
         1371  +}
         1372  +
         1373  +/*
         1374  +// Unzip Helper Functions - should be here?
         1375  +///////////////////////////////////////////
         1376  +*/
         1377  +
         1378  +/*
         1379  +  Read the local header of the current zipfile
         1380  +  Check the coherency of the local header and info in the end of central
         1381  +        directory about this file
         1382  +  store in *piSizeVar the size of extra info in local header
         1383  +        (filename and size of extra field data)
         1384  +*/
         1385  +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar,
         1386  +                                                    ZPOS64_T * poffset_local_extrafield,
         1387  +                                                    uInt  * psize_local_extrafield)
         1388  +{
         1389  +    uLong uMagic,uData,uFlags;
         1390  +    uLong size_filename;
         1391  +    uLong size_extra_field;
         1392  +    int err=UNZ_OK;
         1393  +
         1394  +    *piSizeVar = 0;
         1395  +    *poffset_local_extrafield = 0;
         1396  +    *psize_local_extrafield = 0;
         1397  +
         1398  +    if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
         1399  +                                s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
         1400  +        return UNZ_ERRNO;
         1401  +
         1402  +
         1403  +    if (err==UNZ_OK)
         1404  +    {
         1405  +        if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
         1406  +            err=UNZ_ERRNO;
         1407  +        else if (uMagic!=0x04034b50)
         1408  +            err=UNZ_BADZIPFILE;
         1409  +    }
         1410  +
         1411  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
         1412  +        err=UNZ_ERRNO;
         1413  +/*
         1414  +    else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
         1415  +        err=UNZ_BADZIPFILE;
         1416  +*/
         1417  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
         1418  +        err=UNZ_ERRNO;
         1419  +
         1420  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
         1421  +        err=UNZ_ERRNO;
         1422  +    else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
         1423  +        err=UNZ_BADZIPFILE;
         1424  +
         1425  +    if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
         1426  +/* #ifdef HAVE_BZIP2 */
         1427  +                         (s->cur_file_info.compression_method!=Z_BZIP2ED) &&
         1428  +/* #endif */
         1429  +                         (s->cur_file_info.compression_method!=Z_DEFLATED))
         1430  +        err=UNZ_BADZIPFILE;
         1431  +
         1432  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
         1433  +        err=UNZ_ERRNO;
         1434  +
         1435  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
         1436  +        err=UNZ_ERRNO;
         1437  +    else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0))
         1438  +        err=UNZ_BADZIPFILE;
         1439  +
         1440  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
         1441  +        err=UNZ_ERRNO;
         1442  +    else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0))
         1443  +        err=UNZ_BADZIPFILE;
         1444  +
         1445  +    if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
         1446  +        err=UNZ_ERRNO;
         1447  +    else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0))
         1448  +        err=UNZ_BADZIPFILE;
         1449  +
         1450  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
         1451  +        err=UNZ_ERRNO;
         1452  +    else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
         1453  +        err=UNZ_BADZIPFILE;
         1454  +
         1455  +    *piSizeVar += (uInt)size_filename;
         1456  +
         1457  +    if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
         1458  +        err=UNZ_ERRNO;
         1459  +    *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
         1460  +                                    SIZEZIPLOCALHEADER + size_filename;
         1461  +    *psize_local_extrafield = (uInt)size_extra_field;
         1462  +
         1463  +    *piSizeVar += (uInt)size_extra_field;
         1464  +
         1465  +    return err;
         1466  +}
         1467  +
         1468  +/*
         1469  +  Open for reading data the current file in the zipfile.
         1470  +  If there is no error and the file is opened, the return value is UNZ_OK.
         1471  +*/
         1472  +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method,
         1473  +                                            int* level, int raw, const char* password)
         1474  +{
         1475  +    int err=UNZ_OK;
         1476  +    uInt iSizeVar;
         1477  +    unz64_s* s;
         1478  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1479  +    ZPOS64_T offset_local_extrafield;  /* offset of the local extra field */
         1480  +    uInt  size_local_extrafield;    /* size of the local extra field */
         1481  +#    ifndef NOUNCRYPT
         1482  +    char source[12];
         1483  +#    else
         1484  +    if (password != NULL)
         1485  +        return UNZ_PARAMERROR;
         1486  +#    endif
         1487  +
         1488  +    if (file==NULL)
         1489  +        return UNZ_PARAMERROR;
         1490  +    s=(unz64_s*)file;
         1491  +    if (!s->current_file_ok)
         1492  +        return UNZ_PARAMERROR;
         1493  +
         1494  +    if (s->pfile_in_zip_read != NULL)
         1495  +        unzCloseCurrentFile(file);
         1496  +
         1497  +    if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
         1498  +        return UNZ_BADZIPFILE;
         1499  +
         1500  +    pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s));
         1501  +    if (pfile_in_zip_read_info==NULL)
         1502  +        return UNZ_INTERNALERROR;
         1503  +
         1504  +    pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
         1505  +    pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
         1506  +    pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
         1507  +    pfile_in_zip_read_info->pos_local_extrafield=0;
         1508  +    pfile_in_zip_read_info->raw=raw;
         1509  +
         1510  +    if (pfile_in_zip_read_info->read_buffer==NULL)
         1511  +    {
         1512  +        TRYFREE(pfile_in_zip_read_info);
         1513  +        return UNZ_INTERNALERROR;
         1514  +    }
         1515  +
         1516  +    pfile_in_zip_read_info->stream_initialised=0;
         1517  +
         1518  +    if (method!=NULL)
         1519  +        *method = (int)s->cur_file_info.compression_method;
         1520  +
         1521  +    if (level!=NULL)
         1522  +    {
         1523  +        *level = 6;
         1524  +        switch (s->cur_file_info.flag & 0x06)
         1525  +        {
         1526  +          case 6 : *level = 1; break;
         1527  +          case 4 : *level = 2; break;
         1528  +          case 2 : *level = 9; break;
         1529  +        }
         1530  +    }
         1531  +
         1532  +    if ((s->cur_file_info.compression_method!=0) &&
         1533  +/* #ifdef HAVE_BZIP2 */
         1534  +        (s->cur_file_info.compression_method!=Z_BZIP2ED) &&
         1535  +/* #endif */
         1536  +        (s->cur_file_info.compression_method!=Z_DEFLATED))
         1537  +
         1538  +        err=UNZ_BADZIPFILE;
         1539  +
         1540  +    pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
         1541  +    pfile_in_zip_read_info->crc32=0;
         1542  +    pfile_in_zip_read_info->total_out_64=0;
         1543  +    pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method;
         1544  +    pfile_in_zip_read_info->filestream=s->filestream;
         1545  +    pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
         1546  +    pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
         1547  +
         1548  +    pfile_in_zip_read_info->stream.total_out = 0;
         1549  +
         1550  +    if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw))
         1551  +    {
         1552  +#ifdef HAVE_BZIP2
         1553  +      pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0;
         1554  +      pfile_in_zip_read_info->bstream.bzfree = (free_func)0;
         1555  +      pfile_in_zip_read_info->bstream.opaque = (voidpf)0;
         1556  +      pfile_in_zip_read_info->bstream.state = (voidpf)0;
         1557  +
         1558  +      pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
         1559  +      pfile_in_zip_read_info->stream.zfree = (free_func)0;
         1560  +      pfile_in_zip_read_info->stream.opaque = (voidpf)0;
         1561  +      pfile_in_zip_read_info->stream.next_in = (voidpf)0;
         1562  +      pfile_in_zip_read_info->stream.avail_in = 0;
         1563  +
         1564  +      err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0);
         1565  +      if (err == Z_OK)
         1566  +        pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED;
         1567  +      else
         1568  +      {
         1569  +        TRYFREE(pfile_in_zip_read_info);
         1570  +        return err;
         1571  +      }
         1572  +#else
         1573  +      pfile_in_zip_read_info->raw=1;
         1574  +#endif
         1575  +    }
         1576  +    else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw))
         1577  +    {
         1578  +      pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
         1579  +      pfile_in_zip_read_info->stream.zfree = (free_func)0;
         1580  +      pfile_in_zip_read_info->stream.opaque = (voidpf)0;
         1581  +      pfile_in_zip_read_info->stream.next_in = 0;
         1582  +      pfile_in_zip_read_info->stream.avail_in = 0;
         1583  +
         1584  +      err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
         1585  +      if (err == Z_OK)
         1586  +        pfile_in_zip_read_info->stream_initialised=Z_DEFLATED;
         1587  +      else
         1588  +      {
         1589  +        TRYFREE(pfile_in_zip_read_info);
         1590  +        return err;
         1591  +      }
         1592  +        /* windowBits is passed < 0 to tell that there is no zlib header.
         1593  +         * Note that in this case inflate *requires* an extra "dummy" byte
         1594  +         * after the compressed stream in order to complete decompression and
         1595  +         * return Z_STREAM_END.
         1596  +         * In unzip, i don't wait absolutely Z_STREAM_END because I known the
         1597  +         * size of both compressed and uncompressed data
         1598  +         */
         1599  +    }
         1600  +    pfile_in_zip_read_info->rest_read_compressed =
         1601  +            s->cur_file_info.compressed_size ;
         1602  +    pfile_in_zip_read_info->rest_read_uncompressed =
         1603  +            s->cur_file_info.uncompressed_size ;
         1604  +
         1605  +
         1606  +    pfile_in_zip_read_info->pos_in_zipfile =
         1607  +            s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
         1608  +              iSizeVar;
         1609  +
         1610  +    pfile_in_zip_read_info->stream.avail_in = (uInt)0;
         1611  +
         1612  +    s->pfile_in_zip_read = pfile_in_zip_read_info;
         1613  +                s->encrypted = 0;
         1614  +
         1615  +#    ifndef NOUNCRYPT
         1616  +    if (password != NULL)
         1617  +    {
         1618  +        int i;
         1619  +        s->pcrc_32_tab = get_crc_table();
         1620  +        init_keys(password,s->keys,s->pcrc_32_tab);
         1621  +        if (ZSEEK64(s->z_filefunc, s->filestream,
         1622  +                  s->pfile_in_zip_read->pos_in_zipfile +
         1623  +                     s->pfile_in_zip_read->byte_before_the_zipfile,
         1624  +                  SEEK_SET)!=0)
         1625  +            return UNZ_INTERNALERROR;
         1626  +        if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12)
         1627  +            return UNZ_INTERNALERROR;
         1628  +
         1629  +        for (i = 0; i<12; i++)
         1630  +            zdecode(s->keys,s->pcrc_32_tab,source[i]);
         1631  +
         1632  +        s->pfile_in_zip_read->pos_in_zipfile+=12;
         1633  +        s->encrypted=1;
         1634  +    }
         1635  +#    endif
         1636  +
         1637  +
         1638  +    return UNZ_OK;
         1639  +}
         1640  +
         1641  +extern int ZEXPORT unzOpenCurrentFile (unzFile file)
         1642  +{
         1643  +    return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
         1644  +}
         1645  +
         1646  +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char*  password)
         1647  +{
         1648  +    return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
         1649  +}
         1650  +
         1651  +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw)
         1652  +{
         1653  +    return unzOpenCurrentFile3(file, method, level, raw, NULL);
         1654  +}
         1655  +
         1656  +/** Addition for GDAL : START */
         1657  +
         1658  +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file)
         1659  +{
         1660  +    unz64_s* s;
         1661  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1662  +    s=(unz64_s*)file;
         1663  +    if (file==NULL)
         1664  +        return 0; //UNZ_PARAMERROR;
         1665  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1666  +    if (pfile_in_zip_read_info==NULL)
         1667  +        return 0; //UNZ_PARAMERROR;
         1668  +    return pfile_in_zip_read_info->pos_in_zipfile +
         1669  +                         pfile_in_zip_read_info->byte_before_the_zipfile;
         1670  +}
         1671  +
         1672  +/** Addition for GDAL : END */
         1673  +
         1674  +/*
         1675  +  Read bytes from the current file.
         1676  +  buf contain buffer where data must be copied
         1677  +  len the size of buf.
         1678  +
         1679  +  return the number of byte copied if somes bytes are copied
         1680  +  return 0 if the end of file was reached
         1681  +  return <0 with error code if there is an error
         1682  +    (UNZ_ERRNO for IO error, or zLib error for uncompress error)
         1683  +*/
         1684  +extern int ZEXPORT unzReadCurrentFile  (unzFile file, voidp buf, unsigned len)
         1685  +{
         1686  +    int err=UNZ_OK;
         1687  +    uInt iRead = 0;
         1688  +    unz64_s* s;
         1689  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1690  +    if (file==NULL)
         1691  +        return UNZ_PARAMERROR;
         1692  +    s=(unz64_s*)file;
         1693  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1694  +
         1695  +    if (pfile_in_zip_read_info==NULL)
         1696  +        return UNZ_PARAMERROR;
         1697  +
         1698  +
         1699  +    if (pfile_in_zip_read_info->read_buffer == NULL)
         1700  +        return UNZ_END_OF_LIST_OF_FILE;
         1701  +    if (len==0)
         1702  +        return 0;
         1703  +
         1704  +    pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
         1705  +
         1706  +    pfile_in_zip_read_info->stream.avail_out = (uInt)len;
         1707  +
         1708  +    if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
         1709  +        (!(pfile_in_zip_read_info->raw)))
         1710  +        pfile_in_zip_read_info->stream.avail_out =
         1711  +            (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
         1712  +
         1713  +    if ((len>pfile_in_zip_read_info->rest_read_compressed+
         1714  +           pfile_in_zip_read_info->stream.avail_in) &&
         1715  +         (pfile_in_zip_read_info->raw))
         1716  +        pfile_in_zip_read_info->stream.avail_out =
         1717  +            (uInt)pfile_in_zip_read_info->rest_read_compressed+
         1718  +            pfile_in_zip_read_info->stream.avail_in;
         1719  +
         1720  +    while (pfile_in_zip_read_info->stream.avail_out>0)
         1721  +    {
         1722  +        if ((pfile_in_zip_read_info->stream.avail_in==0) &&
         1723  +            (pfile_in_zip_read_info->rest_read_compressed>0))
         1724  +        {
         1725  +            uInt uReadThis = UNZ_BUFSIZE;
         1726  +            if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
         1727  +                uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
         1728  +            if (uReadThis == 0)
         1729  +                return UNZ_EOF;
         1730  +            if (ZSEEK64(pfile_in_zip_read_info->z_filefunc,
         1731  +                      pfile_in_zip_read_info->filestream,
         1732  +                      pfile_in_zip_read_info->pos_in_zipfile +
         1733  +                         pfile_in_zip_read_info->byte_before_the_zipfile,
         1734  +                         ZLIB_FILEFUNC_SEEK_SET)!=0)
         1735  +                return UNZ_ERRNO;
         1736  +            if (ZREAD64(pfile_in_zip_read_info->z_filefunc,
         1737  +                      pfile_in_zip_read_info->filestream,
         1738  +                      pfile_in_zip_read_info->read_buffer,
         1739  +                      uReadThis)!=uReadThis)
         1740  +                return UNZ_ERRNO;
         1741  +
         1742  +
         1743  +#            ifndef NOUNCRYPT
         1744  +            if(s->encrypted)
         1745  +            {
         1746  +                uInt i;
         1747  +                for(i=0;i<uReadThis;i++)
         1748  +                  pfile_in_zip_read_info->read_buffer[i] =
         1749  +                      zdecode(s->keys,s->pcrc_32_tab,
         1750  +                              pfile_in_zip_read_info->read_buffer[i]);
         1751  +            }
         1752  +#            endif
         1753  +
         1754  +
         1755  +            pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
         1756  +
         1757  +            pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
         1758  +
         1759  +            pfile_in_zip_read_info->stream.next_in =
         1760  +                (Bytef*)pfile_in_zip_read_info->read_buffer;
         1761  +            pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
         1762  +        }
         1763  +
         1764  +        if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
         1765  +        {
         1766  +            uInt uDoCopy,i ;
         1767  +
         1768  +            if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
         1769  +                (pfile_in_zip_read_info->rest_read_compressed == 0))
         1770  +                return (iRead==0) ? UNZ_EOF : iRead;
         1771  +
         1772  +            if (pfile_in_zip_read_info->stream.avail_out <
         1773  +                            pfile_in_zip_read_info->stream.avail_in)
         1774  +                uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
         1775  +            else
         1776  +                uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
         1777  +
         1778  +            for (i=0;i<uDoCopy;i++)
         1779  +                *(pfile_in_zip_read_info->stream.next_out+i) =
         1780  +                        *(pfile_in_zip_read_info->stream.next_in+i);
         1781  +
         1782  +            pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy;
         1783  +
         1784  +            pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
         1785  +                                pfile_in_zip_read_info->stream.next_out,
         1786  +                                uDoCopy);
         1787  +            pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
         1788  +            pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
         1789  +            pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
         1790  +            pfile_in_zip_read_info->stream.next_out += uDoCopy;
         1791  +            pfile_in_zip_read_info->stream.next_in += uDoCopy;
         1792  +            pfile_in_zip_read_info->stream.total_out += uDoCopy;
         1793  +            iRead += uDoCopy;
         1794  +        }
         1795  +        else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED)
         1796  +        {
         1797  +#ifdef HAVE_BZIP2
         1798  +            uLong uTotalOutBefore,uTotalOutAfter;
         1799  +            const Bytef *bufBefore;
         1800  +            uLong uOutThis;
         1801  +
         1802  +            pfile_in_zip_read_info->bstream.next_in        = (char*)pfile_in_zip_read_info->stream.next_in;
         1803  +            pfile_in_zip_read_info->bstream.avail_in       = pfile_in_zip_read_info->stream.avail_in;
         1804  +            pfile_in_zip_read_info->bstream.total_in_lo32  = pfile_in_zip_read_info->stream.total_in;
         1805  +            pfile_in_zip_read_info->bstream.total_in_hi32  = 0;
         1806  +            pfile_in_zip_read_info->bstream.next_out       = (char*)pfile_in_zip_read_info->stream.next_out;
         1807  +            pfile_in_zip_read_info->bstream.avail_out      = pfile_in_zip_read_info->stream.avail_out;
         1808  +            pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out;
         1809  +            pfile_in_zip_read_info->bstream.total_out_hi32 = 0;
         1810  +
         1811  +            uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32;
         1812  +            bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out;
         1813  +
         1814  +            err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream);
         1815  +
         1816  +            uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32;
         1817  +            uOutThis = uTotalOutAfter-uTotalOutBefore;
         1818  +
         1819  +            pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis;
         1820  +
         1821  +            pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis));
         1822  +            pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis;
         1823  +            iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
         1824  +
         1825  +            pfile_in_zip_read_info->stream.next_in   = (Bytef*)pfile_in_zip_read_info->bstream.next_in;
         1826  +            pfile_in_zip_read_info->stream.avail_in  = pfile_in_zip_read_info->bstream.avail_in;
         1827  +            pfile_in_zip_read_info->stream.total_in  = pfile_in_zip_read_info->bstream.total_in_lo32;
         1828  +            pfile_in_zip_read_info->stream.next_out  = (Bytef*)pfile_in_zip_read_info->bstream.next_out;
         1829  +            pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out;
         1830  +            pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32;
         1831  +
         1832  +            if (err==BZ_STREAM_END)
         1833  +              return (iRead==0) ? UNZ_EOF : iRead;
         1834  +            if (err!=BZ_OK)
         1835  +              break;
         1836  +#endif
         1837  +        } // end Z_BZIP2ED
         1838  +        else
         1839  +        {
         1840  +            ZPOS64_T uTotalOutBefore,uTotalOutAfter;
         1841  +            const Bytef *bufBefore;
         1842  +            ZPOS64_T uOutThis;
         1843  +            int flush=Z_SYNC_FLUSH;
         1844  +
         1845  +            uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
         1846  +            bufBefore = pfile_in_zip_read_info->stream.next_out;
         1847  +
         1848  +            /*
         1849  +            if ((pfile_in_zip_read_info->rest_read_uncompressed ==
         1850  +                     pfile_in_zip_read_info->stream.avail_out) &&
         1851  +                (pfile_in_zip_read_info->rest_read_compressed == 0))
         1852  +                flush = Z_FINISH;
         1853  +            */
         1854  +            err=inflate(&pfile_in_zip_read_info->stream,flush);
         1855  +
         1856  +            if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
         1857  +              err = Z_DATA_ERROR;
         1858  +
         1859  +            uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
         1860  +            uOutThis = uTotalOutAfter-uTotalOutBefore;
         1861  +
         1862  +            pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis;
         1863  +
         1864  +            pfile_in_zip_read_info->crc32 =
         1865  +                crc32(pfile_in_zip_read_info->crc32,bufBefore,
         1866  +                        (uInt)(uOutThis));
         1867  +
         1868  +            pfile_in_zip_read_info->rest_read_uncompressed -=
         1869  +                uOutThis;
         1870  +
         1871  +            iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
         1872  +
         1873  +            if (err==Z_STREAM_END)
         1874  +                return (iRead==0) ? UNZ_EOF : iRead;
         1875  +            if (err!=Z_OK)
         1876  +                break;
         1877  +        }
         1878  +    }
         1879  +
         1880  +    if (err==Z_OK)
         1881  +        return iRead;
         1882  +    return err;
         1883  +}
         1884  +
         1885  +
         1886  +/*
         1887  +  Give the current position in uncompressed data
         1888  +*/
         1889  +extern z_off_t ZEXPORT unztell (unzFile file)
         1890  +{
         1891  +    unz64_s* s;
         1892  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1893  +    if (file==NULL)
         1894  +        return UNZ_PARAMERROR;
         1895  +    s=(unz64_s*)file;
         1896  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1897  +
         1898  +    if (pfile_in_zip_read_info==NULL)
         1899  +        return UNZ_PARAMERROR;
         1900  +
         1901  +    return (z_off_t)pfile_in_zip_read_info->stream.total_out;
         1902  +}
         1903  +
         1904  +extern ZPOS64_T ZEXPORT unztell64 (unzFile file)
         1905  +{
         1906  +
         1907  +    unz64_s* s;
         1908  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1909  +    if (file==NULL)
         1910  +        return (ZPOS64_T)-1;
         1911  +    s=(unz64_s*)file;
         1912  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1913  +
         1914  +    if (pfile_in_zip_read_info==NULL)
         1915  +        return (ZPOS64_T)-1;
         1916  +
         1917  +    return pfile_in_zip_read_info->total_out_64;
         1918  +}
         1919  +
         1920  +
         1921  +/*
         1922  +  return 1 if the end of file was reached, 0 elsewhere
         1923  +*/
         1924  +extern int ZEXPORT unzeof (unzFile file)
         1925  +{
         1926  +    unz64_s* s;
         1927  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1928  +    if (file==NULL)
         1929  +        return UNZ_PARAMERROR;
         1930  +    s=(unz64_s*)file;
         1931  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1932  +
         1933  +    if (pfile_in_zip_read_info==NULL)
         1934  +        return UNZ_PARAMERROR;
         1935  +
         1936  +    if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
         1937  +        return 1;
         1938  +    else
         1939  +        return 0;
         1940  +}
         1941  +
         1942  +
         1943  +
         1944  +/*
         1945  +Read extra field from the current file (opened by unzOpenCurrentFile)
         1946  +This is the local-header version of the extra field (sometimes, there is
         1947  +more info in the local-header version than in the central-header)
         1948  +
         1949  +  if buf==NULL, it return the size of the local extra field that can be read
         1950  +
         1951  +  if buf!=NULL, len is the size of the buffer, the extra header is copied in
         1952  +    buf.
         1953  +  the return value is the number of bytes copied in buf, or (if <0)
         1954  +    the error code
         1955  +*/
         1956  +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len)
         1957  +{
         1958  +    unz64_s* s;
         1959  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         1960  +    uInt read_now;
         1961  +    ZPOS64_T size_to_read;
         1962  +
         1963  +    if (file==NULL)
         1964  +        return UNZ_PARAMERROR;
         1965  +    s=(unz64_s*)file;
         1966  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         1967  +
         1968  +    if (pfile_in_zip_read_info==NULL)
         1969  +        return UNZ_PARAMERROR;
         1970  +
         1971  +    size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
         1972  +                pfile_in_zip_read_info->pos_local_extrafield);
         1973  +
         1974  +    if (buf==NULL)
         1975  +        return (int)size_to_read;
         1976  +
         1977  +    if (len>size_to_read)
         1978  +        read_now = (uInt)size_to_read;
         1979  +    else
         1980  +        read_now = (uInt)len ;
         1981  +
         1982  +    if (read_now==0)
         1983  +        return 0;
         1984  +
         1985  +    if (ZSEEK64(pfile_in_zip_read_info->z_filefunc,
         1986  +              pfile_in_zip_read_info->filestream,
         1987  +              pfile_in_zip_read_info->offset_local_extrafield +
         1988  +              pfile_in_zip_read_info->pos_local_extrafield,
         1989  +              ZLIB_FILEFUNC_SEEK_SET)!=0)
         1990  +        return UNZ_ERRNO;
         1991  +
         1992  +    if (ZREAD64(pfile_in_zip_read_info->z_filefunc,
         1993  +              pfile_in_zip_read_info->filestream,
         1994  +              buf,read_now)!=read_now)
         1995  +        return UNZ_ERRNO;
         1996  +
         1997  +    return (int)read_now;
         1998  +}
         1999  +
         2000  +/*
         2001  +  Close the file in zip opened with unzipOpenCurrentFile
         2002  +  Return UNZ_CRCERROR if all the file was read but the CRC is not good
         2003  +*/
         2004  +extern int ZEXPORT unzCloseCurrentFile (unzFile file)
         2005  +{
         2006  +    int err=UNZ_OK;
         2007  +
         2008  +    unz64_s* s;
         2009  +    file_in_zip64_read_info_s* pfile_in_zip_read_info;
         2010  +    if (file==NULL)
         2011  +        return UNZ_PARAMERROR;
         2012  +    s=(unz64_s*)file;
         2013  +    pfile_in_zip_read_info=s->pfile_in_zip_read;
         2014  +
         2015  +    if (pfile_in_zip_read_info==NULL)
         2016  +        return UNZ_PARAMERROR;
         2017  +
         2018  +
         2019  +    if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
         2020  +        (!pfile_in_zip_read_info->raw))
         2021  +    {
         2022  +        if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
         2023  +            err=UNZ_CRCERROR;
         2024  +    }
         2025  +
         2026  +
         2027  +    TRYFREE(pfile_in_zip_read_info->read_buffer);
         2028  +    pfile_in_zip_read_info->read_buffer = NULL;
         2029  +    if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED)
         2030  +        inflateEnd(&pfile_in_zip_read_info->stream);
         2031  +#ifdef HAVE_BZIP2
         2032  +    else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED)
         2033  +        BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream);
         2034  +#endif
         2035  +
         2036  +
         2037  +    pfile_in_zip_read_info->stream_initialised = 0;
         2038  +    TRYFREE(pfile_in_zip_read_info);
         2039  +
         2040  +    s->pfile_in_zip_read=NULL;
         2041  +
         2042  +    return err;
         2043  +}
         2044  +
         2045  +
         2046  +/*
         2047  +  Get the global comment string of the ZipFile, in the szComment buffer.
         2048  +  uSizeBuf is the size of the szComment buffer.
         2049  +  return the number of byte copied or an error code <0
         2050  +*/
         2051  +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf)
         2052  +{
         2053  +    unz64_s* s;
         2054  +    uLong uReadThis ;
         2055  +    if (file==NULL)
         2056  +        return (int)UNZ_PARAMERROR;
         2057  +    s=(unz64_s*)file;
         2058  +
         2059  +    uReadThis = uSizeBuf;
         2060  +    if (uReadThis>s->gi.size_comment)
         2061  +        uReadThis = s->gi.size_comment;
         2062  +
         2063  +    if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
         2064  +        return UNZ_ERRNO;
         2065  +
         2066  +    if (uReadThis>0)
         2067  +    {
         2068  +      *szComment='\0';
         2069  +      if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
         2070  +        return UNZ_ERRNO;
         2071  +    }
         2072  +
         2073  +    if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
         2074  +        *(szComment+s->gi.size_comment)='\0';
         2075  +    return (int)uReadThis;
         2076  +}
         2077  +
         2078  +/* Additions by RX '2004 */
         2079  +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file)
         2080  +{
         2081  +    unz64_s* s;
         2082  +
         2083  +    if (file==NULL)
         2084  +          return 0; //UNZ_PARAMERROR;
         2085  +    s=(unz64_s*)file;
         2086  +    if (!s->current_file_ok)
         2087  +      return 0;
         2088  +    if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
         2089  +      if (s->num_file==s->gi.number_entry)
         2090  +         return 0;
         2091  +    return s->pos_in_central_dir;
         2092  +}
         2093  +
         2094  +extern uLong ZEXPORT unzGetOffset (unzFile file)
         2095  +{
         2096  +    ZPOS64_T offset64;
         2097  +
         2098  +    if (file==NULL)
         2099  +          return 0; //UNZ_PARAMERROR;
         2100  +    offset64 = unzGetOffset64(file);
         2101  +    return (uLong)offset64;
         2102  +}
         2103  +
         2104  +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos)
         2105  +{
         2106  +    unz64_s* s;
         2107  +    int err;
         2108  +
         2109  +    if (file==NULL)
         2110  +        return UNZ_PARAMERROR;
         2111  +    s=(unz64_s*)file;
         2112  +
         2113  +    s->pos_in_central_dir = pos;
         2114  +    s->num_file = s->gi.number_entry;      /* hack */
         2115  +    err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info,
         2116  +                                              &s->cur_file_info_internal,
         2117  +                                              NULL,0,NULL,0,NULL,0);
         2118  +    s->current_file_ok = (err == UNZ_OK);
         2119  +    return err;
         2120  +}
         2121  +
         2122  +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos)
         2123  +{
         2124  +    return unzSetOffset64(file,pos);
         2125  +}

Added compat/zlib/contrib/minizip/unzip.h.

            1  +/* unzip.h -- IO for uncompress .zip files using zlib
            2  +   Version 1.1, February 14h, 2010
            3  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications of Unzip for Zip64
            8  +         Copyright (C) 2007-2008 Even Rouault
            9  +
           10  +         Modifications for Zip64 support on both zip and unzip
           11  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
           12  +
           13  +         For more info read MiniZip_info.txt
           14  +
           15  +         ---------------------------------------------------------------------------------
           16  +
           17  +        Condition of use and distribution are the same than zlib :
           18  +
           19  +  This software is provided 'as-is', without any express or implied
           20  +  warranty.  In no event will the authors be held liable for any damages
           21  +  arising from the use of this software.
           22  +
           23  +  Permission is granted to anyone to use this software for any purpose,
           24  +  including commercial applications, and to alter it and redistribute it
           25  +  freely, subject to the following restrictions:
           26  +
           27  +  1. The origin of this software must not be misrepresented; you must not
           28  +     claim that you wrote the original software. If you use this software
           29  +     in a product, an acknowledgment in the product documentation would be
           30  +     appreciated but is not required.
           31  +  2. Altered source versions must be plainly marked as such, and must not be
           32  +     misrepresented as being the original software.
           33  +  3. This notice may not be removed or altered from any source distribution.
           34  +
           35  +  ---------------------------------------------------------------------------------
           36  +
           37  +        Changes
           38  +
           39  +        See header of unzip64.c
           40  +
           41  +*/
           42  +
           43  +#ifndef _unz64_H
           44  +#define _unz64_H
           45  +
           46  +#ifdef __cplusplus
           47  +extern "C" {
           48  +#endif
           49  +
           50  +#ifndef _ZLIB_H
           51  +#include "zlib.h"
           52  +#endif
           53  +
           54  +#ifndef  _ZLIBIOAPI_H
           55  +#include "ioapi.h"
           56  +#endif
           57  +
           58  +#ifdef HAVE_BZIP2
           59  +#include "bzlib.h"
           60  +#endif
           61  +
           62  +#define Z_BZIP2ED 12
           63  +
           64  +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
           65  +/* like the STRICT of WIN32, we define a pointer that cannot be converted
           66  +    from (void*) without cast */
           67  +typedef struct TagunzFile__ { int unused; } unzFile__;
           68  +typedef unzFile__ *unzFile;
           69  +#else
           70  +typedef voidp unzFile;
           71  +#endif
           72  +
           73  +
           74  +#define UNZ_OK                          (0)
           75  +#define UNZ_END_OF_LIST_OF_FILE         (-100)
           76  +#define UNZ_ERRNO                       (Z_ERRNO)
           77  +#define UNZ_EOF                         (0)
           78  +#define UNZ_PARAMERROR                  (-102)
           79  +#define UNZ_BADZIPFILE                  (-103)
           80  +#define UNZ_INTERNALERROR               (-104)
           81  +#define UNZ_CRCERROR                    (-105)
           82  +
           83  +/* tm_unz contain date/time info */
           84  +typedef struct tm_unz_s
           85  +{
           86  +    uInt tm_sec;            /* seconds after the minute - [0,59] */
           87  +    uInt tm_min;            /* minutes after the hour - [0,59] */
           88  +    uInt tm_hour;           /* hours since midnight - [0,23] */
           89  +    uInt tm_mday;           /* day of the month - [1,31] */
           90  +    uInt tm_mon;            /* months since January - [0,11] */
           91  +    uInt tm_year;           /* years - [1980..2044] */
           92  +} tm_unz;
           93  +
           94  +/* unz_global_info structure contain global data about the ZIPfile
           95  +   These data comes from the end of central dir */
           96  +typedef struct unz_global_info64_s
           97  +{
           98  +    ZPOS64_T number_entry;         /* total number of entries in
           99  +                                     the central dir on this disk */
          100  +    uLong size_comment;         /* size of the global comment of the zipfile */
          101  +} unz_global_info64;
          102  +
          103  +typedef struct unz_global_info_s
          104  +{
          105  +    uLong number_entry;         /* total number of entries in
          106  +                                     the central dir on this disk */
          107  +    uLong size_comment;         /* size of the global comment of the zipfile */
          108  +} unz_global_info;
          109  +
          110  +/* unz_file_info contain information about a file in the zipfile */
          111  +typedef struct unz_file_info64_s
          112  +{
          113  +    uLong version;              /* version made by                 2 bytes */
          114  +    uLong version_needed;       /* version needed to extract       2 bytes */
          115  +    uLong flag;                 /* general purpose bit flag        2 bytes */
          116  +    uLong compression_method;   /* compression method              2 bytes */
          117  +    uLong dosDate;              /* last mod file date in Dos fmt   4 bytes */
          118  +    uLong crc;                  /* crc-32                          4 bytes */
          119  +    ZPOS64_T compressed_size;   /* compressed size                 8 bytes */
          120  +    ZPOS64_T uncompressed_size; /* uncompressed size               8 bytes */
          121  +    uLong size_filename;        /* filename length                 2 bytes */
          122  +    uLong size_file_extra;      /* extra field length              2 bytes */
          123  +    uLong size_file_comment;    /* file comment length             2 bytes */
          124  +
          125  +    uLong disk_num_start;       /* disk number start               2 bytes */
          126  +    uLong internal_fa;          /* internal file attributes        2 bytes */
          127  +    uLong external_fa;          /* external file attributes        4 bytes */
          128  +
          129  +    tm_unz tmu_date;
          130  +} unz_file_info64;
          131  +
          132  +typedef struct unz_file_info_s
          133  +{
          134  +    uLong version;              /* version made by                 2 bytes */
          135  +    uLong version_needed;       /* version needed to extract       2 bytes */
          136  +    uLong flag;                 /* general purpose bit flag        2 bytes */
          137  +    uLong compression_method;   /* compression method              2 bytes */
          138  +    uLong dosDate;              /* last mod file date in Dos fmt   4 bytes */
          139  +    uLong crc;                  /* crc-32                          4 bytes */
          140  +    uLong compressed_size;      /* compressed size                 4 bytes */
          141  +    uLong uncompressed_size;    /* uncompressed size               4 bytes */
          142  +    uLong size_filename;        /* filename length                 2 bytes */
          143  +    uLong size_file_extra;      /* extra field length              2 bytes */
          144  +    uLong size_file_comment;    /* file comment length             2 bytes */
          145  +
          146  +    uLong disk_num_start;       /* disk number start               2 bytes */
          147  +    uLong internal_fa;          /* internal file attributes        2 bytes */
          148  +    uLong external_fa;          /* external file attributes        4 bytes */
          149  +
          150  +    tm_unz tmu_date;
          151  +} unz_file_info;
          152  +
          153  +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
          154  +                                                 const char* fileName2,
          155  +                                                 int iCaseSensitivity));
          156  +/*
          157  +   Compare two filename (fileName1,fileName2).
          158  +   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
          159  +   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
          160  +                                or strcasecmp)
          161  +   If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
          162  +    (like 1 on Unix, 2 on Windows)
          163  +*/
          164  +
          165  +
          166  +extern unzFile ZEXPORT unzOpen OF((const char *path));
          167  +extern unzFile ZEXPORT unzOpen64 OF((const void *path));
          168  +/*
          169  +  Open a Zip file. path contain the full pathname (by example,
          170  +     on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
          171  +     "zlib/zlib113.zip".
          172  +     If the zipfile cannot be opened (file don't exist or in not valid), the
          173  +       return value is NULL.
          174  +     Else, the return value is a unzFile Handle, usable with other function
          175  +       of this unzip package.
          176  +     the "64" function take a const void* pointer, because the path is just the
          177  +       value passed to the open64_file_func callback.
          178  +     Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path
          179  +       is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char*
          180  +       does not describe the reality
          181  +*/
          182  +
          183  +
          184  +extern unzFile ZEXPORT unzOpen2 OF((const char *path,
          185  +                                    zlib_filefunc_def* pzlib_filefunc_def));
          186  +/*
          187  +   Open a Zip file, like unzOpen, but provide a set of file low level API
          188  +      for read/write the zip file (see ioapi.h)
          189  +*/
          190  +
          191  +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path,
          192  +                                    zlib_filefunc64_def* pzlib_filefunc_def));
          193  +/*
          194  +   Open a Zip file, like unz64Open, but provide a set of file low level API
          195  +      for read/write the zip file (see ioapi.h)
          196  +*/
          197  +
          198  +extern int ZEXPORT unzClose OF((unzFile file));
          199  +/*
          200  +  Close a ZipFile opened with unzipOpen.
          201  +  If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
          202  +    these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
          203  +  return UNZ_OK if there is no problem. */
          204  +
          205  +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
          206  +                                        unz_global_info *pglobal_info));
          207  +
          208  +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file,
          209  +                                        unz_global_info64 *pglobal_info));
          210  +/*
          211  +  Write info about the ZipFile in the *pglobal_info structure.
          212  +  No preparation of the structure is needed
          213  +  return UNZ_OK if there is no problem. */
          214  +
          215  +
          216  +extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
          217  +                                           char *szComment,
          218  +                                           uLong uSizeBuf));
          219  +/*
          220  +  Get the global comment string of the ZipFile, in the szComment buffer.
          221  +  uSizeBuf is the size of the szComment buffer.
          222  +  return the number of byte copied or an error code <0
          223  +*/
          224  +
          225  +
          226  +/***************************************************************************/
          227  +/* Unzip package allow you browse the directory of the zipfile */
          228  +
          229  +extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
          230  +/*
          231  +  Set the current file of the zipfile to the first file.
          232  +  return UNZ_OK if there is no problem
          233  +*/
          234  +
          235  +extern int ZEXPORT unzGoToNextFile OF((unzFile file));
          236  +/*
          237  +  Set the current file of the zipfile to the next file.
          238  +  return UNZ_OK if there is no problem
          239  +  return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
          240  +*/
          241  +
          242  +extern int ZEXPORT unzLocateFile OF((unzFile file,
          243  +                     const char *szFileName,
          244  +                     int iCaseSensitivity));
          245  +/*
          246  +  Try locate the file szFileName in the zipfile.
          247  +  For the iCaseSensitivity signification, see unzStringFileNameCompare
          248  +
          249  +  return value :
          250  +  UNZ_OK if the file is found. It becomes the current file.
          251  +  UNZ_END_OF_LIST_OF_FILE if the file is not found
          252  +*/
          253  +
          254  +
          255  +/* ****************************************** */
          256  +/* Ryan supplied functions */
          257  +/* unz_file_info contain information about a file in the zipfile */
          258  +typedef struct unz_file_pos_s
          259  +{
          260  +    uLong pos_in_zip_directory;   /* offset in zip file directory */
          261  +    uLong num_of_file;            /* # of file */
          262  +} unz_file_pos;
          263  +
          264  +extern int ZEXPORT unzGetFilePos(
          265  +    unzFile file,
          266  +    unz_file_pos* file_pos);
          267  +
          268  +extern int ZEXPORT unzGoToFilePos(
          269  +    unzFile file,
          270  +    unz_file_pos* file_pos);
          271  +
          272  +typedef struct unz64_file_pos_s
          273  +{
          274  +    ZPOS64_T pos_in_zip_directory;   /* offset in zip file directory */
          275  +    ZPOS64_T num_of_file;            /* # of file */
          276  +} unz64_file_pos;
          277  +
          278  +extern int ZEXPORT unzGetFilePos64(
          279  +    unzFile file,
          280  +    unz64_file_pos* file_pos);
          281  +
          282  +extern int ZEXPORT unzGoToFilePos64(
          283  +    unzFile file,
          284  +    const unz64_file_pos* file_pos);
          285  +
          286  +/* ****************************************** */
          287  +
          288  +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
          289  +                         unz_file_info64 *pfile_info,
          290  +                         char *szFileName,
          291  +                         uLong fileNameBufferSize,
          292  +                         void *extraField,
          293  +                         uLong extraFieldBufferSize,
          294  +                         char *szComment,
          295  +                         uLong commentBufferSize));
          296  +
          297  +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
          298  +                         unz_file_info *pfile_info,
          299  +                         char *szFileName,
          300  +                         uLong fileNameBufferSize,
          301  +                         void *extraField,
          302  +                         uLong extraFieldBufferSize,
          303  +                         char *szComment,
          304  +                         uLong commentBufferSize));
          305  +/*
          306  +  Get Info about the current file
          307  +  if pfile_info!=NULL, the *pfile_info structure will contain somes info about
          308  +        the current file
          309  +  if szFileName!=NULL, the filemane string will be copied in szFileName
          310  +            (fileNameBufferSize is the size of the buffer)
          311  +  if extraField!=NULL, the extra field information will be copied in extraField
          312  +            (extraFieldBufferSize is the size of the buffer).
          313  +            This is the Central-header version of the extra field
          314  +  if szComment!=NULL, the comment string of the file will be copied in szComment
          315  +            (commentBufferSize is the size of the buffer)
          316  +*/
          317  +
          318  +
          319  +/** Addition for GDAL : START */
          320  +
          321  +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
          322  +
          323  +/** Addition for GDAL : END */
          324  +
          325  +
          326  +/***************************************************************************/
          327  +/* for reading the content of the current zipfile, you can open it, read data
          328  +   from it, and close it (you can close it before reading all the file)
          329  +   */
          330  +
          331  +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
          332  +/*
          333  +  Open for reading data the current file in the zipfile.
          334  +  If there is no error, the return value is UNZ_OK.
          335  +*/
          336  +
          337  +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
          338  +                                                  const char* password));
          339  +/*
          340  +  Open for reading data the current file in the zipfile.
          341  +  password is a crypting password
          342  +  If there is no error, the return value is UNZ_OK.
          343  +*/
          344  +
          345  +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
          346  +                                           int* method,
          347  +                                           int* level,
          348  +                                           int raw));
          349  +/*
          350  +  Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
          351  +    if raw==1
          352  +  *method will receive method of compression, *level will receive level of
          353  +     compression
          354  +  note : you can set level parameter as NULL (if you did not want known level,
          355  +         but you CANNOT set method parameter as NULL
          356  +*/
          357  +
          358  +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
          359  +                                           int* method,
          360  +                                           int* level,
          361  +                                           int raw,
          362  +                                           const char* password));
          363  +/*
          364  +  Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
          365  +    if raw==1
          366  +  *method will receive method of compression, *level will receive level of
          367  +     compression
          368  +  note : you can set level parameter as NULL (if you did not want known level,
          369  +         but you CANNOT set method parameter as NULL
          370  +*/
          371  +
          372  +
          373  +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
          374  +/*
          375  +  Close the file in zip opened with unzOpenCurrentFile
          376  +  Return UNZ_CRCERROR if all the file was read but the CRC is not good
          377  +*/
          378  +
          379  +extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
          380  +                      voidp buf,
          381  +                      unsigned len));
          382  +/*
          383  +  Read bytes from the current file (opened by unzOpenCurrentFile)
          384  +  buf contain buffer where data must be copied
          385  +  len the size of buf.
          386  +
          387  +  return the number of byte copied if somes bytes are copied
          388  +  return 0 if the end of file was reached
          389  +  return <0 with error code if there is an error
          390  +    (UNZ_ERRNO for IO error, or zLib error for uncompress error)
          391  +*/
          392  +
          393  +extern z_off_t ZEXPORT unztell OF((unzFile file));
          394  +
          395  +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
          396  +/*
          397  +  Give the current position in uncompressed data
          398  +*/
          399  +
          400  +extern int ZEXPORT unzeof OF((unzFile file));
          401  +/*
          402  +  return 1 if the end of file was reached, 0 elsewhere
          403  +*/
          404  +
          405  +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
          406  +                                             voidp buf,
          407  +                                             unsigned len));
          408  +/*
          409  +  Read extra field from the current file (opened by unzOpenCurrentFile)
          410  +  This is the local-header version of the extra field (sometimes, there is
          411  +    more info in the local-header version than in the central-header)
          412  +
          413  +  if buf==NULL, it return the size of the local extra field
          414  +
          415  +  if buf!=NULL, len is the size of the buffer, the extra header is copied in
          416  +    buf.
          417  +  the return value is the number of bytes copied in buf, or (if <0)
          418  +    the error code
          419  +*/
          420  +
          421  +/***************************************************************************/
          422  +
          423  +/* Get the current file offset */
          424  +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file);
          425  +extern uLong ZEXPORT unzGetOffset (unzFile file);
          426  +
          427  +/* Set the current file offset */
          428  +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
          429  +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
          430  +
          431  +
          432  +
          433  +#ifdef __cplusplus
          434  +}
          435  +#endif
          436  +
          437  +#endif /* _unz64_H */

Added compat/zlib/contrib/minizip/zip.c.

            1  +/* zip.c -- IO on .zip files using zlib
            2  +   Version 1.1, February 14h, 2010
            3  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications for Zip64 support
            8  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            9  +
           10  +         For more info read MiniZip_info.txt
           11  +
           12  +         Changes
           13  +   Oct-2009 - Mathias Svensson - Remove old C style function prototypes
           14  +   Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives
           15  +   Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions.
           16  +   Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data
           17  +                                 It is used when recreting zip archive with RAW when deleting items from a zip.
           18  +                                 ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed.
           19  +   Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required)
           20  +   Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer
           21  +
           22  +*/
           23  +
           24  +
           25  +#include <stdio.h>
           26  +#include <stdlib.h>
           27  +#include <string.h>
           28  +#include <time.h>
           29  +#include "zlib.h"
           30  +#include "zip.h"
           31  +
           32  +#ifdef STDC
           33  +#  include <stddef.h>
           34  +#  include <string.h>
           35  +#  include <stdlib.h>
           36  +#endif
           37  +#ifdef NO_ERRNO_H
           38  +    extern int errno;
           39  +#else
           40  +#   include <errno.h>
           41  +#endif
           42  +
           43  +
           44  +#ifndef local
           45  +#  define local static
           46  +#endif
           47  +/* compile with -Dlocal if your debugger can't find static symbols */
           48  +
           49  +#ifndef VERSIONMADEBY
           50  +# define VERSIONMADEBY   (0x0) /* platform depedent */
           51  +#endif
           52  +
           53  +#ifndef Z_BUFSIZE
           54  +#define Z_BUFSIZE (64*1024) //(16384)
           55  +#endif
           56  +
           57  +#ifndef Z_MAXFILENAMEINZIP
           58  +#define Z_MAXFILENAMEINZIP (256)
           59  +#endif
           60  +
           61  +#ifndef ALLOC
           62  +# define ALLOC(size) (malloc(size))
           63  +#endif
           64  +#ifndef TRYFREE
           65  +# define TRYFREE(p) {if (p) free(p);}
           66  +#endif
           67  +
           68  +/*
           69  +#define SIZECENTRALDIRITEM (0x2e)
           70  +#define SIZEZIPLOCALHEADER (0x1e)
           71  +*/
           72  +
           73  +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
           74  +
           75  +
           76  +// NOT sure that this work on ALL platform
           77  +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32))
           78  +
           79  +#ifndef SEEK_CUR
           80  +#define SEEK_CUR    1
           81  +#endif
           82  +
           83  +#ifndef SEEK_END
           84  +#define SEEK_END    2
           85  +#endif
           86  +
           87  +#ifndef SEEK_SET
           88  +#define SEEK_SET    0
           89  +#endif
           90  +
           91  +#ifndef DEF_MEM_LEVEL
           92  +#if MAX_MEM_LEVEL >= 8
           93  +#  define DEF_MEM_LEVEL 8
           94  +#else
           95  +#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
           96  +#endif
           97  +#endif
           98  +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
           99  +
          100  +
          101  +#define SIZEDATA_INDATABLOCK (4096-(4*4))
          102  +
          103  +#define LOCALHEADERMAGIC    (0x04034b50)
          104  +#define CENTRALHEADERMAGIC  (0x02014b50)
          105  +#define ENDHEADERMAGIC      (0x06054b50)
          106  +#define ZIP64ENDHEADERMAGIC      (0x6064b50)
          107  +#define ZIP64ENDLOCHEADERMAGIC   (0x7064b50)
          108  +
          109  +#define FLAG_LOCALHEADER_OFFSET (0x06)
          110  +#define CRC_LOCALHEADER_OFFSET  (0x0e)
          111  +
          112  +#define SIZECENTRALHEADER (0x2e) /* 46 */
          113  +
          114  +typedef struct linkedlist_datablock_internal_s
          115  +{
          116  +  struct linkedlist_datablock_internal_s* next_datablock;
          117  +  uLong  avail_in_this_block;
          118  +  uLong  filled_in_this_block;
          119  +  uLong  unused; /* for future use and alignement */
          120  +  unsigned char data[SIZEDATA_INDATABLOCK];
          121  +} linkedlist_datablock_internal;
          122  +
          123  +typedef struct linkedlist_data_s
          124  +{
          125  +    linkedlist_datablock_internal* first_block;
          126  +    linkedlist_datablock_internal* last_block;
          127  +} linkedlist_data;
          128  +
          129  +
          130  +typedef struct
          131  +{
          132  +    z_stream stream;            /* zLib stream structure for inflate */
          133  +#ifdef HAVE_BZIP2
          134  +    bz_stream bstream;          /* bzLib stream structure for bziped */
          135  +#endif
          136  +
          137  +    int  stream_initialised;    /* 1 is stream is initialised */
          138  +    uInt pos_in_buffered_data;  /* last written byte in buffered_data */
          139  +
          140  +    ZPOS64_T pos_local_header;     /* offset of the local header of the file
          141  +                                     currenty writing */
          142  +    char* central_header;       /* central header data for the current file */
          143  +    uLong size_centralExtra;
          144  +    uLong size_centralheader;   /* size of the central header for cur file */
          145  +    uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */
          146  +    uLong flag;                 /* flag of the file currently writing */
          147  +
          148  +    int  method;                /* compression method of file currenty wr.*/
          149  +    int  raw;                   /* 1 for directly writing raw data */
          150  +    Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
          151  +    uLong dosDate;
          152  +    uLong crc32;
          153  +    int  encrypt;
          154  +    int  zip64;               /* Add ZIP64 extened information in the extra field */
          155  +    ZPOS64_T pos_zip64extrainfo;
          156  +    ZPOS64_T totalCompressedData;
          157  +    ZPOS64_T totalUncompressedData;
          158  +#ifndef NOCRYPT
          159  +    unsigned long keys[3];     /* keys defining the pseudo-random sequence */
          160  +    const unsigned long* pcrc_32_tab;
          161  +    int crypt_header_size;
          162  +#endif
          163  +} curfile64_info;
          164  +
          165  +typedef struct
          166  +{
          167  +    zlib_filefunc64_32_def z_filefunc;
          168  +    voidpf filestream;        /* io structore of the zipfile */
          169  +    linkedlist_data central_dir;/* datablock with central dir in construction*/
          170  +    int  in_opened_file_inzip;  /* 1 if a file in the zip is currently writ.*/
          171  +    curfile64_info ci;            /* info on the file curretly writing */
          172  +
          173  +    ZPOS64_T begin_pos;            /* position of the beginning of the zipfile */
          174  +    ZPOS64_T add_position_when_writting_offset;
          175  +    ZPOS64_T number_entry;
          176  +
          177  +#ifndef NO_ADDFILEINEXISTINGZIP
          178  +    char *globalcomment;
          179  +#endif
          180  +
          181  +} zip64_internal;
          182  +
          183  +
          184  +#ifndef NOCRYPT
          185  +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
          186  +#include "crypt.h"
          187  +#endif
          188  +
          189  +local linkedlist_datablock_internal* allocate_new_datablock()
          190  +{
          191  +    linkedlist_datablock_internal* ldi;
          192  +    ldi = (linkedlist_datablock_internal*)
          193  +                 ALLOC(sizeof(linkedlist_datablock_internal));
          194  +    if (ldi!=NULL)
          195  +    {
          196  +        ldi->next_datablock = NULL ;
          197  +        ldi->filled_in_this_block = 0 ;
          198  +        ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
          199  +    }
          200  +    return ldi;
          201  +}
          202  +
          203  +local void free_datablock(linkedlist_datablock_internal* ldi)
          204  +{
          205  +    while (ldi!=NULL)
          206  +    {
          207  +        linkedlist_datablock_internal* ldinext = ldi->next_datablock;
          208  +        TRYFREE(ldi);
          209  +        ldi = ldinext;
          210  +    }
          211  +}
          212  +
          213  +local void init_linkedlist(linkedlist_data* ll)
          214  +{
          215  +    ll->first_block = ll->last_block = NULL;
          216  +}
          217  +
          218  +local void free_linkedlist(linkedlist_data* ll)
          219  +{
          220  +    free_datablock(ll->first_block);
          221  +    ll->first_block = ll->last_block = NULL;
          222  +}
          223  +
          224  +
          225  +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len)
          226  +{
          227  +    linkedlist_datablock_internal* ldi;
          228  +    const unsigned char* from_copy;
          229  +
          230  +    if (ll==NULL)
          231  +        return ZIP_INTERNALERROR;
          232  +
          233  +    if (ll->last_block == NULL)
          234  +    {
          235  +        ll->first_block = ll->last_block = allocate_new_datablock();
          236  +        if (ll->first_block == NULL)
          237  +            return ZIP_INTERNALERROR;
          238  +    }
          239  +
          240  +    ldi = ll->last_block;
          241  +    from_copy = (unsigned char*)buf;
          242  +
          243  +    while (len>0)
          244  +    {
          245  +        uInt copy_this;
          246  +        uInt i;
          247  +        unsigned char* to_copy;
          248  +
          249  +        if (ldi->avail_in_this_block==0)
          250  +        {
          251  +            ldi->next_datablock = allocate_new_datablock();
          252  +            if (ldi->next_datablock == NULL)
          253  +                return ZIP_INTERNALERROR;
          254  +            ldi = ldi->next_datablock ;
          255  +            ll->last_block = ldi;
          256  +        }
          257  +
          258  +        if (ldi->avail_in_this_block < len)
          259  +            copy_this = (uInt)ldi->avail_in_this_block;
          260  +        else
          261  +            copy_this = (uInt)len;
          262  +
          263  +        to_copy = &(ldi->data[ldi->filled_in_this_block]);
          264  +
          265  +        for (i=0;i<copy_this;i++)
          266  +            *(to_copy+i)=*(from_copy+i);
          267  +
          268  +        ldi->filled_in_this_block += copy_this;
          269  +        ldi->avail_in_this_block -= copy_this;
          270  +        from_copy += copy_this ;
          271  +        len -= copy_this;
          272  +    }
          273  +    return ZIP_OK;
          274  +}
          275  +
          276  +
          277  +
          278  +/****************************************************************************/
          279  +
          280  +#ifndef NO_ADDFILEINEXISTINGZIP
          281  +/* ===========================================================================
          282  +   Inputs a long in LSB order to the given file
          283  +   nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T)
          284  +*/
          285  +
          286  +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte));
          287  +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)
          288  +{
          289  +    unsigned char buf[8];
          290  +    int n;
          291  +    for (n = 0; n < nbByte; n++)
          292  +    {
          293  +        buf[n] = (unsigned char)(x & 0xff);
          294  +        x >>= 8;
          295  +    }
          296  +    if (x != 0)
          297  +      {     /* data overflow - hack for ZIP64 (X Roche) */
          298  +      for (n = 0; n < nbByte; n++)
          299  +        {
          300  +          buf[n] = 0xff;
          301  +        }
          302  +      }
          303  +
          304  +    if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte)
          305  +        return ZIP_ERRNO;
          306  +    else
          307  +        return ZIP_OK;
          308  +}
          309  +
          310  +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte));
          311  +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte)
          312  +{
          313  +    unsigned char* buf=(unsigned char*)dest;
          314  +    int n;
          315  +    for (n = 0; n < nbByte; n++) {
          316  +        buf[n] = (unsigned char)(x & 0xff);
          317  +        x >>= 8;
          318  +    }
          319  +
          320  +    if (x != 0)
          321  +    {     /* data overflow - hack for ZIP64 */
          322  +       for (n = 0; n < nbByte; n++)
          323  +       {
          324  +          buf[n] = 0xff;
          325  +       }
          326  +    }
          327  +}
          328  +
          329  +/****************************************************************************/
          330  +
          331  +
          332  +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm)
          333  +{
          334  +    uLong year = (uLong)ptm->tm_year;
          335  +    if (year>=1980)
          336  +        year-=1980;
          337  +    else if (year>=80)
          338  +        year-=80;
          339  +    return
          340  +      (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
          341  +        ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
          342  +}
          343  +
          344  +
          345  +/****************************************************************************/
          346  +
          347  +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi));
          348  +
          349  +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi)
          350  +{
          351  +    unsigned char c;
          352  +    int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1);
          353  +    if (err==1)
          354  +    {
          355  +        *pi = (int)c;
          356  +        return ZIP_OK;
          357  +    }
          358  +    else
          359  +    {
          360  +        if (ZERROR64(*pzlib_filefunc_def,filestream))
          361  +            return ZIP_ERRNO;
          362  +        else
          363  +            return ZIP_EOF;
          364  +    }
          365  +}
          366  +
          367  +
          368  +/* ===========================================================================
          369  +   Reads a long in LSB order from the given gz_stream. Sets
          370  +*/
          371  +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));
          372  +
          373  +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
          374  +{
          375  +    uLong x ;
          376  +    int i = 0;
          377  +    int err;
          378  +
          379  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          380  +    x = (uLong)i;
          381  +
          382  +    if (err==ZIP_OK)
          383  +        err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          384  +    x += ((uLong)i)<<8;
          385  +
          386  +    if (err==ZIP_OK)
          387  +        *pX = x;
          388  +    else
          389  +        *pX = 0;
          390  +    return err;
          391  +}
          392  +
          393  +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));
          394  +
          395  +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
          396  +{
          397  +    uLong x ;
          398  +    int i = 0;
          399  +    int err;
          400  +
          401  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          402  +    x = (uLong)i;
          403  +
          404  +    if (err==ZIP_OK)
          405  +        err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          406  +    x += ((uLong)i)<<8;
          407  +
          408  +    if (err==ZIP_OK)
          409  +        err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          410  +    x += ((uLong)i)<<16;
          411  +
          412  +    if (err==ZIP_OK)
          413  +        err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          414  +    x += ((uLong)i)<<24;
          415  +
          416  +    if (err==ZIP_OK)
          417  +        *pX = x;
          418  +    else
          419  +        *pX = 0;
          420  +    return err;
          421  +}
          422  +
          423  +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX));
          424  +
          425  +
          426  +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)
          427  +{
          428  +  ZPOS64_T x;
          429  +  int i = 0;
          430  +  int err;
          431  +
          432  +  err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          433  +  x = (ZPOS64_T)i;
          434  +
          435  +  if (err==ZIP_OK)
          436  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          437  +  x += ((ZPOS64_T)i)<<8;
          438  +
          439  +  if (err==ZIP_OK)
          440  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          441  +  x += ((ZPOS64_T)i)<<16;
          442  +
          443  +  if (err==ZIP_OK)
          444  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          445  +  x += ((ZPOS64_T)i)<<24;
          446  +
          447  +  if (err==ZIP_OK)
          448  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          449  +  x += ((ZPOS64_T)i)<<32;
          450  +
          451  +  if (err==ZIP_OK)
          452  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          453  +  x += ((ZPOS64_T)i)<<40;
          454  +
          455  +  if (err==ZIP_OK)
          456  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          457  +  x += ((ZPOS64_T)i)<<48;
          458  +
          459  +  if (err==ZIP_OK)
          460  +    err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
          461  +  x += ((ZPOS64_T)i)<<56;
          462  +
          463  +  if (err==ZIP_OK)
          464  +    *pX = x;
          465  +  else
          466  +    *pX = 0;
          467  +
          468  +  return err;
          469  +}
          470  +
          471  +#ifndef BUFREADCOMMENT
          472  +#define BUFREADCOMMENT (0x400)
          473  +#endif
          474  +/*
          475  +  Locate the Central directory of a zipfile (at the end, just before
          476  +    the global comment)
          477  +*/
          478  +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
          479  +
          480  +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
          481  +{
          482  +  unsigned char* buf;
          483  +  ZPOS64_T uSizeFile;
          484  +  ZPOS64_T uBackRead;
          485  +  ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
          486  +  ZPOS64_T uPosFound=0;
          487  +
          488  +  if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
          489  +    return 0;
          490  +
          491  +
          492  +  uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
          493  +
          494  +  if (uMaxBack>uSizeFile)
          495  +    uMaxBack = uSizeFile;
          496  +
          497  +  buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
          498  +  if (buf==NULL)
          499  +    return 0;
          500  +
          501  +  uBackRead = 4;
          502  +  while (uBackRead<uMaxBack)
          503  +  {
          504  +    uLong uReadSize;
          505  +    ZPOS64_T uReadPos ;
          506  +    int i;
          507  +    if (uBackRead+BUFREADCOMMENT>uMaxBack)
          508  +      uBackRead = uMaxBack;
          509  +    else
          510  +      uBackRead+=BUFREADCOMMENT;
          511  +    uReadPos = uSizeFile-uBackRead ;
          512  +
          513  +    uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
          514  +      (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
          515  +    if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          516  +      break;
          517  +
          518  +    if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
          519  +      break;
          520  +
          521  +    for (i=(int)uReadSize-3; (i--)>0;)
          522  +      if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
          523  +        ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
          524  +      {
          525  +        uPosFound = uReadPos+i;
          526  +        break;
          527  +      }
          528  +
          529  +      if (uPosFound!=0)
          530  +        break;
          531  +  }
          532  +  TRYFREE(buf);
          533  +  return uPosFound;
          534  +}
          535  +
          536  +/*
          537  +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before
          538  +the global comment)
          539  +*/
          540  +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
          541  +
          542  +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
          543  +{
          544  +  unsigned char* buf;
          545  +  ZPOS64_T uSizeFile;
          546  +  ZPOS64_T uBackRead;
          547  +  ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
          548  +  ZPOS64_T uPosFound=0;
          549  +  uLong uL;
          550  +  ZPOS64_T relativeOffset;
          551  +
          552  +  if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
          553  +    return 0;
          554  +
          555  +  uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
          556  +
          557  +  if (uMaxBack>uSizeFile)
          558  +    uMaxBack = uSizeFile;
          559  +
          560  +  buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
          561  +  if (buf==NULL)
          562  +    return 0;
          563  +
          564  +  uBackRead = 4;
          565  +  while (uBackRead<uMaxBack)
          566  +  {
          567  +    uLong uReadSize;
          568  +    ZPOS64_T uReadPos;
          569  +    int i;
          570  +    if (uBackRead+BUFREADCOMMENT>uMaxBack)
          571  +      uBackRead = uMaxBack;
          572  +    else
          573  +      uBackRead+=BUFREADCOMMENT;
          574  +    uReadPos = uSizeFile-uBackRead ;
          575  +
          576  +    uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
          577  +      (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
          578  +    if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          579  +      break;
          580  +
          581  +    if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
          582  +      break;
          583  +
          584  +    for (i=(int)uReadSize-3; (i--)>0;)
          585  +    {
          586  +      // Signature "0x07064b50" Zip64 end of central directory locater
          587  +      if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07))
          588  +      {
          589  +        uPosFound = uReadPos+i;
          590  +        break;
          591  +      }
          592  +    }
          593  +
          594  +      if (uPosFound!=0)
          595  +        break;
          596  +  }
          597  +
          598  +  TRYFREE(buf);
          599  +  if (uPosFound == 0)
          600  +    return 0;
          601  +
          602  +  /* Zip64 end of central directory locator */
          603  +  if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
          604  +    return 0;
          605  +
          606  +  /* the signature, already checked */
          607  +  if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
          608  +    return 0;
          609  +
          610  +  /* number of the disk with the start of the zip64 end of  central directory */
          611  +  if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
          612  +    return 0;
          613  +  if (uL != 0)
          614  +    return 0;
          615  +
          616  +  /* relative offset of the zip64 end of central directory record */
          617  +  if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK)
          618  +    return 0;
          619  +
          620  +  /* total number of disks */
          621  +  if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
          622  +    return 0;
          623  +  if (uL != 1)
          624  +    return 0;
          625  +
          626  +  /* Goto Zip64 end of central directory record */
          627  +  if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
          628  +    return 0;
          629  +
          630  +  /* the signature */
          631  +  if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
          632  +    return 0;
          633  +
          634  +  if (uL != 0x06064b50) // signature of 'Zip64 end of central directory'
          635  +    return 0;
          636  +
          637  +  return relativeOffset;
          638  +}
          639  +
          640  +int LoadCentralDirectoryRecord(zip64_internal* pziinit)
          641  +{
          642  +  int err=ZIP_OK;
          643  +  ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
          644  +
          645  +  ZPOS64_T size_central_dir;     /* size of the central directory  */
          646  +  ZPOS64_T offset_central_dir;   /* offset of start of central directory */
          647  +  ZPOS64_T central_pos;
          648  +  uLong uL;
          649  +
          650  +  uLong number_disk;          /* number of the current dist, used for
          651  +                              spaning ZIP, unsupported, always 0*/
          652  +  uLong number_disk_with_CD;  /* number the the disk with central dir, used
          653  +                              for spaning ZIP, unsupported, always 0*/
          654  +  ZPOS64_T number_entry;
          655  +  ZPOS64_T number_entry_CD;      /* total number of entries in
          656  +                                the central dir
          657  +                                (same than number_entry on nospan) */
          658  +  uLong VersionMadeBy;
          659  +  uLong VersionNeeded;
          660  +  uLong size_comment;
          661  +
          662  +  int hasZIP64Record = 0;
          663  +
          664  +  // check first if we find a ZIP64 record
          665  +  central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream);
          666  +  if(central_pos > 0)
          667  +  {
          668  +    hasZIP64Record = 1;
          669  +  }
          670  +  else if(central_pos == 0)
          671  +  {
          672  +    central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream);
          673  +  }
          674  +
          675  +/* disable to allow appending to empty ZIP archive
          676  +        if (central_pos==0)
          677  +            err=ZIP_ERRNO;
          678  +*/
          679  +
          680  +  if(hasZIP64Record)
          681  +  {
          682  +    ZPOS64_T sizeEndOfCentralDirectory;
          683  +    if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
          684  +      err=ZIP_ERRNO;
          685  +
          686  +    /* the signature, already checked */
          687  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK)
          688  +      err=ZIP_ERRNO;
          689  +
          690  +    /* size of zip64 end of central directory record */
          691  +    if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK)
          692  +      err=ZIP_ERRNO;
          693  +
          694  +    /* version made by */
          695  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK)
          696  +      err=ZIP_ERRNO;
          697  +
          698  +    /* version needed to extract */
          699  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK)
          700  +      err=ZIP_ERRNO;
          701  +
          702  +    /* number of this disk */
          703  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK)
          704  +      err=ZIP_ERRNO;
          705  +
          706  +    /* number of the disk with the start of the central directory */
          707  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK)
          708  +      err=ZIP_ERRNO;
          709  +
          710  +    /* total number of entries in the central directory on this disk */
          711  +    if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK)
          712  +      err=ZIP_ERRNO;
          713  +
          714  +    /* total number of entries in the central directory */
          715  +    if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK)
          716  +      err=ZIP_ERRNO;
          717  +
          718  +    if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0))
          719  +      err=ZIP_BADZIPFILE;
          720  +
          721  +    /* size of the central directory */
          722  +    if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK)
          723  +      err=ZIP_ERRNO;
          724  +
          725  +    /* offset of start of central directory with respect to the
          726  +    starting disk number */
          727  +    if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK)
          728  +      err=ZIP_ERRNO;
          729  +
          730  +    // TODO..
          731  +    // read the comment from the standard central header.
          732  +    size_comment = 0;
          733  +  }
          734  +  else
          735  +  {
          736  +    // Read End of central Directory info
          737  +    if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
          738  +      err=ZIP_ERRNO;
          739  +
          740  +    /* the signature, already checked */
          741  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK)
          742  +      err=ZIP_ERRNO;
          743  +
          744  +    /* number of this disk */
          745  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK)
          746  +      err=ZIP_ERRNO;
          747  +
          748  +    /* number of the disk with the start of the central directory */
          749  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK)
          750  +      err=ZIP_ERRNO;
          751  +
          752  +    /* total number of entries in the central dir on this disk */
          753  +    number_entry = 0;
          754  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
          755  +      err=ZIP_ERRNO;
          756  +    else
          757  +      number_entry = uL;
          758  +
          759  +    /* total number of entries in the central dir */
          760  +    number_entry_CD = 0;
          761  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
          762  +      err=ZIP_ERRNO;
          763  +    else
          764  +      number_entry_CD = uL;
          765  +
          766  +    if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0))
          767  +      err=ZIP_BADZIPFILE;
          768  +
          769  +    /* size of the central directory */
          770  +    size_central_dir = 0;
          771  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
          772  +      err=ZIP_ERRNO;
          773  +    else
          774  +      size_central_dir = uL;
          775  +
          776  +    /* offset of start of central directory with respect to the starting disk number */
          777  +    offset_central_dir = 0;
          778  +    if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
          779  +      err=ZIP_ERRNO;
          780  +    else
          781  +      offset_central_dir = uL;
          782  +
          783  +
          784  +    /* zipfile global comment length */
          785  +    if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK)
          786  +      err=ZIP_ERRNO;
          787  +  }
          788  +
          789  +  if ((central_pos<offset_central_dir+size_central_dir) &&
          790  +    (err==ZIP_OK))
          791  +    err=ZIP_BADZIPFILE;
          792  +
          793  +  if (err!=ZIP_OK)
          794  +  {
          795  +    ZCLOSE64(pziinit->z_filefunc, pziinit->filestream);
          796  +    return ZIP_ERRNO;
          797  +  }
          798  +
          799  +  if (size_comment>0)
          800  +  {
          801  +    pziinit->globalcomment = (char*)ALLOC(size_comment+1);
          802  +    if (pziinit->globalcomment)
          803  +    {
          804  +      size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment);
          805  +      pziinit->globalcomment[size_comment]=0;
          806  +    }
          807  +  }
          808  +
          809  +  byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir);
          810  +  pziinit->add_position_when_writting_offset = byte_before_the_zipfile;
          811  +
          812  +  {
          813  +    ZPOS64_T size_central_dir_to_read = size_central_dir;
          814  +    size_t buf_size = SIZEDATA_INDATABLOCK;
          815  +    void* buf_read = (void*)ALLOC(buf_size);
          816  +    if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
          817  +      err=ZIP_ERRNO;
          818  +
          819  +    while ((size_central_dir_to_read>0) && (err==ZIP_OK))
          820  +    {
          821  +      ZPOS64_T read_this = SIZEDATA_INDATABLOCK;
          822  +      if (read_this > size_central_dir_to_read)
          823  +        read_this = size_central_dir_to_read;
          824  +
          825  +      if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this)
          826  +        err=ZIP_ERRNO;
          827  +
          828  +      if (err==ZIP_OK)
          829  +        err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this);
          830  +
          831  +      size_central_dir_to_read-=read_this;
          832  +    }
          833  +    TRYFREE(buf_read);
          834  +  }
          835  +  pziinit->begin_pos = byte_before_the_zipfile;
          836  +  pziinit->number_entry = number_entry_CD;
          837  +
          838  +  if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0)
          839  +    err=ZIP_ERRNO;
          840  +
          841  +  return err;
          842  +}
          843  +
          844  +
          845  +#endif /* !NO_ADDFILEINEXISTINGZIP*/
          846  +
          847  +
          848  +/************************************************************/
          849  +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def)
          850  +{
          851  +    zip64_internal ziinit;
          852  +    zip64_internal* zi;
          853  +    int err=ZIP_OK;
          854  +
          855  +    ziinit.z_filefunc.zseek32_file = NULL;
          856  +    ziinit.z_filefunc.ztell32_file = NULL;
          857  +    if (pzlib_filefunc64_32_def==NULL)
          858  +        fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64);
          859  +    else
          860  +        ziinit.z_filefunc = *pzlib_filefunc64_32_def;
          861  +
          862  +    ziinit.filestream = ZOPEN64(ziinit.z_filefunc,
          863  +                  pathname,
          864  +                  (append == APPEND_STATUS_CREATE) ?
          865  +                  (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) :
          866  +                    (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));
          867  +
          868  +    if (ziinit.filestream == NULL)
          869  +        return NULL;
          870  +
          871  +    if (append == APPEND_STATUS_CREATEAFTER)
          872  +        ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END);
          873  +
          874  +    ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream);
          875  +    ziinit.in_opened_file_inzip = 0;
          876  +    ziinit.ci.stream_initialised = 0;
          877  +    ziinit.number_entry = 0;
          878  +    ziinit.add_position_when_writting_offset = 0;
          879  +    init_linkedlist(&(ziinit.central_dir));
          880  +
          881  +
          882  +
          883  +    zi = (zip64_internal*)ALLOC(sizeof(zip64_internal));
          884  +    if (zi==NULL)
          885  +    {
          886  +        ZCLOSE64(ziinit.z_filefunc,ziinit.filestream);
          887  +        return NULL;
          888  +    }
          889  +
          890  +    /* now we add file in a zipfile */
          891  +#    ifndef NO_ADDFILEINEXISTINGZIP
          892  +    ziinit.globalcomment = NULL;
          893  +    if (append == APPEND_STATUS_ADDINZIP)
          894  +    {
          895  +      // Read and Cache Central Directory Records
          896  +      err = LoadCentralDirectoryRecord(&ziinit);
          897  +    }
          898  +
          899  +    if (globalcomment)
          900  +    {
          901  +      *globalcomment = ziinit.globalcomment;
          902  +    }
          903  +#    endif /* !NO_ADDFILEINEXISTINGZIP*/
          904  +
          905  +    if (err != ZIP_OK)
          906  +    {
          907  +#    ifndef NO_ADDFILEINEXISTINGZIP
          908  +        TRYFREE(ziinit.globalcomment);
          909  +#    endif /* !NO_ADDFILEINEXISTINGZIP*/
          910  +        TRYFREE(zi);
          911  +        return NULL;
          912  +    }
          913  +    else
          914  +    {
          915  +        *zi = ziinit;
          916  +        return (zipFile)zi;
          917  +    }
          918  +}
          919  +
          920  +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def)
          921  +{
          922  +    if (pzlib_filefunc32_def != NULL)
          923  +    {
          924  +        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
          925  +        fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def);
          926  +        return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
          927  +    }
          928  +    else
          929  +        return zipOpen3(pathname, append, globalcomment, NULL);
          930  +}
          931  +
          932  +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)
          933  +{
          934  +    if (pzlib_filefunc_def != NULL)
          935  +    {
          936  +        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
          937  +        zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
          938  +        zlib_filefunc64_32_def_fill.ztell32_file = NULL;
          939  +        zlib_filefunc64_32_def_fill.zseek32_file = NULL;
          940  +        return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
          941  +    }
          942  +    else
          943  +        return zipOpen3(pathname, append, globalcomment, NULL);
          944  +}
          945  +
          946  +
          947  +
          948  +extern zipFile ZEXPORT zipOpen (const char* pathname, int append)
          949  +{
          950  +    return zipOpen3((const void*)pathname,append,NULL,NULL);
          951  +}
          952  +
          953  +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append)
          954  +{
          955  +    return zipOpen3(pathname,append,NULL,NULL);
          956  +}
          957  +
          958  +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local)
          959  +{
          960  +  /* write the local header */
          961  +  int err;
          962  +  uInt size_filename = (uInt)strlen(filename);
          963  +  uInt size_extrafield = size_extrafield_local;
          964  +
          965  +  err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4);
          966  +
          967  +  if (err==ZIP_OK)
          968  +  {
          969  +    if(zi->ci.zip64)
          970  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */
          971  +    else
          972  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */
          973  +  }
          974  +
          975  +  if (err==ZIP_OK)
          976  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2);
          977  +
          978  +  if (err==ZIP_OK)
          979  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2);
          980  +
          981  +  if (err==ZIP_OK)
          982  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4);
          983  +
          984  +  // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later
          985  +  if (err==ZIP_OK)
          986  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */
          987  +  if (err==ZIP_OK)
          988  +  {
          989  +    if(zi->ci.zip64)
          990  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */
          991  +    else
          992  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */
          993  +  }
          994  +  if (err==ZIP_OK)
          995  +  {
          996  +    if(zi->ci.zip64)
          997  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */
          998  +    else
          999  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */
         1000  +  }
         1001  +
         1002  +  if (err==ZIP_OK)
         1003  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2);
         1004  +
         1005  +  if(zi->ci.zip64)
         1006  +  {
         1007  +    size_extrafield += 20;
         1008  +  }
         1009  +
         1010  +  if (err==ZIP_OK)
         1011  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2);
         1012  +
         1013  +  if ((err==ZIP_OK) && (size_filename > 0))
         1014  +  {
         1015  +    if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename)
         1016  +      err = ZIP_ERRNO;
         1017  +  }
         1018  +
         1019  +  if ((err==ZIP_OK) && (size_extrafield_local > 0))
         1020  +  {
         1021  +    if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local)
         1022  +      err = ZIP_ERRNO;
         1023  +  }
         1024  +
         1025  +
         1026  +  if ((err==ZIP_OK) && (zi->ci.zip64))
         1027  +  {
         1028  +      // write the Zip64 extended info
         1029  +      short HeaderID = 1;
         1030  +      short DataSize = 16;
         1031  +      ZPOS64_T CompressedSize = 0;
         1032  +      ZPOS64_T UncompressedSize = 0;
         1033  +
         1034  +      // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file)
         1035  +      zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream);
         1036  +
         1037  +      err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2);
         1038  +      err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2);
         1039  +
         1040  +      err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8);
         1041  +      err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8);
         1042  +  }
         1043  +
         1044  +  return err;
         1045  +}
         1046  +
         1047  +/*
         1048  + NOTE.
         1049  + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped
         1050  + before calling this function it can be done with zipRemoveExtraInfoBlock
         1051  +
         1052  + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize
         1053  + unnecessary allocations.
         1054  + */
         1055  +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1056  +                                         const void* extrafield_local, uInt size_extrafield_local,
         1057  +                                         const void* extrafield_global, uInt size_extrafield_global,
         1058  +                                         const char* comment, int method, int level, int raw,
         1059  +                                         int windowBits,int memLevel, int strategy,
         1060  +                                         const char* password, uLong crcForCrypting,
         1061  +                                         uLong versionMadeBy, uLong flagBase, int zip64)
         1062  +{
         1063  +    zip64_internal* zi;
         1064  +    uInt size_filename;
         1065  +    uInt size_comment;
         1066  +    uInt i;
         1067  +    int err = ZIP_OK;
         1068  +
         1069  +#    ifdef NOCRYPT
         1070  +    (crcForCrypting);
         1071  +    if (password != NULL)
         1072  +        return ZIP_PARAMERROR;
         1073  +#    endif
         1074  +
         1075  +    if (file == NULL)
         1076  +        return ZIP_PARAMERROR;
         1077  +
         1078  +#ifdef HAVE_BZIP2
         1079  +    if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED))
         1080  +      return ZIP_PARAMERROR;
         1081  +#else
         1082  +    if ((method!=0) && (method!=Z_DEFLATED))
         1083  +      return ZIP_PARAMERROR;
         1084  +#endif
         1085  +
         1086  +    zi = (zip64_internal*)file;
         1087  +
         1088  +    if (zi->in_opened_file_inzip == 1)
         1089  +    {
         1090  +        err = zipCloseFileInZip (file);
         1091  +        if (err != ZIP_OK)
         1092  +            return err;
         1093  +    }
         1094  +
         1095  +    if (filename==NULL)
         1096  +        filename="-";
         1097  +
         1098  +    if (comment==NULL)
         1099  +        size_comment = 0;
         1100  +    else
         1101  +        size_comment = (uInt)strlen(comment);
         1102  +
         1103  +    size_filename = (uInt)strlen(filename);
         1104  +
         1105  +    if (zipfi == NULL)
         1106  +        zi->ci.dosDate = 0;
         1107  +    else
         1108  +    {
         1109  +        if (zipfi->dosDate != 0)
         1110  +            zi->ci.dosDate = zipfi->dosDate;
         1111  +        else
         1112  +          zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date);
         1113  +    }
         1114  +
         1115  +    zi->ci.flag = flagBase;
         1116  +    if ((level==8) || (level==9))
         1117  +      zi->ci.flag |= 2;
         1118  +    if (level==2)
         1119  +      zi->ci.flag |= 4;
         1120  +    if (level==1)
         1121  +      zi->ci.flag |= 6;
         1122  +    if (password != NULL)
         1123  +      zi->ci.flag |= 1;
         1124  +
         1125  +    zi->ci.crc32 = 0;
         1126  +    zi->ci.method = method;
         1127  +    zi->ci.encrypt = 0;
         1128  +    zi->ci.stream_initialised = 0;
         1129  +    zi->ci.pos_in_buffered_data = 0;
         1130  +    zi->ci.raw = raw;
         1131  +    zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream);
         1132  +
         1133  +    zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment;
         1134  +    zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data
         1135  +
         1136  +    zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree);
         1137  +
         1138  +    zi->ci.size_centralExtra = size_extrafield_global;
         1139  +    zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
         1140  +    /* version info */
         1141  +    zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2);
         1142  +    zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2);
         1143  +    zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
         1144  +    zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
         1145  +    zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
         1146  +    zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
         1147  +    zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
         1148  +    zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
         1149  +    zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
         1150  +    zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
         1151  +    zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
         1152  +    zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
         1153  +
         1154  +    if (zipfi==NULL)
         1155  +        zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2);
         1156  +    else
         1157  +        zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2);
         1158  +
         1159  +    if (zipfi==NULL)
         1160  +        zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4);
         1161  +    else
         1162  +        zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
         1163  +
         1164  +    if(zi->ci.pos_local_header >= 0xffffffff)
         1165  +      zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4);
         1166  +    else
         1167  +      zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4);
         1168  +
         1169  +    for (i=0;i<size_filename;i++)
         1170  +        *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
         1171  +
         1172  +    for (i=0;i<size_extrafield_global;i++)
         1173  +        *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
         1174  +              *(((const char*)extrafield_global)+i);
         1175  +
         1176  +    for (i=0;i<size_comment;i++)
         1177  +        *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
         1178  +              size_extrafield_global+i) = *(comment+i);
         1179  +    if (zi->ci.central_header == NULL)
         1180  +        return ZIP_INTERNALERROR;
         1181  +
         1182  +    zi->ci.zip64 = zip64;
         1183  +    zi->ci.totalCompressedData = 0;
         1184  +    zi->ci.totalUncompressedData = 0;
         1185  +    zi->ci.pos_zip64extrainfo = 0;
         1186  +
         1187  +    err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local);
         1188  +
         1189  +#ifdef HAVE_BZIP2
         1190  +    zi->ci.bstream.avail_in = (uInt)0;
         1191  +    zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
         1192  +    zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
         1193  +    zi->ci.bstream.total_in_hi32 = 0;
         1194  +    zi->ci.bstream.total_in_lo32 = 0;
         1195  +    zi->ci.bstream.total_out_hi32 = 0;
         1196  +    zi->ci.bstream.total_out_lo32 = 0;
         1197  +#endif
         1198  +
         1199  +    zi->ci.stream.avail_in = (uInt)0;
         1200  +    zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
         1201  +    zi->ci.stream.next_out = zi->ci.buffered_data;
         1202  +    zi->ci.stream.total_in = 0;
         1203  +    zi->ci.stream.total_out = 0;
         1204  +    zi->ci.stream.data_type = Z_BINARY;
         1205  +
         1206  +#ifdef HAVE_BZIP2
         1207  +    if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
         1208  +#else
         1209  +    if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
         1210  +#endif
         1211  +    {
         1212  +        if(zi->ci.method == Z_DEFLATED)
         1213  +        {
         1214  +          zi->ci.stream.zalloc = (alloc_func)0;
         1215  +          zi->ci.stream.zfree = (free_func)0;
         1216  +          zi->ci.stream.opaque = (voidpf)0;
         1217  +
         1218  +          if (windowBits>0)
         1219  +              windowBits = -windowBits;
         1220  +
         1221  +          err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
         1222  +
         1223  +          if (err==Z_OK)
         1224  +              zi->ci.stream_initialised = Z_DEFLATED;
         1225  +        }
         1226  +        else if(zi->ci.method == Z_BZIP2ED)
         1227  +        {
         1228  +#ifdef HAVE_BZIP2
         1229  +            // Init BZip stuff here
         1230  +          zi->ci.bstream.bzalloc = 0;
         1231  +          zi->ci.bstream.bzfree = 0;
         1232  +          zi->ci.bstream.opaque = (voidpf)0;
         1233  +
         1234  +          err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35);
         1235  +          if(err == BZ_OK)
         1236  +            zi->ci.stream_initialised = Z_BZIP2ED;
         1237  +#endif
         1238  +        }
         1239  +
         1240  +    }
         1241  +
         1242  +#    ifndef NOCRYPT
         1243  +    zi->ci.crypt_header_size = 0;
         1244  +    if ((err==Z_OK) && (password != NULL))
         1245  +    {
         1246  +        unsigned char bufHead[RAND_HEAD_LEN];
         1247  +        unsigned int sizeHead;
         1248  +        zi->ci.encrypt = 1;
         1249  +        zi->ci.pcrc_32_tab = get_crc_table();
         1250  +        /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
         1251  +
         1252  +        sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting);
         1253  +        zi->ci.crypt_header_size = sizeHead;
         1254  +
         1255  +        if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead)
         1256  +                err = ZIP_ERRNO;
         1257  +    }
         1258  +#    endif
         1259  +
         1260  +    if (err==Z_OK)
         1261  +        zi->in_opened_file_inzip = 1;
         1262  +    return err;
         1263  +}
         1264  +
         1265  +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1266  +                                         const void* extrafield_local, uInt size_extrafield_local,
         1267  +                                         const void* extrafield_global, uInt size_extrafield_global,
         1268  +                                         const char* comment, int method, int level, int raw,
         1269  +                                         int windowBits,int memLevel, int strategy,
         1270  +                                         const char* password, uLong crcForCrypting,
         1271  +                                         uLong versionMadeBy, uLong flagBase)
         1272  +{
         1273  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1274  +                                 extrafield_local, size_extrafield_local,
         1275  +                                 extrafield_global, size_extrafield_global,
         1276  +                                 comment, method, level, raw,
         1277  +                                 windowBits, memLevel, strategy,
         1278  +                                 password, crcForCrypting, versionMadeBy, flagBase, 0);
         1279  +}
         1280  +
         1281  +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1282  +                                         const void* extrafield_local, uInt size_extrafield_local,
         1283  +                                         const void* extrafield_global, uInt size_extrafield_global,
         1284  +                                         const char* comment, int method, int level, int raw,
         1285  +                                         int windowBits,int memLevel, int strategy,
         1286  +                                         const char* password, uLong crcForCrypting)
         1287  +{
         1288  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1289  +                                 extrafield_local, size_extrafield_local,
         1290  +                                 extrafield_global, size_extrafield_global,
         1291  +                                 comment, method, level, raw,
         1292  +                                 windowBits, memLevel, strategy,
         1293  +                                 password, crcForCrypting, VERSIONMADEBY, 0, 0);
         1294  +}
         1295  +
         1296  +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1297  +                                         const void* extrafield_local, uInt size_extrafield_local,
         1298  +                                         const void* extrafield_global, uInt size_extrafield_global,
         1299  +                                         const char* comment, int method, int level, int raw,
         1300  +                                         int windowBits,int memLevel, int strategy,
         1301  +                                         const char* password, uLong crcForCrypting, int zip64)
         1302  +{
         1303  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1304  +                                 extrafield_local, size_extrafield_local,
         1305  +                                 extrafield_global, size_extrafield_global,
         1306  +                                 comment, method, level, raw,
         1307  +                                 windowBits, memLevel, strategy,
         1308  +                                 password, crcForCrypting, VERSIONMADEBY, 0, zip64);
         1309  +}
         1310  +
         1311  +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1312  +                                        const void* extrafield_local, uInt size_extrafield_local,
         1313  +                                        const void* extrafield_global, uInt size_extrafield_global,
         1314  +                                        const char* comment, int method, int level, int raw)
         1315  +{
         1316  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1317  +                                 extrafield_local, size_extrafield_local,
         1318  +                                 extrafield_global, size_extrafield_global,
         1319  +                                 comment, method, level, raw,
         1320  +                                 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
         1321  +                                 NULL, 0, VERSIONMADEBY, 0, 0);
         1322  +}
         1323  +
         1324  +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1325  +                                        const void* extrafield_local, uInt size_extrafield_local,
         1326  +                                        const void* extrafield_global, uInt size_extrafield_global,
         1327  +                                        const char* comment, int method, int level, int raw, int zip64)
         1328  +{
         1329  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1330  +                                 extrafield_local, size_extrafield_local,
         1331  +                                 extrafield_global, size_extrafield_global,
         1332  +                                 comment, method, level, raw,
         1333  +                                 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
         1334  +                                 NULL, 0, VERSIONMADEBY, 0, zip64);
         1335  +}
         1336  +
         1337  +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1338  +                                        const void* extrafield_local, uInt size_extrafield_local,
         1339  +                                        const void*extrafield_global, uInt size_extrafield_global,
         1340  +                                        const char* comment, int method, int level, int zip64)
         1341  +{
         1342  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1343  +                                 extrafield_local, size_extrafield_local,
         1344  +                                 extrafield_global, size_extrafield_global,
         1345  +                                 comment, method, level, 0,
         1346  +                                 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
         1347  +                                 NULL, 0, VERSIONMADEBY, 0, zip64);
         1348  +}
         1349  +
         1350  +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi,
         1351  +                                        const void* extrafield_local, uInt size_extrafield_local,
         1352  +                                        const void*extrafield_global, uInt size_extrafield_global,
         1353  +                                        const char* comment, int method, int level)
         1354  +{
         1355  +    return zipOpenNewFileInZip4_64 (file, filename, zipfi,
         1356  +                                 extrafield_local, size_extrafield_local,
         1357  +                                 extrafield_global, size_extrafield_global,
         1358  +                                 comment, method, level, 0,
         1359  +                                 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
         1360  +                                 NULL, 0, VERSIONMADEBY, 0, 0);
         1361  +}
         1362  +
         1363  +local int zip64FlushWriteBuffer(zip64_internal* zi)
         1364  +{
         1365  +    int err=ZIP_OK;
         1366  +
         1367  +    if (zi->ci.encrypt != 0)
         1368  +    {
         1369  +#ifndef NOCRYPT
         1370  +        uInt i;
         1371  +        int t;
         1372  +        for (i=0;i<zi->ci.pos_in_buffered_data;i++)
         1373  +            zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t);
         1374  +#endif
         1375  +    }
         1376  +
         1377  +    if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data)
         1378  +      err = ZIP_ERRNO;
         1379  +
         1380  +    zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data;
         1381  +
         1382  +#ifdef HAVE_BZIP2
         1383  +    if(zi->ci.method == Z_BZIP2ED)
         1384  +    {
         1385  +      zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32;
         1386  +      zi->ci.bstream.total_in_lo32 = 0;
         1387  +      zi->ci.bstream.total_in_hi32 = 0;
         1388  +    }
         1389  +    else
         1390  +#endif
         1391  +    {
         1392  +      zi->ci.totalUncompressedData += zi->ci.stream.total_in;
         1393  +      zi->ci.stream.total_in = 0;
         1394  +    }
         1395  +
         1396  +
         1397  +    zi->ci.pos_in_buffered_data = 0;
         1398  +
         1399  +    return err;
         1400  +}
         1401  +
         1402  +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len)
         1403  +{
         1404  +    zip64_internal* zi;
         1405  +    int err=ZIP_OK;
         1406  +
         1407  +    if (file == NULL)
         1408  +        return ZIP_PARAMERROR;
         1409  +    zi = (zip64_internal*)file;
         1410  +
         1411  +    if (zi->in_opened_file_inzip == 0)
         1412  +        return ZIP_PARAMERROR;
         1413  +
         1414  +    zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len);
         1415  +
         1416  +#ifdef HAVE_BZIP2
         1417  +    if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw))
         1418  +    {
         1419  +      zi->ci.bstream.next_in = (void*)buf;
         1420  +      zi->ci.bstream.avail_in = len;
         1421  +      err = BZ_RUN_OK;
         1422  +
         1423  +      while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0))
         1424  +      {
         1425  +        if (zi->ci.bstream.avail_out == 0)
         1426  +        {
         1427  +          if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
         1428  +            err = ZIP_ERRNO;
         1429  +          zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
         1430  +          zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
         1431  +        }
         1432  +
         1433  +
         1434  +        if(err != BZ_RUN_OK)
         1435  +          break;
         1436  +
         1437  +        if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
         1438  +        {
         1439  +          uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32;
         1440  +//          uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32;
         1441  +          err=BZ2_bzCompress(&zi->ci.bstream,  BZ_RUN);
         1442  +
         1443  +          zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ;
         1444  +        }
         1445  +      }
         1446  +
         1447  +      if(err == BZ_RUN_OK)
         1448  +        err = ZIP_OK;
         1449  +    }
         1450  +    else
         1451  +#endif
         1452  +    {
         1453  +      zi->ci.stream.next_in = (Bytef*)buf;
         1454  +      zi->ci.stream.avail_in = len;
         1455  +
         1456  +      while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
         1457  +      {
         1458  +          if (zi->ci.stream.avail_out == 0)
         1459  +          {
         1460  +              if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
         1461  +                  err = ZIP_ERRNO;
         1462  +              zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
         1463  +              zi->ci.stream.next_out = zi->ci.buffered_data;
         1464  +          }
         1465  +
         1466  +
         1467  +          if(err != ZIP_OK)
         1468  +              break;
         1469  +
         1470  +          if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
         1471  +          {
         1472  +              uLong uTotalOutBefore = zi->ci.stream.total_out;
         1473  +              err=deflate(&zi->ci.stream,  Z_NO_FLUSH);
         1474  +              if(uTotalOutBefore > zi->ci.stream.total_out)
         1475  +              {
         1476  +                int bBreak = 0;
         1477  +                bBreak++;
         1478  +              }
         1479  +
         1480  +              zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
         1481  +          }
         1482  +          else
         1483  +          {
         1484  +              uInt copy_this,i;
         1485  +              if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
         1486  +                  copy_this = zi->ci.stream.avail_in;
         1487  +              else
         1488  +                  copy_this = zi->ci.stream.avail_out;
         1489  +
         1490  +              for (i = 0; i < copy_this; i++)
         1491  +                  *(((char*)zi->ci.stream.next_out)+i) =
         1492  +                      *(((const char*)zi->ci.stream.next_in)+i);
         1493  +              {
         1494  +                  zi->ci.stream.avail_in -= copy_this;
         1495  +                  zi->ci.stream.avail_out-= copy_this;
         1496  +                  zi->ci.stream.next_in+= copy_this;
         1497  +                  zi->ci.stream.next_out+= copy_this;
         1498  +                  zi->ci.stream.total_in+= copy_this;
         1499  +                  zi->ci.stream.total_out+= copy_this;
         1500  +                  zi->ci.pos_in_buffered_data += copy_this;
         1501  +              }
         1502  +          }
         1503  +      }// while(...)
         1504  +    }
         1505  +
         1506  +    return err;
         1507  +}
         1508  +
         1509  +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32)
         1510  +{
         1511  +    return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32);
         1512  +}
         1513  +
         1514  +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32)
         1515  +{
         1516  +    zip64_internal* zi;
         1517  +    ZPOS64_T compressed_size;
         1518  +    uLong invalidValue = 0xffffffff;
         1519  +    short datasize = 0;
         1520  +    int err=ZIP_OK;
         1521  +
         1522  +    if (file == NULL)
         1523  +        return ZIP_PARAMERROR;
         1524  +    zi = (zip64_internal*)file;
         1525  +
         1526  +    if (zi->in_opened_file_inzip == 0)
         1527  +        return ZIP_PARAMERROR;
         1528  +    zi->ci.stream.avail_in = 0;
         1529  +
         1530  +    if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
         1531  +                {
         1532  +                        while (err==ZIP_OK)
         1533  +                        {
         1534  +                                uLong uTotalOutBefore;
         1535  +                                if (zi->ci.stream.avail_out == 0)
         1536  +                                {
         1537  +                                        if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
         1538  +                                                err = ZIP_ERRNO;
         1539  +                                        zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
         1540  +                                        zi->ci.stream.next_out = zi->ci.buffered_data;
         1541  +                                }
         1542  +                                uTotalOutBefore = zi->ci.stream.total_out;
         1543  +                                err=deflate(&zi->ci.stream,  Z_FINISH);
         1544  +                                zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
         1545  +                        }
         1546  +                }
         1547  +    else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
         1548  +    {
         1549  +#ifdef HAVE_BZIP2
         1550  +      err = BZ_FINISH_OK;
         1551  +      while (err==BZ_FINISH_OK)
         1552  +      {
         1553  +        uLong uTotalOutBefore;
         1554  +        if (zi->ci.bstream.avail_out == 0)
         1555  +        {
         1556  +          if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
         1557  +            err = ZIP_ERRNO;
         1558  +          zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
         1559  +          zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
         1560  +        }
         1561  +        uTotalOutBefore = zi->ci.bstream.total_out_lo32;
         1562  +        err=BZ2_bzCompress(&zi->ci.bstream,  BZ_FINISH);
         1563  +        if(err == BZ_STREAM_END)
         1564  +          err = Z_STREAM_END;
         1565  +
         1566  +        zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore);
         1567  +      }
         1568  +
         1569  +      if(err == BZ_FINISH_OK)
         1570  +        err = ZIP_OK;
         1571  +#endif
         1572  +    }
         1573  +
         1574  +    if (err==Z_STREAM_END)
         1575  +        err=ZIP_OK; /* this is normal */
         1576  +
         1577  +    if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
         1578  +                {
         1579  +        if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO)
         1580  +            err = ZIP_ERRNO;
         1581  +                }
         1582  +
         1583  +    if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
         1584  +    {
         1585  +        int tmp_err = deflateEnd(&zi->ci.stream);
         1586  +        if (err == ZIP_OK)
         1587  +            err = tmp_err;
         1588  +        zi->ci.stream_initialised = 0;
         1589  +    }
         1590  +#ifdef HAVE_BZIP2
         1591  +    else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
         1592  +    {
         1593  +      int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream);
         1594  +                        if (err==ZIP_OK)
         1595  +                                err = tmperr;
         1596  +                        zi->ci.stream_initialised = 0;
         1597  +    }
         1598  +#endif
         1599  +
         1600  +    if (!zi->ci.raw)
         1601  +    {
         1602  +        crc32 = (uLong)zi->ci.crc32;
         1603  +        uncompressed_size = zi->ci.totalUncompressedData;
         1604  +    }
         1605  +    compressed_size = zi->ci.totalCompressedData;
         1606  +
         1607  +#    ifndef NOCRYPT
         1608  +    compressed_size += zi->ci.crypt_header_size;
         1609  +#    endif
         1610  +
         1611  +    // update Current Item crc and sizes,
         1612  +    if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff)
         1613  +    {
         1614  +      /*version Made by*/
         1615  +      zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2);
         1616  +      /*version needed*/
         1617  +      zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2);
         1618  +
         1619  +    }
         1620  +
         1621  +    zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/
         1622  +
         1623  +
         1624  +    if(compressed_size >= 0xffffffff)
         1625  +      zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/
         1626  +    else
         1627  +      zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/
         1628  +
         1629  +    /// set internal file attributes field
         1630  +    if (zi->ci.stream.data_type == Z_ASCII)
         1631  +        zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);
         1632  +
         1633  +    if(uncompressed_size >= 0xffffffff)
         1634  +      zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/
         1635  +    else
         1636  +      zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/
         1637  +
         1638  +    // Add ZIP64 extra info field for uncompressed size
         1639  +    if(uncompressed_size >= 0xffffffff)
         1640  +      datasize += 8;
         1641  +
         1642  +    // Add ZIP64 extra info field for compressed size
         1643  +    if(compressed_size >= 0xffffffff)
         1644  +      datasize += 8;
         1645  +
         1646  +    // Add ZIP64 extra info field for relative offset to local file header of current file
         1647  +    if(zi->ci.pos_local_header >= 0xffffffff)
         1648  +      datasize += 8;
         1649  +
         1650  +    if(datasize > 0)
         1651  +    {
         1652  +      char* p = NULL;
         1653  +
         1654  +      if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
         1655  +      {
         1656  +        // we can not write more data to the buffer that we have room for.
         1657  +        return ZIP_BADZIPFILE;
         1658  +      }
         1659  +
         1660  +      p = zi->ci.central_header + zi->ci.size_centralheader;
         1661  +
         1662  +      // Add Extra Information Header for 'ZIP64 information'
         1663  +      zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID
         1664  +      p += 2;
         1665  +      zip64local_putValue_inmemory(p, datasize, 2); // DataSize
         1666  +      p += 2;
         1667  +
         1668  +      if(uncompressed_size >= 0xffffffff)
         1669  +      {
         1670  +        zip64local_putValue_inmemory(p, uncompressed_size, 8);
         1671  +        p += 8;
         1672  +      }
         1673  +
         1674  +      if(compressed_size >= 0xffffffff)
         1675  +      {
         1676  +        zip64local_putValue_inmemory(p, compressed_size, 8);
         1677  +        p += 8;
         1678  +      }
         1679  +
         1680  +      if(zi->ci.pos_local_header >= 0xffffffff)
         1681  +      {
         1682  +        zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8);
         1683  +        p += 8;
         1684  +      }
         1685  +
         1686  +      // Update how much extra free space we got in the memory buffer
         1687  +      // and increase the centralheader size so the new ZIP64 fields are included
         1688  +      // ( 4 below is the size of HeaderID and DataSize field )
         1689  +      zi->ci.size_centralExtraFree -= datasize + 4;
         1690  +      zi->ci.size_centralheader += datasize + 4;
         1691  +
         1692  +      // Update the extra info size field
         1693  +      zi->ci.size_centralExtra += datasize + 4;
         1694  +      zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2);
         1695  +    }
         1696  +
         1697  +    if (err==ZIP_OK)
         1698  +        err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader);
         1699  +
         1700  +    free(zi->ci.central_header);
         1701  +
         1702  +    if (err==ZIP_OK)
         1703  +    {
         1704  +        // Update the LocalFileHeader with the new values.
         1705  +
         1706  +        ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream);
         1707  +
         1708  +        if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0)
         1709  +            err = ZIP_ERRNO;
         1710  +
         1711  +        if (err==ZIP_OK)
         1712  +            err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
         1713  +
         1714  +        if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff )
         1715  +        {
         1716  +          if(zi->ci.pos_zip64extrainfo > 0)
         1717  +          {
         1718  +            // Update the size in the ZIP64 extended field.
         1719  +            if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0)
         1720  +              err = ZIP_ERRNO;
         1721  +
         1722  +            if (err==ZIP_OK) /* compressed size, unknown */
         1723  +              err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8);
         1724  +
         1725  +            if (err==ZIP_OK) /* uncompressed size, unknown */
         1726  +              err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8);
         1727  +          }
         1728  +          else
         1729  +              err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal
         1730  +        }
         1731  +        else
         1732  +        {
         1733  +          if (err==ZIP_OK) /* compressed size, unknown */
         1734  +              err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
         1735  +
         1736  +          if (err==ZIP_OK) /* uncompressed size, unknown */
         1737  +              err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
         1738  +        }
         1739  +
         1740  +        if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0)
         1741  +            err = ZIP_ERRNO;
         1742  +    }
         1743  +
         1744  +    zi->number_entry ++;
         1745  +    zi->in_opened_file_inzip = 0;
         1746  +
         1747  +    return err;
         1748  +}
         1749  +
         1750  +extern int ZEXPORT zipCloseFileInZip (zipFile file)
         1751  +{
         1752  +    return zipCloseFileInZipRaw (file,0,0);
         1753  +}
         1754  +
         1755  +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip)
         1756  +{
         1757  +  int err = ZIP_OK;
         1758  +  ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset;
         1759  +
         1760  +  err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4);
         1761  +
         1762  +  /*num disks*/
         1763  +    if (err==ZIP_OK) /* number of the disk with the start of the central directory */
         1764  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
         1765  +
         1766  +  /*relative offset*/
         1767  +    if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */
         1768  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8);
         1769  +
         1770  +  /*total disks*/ /* Do not support spawning of disk so always say 1 here*/
         1771  +    if (err==ZIP_OK) /* number of the disk with the start of the central directory */
         1772  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4);
         1773  +
         1774  +    return err;
         1775  +}
         1776  +
         1777  +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
         1778  +{
         1779  +  int err = ZIP_OK;
         1780  +
         1781  +  uLong Zip64DataSize = 44;
         1782  +
         1783  +  err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4);
         1784  +
         1785  +  if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */
         1786  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ?
         1787  +
         1788  +  if (err==ZIP_OK) /* version made by */
         1789  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);
         1790  +
         1791  +  if (err==ZIP_OK) /* version needed */
         1792  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);
         1793  +
         1794  +  if (err==ZIP_OK) /* number of this disk */
         1795  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
         1796  +
         1797  +  if (err==ZIP_OK) /* number of the disk with the start of the central directory */
         1798  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
         1799  +
         1800  +  if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
         1801  +    err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
         1802  +
         1803  +  if (err==ZIP_OK) /* total number of entries in the central dir */
         1804  +    err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
         1805  +
         1806  +  if (err==ZIP_OK) /* size of the central directory */
         1807  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8);
         1808  +
         1809  +  if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
         1810  +  {
         1811  +    ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
         1812  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8);
         1813  +  }
         1814  +  return err;
         1815  +}
         1816  +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
         1817  +{
         1818  +  int err = ZIP_OK;
         1819  +
         1820  +  /*signature*/
         1821  +  err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);
         1822  +
         1823  +  if (err==ZIP_OK) /* number of this disk */
         1824  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
         1825  +
         1826  +  if (err==ZIP_OK) /* number of the disk with the start of the central directory */
         1827  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
         1828  +
         1829  +  if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
         1830  +  {
         1831  +    {
         1832  +      if(zi->number_entry >= 0xFFFF)
         1833  +        err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record
         1834  +      else
         1835  +        err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
         1836  +    }
         1837  +  }
         1838  +
         1839  +  if (err==ZIP_OK) /* total number of entries in the central dir */
         1840  +  {
         1841  +    if(zi->number_entry >= 0xFFFF)
         1842  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record
         1843  +    else
         1844  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
         1845  +  }
         1846  +
         1847  +  if (err==ZIP_OK) /* size of the central directory */
         1848  +    err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4);
         1849  +
         1850  +  if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
         1851  +  {
         1852  +    ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
         1853  +    if(pos >= 0xffffffff)
         1854  +    {
         1855  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4);
         1856  +    }
         1857  +    else
         1858  +      err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
         1859  +  }
         1860  +
         1861  +   return err;
         1862  +}
         1863  +
         1864  +int Write_GlobalComment(zip64_internal* zi, const char* global_comment)
         1865  +{
         1866  +  int err = ZIP_OK;
         1867  +  uInt size_global_comment = 0;
         1868  +
         1869  +  if(global_comment != NULL)
         1870  +    size_global_comment = (uInt)strlen(global_comment);
         1871  +
         1872  +  err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2);
         1873  +
         1874  +  if (err == ZIP_OK && size_global_comment > 0)
         1875  +  {
         1876  +    if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment)
         1877  +      err = ZIP_ERRNO;
         1878  +  }
         1879  +  return err;
         1880  +}
         1881  +
         1882  +extern int ZEXPORT zipClose (zipFile file, const char* global_comment)
         1883  +{
         1884  +    zip64_internal* zi;
         1885  +    int err = 0;
         1886  +    uLong size_centraldir = 0;
         1887  +    ZPOS64_T centraldir_pos_inzip;
         1888  +    ZPOS64_T pos;
         1889  +
         1890  +    if (file == NULL)
         1891  +        return ZIP_PARAMERROR;
         1892  +
         1893  +    zi = (zip64_internal*)file;
         1894  +
         1895  +    if (zi->in_opened_file_inzip == 1)
         1896  +    {
         1897  +        err = zipCloseFileInZip (file);
         1898  +    }
         1899  +
         1900  +#ifndef NO_ADDFILEINEXISTINGZIP
         1901  +    if (global_comment==NULL)
         1902  +        global_comment = zi->globalcomment;
         1903  +#endif
         1904  +
         1905  +    centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream);
         1906  +
         1907  +    if (err==ZIP_OK)
         1908  +    {
         1909  +        linkedlist_datablock_internal* ldi = zi->central_dir.first_block;
         1910  +        while (ldi!=NULL)
         1911  +        {
         1912  +            if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
         1913  +            {
         1914  +                if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block)
         1915  +                    err = ZIP_ERRNO;
         1916  +            }
         1917  +
         1918  +            size_centraldir += ldi->filled_in_this_block;
         1919  +            ldi = ldi->next_datablock;
         1920  +        }
         1921  +    }
         1922  +    free_linkedlist(&(zi->central_dir));
         1923  +
         1924  +    pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
         1925  +    if(pos >= 0xffffffff || zi->number_entry > 0xFFFF)
         1926  +    {
         1927  +      ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream);
         1928  +      Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
         1929  +
         1930  +      Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos);
         1931  +    }
         1932  +
         1933  +    if (err==ZIP_OK)
         1934  +      err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
         1935  +
         1936  +    if(err == ZIP_OK)
         1937  +      err = Write_GlobalComment(zi, global_comment);
         1938  +
         1939  +    if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0)
         1940  +        if (err == ZIP_OK)
         1941  +            err = ZIP_ERRNO;
         1942  +
         1943  +#ifndef NO_ADDFILEINEXISTINGZIP
         1944  +    TRYFREE(zi->globalcomment);
         1945  +#endif
         1946  +    TRYFREE(zi);
         1947  +
         1948  +    return err;
         1949  +}
         1950  +
         1951  +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader)
         1952  +{
         1953  +  char* p = pData;
         1954  +  int size = 0;
         1955  +  char* pNewHeader;
         1956  +  char* pTmp;
         1957  +  short header;
         1958  +  short dataSize;
         1959  +
         1960  +  int retVal = ZIP_OK;
         1961  +
         1962  +  if(pData == NULL || *dataLen < 4)
         1963  +    return ZIP_PARAMERROR;
         1964  +
         1965  +  pNewHeader = (char*)ALLOC(*dataLen);
         1966  +  pTmp = pNewHeader;
         1967  +
         1968  +  while(p < (pData + *dataLen))
         1969  +  {
         1970  +    header = *(short*)p;
         1971  +    dataSize = *(((short*)p)+1);
         1972  +
         1973  +    if( header == sHeader ) // Header found.
         1974  +    {
         1975  +      p += dataSize + 4; // skip it. do not copy to temp buffer
         1976  +    }
         1977  +    else
         1978  +    {
         1979  +      // Extra Info block should not be removed, So copy it to the temp buffer.
         1980  +      memcpy(pTmp, p, dataSize + 4);
         1981  +      p += dataSize + 4;
         1982  +      size += dataSize + 4;
         1983  +    }
         1984  +
         1985  +  }
         1986  +
         1987  +  if(size < *dataLen)
         1988  +  {
         1989  +    // clean old extra info block.
         1990  +    memset(pData,0, *dataLen);
         1991  +
         1992  +    // copy the new extra info block over the old
         1993  +    if(size > 0)
         1994  +      memcpy(pData, pNewHeader, size);
         1995  +
         1996  +    // set the new extra info size
         1997  +    *dataLen = size;
         1998  +
         1999  +    retVal = ZIP_OK;
         2000  +  }
         2001  +  else
         2002  +    retVal = ZIP_ERRNO;
         2003  +
         2004  +  TRYFREE(pNewHeader);
         2005  +
         2006  +  return retVal;
         2007  +}

Added compat/zlib/contrib/minizip/zip.h.

            1  +/* zip.h -- IO on .zip files using zlib
            2  +   Version 1.1, February 14h, 2010
            3  +   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
            4  +
            5  +         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
            6  +
            7  +         Modifications for Zip64 support
            8  +         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
            9  +
           10  +         For more info read MiniZip_info.txt
           11  +
           12  +         ---------------------------------------------------------------------------
           13  +
           14  +   Condition of use and distribution are the same than zlib :
           15  +
           16  +  This software is provided 'as-is', without any express or implied
           17  +  warranty.  In no event will the authors be held liable for any damages
           18  +  arising from the use of this software.
           19  +
           20  +  Permission is granted to anyone to use this software for any purpose,
           21  +  including commercial applications, and to alter it and redistribute it
           22  +  freely, subject to the following restrictions:
           23  +
           24  +  1. The origin of this software must not be misrepresented; you must not
           25  +     claim that you wrote the original software. If you use this software
           26  +     in a product, an acknowledgment in the product documentation would be
           27  +     appreciated but is not required.
           28  +  2. Altered source versions must be plainly marked as such, and must not be
           29  +     misrepresented as being the original software.
           30  +  3. This notice may not be removed or altered from any source distribution.
           31  +
           32  +        ---------------------------------------------------------------------------
           33  +
           34  +        Changes
           35  +
           36  +        See header of zip.h
           37  +
           38  +*/
           39  +
           40  +#ifndef _zip12_H
           41  +#define _zip12_H
           42  +
           43  +#ifdef __cplusplus
           44  +extern "C" {
           45  +#endif
           46  +
           47  +//#define HAVE_BZIP2
           48  +
           49  +#ifndef _ZLIB_H
           50  +#include "zlib.h"
           51  +#endif
           52  +
           53  +#ifndef _ZLIBIOAPI_H
           54  +#include "ioapi.h"
           55  +#endif
           56  +
           57  +#ifdef HAVE_BZIP2
           58  +#include "bzlib.h"
           59  +#endif
           60  +
           61  +#define Z_BZIP2ED 12
           62  +
           63  +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
           64  +/* like the STRICT of WIN32, we define a pointer that cannot be converted
           65  +    from (void*) without cast */
           66  +typedef struct TagzipFile__ { int unused; } zipFile__;
           67  +typedef zipFile__ *zipFile;
           68  +#else
           69  +typedef voidp zipFile;
           70  +#endif
           71  +
           72  +#define ZIP_OK                          (0)
           73  +#define ZIP_EOF                         (0)
           74  +#define ZIP_ERRNO                       (Z_ERRNO)
           75  +#define ZIP_PARAMERROR                  (-102)
           76  +#define ZIP_BADZIPFILE                  (-103)
           77  +#define ZIP_INTERNALERROR               (-104)
           78  +
           79  +#ifndef DEF_MEM_LEVEL
           80  +#  if MAX_MEM_LEVEL >= 8
           81  +#    define DEF_MEM_LEVEL 8
           82  +#  else
           83  +#    define DEF_MEM_LEVEL  MAX_MEM_LEVEL
           84  +#  endif
           85  +#endif
           86  +/* default memLevel */
           87  +
           88  +/* tm_zip contain date/time info */
           89  +typedef struct tm_zip_s
           90  +{
           91  +    uInt tm_sec;            /* seconds after the minute - [0,59] */
           92  +    uInt tm_min;            /* minutes after the hour - [0,59] */
           93  +    uInt tm_hour;           /* hours since midnight - [0,23] */
           94  +    uInt tm_mday;           /* day of the month - [1,31] */
           95  +    uInt tm_mon;            /* months since January - [0,11] */
           96  +    uInt tm_year;           /* years - [1980..2044] */
           97  +} tm_zip;
           98  +
           99  +typedef struct
          100  +{
          101  +    tm_zip      tmz_date;       /* date in understandable format           */
          102  +    uLong       dosDate;       /* if dos_date == 0, tmu_date is used      */
          103  +/*    uLong       flag;        */   /* general purpose bit flag        2 bytes */
          104  +
          105  +    uLong       internal_fa;    /* internal file attributes        2 bytes */
          106  +    uLong       external_fa;    /* external file attributes        4 bytes */
          107  +} zip_fileinfo;
          108  +
          109  +typedef const char* zipcharpc;
          110  +
          111  +
          112  +#define APPEND_STATUS_CREATE        (0)
          113  +#define APPEND_STATUS_CREATEAFTER   (1)
          114  +#define APPEND_STATUS_ADDINZIP      (2)
          115  +
          116  +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append));
          117  +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append));
          118  +/*
          119  +  Create a zipfile.
          120  +     pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
          121  +       an Unix computer "zlib/zlib113.zip".
          122  +     if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
          123  +       will be created at the end of the file.
          124  +         (useful if the file contain a self extractor code)
          125  +     if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
          126  +       add files in existing zip (be sure you don't add file that doesn't exist)
          127  +     If the zipfile cannot be opened, the return value is NULL.
          128  +     Else, the return value is a zipFile Handle, usable with other function
          129  +       of this zip package.
          130  +*/
          131  +
          132  +/* Note : there is no delete function into a zipfile.
          133  +   If you want delete file into a zipfile, you must open a zipfile, and create another
          134  +   Of couse, you can use RAW reading and writing to copy the file you did not want delte
          135  +*/
          136  +
          137  +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname,
          138  +                                   int append,
          139  +                                   zipcharpc* globalcomment,
          140  +                                   zlib_filefunc_def* pzlib_filefunc_def));
          141  +
          142  +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname,
          143  +                                   int append,
          144  +                                   zipcharpc* globalcomment,
          145  +                                   zlib_filefunc64_def* pzlib_filefunc_def));
          146  +
          147  +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
          148  +                       const char* filename,
          149  +                       const zip_fileinfo* zipfi,
          150  +                       const void* extrafield_local,
          151  +                       uInt size_extrafield_local,
          152  +                       const void* extrafield_global,
          153  +                       uInt size_extrafield_global,
          154  +                       const char* comment,
          155  +                       int method,
          156  +                       int level));
          157  +
          158  +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file,
          159  +                       const char* filename,
          160  +                       const zip_fileinfo* zipfi,
          161  +                       const void* extrafield_local,
          162  +                       uInt size_extrafield_local,
          163  +                       const void* extrafield_global,
          164  +                       uInt size_extrafield_global,
          165  +                       const char* comment,
          166  +                       int method,
          167  +                       int level,
          168  +                       int zip64));
          169  +
          170  +/*
          171  +  Open a file in the ZIP for writing.
          172  +  filename : the filename in zip (if NULL, '-' without quote will be used
          173  +  *zipfi contain supplemental information
          174  +  if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
          175  +    contains the extrafield data the the local header
          176  +  if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
          177  +    contains the extrafield data the the local header
          178  +  if comment != NULL, comment contain the comment string
          179  +  method contain the compression method (0 for store, Z_DEFLATED for deflate)
          180  +  level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
          181  +  zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
          182  +                    this MUST be '1' if the uncompressed size is >= 0xffffffff.
          183  +
          184  +*/
          185  +
          186  +
          187  +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file,
          188  +                                            const char* filename,
          189  +                                            const zip_fileinfo* zipfi,
          190  +                                            const void* extrafield_local,
          191  +                                            uInt size_extrafield_local,
          192  +                                            const void* extrafield_global,
          193  +                                            uInt size_extrafield_global,
          194  +                                            const char* comment,
          195  +                                            int method,
          196  +                                            int level,
          197  +                                            int raw));
          198  +
          199  +
          200  +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file,
          201  +                                            const char* filename,
          202  +                                            const zip_fileinfo* zipfi,
          203  +                                            const void* extrafield_local,
          204  +                                            uInt size_extrafield_local,
          205  +                                            const void* extrafield_global,
          206  +                                            uInt size_extrafield_global,
          207  +                                            const char* comment,
          208  +                                            int method,
          209  +                                            int level,
          210  +                                            int raw,
          211  +                                            int zip64));
          212  +/*
          213  +  Same than zipOpenNewFileInZip, except if raw=1, we write raw file
          214  + */
          215  +
          216  +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
          217  +                                            const char* filename,
          218  +                                            const zip_fileinfo* zipfi,
          219  +                                            const void* extrafield_local,
          220  +                                            uInt size_extrafield_local,
          221  +                                            const void* extrafield_global,
          222  +                                            uInt size_extrafield_global,
          223  +                                            const char* comment,
          224  +                                            int method,
          225  +                                            int level,
          226  +                                            int raw,
          227  +                                            int windowBits,
          228  +                                            int memLevel,
          229  +                                            int strategy,
          230  +                                            const char* password,
          231  +                                            uLong crcForCrypting));
          232  +
          233  +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file,
          234  +                                            const char* filename,
          235  +                                            const zip_fileinfo* zipfi,
          236  +                                            const void* extrafield_local,
          237  +                                            uInt size_extrafield_local,
          238  +                                            const void* extrafield_global,
          239  +                                            uInt size_extrafield_global,
          240  +                                            const char* comment,
          241  +                                            int method,
          242  +                                            int level,
          243  +                                            int raw,
          244  +                                            int windowBits,
          245  +                                            int memLevel,
          246  +                                            int strategy,
          247  +                                            const char* password,
          248  +                                            uLong crcForCrypting,
          249  +                                            int zip64
          250  +                                            ));
          251  +
          252  +/*
          253  +  Same than zipOpenNewFileInZip2, except
          254  +    windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
          255  +    password : crypting password (NULL for no crypting)
          256  +    crcForCrypting : crc of file to compress (needed for crypting)
          257  + */
          258  +
          259  +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file,
          260  +                                            const char* filename,
          261  +                                            const zip_fileinfo* zipfi,
          262  +                                            const void* extrafield_local,
          263  +                                            uInt size_extrafield_local,
          264  +                                            const void* extrafield_global,
          265  +                                            uInt size_extrafield_global,
          266  +                                            const char* comment,
          267  +                                            int method,
          268  +                                            int level,
          269  +                                            int raw,
          270  +                                            int windowBits,
          271  +                                            int memLevel,
          272  +                                            int strategy,
          273  +                                            const char* password,
          274  +                                            uLong crcForCrypting,
          275  +                                            uLong versionMadeBy,
          276  +                                            uLong flagBase
          277  +                                            ));
          278  +
          279  +
          280  +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file,
          281  +                                            const char* filename,
          282  +                                            const zip_fileinfo* zipfi,
          283  +                                            const void* extrafield_local,
          284  +                                            uInt size_extrafield_local,
          285  +                                            const void* extrafield_global,
          286  +                                            uInt size_extrafield_global,
          287  +                                            const char* comment,
          288  +                                            int method,
          289  +                                            int level,
          290  +                                            int raw,
          291  +                                            int windowBits,
          292  +                                            int memLevel,
          293  +                                            int strategy,
          294  +                                            const char* password,
          295  +                                            uLong crcForCrypting,
          296  +                                            uLong versionMadeBy,
          297  +                                            uLong flagBase,
          298  +                                            int zip64
          299  +                                            ));
          300  +/*
          301  +  Same than zipOpenNewFileInZip4, except
          302  +    versionMadeBy : value for Version made by field
          303  +    flag : value for flag field (compression level info will be added)
          304  + */
          305  +
          306  +
          307  +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
          308  +                       const void* buf,
          309  +                       unsigned len));
          310  +/*
          311  +  Write data in the zipfile
          312  +*/
          313  +
          314  +extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
          315  +/*
          316  +  Close the current file in the zipfile
          317  +*/
          318  +
          319  +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
          320  +                                            uLong uncompressed_size,
          321  +                                            uLong crc32));
          322  +
          323  +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file,
          324  +                                            ZPOS64_T uncompressed_size,
          325  +                                            uLong crc32));
          326  +
          327  +/*
          328  +  Close the current file in the zipfile, for file opened with
          329  +    parameter raw=1 in zipOpenNewFileInZip2
          330  +  uncompressed_size and crc32 are value for the uncompressed size
          331  +*/
          332  +
          333  +extern int ZEXPORT zipClose OF((zipFile file,
          334  +                const char* global_comment));
          335  +/*
          336  +  Close the zipfile
          337  +*/
          338  +
          339  +
          340  +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader));
          341  +/*
          342  +  zipRemoveExtraInfoBlock -  Added by Mathias Svensson
          343  +
          344  +  Remove extra information block from a extra information data for the local file header or central directory header
          345  +
          346  +  It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode.
          347  +
          348  +  0x0001 is the signature header for the ZIP64 extra information blocks
          349  +
          350  +  usage.
          351  +                        Remove ZIP64 Extra information from a central director extra field data
          352  +              zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001);
          353  +
          354  +                        Remove ZIP64 Extra information from a Local File Header extra field data
          355  +        zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001);
          356  +*/
          357  +
          358  +#ifdef __cplusplus
          359  +}
          360  +#endif
          361  +
          362  +#endif /* _zip64_H */

Added compat/zlib/contrib/pascal/example.pas.

            1  +(* example.c -- usage example of the zlib compression library
            2  + * Copyright (C) 1995-2003 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + *
            5  + * Pascal translation
            6  + * Copyright (C) 1998 by Jacques Nomssi Nzali.
            7  + * For conditions of distribution and use, see copyright notice in readme.txt
            8  + *
            9  + * Adaptation to the zlibpas interface
           10  + * Copyright (C) 2003 by Cosmin Truta.
           11  + * For conditions of distribution and use, see copyright notice in readme.txt
           12  + *)
           13  +
           14  +program example;
           15  +
           16  +{$DEFINE TEST_COMPRESS}
           17  +{DO NOT $DEFINE TEST_GZIO}
           18  +{$DEFINE TEST_DEFLATE}
           19  +{$DEFINE TEST_INFLATE}
           20  +{$DEFINE TEST_FLUSH}
           21  +{$DEFINE TEST_SYNC}
           22  +{$DEFINE TEST_DICT}
           23  +
           24  +uses SysUtils, zlibpas;
           25  +
           26  +const TESTFILE = 'foo.gz';
           27  +
           28  +(* "hello world" would be more standard, but the repeated "hello"
           29  + * stresses the compression code better, sorry...
           30  + *)
           31  +const hello: PChar = 'hello, hello!';
           32  +
           33  +const dictionary: PChar = 'hello';
           34  +
           35  +var dictId: LongInt; (* Adler32 value of the dictionary *)
           36  +
           37  +procedure CHECK_ERR(err: Integer; msg: String);
           38  +begin
           39  +  if err <> Z_OK then
           40  +  begin
           41  +    WriteLn(msg, ' error: ', err);
           42  +    Halt(1);
           43  +  end;
           44  +end;
           45  +
           46  +procedure EXIT_ERR(const msg: String);
           47  +begin
           48  +  WriteLn('Error: ', msg);
           49  +  Halt(1);
           50  +end;
           51  +
           52  +(* ===========================================================================
           53  + * Test compress and uncompress
           54  + *)
           55  +{$IFDEF TEST_COMPRESS}
           56  +procedure test_compress(compr: Pointer; comprLen: LongInt;
           57  +                        uncompr: Pointer; uncomprLen: LongInt);
           58  +var err: Integer;
           59  +    len: LongInt;
           60  +begin
           61  +  len := StrLen(hello)+1;
           62  +
           63  +  err := compress(compr, comprLen, hello, len);
           64  +  CHECK_ERR(err, 'compress');
           65  +
           66  +  StrCopy(PChar(uncompr), 'garbage');
           67  +
           68  +  err := uncompress(uncompr, uncomprLen, compr, comprLen);
           69  +  CHECK_ERR(err, 'uncompress');
           70  +
           71  +  if StrComp(PChar(uncompr), hello) <> 0 then
           72  +    EXIT_ERR('bad uncompress')
           73  +  else
           74  +    WriteLn('uncompress(): ', PChar(uncompr));
           75  +end;
           76  +{$ENDIF}
           77  +
           78  +(* ===========================================================================
           79  + * Test read/write of .gz files
           80  + *)
           81  +{$IFDEF TEST_GZIO}
           82  +procedure test_gzio(const fname: PChar; (* compressed file name *)
           83  +                    uncompr: Pointer;
           84  +                    uncomprLen: LongInt);
           85  +var err: Integer;
           86  +    len: Integer;
           87  +    zfile: gzFile;
           88  +    pos: LongInt;
           89  +begin
           90  +  len := StrLen(hello)+1;
           91  +
           92  +  zfile := gzopen(fname, 'wb');
           93  +  if zfile = NIL then
           94  +  begin
           95  +    WriteLn('gzopen error');
           96  +    Halt(1);
           97  +  end;
           98  +  gzputc(zfile, 'h');
           99  +  if gzputs(zfile, 'ello') <> 4 then
          100  +  begin
          101  +    WriteLn('gzputs err: ', gzerror(zfile, err));
          102  +    Halt(1);
          103  +  end;
          104  +  {$IFDEF GZ_FORMAT_STRING}
          105  +  if gzprintf(zfile, ', %s!', 'hello') <> 8 then
          106  +  begin
          107  +    WriteLn('gzprintf err: ', gzerror(zfile, err));
          108  +    Halt(1);
          109  +  end;
          110  +  {$ELSE}
          111  +  if gzputs(zfile, ', hello!') <> 8 then
          112  +  begin
          113  +    WriteLn('gzputs err: ', gzerror(zfile, err));
          114  +    Halt(1);
          115  +  end;
          116  +  {$ENDIF}
          117  +  gzseek(zfile, 1, SEEK_CUR); (* add one zero byte *)
          118  +  gzclose(zfile);
          119  +
          120  +  zfile := gzopen(fname, 'rb');
          121  +  if zfile = NIL then
          122  +  begin
          123  +    WriteLn('gzopen error');
          124  +    Halt(1);
          125  +  end;
          126  +
          127  +  StrCopy(PChar(uncompr), 'garbage');
          128  +
          129  +  if gzread(zfile, uncompr, uncomprLen) <> len then
          130  +  begin
          131  +    WriteLn('gzread err: ', gzerror(zfile, err));
          132  +    Halt(1);
          133  +  end;
          134  +  if StrComp(PChar(uncompr), hello) <> 0 then
          135  +  begin
          136  +    WriteLn('bad gzread: ', PChar(uncompr));
          137  +    Halt(1);
          138  +  end
          139  +  else
          140  +    WriteLn('gzread(): ', PChar(uncompr));
          141  +
          142  +  pos := gzseek(zfile, -8, SEEK_CUR);
          143  +  if (pos <> 6) or (gztell(zfile) <> pos) then
          144  +  begin
          145  +    WriteLn('gzseek error, pos=', pos, ', gztell=', gztell(zfile));
          146  +    Halt(1);
          147  +  end;
          148  +
          149  +  if gzgetc(zfile) <> ' ' then
          150  +  begin
          151  +    WriteLn('gzgetc error');
          152  +    Halt(1);
          153  +  end;
          154  +
          155  +  if gzungetc(' ', zfile) <> ' ' then
          156  +  begin
          157  +    WriteLn('gzungetc error');
          158  +    Halt(1);
          159  +  end;
          160  +
          161  +  gzgets(zfile, PChar(uncompr), uncomprLen);
          162  +  uncomprLen := StrLen(PChar(uncompr));
          163  +  if uncomprLen <> 7 then (* " hello!" *)
          164  +  begin
          165  +    WriteLn('gzgets err after gzseek: ', gzerror(zfile, err));
          166  +    Halt(1);
          167  +  end;
          168  +  if StrComp(PChar(uncompr), hello + 6) <> 0 then
          169  +  begin
          170  +    WriteLn('bad gzgets after gzseek');
          171  +    Halt(1);
          172  +  end
          173  +  else
          174  +    WriteLn('gzgets() after gzseek: ', PChar(uncompr));
          175  +
          176  +  gzclose(zfile);
          177  +end;
          178  +{$ENDIF}
          179  +
          180  +(* ===========================================================================
          181  + * Test deflate with small buffers
          182  + *)
          183  +{$IFDEF TEST_DEFLATE}
          184  +procedure test_deflate(compr: Pointer; comprLen: LongInt);
          185  +var c_stream: z_stream; (* compression stream *)
          186  +    err: Integer;
          187  +    len: LongInt;
          188  +begin
          189  +  len := StrLen(hello)+1;
          190  +
          191  +  c_stream.zalloc := NIL;
          192  +  c_stream.zfree := NIL;
          193  +  c_stream.opaque := NIL;
          194  +
          195  +  err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION);
          196  +  CHECK_ERR(err, 'deflateInit');
          197  +
          198  +  c_stream.next_in := hello;
          199  +  c_stream.next_out := compr;
          200  +
          201  +  while (c_stream.total_in <> len) and
          202  +        (c_stream.total_out < comprLen) do
          203  +  begin
          204  +    c_stream.avail_out := 1; { force small buffers }
          205  +    c_stream.avail_in := 1;
          206  +    err := deflate(c_stream, Z_NO_FLUSH);
          207  +    CHECK_ERR(err, 'deflate');
          208  +  end;
          209  +
          210  +  (* Finish the stream, still forcing small buffers: *)
          211  +  while TRUE do
          212  +  begin
          213  +    c_stream.avail_out := 1;
          214  +    err := deflate(c_stream, Z_FINISH);
          215  +    if err = Z_STREAM_END then
          216  +      break;
          217  +    CHECK_ERR(err, 'deflate');
          218  +  end;
          219  +
          220  +  err := deflateEnd(c_stream);
          221  +  CHECK_ERR(err, 'deflateEnd');
          222  +end;
          223  +{$ENDIF}
          224  +
          225  +(* ===========================================================================
          226  + * Test inflate with small buffers
          227  + *)
          228  +{$IFDEF TEST_INFLATE}
          229  +procedure test_inflate(compr: Pointer; comprLen : LongInt;
          230  +                       uncompr: Pointer; uncomprLen : LongInt);
          231  +var err: Integer;
          232  +    d_stream: z_stream; (* decompression stream *)
          233  +begin
          234  +  StrCopy(PChar(uncompr), 'garbage');
          235  +
          236  +  d_stream.zalloc := NIL;
          237  +  d_stream.zfree := NIL;
          238  +  d_stream.opaque := NIL;
          239  +
          240  +  d_stream.next_in := compr;
          241  +  d_stream.avail_in := 0;
          242  +  d_stream.next_out := uncompr;
          243  +
          244  +  err := inflateInit(d_stream);
          245  +  CHECK_ERR(err, 'inflateInit');
          246  +
          247  +  while (d_stream.total_out < uncomprLen) and
          248  +        (d_stream.total_in < comprLen) do
          249  +  begin
          250  +    d_stream.avail_out := 1; (* force small buffers *)
          251  +    d_stream.avail_in := 1;
          252  +    err := inflate(d_stream, Z_NO_FLUSH);
          253  +    if err = Z_STREAM_END then
          254  +      break;
          255  +    CHECK_ERR(err, 'inflate');
          256  +  end;
          257  +
          258  +  err := inflateEnd(d_stream);
          259  +  CHECK_ERR(err, 'inflateEnd');
          260  +
          261  +  if StrComp(PChar(uncompr), hello) <> 0 then
          262  +    EXIT_ERR('bad inflate')
          263  +  else
          264  +    WriteLn('inflate(): ', PChar(uncompr));
          265  +end;
          266  +{$ENDIF}
          267  +
          268  +(* ===========================================================================
          269  + * Test deflate with large buffers and dynamic change of compression level
          270  + *)
          271  +{$IFDEF TEST_DEFLATE}
          272  +procedure test_large_deflate(compr: Pointer; comprLen: LongInt;
          273  +                             uncompr: Pointer; uncomprLen: LongInt);
          274  +var c_stream: z_stream; (* compression stream *)
          275  +    err: Integer;
          276  +begin
          277  +  c_stream.zalloc := NIL;
          278  +  c_stream.zfree := NIL;
          279  +  c_stream.opaque := NIL;
          280  +
          281  +  err := deflateInit(c_stream, Z_BEST_SPEED);
          282  +  CHECK_ERR(err, 'deflateInit');
          283  +
          284  +  c_stream.next_out := compr;
          285  +  c_stream.avail_out := Integer(comprLen);
          286  +
          287  +  (* At this point, uncompr is still mostly zeroes, so it should compress
          288  +   * very well:
          289  +   *)
          290  +  c_stream.next_in := uncompr;
          291  +  c_stream.avail_in := Integer(uncomprLen);
          292  +  err := deflate(c_stream, Z_NO_FLUSH);
          293  +  CHECK_ERR(err, 'deflate');
          294  +  if c_stream.avail_in <> 0 then
          295  +    EXIT_ERR('deflate not greedy');
          296  +
          297  +  (* Feed in already compressed data and switch to no compression: *)
          298  +  deflateParams(c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
          299  +  c_stream.next_in := compr;
          300  +  c_stream.avail_in := Integer(comprLen div 2);
          301  +  err := deflate(c_stream, Z_NO_FLUSH);
          302  +  CHECK_ERR(err, 'deflate');
          303  +
          304  +  (* Switch back to compressing mode: *)
          305  +  deflateParams(c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
          306  +  c_stream.next_in := uncompr;
          307  +  c_stream.avail_in := Integer(uncomprLen);
          308  +  err := deflate(c_stream, Z_NO_FLUSH);
          309  +  CHECK_ERR(err, 'deflate');
          310  +
          311  +  err := deflate(c_stream, Z_FINISH);
          312  +  if err <> Z_STREAM_END then
          313  +    EXIT_ERR('deflate should report Z_STREAM_END');
          314  +
          315  +  err := deflateEnd(c_stream);
          316  +  CHECK_ERR(err, 'deflateEnd');
          317  +end;
          318  +{$ENDIF}
          319  +
          320  +(* ===========================================================================
          321  + * Test inflate with large buffers
          322  + *)
          323  +{$IFDEF TEST_INFLATE}
          324  +procedure test_large_inflate(compr: Pointer; comprLen: LongInt;
          325  +                             uncompr: Pointer; uncomprLen: LongInt);
          326  +var err: Integer;
          327  +    d_stream: z_stream; (* decompression stream *)
          328  +begin
          329  +  StrCopy(PChar(uncompr), 'garbage');
          330  +
          331  +  d_stream.zalloc := NIL;
          332  +  d_stream.zfree := NIL;
          333  +  d_stream.opaque := NIL;
          334  +
          335  +  d_stream.next_in := compr;
          336  +  d_stream.avail_in := Integer(comprLen);
          337  +
          338  +  err := inflateInit(d_stream);
          339  +  CHECK_ERR(err, 'inflateInit');
          340  +
          341  +  while TRUE do
          342  +  begin
          343  +    d_stream.next_out := uncompr;            (* discard the output *)
          344  +    d_stream.avail_out := Integer(uncomprLen);
          345  +    err := inflate(d_stream, Z_NO_FLUSH);
          346  +    if err = Z_STREAM_END then
          347  +      break;
          348  +    CHECK_ERR(err, 'large inflate');
          349  +  end;
          350  +
          351  +  err := inflateEnd(d_stream);
          352  +  CHECK_ERR(err, 'inflateEnd');
          353  +
          354  +  if d_stream.total_out <> 2 * uncomprLen + comprLen div 2 then
          355  +  begin
          356  +    WriteLn('bad large inflate: ', d_stream.total_out);
          357  +    Halt(1);
          358  +  end
          359  +  else
          360  +    WriteLn('large_inflate(): OK');
          361  +end;
          362  +{$ENDIF}
          363  +
          364  +(* ===========================================================================
          365  + * Test deflate with full flush
          366  + *)
          367  +{$IFDEF TEST_FLUSH}
          368  +procedure test_flush(compr: Pointer; var comprLen : LongInt);
          369  +var c_stream: z_stream; (* compression stream *)
          370  +    err: Integer;
          371  +    len: Integer;
          372  +begin
          373  +  len := StrLen(hello)+1;
          374  +
          375  +  c_stream.zalloc := NIL;
          376  +  c_stream.zfree := NIL;
          377  +  c_stream.opaque := NIL;
          378  +
          379  +  err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION);
          380  +  CHECK_ERR(err, 'deflateInit');
          381  +
          382  +  c_stream.next_in := hello;
          383  +  c_stream.next_out := compr;
          384  +  c_stream.avail_in := 3;
          385  +  c_stream.avail_out := Integer(comprLen);
          386  +  err := deflate(c_stream, Z_FULL_FLUSH);
          387  +  CHECK_ERR(err, 'deflate');
          388  +
          389  +  Inc(PByteArray(compr)^[3]); (* force an error in first compressed block *)
          390  +  c_stream.avail_in := len - 3;
          391  +
          392  +  err := deflate(c_stream, Z_FINISH);
          393  +  if err <> Z_STREAM_END then
          394  +    CHECK_ERR(err, 'deflate');
          395  +
          396  +  err := deflateEnd(c_stream);
          397  +  CHECK_ERR(err, 'deflateEnd');
          398  +
          399  +  comprLen := c_stream.total_out;
          400  +end;
          401  +{$ENDIF}
          402  +
          403  +(* ===========================================================================
          404  + * Test inflateSync()
          405  + *)
          406  +{$IFDEF TEST_SYNC}
          407  +procedure test_sync(compr: Pointer; comprLen: LongInt;
          408  +                    uncompr: Pointer; uncomprLen : LongInt);
          409  +var err: Integer;
          410  +    d_stream: z_stream; (* decompression stream *)
          411  +begin
          412  +  StrCopy(PChar(uncompr), 'garbage');
          413  +
          414  +  d_stream.zalloc := NIL;
          415  +  d_stream.zfree := NIL;
          416  +  d_stream.opaque := NIL;
          417  +
          418  +  d_stream.next_in := compr;
          419  +  d_stream.avail_in := 2; (* just read the zlib header *)
          420  +
          421  +  err := inflateInit(d_stream);
          422  +  CHECK_ERR(err, 'inflateInit');
          423  +
          424  +  d_stream.next_out := uncompr;
          425  +  d_stream.avail_out := Integer(uncomprLen);
          426  +
          427  +  inflate(d_stream, Z_NO_FLUSH);
          428  +  CHECK_ERR(err, 'inflate');
          429  +
          430  +  d_stream.avail_in := Integer(comprLen-2);   (* read all compressed data *)
          431  +  err := inflateSync(d_stream);               (* but skip the damaged part *)
          432  +  CHECK_ERR(err, 'inflateSync');
          433  +
          434  +  err := inflate(d_stream, Z_FINISH);
          435  +  if err <> Z_DATA_ERROR then
          436  +    EXIT_ERR('inflate should report DATA_ERROR');
          437  +    (* Because of incorrect adler32 *)
          438  +
          439  +  err := inflateEnd(d_stream);
          440  +  CHECK_ERR(err, 'inflateEnd');
          441  +
          442  +  WriteLn('after inflateSync(): hel', PChar(uncompr));
          443  +end;
          444  +{$ENDIF}
          445  +
          446  +(* ===========================================================================
          447  + * Test deflate with preset dictionary
          448  + *)
          449  +{$IFDEF TEST_DICT}
          450  +procedure test_dict_deflate(compr: Pointer; comprLen: LongInt);
          451  +var c_stream: z_stream; (* compression stream *)
          452  +    err: Integer;
          453  +begin
          454  +  c_stream.zalloc := NIL;
          455  +  c_stream.zfree := NIL;
          456  +  c_stream.opaque := NIL;
          457  +
          458  +  err := deflateInit(c_stream, Z_BEST_COMPRESSION);
          459  +  CHECK_ERR(err, 'deflateInit');
          460  +
          461  +  err := deflateSetDictionary(c_stream, dictionary, StrLen(dictionary));
          462  +  CHECK_ERR(err, 'deflateSetDictionary');
          463  +
          464  +  dictId := c_stream.adler;
          465  +  c_stream.next_out := compr;
          466  +  c_stream.avail_out := Integer(comprLen);
          467  +
          468  +  c_stream.next_in := hello;
          469  +  c_stream.avail_in := StrLen(hello)+1;
          470  +
          471  +  err := deflate(c_stream, Z_FINISH);
          472  +  if err <> Z_STREAM_END then
          473  +    EXIT_ERR('deflate should report Z_STREAM_END');
          474  +
          475  +  err := deflateEnd(c_stream);
          476  +  CHECK_ERR(err, 'deflateEnd');
          477  +end;
          478  +{$ENDIF}
          479  +
          480  +(* ===========================================================================
          481  + * Test inflate with a preset dictionary
          482  + *)
          483  +{$IFDEF TEST_DICT}
          484  +procedure test_dict_inflate(compr: Pointer; comprLen: LongInt;
          485  +                            uncompr: Pointer; uncomprLen: LongInt);
          486  +var err: Integer;
          487  +    d_stream: z_stream; (* decompression stream *)
          488  +begin
          489  +  StrCopy(PChar(uncompr), 'garbage');
          490  +
          491  +  d_stream.zalloc := NIL;
          492  +  d_stream.zfree := NIL;
          493  +  d_stream.opaque := NIL;
          494  +
          495  +  d_stream.next_in := compr;
          496  +  d_stream.avail_in := Integer(comprLen);
          497  +
          498  +  err := inflateInit(d_stream);
          499  +  CHECK_ERR(err, 'inflateInit');
          500  +
          501  +  d_stream.next_out := uncompr;
          502  +  d_stream.avail_out := Integer(uncomprLen);
          503  +
          504  +  while TRUE do
          505  +  begin
          506  +    err := inflate(d_stream, Z_NO_FLUSH);
          507  +    if err = Z_STREAM_END then
          508  +      break;
          509  +    if err = Z_NEED_DICT then
          510  +    begin
          511  +      if d_stream.adler <> dictId then
          512  +        EXIT_ERR('unexpected dictionary');
          513  +      err := inflateSetDictionary(d_stream, dictionary, StrLen(dictionary));
          514  +    end;
          515  +    CHECK_ERR(err, 'inflate with dict');
          516  +  end;
          517  +
          518  +  err := inflateEnd(d_stream);
          519  +  CHECK_ERR(err, 'inflateEnd');
          520  +
          521  +  if StrComp(PChar(uncompr), hello) <> 0 then
          522  +    EXIT_ERR('bad inflate with dict')
          523  +  else
          524  +    WriteLn('inflate with dictionary: ', PChar(uncompr));
          525  +end;
          526  +{$ENDIF}
          527  +
          528  +var compr, uncompr: Pointer;
          529  +    comprLen, uncomprLen: LongInt;
          530  +
          531  +begin
          532  +  if zlibVersion^ <> ZLIB_VERSION[1] then
          533  +    EXIT_ERR('Incompatible zlib version');
          534  +
          535  +  WriteLn('zlib version: ', zlibVersion);
          536  +  WriteLn('zlib compile flags: ', Format('0x%x', [zlibCompileFlags]));
          537  +
          538  +  comprLen := 10000 * SizeOf(Integer); (* don't overflow on MSDOS *)
          539  +  uncomprLen := comprLen;
          540  +  GetMem(compr, comprLen);
          541  +  GetMem(uncompr, uncomprLen);
          542  +  if (compr = NIL) or (uncompr = NIL) then
          543  +    EXIT_ERR('Out of memory');
          544  +  (* compr and uncompr are cleared to avoid reading uninitialized
          545  +   * data and to ensure that uncompr compresses well.
          546  +   *)
          547  +  FillChar(compr^, comprLen, 0);
          548  +  FillChar(uncompr^, uncomprLen, 0);
          549  +
          550  +  {$IFDEF TEST_COMPRESS}
          551  +  WriteLn('** Testing compress');
          552  +  test_compress(compr, comprLen, uncompr, uncomprLen);
          553  +  {$ENDIF}
          554  +
          555  +  {$IFDEF TEST_GZIO}
          556  +  WriteLn('** Testing gzio');
          557  +  if ParamCount >= 1 then
          558  +    test_gzio(ParamStr(1), uncompr, uncomprLen)
          559  +  else
          560  +    test_gzio(TESTFILE, uncompr, uncomprLen);
          561  +  {$ENDIF}
          562  +
          563  +  {$IFDEF TEST_DEFLATE}
          564  +  WriteLn('** Testing deflate with small buffers');
          565  +  test_deflate(compr, comprLen);
          566  +  {$ENDIF}
          567  +  {$IFDEF TEST_INFLATE}
          568  +  WriteLn('** Testing inflate with small buffers');
          569  +  test_inflate(compr, comprLen, uncompr, uncomprLen);
          570  +  {$ENDIF}
          571  +
          572  +  {$IFDEF TEST_DEFLATE}
          573  +  WriteLn('** Testing deflate with large buffers');
          574  +  test_large_deflate(compr, comprLen, uncompr, uncomprLen);
          575  +  {$ENDIF}
          576  +  {$IFDEF TEST_INFLATE}
          577  +  WriteLn('** Testing inflate with large buffers');
          578  +  test_large_inflate(compr, comprLen, uncompr, uncomprLen);
          579  +  {$ENDIF}
          580  +
          581  +  {$IFDEF TEST_FLUSH}
          582  +  WriteLn('** Testing deflate with full flush');
          583  +  test_flush(compr, comprLen);
          584  +  {$ENDIF}
          585  +  {$IFDEF TEST_SYNC}
          586  +  WriteLn('** Testing inflateSync');
          587  +  test_sync(compr, comprLen, uncompr, uncomprLen);
          588  +  {$ENDIF}
          589  +  comprLen := uncomprLen;
          590  +
          591  +  {$IFDEF TEST_DICT}
          592  +  WriteLn('** Testing deflate and inflate with preset dictionary');
          593  +  test_dict_deflate(compr, comprLen);
          594  +  test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
          595  +  {$ENDIF}
          596  +
          597  +  FreeMem(compr, comprLen);
          598  +  FreeMem(uncompr, uncomprLen);
          599  +end.

Added compat/zlib/contrib/pascal/readme.txt.

            1  +
            2  +This directory contains a Pascal (Delphi, Kylix) interface to the
            3  +zlib data compression library.
            4  +
            5  +
            6  +Directory listing
            7  +=================
            8  +
            9  +zlibd32.mak     makefile for Borland C++
           10  +example.pas     usage example of zlib
           11  +zlibpas.pas     the Pascal interface to zlib
           12  +readme.txt      this file
           13  +
           14  +
           15  +Compatibility notes
           16  +===================
           17  +
           18  +- Although the name "zlib" would have been more normal for the
           19  +  zlibpas unit, this name is already taken by Borland's ZLib unit.
           20  +  This is somehow unfortunate, because that unit is not a genuine
           21  +  interface to the full-fledged zlib functionality, but a suite of
           22  +  class wrappers around zlib streams.  Other essential features,
           23  +  such as checksums, are missing.
           24  +  It would have been more appropriate for that unit to have a name
           25  +  like "ZStreams", or something similar.
           26  +
           27  +- The C and zlib-supplied types int, uInt, long, uLong, etc. are
           28  +  translated directly into Pascal types of similar sizes (Integer,
           29  +  LongInt, etc.), to avoid namespace pollution.  In particular,
           30  +  there is no conversion of unsigned int into a Pascal unsigned
           31  +  integer.  The Word type is non-portable and has the same size
           32  +  (16 bits) both in a 16-bit and in a 32-bit environment, unlike
           33  +  Integer.  Even if there is a 32-bit Cardinal type, there is no
           34  +  real need for unsigned int in zlib under a 32-bit environment.
           35  +
           36  +- Except for the callbacks, the zlib function interfaces are
           37  +  assuming the calling convention normally used in Pascal
           38  +  (__pascal for DOS and Windows16, __fastcall for Windows32).
           39  +  Since the cdecl keyword is used, the old Turbo Pascal does
           40  +  not work with this interface.
           41  +
           42  +- The gz* function interfaces are not translated, to avoid
           43  +  interfacing problems with the C runtime library.  Besides,
           44  +    gzprintf(gzFile file, const char *format, ...)
           45  +  cannot be translated into Pascal.
           46  +
           47  +
           48  +Legal issues
           49  +============
           50  +
           51  +The zlibpas interface is:
           52  +  Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler.
           53  +  Copyright (C) 1998 by Bob Dellaca.
           54  +  Copyright (C) 2003 by Cosmin Truta.
           55  +
           56  +The example program is:
           57  +  Copyright (C) 1995-2003 by Jean-loup Gailly.
           58  +  Copyright (C) 1998,1999,2000 by Jacques Nomssi Nzali.
           59  +  Copyright (C) 2003 by Cosmin Truta.
           60  +
           61  +  This software is provided 'as-is', without any express or implied
           62  +  warranty.  In no event will the author be held liable for any damages
           63  +  arising from the use of this software.
           64  +
           65  +  Permission is granted to anyone to use this software for any purpose,
           66  +  including commercial applications, and to alter it and redistribute it
           67  +  freely, subject to the following restrictions:
           68  +
           69  +  1. The origin of this software must not be misrepresented; you must not
           70  +     claim that you wrote the original software. If you use this software
           71  +     in a product, an acknowledgment in the product documentation would be
           72  +     appreciated but is not required.
           73  +  2. Altered source versions must be plainly marked as such, and must not be
           74  +     misrepresented as being the original software.
           75  +  3. This notice may not be removed or altered from any source distribution.
           76  +

Added compat/zlib/contrib/pascal/zlibd32.mak.

            1  +# Makefile for zlib
            2  +# For use with Delphi and C++ Builder under Win32
            3  +# Updated for zlib 1.2.x by Cosmin Truta
            4  +
            5  +# ------------ Borland C++ ------------
            6  +
            7  +# This project uses the Delphi (fastcall/register) calling convention:
            8  +LOC = -DZEXPORT=__fastcall -DZEXPORTVA=__cdecl
            9  +
           10  +CC = bcc32
           11  +LD = bcc32
           12  +AR = tlib
           13  +# do not use "-pr" in CFLAGS
           14  +CFLAGS = -a -d -k- -O2 $(LOC)
           15  +LDFLAGS =
           16  +
           17  +
           18  +# variables
           19  +ZLIB_LIB = zlib.lib
           20  +
           21  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           22  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           23  +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj
           24  +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj
           25  +
           26  +
           27  +# targets
           28  +all: $(ZLIB_LIB) example.exe minigzip.exe
           29  +
           30  +.c.obj:
           31  +	$(CC) -c $(CFLAGS) $*.c
           32  +
           33  +adler32.obj: adler32.c zlib.h zconf.h
           34  +
           35  +compress.obj: compress.c zlib.h zconf.h
           36  +
           37  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           38  +
           39  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           40  +
           41  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           42  +
           43  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           44  +
           45  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           46  +
           47  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           48  +
           49  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           50  + inffast.h inffixed.h
           51  +
           52  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           53  + inffast.h
           54  +
           55  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           56  + inffast.h inffixed.h
           57  +
           58  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           59  +
           60  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           61  +
           62  +uncompr.obj: uncompr.c zlib.h zconf.h
           63  +
           64  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           65  +
           66  +example.obj: test/example.c zlib.h zconf.h
           67  +
           68  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           69  +
           70  +
           71  +# For the sake of the old Borland make,
           72  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           73  +$(ZLIB_LIB): $(OBJ1) $(OBJ2)
           74  +	-del $(ZLIB_LIB)
           75  +	$(AR) $(ZLIB_LIB) $(OBJP1)
           76  +	$(AR) $(ZLIB_LIB) $(OBJP2)
           77  +
           78  +
           79  +# testing
           80  +test: example.exe minigzip.exe
           81  +	example
           82  +	echo hello world | minigzip | minigzip -d
           83  +
           84  +example.exe: example.obj $(ZLIB_LIB)
           85  +	$(LD) $(LDFLAGS) example.obj $(ZLIB_LIB)
           86  +
           87  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
           88  +	$(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB)
           89  +
           90  +
           91  +# cleanup
           92  +clean:
           93  +	-del *.obj
           94  +	-del *.exe
           95  +	-del *.lib
           96  +	-del *.tds
           97  +	-del zlib.bak
           98  +	-del foo.gz
           99  +

Added compat/zlib/contrib/pascal/zlibpas.pas.

            1  +(* zlibpas -- Pascal interface to the zlib data compression library
            2  + *
            3  + * Copyright (C) 2003 Cosmin Truta.
            4  + * Derived from original sources by Bob Dellaca.
            5  + * For conditions of distribution and use, see copyright notice in readme.txt
            6  + *)
            7  +
            8  +unit zlibpas;
            9  +
           10  +interface
           11  +
           12  +const
           13  +  ZLIB_VERSION = '1.2.7';
           14  +  ZLIB_VERNUM  = $1270;
           15  +
           16  +type
           17  +  alloc_func = function(opaque: Pointer; items, size: Integer): Pointer;
           18  +                 cdecl;
           19  +  free_func  = procedure(opaque, address: Pointer);
           20  +                 cdecl;
           21  +
           22  +  in_func    = function(opaque: Pointer; var buf: PByte): Integer;
           23  +                 cdecl;
           24  +  out_func   = function(opaque: Pointer; buf: PByte; size: Integer): Integer;
           25  +                 cdecl;
           26  +
           27  +  z_streamp = ^z_stream;
           28  +  z_stream = packed record
           29  +    next_in: PChar;       (* next input byte *)
           30  +    avail_in: Integer;    (* number of bytes available at next_in *)
           31  +    total_in: LongInt;    (* total nb of input bytes read so far *)
           32  +
           33  +    next_out: PChar;      (* next output byte should be put there *)
           34  +    avail_out: Integer;   (* remaining free space at next_out *)
           35  +    total_out: LongInt;   (* total nb of bytes output so far *)
           36  +
           37  +    msg: PChar;           (* last error message, NULL if no error *)
           38  +    state: Pointer;       (* not visible by applications *)
           39  +
           40  +    zalloc: alloc_func;   (* used to allocate the internal state *)
           41  +    zfree: free_func;     (* used to free the internal state *)
           42  +    opaque: Pointer;      (* private data object passed to zalloc and zfree *)
           43  +
           44  +    data_type: Integer;   (* best guess about the data type: ascii or binary *)
           45  +    adler: LongInt;       (* adler32 value of the uncompressed data *)
           46  +    reserved: LongInt;    (* reserved for future use *)
           47  +  end;
           48  +
           49  +  gz_headerp = ^gz_header;
           50  +  gz_header = packed record
           51  +    text: Integer;        (* true if compressed data believed to be text *)
           52  +    time: LongInt;        (* modification time *)
           53  +    xflags: Integer;      (* extra flags (not used when writing a gzip file) *)
           54  +    os: Integer;          (* operating system *)
           55  +    extra: PChar;         (* pointer to extra field or Z_NULL if none *)
           56  +    extra_len: Integer;   (* extra field length (valid if extra != Z_NULL) *)
           57  +    extra_max: Integer;   (* space at extra (only when reading header) *)
           58  +    name: PChar;          (* pointer to zero-terminated file name or Z_NULL *)
           59  +    name_max: Integer;    (* space at name (only when reading header) *)
           60  +    comment: PChar;       (* pointer to zero-terminated comment or Z_NULL *)
           61  +    comm_max: Integer;    (* space at comment (only when reading header) *)
           62  +    hcrc: Integer;        (* true if there was or will be a header crc *)
           63  +    done: Integer;        (* true when done reading gzip header *)
           64  +  end;
           65  +
           66  +(* constants *)
           67  +const
           68  +  Z_NO_FLUSH      = 0;
           69  +  Z_PARTIAL_FLUSH = 1;
           70  +  Z_SYNC_FLUSH    = 2;
           71  +  Z_FULL_FLUSH    = 3;
           72  +  Z_FINISH        = 4;
           73  +  Z_BLOCK         = 5;
           74  +  Z_TREES         = 6;
           75  +
           76  +  Z_OK            =  0;
           77  +  Z_STREAM_END    =  1;
           78  +  Z_NEED_DICT     =  2;
           79  +  Z_ERRNO         = -1;
           80  +  Z_STREAM_ERROR  = -2;
           81  +  Z_DATA_ERROR    = -3;
           82  +  Z_MEM_ERROR     = -4;
           83  +  Z_BUF_ERROR     = -5;
           84  +  Z_VERSION_ERROR = -6;
           85  +
           86  +  Z_NO_COMPRESSION       =  0;
           87  +  Z_BEST_SPEED           =  1;
           88  +  Z_BEST_COMPRESSION     =  9;
           89  +  Z_DEFAULT_COMPRESSION  = -1;
           90  +
           91  +  Z_FILTERED            = 1;
           92  +  Z_HUFFMAN_ONLY        = 2;
           93  +  Z_RLE                 = 3;
           94  +  Z_FIXED               = 4;
           95  +  Z_DEFAULT_STRATEGY    = 0;
           96  +
           97  +  Z_BINARY   = 0;
           98  +  Z_TEXT     = 1;
           99  +  Z_ASCII    = 1;
          100  +  Z_UNKNOWN  = 2;
          101  +
          102  +  Z_DEFLATED = 8;
          103  +
          104  +(* basic functions *)
          105  +function zlibVersion: PChar;
          106  +function deflateInit(var strm: z_stream; level: Integer): Integer;
          107  +function deflate(var strm: z_stream; flush: Integer): Integer;
          108  +function deflateEnd(var strm: z_stream): Integer;
          109  +function inflateInit(var strm: z_stream): Integer;
          110  +function inflate(var strm: z_stream; flush: Integer): Integer;
          111  +function inflateEnd(var strm: z_stream): Integer;
          112  +
          113  +(* advanced functions *)
          114  +function deflateInit2(var strm: z_stream; level, method, windowBits,
          115  +                      memLevel, strategy: Integer): Integer;
          116  +function deflateSetDictionary(var strm: z_stream; const dictionary: PChar;
          117  +                              dictLength: Integer): Integer;
          118  +function deflateCopy(var dest, source: z_stream): Integer;
          119  +function deflateReset(var strm: z_stream): Integer;
          120  +function deflateParams(var strm: z_stream; level, strategy: Integer): Integer;
          121  +function deflateTune(var strm: z_stream; good_length, max_lazy, nice_length, max_chain: Integer): Integer;
          122  +function deflateBound(var strm: z_stream; sourceLen: LongInt): LongInt;
          123  +function deflatePending(var strm: z_stream; var pending: Integer; var bits: Integer): Integer;
          124  +function deflatePrime(var strm: z_stream; bits, value: Integer): Integer;
          125  +function deflateSetHeader(var strm: z_stream; head: gz_header): Integer;
          126  +function inflateInit2(var strm: z_stream; windowBits: Integer): Integer;
          127  +function inflateSetDictionary(var strm: z_stream; const dictionary: PChar;
          128  +                              dictLength: Integer): Integer;
          129  +function inflateSync(var strm: z_stream): Integer;
          130  +function inflateCopy(var dest, source: z_stream): Integer;
          131  +function inflateReset(var strm: z_stream): Integer;
          132  +function inflateReset2(var strm: z_stream; windowBits: Integer): Integer;
          133  +function inflatePrime(var strm: z_stream; bits, value: Integer): Integer;
          134  +function inflateMark(var strm: z_stream): LongInt;
          135  +function inflateGetHeader(var strm: z_stream; var head: gz_header): Integer;
          136  +function inflateBackInit(var strm: z_stream;
          137  +                         windowBits: Integer; window: PChar): Integer;
          138  +function inflateBack(var strm: z_stream; in_fn: in_func; in_desc: Pointer;
          139  +                     out_fn: out_func; out_desc: Pointer): Integer;
          140  +function inflateBackEnd(var strm: z_stream): Integer;
          141  +function zlibCompileFlags: LongInt;
          142  +
          143  +(* utility functions *)
          144  +function compress(dest: PChar; var destLen: LongInt;
          145  +                  const source: PChar; sourceLen: LongInt): Integer;
          146  +function compress2(dest: PChar; var destLen: LongInt;
          147  +                  const source: PChar; sourceLen: LongInt;
          148  +                  level: Integer): Integer;
          149  +function compressBound(sourceLen: LongInt): LongInt;
          150  +function uncompress(dest: PChar; var destLen: LongInt;
          151  +                    const source: PChar; sourceLen: LongInt): Integer;
          152  +
          153  +(* checksum functions *)
          154  +function adler32(adler: LongInt; const buf: PChar; len: Integer): LongInt;
          155  +function adler32_combine(adler1, adler2, len2: LongInt): LongInt;
          156  +function crc32(crc: LongInt; const buf: PChar; len: Integer): LongInt;
          157  +function crc32_combine(crc1, crc2, len2: LongInt): LongInt;
          158  +
          159  +(* various hacks, don't look :) *)
          160  +function deflateInit_(var strm: z_stream; level: Integer;
          161  +                      const version: PChar; stream_size: Integer): Integer;
          162  +function inflateInit_(var strm: z_stream; const version: PChar;
          163  +                      stream_size: Integer): Integer;
          164  +function deflateInit2_(var strm: z_stream;
          165  +                       level, method, windowBits, memLevel, strategy: Integer;
          166  +                       const version: PChar; stream_size: Integer): Integer;
          167  +function inflateInit2_(var strm: z_stream; windowBits: Integer;
          168  +                       const version: PChar; stream_size: Integer): Integer;
          169  +function inflateBackInit_(var strm: z_stream;
          170  +                          windowBits: Integer; window: PChar;
          171  +                          const version: PChar; stream_size: Integer): Integer;
          172  +
          173  +
          174  +implementation
          175  +
          176  +{$L adler32.obj}
          177  +{$L compress.obj}
          178  +{$L crc32.obj}
          179  +{$L deflate.obj}
          180  +{$L infback.obj}
          181  +{$L inffast.obj}
          182  +{$L inflate.obj}
          183  +{$L inftrees.obj}
          184  +{$L trees.obj}
          185  +{$L uncompr.obj}
          186  +{$L zutil.obj}
          187  +
          188  +function adler32; external;
          189  +function adler32_combine; external;
          190  +function compress; external;
          191  +function compress2; external;
          192  +function compressBound; external;
          193  +function crc32; external;
          194  +function crc32_combine; external;
          195  +function deflate; external;
          196  +function deflateBound; external;
          197  +function deflateCopy; external;
          198  +function deflateEnd; external;
          199  +function deflateInit_; external;
          200  +function deflateInit2_; external;
          201  +function deflateParams; external;
          202  +function deflatePending; external;
          203  +function deflatePrime; external;
          204  +function deflateReset; external;
          205  +function deflateSetDictionary; external;
          206  +function deflateSetHeader; external;
          207  +function deflateTune; external;
          208  +function inflate; external;
          209  +function inflateBack; external;
          210  +function inflateBackEnd; external;
          211  +function inflateBackInit_; external;
          212  +function inflateCopy; external;
          213  +function inflateEnd; external;
          214  +function inflateGetHeader; external;
          215  +function inflateInit_; external;
          216  +function inflateInit2_; external;
          217  +function inflateMark; external;
          218  +function inflatePrime; external;
          219  +function inflateReset; external;
          220  +function inflateReset2; external;
          221  +function inflateSetDictionary; external;
          222  +function inflateSync; external;
          223  +function uncompress; external;
          224  +function zlibCompileFlags; external;
          225  +function zlibVersion; external;
          226  +
          227  +function deflateInit(var strm: z_stream; level: Integer): Integer;
          228  +begin
          229  +  Result := deflateInit_(strm, level, ZLIB_VERSION, sizeof(z_stream));
          230  +end;
          231  +
          232  +function deflateInit2(var strm: z_stream; level, method, windowBits, memLevel,
          233  +                      strategy: Integer): Integer;
          234  +begin
          235  +  Result := deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
          236  +                          ZLIB_VERSION, sizeof(z_stream));
          237  +end;
          238  +
          239  +function inflateInit(var strm: z_stream): Integer;
          240  +begin
          241  +  Result := inflateInit_(strm, ZLIB_VERSION, sizeof(z_stream));
          242  +end;
          243  +
          244  +function inflateInit2(var strm: z_stream; windowBits: Integer): Integer;
          245  +begin
          246  +  Result := inflateInit2_(strm, windowBits, ZLIB_VERSION, sizeof(z_stream));
          247  +end;
          248  +
          249  +function inflateBackInit(var strm: z_stream;
          250  +                         windowBits: Integer; window: PChar): Integer;
          251  +begin
          252  +  Result := inflateBackInit_(strm, windowBits, window,
          253  +                             ZLIB_VERSION, sizeof(z_stream));
          254  +end;
          255  +
          256  +function _malloc(Size: Integer): Pointer; cdecl;
          257  +begin
          258  +  GetMem(Result, Size);
          259  +end;
          260  +
          261  +procedure _free(Block: Pointer); cdecl;
          262  +begin
          263  +  FreeMem(Block);
          264  +end;
          265  +
          266  +procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
          267  +begin
          268  +  FillChar(P^, count, B);
          269  +end;
          270  +
          271  +procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
          272  +begin
          273  +  Move(source^, dest^, count);
          274  +end;
          275  +
          276  +end.

Added compat/zlib/contrib/puff/Makefile.

            1  +CFLAGS=-O
            2  +
            3  +puff: puff.o pufftest.o
            4  +
            5  +puff.o: puff.h
            6  +
            7  +pufftest.o: puff.h
            8  +
            9  +test: puff
           10  +	puff zeros.raw
           11  +
           12  +puft: puff.c puff.h pufftest.o
           13  +	cc -fprofile-arcs -ftest-coverage -o puft puff.c pufftest.o
           14  +
           15  +# puff full coverage test (should say 100%)
           16  +cov: puft
           17  +	@rm -f *.gcov *.gcda
           18  +	@puft -w zeros.raw 2>&1 | cat > /dev/null
           19  +	@echo '04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2
           20  +	@echo '00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2
           21  +	@echo '00 00 00 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 254
           22  +	@echo '00 01 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2
           23  +	@echo '01 01 00 fe ff 0a' | xxd -r -p | puft -f 2>&1 | cat > /dev/null
           24  +	@echo '02 7e ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246
           25  +	@echo '02' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2
           26  +	@echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2
           27  +	@echo '04 80 49 92 24 49 92 24 71 ff ff 93 11 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 249
           28  +	@echo '04 c0 81 08 00 00 00 00 20 7f eb 0b 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246
           29  +	@echo '0b 00 00' | xxd -r -p | puft -f 2>&1 | cat > /dev/null
           30  +	@echo '1a 07' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246
           31  +	@echo '0c c0 81 00 00 00 00 00 90 ff 6b 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 245
           32  +	@puft -f zeros.raw 2>&1 | cat > /dev/null
           33  +	@echo 'fc 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 253
           34  +	@echo '04 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 252
           35  +	@echo '04 00 24 49' | xxd -r -p | puft 2> /dev/null || test $$? -eq 251
           36  +	@echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 84' | xxd -r -p | puft 2> /dev/null || test $$? -eq 248
           37  +	@echo '04 00 24 e9 ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 250
           38  +	@echo '04 00 24 e9 ff 6d' | xxd -r -p | puft 2> /dev/null || test $$? -eq 247
           39  +	@gcov -n puff.c
           40  +
           41  +clean:
           42  +	rm -f puff puft *.o *.gc*

Added compat/zlib/contrib/puff/README.

            1  +Puff -- A Simple Inflate
            2  +3 Mar 2003
            3  +Mark Adler
            4  +madler@alumni.caltech.edu
            5  +
            6  +What this is --
            7  +
            8  +puff.c provides the routine puff() to decompress the deflate data format.  It
            9  +does so more slowly than zlib, but the code is about one-fifth the size of the
           10  +inflate code in zlib, and written to be very easy to read.
           11  +
           12  +Why I wrote this --
           13  +
           14  +puff.c was written to document the deflate format unambiguously, by virtue of
           15  +being working C code.  It is meant to supplement RFC 1951, which formally
           16  +describes the deflate format.  I have received many questions on details of the
           17  +deflate format, and I hope that reading this code will answer those questions.
           18  +puff.c is heavily commented with details of the deflate format, especially
           19  +those little nooks and cranies of the format that might not be obvious from a
           20  +specification.
           21  +
           22  +puff.c may also be useful in applications where code size or memory usage is a
           23  +very limited resource, and speed is not as important.
           24  +
           25  +How to use it --
           26  +
           27  +Well, most likely you should just be reading puff.c and using zlib for actual
           28  +applications, but if you must ...
           29  +
           30  +Include puff.h in your code, which provides this prototype:
           31  +
           32  +int puff(unsigned char *dest,           /* pointer to destination pointer */
           33  +         unsigned long *destlen,        /* amount of output space */
           34  +         unsigned char *source,         /* pointer to source data pointer */
           35  +         unsigned long *sourcelen);     /* amount of input available */
           36  +
           37  +Then you can call puff() to decompress a deflate stream that is in memory in
           38  +its entirety at source, to a sufficiently sized block of memory for the
           39  +decompressed data at dest.  puff() is the only external symbol in puff.c  The
           40  +only C library functions that puff.c needs are setjmp() and longjmp(), which
           41  +are used to simplify error checking in the code to improve readabilty.  puff.c
           42  +does no memory allocation, and uses less than 2K bytes off of the stack.
           43  +
           44  +If destlen is not enough space for the uncompressed data, then inflate will
           45  +return an error without writing more than destlen bytes.  Note that this means
           46  +that in order to decompress the deflate data successfully, you need to know
           47  +the size of the uncompressed data ahead of time.
           48  +
           49  +If needed, puff() can determine the size of the uncompressed data with no
           50  +output space.  This is done by passing dest equal to (unsigned char *)0.  Then
           51  +the initial value of *destlen is ignored and *destlen is set to the length of
           52  +the uncompressed data.  So if the size of the uncompressed data is not known,
           53  +then two passes of puff() can be used--first to determine the size, and second
           54  +to do the actual inflation after allocating the appropriate memory.  Not
           55  +pretty, but it works.  (This is one of the reasons you should be using zlib.)
           56  +
           57  +The deflate format is self-terminating.  If the deflate stream does not end
           58  +in *sourcelen bytes, puff() will return an error without reading at or past
           59  +endsource.
           60  +
           61  +On return, *sourcelen is updated to the amount of input data consumed, and
           62  +*destlen is updated to the size of the uncompressed data.  See the comments
           63  +in puff.c for the possible return codes for puff().

Added compat/zlib/contrib/puff/puff.c.

            1  +/*
            2  + * puff.c
            3  + * Copyright (C) 2002-2010 Mark Adler
            4  + * For conditions of distribution and use, see copyright notice in puff.h
            5  + * version 2.2, 25 Apr 2010
            6  + *
            7  + * puff.c is a simple inflate written to be an unambiguous way to specify the
            8  + * deflate format.  It is not written for speed but rather simplicity.  As a
            9  + * side benefit, this code might actually be useful when small code is more
           10  + * important than speed, such as bootstrap applications.  For typical deflate
           11  + * data, zlib's inflate() is about four times as fast as puff().  zlib's
           12  + * inflate compiles to around 20K on my machine, whereas puff.c compiles to
           13  + * around 4K on my machine (a PowerPC using GNU cc).  If the faster decode()
           14  + * function here is used, then puff() is only twice as slow as zlib's
           15  + * inflate().
           16  + *
           17  + * All dynamically allocated memory comes from the stack.  The stack required
           18  + * is less than 2K bytes.  This code is compatible with 16-bit int's and
           19  + * assumes that long's are at least 32 bits.  puff.c uses the short data type,
           20  + * assumed to be 16 bits, for arrays in order to to conserve memory.  The code
           21  + * works whether integers are stored big endian or little endian.
           22  + *
           23  + * In the comments below are "Format notes" that describe the inflate process
           24  + * and document some of the less obvious aspects of the format.  This source
           25  + * code is meant to supplement RFC 1951, which formally describes the deflate
           26  + * format:
           27  + *
           28  + *    http://www.zlib.org/rfc-deflate.html
           29  + */
           30  +
           31  +/*
           32  + * Change history:
           33  + *
           34  + * 1.0  10 Feb 2002     - First version
           35  + * 1.1  17 Feb 2002     - Clarifications of some comments and notes
           36  + *                      - Update puff() dest and source pointers on negative
           37  + *                        errors to facilitate debugging deflators
           38  + *                      - Remove longest from struct huffman -- not needed
           39  + *                      - Simplify offs[] index in construct()
           40  + *                      - Add input size and checking, using longjmp() to
           41  + *                        maintain easy readability
           42  + *                      - Use short data type for large arrays
           43  + *                      - Use pointers instead of long to specify source and
           44  + *                        destination sizes to avoid arbitrary 4 GB limits
           45  + * 1.2  17 Mar 2002     - Add faster version of decode(), doubles speed (!),
           46  + *                        but leave simple version for readabilty
           47  + *                      - Make sure invalid distances detected if pointers
           48  + *                        are 16 bits
           49  + *                      - Fix fixed codes table error
           50  + *                      - Provide a scanning mode for determining size of
           51  + *                        uncompressed data
           52  + * 1.3  20 Mar 2002     - Go back to lengths for puff() parameters [Gailly]
           53  + *                      - Add a puff.h file for the interface
           54  + *                      - Add braces in puff() for else do [Gailly]
           55  + *                      - Use indexes instead of pointers for readability
           56  + * 1.4  31 Mar 2002     - Simplify construct() code set check
           57  + *                      - Fix some comments
           58  + *                      - Add FIXLCODES #define
           59  + * 1.5   6 Apr 2002     - Minor comment fixes
           60  + * 1.6   7 Aug 2002     - Minor format changes
           61  + * 1.7   3 Mar 2003     - Added test code for distribution
           62  + *                      - Added zlib-like license
           63  + * 1.8   9 Jan 2004     - Added some comments on no distance codes case
           64  + * 1.9  21 Feb 2008     - Fix bug on 16-bit integer architectures [Pohland]
           65  + *                      - Catch missing end-of-block symbol error
           66  + * 2.0  25 Jul 2008     - Add #define to permit distance too far back
           67  + *                      - Add option in TEST code for puff to write the data
           68  + *                      - Add option in TEST code to skip input bytes
           69  + *                      - Allow TEST code to read from piped stdin
           70  + * 2.1   4 Apr 2010     - Avoid variable initialization for happier compilers
           71  + *                      - Avoid unsigned comparisons for even happier compilers
           72  + * 2.2  25 Apr 2010     - Fix bug in variable initializations [Oberhumer]
           73  + *                      - Add const where appropriate [Oberhumer]
           74  + *                      - Split if's and ?'s for coverage testing
           75  + *                      - Break out test code to separate file
           76  + *                      - Move NIL to puff.h
           77  + *                      - Allow incomplete code only if single code length is 1
           78  + *                      - Add full code coverage test to Makefile
           79  + */
           80  +
           81  +#include <setjmp.h>             /* for setjmp(), longjmp(), and jmp_buf */
           82  +#include "puff.h"               /* prototype for puff() */
           83  +
           84  +#define local static            /* for local function definitions */
           85  +
           86  +/*
           87  + * Maximums for allocations and loops.  It is not useful to change these --
           88  + * they are fixed by the deflate format.
           89  + */
           90  +#define MAXBITS 15              /* maximum bits in a code */
           91  +#define MAXLCODES 286           /* maximum number of literal/length codes */
           92  +#define MAXDCODES 30            /* maximum number of distance codes */
           93  +#define MAXCODES (MAXLCODES+MAXDCODES)  /* maximum codes lengths to read */
           94  +#define FIXLCODES 288           /* number of fixed literal/length codes */
           95  +
           96  +/* input and output state */
           97  +struct state {
           98  +    /* output state */
           99  +    unsigned char *out;         /* output buffer */
          100  +    unsigned long outlen;       /* available space at out */
          101  +    unsigned long outcnt;       /* bytes written to out so far */
          102  +
          103  +    /* input state */
          104  +    const unsigned char *in;    /* input buffer */
          105  +    unsigned long inlen;        /* available input at in */
          106  +    unsigned long incnt;        /* bytes read so far */
          107  +    int bitbuf;                 /* bit buffer */
          108  +    int bitcnt;                 /* number of bits in bit buffer */
          109  +
          110  +    /* input limit error return state for bits() and decode() */
          111  +    jmp_buf env;
          112  +};
          113  +
          114  +/*
          115  + * Return need bits from the input stream.  This always leaves less than
          116  + * eight bits in the buffer.  bits() works properly for need == 0.
          117  + *
          118  + * Format notes:
          119  + *
          120  + * - Bits are stored in bytes from the least significant bit to the most
          121  + *   significant bit.  Therefore bits are dropped from the bottom of the bit
          122  + *   buffer, using shift right, and new bytes are appended to the top of the
          123  + *   bit buffer, using shift left.
          124  + */
          125  +local int bits(struct state *s, int need)
          126  +{
          127  +    long val;           /* bit accumulator (can use up to 20 bits) */
          128  +
          129  +    /* load at least need bits into val */
          130  +    val = s->bitbuf;
          131  +    while (s->bitcnt < need) {
          132  +        if (s->incnt == s->inlen)
          133  +            longjmp(s->env, 1);         /* out of input */
          134  +        val |= (long)(s->in[s->incnt++]) << s->bitcnt;  /* load eight bits */
          135  +        s->bitcnt += 8;
          136  +    }
          137  +
          138  +    /* drop need bits and update buffer, always zero to seven bits left */
          139  +    s->bitbuf = (int)(val >> need);
          140  +    s->bitcnt -= need;
          141  +
          142  +    /* return need bits, zeroing the bits above that */
          143  +    return (int)(val & ((1L << need) - 1));
          144  +}
          145  +
          146  +/*
          147  + * Process a stored block.
          148  + *
          149  + * Format notes:
          150  + *
          151  + * - After the two-bit stored block type (00), the stored block length and
          152  + *   stored bytes are byte-aligned for fast copying.  Therefore any leftover
          153  + *   bits in the byte that has the last bit of the type, as many as seven, are
          154  + *   discarded.  The value of the discarded bits are not defined and should not
          155  + *   be checked against any expectation.
          156  + *
          157  + * - The second inverted copy of the stored block length does not have to be
          158  + *   checked, but it's probably a good idea to do so anyway.
          159  + *
          160  + * - A stored block can have zero length.  This is sometimes used to byte-align
          161  + *   subsets of the compressed data for random access or partial recovery.
          162  + */
          163  +local int stored(struct state *s)
          164  +{
          165  +    unsigned len;       /* length of stored block */
          166  +
          167  +    /* discard leftover bits from current byte (assumes s->bitcnt < 8) */
          168  +    s->bitbuf = 0;
          169  +    s->bitcnt = 0;
          170  +
          171  +    /* get length and check against its one's complement */
          172  +    if (s->incnt + 4 > s->inlen)
          173  +        return 2;                               /* not enough input */
          174  +    len = s->in[s->incnt++];
          175  +    len |= s->in[s->incnt++] << 8;
          176  +    if (s->in[s->incnt++] != (~len & 0xff) ||
          177  +        s->in[s->incnt++] != ((~len >> 8) & 0xff))
          178  +        return -2;                              /* didn't match complement! */
          179  +
          180  +    /* copy len bytes from in to out */
          181  +    if (s->incnt + len > s->inlen)
          182  +        return 2;                               /* not enough input */
          183  +    if (s->out != NIL) {
          184  +        if (s->outcnt + len > s->outlen)
          185  +            return 1;                           /* not enough output space */
          186  +        while (len--)
          187  +            s->out[s->outcnt++] = s->in[s->incnt++];
          188  +    }
          189  +    else {                                      /* just scanning */
          190  +        s->outcnt += len;
          191  +        s->incnt += len;
          192  +    }
          193  +
          194  +    /* done with a valid stored block */
          195  +    return 0;
          196  +}
          197  +
          198  +/*
          199  + * Huffman code decoding tables.  count[1..MAXBITS] is the number of symbols of
          200  + * each length, which for a canonical code are stepped through in order.
          201  + * symbol[] are the symbol values in canonical order, where the number of
          202  + * entries is the sum of the counts in count[].  The decoding process can be
          203  + * seen in the function decode() below.
          204  + */
          205  +struct huffman {
          206  +    short *count;       /* number of symbols of each length */
          207  +    short *symbol;      /* canonically ordered symbols */
          208  +};
          209  +
          210  +/*
          211  + * Decode a code from the stream s using huffman table h.  Return the symbol or
          212  + * a negative value if there is an error.  If all of the lengths are zero, i.e.
          213  + * an empty code, or if the code is incomplete and an invalid code is received,
          214  + * then -10 is returned after reading MAXBITS bits.
          215  + *
          216  + * Format notes:
          217  + *
          218  + * - The codes as stored in the compressed data are bit-reversed relative to
          219  + *   a simple integer ordering of codes of the same lengths.  Hence below the
          220  + *   bits are pulled from the compressed data one at a time and used to
          221  + *   build the code value reversed from what is in the stream in order to
          222  + *   permit simple integer comparisons for decoding.  A table-based decoding
          223  + *   scheme (as used in zlib) does not need to do this reversal.
          224  + *
          225  + * - The first code for the shortest length is all zeros.  Subsequent codes of
          226  + *   the same length are simply integer increments of the previous code.  When
          227  + *   moving up a length, a zero bit is appended to the code.  For a complete
          228  + *   code, the last code of the longest length will be all ones.
          229  + *
          230  + * - Incomplete codes are handled by this decoder, since they are permitted
          231  + *   in the deflate format.  See the format notes for fixed() and dynamic().
          232  + */
          233  +#ifdef SLOW
          234  +local int decode(struct state *s, const struct huffman *h)
          235  +{
          236  +    int len;            /* current number of bits in code */
          237  +    int code;           /* len bits being decoded */
          238  +    int first;          /* first code of length len */
          239  +    int count;          /* number of codes of length len */
          240  +    int index;          /* index of first code of length len in symbol table */
          241  +
          242  +    code = first = index = 0;
          243  +    for (len = 1; len <= MAXBITS; len++) {
          244  +        code |= bits(s, 1);             /* get next bit */
          245  +        count = h->count[len];
          246  +        if (code - count < first)       /* if length len, return symbol */
          247  +            return h->symbol[index + (code - first)];
          248  +        index += count;                 /* else update for next length */
          249  +        first += count;
          250  +        first <<= 1;
          251  +        code <<= 1;
          252  +    }
          253  +    return -10;                         /* ran out of codes */
          254  +}
          255  +
          256  +/*
          257  + * A faster version of decode() for real applications of this code.   It's not
          258  + * as readable, but it makes puff() twice as fast.  And it only makes the code
          259  + * a few percent larger.
          260  + */
          261  +#else /* !SLOW */
          262  +local int decode(struct state *s, const struct huffman *h)
          263  +{
          264  +    int len;            /* current number of bits in code */
          265  +    int code;           /* len bits being decoded */
          266  +    int first;          /* first code of length len */
          267  +    int count;          /* number of codes of length len */
          268  +    int index;          /* index of first code of length len in symbol table */
          269  +    int bitbuf;         /* bits from stream */
          270  +    int left;           /* bits left in next or left to process */
          271  +    short *next;        /* next number of codes */
          272  +
          273  +    bitbuf = s->bitbuf;
          274  +    left = s->bitcnt;
          275  +    code = first = index = 0;
          276  +    len = 1;
          277  +    next = h->count + 1;
          278  +    while (1) {
          279  +        while (left--) {
          280  +            code |= bitbuf & 1;
          281  +            bitbuf >>= 1;
          282  +            count = *next++;
          283  +            if (code - count < first) { /* if length len, return symbol */
          284  +                s->bitbuf = bitbuf;
          285  +                s->bitcnt = (s->bitcnt - len) & 7;
          286  +                return h->symbol[index + (code - first)];
          287  +            }
          288  +            index += count;             /* else update for next length */
          289  +            first += count;
          290  +            first <<= 1;
          291  +            code <<= 1;
          292  +            len++;
          293  +        }
          294  +        left = (MAXBITS+1) - len;
          295  +        if (left == 0)
          296  +            break;
          297  +        if (s->incnt == s->inlen)
          298  +            longjmp(s->env, 1);         /* out of input */
          299  +        bitbuf = s->in[s->incnt++];
          300  +        if (left > 8)
          301  +            left = 8;
          302  +    }
          303  +    return -10;                         /* ran out of codes */
          304  +}
          305  +#endif /* SLOW */
          306  +
          307  +/*
          308  + * Given the list of code lengths length[0..n-1] representing a canonical
          309  + * Huffman code for n symbols, construct the tables required to decode those
          310  + * codes.  Those tables are the number of codes of each length, and the symbols
          311  + * sorted by length, retaining their original order within each length.  The
          312  + * return value is zero for a complete code set, negative for an over-
          313  + * subscribed code set, and positive for an incomplete code set.  The tables
          314  + * can be used if the return value is zero or positive, but they cannot be used
          315  + * if the return value is negative.  If the return value is zero, it is not
          316  + * possible for decode() using that table to return an error--any stream of
          317  + * enough bits will resolve to a symbol.  If the return value is positive, then
          318  + * it is possible for decode() using that table to return an error for received
          319  + * codes past the end of the incomplete lengths.
          320  + *
          321  + * Not used by decode(), but used for error checking, h->count[0] is the number
          322  + * of the n symbols not in the code.  So n - h->count[0] is the number of
          323  + * codes.  This is useful for checking for incomplete codes that have more than
          324  + * one symbol, which is an error in a dynamic block.
          325  + *
          326  + * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
          327  + * This is assured by the construction of the length arrays in dynamic() and
          328  + * fixed() and is not verified by construct().
          329  + *
          330  + * Format notes:
          331  + *
          332  + * - Permitted and expected examples of incomplete codes are one of the fixed
          333  + *   codes and any code with a single symbol which in deflate is coded as one
          334  + *   bit instead of zero bits.  See the format notes for fixed() and dynamic().
          335  + *
          336  + * - Within a given code length, the symbols are kept in ascending order for
          337  + *   the code bits definition.
          338  + */
          339  +local int construct(struct huffman *h, const short *length, int n)
          340  +{
          341  +    int symbol;         /* current symbol when stepping through length[] */
          342  +    int len;            /* current length when stepping through h->count[] */
          343  +    int left;           /* number of possible codes left of current length */
          344  +    short offs[MAXBITS+1];      /* offsets in symbol table for each length */
          345  +
          346  +    /* count number of codes of each length */
          347  +    for (len = 0; len <= MAXBITS; len++)
          348  +        h->count[len] = 0;
          349  +    for (symbol = 0; symbol < n; symbol++)
          350  +        (h->count[length[symbol]])++;   /* assumes lengths are within bounds */
          351  +    if (h->count[0] == n)               /* no codes! */
          352  +        return 0;                       /* complete, but decode() will fail */
          353  +
          354  +    /* check for an over-subscribed or incomplete set of lengths */
          355  +    left = 1;                           /* one possible code of zero length */
          356  +    for (len = 1; len <= MAXBITS; len++) {
          357  +        left <<= 1;                     /* one more bit, double codes left */
          358  +        left -= h->count[len];          /* deduct count from possible codes */
          359  +        if (left < 0)
          360  +            return left;                /* over-subscribed--return negative */
          361  +    }                                   /* left > 0 means incomplete */
          362  +
          363  +    /* generate offsets into symbol table for each length for sorting */
          364  +    offs[1] = 0;
          365  +    for (len = 1; len < MAXBITS; len++)
          366  +        offs[len + 1] = offs[len] + h->count[len];
          367  +
          368  +    /*
          369  +     * put symbols in table sorted by length, by symbol order within each
          370  +     * length
          371  +     */
          372  +    for (symbol = 0; symbol < n; symbol++)
          373  +        if (length[symbol] != 0)
          374  +            h->symbol[offs[length[symbol]]++] = symbol;
          375  +
          376  +    /* return zero for complete set, positive for incomplete set */
          377  +    return left;
          378  +}
          379  +
          380  +/*
          381  + * Decode literal/length and distance codes until an end-of-block code.
          382  + *
          383  + * Format notes:
          384  + *
          385  + * - Compressed data that is after the block type if fixed or after the code
          386  + *   description if dynamic is a combination of literals and length/distance
          387  + *   pairs terminated by and end-of-block code.  Literals are simply Huffman
          388  + *   coded bytes.  A length/distance pair is a coded length followed by a
          389  + *   coded distance to represent a string that occurs earlier in the
          390  + *   uncompressed data that occurs again at the current location.
          391  + *
          392  + * - Literals, lengths, and the end-of-block code are combined into a single
          393  + *   code of up to 286 symbols.  They are 256 literals (0..255), 29 length
          394  + *   symbols (257..285), and the end-of-block symbol (256).
          395  + *
          396  + * - There are 256 possible lengths (3..258), and so 29 symbols are not enough
          397  + *   to represent all of those.  Lengths 3..10 and 258 are in fact represented
          398  + *   by just a length symbol.  Lengths 11..257 are represented as a symbol and
          399  + *   some number of extra bits that are added as an integer to the base length
          400  + *   of the length symbol.  The number of extra bits is determined by the base
          401  + *   length symbol.  These are in the static arrays below, lens[] for the base
          402  + *   lengths and lext[] for the corresponding number of extra bits.
          403  + *
          404  + * - The reason that 258 gets its own symbol is that the longest length is used
          405  + *   often in highly redundant files.  Note that 258 can also be coded as the
          406  + *   base value 227 plus the maximum extra value of 31.  While a good deflate
          407  + *   should never do this, it is not an error, and should be decoded properly.
          408  + *
          409  + * - If a length is decoded, including its extra bits if any, then it is
          410  + *   followed a distance code.  There are up to 30 distance symbols.  Again
          411  + *   there are many more possible distances (1..32768), so extra bits are added
          412  + *   to a base value represented by the symbol.  The distances 1..4 get their
          413  + *   own symbol, but the rest require extra bits.  The base distances and
          414  + *   corresponding number of extra bits are below in the static arrays dist[]
          415  + *   and dext[].
          416  + *
          417  + * - Literal bytes are simply written to the output.  A length/distance pair is
          418  + *   an instruction to copy previously uncompressed bytes to the output.  The
          419  + *   copy is from distance bytes back in the output stream, copying for length
          420  + *   bytes.
          421  + *
          422  + * - Distances pointing before the beginning of the output data are not
          423  + *   permitted.
          424  + *
          425  + * - Overlapped copies, where the length is greater than the distance, are
          426  + *   allowed and common.  For example, a distance of one and a length of 258
          427  + *   simply copies the last byte 258 times.  A distance of four and a length of
          428  + *   twelve copies the last four bytes three times.  A simple forward copy
          429  + *   ignoring whether the length is greater than the distance or not implements
          430  + *   this correctly.  You should not use memcpy() since its behavior is not
          431  + *   defined for overlapped arrays.  You should not use memmove() or bcopy()
          432  + *   since though their behavior -is- defined for overlapping arrays, it is
          433  + *   defined to do the wrong thing in this case.
          434  + */
          435  +local int codes(struct state *s,
          436  +                const struct huffman *lencode,
          437  +                const struct huffman *distcode)
          438  +{
          439  +    int symbol;         /* decoded symbol */
          440  +    int len;            /* length for copy */
          441  +    unsigned dist;      /* distance for copy */
          442  +    static const short lens[29] = { /* Size base for length codes 257..285 */
          443  +        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
          444  +        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
          445  +    static const short lext[29] = { /* Extra bits for length codes 257..285 */
          446  +        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
          447  +        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
          448  +    static const short dists[30] = { /* Offset base for distance codes 0..29 */
          449  +        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
          450  +        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
          451  +        8193, 12289, 16385, 24577};
          452  +    static const short dext[30] = { /* Extra bits for distance codes 0..29 */
          453  +        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
          454  +        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
          455  +        12, 12, 13, 13};
          456  +
          457  +    /* decode literals and length/distance pairs */
          458  +    do {
          459  +        symbol = decode(s, lencode);
          460  +        if (symbol < 0)
          461  +            return symbol;              /* invalid symbol */
          462  +        if (symbol < 256) {             /* literal: symbol is the byte */
          463  +            /* write out the literal */
          464  +            if (s->out != NIL) {
          465  +                if (s->outcnt == s->outlen)
          466  +                    return 1;
          467  +                s->out[s->outcnt] = symbol;
          468  +            }
          469  +            s->outcnt++;
          470  +        }
          471  +        else if (symbol > 256) {        /* length */
          472  +            /* get and compute length */
          473  +            symbol -= 257;
          474  +            if (symbol >= 29)
          475  +                return -10;             /* invalid fixed code */
          476  +            len = lens[symbol] + bits(s, lext[symbol]);
          477  +
          478  +            /* get and check distance */
          479  +            symbol = decode(s, distcode);
          480  +            if (symbol < 0)
          481  +                return symbol;          /* invalid symbol */
          482  +            dist = dists[symbol] + bits(s, dext[symbol]);
          483  +#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
          484  +            if (dist > s->outcnt)
          485  +                return -11;     /* distance too far back */
          486  +#endif
          487  +
          488  +            /* copy length bytes from distance bytes back */
          489  +            if (s->out != NIL) {
          490  +                if (s->outcnt + len > s->outlen)
          491  +                    return 1;
          492  +                while (len--) {
          493  +                    s->out[s->outcnt] =
          494  +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
          495  +                        dist > s->outcnt ?
          496  +                            0 :
          497  +#endif
          498  +                            s->out[s->outcnt - dist];
          499  +                    s->outcnt++;
          500  +                }
          501  +            }
          502  +            else
          503  +                s->outcnt += len;
          504  +        }
          505  +    } while (symbol != 256);            /* end of block symbol */
          506  +
          507  +    /* done with a valid fixed or dynamic block */
          508  +    return 0;
          509  +}
          510  +
          511  +/*
          512  + * Process a fixed codes block.
          513  + *
          514  + * Format notes:
          515  + *
          516  + * - This block type can be useful for compressing small amounts of data for
          517  + *   which the size of the code descriptions in a dynamic block exceeds the
          518  + *   benefit of custom codes for that block.  For fixed codes, no bits are
          519  + *   spent on code descriptions.  Instead the code lengths for literal/length
          520  + *   codes and distance codes are fixed.  The specific lengths for each symbol
          521  + *   can be seen in the "for" loops below.
          522  + *
          523  + * - The literal/length code is complete, but has two symbols that are invalid
          524  + *   and should result in an error if received.  This cannot be implemented
          525  + *   simply as an incomplete code since those two symbols are in the "middle"
          526  + *   of the code.  They are eight bits long and the longest literal/length\
          527  + *   code is nine bits.  Therefore the code must be constructed with those
          528  + *   symbols, and the invalid symbols must be detected after decoding.
          529  + *
          530  + * - The fixed distance codes also have two invalid symbols that should result
          531  + *   in an error if received.  Since all of the distance codes are the same
          532  + *   length, this can be implemented as an incomplete code.  Then the invalid
          533  + *   codes are detected while decoding.
          534  + */
          535  +local int fixed(struct state *s)
          536  +{
          537  +    static int virgin = 1;
          538  +    static short lencnt[MAXBITS+1], lensym[FIXLCODES];
          539  +    static short distcnt[MAXBITS+1], distsym[MAXDCODES];
          540  +    static struct huffman lencode, distcode;
          541  +
          542  +    /* build fixed huffman tables if first call (may not be thread safe) */
          543  +    if (virgin) {
          544  +        int symbol;
          545  +        short lengths[FIXLCODES];
          546  +
          547  +        /* construct lencode and distcode */
          548  +        lencode.count = lencnt;
          549  +        lencode.symbol = lensym;
          550  +        distcode.count = distcnt;
          551  +        distcode.symbol = distsym;
          552  +
          553  +        /* literal/length table */
          554  +        for (symbol = 0; symbol < 144; symbol++)
          555  +            lengths[symbol] = 8;
          556  +        for (; symbol < 256; symbol++)
          557  +            lengths[symbol] = 9;
          558  +        for (; symbol < 280; symbol++)
          559  +            lengths[symbol] = 7;
          560  +        for (; symbol < FIXLCODES; symbol++)
          561  +            lengths[symbol] = 8;
          562  +        construct(&lencode, lengths, FIXLCODES);
          563  +
          564  +        /* distance table */
          565  +        for (symbol = 0; symbol < MAXDCODES; symbol++)
          566  +            lengths[symbol] = 5;
          567  +        construct(&distcode, lengths, MAXDCODES);
          568  +
          569  +        /* do this just once */
          570  +        virgin = 0;
          571  +    }
          572  +
          573  +    /* decode data until end-of-block code */
          574  +    return codes(s, &lencode, &distcode);
          575  +}
          576  +
          577  +/*
          578  + * Process a dynamic codes block.
          579  + *
          580  + * Format notes:
          581  + *
          582  + * - A dynamic block starts with a description of the literal/length and
          583  + *   distance codes for that block.  New dynamic blocks allow the compressor to
          584  + *   rapidly adapt to changing data with new codes optimized for that data.
          585  + *
          586  + * - The codes used by the deflate format are "canonical", which means that
          587  + *   the actual bits of the codes are generated in an unambiguous way simply
          588  + *   from the number of bits in each code.  Therefore the code descriptions
          589  + *   are simply a list of code lengths for each symbol.
          590  + *
          591  + * - The code lengths are stored in order for the symbols, so lengths are
          592  + *   provided for each of the literal/length symbols, and for each of the
          593  + *   distance symbols.
          594  + *
          595  + * - If a symbol is not used in the block, this is represented by a zero as
          596  + *   as the code length.  This does not mean a zero-length code, but rather
          597  + *   that no code should be created for this symbol.  There is no way in the
          598  + *   deflate format to represent a zero-length code.
          599  + *
          600  + * - The maximum number of bits in a code is 15, so the possible lengths for
          601  + *   any code are 1..15.
          602  + *
          603  + * - The fact that a length of zero is not permitted for a code has an
          604  + *   interesting consequence.  Normally if only one symbol is used for a given
          605  + *   code, then in fact that code could be represented with zero bits.  However
          606  + *   in deflate, that code has to be at least one bit.  So for example, if
          607  + *   only a single distance base symbol appears in a block, then it will be
          608  + *   represented by a single code of length one, in particular one 0 bit.  This
          609  + *   is an incomplete code, since if a 1 bit is received, it has no meaning,
          610  + *   and should result in an error.  So incomplete distance codes of one symbol
          611  + *   should be permitted, and the receipt of invalid codes should be handled.
          612  + *
          613  + * - It is also possible to have a single literal/length code, but that code
          614  + *   must be the end-of-block code, since every dynamic block has one.  This
          615  + *   is not the most efficient way to create an empty block (an empty fixed
          616  + *   block is fewer bits), but it is allowed by the format.  So incomplete
          617  + *   literal/length codes of one symbol should also be permitted.
          618  + *
          619  + * - If there are only literal codes and no lengths, then there are no distance
          620  + *   codes.  This is represented by one distance code with zero bits.
          621  + *
          622  + * - The list of up to 286 length/literal lengths and up to 30 distance lengths
          623  + *   are themselves compressed using Huffman codes and run-length encoding.  In
          624  + *   the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
          625  + *   that length, and the symbols 16, 17, and 18 are run-length instructions.
          626  + *   Each of 16, 17, and 18 are follwed by extra bits to define the length of
          627  + *   the run.  16 copies the last length 3 to 6 times.  17 represents 3 to 10
          628  + *   zero lengths, and 18 represents 11 to 138 zero lengths.  Unused symbols
          629  + *   are common, hence the special coding for zero lengths.
          630  + *
          631  + * - The symbols for 0..18 are Huffman coded, and so that code must be
          632  + *   described first.  This is simply a sequence of up to 19 three-bit values
          633  + *   representing no code (0) or the code length for that symbol (1..7).
          634  + *
          635  + * - A dynamic block starts with three fixed-size counts from which is computed
          636  + *   the number of literal/length code lengths, the number of distance code
          637  + *   lengths, and the number of code length code lengths (ok, you come up with
          638  + *   a better name!) in the code descriptions.  For the literal/length and
          639  + *   distance codes, lengths after those provided are considered zero, i.e. no
          640  + *   code.  The code length code lengths are received in a permuted order (see
          641  + *   the order[] array below) to make a short code length code length list more
          642  + *   likely.  As it turns out, very short and very long codes are less likely
          643  + *   to be seen in a dynamic code description, hence what may appear initially
          644  + *   to be a peculiar ordering.
          645  + *
          646  + * - Given the number of literal/length code lengths (nlen) and distance code
          647  + *   lengths (ndist), then they are treated as one long list of nlen + ndist
          648  + *   code lengths.  Therefore run-length coding can and often does cross the
          649  + *   boundary between the two sets of lengths.
          650  + *
          651  + * - So to summarize, the code description at the start of a dynamic block is
          652  + *   three counts for the number of code lengths for the literal/length codes,
          653  + *   the distance codes, and the code length codes.  This is followed by the
          654  + *   code length code lengths, three bits each.  This is used to construct the
          655  + *   code length code which is used to read the remainder of the lengths.  Then
          656  + *   the literal/length code lengths and distance lengths are read as a single
          657  + *   set of lengths using the code length codes.  Codes are constructed from
          658  + *   the resulting two sets of lengths, and then finally you can start
          659  + *   decoding actual compressed data in the block.
          660  + *
          661  + * - For reference, a "typical" size for the code description in a dynamic
          662  + *   block is around 80 bytes.
          663  + */
          664  +local int dynamic(struct state *s)
          665  +{
          666  +    int nlen, ndist, ncode;             /* number of lengths in descriptor */
          667  +    int index;                          /* index of lengths[] */
          668  +    int err;                            /* construct() return value */
          669  +    short lengths[MAXCODES];            /* descriptor code lengths */
          670  +    short lencnt[MAXBITS+1], lensym[MAXLCODES];         /* lencode memory */
          671  +    short distcnt[MAXBITS+1], distsym[MAXDCODES];       /* distcode memory */
          672  +    struct huffman lencode, distcode;   /* length and distance codes */
          673  +    static const short order[19] =      /* permutation of code length codes */
          674  +        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
          675  +
          676  +    /* construct lencode and distcode */
          677  +    lencode.count = lencnt;
          678  +    lencode.symbol = lensym;
          679  +    distcode.count = distcnt;
          680  +    distcode.symbol = distsym;
          681  +
          682  +    /* get number of lengths in each table, check lengths */
          683  +    nlen = bits(s, 5) + 257;
          684  +    ndist = bits(s, 5) + 1;
          685  +    ncode = bits(s, 4) + 4;
          686  +    if (nlen > MAXLCODES || ndist > MAXDCODES)
          687  +        return -3;                      /* bad counts */
          688  +
          689  +    /* read code length code lengths (really), missing lengths are zero */
          690  +    for (index = 0; index < ncode; index++)
          691  +        lengths[order[index]] = bits(s, 3);
          692  +    for (; index < 19; index++)
          693  +        lengths[order[index]] = 0;
          694  +
          695  +    /* build huffman table for code lengths codes (use lencode temporarily) */
          696  +    err = construct(&lencode, lengths, 19);
          697  +    if (err != 0)               /* require complete code set here */
          698  +        return -4;
          699  +
          700  +    /* read length/literal and distance code length tables */
          701  +    index = 0;
          702  +    while (index < nlen + ndist) {
          703  +        int symbol;             /* decoded value */
          704  +        int len;                /* last length to repeat */
          705  +
          706  +        symbol = decode(s, &lencode);
          707  +        if (symbol < 16)                /* length in 0..15 */
          708  +            lengths[index++] = symbol;
          709  +        else {                          /* repeat instruction */
          710  +            len = 0;                    /* assume repeating zeros */
          711  +            if (symbol == 16) {         /* repeat last length 3..6 times */
          712  +                if (index == 0)
          713  +                    return -5;          /* no last length! */
          714  +                len = lengths[index - 1];       /* last length */
          715  +                symbol = 3 + bits(s, 2);
          716  +            }
          717  +            else if (symbol == 17)      /* repeat zero 3..10 times */
          718  +                symbol = 3 + bits(s, 3);
          719  +            else                        /* == 18, repeat zero 11..138 times */
          720  +                symbol = 11 + bits(s, 7);
          721  +            if (index + symbol > nlen + ndist)
          722  +                return -6;              /* too many lengths! */
          723  +            while (symbol--)            /* repeat last or zero symbol times */
          724  +                lengths[index++] = len;
          725  +        }
          726  +    }
          727  +
          728  +    /* check for end-of-block code -- there better be one! */
          729  +    if (lengths[256] == 0)
          730  +        return -9;
          731  +
          732  +    /* build huffman table for literal/length codes */
          733  +    err = construct(&lencode, lengths, nlen);
          734  +    if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
          735  +        return -7;      /* incomplete code ok only for single length 1 code */
          736  +
          737  +    /* build huffman table for distance codes */
          738  +    err = construct(&distcode, lengths + nlen, ndist);
          739  +    if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
          740  +        return -8;      /* incomplete code ok only for single length 1 code */
          741  +
          742  +    /* decode data until end-of-block code */
          743  +    return codes(s, &lencode, &distcode);
          744  +}
          745  +
          746  +/*
          747  + * Inflate source to dest.  On return, destlen and sourcelen are updated to the
          748  + * size of the uncompressed data and the size of the deflate data respectively.
          749  + * On success, the return value of puff() is zero.  If there is an error in the
          750  + * source data, i.e. it is not in the deflate format, then a negative value is
          751  + * returned.  If there is not enough input available or there is not enough
          752  + * output space, then a positive error is returned.  In that case, destlen and
          753  + * sourcelen are not updated to facilitate retrying from the beginning with the
          754  + * provision of more input data or more output space.  In the case of invalid
          755  + * inflate data (a negative error), the dest and source pointers are updated to
          756  + * facilitate the debugging of deflators.
          757  + *
          758  + * puff() also has a mode to determine the size of the uncompressed output with
          759  + * no output written.  For this dest must be (unsigned char *)0.  In this case,
          760  + * the input value of *destlen is ignored, and on return *destlen is set to the
          761  + * size of the uncompressed output.
          762  + *
          763  + * The return codes are:
          764  + *
          765  + *   2:  available inflate data did not terminate
          766  + *   1:  output space exhausted before completing inflate
          767  + *   0:  successful inflate
          768  + *  -1:  invalid block type (type == 3)
          769  + *  -2:  stored block length did not match one's complement
          770  + *  -3:  dynamic block code description: too many length or distance codes
          771  + *  -4:  dynamic block code description: code lengths codes incomplete
          772  + *  -5:  dynamic block code description: repeat lengths with no first length
          773  + *  -6:  dynamic block code description: repeat more than specified lengths
          774  + *  -7:  dynamic block code description: invalid literal/length code lengths
          775  + *  -8:  dynamic block code description: invalid distance code lengths
          776  + *  -9:  dynamic block code description: missing end-of-block code
          777  + * -10:  invalid literal/length or distance code in fixed or dynamic block
          778  + * -11:  distance is too far back in fixed or dynamic block
          779  + *
          780  + * Format notes:
          781  + *
          782  + * - Three bits are read for each block to determine the kind of block and
          783  + *   whether or not it is the last block.  Then the block is decoded and the
          784  + *   process repeated if it was not the last block.
          785  + *
          786  + * - The leftover bits in the last byte of the deflate data after the last
          787  + *   block (if it was a fixed or dynamic block) are undefined and have no
          788  + *   expected values to check.
          789  + */
          790  +int puff(unsigned char *dest,           /* pointer to destination pointer */
          791  +         unsigned long *destlen,        /* amount of output space */
          792  +         const unsigned char *source,   /* pointer to source data pointer */
          793  +         unsigned long *sourcelen)      /* amount of input available */
          794  +{
          795  +    struct state s;             /* input/output state */
          796  +    int last, type;             /* block information */
          797  +    int err;                    /* return value */
          798  +
          799  +    /* initialize output state */
          800  +    s.out = dest;
          801  +    s.outlen = *destlen;                /* ignored if dest is NIL */
          802  +    s.outcnt = 0;
          803  +
          804  +    /* initialize input state */
          805  +    s.in = source;
          806  +    s.inlen = *sourcelen;
          807  +    s.incnt = 0;
          808  +    s.bitbuf = 0;
          809  +    s.bitcnt = 0;
          810  +
          811  +    /* return if bits() or decode() tries to read past available input */
          812  +    if (setjmp(s.env) != 0)             /* if came back here via longjmp() */
          813  +        err = 2;                        /* then skip do-loop, return error */
          814  +    else {
          815  +        /* process blocks until last block or error */
          816  +        do {
          817  +            last = bits(&s, 1);         /* one if last block */
          818  +            type = bits(&s, 2);         /* block type 0..3 */
          819  +            err = type == 0 ?
          820  +                    stored(&s) :
          821  +                    (type == 1 ?
          822  +                        fixed(&s) :
          823  +                        (type == 2 ?
          824  +                            dynamic(&s) :
          825  +                            -1));       /* type == 3, invalid */
          826  +            if (err != 0)
          827  +                break;                  /* return with error */
          828  +        } while (!last);
          829  +    }
          830  +
          831  +    /* update the lengths and return */
          832  +    if (err <= 0) {
          833  +        *destlen = s.outcnt;
          834  +        *sourcelen = s.incnt;
          835  +    }
          836  +    return err;
          837  +}

Added compat/zlib/contrib/puff/puff.h.

            1  +/* puff.h
            2  +  Copyright (C) 2002-2010 Mark Adler, all rights reserved
            3  +  version 2.2, 25 Apr 2010
            4  +
            5  +  This software is provided 'as-is', without any express or implied
            6  +  warranty.  In no event will the author be held liable for any damages
            7  +  arising from the use of this software.
            8  +
            9  +  Permission is granted to anyone to use this software for any purpose,
           10  +  including commercial applications, and to alter it and redistribute it
           11  +  freely, subject to the following restrictions:
           12  +
           13  +  1. The origin of this software must not be misrepresented; you must not
           14  +     claim that you wrote the original software. If you use this software
           15  +     in a product, an acknowledgment in the product documentation would be
           16  +     appreciated but is not required.
           17  +  2. Altered source versions must be plainly marked as such, and must not be
           18  +     misrepresented as being the original software.
           19  +  3. This notice may not be removed or altered from any source distribution.
           20  +
           21  +  Mark Adler    madler@alumni.caltech.edu
           22  + */
           23  +
           24  +
           25  +/*
           26  + * See puff.c for purpose and usage.
           27  + */
           28  +#ifndef NIL
           29  +#  define NIL ((unsigned char *)0)      /* for no output option */
           30  +#endif
           31  +
           32  +int puff(unsigned char *dest,           /* pointer to destination pointer */
           33  +         unsigned long *destlen,        /* amount of output space */
           34  +         const unsigned char *source,   /* pointer to source data pointer */
           35  +         unsigned long *sourcelen);     /* amount of input available */

Added compat/zlib/contrib/puff/pufftest.c.

            1  +/*
            2  + * pufftest.c
            3  + * Copyright (C) 2002-2010 Mark Adler
            4  + * For conditions of distribution and use, see copyright notice in puff.h
            5  + * version 2.2, 25 Apr 2010
            6  + */
            7  +
            8  +/* Example of how to use puff().
            9  +
           10  +   Usage: puff [-w] [-f] [-nnn] file
           11  +          ... | puff [-w] [-f] [-nnn]
           12  +
           13  +   where file is the input file with deflate data, nnn is the number of bytes
           14  +   of input to skip before inflating (e.g. to skip a zlib or gzip header), and
           15  +   -w is used to write the decompressed data to stdout.  -f is for coverage
           16  +   testing, and causes pufftest to fail with not enough output space (-f does
           17  +   a write like -w, so -w is not required). */
           18  +
           19  +#include <stdio.h>
           20  +#include <stdlib.h>
           21  +#include "puff.h"
           22  +
           23  +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
           24  +#  include <fcntl.h>
           25  +#  include <io.h>
           26  +#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
           27  +#else
           28  +#  define SET_BINARY_MODE(file)
           29  +#endif
           30  +
           31  +#define local static
           32  +
           33  +/* Return size times approximately the cube root of 2, keeping the result as 1,
           34  +   3, or 5 times a power of 2 -- the result is always > size, until the result
           35  +   is the maximum value of an unsigned long, where it remains.  This is useful
           36  +   to keep reallocations less than ~33% over the actual data. */
           37  +local size_t bythirds(size_t size)
           38  +{
           39  +    int n;
           40  +    size_t m;
           41  +
           42  +    m = size;
           43  +    for (n = 0; m; n++)
           44  +        m >>= 1;
           45  +    if (n < 3)
           46  +        return size + 1;
           47  +    n -= 3;
           48  +    m = size >> n;
           49  +    m += m == 6 ? 2 : 1;
           50  +    m <<= n;
           51  +    return m > size ? m : (size_t)(-1);
           52  +}
           53  +
           54  +/* Read the input file *name, or stdin if name is NULL, into allocated memory.
           55  +   Reallocate to larger buffers until the entire file is read in.  Return a
           56  +   pointer to the allocated data, or NULL if there was a memory allocation
           57  +   failure.  *len is the number of bytes of data read from the input file (even
           58  +   if load() returns NULL).  If the input file was empty or could not be opened
           59  +   or read, *len is zero. */
           60  +local void *load(const char *name, size_t *len)
           61  +{
           62  +    size_t size;
           63  +    void *buf, *swap;
           64  +    FILE *in;
           65  +
           66  +    *len = 0;
           67  +    buf = malloc(size = 4096);
           68  +    if (buf == NULL)
           69  +        return NULL;
           70  +    in = name == NULL ? stdin : fopen(name, "rb");
           71  +    if (in != NULL) {
           72  +        for (;;) {
           73  +            *len += fread((char *)buf + *len, 1, size - *len, in);
           74  +            if (*len < size) break;
           75  +            size = bythirds(size);
           76  +            if (size == *len || (swap = realloc(buf, size)) == NULL) {
           77  +                free(buf);
           78  +                buf = NULL;
           79  +                break;
           80  +            }
           81  +            buf = swap;
           82  +        }
           83  +        fclose(in);
           84  +    }
           85  +    return buf;
           86  +}
           87  +
           88  +int main(int argc, char **argv)
           89  +{
           90  +    int ret, put = 0, fail = 0;
           91  +    unsigned skip = 0;
           92  +    char *arg, *name = NULL;
           93  +    unsigned char *source = NULL, *dest;
           94  +    size_t len = 0;
           95  +    unsigned long sourcelen, destlen;
           96  +
           97  +    /* process arguments */
           98  +    while (arg = *++argv, --argc)
           99  +        if (arg[0] == '-') {
          100  +            if (arg[1] == 'w' && arg[2] == 0)
          101  +                put = 1;
          102  +            else if (arg[1] == 'f' && arg[2] == 0)
          103  +                fail = 1, put = 1;
          104  +            else if (arg[1] >= '0' && arg[1] <= '9')
          105  +                skip = (unsigned)atoi(arg + 1);
          106  +            else {
          107  +                fprintf(stderr, "invalid option %s\n", arg);
          108  +                return 3;
          109  +            }
          110  +        }
          111  +        else if (name != NULL) {
          112  +            fprintf(stderr, "only one file name allowed\n");
          113  +            return 3;
          114  +        }
          115  +        else
          116  +            name = arg;
          117  +    source = load(name, &len);
          118  +    if (source == NULL) {
          119  +        fprintf(stderr, "memory allocation failure\n");
          120  +        return 4;
          121  +    }
          122  +    if (len == 0) {
          123  +        fprintf(stderr, "could not read %s, or it was empty\n",
          124  +                name == NULL ? "<stdin>" : name);
          125  +        free(source);
          126  +        return 3;
          127  +    }
          128  +    if (skip >= len) {
          129  +        fprintf(stderr, "skip request of %d leaves no input\n", skip);
          130  +        free(source);
          131  +        return 3;
          132  +    }
          133  +
          134  +    /* test inflate data with offset skip */
          135  +    len -= skip;
          136  +    sourcelen = (unsigned long)len;
          137  +    ret = puff(NIL, &destlen, source + skip, &sourcelen);
          138  +    if (ret)
          139  +        fprintf(stderr, "puff() failed with return code %d\n", ret);
          140  +    else {
          141  +        fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen);
          142  +        if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n",
          143  +                                     len - sourcelen);
          144  +    }
          145  +
          146  +    /* if requested, inflate again and write decompressd data to stdout */
          147  +    if (put && ret == 0) {
          148  +        if (fail)
          149  +            destlen >>= 1;
          150  +        dest = malloc(destlen);
          151  +        if (dest == NULL) {
          152  +            fprintf(stderr, "memory allocation failure\n");
          153  +            free(source);
          154  +            return 4;
          155  +        }
          156  +        puff(dest, &destlen, source + skip, &sourcelen);
          157  +        SET_BINARY_MODE(stdout);
          158  +        fwrite(dest, 1, destlen, stdout);
          159  +        free(dest);
          160  +    }
          161  +
          162  +    /* clean up */
          163  +    free(source);
          164  +    return ret;
          165  +}

Added compat/zlib/contrib/puff/zeros.raw.

cannot compute difference between binary files

Added compat/zlib/contrib/testzlib/testzlib.c.

            1  +#include <stdio.h>
            2  +#include <stdlib.h>
            3  +#include <windows.h>
            4  +
            5  +#include "zlib.h"
            6  +
            7  +
            8  +void MyDoMinus64(LARGE_INTEGER *R,LARGE_INTEGER A,LARGE_INTEGER B)
            9  +{
           10  +    R->HighPart = A.HighPart - B.HighPart;
           11  +    if (A.LowPart >= B.LowPart)
           12  +        R->LowPart = A.LowPart - B.LowPart;
           13  +    else
           14  +    {
           15  +        R->LowPart = A.LowPart - B.LowPart;
           16  +        R->HighPart --;
           17  +    }
           18  +}
           19  +
           20  +#ifdef _M_X64
           21  +// see http://msdn2.microsoft.com/library/twchhe95(en-us,vs.80).aspx for __rdtsc
           22  +unsigned __int64 __rdtsc(void);
           23  +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64)
           24  +{
           25  + //   printf("rdtsc = %I64x\n",__rdtsc());
           26  +   pbeginTime64->QuadPart=__rdtsc();
           27  +}
           28  +
           29  +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf)
           30  +{
           31  +    LARGE_INTEGER LIres;
           32  +    unsigned _int64 res=__rdtsc()-((unsigned _int64)(beginTime64.QuadPart));
           33  +    LIres.QuadPart=res;
           34  +   // printf("rdtsc = %I64x\n",__rdtsc());
           35  +    return LIres;
           36  +}
           37  +#else
           38  +#ifdef _M_IX86
           39  +void myGetRDTSC32(LARGE_INTEGER * pbeginTime64)
           40  +{
           41  +    DWORD dwEdx,dwEax;
           42  +    _asm
           43  +    {
           44  +        rdtsc
           45  +        mov dwEax,eax
           46  +        mov dwEdx,edx
           47  +    }
           48  +    pbeginTime64->LowPart=dwEax;
           49  +    pbeginTime64->HighPart=dwEdx;
           50  +}
           51  +
           52  +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64)
           53  +{
           54  +    myGetRDTSC32(pbeginTime64);
           55  +}
           56  +
           57  +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf)
           58  +{
           59  +    LARGE_INTEGER LIres,endTime64;
           60  +    myGetRDTSC32(&endTime64);
           61  +
           62  +    LIres.LowPart=LIres.HighPart=0;
           63  +    MyDoMinus64(&LIres,endTime64,beginTime64);
           64  +    return LIres;
           65  +}
           66  +#else
           67  +void myGetRDTSC32(LARGE_INTEGER * pbeginTime64)
           68  +{
           69  +}
           70  +
           71  +void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64)
           72  +{
           73  +}
           74  +
           75  +LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf)
           76  +{
           77  +    LARGE_INTEGER lr;
           78  +    lr.QuadPart=0;
           79  +    return lr;
           80  +}
           81  +#endif
           82  +#endif
           83  +
           84  +void BeginCountPerfCounter(LARGE_INTEGER * pbeginTime64,BOOL fComputeTimeQueryPerf)
           85  +{
           86  +    if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(pbeginTime64)))
           87  +    {
           88  +        pbeginTime64->LowPart = GetTickCount();
           89  +        pbeginTime64->HighPart = 0;
           90  +    }
           91  +}
           92  +
           93  +DWORD GetMsecSincePerfCounter(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf)
           94  +{
           95  +    LARGE_INTEGER endTime64,ticksPerSecond,ticks;
           96  +    DWORDLONG ticksShifted,tickSecShifted;
           97  +    DWORD dwLog=16+0;
           98  +    DWORD dwRet;
           99  +    if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(&endTime64)))
          100  +        dwRet = (GetTickCount() - beginTime64.LowPart)*1;
          101  +    else
          102  +    {
          103  +        MyDoMinus64(&ticks,endTime64,beginTime64);
          104  +        QueryPerformanceFrequency(&ticksPerSecond);
          105  +
          106  +
          107  +        {
          108  +            ticksShifted = Int64ShrlMod32(*(DWORDLONG*)&ticks,dwLog);
          109  +            tickSecShifted = Int64ShrlMod32(*(DWORDLONG*)&ticksPerSecond,dwLog);
          110  +
          111  +        }
          112  +
          113  +        dwRet = (DWORD)((((DWORD)ticksShifted)*1000)/(DWORD)(tickSecShifted));
          114  +        dwRet *=1;
          115  +    }
          116  +    return dwRet;
          117  +}
          118  +
          119  +int ReadFileMemory(const char* filename,long* plFileSize,void** pFilePtr)
          120  +{
          121  +    FILE* stream;
          122  +    void* ptr;
          123  +    int retVal=1;
          124  +    stream=fopen(filename, "rb");
          125  +    if (stream==NULL)
          126  +        return 0;
          127  +
          128  +    fseek(stream,0,SEEK_END);
          129  +
          130  +    *plFileSize=ftell(stream);
          131  +    fseek(stream,0,SEEK_SET);
          132  +    ptr=malloc((*plFileSize)+1);
          133  +    if (ptr==NULL)
          134  +        retVal=0;
          135  +    else
          136  +    {
          137  +        if (fread(ptr, 1, *plFileSize,stream) != (*plFileSize))
          138  +            retVal=0;
          139  +    }
          140  +    fclose(stream);
          141  +    *pFilePtr=ptr;
          142  +    return retVal;
          143  +}
          144  +
          145  +int main(int argc, char *argv[])
          146  +{
          147  +    int BlockSizeCompress=0x8000;
          148  +    int BlockSizeUncompress=0x8000;
          149  +    int cprLevel=Z_DEFAULT_COMPRESSION ;
          150  +    long lFileSize;
          151  +    unsigned char* FilePtr;
          152  +    long lBufferSizeCpr;
          153  +    long lBufferSizeUncpr;
          154  +    long lCompressedSize=0;
          155  +    unsigned char* CprPtr;
          156  +    unsigned char* UncprPtr;
          157  +    long lSizeCpr,lSizeUncpr;
          158  +    DWORD dwGetTick,dwMsecQP;
          159  +    LARGE_INTEGER li_qp,li_rdtsc,dwResRdtsc;
          160  +
          161  +    if (argc<=1)
          162  +    {
          163  +        printf("run TestZlib <File> [BlockSizeCompress] [BlockSizeUncompress] [compres. level]\n");
          164  +        return 0;
          165  +    }
          166  +
          167  +    if (ReadFileMemory(argv[1],&lFileSize,&FilePtr)==0)
          168  +    {
          169  +        printf("error reading %s\n",argv[1]);
          170  +        return 1;
          171  +    }
          172  +    else printf("file %s read, %u bytes\n",argv[1],lFileSize);
          173  +
          174  +    if (argc>=3)
          175  +        BlockSizeCompress=atol(argv[2]);
          176  +
          177  +    if (argc>=4)
          178  +        BlockSizeUncompress=atol(argv[3]);
          179  +
          180  +    if (argc>=5)
          181  +        cprLevel=(int)atol(argv[4]);
          182  +
          183  +    lBufferSizeCpr = lFileSize + (lFileSize/0x10) + 0x200;
          184  +    lBufferSizeUncpr = lBufferSizeCpr;
          185  +
          186  +    CprPtr=(unsigned char*)malloc(lBufferSizeCpr + BlockSizeCompress);
          187  +
          188  +    BeginCountPerfCounter(&li_qp,TRUE);
          189  +    dwGetTick=GetTickCount();
          190  +    BeginCountRdtsc(&li_rdtsc);
          191  +    {
          192  +        z_stream zcpr;
          193  +        int ret=Z_OK;
          194  +        long lOrigToDo = lFileSize;
          195  +        long lOrigDone = 0;
          196  +        int step=0;
          197  +        memset(&zcpr,0,sizeof(z_stream));
          198  +        deflateInit(&zcpr,cprLevel);
          199  +
          200  +        zcpr.next_in = FilePtr;
          201  +        zcpr.next_out = CprPtr;
          202  +
          203  +
          204  +        do
          205  +        {
          206  +            long all_read_before = zcpr.total_in;
          207  +            zcpr.avail_in = min(lOrigToDo,BlockSizeCompress);
          208  +            zcpr.avail_out = BlockSizeCompress;
          209  +            ret=deflate(&zcpr,(zcpr.avail_in==lOrigToDo) ? Z_FINISH : Z_SYNC_FLUSH);
          210  +            lOrigDone += (zcpr.total_in-all_read_before);
          211  +            lOrigToDo -= (zcpr.total_in-all_read_before);
          212  +            step++;
          213  +        } while (ret==Z_OK);
          214  +
          215  +        lSizeCpr=zcpr.total_out;
          216  +        deflateEnd(&zcpr);
          217  +        dwGetTick=GetTickCount()-dwGetTick;
          218  +        dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE);
          219  +        dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE);
          220  +        printf("total compress size = %u, in %u step\n",lSizeCpr,step);
          221  +        printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.);
          222  +        printf("defcpr time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.);
          223  +        printf("defcpr result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart);
          224  +    }
          225  +
          226  +    CprPtr=(unsigned char*)realloc(CprPtr,lSizeCpr);
          227  +    UncprPtr=(unsigned char*)malloc(lBufferSizeUncpr + BlockSizeUncompress);
          228  +
          229  +    BeginCountPerfCounter(&li_qp,TRUE);
          230  +    dwGetTick=GetTickCount();
          231  +    BeginCountRdtsc(&li_rdtsc);
          232  +    {
          233  +        z_stream zcpr;
          234  +        int ret=Z_OK;
          235  +        long lOrigToDo = lSizeCpr;
          236  +        long lOrigDone = 0;
          237  +        int step=0;
          238  +        memset(&zcpr,0,sizeof(z_stream));
          239  +        inflateInit(&zcpr);
          240  +
          241  +        zcpr.next_in = CprPtr;
          242  +        zcpr.next_out = UncprPtr;
          243  +
          244  +
          245  +        do
          246  +        {
          247  +            long all_read_before = zcpr.total_in;
          248  +            zcpr.avail_in = min(lOrigToDo,BlockSizeUncompress);
          249  +            zcpr.avail_out = BlockSizeUncompress;
          250  +            ret=inflate(&zcpr,Z_SYNC_FLUSH);
          251  +            lOrigDone += (zcpr.total_in-all_read_before);
          252  +            lOrigToDo -= (zcpr.total_in-all_read_before);
          253  +            step++;
          254  +        } while (ret==Z_OK);
          255  +
          256  +        lSizeUncpr=zcpr.total_out;
          257  +        inflateEnd(&zcpr);
          258  +        dwGetTick=GetTickCount()-dwGetTick;
          259  +        dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE);
          260  +        dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE);
          261  +        printf("total uncompress size = %u, in %u step\n",lSizeUncpr,step);
          262  +        printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.);
          263  +        printf("uncpr  time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.);
          264  +        printf("uncpr  result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart);
          265  +    }
          266  +
          267  +    if (lSizeUncpr==lFileSize)
          268  +    {
          269  +        if (memcmp(FilePtr,UncprPtr,lFileSize)==0)
          270  +            printf("compare ok\n");
          271  +
          272  +    }
          273  +
          274  +    return 0;
          275  +}

Added compat/zlib/contrib/testzlib/testzlib.txt.

            1  +To build testzLib with Visual Studio 2005:
            2  +
            3  +copy to a directory file from :
            4  +- root of zLib tree
            5  +- contrib/testzlib
            6  +- contrib/masmx86
            7  +- contrib/masmx64
            8  +- contrib/vstudio/vc7
            9  +
           10  +and open testzlib8.sln

Added compat/zlib/contrib/untgz/Makefile.

            1  +CC=cc
            2  +CFLAGS=-g
            3  +
            4  +untgz: untgz.o ../../libz.a
            5  +	$(CC) $(CFLAGS) -o untgz untgz.o -L../.. -lz
            6  +
            7  +untgz.o: untgz.c ../../zlib.h
            8  +	$(CC) $(CFLAGS) -c -I../.. untgz.c
            9  +
           10  +../../libz.a:
           11  +	cd ../..; ./configure; make
           12  +
           13  +clean:
           14  +	rm -f untgz untgz.o *~

Added compat/zlib/contrib/untgz/Makefile.msc.

            1  +CC=cl
            2  +CFLAGS=-MD
            3  +
            4  +untgz.exe: untgz.obj ..\..\zlib.lib
            5  +	$(CC) $(CFLAGS) untgz.obj ..\..\zlib.lib
            6  +
            7  +untgz.obj: untgz.c ..\..\zlib.h
            8  +	$(CC) $(CFLAGS) -c -I..\.. untgz.c
            9  +
           10  +..\..\zlib.lib:
           11  +	cd ..\..
           12  +	$(MAKE) -f win32\makefile.msc
           13  +	cd contrib\untgz
           14  +
           15  +clean:
           16  +	-del untgz.obj
           17  +	-del untgz.exe

Added compat/zlib/contrib/untgz/untgz.c.

            1  +/*
            2  + * untgz.c -- Display contents and extract files from a gzip'd TAR file
            3  + *
            4  + * written by Pedro A. Aranda Gutierrez <paag@tid.es>
            5  + * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
            6  + * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
            7  + */
            8  +
            9  +#include <stdio.h>
           10  +#include <stdlib.h>
           11  +#include <string.h>
           12  +#include <time.h>
           13  +#include <errno.h>
           14  +
           15  +#include "zlib.h"
           16  +
           17  +#ifdef unix
           18  +#  include <unistd.h>
           19  +#else
           20  +#  include <direct.h>
           21  +#  include <io.h>
           22  +#endif
           23  +
           24  +#ifdef WIN32
           25  +#include <windows.h>
           26  +#  ifndef F_OK
           27  +#    define F_OK  0
           28  +#  endif
           29  +#  define mkdir(dirname,mode)   _mkdir(dirname)
           30  +#  ifdef _MSC_VER
           31  +#    define access(path,mode)   _access(path,mode)
           32  +#    define chmod(path,mode)    _chmod(path,mode)
           33  +#    define strdup(str)         _strdup(str)
           34  +#  endif
           35  +#else
           36  +#  include <utime.h>
           37  +#endif
           38  +
           39  +
           40  +/* values used in typeflag field */
           41  +
           42  +#define REGTYPE  '0'            /* regular file */
           43  +#define AREGTYPE '\0'           /* regular file */
           44  +#define LNKTYPE  '1'            /* link */
           45  +#define SYMTYPE  '2'            /* reserved */
           46  +#define CHRTYPE  '3'            /* character special */
           47  +#define BLKTYPE  '4'            /* block special */
           48  +#define DIRTYPE  '5'            /* directory */
           49  +#define FIFOTYPE '6'            /* FIFO special */
           50  +#define CONTTYPE '7'            /* reserved */
           51  +
           52  +/* GNU tar extensions */
           53  +
           54  +#define GNUTYPE_DUMPDIR  'D'    /* file names from dumped directory */
           55  +#define GNUTYPE_LONGLINK 'K'    /* long link name */
           56  +#define GNUTYPE_LONGNAME 'L'    /* long file name */
           57  +#define GNUTYPE_MULTIVOL 'M'    /* continuation of file from another volume */
           58  +#define GNUTYPE_NAMES    'N'    /* file name that does not fit into main hdr */
           59  +#define GNUTYPE_SPARSE   'S'    /* sparse file */
           60  +#define GNUTYPE_VOLHDR   'V'    /* tape/volume header */
           61  +
           62  +
           63  +/* tar header */
           64  +
           65  +#define BLOCKSIZE     512
           66  +#define SHORTNAMESIZE 100
           67  +
           68  +struct tar_header
           69  +{                               /* byte offset */
           70  +  char name[100];               /*   0 */
           71  +  char mode[8];                 /* 100 */
           72  +  char uid[8];                  /* 108 */
           73  +  char gid[8];                  /* 116 */
           74  +  char size[12];                /* 124 */
           75  +  char mtime[12];               /* 136 */
           76  +  char chksum[8];               /* 148 */
           77  +  char typeflag;                /* 156 */
           78  +  char linkname[100];           /* 157 */
           79  +  char magic[6];                /* 257 */
           80  +  char version[2];              /* 263 */
           81  +  char uname[32];               /* 265 */
           82  +  char gname[32];               /* 297 */
           83  +  char devmajor[8];             /* 329 */
           84  +  char devminor[8];             /* 337 */
           85  +  char prefix[155];             /* 345 */
           86  +                                /* 500 */
           87  +};
           88  +
           89  +union tar_buffer
           90  +{
           91  +  char               buffer[BLOCKSIZE];
           92  +  struct tar_header  header;
           93  +};
           94  +
           95  +struct attr_item
           96  +{
           97  +  struct attr_item  *next;
           98  +  char              *fname;
           99  +  int                mode;
          100  +  time_t             time;
          101  +};
          102  +
          103  +enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
          104  +
          105  +char *TGZfname          OF((const char *));
          106  +void TGZnotfound        OF((const char *));
          107  +
          108  +int getoct              OF((char *, int));
          109  +char *strtime           OF((time_t *));
          110  +int setfiletime         OF((char *, time_t));
          111  +void push_attr          OF((struct attr_item **, char *, int, time_t));
          112  +void restore_attr       OF((struct attr_item **));
          113  +
          114  +int ExprMatch           OF((char *, char *));
          115  +
          116  +int makedir             OF((char *));
          117  +int matchname           OF((int, int, char **, char *));
          118  +
          119  +void error              OF((const char *));
          120  +int tar                 OF((gzFile, int, int, int, char **));
          121  +
          122  +void help               OF((int));
          123  +int main                OF((int, char **));
          124  +
          125  +char *prog;
          126  +
          127  +const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
          128  +
          129  +/* return the file name of the TGZ archive */
          130  +/* or NULL if it does not exist */
          131  +
          132  +char *TGZfname (const char *arcname)
          133  +{
          134  +  static char buffer[1024];
          135  +  int origlen,i;
          136  +
          137  +  strcpy(buffer,arcname);
          138  +  origlen = strlen(buffer);
          139  +
          140  +  for (i=0; TGZsuffix[i]; i++)
          141  +    {
          142  +       strcpy(buffer+origlen,TGZsuffix[i]);
          143  +       if (access(buffer,F_OK) == 0)
          144  +         return buffer;
          145  +    }
          146  +  return NULL;
          147  +}
          148  +
          149  +
          150  +/* error message for the filename */
          151  +
          152  +void TGZnotfound (const char *arcname)
          153  +{
          154  +  int i;
          155  +
          156  +  fprintf(stderr,"%s: Couldn't find ",prog);
          157  +  for (i=0;TGZsuffix[i];i++)
          158  +    fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
          159  +            arcname,
          160  +            TGZsuffix[i]);
          161  +  exit(1);
          162  +}
          163  +
          164  +
          165  +/* convert octal digits to int */
          166  +/* on error return -1 */
          167  +
          168  +int getoct (char *p,int width)
          169  +{
          170  +  int result = 0;
          171  +  char c;
          172  +
          173  +  while (width--)
          174  +    {
          175  +      c = *p++;
          176  +      if (c == 0)
          177  +        break;
          178  +      if (c == ' ')
          179  +        continue;
          180  +      if (c < '0' || c > '7')
          181  +        return -1;
          182  +      result = result * 8 + (c - '0');
          183  +    }
          184  +  return result;
          185  +}
          186  +
          187  +
          188  +/* convert time_t to string */
          189  +/* use the "YYYY/MM/DD hh:mm:ss" format */
          190  +
          191  +char *strtime (time_t *t)
          192  +{
          193  +  struct tm   *local;
          194  +  static char result[32];
          195  +
          196  +  local = localtime(t);
          197  +  sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
          198  +          local->tm_year+1900, local->tm_mon+1, local->tm_mday,
          199  +          local->tm_hour, local->tm_min, local->tm_sec);
          200  +  return result;
          201  +}
          202  +
          203  +
          204  +/* set file time */
          205  +
          206  +int setfiletime (char *fname,time_t ftime)
          207  +{
          208  +#ifdef WIN32
          209  +  static int isWinNT = -1;
          210  +  SYSTEMTIME st;
          211  +  FILETIME locft, modft;
          212  +  struct tm *loctm;
          213  +  HANDLE hFile;
          214  +  int result;
          215  +
          216  +  loctm = localtime(&ftime);
          217  +  if (loctm == NULL)
          218  +    return -1;
          219  +
          220  +  st.wYear         = (WORD)loctm->tm_year + 1900;
          221  +  st.wMonth        = (WORD)loctm->tm_mon + 1;
          222  +  st.wDayOfWeek    = (WORD)loctm->tm_wday;
          223  +  st.wDay          = (WORD)loctm->tm_mday;
          224  +  st.wHour         = (WORD)loctm->tm_hour;
          225  +  st.wMinute       = (WORD)loctm->tm_min;
          226  +  st.wSecond       = (WORD)loctm->tm_sec;
          227  +  st.wMilliseconds = 0;
          228  +  if (!SystemTimeToFileTime(&st, &locft) ||
          229  +      !LocalFileTimeToFileTime(&locft, &modft))
          230  +    return -1;
          231  +
          232  +  if (isWinNT < 0)
          233  +    isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
          234  +  hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
          235  +                     (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
          236  +                     NULL);
          237  +  if (hFile == INVALID_HANDLE_VALUE)
          238  +    return -1;
          239  +  result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
          240  +  CloseHandle(hFile);
          241  +  return result;
          242  +#else
          243  +  struct utimbuf settime;
          244  +
          245  +  settime.actime = settime.modtime = ftime;
          246  +  return utime(fname,&settime);
          247  +#endif
          248  +}
          249  +
          250  +
          251  +/* push file attributes */
          252  +
          253  +void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
          254  +{
          255  +  struct attr_item *item;
          256  +
          257  +  item = (struct attr_item *)malloc(sizeof(struct attr_item));
          258  +  if (item == NULL)
          259  +    error("Out of memory");
          260  +  item->fname = strdup(fname);
          261  +  item->mode  = mode;
          262  +  item->time  = time;
          263  +  item->next  = *list;
          264  +  *list       = item;
          265  +}
          266  +
          267  +
          268  +/* restore file attributes */
          269  +
          270  +void restore_attr(struct attr_item **list)
          271  +{
          272  +  struct attr_item *item, *prev;
          273  +
          274  +  for (item = *list; item != NULL; )
          275  +    {
          276  +      setfiletime(item->fname,item->time);
          277  +      chmod(item->fname,item->mode);
          278  +      prev = item;
          279  +      item = item->next;
          280  +      free(prev);
          281  +    }
          282  +  *list = NULL;
          283  +}
          284  +
          285  +
          286  +/* match regular expression */
          287  +
          288  +#define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
          289  +
          290  +int ExprMatch (char *string,char *expr)
          291  +{
          292  +  while (1)
          293  +    {
          294  +      if (ISSPECIAL(*expr))
          295  +        {
          296  +          if (*expr == '/')
          297  +            {
          298  +              if (*string != '\\' && *string != '/')
          299  +                return 0;
          300  +              string ++; expr++;
          301  +            }
          302  +          else if (*expr == '*')
          303  +            {
          304  +              if (*expr ++ == 0)
          305  +                return 1;
          306  +              while (*++string != *expr)
          307  +                if (*string == 0)
          308  +                  return 0;
          309  +            }
          310  +        }
          311  +      else
          312  +        {
          313  +          if (*string != *expr)
          314  +            return 0;
          315  +          if (*expr++ == 0)
          316  +            return 1;
          317  +          string++;
          318  +        }
          319  +    }
          320  +}
          321  +
          322  +
          323  +/* recursive mkdir */
          324  +/* abort on ENOENT; ignore other errors like "directory already exists" */
          325  +/* return 1 if OK */
          326  +/*        0 on error */
          327  +
          328  +int makedir (char *newdir)
          329  +{
          330  +  char *buffer = strdup(newdir);
          331  +  char *p;
          332  +  int  len = strlen(buffer);
          333  +
          334  +  if (len <= 0) {
          335  +    free(buffer);
          336  +    return 0;
          337  +  }
          338  +  if (buffer[len-1] == '/') {
          339  +    buffer[len-1] = '\0';
          340  +  }
          341  +  if (mkdir(buffer, 0755) == 0)
          342  +    {
          343  +      free(buffer);
          344  +      return 1;
          345  +    }
          346  +
          347  +  p = buffer+1;
          348  +  while (1)
          349  +    {
          350  +      char hold;
          351  +
          352  +      while(*p && *p != '\\' && *p != '/')
          353  +        p++;
          354  +      hold = *p;
          355  +      *p = 0;
          356  +      if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
          357  +        {
          358  +          fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
          359  +          free(buffer);
          360  +          return 0;
          361  +        }
          362  +      if (hold == 0)
          363  +        break;
          364  +      *p++ = hold;
          365  +    }
          366  +  free(buffer);
          367  +  return 1;
          368  +}
          369  +
          370  +
          371  +int matchname (int arg,int argc,char **argv,char *fname)
          372  +{
          373  +  if (arg == argc)      /* no arguments given (untgz tgzarchive) */
          374  +    return 1;
          375  +
          376  +  while (arg < argc)
          377  +    if (ExprMatch(fname,argv[arg++]))
          378  +      return 1;
          379  +
          380  +  return 0; /* ignore this for the moment being */
          381  +}
          382  +
          383  +
          384  +/* tar file list or extract */
          385  +
          386  +int tar (gzFile in,int action,int arg,int argc,char **argv)
          387  +{
          388  +  union  tar_buffer buffer;
          389  +  int    len;
          390  +  int    err;
          391  +  int    getheader = 1;
          392  +  int    remaining = 0;
          393  +  FILE   *outfile = NULL;
          394  +  char   fname[BLOCKSIZE];
          395  +  int    tarmode;
          396  +  time_t tartime;
          397  +  struct attr_item *attributes = NULL;
          398  +
          399  +  if (action == TGZ_LIST)
          400  +    printf("    date      time     size                       file\n"
          401  +           " ---------- -------- --------- -------------------------------------\n");
          402  +  while (1)
          403  +    {
          404  +      len = gzread(in, &buffer, BLOCKSIZE);
          405  +      if (len < 0)
          406  +        error(gzerror(in, &err));
          407  +      /*
          408  +       * Always expect complete blocks to process
          409  +       * the tar information.
          410  +       */
          411  +      if (len != BLOCKSIZE)
          412  +        {
          413  +          action = TGZ_INVALID; /* force error exit */
          414  +          remaining = 0;        /* force I/O cleanup */
          415  +        }
          416  +
          417  +      /*
          418  +       * If we have to get a tar header
          419  +       */
          420  +      if (getheader >= 1)
          421  +        {
          422  +          /*
          423  +           * if we met the end of the tar
          424  +           * or the end-of-tar block,
          425  +           * we are done
          426  +           */
          427  +          if (len == 0 || buffer.header.name[0] == 0)
          428  +            break;
          429  +
          430  +          tarmode = getoct(buffer.header.mode,8);
          431  +          tartime = (time_t)getoct(buffer.header.mtime,12);
          432  +          if (tarmode == -1 || tartime == (time_t)-1)
          433  +            {
          434  +              buffer.header.name[0] = 0;
          435  +              action = TGZ_INVALID;
          436  +            }
          437  +
          438  +          if (getheader == 1)
          439  +            {
          440  +              strncpy(fname,buffer.header.name,SHORTNAMESIZE);
          441  +              if (fname[SHORTNAMESIZE-1] != 0)
          442  +                  fname[SHORTNAMESIZE] = 0;
          443  +            }
          444  +          else
          445  +            {
          446  +              /*
          447  +               * The file name is longer than SHORTNAMESIZE
          448  +               */
          449  +              if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
          450  +                  error("bad long name");
          451  +              getheader = 1;
          452  +            }
          453  +
          454  +          /*
          455  +           * Act according to the type flag
          456  +           */
          457  +          switch (buffer.header.typeflag)
          458  +            {
          459  +            case DIRTYPE:
          460  +              if (action == TGZ_LIST)
          461  +                printf(" %s     <dir> %s\n",strtime(&tartime),fname);
          462  +              if (action == TGZ_EXTRACT)
          463  +                {
          464  +                  makedir(fname);
          465  +                  push_attr(&attributes,fname,tarmode,tartime);
          466  +                }
          467  +              break;
          468  +            case REGTYPE:
          469  +            case AREGTYPE:
          470  +              remaining = getoct(buffer.header.size,12);
          471  +              if (remaining == -1)
          472  +                {
          473  +                  action = TGZ_INVALID;
          474  +                  break;
          475  +                }
          476  +              if (action == TGZ_LIST)
          477  +                printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
          478  +              else if (action == TGZ_EXTRACT)
          479  +                {
          480  +                  if (matchname(arg,argc,argv,fname))
          481  +                    {
          482  +                      outfile = fopen(fname,"wb");
          483  +                      if (outfile == NULL) {
          484  +                        /* try creating directory */
          485  +                        char *p = strrchr(fname, '/');
          486  +                        if (p != NULL) {
          487  +                          *p = '\0';
          488  +                          makedir(fname);
          489  +                          *p = '/';
          490  +                          outfile = fopen(fname,"wb");
          491  +                        }
          492  +                      }
          493  +                      if (outfile != NULL)
          494  +                        printf("Extracting %s\n",fname);
          495  +                      else
          496  +                        fprintf(stderr, "%s: Couldn't create %s",prog,fname);
          497  +                    }
          498  +                  else
          499  +                    outfile = NULL;
          500  +                }
          501  +              getheader = 0;
          502  +              break;
          503  +            case GNUTYPE_LONGLINK:
          504  +            case GNUTYPE_LONGNAME:
          505  +              remaining = getoct(buffer.header.size,12);
          506  +              if (remaining < 0 || remaining >= BLOCKSIZE)
          507  +                {
          508  +                  action = TGZ_INVALID;
          509  +                  break;
          510  +                }
          511  +              len = gzread(in, fname, BLOCKSIZE);
          512  +              if (len < 0)
          513  +                error(gzerror(in, &err));
          514  +              if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
          515  +                {
          516  +                  action = TGZ_INVALID;
          517  +                  break;
          518  +                }
          519  +              getheader = 2;
          520  +              break;
          521  +            default:
          522  +              if (action == TGZ_LIST)
          523  +                printf(" %s     <---> %s\n",strtime(&tartime),fname);
          524  +              break;
          525  +            }
          526  +        }
          527  +      else
          528  +        {
          529  +          unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
          530  +
          531  +          if (outfile != NULL)
          532  +            {
          533  +              if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
          534  +                {
          535  +                  fprintf(stderr,
          536  +                    "%s: Error writing %s -- skipping\n",prog,fname);
          537  +                  fclose(outfile);
          538  +                  outfile = NULL;
          539  +                  remove(fname);
          540  +                }
          541  +            }
          542  +          remaining -= bytes;
          543  +        }
          544  +
          545  +      if (remaining == 0)
          546  +        {
          547  +          getheader = 1;
          548  +          if (outfile != NULL)
          549  +            {
          550  +              fclose(outfile);
          551  +              outfile = NULL;
          552  +              if (action != TGZ_INVALID)
          553  +                push_attr(&attributes,fname,tarmode,tartime);
          554  +            }
          555  +        }
          556  +
          557  +      /*
          558  +       * Abandon if errors are found
          559  +       */
          560  +      if (action == TGZ_INVALID)
          561  +        {
          562  +          error("broken archive");
          563  +          break;
          564  +        }
          565  +    }
          566  +
          567  +  /*
          568  +   * Restore file modes and time stamps
          569  +   */
          570  +  restore_attr(&attributes);
          571  +
          572  +  if (gzclose(in) != Z_OK)
          573  +    error("failed gzclose");
          574  +
          575  +  return 0;
          576  +}
          577  +
          578  +
          579  +/* ============================================================ */
          580  +
          581  +void help(int exitval)
          582  +{
          583  +  printf("untgz version 0.2.1\n"
          584  +         "  using zlib version %s\n\n",
          585  +         zlibVersion());
          586  +  printf("Usage: untgz file.tgz            extract all files\n"
          587  +         "       untgz file.tgz fname ...  extract selected files\n"
          588  +         "       untgz -l file.tgz         list archive contents\n"
          589  +         "       untgz -h                  display this help\n");
          590  +  exit(exitval);
          591  +}
          592  +
          593  +void error(const char *msg)
          594  +{
          595  +  fprintf(stderr, "%s: %s\n", prog, msg);
          596  +  exit(1);
          597  +}
          598  +
          599  +
          600  +/* ============================================================ */
          601  +
          602  +#if defined(WIN32) && defined(__GNUC__)
          603  +int _CRT_glob = 0;      /* disable argument globbing in MinGW */
          604  +#endif
          605  +
          606  +int main(int argc,char **argv)
          607  +{
          608  +    int         action = TGZ_EXTRACT;
          609  +    int         arg = 1;
          610  +    char        *TGZfile;
          611  +    gzFile      *f;
          612  +
          613  +    prog = strrchr(argv[0],'\\');
          614  +    if (prog == NULL)
          615  +      {
          616  +        prog = strrchr(argv[0],'/');
          617  +        if (prog == NULL)
          618  +          {
          619  +            prog = strrchr(argv[0],':');
          620  +            if (prog == NULL)
          621  +              prog = argv[0];
          622  +            else
          623  +              prog++;
          624  +          }
          625  +        else
          626  +          prog++;
          627  +      }
          628  +    else
          629  +      prog++;
          630  +
          631  +    if (argc == 1)
          632  +      help(0);
          633  +
          634  +    if (strcmp(argv[arg],"-l") == 0)
          635  +      {
          636  +        action = TGZ_LIST;
          637  +        if (argc == ++arg)
          638  +          help(0);
          639  +      }
          640  +    else if (strcmp(argv[arg],"-h") == 0)
          641  +      {
          642  +        help(0);
          643  +      }
          644  +
          645  +    if ((TGZfile = TGZfname(argv[arg])) == NULL)
          646  +      TGZnotfound(argv[arg]);
          647  +
          648  +    ++arg;
          649  +    if ((action == TGZ_LIST) && (arg != argc))
          650  +      help(1);
          651  +
          652  +/*
          653  + *  Process the TGZ file
          654  + */
          655  +    switch(action)
          656  +      {
          657  +      case TGZ_LIST:
          658  +      case TGZ_EXTRACT:
          659  +        f = gzopen(TGZfile,"rb");
          660  +        if (f == NULL)
          661  +          {
          662  +            fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
          663  +            return 1;
          664  +          }
          665  +        exit(tar(f, action, arg, argc, argv));
          666  +      break;
          667  +
          668  +      default:
          669  +        error("Unknown option");
          670  +        exit(1);
          671  +      }
          672  +
          673  +    return 0;
          674  +}

Added compat/zlib/contrib/vstudio/readme.txt.

            1  +Building instructions for the DLL versions of Zlib 1.2.7
            2  +========================================================
            3  +
            4  +This directory contains projects that build zlib and minizip using
            5  +Microsoft Visual C++ 9.0/10.0.
            6  +
            7  +You don't need to build these projects yourself. You can download the
            8  +binaries from:
            9  +  http://www.winimage.com/zLibDll
           10  +
           11  +More information can be found at this site.
           12  +
           13  +
           14  +
           15  +
           16  +
           17  +Build instructions for Visual Studio 2008 (32 bits or 64 bits)
           18  +--------------------------------------------------------------
           19  +- Uncompress current zlib, including all contrib/* files
           20  +- Compile assembly code (with Visual Studio Command Prompt) by running:
           21  +   bld_ml64.bat (in contrib\masmx64)
           22  +   bld_ml32.bat (in contrib\masmx86)
           23  +- Open contrib\vstudio\vc9\zlibvc.sln with Microsoft Visual C++ 2008
           24  +- Or run: vcbuild /rebuild contrib\vstudio\vc9\zlibvc.sln "Release|Win32"
           25  +
           26  +Build instructions for Visual Studio 2010 (32 bits or 64 bits)
           27  +--------------------------------------------------------------
           28  +- Uncompress current zlib, including all contrib/* files
           29  +- Open contrib\vstudio\vc10\zlibvc.sln with Microsoft Visual C++ 2010
           30  +
           31  +
           32  +Important
           33  +---------
           34  +- To use zlibwapi.dll in your application, you must define the
           35  +  macro ZLIB_WINAPI when compiling your application's source files.
           36  +
           37  +
           38  +Additional notes
           39  +----------------
           40  +- This DLL, named zlibwapi.dll, is compatible to the old zlib.dll built
           41  +  by Gilles Vollant from the zlib 1.1.x sources, and distributed at
           42  +    http://www.winimage.com/zLibDll
           43  +  It uses the WINAPI calling convention for the exported functions, and
           44  +  includes the minizip functionality. If your application needs that
           45  +  particular build of zlib.dll, you can rename zlibwapi.dll to zlib.dll.
           46  +
           47  +- The new DLL was renamed because there exist several incompatible
           48  +  versions of zlib.dll on the Internet.
           49  +
           50  +- There is also an official DLL build of zlib, named zlib1.dll. This one
           51  +  is exporting the functions using the CDECL convention. See the file
           52  +  win32\DLL_FAQ.txt found in this zlib distribution.
           53  +
           54  +- There used to be a ZLIB_DLL macro in zlib 1.1.x, but now this symbol
           55  +  has a slightly different effect. To avoid compatibility problems, do
           56  +  not define it here.
           57  +
           58  +
           59  +Gilles Vollant
           60  +info@winimage.com

Added compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="Release|Itanium">
           17  +      <Configuration>Release</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="Release|Win32">
           21  +      <Configuration>Release</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="Release|x64">
           25  +      <Configuration>Release</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +  </ItemGroup>
           29  +  <PropertyGroup Label="Globals">
           30  +    <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694382A}</ProjectGuid>
           31  +    <Keyword>Win32Proj</Keyword>
           32  +  </PropertyGroup>
           33  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           34  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           35  +    <ConfigurationType>Application</ConfigurationType>
           36  +    <CharacterSet>MultiByte</CharacterSet>
           37  +  </PropertyGroup>
           38  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           39  +    <ConfigurationType>Application</ConfigurationType>
           40  +    <CharacterSet>MultiByte</CharacterSet>
           41  +  </PropertyGroup>
           42  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           43  +    <ConfigurationType>Application</ConfigurationType>
           44  +    <CharacterSet>MultiByte</CharacterSet>
           45  +  </PropertyGroup>
           46  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           47  +    <ConfigurationType>Application</ConfigurationType>
           48  +    <CharacterSet>MultiByte</CharacterSet>
           49  +  </PropertyGroup>
           50  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           51  +    <ConfigurationType>Application</ConfigurationType>
           52  +    <CharacterSet>MultiByte</CharacterSet>
           53  +  </PropertyGroup>
           54  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           55  +    <ConfigurationType>Application</ConfigurationType>
           56  +    <CharacterSet>MultiByte</CharacterSet>
           57  +  </PropertyGroup>
           58  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           59  +  <ImportGroup Label="ExtensionSettings">
           60  +  </ImportGroup>
           61  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           62  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           63  +  </ImportGroup>
           64  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           65  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           66  +  </ImportGroup>
           67  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
           68  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           69  +  </ImportGroup>
           70  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
           71  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           72  +  </ImportGroup>
           73  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
           74  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           75  +  </ImportGroup>
           76  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
           77  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           78  +  </ImportGroup>
           79  +  <PropertyGroup Label="UserMacros" />
           80  +  <PropertyGroup>
           81  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
           82  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\</OutDir>
           83  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir>
           84  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
           85  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
           86  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\</OutDir>
           87  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir>
           88  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
           89  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
           90  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\</OutDir>
           91  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir>
           92  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
           93  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
           94  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir>
           95  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir>
           96  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental>
           97  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest>
           98  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\</OutDir>
           99  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir>
          100  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
          101  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
          102  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir>
          103  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir>
          104  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental>
          105  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest>
          106  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          107  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          108  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          109  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          110  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          111  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          112  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          113  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          114  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          115  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          116  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          117  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          118  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          119  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          120  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          121  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          122  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          123  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          124  +  </PropertyGroup>
          125  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          126  +    <ClCompile>
          127  +      <Optimization>Disabled</Optimization>
          128  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          129  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          130  +      <MinimalRebuild>true</MinimalRebuild>
          131  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          132  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          133  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          134  +      <PrecompiledHeader>
          135  +      </PrecompiledHeader>
          136  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          137  +      <WarningLevel>Level3</WarningLevel>
          138  +      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
          139  +    </ClCompile>
          140  +    <Link>
          141  +      <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          142  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          143  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          144  +      <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile>
          145  +      <SubSystem>Console</SubSystem>
          146  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          147  +      <DataExecutionPrevention>
          148  +      </DataExecutionPrevention>
          149  +      <TargetMachine>MachineX86</TargetMachine>
          150  +    </Link>
          151  +  </ItemDefinitionGroup>
          152  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          153  +    <ClCompile>
          154  +      <Optimization>MaxSpeed</Optimization>
          155  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          156  +      <OmitFramePointers>true</OmitFramePointers>
          157  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          158  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          159  +      <StringPooling>true</StringPooling>
          160  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          161  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          162  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          163  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          164  +      <PrecompiledHeader>
          165  +      </PrecompiledHeader>
          166  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          167  +      <WarningLevel>Level3</WarningLevel>
          168  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          169  +    </ClCompile>
          170  +    <Link>
          171  +      <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          172  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          173  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          174  +      <SubSystem>Console</SubSystem>
          175  +      <OptimizeReferences>true</OptimizeReferences>
          176  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          177  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          178  +      <DataExecutionPrevention>
          179  +      </DataExecutionPrevention>
          180  +      <TargetMachine>MachineX86</TargetMachine>
          181  +    </Link>
          182  +  </ItemDefinitionGroup>
          183  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          184  +    <Midl>
          185  +      <TargetEnvironment>X64</TargetEnvironment>
          186  +    </Midl>
          187  +    <ClCompile>
          188  +      <Optimization>Disabled</Optimization>
          189  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          190  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          191  +      <MinimalRebuild>true</MinimalRebuild>
          192  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          193  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          194  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          195  +      <PrecompiledHeader>
          196  +      </PrecompiledHeader>
          197  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          198  +      <WarningLevel>Level3</WarningLevel>
          199  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          200  +    </ClCompile>
          201  +    <Link>
          202  +      <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          203  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          204  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          205  +      <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile>
          206  +      <SubSystem>Console</SubSystem>
          207  +      <TargetMachine>MachineX64</TargetMachine>
          208  +    </Link>
          209  +  </ItemDefinitionGroup>
          210  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          211  +    <Midl>
          212  +      <TargetEnvironment>Itanium</TargetEnvironment>
          213  +    </Midl>
          214  +    <ClCompile>
          215  +      <Optimization>Disabled</Optimization>
          216  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          217  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          218  +      <MinimalRebuild>true</MinimalRebuild>
          219  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          220  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          221  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          222  +      <PrecompiledHeader>
          223  +      </PrecompiledHeader>
          224  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          225  +      <WarningLevel>Level3</WarningLevel>
          226  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          227  +    </ClCompile>
          228  +    <Link>
          229  +      <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          230  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          231  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          232  +      <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile>
          233  +      <SubSystem>Console</SubSystem>
          234  +      <TargetMachine>MachineIA64</TargetMachine>
          235  +    </Link>
          236  +  </ItemDefinitionGroup>
          237  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          238  +    <Midl>
          239  +      <TargetEnvironment>X64</TargetEnvironment>
          240  +    </Midl>
          241  +    <ClCompile>
          242  +      <Optimization>MaxSpeed</Optimization>
          243  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          244  +      <OmitFramePointers>true</OmitFramePointers>
          245  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          246  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          247  +      <StringPooling>true</StringPooling>
          248  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          249  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          250  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          251  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          252  +      <PrecompiledHeader>
          253  +      </PrecompiledHeader>
          254  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          255  +      <WarningLevel>Level3</WarningLevel>
          256  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          257  +    </ClCompile>
          258  +    <Link>
          259  +      <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          260  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          261  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          262  +      <SubSystem>Console</SubSystem>
          263  +      <OptimizeReferences>true</OptimizeReferences>
          264  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          265  +      <TargetMachine>MachineX64</TargetMachine>
          266  +    </Link>
          267  +  </ItemDefinitionGroup>
          268  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          269  +    <Midl>
          270  +      <TargetEnvironment>Itanium</TargetEnvironment>
          271  +    </Midl>
          272  +    <ClCompile>
          273  +      <Optimization>MaxSpeed</Optimization>
          274  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          275  +      <OmitFramePointers>true</OmitFramePointers>
          276  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          277  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          278  +      <StringPooling>true</StringPooling>
          279  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          280  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          281  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          282  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          283  +      <PrecompiledHeader>
          284  +      </PrecompiledHeader>
          285  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          286  +      <WarningLevel>Level3</WarningLevel>
          287  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          288  +    </ClCompile>
          289  +    <Link>
          290  +      <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          291  +      <OutputFile>$(OutDir)miniunz.exe</OutputFile>
          292  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          293  +      <SubSystem>Console</SubSystem>
          294  +      <OptimizeReferences>true</OptimizeReferences>
          295  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          296  +      <TargetMachine>MachineIA64</TargetMachine>
          297  +    </Link>
          298  +  </ItemDefinitionGroup>
          299  +  <ItemGroup>
          300  +    <ClCompile Include="..\..\minizip\miniunz.c" />
          301  +  </ItemGroup>
          302  +  <ItemGroup>
          303  +    <ProjectReference Include="zlibvc.vcxproj">
          304  +      <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project>
          305  +    </ProjectReference>
          306  +  </ItemGroup>
          307  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          308  +  <ImportGroup Label="ExtensionTargets">
          309  +  </ImportGroup>
          310  +</Project>

Added compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{048af943-022b-4db6-beeb-a54c34774ee2}</UniqueIdentifier>
            6  +      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm</Extensions>
            7  +    </Filter>
            8  +    <Filter Include="Header Files">
            9  +      <UniqueIdentifier>{c1d600d2-888f-4aea-b73e-8b0dd9befa0c}</UniqueIdentifier>
           10  +      <Extensions>h;hpp;hxx;hm;inl;inc</Extensions>
           11  +    </Filter>
           12  +    <Filter Include="Resource Files">
           13  +      <UniqueIdentifier>{0844199a-966b-4f19-81db-1e0125e141b9}</UniqueIdentifier>
           14  +      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
           15  +    </Filter>
           16  +  </ItemGroup>
           17  +  <ItemGroup>
           18  +    <ClCompile Include="..\..\minizip\miniunz.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +  </ItemGroup>
           22  +</Project>

Added compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="Release|Itanium">
           17  +      <Configuration>Release</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="Release|Win32">
           21  +      <Configuration>Release</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="Release|x64">
           25  +      <Configuration>Release</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +  </ItemGroup>
           29  +  <PropertyGroup Label="Globals">
           30  +    <ProjectGuid>{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid>
           31  +    <Keyword>Win32Proj</Keyword>
           32  +  </PropertyGroup>
           33  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           34  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           35  +    <ConfigurationType>Application</ConfigurationType>
           36  +    <CharacterSet>MultiByte</CharacterSet>
           37  +  </PropertyGroup>
           38  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           39  +    <ConfigurationType>Application</ConfigurationType>
           40  +    <CharacterSet>MultiByte</CharacterSet>
           41  +  </PropertyGroup>
           42  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           43  +    <ConfigurationType>Application</ConfigurationType>
           44  +    <CharacterSet>MultiByte</CharacterSet>
           45  +  </PropertyGroup>
           46  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           47  +    <ConfigurationType>Application</ConfigurationType>
           48  +    <CharacterSet>MultiByte</CharacterSet>
           49  +  </PropertyGroup>
           50  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           51  +    <ConfigurationType>Application</ConfigurationType>
           52  +    <CharacterSet>MultiByte</CharacterSet>
           53  +  </PropertyGroup>
           54  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           55  +    <ConfigurationType>Application</ConfigurationType>
           56  +    <CharacterSet>MultiByte</CharacterSet>
           57  +  </PropertyGroup>
           58  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           59  +  <ImportGroup Label="ExtensionSettings">
           60  +  </ImportGroup>
           61  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           62  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           63  +  </ImportGroup>
           64  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           65  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           66  +  </ImportGroup>
           67  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
           68  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           69  +  </ImportGroup>
           70  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
           71  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           72  +  </ImportGroup>
           73  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
           74  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           75  +  </ImportGroup>
           76  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
           77  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           78  +  </ImportGroup>
           79  +  <PropertyGroup Label="UserMacros" />
           80  +  <PropertyGroup>
           81  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
           82  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\</OutDir>
           83  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir>
           84  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
           85  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
           86  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\</OutDir>
           87  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir>
           88  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
           89  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</OutDir>
           90  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</IntDir>
           91  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
           92  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
           93  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</OutDir>
           94  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</IntDir>
           95  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental>
           96  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest>
           97  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</OutDir>
           98  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</IntDir>
           99  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
          100  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</OutDir>
          101  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</IntDir>
          102  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental>
          103  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          104  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          105  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          106  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          107  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          108  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          109  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          110  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          111  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          112  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          113  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          114  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          115  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          116  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          117  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          118  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          119  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          120  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          121  +  </PropertyGroup>
          122  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          123  +    <ClCompile>
          124  +      <Optimization>Disabled</Optimization>
          125  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          126  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          127  +      <MinimalRebuild>true</MinimalRebuild>
          128  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          129  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          130  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          131  +      <PrecompiledHeader>
          132  +      </PrecompiledHeader>
          133  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          134  +      <WarningLevel>Level3</WarningLevel>
          135  +      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
          136  +    </ClCompile>
          137  +    <Link>
          138  +      <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          139  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          140  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          141  +      <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile>
          142  +      <SubSystem>Console</SubSystem>
          143  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          144  +      <DataExecutionPrevention>
          145  +      </DataExecutionPrevention>
          146  +      <TargetMachine>MachineX86</TargetMachine>
          147  +    </Link>
          148  +  </ItemDefinitionGroup>
          149  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          150  +    <ClCompile>
          151  +      <Optimization>MaxSpeed</Optimization>
          152  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          153  +      <OmitFramePointers>true</OmitFramePointers>
          154  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          155  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          156  +      <StringPooling>true</StringPooling>
          157  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          158  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          159  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          160  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          161  +      <PrecompiledHeader>
          162  +      </PrecompiledHeader>
          163  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          164  +      <WarningLevel>Level3</WarningLevel>
          165  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          166  +    </ClCompile>
          167  +    <Link>
          168  +      <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          169  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          170  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          171  +      <SubSystem>Console</SubSystem>
          172  +      <OptimizeReferences>true</OptimizeReferences>
          173  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          174  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          175  +      <DataExecutionPrevention>
          176  +      </DataExecutionPrevention>
          177  +      <TargetMachine>MachineX86</TargetMachine>
          178  +    </Link>
          179  +  </ItemDefinitionGroup>
          180  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          181  +    <Midl>
          182  +      <TargetEnvironment>X64</TargetEnvironment>
          183  +    </Midl>
          184  +    <ClCompile>
          185  +      <Optimization>Disabled</Optimization>
          186  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          187  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          188  +      <MinimalRebuild>true</MinimalRebuild>
          189  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          190  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          191  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          192  +      <PrecompiledHeader>
          193  +      </PrecompiledHeader>
          194  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          195  +      <WarningLevel>Level3</WarningLevel>
          196  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          197  +    </ClCompile>
          198  +    <Link>
          199  +      <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          200  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          201  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          202  +      <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile>
          203  +      <SubSystem>Console</SubSystem>
          204  +      <TargetMachine>MachineX64</TargetMachine>
          205  +    </Link>
          206  +  </ItemDefinitionGroup>
          207  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          208  +    <Midl>
          209  +      <TargetEnvironment>Itanium</TargetEnvironment>
          210  +    </Midl>
          211  +    <ClCompile>
          212  +      <Optimization>Disabled</Optimization>
          213  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          214  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          215  +      <MinimalRebuild>true</MinimalRebuild>
          216  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          217  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          218  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          219  +      <PrecompiledHeader>
          220  +      </PrecompiledHeader>
          221  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          222  +      <WarningLevel>Level3</WarningLevel>
          223  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          224  +    </ClCompile>
          225  +    <Link>
          226  +      <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          227  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          228  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          229  +      <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile>
          230  +      <SubSystem>Console</SubSystem>
          231  +      <TargetMachine>MachineIA64</TargetMachine>
          232  +    </Link>
          233  +  </ItemDefinitionGroup>
          234  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          235  +    <Midl>
          236  +      <TargetEnvironment>X64</TargetEnvironment>
          237  +    </Midl>
          238  +    <ClCompile>
          239  +      <Optimization>MaxSpeed</Optimization>
          240  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          241  +      <OmitFramePointers>true</OmitFramePointers>
          242  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          243  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          244  +      <StringPooling>true</StringPooling>
          245  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          246  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          247  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          248  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          249  +      <PrecompiledHeader>
          250  +      </PrecompiledHeader>
          251  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          252  +      <WarningLevel>Level3</WarningLevel>
          253  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          254  +    </ClCompile>
          255  +    <Link>
          256  +      <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          257  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          258  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          259  +      <SubSystem>Console</SubSystem>
          260  +      <OptimizeReferences>true</OptimizeReferences>
          261  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          262  +      <TargetMachine>MachineX64</TargetMachine>
          263  +    </Link>
          264  +  </ItemDefinitionGroup>
          265  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          266  +    <Midl>
          267  +      <TargetEnvironment>Itanium</TargetEnvironment>
          268  +    </Midl>
          269  +    <ClCompile>
          270  +      <Optimization>MaxSpeed</Optimization>
          271  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          272  +      <OmitFramePointers>true</OmitFramePointers>
          273  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          274  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          275  +      <StringPooling>true</StringPooling>
          276  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          277  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          278  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          279  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          280  +      <PrecompiledHeader>
          281  +      </PrecompiledHeader>
          282  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          283  +      <WarningLevel>Level3</WarningLevel>
          284  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          285  +    </ClCompile>
          286  +    <Link>
          287  +      <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          288  +      <OutputFile>$(OutDir)minizip.exe</OutputFile>
          289  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          290  +      <SubSystem>Console</SubSystem>
          291  +      <OptimizeReferences>true</OptimizeReferences>
          292  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          293  +      <TargetMachine>MachineIA64</TargetMachine>
          294  +    </Link>
          295  +  </ItemDefinitionGroup>
          296  +  <ItemGroup>
          297  +    <ClCompile Include="..\..\minizip\minizip.c" />
          298  +  </ItemGroup>
          299  +  <ItemGroup>
          300  +    <ProjectReference Include="zlibvc.vcxproj">
          301  +      <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project>
          302  +    </ProjectReference>
          303  +  </ItemGroup>
          304  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          305  +  <ImportGroup Label="ExtensionTargets">
          306  +  </ImportGroup>
          307  +</Project>

Added compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{c0419b40-bf50-40da-b153-ff74215b79de}</UniqueIdentifier>
            6  +      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm</Extensions>
            7  +    </Filter>
            8  +    <Filter Include="Header Files">
            9  +      <UniqueIdentifier>{bb87b070-735b-478e-92ce-7383abb2f36c}</UniqueIdentifier>
           10  +      <Extensions>h;hpp;hxx;hm;inl;inc</Extensions>
           11  +    </Filter>
           12  +    <Filter Include="Resource Files">
           13  +      <UniqueIdentifier>{f46ab6a6-548f-43cb-ae96-681abb5bd5db}</UniqueIdentifier>
           14  +      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
           15  +    </Filter>
           16  +  </ItemGroup>
           17  +  <ItemGroup>
           18  +    <ClCompile Include="..\..\minizip\minizip.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +  </ItemGroup>
           22  +</Project>

Added compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium">
           17  +      <Configuration>ReleaseWithoutAsm</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Win32">
           21  +      <Configuration>ReleaseWithoutAsm</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="ReleaseWithoutAsm|x64">
           25  +      <Configuration>ReleaseWithoutAsm</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +    <ProjectConfiguration Include="Release|Itanium">
           29  +      <Configuration>Release</Configuration>
           30  +      <Platform>Itanium</Platform>
           31  +    </ProjectConfiguration>
           32  +    <ProjectConfiguration Include="Release|Win32">
           33  +      <Configuration>Release</Configuration>
           34  +      <Platform>Win32</Platform>
           35  +    </ProjectConfiguration>
           36  +    <ProjectConfiguration Include="Release|x64">
           37  +      <Configuration>Release</Configuration>
           38  +      <Platform>x64</Platform>
           39  +    </ProjectConfiguration>
           40  +  </ItemGroup>
           41  +  <PropertyGroup Label="Globals">
           42  +    <ProjectGuid>{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid>
           43  +    <RootNamespace>testzlib</RootNamespace>
           44  +    <Keyword>Win32Proj</Keyword>
           45  +  </PropertyGroup>
           46  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           47  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           48  +    <ConfigurationType>Application</ConfigurationType>
           49  +    <CharacterSet>MultiByte</CharacterSet>
           50  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           51  +  </PropertyGroup>
           52  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration">
           53  +    <ConfigurationType>Application</ConfigurationType>
           54  +    <CharacterSet>MultiByte</CharacterSet>
           55  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           56  +  </PropertyGroup>
           57  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           58  +    <ConfigurationType>Application</ConfigurationType>
           59  +    <CharacterSet>MultiByte</CharacterSet>
           60  +  </PropertyGroup>
           61  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           62  +    <ConfigurationType>Application</ConfigurationType>
           63  +    <CharacterSet>MultiByte</CharacterSet>
           64  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           65  +  </PropertyGroup>
           66  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration">
           67  +    <ConfigurationType>Application</ConfigurationType>
           68  +    <CharacterSet>MultiByte</CharacterSet>
           69  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           70  +  </PropertyGroup>
           71  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           72  +    <ConfigurationType>Application</ConfigurationType>
           73  +    <CharacterSet>MultiByte</CharacterSet>
           74  +  </PropertyGroup>
           75  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           76  +    <ConfigurationType>Application</ConfigurationType>
           77  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           78  +  </PropertyGroup>
           79  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration">
           80  +    <ConfigurationType>Application</ConfigurationType>
           81  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           82  +  </PropertyGroup>
           83  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           84  +    <ConfigurationType>Application</ConfigurationType>
           85  +  </PropertyGroup>
           86  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           87  +  <ImportGroup Label="ExtensionSettings">
           88  +  </ImportGroup>
           89  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           90  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           91  +  </ImportGroup>
           92  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets">
           93  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           94  +  </ImportGroup>
           95  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           96  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           97  +  </ImportGroup>
           98  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
           99  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          100  +  </ImportGroup>
          101  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets">
          102  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          103  +  </ImportGroup>
          104  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
          105  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          106  +  </ImportGroup>
          107  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
          108  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          109  +  </ImportGroup>
          110  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets">
          111  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          112  +  </ImportGroup>
          113  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
          114  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          115  +  </ImportGroup>
          116  +  <PropertyGroup Label="UserMacros" />
          117  +  <PropertyGroup>
          118  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
          119  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\</OutDir>
          120  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir>
          121  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
          122  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
          123  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\</OutDir>
          124  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir>
          125  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental>
          126  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest>
          127  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\</OutDir>
          128  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir>
          129  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
          130  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
          131  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\</OutDir>
          132  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir>
          133  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
          134  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\</OutDir>
          135  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir>
          136  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental>
          137  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest>
          138  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\</OutDir>
          139  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir>
          140  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest>
          141  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\</OutDir>
          142  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir>
          143  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental>
          144  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest>
          145  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\</OutDir>
          146  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir>
          147  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
          148  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\</OutDir>
          149  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir>
          150  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental>
          151  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest>
          152  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          153  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          154  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          155  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          156  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          157  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          158  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          159  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          160  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          161  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          162  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          163  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          164  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          165  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          166  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          167  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          168  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          169  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          170  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          171  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          172  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          173  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          174  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          175  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          176  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          177  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          178  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          179  +  </PropertyGroup>
          180  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          181  +    <ClCompile>
          182  +      <Optimization>Disabled</Optimization>
          183  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          184  +      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          185  +      <MinimalRebuild>true</MinimalRebuild>
          186  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          187  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          188  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          189  +      <PrecompiledHeader>
          190  +      </PrecompiledHeader>
          191  +      <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
          192  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          193  +      <WarningLevel>Level3</WarningLevel>
          194  +      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
          195  +    </ClCompile>
          196  +    <Link>
          197  +      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
          198  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          199  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          200  +      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
          201  +      <SubSystem>Console</SubSystem>
          202  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          203  +      <DataExecutionPrevention>
          204  +      </DataExecutionPrevention>
          205  +      <TargetMachine>MachineX86</TargetMachine>
          206  +    </Link>
          207  +  </ItemDefinitionGroup>
          208  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
          209  +    <ClCompile>
          210  +      <Optimization>MaxSpeed</Optimization>
          211  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          212  +      <OmitFramePointers>true</OmitFramePointers>
          213  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          214  +      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          215  +      <StringPooling>true</StringPooling>
          216  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          217  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          218  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          219  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          220  +      <PrecompiledHeader>
          221  +      </PrecompiledHeader>
          222  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          223  +      <WarningLevel>Level3</WarningLevel>
          224  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          225  +    </ClCompile>
          226  +    <Link>
          227  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          228  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          229  +      <SubSystem>Console</SubSystem>
          230  +      <OptimizeReferences>true</OptimizeReferences>
          231  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          232  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          233  +      <DataExecutionPrevention>
          234  +      </DataExecutionPrevention>
          235  +      <TargetMachine>MachineX86</TargetMachine>
          236  +    </Link>
          237  +  </ItemDefinitionGroup>
          238  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          239  +    <ClCompile>
          240  +      <Optimization>MaxSpeed</Optimization>
          241  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          242  +      <OmitFramePointers>true</OmitFramePointers>
          243  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          244  +      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          245  +      <StringPooling>true</StringPooling>
          246  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          247  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          248  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          249  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          250  +      <PrecompiledHeader>
          251  +      </PrecompiledHeader>
          252  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          253  +      <WarningLevel>Level3</WarningLevel>
          254  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          255  +    </ClCompile>
          256  +    <Link>
          257  +      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
          258  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          259  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          260  +      <SubSystem>Console</SubSystem>
          261  +      <OptimizeReferences>true</OptimizeReferences>
          262  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          263  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          264  +      <DataExecutionPrevention>
          265  +      </DataExecutionPrevention>
          266  +      <TargetMachine>MachineX86</TargetMachine>
          267  +    </Link>
          268  +  </ItemDefinitionGroup>
          269  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          270  +    <ClCompile>
          271  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          272  +      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          273  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          274  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          275  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          276  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          277  +    </ClCompile>
          278  +    <Link>
          279  +      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
          280  +    </Link>
          281  +  </ItemDefinitionGroup>
          282  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          283  +    <Midl>
          284  +      <TargetEnvironment>Itanium</TargetEnvironment>
          285  +    </Midl>
          286  +    <ClCompile>
          287  +      <Optimization>Disabled</Optimization>
          288  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          289  +      <PreprocessorDefinitions>ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          290  +      <MinimalRebuild>true</MinimalRebuild>
          291  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          292  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          293  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          294  +      <PrecompiledHeader>
          295  +      </PrecompiledHeader>
          296  +      <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
          297  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          298  +      <WarningLevel>Level3</WarningLevel>
          299  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          300  +    </ClCompile>
          301  +    <Link>
          302  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          303  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          304  +      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
          305  +      <SubSystem>Console</SubSystem>
          306  +      <TargetMachine>MachineIA64</TargetMachine>
          307  +    </Link>
          308  +  </ItemDefinitionGroup>
          309  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
          310  +    <ClCompile>
          311  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          312  +      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          313  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          314  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          315  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          316  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          317  +    </ClCompile>
          318  +    <Link>
          319  +      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
          320  +    </Link>
          321  +  </ItemDefinitionGroup>
          322  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
          323  +    <Midl>
          324  +      <TargetEnvironment>Itanium</TargetEnvironment>
          325  +    </Midl>
          326  +    <ClCompile>
          327  +      <Optimization>MaxSpeed</Optimization>
          328  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          329  +      <OmitFramePointers>true</OmitFramePointers>
          330  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          331  +      <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          332  +      <StringPooling>true</StringPooling>
          333  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          334  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          335  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          336  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          337  +      <PrecompiledHeader>
          338  +      </PrecompiledHeader>
          339  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          340  +      <WarningLevel>Level3</WarningLevel>
          341  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          342  +    </ClCompile>
          343  +    <Link>
          344  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          345  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          346  +      <SubSystem>Console</SubSystem>
          347  +      <OptimizeReferences>true</OptimizeReferences>
          348  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          349  +      <TargetMachine>MachineIA64</TargetMachine>
          350  +    </Link>
          351  +  </ItemDefinitionGroup>
          352  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          353  +    <ClCompile>
          354  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          355  +      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          356  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          357  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          358  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          359  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          360  +    </ClCompile>
          361  +    <Link>
          362  +      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
          363  +    </Link>
          364  +  </ItemDefinitionGroup>
          365  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          366  +    <Midl>
          367  +      <TargetEnvironment>Itanium</TargetEnvironment>
          368  +    </Midl>
          369  +    <ClCompile>
          370  +      <Optimization>MaxSpeed</Optimization>
          371  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          372  +      <OmitFramePointers>true</OmitFramePointers>
          373  +      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          374  +      <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          375  +      <StringPooling>true</StringPooling>
          376  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          377  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          378  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          379  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          380  +      <PrecompiledHeader>
          381  +      </PrecompiledHeader>
          382  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          383  +      <WarningLevel>Level3</WarningLevel>
          384  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          385  +    </ClCompile>
          386  +    <Link>
          387  +      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
          388  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          389  +      <SubSystem>Console</SubSystem>
          390  +      <OptimizeReferences>true</OptimizeReferences>
          391  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          392  +      <TargetMachine>MachineIA64</TargetMachine>
          393  +    </Link>
          394  +  </ItemDefinitionGroup>
          395  +  <ItemGroup>
          396  +    <ClCompile Include="..\..\..\adler32.c" />
          397  +    <ClCompile Include="..\..\..\compress.c" />
          398  +    <ClCompile Include="..\..\..\crc32.c" />
          399  +    <ClCompile Include="..\..\..\deflate.c" />
          400  +    <ClCompile Include="..\..\..\infback.c" />
          401  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
          402  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
          403  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
          404  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
          405  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
          406  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
          407  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
          408  +    </ClCompile>
          409  +    <ClCompile Include="..\..\..\inffast.c" />
          410  +    <ClCompile Include="..\..\..\inflate.c" />
          411  +    <ClCompile Include="..\..\..\inftrees.c" />
          412  +    <ClCompile Include="..\..\testzlib\testzlib.c" />
          413  +    <ClCompile Include="..\..\..\trees.c" />
          414  +    <ClCompile Include="..\..\..\uncompr.c" />
          415  +    <ClCompile Include="..\..\..\zutil.c" />
          416  +  </ItemGroup>
          417  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          418  +  <ImportGroup Label="ExtensionTargets">
          419  +  </ImportGroup>
          420  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{c1f6a2e3-5da5-4955-8653-310d3efe05a9}</UniqueIdentifier>
            6  +      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm</Extensions>
            7  +    </Filter>
            8  +    <Filter Include="Header Files">
            9  +      <UniqueIdentifier>{c2aaffdc-2c95-4d6f-8466-4bec5890af2c}</UniqueIdentifier>
           10  +      <Extensions>h;hpp;hxx;hm;inl;inc</Extensions>
           11  +    </Filter>
           12  +    <Filter Include="Resource Files">
           13  +      <UniqueIdentifier>{c274fe07-05f2-461c-964b-f6341e4e7eb5}</UniqueIdentifier>
           14  +      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
           15  +    </Filter>
           16  +  </ItemGroup>
           17  +  <ItemGroup>
           18  +    <ClCompile Include="..\..\..\adler32.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +    <ClCompile Include="..\..\..\compress.c">
           22  +      <Filter>Source Files</Filter>
           23  +    </ClCompile>
           24  +    <ClCompile Include="..\..\..\crc32.c">
           25  +      <Filter>Source Files</Filter>
           26  +    </ClCompile>
           27  +    <ClCompile Include="..\..\..\deflate.c">
           28  +      <Filter>Source Files</Filter>
           29  +    </ClCompile>
           30  +    <ClCompile Include="..\..\..\infback.c">
           31  +      <Filter>Source Files</Filter>
           32  +    </ClCompile>
           33  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
           34  +      <Filter>Source Files</Filter>
           35  +    </ClCompile>
           36  +    <ClCompile Include="..\..\..\inffast.c">
           37  +      <Filter>Source Files</Filter>
           38  +    </ClCompile>
           39  +    <ClCompile Include="..\..\..\inflate.c">
           40  +      <Filter>Source Files</Filter>
           41  +    </ClCompile>
           42  +    <ClCompile Include="..\..\..\inftrees.c">
           43  +      <Filter>Source Files</Filter>
           44  +    </ClCompile>
           45  +    <ClCompile Include="..\..\testzlib\testzlib.c">
           46  +      <Filter>Source Files</Filter>
           47  +    </ClCompile>
           48  +    <ClCompile Include="..\..\..\trees.c">
           49  +      <Filter>Source Files</Filter>
           50  +    </ClCompile>
           51  +    <ClCompile Include="..\..\..\uncompr.c">
           52  +      <Filter>Source Files</Filter>
           53  +    </ClCompile>
           54  +    <ClCompile Include="..\..\..\zutil.c">
           55  +      <Filter>Source Files</Filter>
           56  +    </ClCompile>
           57  +  </ItemGroup>
           58  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="Release|Itanium">
           17  +      <Configuration>Release</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="Release|Win32">
           21  +      <Configuration>Release</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="Release|x64">
           25  +      <Configuration>Release</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +  </ItemGroup>
           29  +  <PropertyGroup Label="Globals">
           30  +    <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694366A}</ProjectGuid>
           31  +    <Keyword>Win32Proj</Keyword>
           32  +  </PropertyGroup>
           33  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           34  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           35  +    <ConfigurationType>Application</ConfigurationType>
           36  +    <CharacterSet>MultiByte</CharacterSet>
           37  +  </PropertyGroup>
           38  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           39  +    <ConfigurationType>Application</ConfigurationType>
           40  +    <CharacterSet>MultiByte</CharacterSet>
           41  +  </PropertyGroup>
           42  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           43  +    <ConfigurationType>Application</ConfigurationType>
           44  +    <CharacterSet>MultiByte</CharacterSet>
           45  +  </PropertyGroup>
           46  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           47  +    <ConfigurationType>Application</ConfigurationType>
           48  +    <CharacterSet>MultiByte</CharacterSet>
           49  +  </PropertyGroup>
           50  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           51  +    <ConfigurationType>Application</ConfigurationType>
           52  +    <CharacterSet>MultiByte</CharacterSet>
           53  +  </PropertyGroup>
           54  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           55  +    <ConfigurationType>Application</ConfigurationType>
           56  +    <CharacterSet>MultiByte</CharacterSet>
           57  +  </PropertyGroup>
           58  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           59  +  <ImportGroup Label="ExtensionSettings">
           60  +  </ImportGroup>
           61  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           62  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           63  +  </ImportGroup>
           64  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           65  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           66  +  </ImportGroup>
           67  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
           68  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           69  +  </ImportGroup>
           70  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
           71  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           72  +  </ImportGroup>
           73  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
           74  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           75  +  </ImportGroup>
           76  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
           77  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           78  +  </ImportGroup>
           79  +  <PropertyGroup Label="UserMacros" />
           80  +  <PropertyGroup>
           81  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
           82  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\</OutDir>
           83  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir>
           84  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
           85  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
           86  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\</OutDir>
           87  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir>
           88  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
           89  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
           90  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\</OutDir>
           91  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir>
           92  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
           93  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
           94  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir>
           95  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir>
           96  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental>
           97  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest>
           98  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\</OutDir>
           99  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir>
          100  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
          101  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
          102  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir>
          103  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir>
          104  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental>
          105  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest>
          106  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          107  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          108  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          109  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          110  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          111  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          112  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          113  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          114  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          115  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          116  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          117  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          118  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          119  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          120  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          121  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          122  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          123  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          124  +  </PropertyGroup>
          125  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          126  +    <ClCompile>
          127  +      <Optimization>Disabled</Optimization>
          128  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          129  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          130  +      <MinimalRebuild>true</MinimalRebuild>
          131  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          132  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          133  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          134  +      <PrecompiledHeader>
          135  +      </PrecompiledHeader>
          136  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          137  +      <WarningLevel>Level3</WarningLevel>
          138  +      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
          139  +    </ClCompile>
          140  +    <Link>
          141  +      <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          142  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          143  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          144  +      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
          145  +      <SubSystem>Console</SubSystem>
          146  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          147  +      <DataExecutionPrevention>
          148  +      </DataExecutionPrevention>
          149  +      <TargetMachine>MachineX86</TargetMachine>
          150  +    </Link>
          151  +  </ItemDefinitionGroup>
          152  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          153  +    <ClCompile>
          154  +      <Optimization>MaxSpeed</Optimization>
          155  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          156  +      <OmitFramePointers>true</OmitFramePointers>
          157  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          158  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          159  +      <StringPooling>true</StringPooling>
          160  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          161  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          162  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          163  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          164  +      <PrecompiledHeader>
          165  +      </PrecompiledHeader>
          166  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          167  +      <WarningLevel>Level3</WarningLevel>
          168  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          169  +    </ClCompile>
          170  +    <Link>
          171  +      <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          172  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          173  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          174  +      <SubSystem>Console</SubSystem>
          175  +      <OptimizeReferences>true</OptimizeReferences>
          176  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          177  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          178  +      <DataExecutionPrevention>
          179  +      </DataExecutionPrevention>
          180  +      <TargetMachine>MachineX86</TargetMachine>
          181  +    </Link>
          182  +  </ItemDefinitionGroup>
          183  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          184  +    <Midl>
          185  +      <TargetEnvironment>X64</TargetEnvironment>
          186  +    </Midl>
          187  +    <ClCompile>
          188  +      <Optimization>Disabled</Optimization>
          189  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          190  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          191  +      <MinimalRebuild>true</MinimalRebuild>
          192  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          193  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          194  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          195  +      <PrecompiledHeader>
          196  +      </PrecompiledHeader>
          197  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          198  +      <WarningLevel>Level3</WarningLevel>
          199  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          200  +    </ClCompile>
          201  +    <Link>
          202  +      <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          203  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          204  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          205  +      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
          206  +      <SubSystem>Console</SubSystem>
          207  +      <TargetMachine>MachineX64</TargetMachine>
          208  +    </Link>
          209  +  </ItemDefinitionGroup>
          210  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          211  +    <Midl>
          212  +      <TargetEnvironment>Itanium</TargetEnvironment>
          213  +    </Midl>
          214  +    <ClCompile>
          215  +      <Optimization>Disabled</Optimization>
          216  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          217  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          218  +      <MinimalRebuild>true</MinimalRebuild>
          219  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          220  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          221  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          222  +      <PrecompiledHeader>
          223  +      </PrecompiledHeader>
          224  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          225  +      <WarningLevel>Level3</WarningLevel>
          226  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          227  +    </ClCompile>
          228  +    <Link>
          229  +      <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          230  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          231  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          232  +      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
          233  +      <SubSystem>Console</SubSystem>
          234  +      <TargetMachine>MachineIA64</TargetMachine>
          235  +    </Link>
          236  +  </ItemDefinitionGroup>
          237  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          238  +    <Midl>
          239  +      <TargetEnvironment>X64</TargetEnvironment>
          240  +    </Midl>
          241  +    <ClCompile>
          242  +      <Optimization>MaxSpeed</Optimization>
          243  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          244  +      <OmitFramePointers>true</OmitFramePointers>
          245  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          246  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          247  +      <StringPooling>true</StringPooling>
          248  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          249  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          250  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          251  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          252  +      <PrecompiledHeader>
          253  +      </PrecompiledHeader>
          254  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          255  +      <WarningLevel>Level3</WarningLevel>
          256  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          257  +    </ClCompile>
          258  +    <Link>
          259  +      <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          260  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          261  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          262  +      <SubSystem>Console</SubSystem>
          263  +      <OptimizeReferences>true</OptimizeReferences>
          264  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          265  +      <TargetMachine>MachineX64</TargetMachine>
          266  +    </Link>
          267  +  </ItemDefinitionGroup>
          268  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          269  +    <Midl>
          270  +      <TargetEnvironment>Itanium</TargetEnvironment>
          271  +    </Midl>
          272  +    <ClCompile>
          273  +      <Optimization>MaxSpeed</Optimization>
          274  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          275  +      <OmitFramePointers>true</OmitFramePointers>
          276  +      <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          277  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          278  +      <StringPooling>true</StringPooling>
          279  +      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
          280  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          281  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          282  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          283  +      <PrecompiledHeader>
          284  +      </PrecompiledHeader>
          285  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          286  +      <WarningLevel>Level3</WarningLevel>
          287  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          288  +    </ClCompile>
          289  +    <Link>
          290  +      <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
          291  +      <OutputFile>$(OutDir)testzlibdll.exe</OutputFile>
          292  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          293  +      <SubSystem>Console</SubSystem>
          294  +      <OptimizeReferences>true</OptimizeReferences>
          295  +      <EnableCOMDATFolding>true</EnableCOMDATFolding>
          296  +      <TargetMachine>MachineIA64</TargetMachine>
          297  +    </Link>
          298  +  </ItemDefinitionGroup>
          299  +  <ItemGroup>
          300  +    <ClCompile Include="..\..\testzlib\testzlib.c" />
          301  +  </ItemGroup>
          302  +  <ItemGroup>
          303  +    <ProjectReference Include="zlibvc.vcxproj">
          304  +      <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project>
          305  +    </ProjectReference>
          306  +  </ItemGroup>
          307  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          308  +  <ImportGroup Label="ExtensionTargets">
          309  +  </ImportGroup>
          310  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{fa61a89f-93fc-4c89-b29e-36224b7592f4}</UniqueIdentifier>
            6  +      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm</Extensions>
            7  +    </Filter>
            8  +    <Filter Include="Header Files">
            9  +      <UniqueIdentifier>{d4b85da0-2ba2-4934-b57f-e2584e3848ee}</UniqueIdentifier>
           10  +      <Extensions>h;hpp;hxx;hm;inl;inc</Extensions>
           11  +    </Filter>
           12  +    <Filter Include="Resource Files">
           13  +      <UniqueIdentifier>{e573e075-00bd-4a7d-bd67-a8cc9bfc5aca}</UniqueIdentifier>
           14  +      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
           15  +    </Filter>
           16  +  </ItemGroup>
           17  +  <ItemGroup>
           18  +    <ClCompile Include="..\..\testzlib\testzlib.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +  </ItemGroup>
           22  +</Project>

Added compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlib.rc.

            1  +#include <windows.h>
            2  +
            3  +#define IDR_VERSION1  1
            4  +IDR_VERSION1	VERSIONINFO	MOVEABLE IMPURE LOADONCALL DISCARDABLE
            5  +  FILEVERSION	 1.2.7,0
            6  +  PRODUCTVERSION 1.2.7,0
            7  +  FILEFLAGSMASK	VS_FFI_FILEFLAGSMASK
            8  +  FILEFLAGS	0
            9  +  FILEOS	VOS_DOS_WINDOWS32
           10  +  FILETYPE	VFT_DLL
           11  +  FILESUBTYPE	0	// not used
           12  +BEGIN
           13  +  BLOCK "StringFileInfo"
           14  +  BEGIN
           15  +    BLOCK "040904E4"
           16  +    //language ID = U.S. English, char set = Windows, Multilingual
           17  +
           18  +    BEGIN
           19  +      VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
           20  +      VALUE "FileVersion",	"1.2.7\0"
           21  +      VALUE "InternalName",	"zlib\0"
           22  +      VALUE "OriginalFilename",	"zlib.dll\0"
           23  +      VALUE "ProductName",	"ZLib.DLL\0"
           24  +      VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
           25  +      VALUE "LegalCopyright", "(C) 1995-2012 Jean-loup Gailly & Mark Adler\0"
           26  +    END
           27  +  END
           28  +  BLOCK "VarFileInfo"
           29  +  BEGIN
           30  +    VALUE "Translation", 0x0409, 1252
           31  +  END
           32  +END

Added compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium">
           17  +      <Configuration>ReleaseWithoutAsm</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Win32">
           21  +      <Configuration>ReleaseWithoutAsm</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="ReleaseWithoutAsm|x64">
           25  +      <Configuration>ReleaseWithoutAsm</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +    <ProjectConfiguration Include="Release|Itanium">
           29  +      <Configuration>Release</Configuration>
           30  +      <Platform>Itanium</Platform>
           31  +    </ProjectConfiguration>
           32  +    <ProjectConfiguration Include="Release|Win32">
           33  +      <Configuration>Release</Configuration>
           34  +      <Platform>Win32</Platform>
           35  +    </ProjectConfiguration>
           36  +    <ProjectConfiguration Include="Release|x64">
           37  +      <Configuration>Release</Configuration>
           38  +      <Platform>x64</Platform>
           39  +    </ProjectConfiguration>
           40  +  </ItemGroup>
           41  +  <PropertyGroup Label="Globals">
           42  +    <ProjectGuid>{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}</ProjectGuid>
           43  +  </PropertyGroup>
           44  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           45  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration">
           46  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           47  +    <UseOfMfc>false</UseOfMfc>
           48  +  </PropertyGroup>
           49  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           50  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           51  +    <UseOfMfc>false</UseOfMfc>
           52  +  </PropertyGroup>
           53  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           54  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           55  +    <UseOfMfc>false</UseOfMfc>
           56  +  </PropertyGroup>
           57  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration">
           58  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           59  +    <UseOfMfc>false</UseOfMfc>
           60  +  </PropertyGroup>
           61  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           62  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           63  +    <UseOfMfc>false</UseOfMfc>
           64  +  </PropertyGroup>
           65  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           66  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           67  +    <UseOfMfc>false</UseOfMfc>
           68  +  </PropertyGroup>
           69  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration">
           70  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           71  +    <UseOfMfc>false</UseOfMfc>
           72  +  </PropertyGroup>
           73  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           74  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           75  +    <UseOfMfc>false</UseOfMfc>
           76  +  </PropertyGroup>
           77  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           78  +    <ConfigurationType>StaticLibrary</ConfigurationType>
           79  +    <UseOfMfc>false</UseOfMfc>
           80  +  </PropertyGroup>
           81  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           82  +  <ImportGroup Label="ExtensionSettings">
           83  +  </ImportGroup>
           84  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets">
           85  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           86  +  </ImportGroup>
           87  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           88  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           89  +  </ImportGroup>
           90  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           91  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           92  +  </ImportGroup>
           93  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets">
           94  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           95  +  </ImportGroup>
           96  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
           97  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           98  +  </ImportGroup>
           99  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
          100  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          101  +  </ImportGroup>
          102  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets">
          103  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          104  +  </ImportGroup>
          105  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
          106  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          107  +  </ImportGroup>
          108  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
          109  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          110  +  </ImportGroup>
          111  +  <PropertyGroup Label="UserMacros" />
          112  +  <PropertyGroup>
          113  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
          114  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\</OutDir>
          115  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir>
          116  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\</OutDir>
          117  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir>
          118  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\</OutDir>
          119  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir>
          120  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\</OutDir>
          121  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir>
          122  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir>
          123  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir>
          124  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\</OutDir>
          125  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir>
          126  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir>
          127  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir>
          128  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\</OutDir>
          129  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir>
          130  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir>
          131  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir>
          132  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          133  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          134  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          135  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          136  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          137  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          138  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          139  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          140  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          141  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          142  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          143  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          144  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          145  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          146  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          147  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          148  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          149  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          150  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          151  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          152  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          153  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          154  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          155  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          156  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          157  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          158  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          159  +  </PropertyGroup>
          160  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          161  +    <ClCompile>
          162  +      <Optimization>Disabled</Optimization>
          163  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          164  +      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          165  +      <ExceptionHandling>
          166  +      </ExceptionHandling>
          167  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          168  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          169  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          170  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          171  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          172  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          173  +      <WarningLevel>Level3</WarningLevel>
          174  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          175  +      <DebugInformationFormat>OldStyle</DebugInformationFormat>
          176  +    </ClCompile>
          177  +    <ResourceCompile>
          178  +      <Culture>0x040c</Culture>
          179  +    </ResourceCompile>
          180  +    <Lib>
          181  +      <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          182  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          183  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          184  +    </Lib>
          185  +  </ItemDefinitionGroup>
          186  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          187  +    <ClCompile>
          188  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          189  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          190  +      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          191  +      <StringPooling>true</StringPooling>
          192  +      <ExceptionHandling>
          193  +      </ExceptionHandling>
          194  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          195  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          196  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          197  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          198  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          199  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          200  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          201  +      <WarningLevel>Level3</WarningLevel>
          202  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          203  +    </ClCompile>
          204  +    <ResourceCompile>
          205  +      <Culture>0x040c</Culture>
          206  +    </ResourceCompile>
          207  +    <Lib>
          208  +      <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          209  +      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
          210  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          211  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          212  +    </Lib>
          213  +  </ItemDefinitionGroup>
          214  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
          215  +    <ClCompile>
          216  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          217  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          218  +      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          219  +      <StringPooling>true</StringPooling>
          220  +      <ExceptionHandling>
          221  +      </ExceptionHandling>
          222  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          223  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          224  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          225  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          226  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          227  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          228  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          229  +      <WarningLevel>Level3</WarningLevel>
          230  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          231  +    </ClCompile>
          232  +    <ResourceCompile>
          233  +      <Culture>0x040c</Culture>
          234  +    </ResourceCompile>
          235  +    <Lib>
          236  +      <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          237  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          238  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          239  +    </Lib>
          240  +  </ItemDefinitionGroup>
          241  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          242  +    <Midl>
          243  +      <TargetEnvironment>X64</TargetEnvironment>
          244  +    </Midl>
          245  +    <ClCompile>
          246  +      <Optimization>Disabled</Optimization>
          247  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          248  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          249  +      <ExceptionHandling>
          250  +      </ExceptionHandling>
          251  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          252  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          253  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          254  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          255  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          256  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          257  +      <WarningLevel>Level3</WarningLevel>
          258  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          259  +      <DebugInformationFormat>OldStyle</DebugInformationFormat>
          260  +    </ClCompile>
          261  +    <ResourceCompile>
          262  +      <Culture>0x040c</Culture>
          263  +    </ResourceCompile>
          264  +    <Lib>
          265  +      <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          266  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          267  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          268  +    </Lib>
          269  +  </ItemDefinitionGroup>
          270  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          271  +    <Midl>
          272  +      <TargetEnvironment>Itanium</TargetEnvironment>
          273  +    </Midl>
          274  +    <ClCompile>
          275  +      <Optimization>Disabled</Optimization>
          276  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          277  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          278  +      <ExceptionHandling>
          279  +      </ExceptionHandling>
          280  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          281  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          282  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          283  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          284  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          285  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          286  +      <WarningLevel>Level3</WarningLevel>
          287  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          288  +      <DebugInformationFormat>OldStyle</DebugInformationFormat>
          289  +    </ClCompile>
          290  +    <ResourceCompile>
          291  +      <Culture>0x040c</Culture>
          292  +    </ResourceCompile>
          293  +    <Lib>
          294  +      <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          295  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          296  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          297  +    </Lib>
          298  +  </ItemDefinitionGroup>
          299  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          300  +    <Midl>
          301  +      <TargetEnvironment>X64</TargetEnvironment>
          302  +    </Midl>
          303  +    <ClCompile>
          304  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          305  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          306  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          307  +      <StringPooling>true</StringPooling>
          308  +      <ExceptionHandling>
          309  +      </ExceptionHandling>
          310  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          311  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          312  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          313  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          314  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          315  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          316  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          317  +      <WarningLevel>Level3</WarningLevel>
          318  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          319  +    </ClCompile>
          320  +    <ResourceCompile>
          321  +      <Culture>0x040c</Culture>
          322  +    </ResourceCompile>
          323  +    <Lib>
          324  +      <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          325  +      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
          326  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          327  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          328  +    </Lib>
          329  +  </ItemDefinitionGroup>
          330  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          331  +    <Midl>
          332  +      <TargetEnvironment>Itanium</TargetEnvironment>
          333  +    </Midl>
          334  +    <ClCompile>
          335  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          336  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          337  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          338  +      <StringPooling>true</StringPooling>
          339  +      <ExceptionHandling>
          340  +      </ExceptionHandling>
          341  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          342  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          343  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          344  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          345  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          346  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          347  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          348  +      <WarningLevel>Level3</WarningLevel>
          349  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          350  +    </ClCompile>
          351  +    <ResourceCompile>
          352  +      <Culture>0x040c</Culture>
          353  +    </ResourceCompile>
          354  +    <Lib>
          355  +      <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          356  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          357  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          358  +    </Lib>
          359  +  </ItemDefinitionGroup>
          360  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
          361  +    <Midl>
          362  +      <TargetEnvironment>X64</TargetEnvironment>
          363  +    </Midl>
          364  +    <ClCompile>
          365  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          366  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          367  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          368  +      <StringPooling>true</StringPooling>
          369  +      <ExceptionHandling>
          370  +      </ExceptionHandling>
          371  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          372  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          373  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          374  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          375  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          376  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          377  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          378  +      <WarningLevel>Level3</WarningLevel>
          379  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          380  +    </ClCompile>
          381  +    <ResourceCompile>
          382  +      <Culture>0x040c</Culture>
          383  +    </ResourceCompile>
          384  +    <Lib>
          385  +      <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          386  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          387  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          388  +    </Lib>
          389  +  </ItemDefinitionGroup>
          390  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
          391  +    <Midl>
          392  +      <TargetEnvironment>Itanium</TargetEnvironment>
          393  +    </Midl>
          394  +    <ClCompile>
          395  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          396  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          397  +      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          398  +      <StringPooling>true</StringPooling>
          399  +      <ExceptionHandling>
          400  +      </ExceptionHandling>
          401  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          402  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          403  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          404  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile>
          405  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          406  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          407  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          408  +      <WarningLevel>Level3</WarningLevel>
          409  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          410  +    </ClCompile>
          411  +    <ResourceCompile>
          412  +      <Culture>0x040c</Culture>
          413  +    </ResourceCompile>
          414  +    <Lib>
          415  +      <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
          416  +      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
          417  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          418  +    </Lib>
          419  +  </ItemDefinitionGroup>
          420  +  <ItemGroup>
          421  +    <ClCompile Include="..\..\..\adler32.c" />
          422  +    <ClCompile Include="..\..\..\compress.c" />
          423  +    <ClCompile Include="..\..\..\crc32.c" />
          424  +    <ClCompile Include="..\..\..\deflate.c" />
          425  +    <ClCompile Include="..\..\..\gzclose.c" />
          426  +    <ClCompile Include="..\..\..\gzlib.c" />
          427  +    <ClCompile Include="..\..\..\gzread.c" />
          428  +    <ClCompile Include="..\..\..\gzwrite.c" />
          429  +    <ClCompile Include="..\..\..\infback.c" />
          430  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
          431  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
          432  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
          433  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
          434  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
          435  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
          436  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
          437  +    </ClCompile>
          438  +    <ClCompile Include="..\..\..\inffast.c" />
          439  +    <ClCompile Include="..\..\..\inflate.c" />
          440  +    <ClCompile Include="..\..\..\inftrees.c" />
          441  +    <ClCompile Include="..\..\minizip\ioapi.c" />
          442  +    <ClCompile Include="..\..\..\trees.c" />
          443  +    <ClCompile Include="..\..\..\uncompr.c" />
          444  +    <ClCompile Include="..\..\minizip\unzip.c" />
          445  +    <ClCompile Include="..\..\minizip\zip.c" />
          446  +    <ClCompile Include="..\..\..\zutil.c" />
          447  +  </ItemGroup>
          448  +  <ItemGroup>
          449  +    <ResourceCompile Include="zlib.rc" />
          450  +  </ItemGroup>
          451  +  <ItemGroup>
          452  +    <None Include="zlibvc.def" />
          453  +  </ItemGroup>
          454  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          455  +  <ImportGroup Label="ExtensionTargets">
          456  +  </ImportGroup>
          457  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{174213f6-7f66-4ae8-a3a8-a1e0a1e6ffdd}</UniqueIdentifier>
            6  +    </Filter>
            7  +  </ItemGroup>
            8  +  <ItemGroup>
            9  +    <ClCompile Include="..\..\..\adler32.c">
           10  +      <Filter>Source Files</Filter>
           11  +    </ClCompile>
           12  +    <ClCompile Include="..\..\..\compress.c">
           13  +      <Filter>Source Files</Filter>
           14  +    </ClCompile>
           15  +    <ClCompile Include="..\..\..\crc32.c">
           16  +      <Filter>Source Files</Filter>
           17  +    </ClCompile>
           18  +    <ClCompile Include="..\..\..\deflate.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +    <ClCompile Include="..\..\..\gzclose.c">
           22  +      <Filter>Source Files</Filter>
           23  +    </ClCompile>
           24  +    <ClCompile Include="..\..\..\gzlib.c">
           25  +      <Filter>Source Files</Filter>
           26  +    </ClCompile>
           27  +    <ClCompile Include="..\..\..\gzread.c">
           28  +      <Filter>Source Files</Filter>
           29  +    </ClCompile>
           30  +    <ClCompile Include="..\..\..\gzwrite.c">
           31  +      <Filter>Source Files</Filter>
           32  +    </ClCompile>
           33  +    <ClCompile Include="..\..\..\infback.c">
           34  +      <Filter>Source Files</Filter>
           35  +    </ClCompile>
           36  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
           37  +      <Filter>Source Files</Filter>
           38  +    </ClCompile>
           39  +    <ClCompile Include="..\..\..\inffast.c">
           40  +      <Filter>Source Files</Filter>
           41  +    </ClCompile>
           42  +    <ClCompile Include="..\..\..\inflate.c">
           43  +      <Filter>Source Files</Filter>
           44  +    </ClCompile>
           45  +    <ClCompile Include="..\..\..\inftrees.c">
           46  +      <Filter>Source Files</Filter>
           47  +    </ClCompile>
           48  +    <ClCompile Include="..\..\minizip\ioapi.c">
           49  +      <Filter>Source Files</Filter>
           50  +    </ClCompile>
           51  +    <ClCompile Include="..\..\..\trees.c">
           52  +      <Filter>Source Files</Filter>
           53  +    </ClCompile>
           54  +    <ClCompile Include="..\..\..\uncompr.c">
           55  +      <Filter>Source Files</Filter>
           56  +    </ClCompile>
           57  +    <ClCompile Include="..\..\minizip\unzip.c">
           58  +      <Filter>Source Files</Filter>
           59  +    </ClCompile>
           60  +    <ClCompile Include="..\..\minizip\zip.c">
           61  +      <Filter>Source Files</Filter>
           62  +    </ClCompile>
           63  +    <ClCompile Include="..\..\..\zutil.c">
           64  +      <Filter>Source Files</Filter>
           65  +    </ClCompile>
           66  +  </ItemGroup>
           67  +  <ItemGroup>
           68  +    <ResourceCompile Include="zlib.rc">
           69  +      <Filter>Source Files</Filter>
           70  +    </ResourceCompile>
           71  +  </ItemGroup>
           72  +  <ItemGroup>
           73  +    <None Include="zlibvc.def">
           74  +      <Filter>Source Files</Filter>
           75  +    </None>
           76  +  </ItemGroup>
           77  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlibvc.def.

            1  +LIBRARY
            2  +; zlib data compression and ZIP file I/O library
            3  +
            4  +VERSION		1.2.7
            5  +
            6  +EXPORTS
            7  +        adler32                                  @1
            8  +        compress                                 @2
            9  +        crc32                                    @3
           10  +        deflate                                  @4
           11  +        deflateCopy                              @5
           12  +        deflateEnd                               @6
           13  +        deflateInit2_                            @7
           14  +        deflateInit_                             @8
           15  +        deflateParams                            @9
           16  +        deflateReset                             @10
           17  +        deflateSetDictionary                     @11
           18  +        gzclose                                  @12
           19  +        gzdopen                                  @13
           20  +        gzerror                                  @14
           21  +        gzflush                                  @15
           22  +        gzopen                                   @16
           23  +        gzread                                   @17
           24  +        gzwrite                                  @18
           25  +        inflate                                  @19
           26  +        inflateEnd                               @20
           27  +        inflateInit2_                            @21
           28  +        inflateInit_                             @22
           29  +        inflateReset                             @23
           30  +        inflateSetDictionary                     @24
           31  +        inflateSync                              @25
           32  +        uncompress                               @26
           33  +        zlibVersion                              @27
           34  +        gzprintf                                 @28
           35  +        gzputc                                   @29
           36  +        gzgetc                                   @30
           37  +        gzseek                                   @31
           38  +        gzrewind                                 @32
           39  +        gztell                                   @33
           40  +        gzeof                                    @34
           41  +        gzsetparams                              @35
           42  +        zError                                   @36
           43  +        inflateSyncPoint                         @37
           44  +        get_crc_table                            @38
           45  +        compress2                                @39
           46  +        gzputs                                   @40
           47  +        gzgets                                   @41
           48  +        inflateCopy                              @42
           49  +        inflateBackInit_                         @43
           50  +        inflateBack                              @44
           51  +        inflateBackEnd                           @45
           52  +        compressBound                            @46
           53  +        deflateBound                             @47
           54  +        gzclearerr                               @48
           55  +        gzungetc                                 @49
           56  +        zlibCompileFlags                         @50
           57  +        deflatePrime                             @51
           58  +        deflatePending                           @52
           59  +
           60  +        unzOpen                                  @61
           61  +        unzClose                                 @62
           62  +        unzGetGlobalInfo                         @63
           63  +        unzGetCurrentFileInfo                    @64
           64  +        unzGoToFirstFile                         @65
           65  +        unzGoToNextFile                          @66
           66  +        unzOpenCurrentFile                       @67
           67  +        unzReadCurrentFile                       @68
           68  +        unzOpenCurrentFile3                      @69
           69  +        unztell                                  @70
           70  +        unzeof                                   @71
           71  +        unzCloseCurrentFile                      @72
           72  +        unzGetGlobalComment                      @73
           73  +        unzStringFileNameCompare                 @74
           74  +        unzLocateFile                            @75
           75  +        unzGetLocalExtrafield                    @76
           76  +        unzOpen2                                 @77
           77  +        unzOpenCurrentFile2                      @78
           78  +        unzOpenCurrentFilePassword               @79
           79  +
           80  +        zipOpen                                  @80
           81  +        zipOpenNewFileInZip                      @81
           82  +        zipWriteInFileInZip                      @82
           83  +        zipCloseFileInZip                        @83
           84  +        zipClose                                 @84
           85  +        zipOpenNewFileInZip2                     @86
           86  +        zipCloseFileInZipRaw                     @87
           87  +        zipOpen2                                 @88
           88  +        zipOpenNewFileInZip3                     @89
           89  +
           90  +        unzGetFilePos                            @100
           91  +        unzGoToFilePos                           @101
           92  +
           93  +        fill_win32_filefunc                      @110
           94  +
           95  +; zlibwapi v1.2.4 added:
           96  +        fill_win32_filefunc64                   @111
           97  +        fill_win32_filefunc64A                  @112
           98  +        fill_win32_filefunc64W                  @113
           99  +
          100  +        unzOpen64                               @120
          101  +        unzOpen2_64                             @121
          102  +        unzGetGlobalInfo64                      @122
          103  +        unzGetCurrentFileInfo64                 @124
          104  +        unzGetCurrentFileZStreamPos64           @125
          105  +        unztell64                               @126
          106  +        unzGetFilePos64                         @127
          107  +        unzGoToFilePos64                        @128
          108  +
          109  +        zipOpen64                               @130
          110  +        zipOpen2_64                             @131
          111  +        zipOpenNewFileInZip64                   @132
          112  +        zipOpenNewFileInZip2_64                 @133
          113  +        zipOpenNewFileInZip3_64                 @134
          114  +        zipOpenNewFileInZip4_64                 @135
          115  +        zipCloseFileInZipRaw64                  @136
          116  +
          117  +; zlib1 v1.2.4 added:
          118  +        adler32_combine                         @140
          119  +        crc32_combine                           @142
          120  +        deflateSetHeader                        @144
          121  +        deflateTune                             @145
          122  +        gzbuffer                                @146
          123  +        gzclose_r                               @147
          124  +        gzclose_w                               @148
          125  +        gzdirect                                @149
          126  +        gzoffset                                @150
          127  +        inflateGetHeader                        @156
          128  +        inflateMark                             @157
          129  +        inflatePrime                            @158
          130  +        inflateReset2                           @159
          131  +        inflateUndermine                        @160
          132  +
          133  +; zlib1 v1.2.6 added:
          134  +        gzgetc_                                 @161
          135  +        inflateResetKeep                        @163
          136  +        deflateResetKeep                        @164
          137  +
          138  +; zlib1 v1.2.7 added:
          139  +        gzopen_w                                @165

Added compat/zlib/contrib/vstudio/vc10/zlibvc.sln.

            1  +
            2  +Microsoft Visual Studio Solution File, Format Version 11.00
            3  +# Visual Studio 2010
            4  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}"
            5  +EndProject
            6  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}"
            7  +EndProject
            8  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}"
            9  +EndProject
           10  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}"
           11  +EndProject
           12  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}"
           13  +EndProject
           14  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}"
           15  +EndProject
           16  +Global
           17  +	GlobalSection(SolutionConfigurationPlatforms) = preSolution
           18  +		Debug|Itanium = Debug|Itanium
           19  +		Debug|Win32 = Debug|Win32
           20  +		Debug|x64 = Debug|x64
           21  +		Release|Itanium = Release|Itanium
           22  +		Release|Win32 = Release|Win32
           23  +		Release|x64 = Release|x64
           24  +		ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium
           25  +		ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32
           26  +		ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64
           27  +	EndGlobalSection
           28  +	GlobalSection(ProjectConfigurationPlatforms) = postSolution
           29  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium
           30  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium
           31  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32
           32  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32
           33  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64
           34  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64
           35  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium
           36  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium
           37  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32
           38  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32
           39  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64
           40  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64
           41  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           42  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           43  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           44  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           45  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           46  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           47  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium
           48  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium
           49  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32
           50  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32
           51  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64
           52  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64
           53  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium
           54  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium
           55  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32
           56  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32
           57  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64
           58  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64
           59  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           60  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           61  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           62  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           63  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           64  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           65  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium
           66  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium
           67  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32
           68  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32
           69  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64
           70  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64
           71  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium
           72  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium
           73  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32
           74  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32
           75  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64
           76  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64
           77  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           78  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           79  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           80  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           81  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           82  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           83  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium
           84  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium
           85  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32
           86  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32
           87  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64
           88  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64
           89  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium
           90  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium
           91  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32
           92  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32
           93  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64
           94  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64
           95  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
           96  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
           97  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
           98  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
           99  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium
          100  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium
          101  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32
          102  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32
          103  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64
          104  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64
          105  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium
          106  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium
          107  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32
          108  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32
          109  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64
          110  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64
          111  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
          112  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
          113  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
          114  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
          115  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium
          116  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium
          117  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32
          118  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32
          119  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64
          120  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64
          121  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium
          122  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium
          123  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32
          124  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32
          125  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64
          126  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64
          127  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
          128  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
          129  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
          130  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
          131  +	EndGlobalSection
          132  +	GlobalSection(SolutionProperties) = preSolution
          133  +		HideSolutionNode = FALSE
          134  +	EndGlobalSection
          135  +EndGlobal

Added compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup Label="ProjectConfigurations">
            4  +    <ProjectConfiguration Include="Debug|Itanium">
            5  +      <Configuration>Debug</Configuration>
            6  +      <Platform>Itanium</Platform>
            7  +    </ProjectConfiguration>
            8  +    <ProjectConfiguration Include="Debug|Win32">
            9  +      <Configuration>Debug</Configuration>
           10  +      <Platform>Win32</Platform>
           11  +    </ProjectConfiguration>
           12  +    <ProjectConfiguration Include="Debug|x64">
           13  +      <Configuration>Debug</Configuration>
           14  +      <Platform>x64</Platform>
           15  +    </ProjectConfiguration>
           16  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium">
           17  +      <Configuration>ReleaseWithoutAsm</Configuration>
           18  +      <Platform>Itanium</Platform>
           19  +    </ProjectConfiguration>
           20  +    <ProjectConfiguration Include="ReleaseWithoutAsm|Win32">
           21  +      <Configuration>ReleaseWithoutAsm</Configuration>
           22  +      <Platform>Win32</Platform>
           23  +    </ProjectConfiguration>
           24  +    <ProjectConfiguration Include="ReleaseWithoutAsm|x64">
           25  +      <Configuration>ReleaseWithoutAsm</Configuration>
           26  +      <Platform>x64</Platform>
           27  +    </ProjectConfiguration>
           28  +    <ProjectConfiguration Include="Release|Itanium">
           29  +      <Configuration>Release</Configuration>
           30  +      <Platform>Itanium</Platform>
           31  +    </ProjectConfiguration>
           32  +    <ProjectConfiguration Include="Release|Win32">
           33  +      <Configuration>Release</Configuration>
           34  +      <Platform>Win32</Platform>
           35  +    </ProjectConfiguration>
           36  +    <ProjectConfiguration Include="Release|x64">
           37  +      <Configuration>Release</Configuration>
           38  +      <Platform>x64</Platform>
           39  +    </ProjectConfiguration>
           40  +  </ItemGroup>
           41  +  <PropertyGroup Label="Globals">
           42  +    <ProjectGuid>{8FD826F8-3739-44E6-8CC8-997122E53B8D}</ProjectGuid>
           43  +  </PropertyGroup>
           44  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
           45  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
           46  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           47  +    <UseOfMfc>false</UseOfMfc>
           48  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           49  +  </PropertyGroup>
           50  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration">
           51  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           52  +    <UseOfMfc>false</UseOfMfc>
           53  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           54  +  </PropertyGroup>
           55  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
           56  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           57  +    <UseOfMfc>false</UseOfMfc>
           58  +  </PropertyGroup>
           59  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration">
           60  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           61  +    <UseOfMfc>false</UseOfMfc>
           62  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           63  +  </PropertyGroup>
           64  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration">
           65  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           66  +    <UseOfMfc>false</UseOfMfc>
           67  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           68  +  </PropertyGroup>
           69  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration">
           70  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           71  +    <UseOfMfc>false</UseOfMfc>
           72  +  </PropertyGroup>
           73  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
           74  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           75  +    <UseOfMfc>false</UseOfMfc>
           76  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           77  +  </PropertyGroup>
           78  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration">
           79  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           80  +    <UseOfMfc>false</UseOfMfc>
           81  +    <WholeProgramOptimization>true</WholeProgramOptimization>
           82  +  </PropertyGroup>
           83  +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
           84  +    <ConfigurationType>DynamicLibrary</ConfigurationType>
           85  +    <UseOfMfc>false</UseOfMfc>
           86  +  </PropertyGroup>
           87  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
           88  +  <ImportGroup Label="ExtensionSettings">
           89  +  </ImportGroup>
           90  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
           91  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           92  +  </ImportGroup>
           93  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets">
           94  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           95  +  </ImportGroup>
           96  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
           97  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
           98  +  </ImportGroup>
           99  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets">
          100  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          101  +  </ImportGroup>
          102  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets">
          103  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          104  +  </ImportGroup>
          105  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets">
          106  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          107  +  </ImportGroup>
          108  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
          109  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          110  +  </ImportGroup>
          111  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets">
          112  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          113  +  </ImportGroup>
          114  +  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
          115  +    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
          116  +  </ImportGroup>
          117  +  <PropertyGroup Label="UserMacros" />
          118  +  <PropertyGroup>
          119  +    <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
          120  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\</OutDir>
          121  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir>
          122  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
          123  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
          124  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\</OutDir>
          125  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir>
          126  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental>
          127  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest>
          128  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\</OutDir>
          129  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir>
          130  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
          131  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
          132  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\</OutDir>
          133  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir>
          134  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
          135  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
          136  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir>
          137  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir>
          138  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental>
          139  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest>
          140  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\</OutDir>
          141  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir>
          142  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</LinkIncremental>
          143  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest>
          144  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir>
          145  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir>
          146  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental>
          147  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest>
          148  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\</OutDir>
          149  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir>
          150  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
          151  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
          152  +    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir>
          153  +    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir>
          154  +    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental>
          155  +    <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest>
          156  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          157  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          158  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" />
          159  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          160  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          161  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
          162  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          163  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          164  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
          165  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          166  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          167  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" />
          168  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          169  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          170  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" />
          171  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          172  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          173  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" />
          174  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet>
          175  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          176  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" />
          177  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
          178  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          179  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
          180  +    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
          181  +    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          182  +    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
          183  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">zlibwapi</TargetName>
          184  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">zlibwapi</TargetName>
          185  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">zlibwapi</TargetName>
          186  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">zlibwapi</TargetName>
          187  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">zlibwapi</TargetName>
          188  +    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">zlibwapi</TargetName>
          189  +  </PropertyGroup>
          190  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
          191  +    <Midl>
          192  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          193  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          194  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          195  +      <TargetEnvironment>Win32</TargetEnvironment>
          196  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          197  +    </Midl>
          198  +    <ClCompile>
          199  +      <Optimization>Disabled</Optimization>
          200  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          201  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          202  +      <ExceptionHandling>
          203  +      </ExceptionHandling>
          204  +      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
          205  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          206  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          207  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          208  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          209  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          210  +      <BrowseInformation>
          211  +      </BrowseInformation>
          212  +      <WarningLevel>Level3</WarningLevel>
          213  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          214  +      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
          215  +    </ClCompile>
          216  +    <ResourceCompile>
          217  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          218  +      <Culture>0x040c</Culture>
          219  +    </ResourceCompile>
          220  +    <Link>
          221  +      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
          222  +      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
          223  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          224  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          225  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          226  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          227  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          228  +      <GenerateMapFile>true</GenerateMapFile>
          229  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          230  +      <SubSystem>Windows</SubSystem>
          231  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          232  +      <DataExecutionPrevention>
          233  +      </DataExecutionPrevention>
          234  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          235  +    </Link>
          236  +    <PreBuildEvent>
          237  +      <Command>cd ..\..\masmx86
          238  +bld_ml32.bat</Command>
          239  +    </PreBuildEvent>
          240  +  </ItemDefinitionGroup>
          241  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
          242  +    <Midl>
          243  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          244  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          245  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          246  +      <TargetEnvironment>Win32</TargetEnvironment>
          247  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          248  +    </Midl>
          249  +    <ClCompile>
          250  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          251  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          252  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          253  +      <StringPooling>true</StringPooling>
          254  +      <ExceptionHandling>
          255  +      </ExceptionHandling>
          256  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          257  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          258  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          259  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          260  +      <AssemblerOutput>All</AssemblerOutput>
          261  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          262  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          263  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          264  +      <BrowseInformation>
          265  +      </BrowseInformation>
          266  +      <WarningLevel>Level3</WarningLevel>
          267  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          268  +    </ClCompile>
          269  +    <ResourceCompile>
          270  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          271  +      <Culture>0x040c</Culture>
          272  +    </ResourceCompile>
          273  +    <Link>
          274  +      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
          275  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          276  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          277  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          278  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          279  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          280  +      <GenerateMapFile>true</GenerateMapFile>
          281  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          282  +      <SubSystem>Windows</SubSystem>
          283  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          284  +      <DataExecutionPrevention>
          285  +      </DataExecutionPrevention>
          286  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          287  +    </Link>
          288  +  </ItemDefinitionGroup>
          289  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
          290  +    <Midl>
          291  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          292  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          293  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          294  +      <TargetEnvironment>Win32</TargetEnvironment>
          295  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          296  +    </Midl>
          297  +    <ClCompile>
          298  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          299  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          300  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          301  +      <StringPooling>true</StringPooling>
          302  +      <ExceptionHandling>
          303  +      </ExceptionHandling>
          304  +      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
          305  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          306  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          307  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          308  +      <AssemblerOutput>All</AssemblerOutput>
          309  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          310  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          311  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          312  +      <BrowseInformation>
          313  +      </BrowseInformation>
          314  +      <WarningLevel>Level3</WarningLevel>
          315  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          316  +    </ClCompile>
          317  +    <ResourceCompile>
          318  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          319  +      <Culture>0x040c</Culture>
          320  +    </ResourceCompile>
          321  +    <Link>
          322  +      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
          323  +      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
          324  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          325  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          326  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          327  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          328  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          329  +      <GenerateMapFile>true</GenerateMapFile>
          330  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          331  +      <SubSystem>Windows</SubSystem>
          332  +      <RandomizedBaseAddress>false</RandomizedBaseAddress>
          333  +      <DataExecutionPrevention>
          334  +      </DataExecutionPrevention>
          335  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          336  +    </Link>
          337  +    <PreBuildEvent>
          338  +      <Command>cd ..\..\masmx86
          339  +bld_ml32.bat</Command>
          340  +    </PreBuildEvent>
          341  +  </ItemDefinitionGroup>
          342  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
          343  +    <Midl>
          344  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          345  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          346  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          347  +      <TargetEnvironment>X64</TargetEnvironment>
          348  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          349  +    </Midl>
          350  +    <ClCompile>
          351  +      <Optimization>Disabled</Optimization>
          352  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          353  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          354  +      <ExceptionHandling>
          355  +      </ExceptionHandling>
          356  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          357  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          358  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          359  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          360  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          361  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          362  +      <BrowseInformation>
          363  +      </BrowseInformation>
          364  +      <WarningLevel>Level3</WarningLevel>
          365  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          366  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          367  +    </ClCompile>
          368  +    <ResourceCompile>
          369  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          370  +      <Culture>0x040c</Culture>
          371  +    </ResourceCompile>
          372  +    <Link>
          373  +      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
          374  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          375  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          376  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          377  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          378  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          379  +      <GenerateMapFile>true</GenerateMapFile>
          380  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          381  +      <SubSystem>Windows</SubSystem>
          382  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          383  +      <TargetMachine>MachineX64</TargetMachine>
          384  +    </Link>
          385  +    <PreBuildEvent>
          386  +      <Command>cd ..\..\contrib\masmx64
          387  +bld_ml64.bat</Command>
          388  +    </PreBuildEvent>
          389  +  </ItemDefinitionGroup>
          390  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
          391  +    <Midl>
          392  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          393  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          394  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          395  +      <TargetEnvironment>Itanium</TargetEnvironment>
          396  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          397  +    </Midl>
          398  +    <ClCompile>
          399  +      <Optimization>Disabled</Optimization>
          400  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          401  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          402  +      <ExceptionHandling>
          403  +      </ExceptionHandling>
          404  +      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
          405  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          406  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          407  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          408  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          409  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          410  +      <BrowseInformation>
          411  +      </BrowseInformation>
          412  +      <WarningLevel>Level3</WarningLevel>
          413  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          414  +      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
          415  +    </ClCompile>
          416  +    <ResourceCompile>
          417  +      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          418  +      <Culture>0x040c</Culture>
          419  +    </ResourceCompile>
          420  +    <Link>
          421  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          422  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          423  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          424  +      <GenerateDebugInformation>true</GenerateDebugInformation>
          425  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          426  +      <GenerateMapFile>true</GenerateMapFile>
          427  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          428  +      <SubSystem>Windows</SubSystem>
          429  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          430  +      <TargetMachine>MachineIA64</TargetMachine>
          431  +    </Link>
          432  +  </ItemDefinitionGroup>
          433  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
          434  +    <Midl>
          435  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          436  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          437  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          438  +      <TargetEnvironment>X64</TargetEnvironment>
          439  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          440  +    </Midl>
          441  +    <ClCompile>
          442  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          443  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          444  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          445  +      <StringPooling>true</StringPooling>
          446  +      <ExceptionHandling>
          447  +      </ExceptionHandling>
          448  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          449  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          450  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          451  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          452  +      <AssemblerOutput>All</AssemblerOutput>
          453  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          454  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          455  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          456  +      <BrowseInformation>
          457  +      </BrowseInformation>
          458  +      <WarningLevel>Level3</WarningLevel>
          459  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          460  +    </ClCompile>
          461  +    <ResourceCompile>
          462  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          463  +      <Culture>0x040c</Culture>
          464  +    </ResourceCompile>
          465  +    <Link>
          466  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          467  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          468  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          469  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          470  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          471  +      <GenerateMapFile>true</GenerateMapFile>
          472  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          473  +      <SubSystem>Windows</SubSystem>
          474  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          475  +      <TargetMachine>MachineX64</TargetMachine>
          476  +    </Link>
          477  +  </ItemDefinitionGroup>
          478  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
          479  +    <Midl>
          480  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          481  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          482  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          483  +      <TargetEnvironment>Itanium</TargetEnvironment>
          484  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          485  +    </Midl>
          486  +    <ClCompile>
          487  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          488  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          489  +      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          490  +      <StringPooling>true</StringPooling>
          491  +      <ExceptionHandling>
          492  +      </ExceptionHandling>
          493  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          494  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          495  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          496  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          497  +      <AssemblerOutput>All</AssemblerOutput>
          498  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          499  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          500  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          501  +      <BrowseInformation>
          502  +      </BrowseInformation>
          503  +      <WarningLevel>Level3</WarningLevel>
          504  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          505  +    </ClCompile>
          506  +    <ResourceCompile>
          507  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          508  +      <Culture>0x040c</Culture>
          509  +    </ResourceCompile>
          510  +    <Link>
          511  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          512  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          513  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          514  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          515  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          516  +      <GenerateMapFile>true</GenerateMapFile>
          517  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          518  +      <SubSystem>Windows</SubSystem>
          519  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          520  +      <TargetMachine>MachineIA64</TargetMachine>
          521  +    </Link>
          522  +  </ItemDefinitionGroup>
          523  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
          524  +    <Midl>
          525  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          526  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          527  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          528  +      <TargetEnvironment>X64</TargetEnvironment>
          529  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          530  +    </Midl>
          531  +    <ClCompile>
          532  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          533  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          534  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          535  +      <StringPooling>true</StringPooling>
          536  +      <ExceptionHandling>
          537  +      </ExceptionHandling>
          538  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          539  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          540  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          541  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          542  +      <AssemblerOutput>All</AssemblerOutput>
          543  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          544  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          545  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          546  +      <BrowseInformation>
          547  +      </BrowseInformation>
          548  +      <WarningLevel>Level3</WarningLevel>
          549  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          550  +    </ClCompile>
          551  +    <ResourceCompile>
          552  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          553  +      <Culture>0x040c</Culture>
          554  +    </ResourceCompile>
          555  +    <Link>
          556  +      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
          557  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          558  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          559  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          560  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          561  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          562  +      <GenerateMapFile>true</GenerateMapFile>
          563  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          564  +      <SubSystem>Windows</SubSystem>
          565  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          566  +      <TargetMachine>MachineX64</TargetMachine>
          567  +    </Link>
          568  +    <PreBuildEvent>
          569  +      <Command>cd ..\..\masmx64
          570  +bld_ml64.bat</Command>
          571  +    </PreBuildEvent>
          572  +  </ItemDefinitionGroup>
          573  +  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
          574  +    <Midl>
          575  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          576  +      <MkTypLibCompatible>true</MkTypLibCompatible>
          577  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          578  +      <TargetEnvironment>Itanium</TargetEnvironment>
          579  +      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
          580  +    </Midl>
          581  +    <ClCompile>
          582  +      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
          583  +      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          584  +      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          585  +      <StringPooling>true</StringPooling>
          586  +      <ExceptionHandling>
          587  +      </ExceptionHandling>
          588  +      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
          589  +      <BufferSecurityCheck>false</BufferSecurityCheck>
          590  +      <FunctionLevelLinking>true</FunctionLevelLinking>
          591  +      <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile>
          592  +      <AssemblerOutput>All</AssemblerOutput>
          593  +      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
          594  +      <ObjectFileName>$(IntDir)</ObjectFileName>
          595  +      <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
          596  +      <BrowseInformation>
          597  +      </BrowseInformation>
          598  +      <WarningLevel>Level3</WarningLevel>
          599  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          600  +    </ClCompile>
          601  +    <ResourceCompile>
          602  +      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          603  +      <Culture>0x040c</Culture>
          604  +    </ResourceCompile>
          605  +    <Link>
          606  +      <OutputFile>$(OutDir)zlibwapi.dll</OutputFile>
          607  +      <SuppressStartupBanner>true</SuppressStartupBanner>
          608  +      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
          609  +      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
          610  +      <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile>
          611  +      <GenerateMapFile>true</GenerateMapFile>
          612  +      <MapFileName>$(OutDir)zlibwapi.map</MapFileName>
          613  +      <SubSystem>Windows</SubSystem>
          614  +      <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary>
          615  +      <TargetMachine>MachineIA64</TargetMachine>
          616  +    </Link>
          617  +  </ItemDefinitionGroup>
          618  +  <ItemGroup>
          619  +    <ClCompile Include="..\..\..\adler32.c" />
          620  +    <ClCompile Include="..\..\..\compress.c" />
          621  +    <ClCompile Include="..\..\..\crc32.c" />
          622  +    <ClCompile Include="..\..\..\deflate.c" />
          623  +    <ClCompile Include="..\..\..\gzclose.c" />
          624  +    <ClCompile Include="..\..\..\gzlib.c" />
          625  +    <ClCompile Include="..\..\..\gzread.c" />
          626  +    <ClCompile Include="..\..\..\gzwrite.c" />
          627  +    <ClCompile Include="..\..\..\infback.c" />
          628  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
          629  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
          630  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
          631  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
          632  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
          633  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
          634  +      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
          635  +    </ClCompile>
          636  +    <ClCompile Include="..\..\..\inffast.c" />
          637  +    <ClCompile Include="..\..\..\inflate.c" />
          638  +    <ClCompile Include="..\..\..\inftrees.c" />
          639  +    <ClCompile Include="..\..\minizip\ioapi.c" />
          640  +    <ClCompile Include="..\..\minizip\iowin32.c" />
          641  +    <ClCompile Include="..\..\..\trees.c" />
          642  +    <ClCompile Include="..\..\..\uncompr.c" />
          643  +    <ClCompile Include="..\..\minizip\unzip.c">
          644  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          645  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          646  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          647  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          648  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          649  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          650  +    </ClCompile>
          651  +    <ClCompile Include="..\..\minizip\zip.c">
          652  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          653  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          654  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          655  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          656  +      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
          657  +      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
          658  +    </ClCompile>
          659  +    <ClCompile Include="..\..\..\zutil.c" />
          660  +  </ItemGroup>
          661  +  <ItemGroup>
          662  +    <ResourceCompile Include="zlib.rc" />
          663  +  </ItemGroup>
          664  +  <ItemGroup>
          665  +    <None Include="zlibvc.def" />
          666  +  </ItemGroup>
          667  +  <ItemGroup>
          668  +    <ClInclude Include="..\..\..\deflate.h" />
          669  +    <ClInclude Include="..\..\..\infblock.h" />
          670  +    <ClInclude Include="..\..\..\infcodes.h" />
          671  +    <ClInclude Include="..\..\..\inffast.h" />
          672  +    <ClInclude Include="..\..\..\inftrees.h" />
          673  +    <ClInclude Include="..\..\..\infutil.h" />
          674  +    <ClInclude Include="..\..\..\zconf.h" />
          675  +    <ClInclude Include="..\..\..\zlib.h" />
          676  +    <ClInclude Include="..\..\..\zutil.h" />
          677  +  </ItemGroup>
          678  +  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
          679  +  <ImportGroup Label="ExtensionTargets">
          680  +  </ImportGroup>
          681  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +  <ItemGroup>
            4  +    <Filter Include="Source Files">
            5  +      <UniqueIdentifier>{07934a85-8b61-443d-a0ee-b2eedb74f3cd}</UniqueIdentifier>
            6  +      <Extensions>cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90</Extensions>
            7  +    </Filter>
            8  +    <Filter Include="Header Files">
            9  +      <UniqueIdentifier>{1d99675b-433d-4a21-9e50-ed4ab8b19762}</UniqueIdentifier>
           10  +      <Extensions>h;hpp;hxx;hm;inl;fi;fd</Extensions>
           11  +    </Filter>
           12  +    <Filter Include="Resource Files">
           13  +      <UniqueIdentifier>{431c0958-fa71-44d0-9084-2d19d100c0cc}</UniqueIdentifier>
           14  +      <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe</Extensions>
           15  +    </Filter>
           16  +  </ItemGroup>
           17  +  <ItemGroup>
           18  +    <ClCompile Include="..\..\..\adler32.c">
           19  +      <Filter>Source Files</Filter>
           20  +    </ClCompile>
           21  +    <ClCompile Include="..\..\..\compress.c">
           22  +      <Filter>Source Files</Filter>
           23  +    </ClCompile>
           24  +    <ClCompile Include="..\..\..\crc32.c">
           25  +      <Filter>Source Files</Filter>
           26  +    </ClCompile>
           27  +    <ClCompile Include="..\..\..\deflate.c">
           28  +      <Filter>Source Files</Filter>
           29  +    </ClCompile>
           30  +    <ClCompile Include="..\..\..\gzclose.c">
           31  +      <Filter>Source Files</Filter>
           32  +    </ClCompile>
           33  +    <ClCompile Include="..\..\..\gzlib.c">
           34  +      <Filter>Source Files</Filter>
           35  +    </ClCompile>
           36  +    <ClCompile Include="..\..\..\gzread.c">
           37  +      <Filter>Source Files</Filter>
           38  +    </ClCompile>
           39  +    <ClCompile Include="..\..\..\gzwrite.c">
           40  +      <Filter>Source Files</Filter>
           41  +    </ClCompile>
           42  +    <ClCompile Include="..\..\..\infback.c">
           43  +      <Filter>Source Files</Filter>
           44  +    </ClCompile>
           45  +    <ClCompile Include="..\..\masmx64\inffas8664.c">
           46  +      <Filter>Source Files</Filter>
           47  +    </ClCompile>
           48  +    <ClCompile Include="..\..\..\inffast.c">
           49  +      <Filter>Source Files</Filter>
           50  +    </ClCompile>
           51  +    <ClCompile Include="..\..\..\inflate.c">
           52  +      <Filter>Source Files</Filter>
           53  +    </ClCompile>
           54  +    <ClCompile Include="..\..\..\inftrees.c">
           55  +      <Filter>Source Files</Filter>
           56  +    </ClCompile>
           57  +    <ClCompile Include="..\..\minizip\ioapi.c">
           58  +      <Filter>Source Files</Filter>
           59  +    </ClCompile>
           60  +    <ClCompile Include="..\..\minizip\iowin32.c">
           61  +      <Filter>Source Files</Filter>
           62  +    </ClCompile>
           63  +    <ClCompile Include="..\..\..\trees.c">
           64  +      <Filter>Source Files</Filter>
           65  +    </ClCompile>
           66  +    <ClCompile Include="..\..\..\uncompr.c">
           67  +      <Filter>Source Files</Filter>
           68  +    </ClCompile>
           69  +    <ClCompile Include="..\..\minizip\unzip.c">
           70  +      <Filter>Source Files</Filter>
           71  +    </ClCompile>
           72  +    <ClCompile Include="..\..\minizip\zip.c">
           73  +      <Filter>Source Files</Filter>
           74  +    </ClCompile>
           75  +    <ClCompile Include="..\..\..\zutil.c">
           76  +      <Filter>Source Files</Filter>
           77  +    </ClCompile>
           78  +  </ItemGroup>
           79  +  <ItemGroup>
           80  +    <ResourceCompile Include="zlib.rc">
           81  +      <Filter>Source Files</Filter>
           82  +    </ResourceCompile>
           83  +  </ItemGroup>
           84  +  <ItemGroup>
           85  +    <None Include="zlibvc.def">
           86  +      <Filter>Source Files</Filter>
           87  +    </None>
           88  +  </ItemGroup>
           89  +  <ItemGroup>
           90  +    <ClInclude Include="..\..\..\deflate.h">
           91  +      <Filter>Header Files</Filter>
           92  +    </ClInclude>
           93  +    <ClInclude Include="..\..\..\infblock.h">
           94  +      <Filter>Header Files</Filter>
           95  +    </ClInclude>
           96  +    <ClInclude Include="..\..\..\infcodes.h">
           97  +      <Filter>Header Files</Filter>
           98  +    </ClInclude>
           99  +    <ClInclude Include="..\..\..\inffast.h">
          100  +      <Filter>Header Files</Filter>
          101  +    </ClInclude>
          102  +    <ClInclude Include="..\..\..\inftrees.h">
          103  +      <Filter>Header Files</Filter>
          104  +    </ClInclude>
          105  +    <ClInclude Include="..\..\..\infutil.h">
          106  +      <Filter>Header Files</Filter>
          107  +    </ClInclude>
          108  +    <ClInclude Include="..\..\..\zconf.h">
          109  +      <Filter>Header Files</Filter>
          110  +    </ClInclude>
          111  +    <ClInclude Include="..\..\..\zlib.h">
          112  +      <Filter>Header Files</Filter>
          113  +    </ClInclude>
          114  +    <ClInclude Include="..\..\..\zutil.h">
          115  +      <Filter>Header Files</Filter>
          116  +    </ClInclude>
          117  +  </ItemGroup>
          118  +</Project>

Added compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.user.

            1  +<?xml version="1.0" encoding="utf-8"?>
            2  +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            3  +</Project>

Added compat/zlib/contrib/vstudio/vc9/miniunz.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9.00"
            5  +	Name="miniunz"
            6  +	ProjectGUID="{C52F9E7B-498A-42BE-8DB4-85A15694382A}"
            7  +	Keyword="Win32Proj"
            8  +	TargetFrameworkVersion="131072"
            9  +	>
           10  +	<Platforms>
           11  +		<Platform
           12  +			Name="Win32"
           13  +		/>
           14  +		<Platform
           15  +			Name="x64"
           16  +		/>
           17  +		<Platform
           18  +			Name="Itanium"
           19  +		/>
           20  +	</Platforms>
           21  +	<ToolFiles>
           22  +	</ToolFiles>
           23  +	<Configurations>
           24  +		<Configuration
           25  +			Name="Debug|Win32"
           26  +			OutputDirectory="x86\MiniUnzip$(ConfigurationName)"
           27  +			IntermediateDirectory="x86\MiniUnzip$(ConfigurationName)\Tmp"
           28  +			ConfigurationType="1"
           29  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
           30  +			CharacterSet="2"
           31  +			>
           32  +			<Tool
           33  +				Name="VCPreBuildEventTool"
           34  +			/>
           35  +			<Tool
           36  +				Name="VCCustomBuildTool"
           37  +			/>
           38  +			<Tool
           39  +				Name="VCXMLDataGeneratorTool"
           40  +			/>
           41  +			<Tool
           42  +				Name="VCWebServiceProxyGeneratorTool"
           43  +			/>
           44  +			<Tool
           45  +				Name="VCMIDLTool"
           46  +			/>
           47  +			<Tool
           48  +				Name="VCCLCompilerTool"
           49  +				Optimization="0"
           50  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
           51  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE"
           52  +				MinimalRebuild="true"
           53  +				BasicRuntimeChecks="0"
           54  +				RuntimeLibrary="1"
           55  +				BufferSecurityCheck="false"
           56  +				UsePrecompiledHeader="0"
           57  +				AssemblerListingLocation="$(IntDir)\"
           58  +				WarningLevel="3"
           59  +				Detect64BitPortabilityProblems="true"
           60  +				DebugInformationFormat="4"
           61  +			/>
           62  +			<Tool
           63  +				Name="VCManagedResourceCompilerTool"
           64  +			/>
           65  +			<Tool
           66  +				Name="VCResourceCompilerTool"
           67  +			/>
           68  +			<Tool
           69  +				Name="VCPreLinkEventTool"
           70  +			/>
           71  +			<Tool
           72  +				Name="VCLinkerTool"
           73  +				AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib"
           74  +				OutputFile="$(OutDir)/miniunz.exe"
           75  +				LinkIncremental="2"
           76  +				GenerateManifest="false"
           77  +				GenerateDebugInformation="true"
           78  +				ProgramDatabaseFile="$(OutDir)/miniunz.pdb"
           79  +				SubSystem="1"
           80  +				RandomizedBaseAddress="1"
           81  +				DataExecutionPrevention="0"
           82  +				TargetMachine="1"
           83  +			/>
           84  +			<Tool
           85  +				Name="VCALinkTool"
           86  +			/>
           87  +			<Tool
           88  +				Name="VCManifestTool"
           89  +			/>
           90  +			<Tool
           91  +				Name="VCXDCMakeTool"
           92  +			/>
           93  +			<Tool
           94  +				Name="VCBscMakeTool"
           95  +			/>
           96  +			<Tool
           97  +				Name="VCFxCopTool"
           98  +			/>
           99  +			<Tool
          100  +				Name="VCAppVerifierTool"
          101  +			/>
          102  +			<Tool
          103  +				Name="VCPostBuildEventTool"
          104  +			/>
          105  +		</Configuration>
          106  +		<Configuration
          107  +			Name="Release|Win32"
          108  +			OutputDirectory="x86\MiniUnzip$(ConfigurationName)"
          109  +			IntermediateDirectory="x86\MiniUnzip$(ConfigurationName)\Tmp"
          110  +			ConfigurationType="1"
          111  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          112  +			CharacterSet="2"
          113  +			>
          114  +			<Tool
          115  +				Name="VCPreBuildEventTool"
          116  +			/>
          117  +			<Tool
          118  +				Name="VCCustomBuildTool"
          119  +			/>
          120  +			<Tool
          121  +				Name="VCXMLDataGeneratorTool"
          122  +			/>
          123  +			<Tool
          124  +				Name="VCWebServiceProxyGeneratorTool"
          125  +			/>
          126  +			<Tool
          127  +				Name="VCMIDLTool"
          128  +			/>
          129  +			<Tool
          130  +				Name="VCCLCompilerTool"
          131  +				Optimization="2"
          132  +				InlineFunctionExpansion="1"
          133  +				OmitFramePointers="true"
          134  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          135  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE"
          136  +				StringPooling="true"
          137  +				BasicRuntimeChecks="0"
          138  +				RuntimeLibrary="0"
          139  +				BufferSecurityCheck="false"
          140  +				EnableFunctionLevelLinking="true"
          141  +				UsePrecompiledHeader="0"
          142  +				AssemblerListingLocation="$(IntDir)\"
          143  +				WarningLevel="3"
          144  +				Detect64BitPortabilityProblems="true"
          145  +				DebugInformationFormat="3"
          146  +			/>
          147  +			<Tool
          148  +				Name="VCManagedResourceCompilerTool"
          149  +			/>
          150  +			<Tool
          151  +				Name="VCResourceCompilerTool"
          152  +			/>
          153  +			<Tool
          154  +				Name="VCPreLinkEventTool"
          155  +			/>
          156  +			<Tool
          157  +				Name="VCLinkerTool"
          158  +				AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib"
          159  +				OutputFile="$(OutDir)/miniunz.exe"
          160  +				LinkIncremental="1"
          161  +				GenerateManifest="false"
          162  +				GenerateDebugInformation="true"
          163  +				SubSystem="1"
          164  +				OptimizeReferences="2"
          165  +				EnableCOMDATFolding="2"
          166  +				OptimizeForWindows98="1"
          167  +				RandomizedBaseAddress="1"
          168  +				DataExecutionPrevention="0"
          169  +				TargetMachine="1"
          170  +			/>
          171  +			<Tool
          172  +				Name="VCALinkTool"
          173  +			/>
          174  +			<Tool
          175  +				Name="VCManifestTool"
          176  +			/>
          177  +			<Tool
          178  +				Name="VCXDCMakeTool"
          179  +			/>
          180  +			<Tool
          181  +				Name="VCBscMakeTool"
          182  +			/>
          183  +			<Tool
          184  +				Name="VCFxCopTool"
          185  +			/>
          186  +			<Tool
          187  +				Name="VCAppVerifierTool"
          188  +			/>
          189  +			<Tool
          190  +				Name="VCPostBuildEventTool"
          191  +			/>
          192  +		</Configuration>
          193  +		<Configuration
          194  +			Name="Debug|x64"
          195  +			OutputDirectory="x64\MiniUnzip$(ConfigurationName)"
          196  +			IntermediateDirectory="x64\MiniUnzip$(ConfigurationName)\Tmp"
          197  +			ConfigurationType="1"
          198  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          199  +			CharacterSet="2"
          200  +			>
          201  +			<Tool
          202  +				Name="VCPreBuildEventTool"
          203  +			/>
          204  +			<Tool
          205  +				Name="VCCustomBuildTool"
          206  +			/>
          207  +			<Tool
          208  +				Name="VCXMLDataGeneratorTool"
          209  +			/>
          210  +			<Tool
          211  +				Name="VCWebServiceProxyGeneratorTool"
          212  +			/>
          213  +			<Tool
          214  +				Name="VCMIDLTool"
          215  +				TargetEnvironment="3"
          216  +			/>
          217  +			<Tool
          218  +				Name="VCCLCompilerTool"
          219  +				Optimization="0"
          220  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          221  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          222  +				MinimalRebuild="true"
          223  +				BasicRuntimeChecks="0"
          224  +				RuntimeLibrary="3"
          225  +				BufferSecurityCheck="false"
          226  +				UsePrecompiledHeader="0"
          227  +				AssemblerListingLocation="$(IntDir)\"
          228  +				WarningLevel="3"
          229  +				Detect64BitPortabilityProblems="true"
          230  +				DebugInformationFormat="3"
          231  +			/>
          232  +			<Tool
          233  +				Name="VCManagedResourceCompilerTool"
          234  +			/>
          235  +			<Tool
          236  +				Name="VCResourceCompilerTool"
          237  +			/>
          238  +			<Tool
          239  +				Name="VCPreLinkEventTool"
          240  +			/>
          241  +			<Tool
          242  +				Name="VCLinkerTool"
          243  +				AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib"
          244  +				OutputFile="$(OutDir)/miniunz.exe"
          245  +				LinkIncremental="2"
          246  +				GenerateManifest="false"
          247  +				GenerateDebugInformation="true"
          248  +				ProgramDatabaseFile="$(OutDir)/miniunz.pdb"
          249  +				SubSystem="1"
          250  +				TargetMachine="17"
          251  +			/>
          252  +			<Tool
          253  +				Name="VCALinkTool"
          254  +			/>
          255  +			<Tool
          256  +				Name="VCManifestTool"
          257  +			/>
          258  +			<Tool
          259  +				Name="VCXDCMakeTool"
          260  +			/>
          261  +			<Tool
          262  +				Name="VCBscMakeTool"
          263  +			/>
          264  +			<Tool
          265  +				Name="VCFxCopTool"
          266  +			/>
          267  +			<Tool
          268  +				Name="VCAppVerifierTool"
          269  +			/>
          270  +			<Tool
          271  +				Name="VCWebDeploymentTool"
          272  +			/>
          273  +			<Tool
          274  +				Name="VCPostBuildEventTool"
          275  +			/>
          276  +		</Configuration>
          277  +		<Configuration
          278  +			Name="Debug|Itanium"
          279  +			OutputDirectory="ia64\MiniUnzip$(ConfigurationName)"
          280  +			IntermediateDirectory="ia64\MiniUnzip$(ConfigurationName)\Tmp"
          281  +			ConfigurationType="1"
          282  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          283  +			CharacterSet="2"
          284  +			>
          285  +			<Tool
          286  +				Name="VCPreBuildEventTool"
          287  +			/>
          288  +			<Tool
          289  +				Name="VCCustomBuildTool"
          290  +			/>
          291  +			<Tool
          292  +				Name="VCXMLDataGeneratorTool"
          293  +			/>
          294  +			<Tool
          295  +				Name="VCWebServiceProxyGeneratorTool"
          296  +			/>
          297  +			<Tool
          298  +				Name="VCMIDLTool"
          299  +				TargetEnvironment="2"
          300  +			/>
          301  +			<Tool
          302  +				Name="VCCLCompilerTool"
          303  +				Optimization="0"
          304  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          305  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          306  +				MinimalRebuild="true"
          307  +				BasicRuntimeChecks="0"
          308  +				RuntimeLibrary="3"
          309  +				BufferSecurityCheck="false"
          310  +				UsePrecompiledHeader="0"
          311  +				AssemblerListingLocation="$(IntDir)\"
          312  +				WarningLevel="3"
          313  +				Detect64BitPortabilityProblems="true"
          314  +				DebugInformationFormat="3"
          315  +			/>
          316  +			<Tool
          317  +				Name="VCManagedResourceCompilerTool"
          318  +			/>
          319  +			<Tool
          320  +				Name="VCResourceCompilerTool"
          321  +			/>
          322  +			<Tool
          323  +				Name="VCPreLinkEventTool"
          324  +			/>
          325  +			<Tool
          326  +				Name="VCLinkerTool"
          327  +				AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib"
          328  +				OutputFile="$(OutDir)/miniunz.exe"
          329  +				LinkIncremental="2"
          330  +				GenerateManifest="false"
          331  +				GenerateDebugInformation="true"
          332  +				ProgramDatabaseFile="$(OutDir)/miniunz.pdb"
          333  +				SubSystem="1"
          334  +				TargetMachine="5"
          335  +			/>
          336  +			<Tool
          337  +				Name="VCALinkTool"
          338  +			/>
          339  +			<Tool
          340  +				Name="VCManifestTool"
          341  +			/>
          342  +			<Tool
          343  +				Name="VCXDCMakeTool"
          344  +			/>
          345  +			<Tool
          346  +				Name="VCBscMakeTool"
          347  +			/>
          348  +			<Tool
          349  +				Name="VCFxCopTool"
          350  +			/>
          351  +			<Tool
          352  +				Name="VCAppVerifierTool"
          353  +			/>
          354  +			<Tool
          355  +				Name="VCWebDeploymentTool"
          356  +			/>
          357  +			<Tool
          358  +				Name="VCPostBuildEventTool"
          359  +			/>
          360  +		</Configuration>
          361  +		<Configuration
          362  +			Name="Release|x64"
          363  +			OutputDirectory="x64\MiniUnzip$(ConfigurationName)"
          364  +			IntermediateDirectory="x64\MiniUnzip$(ConfigurationName)\Tmp"
          365  +			ConfigurationType="1"
          366  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          367  +			CharacterSet="2"
          368  +			>
          369  +			<Tool
          370  +				Name="VCPreBuildEventTool"
          371  +			/>
          372  +			<Tool
          373  +				Name="VCCustomBuildTool"
          374  +			/>
          375  +			<Tool
          376  +				Name="VCXMLDataGeneratorTool"
          377  +			/>
          378  +			<Tool
          379  +				Name="VCWebServiceProxyGeneratorTool"
          380  +			/>
          381  +			<Tool
          382  +				Name="VCMIDLTool"
          383  +				TargetEnvironment="3"
          384  +			/>
          385  +			<Tool
          386  +				Name="VCCLCompilerTool"
          387  +				Optimization="2"
          388  +				InlineFunctionExpansion="1"
          389  +				OmitFramePointers="true"
          390  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          391  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          392  +				StringPooling="true"
          393  +				BasicRuntimeChecks="0"
          394  +				RuntimeLibrary="2"
          395  +				BufferSecurityCheck="false"
          396  +				EnableFunctionLevelLinking="true"
          397  +				UsePrecompiledHeader="0"
          398  +				AssemblerListingLocation="$(IntDir)\"
          399  +				WarningLevel="3"
          400  +				Detect64BitPortabilityProblems="true"
          401  +				DebugInformationFormat="3"
          402  +			/>
          403  +			<Tool
          404  +				Name="VCManagedResourceCompilerTool"
          405  +			/>
          406  +			<Tool
          407  +				Name="VCResourceCompilerTool"
          408  +			/>
          409  +			<Tool
          410  +				Name="VCPreLinkEventTool"
          411  +			/>
          412  +			<Tool
          413  +				Name="VCLinkerTool"
          414  +				AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib"
          415  +				OutputFile="$(OutDir)/miniunz.exe"
          416  +				LinkIncremental="1"
          417  +				GenerateManifest="false"
          418  +				GenerateDebugInformation="true"
          419  +				SubSystem="1"
          420  +				OptimizeReferences="2"
          421  +				EnableCOMDATFolding="2"
          422  +				OptimizeForWindows98="1"
          423  +				TargetMachine="17"
          424  +			/>
          425  +			<Tool
          426  +				Name="VCALinkTool"
          427  +			/>
          428  +			<Tool
          429  +				Name="VCManifestTool"
          430  +			/>
          431  +			<Tool
          432  +				Name="VCXDCMakeTool"
          433  +			/>
          434  +			<Tool
          435  +				Name="VCBscMakeTool"
          436  +			/>
          437  +			<Tool
          438  +				Name="VCFxCopTool"
          439  +			/>
          440  +			<Tool
          441  +				Name="VCAppVerifierTool"
          442  +			/>
          443  +			<Tool
          444  +				Name="VCWebDeploymentTool"
          445  +			/>
          446  +			<Tool
          447  +				Name="VCPostBuildEventTool"
          448  +			/>
          449  +		</Configuration>
          450  +		<Configuration
          451  +			Name="Release|Itanium"
          452  +			OutputDirectory="ia64\MiniUnzip$(ConfigurationName)"
          453  +			IntermediateDirectory="ia64\MiniUnzip$(ConfigurationName)\Tmp"
          454  +			ConfigurationType="1"
          455  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          456  +			CharacterSet="2"
          457  +			>
          458  +			<Tool
          459  +				Name="VCPreBuildEventTool"
          460  +			/>
          461  +			<Tool
          462  +				Name="VCCustomBuildTool"
          463  +			/>
          464  +			<Tool
          465  +				Name="VCXMLDataGeneratorTool"
          466  +			/>
          467  +			<Tool
          468  +				Name="VCWebServiceProxyGeneratorTool"
          469  +			/>
          470  +			<Tool
          471  +				Name="VCMIDLTool"
          472  +				TargetEnvironment="2"
          473  +			/>
          474  +			<Tool
          475  +				Name="VCCLCompilerTool"
          476  +				Optimization="2"
          477  +				InlineFunctionExpansion="1"
          478  +				OmitFramePointers="true"
          479  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          480  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          481  +				StringPooling="true"
          482  +				BasicRuntimeChecks="0"
          483  +				RuntimeLibrary="2"
          484  +				BufferSecurityCheck="false"
          485  +				EnableFunctionLevelLinking="true"
          486  +				UsePrecompiledHeader="0"
          487  +				AssemblerListingLocation="$(IntDir)\"
          488  +				WarningLevel="3"
          489  +				Detect64BitPortabilityProblems="true"
          490  +				DebugInformationFormat="3"
          491  +			/>
          492  +			<Tool
          493  +				Name="VCManagedResourceCompilerTool"
          494  +			/>
          495  +			<Tool
          496  +				Name="VCResourceCompilerTool"
          497  +			/>
          498  +			<Tool
          499  +				Name="VCPreLinkEventTool"
          500  +			/>
          501  +			<Tool
          502  +				Name="VCLinkerTool"
          503  +				AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib"
          504  +				OutputFile="$(OutDir)/miniunz.exe"
          505  +				LinkIncremental="1"
          506  +				GenerateManifest="false"
          507  +				GenerateDebugInformation="true"
          508  +				SubSystem="1"
          509  +				OptimizeReferences="2"
          510  +				EnableCOMDATFolding="2"
          511  +				OptimizeForWindows98="1"
          512  +				TargetMachine="5"
          513  +			/>
          514  +			<Tool
          515  +				Name="VCALinkTool"
          516  +			/>
          517  +			<Tool
          518  +				Name="VCManifestTool"
          519  +			/>
          520  +			<Tool
          521  +				Name="VCXDCMakeTool"
          522  +			/>
          523  +			<Tool
          524  +				Name="VCBscMakeTool"
          525  +			/>
          526  +			<Tool
          527  +				Name="VCFxCopTool"
          528  +			/>
          529  +			<Tool
          530  +				Name="VCAppVerifierTool"
          531  +			/>
          532  +			<Tool
          533  +				Name="VCWebDeploymentTool"
          534  +			/>
          535  +			<Tool
          536  +				Name="VCPostBuildEventTool"
          537  +			/>
          538  +		</Configuration>
          539  +	</Configurations>
          540  +	<References>
          541  +	</References>
          542  +	<Files>
          543  +		<Filter
          544  +			Name="Source Files"
          545  +			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
          546  +			>
          547  +			<File
          548  +				RelativePath="..\..\minizip\miniunz.c"
          549  +				>
          550  +			</File>
          551  +		</Filter>
          552  +		<Filter
          553  +			Name="Header Files"
          554  +			Filter="h;hpp;hxx;hm;inl;inc"
          555  +			>
          556  +		</Filter>
          557  +		<Filter
          558  +			Name="Resource Files"
          559  +			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
          560  +			>
          561  +		</Filter>
          562  +	</Files>
          563  +	<Globals>
          564  +	</Globals>
          565  +</VisualStudioProject>

Added compat/zlib/contrib/vstudio/vc9/minizip.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9.00"
            5  +	Name="minizip"
            6  +	ProjectGUID="{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}"
            7  +	Keyword="Win32Proj"
            8  +	TargetFrameworkVersion="131072"
            9  +	>
           10  +	<Platforms>
           11  +		<Platform
           12  +			Name="Win32"
           13  +		/>
           14  +		<Platform
           15  +			Name="x64"
           16  +		/>
           17  +		<Platform
           18  +			Name="Itanium"
           19  +		/>
           20  +	</Platforms>
           21  +	<ToolFiles>
           22  +	</ToolFiles>
           23  +	<Configurations>
           24  +		<Configuration
           25  +			Name="Debug|Win32"
           26  +			OutputDirectory="x86\MiniZip$(ConfigurationName)"
           27  +			IntermediateDirectory="x86\MiniZip$(ConfigurationName)\Tmp"
           28  +			ConfigurationType="1"
           29  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
           30  +			CharacterSet="2"
           31  +			>
           32  +			<Tool
           33  +				Name="VCPreBuildEventTool"
           34  +			/>
           35  +			<Tool
           36  +				Name="VCCustomBuildTool"
           37  +			/>
           38  +			<Tool
           39  +				Name="VCXMLDataGeneratorTool"
           40  +			/>
           41  +			<Tool
           42  +				Name="VCWebServiceProxyGeneratorTool"
           43  +			/>
           44  +			<Tool
           45  +				Name="VCMIDLTool"
           46  +			/>
           47  +			<Tool
           48  +				Name="VCCLCompilerTool"
           49  +				Optimization="0"
           50  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
           51  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE"
           52  +				MinimalRebuild="true"
           53  +				BasicRuntimeChecks="0"
           54  +				RuntimeLibrary="1"
           55  +				BufferSecurityCheck="false"
           56  +				UsePrecompiledHeader="0"
           57  +				AssemblerListingLocation="$(IntDir)\"
           58  +				WarningLevel="3"
           59  +				Detect64BitPortabilityProblems="true"
           60  +				DebugInformationFormat="4"
           61  +			/>
           62  +			<Tool
           63  +				Name="VCManagedResourceCompilerTool"
           64  +			/>
           65  +			<Tool
           66  +				Name="VCResourceCompilerTool"
           67  +			/>
           68  +			<Tool
           69  +				Name="VCPreLinkEventTool"
           70  +			/>
           71  +			<Tool
           72  +				Name="VCLinkerTool"
           73  +				AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib"
           74  +				OutputFile="$(OutDir)/minizip.exe"
           75  +				LinkIncremental="2"
           76  +				GenerateManifest="false"
           77  +				GenerateDebugInformation="true"
           78  +				ProgramDatabaseFile="$(OutDir)/minizip.pdb"
           79  +				SubSystem="1"
           80  +				RandomizedBaseAddress="1"
           81  +				DataExecutionPrevention="0"
           82  +				TargetMachine="1"
           83  +			/>
           84  +			<Tool
           85  +				Name="VCALinkTool"
           86  +			/>
           87  +			<Tool
           88  +				Name="VCManifestTool"
           89  +			/>
           90  +			<Tool
           91  +				Name="VCXDCMakeTool"
           92  +			/>
           93  +			<Tool
           94  +				Name="VCBscMakeTool"
           95  +			/>
           96  +			<Tool
           97  +				Name="VCFxCopTool"
           98  +			/>
           99  +			<Tool
          100  +				Name="VCAppVerifierTool"
          101  +			/>
          102  +			<Tool
          103  +				Name="VCPostBuildEventTool"
          104  +			/>
          105  +		</Configuration>
          106  +		<Configuration
          107  +			Name="Release|Win32"
          108  +			OutputDirectory="x86\MiniZip$(ConfigurationName)"
          109  +			IntermediateDirectory="x86\MiniZip$(ConfigurationName)\Tmp"
          110  +			ConfigurationType="1"
          111  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          112  +			CharacterSet="2"
          113  +			>
          114  +			<Tool
          115  +				Name="VCPreBuildEventTool"
          116  +			/>
          117  +			<Tool
          118  +				Name="VCCustomBuildTool"
          119  +			/>
          120  +			<Tool
          121  +				Name="VCXMLDataGeneratorTool"
          122  +			/>
          123  +			<Tool
          124  +				Name="VCWebServiceProxyGeneratorTool"
          125  +			/>
          126  +			<Tool
          127  +				Name="VCMIDLTool"
          128  +			/>
          129  +			<Tool
          130  +				Name="VCCLCompilerTool"
          131  +				Optimization="2"
          132  +				InlineFunctionExpansion="1"
          133  +				OmitFramePointers="true"
          134  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          135  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE"
          136  +				StringPooling="true"
          137  +				BasicRuntimeChecks="0"
          138  +				RuntimeLibrary="0"
          139  +				BufferSecurityCheck="false"
          140  +				EnableFunctionLevelLinking="true"
          141  +				UsePrecompiledHeader="0"
          142  +				AssemblerListingLocation="$(IntDir)\"
          143  +				WarningLevel="3"
          144  +				Detect64BitPortabilityProblems="true"
          145  +				DebugInformationFormat="3"
          146  +			/>
          147  +			<Tool
          148  +				Name="VCManagedResourceCompilerTool"
          149  +			/>
          150  +			<Tool
          151  +				Name="VCResourceCompilerTool"
          152  +			/>
          153  +			<Tool
          154  +				Name="VCPreLinkEventTool"
          155  +			/>
          156  +			<Tool
          157  +				Name="VCLinkerTool"
          158  +				AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib"
          159  +				OutputFile="$(OutDir)/minizip.exe"
          160  +				LinkIncremental="1"
          161  +				GenerateDebugInformation="true"
          162  +				SubSystem="1"
          163  +				OptimizeReferences="2"
          164  +				EnableCOMDATFolding="2"
          165  +				OptimizeForWindows98="1"
          166  +				RandomizedBaseAddress="1"
          167  +				DataExecutionPrevention="0"
          168  +				TargetMachine="1"
          169  +			/>
          170  +			<Tool
          171  +				Name="VCALinkTool"
          172  +			/>
          173  +			<Tool
          174  +				Name="VCManifestTool"
          175  +			/>
          176  +			<Tool
          177  +				Name="VCXDCMakeTool"
          178  +			/>
          179  +			<Tool
          180  +				Name="VCBscMakeTool"
          181  +			/>
          182  +			<Tool
          183  +				Name="VCFxCopTool"
          184  +			/>
          185  +			<Tool
          186  +				Name="VCAppVerifierTool"
          187  +			/>
          188  +			<Tool
          189  +				Name="VCPostBuildEventTool"
          190  +			/>
          191  +		</Configuration>
          192  +		<Configuration
          193  +			Name="Debug|x64"
          194  +			OutputDirectory="x64\$(ConfigurationName)"
          195  +			IntermediateDirectory="x64\$(ConfigurationName)"
          196  +			ConfigurationType="1"
          197  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          198  +			CharacterSet="2"
          199  +			>
          200  +			<Tool
          201  +				Name="VCPreBuildEventTool"
          202  +			/>
          203  +			<Tool
          204  +				Name="VCCustomBuildTool"
          205  +			/>
          206  +			<Tool
          207  +				Name="VCXMLDataGeneratorTool"
          208  +			/>
          209  +			<Tool
          210  +				Name="VCWebServiceProxyGeneratorTool"
          211  +			/>
          212  +			<Tool
          213  +				Name="VCMIDLTool"
          214  +				TargetEnvironment="3"
          215  +			/>
          216  +			<Tool
          217  +				Name="VCCLCompilerTool"
          218  +				Optimization="0"
          219  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          220  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          221  +				MinimalRebuild="true"
          222  +				BasicRuntimeChecks="0"
          223  +				RuntimeLibrary="3"
          224  +				BufferSecurityCheck="false"
          225  +				UsePrecompiledHeader="0"
          226  +				AssemblerListingLocation="$(IntDir)\"
          227  +				WarningLevel="3"
          228  +				Detect64BitPortabilityProblems="true"
          229  +				DebugInformationFormat="3"
          230  +			/>
          231  +			<Tool
          232  +				Name="VCManagedResourceCompilerTool"
          233  +			/>
          234  +			<Tool
          235  +				Name="VCResourceCompilerTool"
          236  +			/>
          237  +			<Tool
          238  +				Name="VCPreLinkEventTool"
          239  +			/>
          240  +			<Tool
          241  +				Name="VCLinkerTool"
          242  +				AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib"
          243  +				OutputFile="$(OutDir)/minizip.exe"
          244  +				LinkIncremental="2"
          245  +				GenerateManifest="false"
          246  +				GenerateDebugInformation="true"
          247  +				ProgramDatabaseFile="$(OutDir)/minizip.pdb"
          248  +				SubSystem="1"
          249  +				TargetMachine="17"
          250  +			/>
          251  +			<Tool
          252  +				Name="VCALinkTool"
          253  +			/>
          254  +			<Tool
          255  +				Name="VCManifestTool"
          256  +			/>
          257  +			<Tool
          258  +				Name="VCXDCMakeTool"
          259  +			/>
          260  +			<Tool
          261  +				Name="VCBscMakeTool"
          262  +			/>
          263  +			<Tool
          264  +				Name="VCFxCopTool"
          265  +			/>
          266  +			<Tool
          267  +				Name="VCAppVerifierTool"
          268  +			/>
          269  +			<Tool
          270  +				Name="VCWebDeploymentTool"
          271  +			/>
          272  +			<Tool
          273  +				Name="VCPostBuildEventTool"
          274  +			/>
          275  +		</Configuration>
          276  +		<Configuration
          277  +			Name="Debug|Itanium"
          278  +			OutputDirectory="ia64\$(ConfigurationName)"
          279  +			IntermediateDirectory="ia64\$(ConfigurationName)"
          280  +			ConfigurationType="1"
          281  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          282  +			CharacterSet="2"
          283  +			>
          284  +			<Tool
          285  +				Name="VCPreBuildEventTool"
          286  +			/>
          287  +			<Tool
          288  +				Name="VCCustomBuildTool"
          289  +			/>
          290  +			<Tool
          291  +				Name="VCXMLDataGeneratorTool"
          292  +			/>
          293  +			<Tool
          294  +				Name="VCWebServiceProxyGeneratorTool"
          295  +			/>
          296  +			<Tool
          297  +				Name="VCMIDLTool"
          298  +				TargetEnvironment="2"
          299  +			/>
          300  +			<Tool
          301  +				Name="VCCLCompilerTool"
          302  +				Optimization="0"
          303  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          304  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          305  +				MinimalRebuild="true"
          306  +				BasicRuntimeChecks="0"
          307  +				RuntimeLibrary="3"
          308  +				BufferSecurityCheck="false"
          309  +				UsePrecompiledHeader="0"
          310  +				AssemblerListingLocation="$(IntDir)\"
          311  +				WarningLevel="3"
          312  +				Detect64BitPortabilityProblems="true"
          313  +				DebugInformationFormat="3"
          314  +			/>
          315  +			<Tool
          316  +				Name="VCManagedResourceCompilerTool"
          317  +			/>
          318  +			<Tool
          319  +				Name="VCResourceCompilerTool"
          320  +			/>
          321  +			<Tool
          322  +				Name="VCPreLinkEventTool"
          323  +			/>
          324  +			<Tool
          325  +				Name="VCLinkerTool"
          326  +				AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib"
          327  +				OutputFile="$(OutDir)/minizip.exe"
          328  +				LinkIncremental="2"
          329  +				GenerateManifest="false"
          330  +				GenerateDebugInformation="true"
          331  +				ProgramDatabaseFile="$(OutDir)/minizip.pdb"
          332  +				SubSystem="1"
          333  +				TargetMachine="5"
          334  +			/>
          335  +			<Tool
          336  +				Name="VCALinkTool"
          337  +			/>
          338  +			<Tool
          339  +				Name="VCManifestTool"
          340  +			/>
          341  +			<Tool
          342  +				Name="VCXDCMakeTool"
          343  +			/>
          344  +			<Tool
          345  +				Name="VCBscMakeTool"
          346  +			/>
          347  +			<Tool
          348  +				Name="VCFxCopTool"
          349  +			/>
          350  +			<Tool
          351  +				Name="VCAppVerifierTool"
          352  +			/>
          353  +			<Tool
          354  +				Name="VCWebDeploymentTool"
          355  +			/>
          356  +			<Tool
          357  +				Name="VCPostBuildEventTool"
          358  +			/>
          359  +		</Configuration>
          360  +		<Configuration
          361  +			Name="Release|x64"
          362  +			OutputDirectory="x64\$(ConfigurationName)"
          363  +			IntermediateDirectory="x64\$(ConfigurationName)"
          364  +			ConfigurationType="1"
          365  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          366  +			CharacterSet="2"
          367  +			>
          368  +			<Tool
          369  +				Name="VCPreBuildEventTool"
          370  +			/>
          371  +			<Tool
          372  +				Name="VCCustomBuildTool"
          373  +			/>
          374  +			<Tool
          375  +				Name="VCXMLDataGeneratorTool"
          376  +			/>
          377  +			<Tool
          378  +				Name="VCWebServiceProxyGeneratorTool"
          379  +			/>
          380  +			<Tool
          381  +				Name="VCMIDLTool"
          382  +				TargetEnvironment="3"
          383  +			/>
          384  +			<Tool
          385  +				Name="VCCLCompilerTool"
          386  +				Optimization="2"
          387  +				InlineFunctionExpansion="1"
          388  +				OmitFramePointers="true"
          389  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          390  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          391  +				StringPooling="true"
          392  +				BasicRuntimeChecks="0"
          393  +				RuntimeLibrary="2"
          394  +				BufferSecurityCheck="false"
          395  +				EnableFunctionLevelLinking="true"
          396  +				UsePrecompiledHeader="0"
          397  +				AssemblerListingLocation="$(IntDir)\"
          398  +				WarningLevel="3"
          399  +				Detect64BitPortabilityProblems="true"
          400  +				DebugInformationFormat="3"
          401  +			/>
          402  +			<Tool
          403  +				Name="VCManagedResourceCompilerTool"
          404  +			/>
          405  +			<Tool
          406  +				Name="VCResourceCompilerTool"
          407  +			/>
          408  +			<Tool
          409  +				Name="VCPreLinkEventTool"
          410  +			/>
          411  +			<Tool
          412  +				Name="VCLinkerTool"
          413  +				AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib"
          414  +				OutputFile="$(OutDir)/minizip.exe"
          415  +				LinkIncremental="1"
          416  +				GenerateDebugInformation="true"
          417  +				SubSystem="1"
          418  +				OptimizeReferences="2"
          419  +				EnableCOMDATFolding="2"
          420  +				OptimizeForWindows98="1"
          421  +				TargetMachine="17"
          422  +			/>
          423  +			<Tool
          424  +				Name="VCALinkTool"
          425  +			/>
          426  +			<Tool
          427  +				Name="VCManifestTool"
          428  +			/>
          429  +			<Tool
          430  +				Name="VCXDCMakeTool"
          431  +			/>
          432  +			<Tool
          433  +				Name="VCBscMakeTool"
          434  +			/>
          435  +			<Tool
          436  +				Name="VCFxCopTool"
          437  +			/>
          438  +			<Tool
          439  +				Name="VCAppVerifierTool"
          440  +			/>
          441  +			<Tool
          442  +				Name="VCWebDeploymentTool"
          443  +			/>
          444  +			<Tool
          445  +				Name="VCPostBuildEventTool"
          446  +			/>
          447  +		</Configuration>
          448  +		<Configuration
          449  +			Name="Release|Itanium"
          450  +			OutputDirectory="ia64\$(ConfigurationName)"
          451  +			IntermediateDirectory="ia64\$(ConfigurationName)"
          452  +			ConfigurationType="1"
          453  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          454  +			CharacterSet="2"
          455  +			>
          456  +			<Tool
          457  +				Name="VCPreBuildEventTool"
          458  +			/>
          459  +			<Tool
          460  +				Name="VCCustomBuildTool"
          461  +			/>
          462  +			<Tool
          463  +				Name="VCXMLDataGeneratorTool"
          464  +			/>
          465  +			<Tool
          466  +				Name="VCWebServiceProxyGeneratorTool"
          467  +			/>
          468  +			<Tool
          469  +				Name="VCMIDLTool"
          470  +				TargetEnvironment="2"
          471  +			/>
          472  +			<Tool
          473  +				Name="VCCLCompilerTool"
          474  +				Optimization="2"
          475  +				InlineFunctionExpansion="1"
          476  +				OmitFramePointers="true"
          477  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          478  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          479  +				StringPooling="true"
          480  +				BasicRuntimeChecks="0"
          481  +				RuntimeLibrary="2"
          482  +				BufferSecurityCheck="false"
          483  +				EnableFunctionLevelLinking="true"
          484  +				UsePrecompiledHeader="0"
          485  +				AssemblerListingLocation="$(IntDir)\"
          486  +				WarningLevel="3"
          487  +				Detect64BitPortabilityProblems="true"
          488  +				DebugInformationFormat="3"
          489  +			/>
          490  +			<Tool
          491  +				Name="VCManagedResourceCompilerTool"
          492  +			/>
          493  +			<Tool
          494  +				Name="VCResourceCompilerTool"
          495  +			/>
          496  +			<Tool
          497  +				Name="VCPreLinkEventTool"
          498  +			/>
          499  +			<Tool
          500  +				Name="VCLinkerTool"
          501  +				AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib"
          502  +				OutputFile="$(OutDir)/minizip.exe"
          503  +				LinkIncremental="1"
          504  +				GenerateDebugInformation="true"
          505  +				SubSystem="1"
          506  +				OptimizeReferences="2"
          507  +				EnableCOMDATFolding="2"
          508  +				OptimizeForWindows98="1"
          509  +				TargetMachine="5"
          510  +			/>
          511  +			<Tool
          512  +				Name="VCALinkTool"
          513  +			/>
          514  +			<Tool
          515  +				Name="VCManifestTool"
          516  +			/>
          517  +			<Tool
          518  +				Name="VCXDCMakeTool"
          519  +			/>
          520  +			<Tool
          521  +				Name="VCBscMakeTool"
          522  +			/>
          523  +			<Tool
          524  +				Name="VCFxCopTool"
          525  +			/>
          526  +			<Tool
          527  +				Name="VCAppVerifierTool"
          528  +			/>
          529  +			<Tool
          530  +				Name="VCWebDeploymentTool"
          531  +			/>
          532  +			<Tool
          533  +				Name="VCPostBuildEventTool"
          534  +			/>
          535  +		</Configuration>
          536  +	</Configurations>
          537  +	<References>
          538  +	</References>
          539  +	<Files>
          540  +		<Filter
          541  +			Name="Source Files"
          542  +			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
          543  +			>
          544  +			<File
          545  +				RelativePath="..\..\minizip\minizip.c"
          546  +				>
          547  +			</File>
          548  +		</Filter>
          549  +		<Filter
          550  +			Name="Header Files"
          551  +			Filter="h;hpp;hxx;hm;inl;inc"
          552  +			>
          553  +		</Filter>
          554  +		<Filter
          555  +			Name="Resource Files"
          556  +			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
          557  +			>
          558  +		</Filter>
          559  +	</Files>
          560  +	<Globals>
          561  +	</Globals>
          562  +</VisualStudioProject>

Added compat/zlib/contrib/vstudio/vc9/testzlib.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9,00"
            5  +	Name="testzlib"
            6  +	ProjectGUID="{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}"
            7  +	RootNamespace="testzlib"
            8  +	Keyword="Win32Proj"
            9  +	TargetFrameworkVersion="131072"
           10  +	>
           11  +	<Platforms>
           12  +		<Platform
           13  +			Name="Win32"
           14  +		/>
           15  +		<Platform
           16  +			Name="x64"
           17  +		/>
           18  +		<Platform
           19  +			Name="Itanium"
           20  +		/>
           21  +	</Platforms>
           22  +	<ToolFiles>
           23  +	</ToolFiles>
           24  +	<Configurations>
           25  +		<Configuration
           26  +			Name="Debug|Win32"
           27  +			OutputDirectory="x86\TestZlib$(ConfigurationName)"
           28  +			IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp"
           29  +			ConfigurationType="1"
           30  +			CharacterSet="2"
           31  +			>
           32  +			<Tool
           33  +				Name="VCPreBuildEventTool"
           34  +			/>
           35  +			<Tool
           36  +				Name="VCCustomBuildTool"
           37  +			/>
           38  +			<Tool
           39  +				Name="VCXMLDataGeneratorTool"
           40  +			/>
           41  +			<Tool
           42  +				Name="VCWebServiceProxyGeneratorTool"
           43  +			/>
           44  +			<Tool
           45  +				Name="VCMIDLTool"
           46  +			/>
           47  +			<Tool
           48  +				Name="VCCLCompilerTool"
           49  +				Optimization="0"
           50  +				AdditionalIncludeDirectories="..\..\.."
           51  +				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
           52  +				MinimalRebuild="true"
           53  +				BasicRuntimeChecks="0"
           54  +				RuntimeLibrary="1"
           55  +				BufferSecurityCheck="false"
           56  +				UsePrecompiledHeader="0"
           57  +				AssemblerOutput="4"
           58  +				AssemblerListingLocation="$(IntDir)\"
           59  +				WarningLevel="3"
           60  +				Detect64BitPortabilityProblems="true"
           61  +				DebugInformationFormat="4"
           62  +			/>
           63  +			<Tool
           64  +				Name="VCManagedResourceCompilerTool"
           65  +			/>
           66  +			<Tool
           67  +				Name="VCResourceCompilerTool"
           68  +			/>
           69  +			<Tool
           70  +				Name="VCPreLinkEventTool"
           71  +			/>
           72  +			<Tool
           73  +				Name="VCLinkerTool"
           74  +				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
           75  +				OutputFile="$(OutDir)/testzlib.exe"
           76  +				LinkIncremental="2"
           77  +				GenerateManifest="false"
           78  +				GenerateDebugInformation="true"
           79  +				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
           80  +				SubSystem="1"
           81  +				RandomizedBaseAddress="1"
           82  +				DataExecutionPrevention="0"
           83  +				TargetMachine="1"
           84  +			/>
           85  +			<Tool
           86  +				Name="VCALinkTool"
           87  +			/>
           88  +			<Tool
           89  +				Name="VCManifestTool"
           90  +			/>
           91  +			<Tool
           92  +				Name="VCXDCMakeTool"
           93  +			/>
           94  +			<Tool
           95  +				Name="VCBscMakeTool"
           96  +			/>
           97  +			<Tool
           98  +				Name="VCFxCopTool"
           99  +			/>
          100  +			<Tool
          101  +				Name="VCAppVerifierTool"
          102  +			/>
          103  +			<Tool
          104  +				Name="VCPostBuildEventTool"
          105  +			/>
          106  +		</Configuration>
          107  +		<Configuration
          108  +			Name="Debug|x64"
          109  +			OutputDirectory="x64\TestZlib$(ConfigurationName)"
          110  +			IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp"
          111  +			ConfigurationType="1"
          112  +			>
          113  +			<Tool
          114  +				Name="VCPreBuildEventTool"
          115  +			/>
          116  +			<Tool
          117  +				Name="VCCustomBuildTool"
          118  +			/>
          119  +			<Tool
          120  +				Name="VCXMLDataGeneratorTool"
          121  +			/>
          122  +			<Tool
          123  +				Name="VCWebServiceProxyGeneratorTool"
          124  +			/>
          125  +			<Tool
          126  +				Name="VCMIDLTool"
          127  +			/>
          128  +			<Tool
          129  +				Name="VCCLCompilerTool"
          130  +				AdditionalIncludeDirectories="..\..\.."
          131  +				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          132  +				BasicRuntimeChecks="0"
          133  +				RuntimeLibrary="3"
          134  +				BufferSecurityCheck="false"
          135  +				AssemblerListingLocation="$(IntDir)\"
          136  +			/>
          137  +			<Tool
          138  +				Name="VCManagedResourceCompilerTool"
          139  +			/>
          140  +			<Tool
          141  +				Name="VCResourceCompilerTool"
          142  +			/>
          143  +			<Tool
          144  +				Name="VCPreLinkEventTool"
          145  +			/>
          146  +			<Tool
          147  +				Name="VCLinkerTool"
          148  +				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj"
          149  +				GenerateManifest="false"
          150  +			/>
          151  +			<Tool
          152  +				Name="VCALinkTool"
          153  +			/>
          154  +			<Tool
          155  +				Name="VCManifestTool"
          156  +			/>
          157  +			<Tool
          158  +				Name="VCXDCMakeTool"
          159  +			/>
          160  +			<Tool
          161  +				Name="VCBscMakeTool"
          162  +			/>
          163  +			<Tool
          164  +				Name="VCFxCopTool"
          165  +			/>
          166  +			<Tool
          167  +				Name="VCAppVerifierTool"
          168  +			/>
          169  +			<Tool
          170  +				Name="VCPostBuildEventTool"
          171  +			/>
          172  +		</Configuration>
          173  +		<Configuration
          174  +			Name="Debug|Itanium"
          175  +			OutputDirectory="ia64\TestZlib$(ConfigurationName)"
          176  +			IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp"
          177  +			ConfigurationType="1"
          178  +			CharacterSet="2"
          179  +			>
          180  +			<Tool
          181  +				Name="VCPreBuildEventTool"
          182  +			/>
          183  +			<Tool
          184  +				Name="VCCustomBuildTool"
          185  +			/>
          186  +			<Tool
          187  +				Name="VCXMLDataGeneratorTool"
          188  +			/>
          189  +			<Tool
          190  +				Name="VCWebServiceProxyGeneratorTool"
          191  +			/>
          192  +			<Tool
          193  +				Name="VCMIDLTool"
          194  +				TargetEnvironment="2"
          195  +			/>
          196  +			<Tool
          197  +				Name="VCCLCompilerTool"
          198  +				Optimization="0"
          199  +				AdditionalIncludeDirectories="..\..\.."
          200  +				PreprocessorDefinitions="ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          201  +				MinimalRebuild="true"
          202  +				BasicRuntimeChecks="0"
          203  +				RuntimeLibrary="3"
          204  +				BufferSecurityCheck="false"
          205  +				UsePrecompiledHeader="0"
          206  +				AssemblerOutput="4"
          207  +				AssemblerListingLocation="$(IntDir)\"
          208  +				WarningLevel="3"
          209  +				Detect64BitPortabilityProblems="true"
          210  +				DebugInformationFormat="3"
          211  +			/>
          212  +			<Tool
          213  +				Name="VCManagedResourceCompilerTool"
          214  +			/>
          215  +			<Tool
          216  +				Name="VCResourceCompilerTool"
          217  +			/>
          218  +			<Tool
          219  +				Name="VCPreLinkEventTool"
          220  +			/>
          221  +			<Tool
          222  +				Name="VCLinkerTool"
          223  +				OutputFile="$(OutDir)/testzlib.exe"
          224  +				LinkIncremental="2"
          225  +				GenerateManifest="false"
          226  +				GenerateDebugInformation="true"
          227  +				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
          228  +				SubSystem="1"
          229  +				TargetMachine="5"
          230  +			/>
          231  +			<Tool
          232  +				Name="VCALinkTool"
          233  +			/>
          234  +			<Tool
          235  +				Name="VCManifestTool"
          236  +			/>
          237  +			<Tool
          238  +				Name="VCXDCMakeTool"
          239  +			/>
          240  +			<Tool
          241  +				Name="VCBscMakeTool"
          242  +			/>
          243  +			<Tool
          244  +				Name="VCFxCopTool"
          245  +			/>
          246  +			<Tool
          247  +				Name="VCAppVerifierTool"
          248  +			/>
          249  +			<Tool
          250  +				Name="VCPostBuildEventTool"
          251  +			/>
          252  +		</Configuration>
          253  +		<Configuration
          254  +			Name="ReleaseWithoutAsm|Win32"
          255  +			OutputDirectory="x86\TestZlib$(ConfigurationName)"
          256  +			IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp"
          257  +			ConfigurationType="1"
          258  +			CharacterSet="2"
          259  +			WholeProgramOptimization="1"
          260  +			>
          261  +			<Tool
          262  +				Name="VCPreBuildEventTool"
          263  +			/>
          264  +			<Tool
          265  +				Name="VCCustomBuildTool"
          266  +			/>
          267  +			<Tool
          268  +				Name="VCXMLDataGeneratorTool"
          269  +			/>
          270  +			<Tool
          271  +				Name="VCWebServiceProxyGeneratorTool"
          272  +			/>
          273  +			<Tool
          274  +				Name="VCMIDLTool"
          275  +			/>
          276  +			<Tool
          277  +				Name="VCCLCompilerTool"
          278  +				Optimization="2"
          279  +				InlineFunctionExpansion="1"
          280  +				OmitFramePointers="true"
          281  +				AdditionalIncludeDirectories="..\..\.."
          282  +				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          283  +				StringPooling="true"
          284  +				BasicRuntimeChecks="0"
          285  +				RuntimeLibrary="0"
          286  +				BufferSecurityCheck="false"
          287  +				EnableFunctionLevelLinking="true"
          288  +				UsePrecompiledHeader="0"
          289  +				AssemblerListingLocation="$(IntDir)\"
          290  +				WarningLevel="3"
          291  +				Detect64BitPortabilityProblems="true"
          292  +				DebugInformationFormat="3"
          293  +			/>
          294  +			<Tool
          295  +				Name="VCManagedResourceCompilerTool"
          296  +			/>
          297  +			<Tool
          298  +				Name="VCResourceCompilerTool"
          299  +			/>
          300  +			<Tool
          301  +				Name="VCPreLinkEventTool"
          302  +			/>
          303  +			<Tool
          304  +				Name="VCLinkerTool"
          305  +				OutputFile="$(OutDir)/testzlib.exe"
          306  +				LinkIncremental="1"
          307  +				GenerateManifest="false"
          308  +				GenerateDebugInformation="true"
          309  +				SubSystem="1"
          310  +				OptimizeReferences="2"
          311  +				EnableCOMDATFolding="2"
          312  +				OptimizeForWindows98="1"
          313  +				RandomizedBaseAddress="1"
          314  +				DataExecutionPrevention="0"
          315  +				TargetMachine="1"
          316  +			/>
          317  +			<Tool
          318  +				Name="VCALinkTool"
          319  +			/>
          320  +			<Tool
          321  +				Name="VCManifestTool"
          322  +			/>
          323  +			<Tool
          324  +				Name="VCXDCMakeTool"
          325  +			/>
          326  +			<Tool
          327  +				Name="VCBscMakeTool"
          328  +			/>
          329  +			<Tool
          330  +				Name="VCFxCopTool"
          331  +			/>
          332  +			<Tool
          333  +				Name="VCAppVerifierTool"
          334  +			/>
          335  +			<Tool
          336  +				Name="VCPostBuildEventTool"
          337  +			/>
          338  +		</Configuration>
          339  +		<Configuration
          340  +			Name="ReleaseWithoutAsm|x64"
          341  +			OutputDirectory="x64\TestZlib$(ConfigurationName)"
          342  +			IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp"
          343  +			ConfigurationType="1"
          344  +			WholeProgramOptimization="1"
          345  +			>
          346  +			<Tool
          347  +				Name="VCPreBuildEventTool"
          348  +			/>
          349  +			<Tool
          350  +				Name="VCCustomBuildTool"
          351  +			/>
          352  +			<Tool
          353  +				Name="VCXMLDataGeneratorTool"
          354  +			/>
          355  +			<Tool
          356  +				Name="VCWebServiceProxyGeneratorTool"
          357  +			/>
          358  +			<Tool
          359  +				Name="VCMIDLTool"
          360  +			/>
          361  +			<Tool
          362  +				Name="VCCLCompilerTool"
          363  +				AdditionalIncludeDirectories="..\..\.."
          364  +				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          365  +				BasicRuntimeChecks="0"
          366  +				RuntimeLibrary="2"
          367  +				BufferSecurityCheck="false"
          368  +				AssemblerListingLocation="$(IntDir)\"
          369  +			/>
          370  +			<Tool
          371  +				Name="VCManagedResourceCompilerTool"
          372  +			/>
          373  +			<Tool
          374  +				Name="VCResourceCompilerTool"
          375  +			/>
          376  +			<Tool
          377  +				Name="VCPreLinkEventTool"
          378  +			/>
          379  +			<Tool
          380  +				Name="VCLinkerTool"
          381  +				AdditionalDependencies=""
          382  +				GenerateManifest="false"
          383  +			/>
          384  +			<Tool
          385  +				Name="VCALinkTool"
          386  +			/>
          387  +			<Tool
          388  +				Name="VCManifestTool"
          389  +			/>
          390  +			<Tool
          391  +				Name="VCXDCMakeTool"
          392  +			/>
          393  +			<Tool
          394  +				Name="VCBscMakeTool"
          395  +			/>
          396  +			<Tool
          397  +				Name="VCFxCopTool"
          398  +			/>
          399  +			<Tool
          400  +				Name="VCAppVerifierTool"
          401  +			/>
          402  +			<Tool
          403  +				Name="VCPostBuildEventTool"
          404  +			/>
          405  +		</Configuration>
          406  +		<Configuration
          407  +			Name="ReleaseWithoutAsm|Itanium"
          408  +			OutputDirectory="ia64\TestZlib$(ConfigurationName)"
          409  +			IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp"
          410  +			ConfigurationType="1"
          411  +			CharacterSet="2"
          412  +			WholeProgramOptimization="1"
          413  +			>
          414  +			<Tool
          415  +				Name="VCPreBuildEventTool"
          416  +			/>
          417  +			<Tool
          418  +				Name="VCCustomBuildTool"
          419  +			/>
          420  +			<Tool
          421  +				Name="VCXMLDataGeneratorTool"
          422  +			/>
          423  +			<Tool
          424  +				Name="VCWebServiceProxyGeneratorTool"
          425  +			/>
          426  +			<Tool
          427  +				Name="VCMIDLTool"
          428  +				TargetEnvironment="2"
          429  +			/>
          430  +			<Tool
          431  +				Name="VCCLCompilerTool"
          432  +				Optimization="2"
          433  +				InlineFunctionExpansion="1"
          434  +				OmitFramePointers="true"
          435  +				AdditionalIncludeDirectories="..\..\.."
          436  +				PreprocessorDefinitions="ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          437  +				StringPooling="true"
          438  +				BasicRuntimeChecks="0"
          439  +				RuntimeLibrary="2"
          440  +				BufferSecurityCheck="false"
          441  +				EnableFunctionLevelLinking="true"
          442  +				UsePrecompiledHeader="0"
          443  +				AssemblerListingLocation="$(IntDir)\"
          444  +				WarningLevel="3"
          445  +				Detect64BitPortabilityProblems="true"
          446  +				DebugInformationFormat="3"
          447  +			/>
          448  +			<Tool
          449  +				Name="VCManagedResourceCompilerTool"
          450  +			/>
          451  +			<Tool
          452  +				Name="VCResourceCompilerTool"
          453  +			/>
          454  +			<Tool
          455  +				Name="VCPreLinkEventTool"
          456  +			/>
          457  +			<Tool
          458  +				Name="VCLinkerTool"
          459  +				OutputFile="$(OutDir)/testzlib.exe"
          460  +				LinkIncremental="1"
          461  +				GenerateManifest="false"
          462  +				GenerateDebugInformation="true"
          463  +				SubSystem="1"
          464  +				OptimizeReferences="2"
          465  +				EnableCOMDATFolding="2"
          466  +				OptimizeForWindows98="1"
          467  +				TargetMachine="5"
          468  +			/>
          469  +			<Tool
          470  +				Name="VCALinkTool"
          471  +			/>
          472  +			<Tool
          473  +				Name="VCManifestTool"
          474  +			/>
          475  +			<Tool
          476  +				Name="VCXDCMakeTool"
          477  +			/>
          478  +			<Tool
          479  +				Name="VCBscMakeTool"
          480  +			/>
          481  +			<Tool
          482  +				Name="VCFxCopTool"
          483  +			/>
          484  +			<Tool
          485  +				Name="VCAppVerifierTool"
          486  +			/>
          487  +			<Tool
          488  +				Name="VCPostBuildEventTool"
          489  +			/>
          490  +		</Configuration>
          491  +		<Configuration
          492  +			Name="Release|Win32"
          493  +			OutputDirectory="x86\TestZlib$(ConfigurationName)"
          494  +			IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp"
          495  +			ConfigurationType="1"
          496  +			CharacterSet="2"
          497  +			WholeProgramOptimization="1"
          498  +			>
          499  +			<Tool
          500  +				Name="VCPreBuildEventTool"
          501  +			/>
          502  +			<Tool
          503  +				Name="VCCustomBuildTool"
          504  +			/>
          505  +			<Tool
          506  +				Name="VCXMLDataGeneratorTool"
          507  +			/>
          508  +			<Tool
          509  +				Name="VCWebServiceProxyGeneratorTool"
          510  +			/>
          511  +			<Tool
          512  +				Name="VCMIDLTool"
          513  +			/>
          514  +			<Tool
          515  +				Name="VCCLCompilerTool"
          516  +				Optimization="2"
          517  +				InlineFunctionExpansion="1"
          518  +				OmitFramePointers="true"
          519  +				AdditionalIncludeDirectories="..\..\.."
          520  +				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          521  +				StringPooling="true"
          522  +				BasicRuntimeChecks="0"
          523  +				RuntimeLibrary="0"
          524  +				BufferSecurityCheck="false"
          525  +				EnableFunctionLevelLinking="true"
          526  +				UsePrecompiledHeader="0"
          527  +				AssemblerListingLocation="$(IntDir)\"
          528  +				WarningLevel="3"
          529  +				Detect64BitPortabilityProblems="true"
          530  +				DebugInformationFormat="3"
          531  +			/>
          532  +			<Tool
          533  +				Name="VCManagedResourceCompilerTool"
          534  +			/>
          535  +			<Tool
          536  +				Name="VCResourceCompilerTool"
          537  +			/>
          538  +			<Tool
          539  +				Name="VCPreLinkEventTool"
          540  +			/>
          541  +			<Tool
          542  +				Name="VCLinkerTool"
          543  +				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
          544  +				OutputFile="$(OutDir)/testzlib.exe"
          545  +				LinkIncremental="1"
          546  +				GenerateManifest="false"
          547  +				GenerateDebugInformation="true"
          548  +				SubSystem="1"
          549  +				OptimizeReferences="2"
          550  +				EnableCOMDATFolding="2"
          551  +				OptimizeForWindows98="1"
          552  +				RandomizedBaseAddress="1"
          553  +				DataExecutionPrevention="0"
          554  +				TargetMachine="1"
          555  +			/>
          556  +			<Tool
          557  +				Name="VCALinkTool"
          558  +			/>
          559  +			<Tool
          560  +				Name="VCManifestTool"
          561  +			/>
          562  +			<Tool
          563  +				Name="VCXDCMakeTool"
          564  +			/>
          565  +			<Tool
          566  +				Name="VCBscMakeTool"
          567  +			/>
          568  +			<Tool
          569  +				Name="VCFxCopTool"
          570  +			/>
          571  +			<Tool
          572  +				Name="VCAppVerifierTool"
          573  +			/>
          574  +			<Tool
          575  +				Name="VCPostBuildEventTool"
          576  +			/>
          577  +		</Configuration>
          578  +		<Configuration
          579  +			Name="Release|x64"
          580  +			OutputDirectory="x64\TestZlib$(ConfigurationName)"
          581  +			IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp"
          582  +			ConfigurationType="1"
          583  +			WholeProgramOptimization="1"
          584  +			>
          585  +			<Tool
          586  +				Name="VCPreBuildEventTool"
          587  +			/>
          588  +			<Tool
          589  +				Name="VCCustomBuildTool"
          590  +			/>
          591  +			<Tool
          592  +				Name="VCXMLDataGeneratorTool"
          593  +			/>
          594  +			<Tool
          595  +				Name="VCWebServiceProxyGeneratorTool"
          596  +			/>
          597  +			<Tool
          598  +				Name="VCMIDLTool"
          599  +			/>
          600  +			<Tool
          601  +				Name="VCCLCompilerTool"
          602  +				AdditionalIncludeDirectories="..\..\.."
          603  +				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          604  +				BasicRuntimeChecks="0"
          605  +				RuntimeLibrary="0"
          606  +				BufferSecurityCheck="false"
          607  +				AssemblerListingLocation="$(IntDir)\"
          608  +			/>
          609  +			<Tool
          610  +				Name="VCManagedResourceCompilerTool"
          611  +			/>
          612  +			<Tool
          613  +				Name="VCResourceCompilerTool"
          614  +			/>
          615  +			<Tool
          616  +				Name="VCPreLinkEventTool"
          617  +			/>
          618  +			<Tool
          619  +				Name="VCLinkerTool"
          620  +				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj"
          621  +				GenerateManifest="false"
          622  +			/>
          623  +			<Tool
          624  +				Name="VCALinkTool"
          625  +			/>
          626  +			<Tool
          627  +				Name="VCManifestTool"
          628  +			/>
          629  +			<Tool
          630  +				Name="VCXDCMakeTool"
          631  +			/>
          632  +			<Tool
          633  +				Name="VCBscMakeTool"
          634  +			/>
          635  +			<Tool
          636  +				Name="VCFxCopTool"
          637  +			/>
          638  +			<Tool
          639  +				Name="VCAppVerifierTool"
          640  +			/>
          641  +			<Tool
          642  +				Name="VCPostBuildEventTool"
          643  +			/>
          644  +		</Configuration>
          645  +		<Configuration
          646  +			Name="Release|Itanium"
          647  +			OutputDirectory="ia64\TestZlib$(ConfigurationName)"
          648  +			IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp"
          649  +			ConfigurationType="1"
          650  +			CharacterSet="2"
          651  +			WholeProgramOptimization="1"
          652  +			>
          653  +			<Tool
          654  +				Name="VCPreBuildEventTool"
          655  +			/>
          656  +			<Tool
          657  +				Name="VCCustomBuildTool"
          658  +			/>
          659  +			<Tool
          660  +				Name="VCXMLDataGeneratorTool"
          661  +			/>
          662  +			<Tool
          663  +				Name="VCWebServiceProxyGeneratorTool"
          664  +			/>
          665  +			<Tool
          666  +				Name="VCMIDLTool"
          667  +				TargetEnvironment="2"
          668  +			/>
          669  +			<Tool
          670  +				Name="VCCLCompilerTool"
          671  +				Optimization="2"
          672  +				InlineFunctionExpansion="1"
          673  +				OmitFramePointers="true"
          674  +				AdditionalIncludeDirectories="..\..\.."
          675  +				PreprocessorDefinitions="ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          676  +				StringPooling="true"
          677  +				BasicRuntimeChecks="0"
          678  +				RuntimeLibrary="2"
          679  +				BufferSecurityCheck="false"
          680  +				EnableFunctionLevelLinking="true"
          681  +				UsePrecompiledHeader="0"
          682  +				AssemblerListingLocation="$(IntDir)\"
          683  +				WarningLevel="3"
          684  +				Detect64BitPortabilityProblems="true"
          685  +				DebugInformationFormat="3"
          686  +			/>
          687  +			<Tool
          688  +				Name="VCManagedResourceCompilerTool"
          689  +			/>
          690  +			<Tool
          691  +				Name="VCResourceCompilerTool"
          692  +			/>
          693  +			<Tool
          694  +				Name="VCPreLinkEventTool"
          695  +			/>
          696  +			<Tool
          697  +				Name="VCLinkerTool"
          698  +				OutputFile="$(OutDir)/testzlib.exe"
          699  +				LinkIncremental="1"
          700  +				GenerateManifest="false"
          701  +				GenerateDebugInformation="true"
          702  +				SubSystem="1"
          703  +				OptimizeReferences="2"
          704  +				EnableCOMDATFolding="2"
          705  +				OptimizeForWindows98="1"
          706  +				TargetMachine="5"
          707  +			/>
          708  +			<Tool
          709  +				Name="VCALinkTool"
          710  +			/>
          711  +			<Tool
          712  +				Name="VCManifestTool"
          713  +			/>
          714  +			<Tool
          715  +				Name="VCXDCMakeTool"
          716  +			/>
          717  +			<Tool
          718  +				Name="VCBscMakeTool"
          719  +			/>
          720  +			<Tool
          721  +				Name="VCFxCopTool"
          722  +			/>
          723  +			<Tool
          724  +				Name="VCAppVerifierTool"
          725  +			/>
          726  +			<Tool
          727  +				Name="VCPostBuildEventTool"
          728  +			/>
          729  +		</Configuration>
          730  +	</Configurations>
          731  +	<References>
          732  +	</References>
          733  +	<Files>
          734  +		<Filter
          735  +			Name="Source Files"
          736  +			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
          737  +			>
          738  +			<File
          739  +				RelativePath="..\..\..\adler32.c"
          740  +				>
          741  +			</File>
          742  +			<File
          743  +				RelativePath="..\..\..\compress.c"
          744  +				>
          745  +			</File>
          746  +			<File
          747  +				RelativePath="..\..\..\crc32.c"
          748  +				>
          749  +			</File>
          750  +			<File
          751  +				RelativePath="..\..\..\deflate.c"
          752  +				>
          753  +			</File>
          754  +			<File
          755  +				RelativePath="..\..\..\infback.c"
          756  +				>
          757  +			</File>
          758  +			<File
          759  +				RelativePath="..\..\masmx64\inffas8664.c"
          760  +				>
          761  +				<FileConfiguration
          762  +					Name="Debug|Win32"
          763  +					ExcludedFromBuild="true"
          764  +					>
          765  +					<Tool
          766  +						Name="VCCLCompilerTool"
          767  +					/>
          768  +				</FileConfiguration>
          769  +				<FileConfiguration
          770  +					Name="Debug|Itanium"
          771  +					ExcludedFromBuild="true"
          772  +					>
          773  +					<Tool
          774  +						Name="VCCLCompilerTool"
          775  +					/>
          776  +				</FileConfiguration>
          777  +				<FileConfiguration
          778  +					Name="ReleaseWithoutAsm|Win32"
          779  +					ExcludedFromBuild="true"
          780  +					>
          781  +					<Tool
          782  +						Name="VCCLCompilerTool"
          783  +					/>
          784  +				</FileConfiguration>
          785  +				<FileConfiguration
          786  +					Name="ReleaseWithoutAsm|Itanium"
          787  +					ExcludedFromBuild="true"
          788  +					>
          789  +					<Tool
          790  +						Name="VCCLCompilerTool"
          791  +					/>
          792  +				</FileConfiguration>
          793  +				<FileConfiguration
          794  +					Name="Release|Win32"
          795  +					ExcludedFromBuild="true"
          796  +					>
          797  +					<Tool
          798  +						Name="VCCLCompilerTool"
          799  +					/>
          800  +				</FileConfiguration>
          801  +				<FileConfiguration
          802  +					Name="Release|Itanium"
          803  +					ExcludedFromBuild="true"
          804  +					>
          805  +					<Tool
          806  +						Name="VCCLCompilerTool"
          807  +					/>
          808  +				</FileConfiguration>
          809  +			</File>
          810  +			<File
          811  +				RelativePath="..\..\..\inffast.c"
          812  +				>
          813  +			</File>
          814  +			<File
          815  +				RelativePath="..\..\..\inflate.c"
          816  +				>
          817  +			</File>
          818  +			<File
          819  +				RelativePath="..\..\..\inftrees.c"
          820  +				>
          821  +			</File>
          822  +			<File
          823  +				RelativePath="..\..\testzlib\testzlib.c"
          824  +				>
          825  +			</File>
          826  +			<File
          827  +				RelativePath="..\..\..\trees.c"
          828  +				>
          829  +			</File>
          830  +			<File
          831  +				RelativePath="..\..\..\uncompr.c"
          832  +				>
          833  +			</File>
          834  +			<File
          835  +				RelativePath="..\..\..\zutil.c"
          836  +				>
          837  +			</File>
          838  +		</Filter>
          839  +		<Filter
          840  +			Name="Header Files"
          841  +			Filter="h;hpp;hxx;hm;inl;inc"
          842  +			>
          843  +		</Filter>
          844  +		<Filter
          845  +			Name="Resource Files"
          846  +			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
          847  +			>
          848  +		</Filter>
          849  +	</Files>
          850  +	<Globals>
          851  +	</Globals>
          852  +</VisualStudioProject>

Added compat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9.00"
            5  +	Name="TestZlibDll"
            6  +	ProjectGUID="{C52F9E7B-498A-42BE-8DB4-85A15694366A}"
            7  +	Keyword="Win32Proj"
            8  +	TargetFrameworkVersion="131072"
            9  +	>
           10  +	<Platforms>
           11  +		<Platform
           12  +			Name="Win32"
           13  +		/>
           14  +		<Platform
           15  +			Name="x64"
           16  +		/>
           17  +		<Platform
           18  +			Name="Itanium"
           19  +		/>
           20  +	</Platforms>
           21  +	<ToolFiles>
           22  +	</ToolFiles>
           23  +	<Configurations>
           24  +		<Configuration
           25  +			Name="Debug|Win32"
           26  +			OutputDirectory="x86\TestZlibDll$(ConfigurationName)"
           27  +			IntermediateDirectory="x86\TestZlibDll$(ConfigurationName)\Tmp"
           28  +			ConfigurationType="1"
           29  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
           30  +			CharacterSet="2"
           31  +			>
           32  +			<Tool
           33  +				Name="VCPreBuildEventTool"
           34  +			/>
           35  +			<Tool
           36  +				Name="VCCustomBuildTool"
           37  +			/>
           38  +			<Tool
           39  +				Name="VCXMLDataGeneratorTool"
           40  +			/>
           41  +			<Tool
           42  +				Name="VCWebServiceProxyGeneratorTool"
           43  +			/>
           44  +			<Tool
           45  +				Name="VCMIDLTool"
           46  +			/>
           47  +			<Tool
           48  +				Name="VCCLCompilerTool"
           49  +				Optimization="0"
           50  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
           51  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE"
           52  +				MinimalRebuild="true"
           53  +				BasicRuntimeChecks="0"
           54  +				RuntimeLibrary="1"
           55  +				BufferSecurityCheck="false"
           56  +				UsePrecompiledHeader="0"
           57  +				AssemblerListingLocation="$(IntDir)\"
           58  +				WarningLevel="3"
           59  +				Detect64BitPortabilityProblems="true"
           60  +				DebugInformationFormat="4"
           61  +			/>
           62  +			<Tool
           63  +				Name="VCManagedResourceCompilerTool"
           64  +			/>
           65  +			<Tool
           66  +				Name="VCResourceCompilerTool"
           67  +			/>
           68  +			<Tool
           69  +				Name="VCPreLinkEventTool"
           70  +			/>
           71  +			<Tool
           72  +				Name="VCLinkerTool"
           73  +				AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib"
           74  +				OutputFile="$(OutDir)/testzlib.exe"
           75  +				LinkIncremental="2"
           76  +				GenerateManifest="false"
           77  +				GenerateDebugInformation="true"
           78  +				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
           79  +				SubSystem="1"
           80  +				RandomizedBaseAddress="1"
           81  +				DataExecutionPrevention="0"
           82  +				TargetMachine="1"
           83  +			/>
           84  +			<Tool
           85  +				Name="VCALinkTool"
           86  +			/>
           87  +			<Tool
           88  +				Name="VCManifestTool"
           89  +			/>
           90  +			<Tool
           91  +				Name="VCXDCMakeTool"
           92  +			/>
           93  +			<Tool
           94  +				Name="VCBscMakeTool"
           95  +			/>
           96  +			<Tool
           97  +				Name="VCFxCopTool"
           98  +			/>
           99  +			<Tool
          100  +				Name="VCAppVerifierTool"
          101  +			/>
          102  +			<Tool
          103  +				Name="VCPostBuildEventTool"
          104  +			/>
          105  +		</Configuration>
          106  +		<Configuration
          107  +			Name="Release|Win32"
          108  +			OutputDirectory="x86\TestZlibDll$(ConfigurationName)"
          109  +			IntermediateDirectory="x86\TestZlibDll$(ConfigurationName)\Tmp"
          110  +			ConfigurationType="1"
          111  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          112  +			CharacterSet="2"
          113  +			>
          114  +			<Tool
          115  +				Name="VCPreBuildEventTool"
          116  +			/>
          117  +			<Tool
          118  +				Name="VCCustomBuildTool"
          119  +			/>
          120  +			<Tool
          121  +				Name="VCXMLDataGeneratorTool"
          122  +			/>
          123  +			<Tool
          124  +				Name="VCWebServiceProxyGeneratorTool"
          125  +			/>
          126  +			<Tool
          127  +				Name="VCMIDLTool"
          128  +			/>
          129  +			<Tool
          130  +				Name="VCCLCompilerTool"
          131  +				Optimization="2"
          132  +				InlineFunctionExpansion="1"
          133  +				OmitFramePointers="true"
          134  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          135  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE"
          136  +				StringPooling="true"
          137  +				BasicRuntimeChecks="0"
          138  +				RuntimeLibrary="0"
          139  +				BufferSecurityCheck="false"
          140  +				EnableFunctionLevelLinking="true"
          141  +				UsePrecompiledHeader="0"
          142  +				AssemblerListingLocation="$(IntDir)\"
          143  +				WarningLevel="3"
          144  +				Detect64BitPortabilityProblems="true"
          145  +				DebugInformationFormat="3"
          146  +			/>
          147  +			<Tool
          148  +				Name="VCManagedResourceCompilerTool"
          149  +			/>
          150  +			<Tool
          151  +				Name="VCResourceCompilerTool"
          152  +			/>
          153  +			<Tool
          154  +				Name="VCPreLinkEventTool"
          155  +			/>
          156  +			<Tool
          157  +				Name="VCLinkerTool"
          158  +				AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib"
          159  +				OutputFile="$(OutDir)/testzlib.exe"
          160  +				LinkIncremental="1"
          161  +				GenerateManifest="false"
          162  +				GenerateDebugInformation="true"
          163  +				SubSystem="1"
          164  +				OptimizeReferences="2"
          165  +				EnableCOMDATFolding="2"
          166  +				OptimizeForWindows98="1"
          167  +				RandomizedBaseAddress="1"
          168  +				DataExecutionPrevention="0"
          169  +				TargetMachine="1"
          170  +			/>
          171  +			<Tool
          172  +				Name="VCALinkTool"
          173  +			/>
          174  +			<Tool
          175  +				Name="VCManifestTool"
          176  +			/>
          177  +			<Tool
          178  +				Name="VCXDCMakeTool"
          179  +			/>
          180  +			<Tool
          181  +				Name="VCBscMakeTool"
          182  +			/>
          183  +			<Tool
          184  +				Name="VCFxCopTool"
          185  +			/>
          186  +			<Tool
          187  +				Name="VCAppVerifierTool"
          188  +			/>
          189  +			<Tool
          190  +				Name="VCPostBuildEventTool"
          191  +			/>
          192  +		</Configuration>
          193  +		<Configuration
          194  +			Name="Debug|x64"
          195  +			OutputDirectory="x64\TestZlibDll$(ConfigurationName)"
          196  +			IntermediateDirectory="x64\TestZlibDll$(ConfigurationName)\Tmp"
          197  +			ConfigurationType="1"
          198  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          199  +			CharacterSet="2"
          200  +			>
          201  +			<Tool
          202  +				Name="VCPreBuildEventTool"
          203  +			/>
          204  +			<Tool
          205  +				Name="VCCustomBuildTool"
          206  +			/>
          207  +			<Tool
          208  +				Name="VCXMLDataGeneratorTool"
          209  +			/>
          210  +			<Tool
          211  +				Name="VCWebServiceProxyGeneratorTool"
          212  +			/>
          213  +			<Tool
          214  +				Name="VCMIDLTool"
          215  +				TargetEnvironment="3"
          216  +			/>
          217  +			<Tool
          218  +				Name="VCCLCompilerTool"
          219  +				Optimization="0"
          220  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          221  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          222  +				MinimalRebuild="true"
          223  +				BasicRuntimeChecks="0"
          224  +				RuntimeLibrary="3"
          225  +				BufferSecurityCheck="false"
          226  +				UsePrecompiledHeader="0"
          227  +				AssemblerListingLocation="$(IntDir)\"
          228  +				WarningLevel="3"
          229  +				Detect64BitPortabilityProblems="true"
          230  +				DebugInformationFormat="3"
          231  +			/>
          232  +			<Tool
          233  +				Name="VCManagedResourceCompilerTool"
          234  +			/>
          235  +			<Tool
          236  +				Name="VCResourceCompilerTool"
          237  +			/>
          238  +			<Tool
          239  +				Name="VCPreLinkEventTool"
          240  +			/>
          241  +			<Tool
          242  +				Name="VCLinkerTool"
          243  +				AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib"
          244  +				OutputFile="$(OutDir)/testzlib.exe"
          245  +				LinkIncremental="2"
          246  +				GenerateManifest="false"
          247  +				GenerateDebugInformation="true"
          248  +				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
          249  +				SubSystem="1"
          250  +				TargetMachine="17"
          251  +			/>
          252  +			<Tool
          253  +				Name="VCALinkTool"
          254  +			/>
          255  +			<Tool
          256  +				Name="VCManifestTool"
          257  +			/>
          258  +			<Tool
          259  +				Name="VCXDCMakeTool"
          260  +			/>
          261  +			<Tool
          262  +				Name="VCBscMakeTool"
          263  +			/>
          264  +			<Tool
          265  +				Name="VCFxCopTool"
          266  +			/>
          267  +			<Tool
          268  +				Name="VCAppVerifierTool"
          269  +			/>
          270  +			<Tool
          271  +				Name="VCWebDeploymentTool"
          272  +			/>
          273  +			<Tool
          274  +				Name="VCPostBuildEventTool"
          275  +			/>
          276  +		</Configuration>
          277  +		<Configuration
          278  +			Name="Debug|Itanium"
          279  +			OutputDirectory="ia64\TestZlibDll$(ConfigurationName)"
          280  +			IntermediateDirectory="ia64\TestZlibDll$(ConfigurationName)\Tmp"
          281  +			ConfigurationType="1"
          282  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          283  +			CharacterSet="2"
          284  +			>
          285  +			<Tool
          286  +				Name="VCPreBuildEventTool"
          287  +			/>
          288  +			<Tool
          289  +				Name="VCCustomBuildTool"
          290  +			/>
          291  +			<Tool
          292  +				Name="VCXMLDataGeneratorTool"
          293  +			/>
          294  +			<Tool
          295  +				Name="VCWebServiceProxyGeneratorTool"
          296  +			/>
          297  +			<Tool
          298  +				Name="VCMIDLTool"
          299  +				TargetEnvironment="2"
          300  +			/>
          301  +			<Tool
          302  +				Name="VCCLCompilerTool"
          303  +				Optimization="0"
          304  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          305  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64"
          306  +				MinimalRebuild="true"
          307  +				BasicRuntimeChecks="0"
          308  +				RuntimeLibrary="3"
          309  +				BufferSecurityCheck="false"
          310  +				UsePrecompiledHeader="0"
          311  +				AssemblerListingLocation="$(IntDir)\"
          312  +				WarningLevel="3"
          313  +				Detect64BitPortabilityProblems="true"
          314  +				DebugInformationFormat="3"
          315  +			/>
          316  +			<Tool
          317  +				Name="VCManagedResourceCompilerTool"
          318  +			/>
          319  +			<Tool
          320  +				Name="VCResourceCompilerTool"
          321  +			/>
          322  +			<Tool
          323  +				Name="VCPreLinkEventTool"
          324  +			/>
          325  +			<Tool
          326  +				Name="VCLinkerTool"
          327  +				AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib"
          328  +				OutputFile="$(OutDir)/testzlib.exe"
          329  +				LinkIncremental="2"
          330  +				GenerateManifest="false"
          331  +				GenerateDebugInformation="true"
          332  +				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
          333  +				SubSystem="1"
          334  +				TargetMachine="5"
          335  +			/>
          336  +			<Tool
          337  +				Name="VCALinkTool"
          338  +			/>
          339  +			<Tool
          340  +				Name="VCManifestTool"
          341  +			/>
          342  +			<Tool
          343  +				Name="VCXDCMakeTool"
          344  +			/>
          345  +			<Tool
          346  +				Name="VCBscMakeTool"
          347  +			/>
          348  +			<Tool
          349  +				Name="VCFxCopTool"
          350  +			/>
          351  +			<Tool
          352  +				Name="VCAppVerifierTool"
          353  +			/>
          354  +			<Tool
          355  +				Name="VCWebDeploymentTool"
          356  +			/>
          357  +			<Tool
          358  +				Name="VCPostBuildEventTool"
          359  +			/>
          360  +		</Configuration>
          361  +		<Configuration
          362  +			Name="Release|x64"
          363  +			OutputDirectory="x64\TestZlibDll$(ConfigurationName)"
          364  +			IntermediateDirectory="x64\TestZlibDll$(ConfigurationName)\Tmp"
          365  +			ConfigurationType="1"
          366  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          367  +			CharacterSet="2"
          368  +			>
          369  +			<Tool
          370  +				Name="VCPreBuildEventTool"
          371  +			/>
          372  +			<Tool
          373  +				Name="VCCustomBuildTool"
          374  +			/>
          375  +			<Tool
          376  +				Name="VCXMLDataGeneratorTool"
          377  +			/>
          378  +			<Tool
          379  +				Name="VCWebServiceProxyGeneratorTool"
          380  +			/>
          381  +			<Tool
          382  +				Name="VCMIDLTool"
          383  +				TargetEnvironment="3"
          384  +			/>
          385  +			<Tool
          386  +				Name="VCCLCompilerTool"
          387  +				Optimization="2"
          388  +				InlineFunctionExpansion="1"
          389  +				OmitFramePointers="true"
          390  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          391  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          392  +				StringPooling="true"
          393  +				BasicRuntimeChecks="0"
          394  +				RuntimeLibrary="2"
          395  +				BufferSecurityCheck="false"
          396  +				EnableFunctionLevelLinking="true"
          397  +				UsePrecompiledHeader="0"
          398  +				AssemblerListingLocation="$(IntDir)\"
          399  +				WarningLevel="3"
          400  +				Detect64BitPortabilityProblems="true"
          401  +				DebugInformationFormat="3"
          402  +			/>
          403  +			<Tool
          404  +				Name="VCManagedResourceCompilerTool"
          405  +			/>
          406  +			<Tool
          407  +				Name="VCResourceCompilerTool"
          408  +			/>
          409  +			<Tool
          410  +				Name="VCPreLinkEventTool"
          411  +			/>
          412  +			<Tool
          413  +				Name="VCLinkerTool"
          414  +				AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib"
          415  +				OutputFile="$(OutDir)/testzlib.exe"
          416  +				LinkIncremental="1"
          417  +				GenerateManifest="false"
          418  +				GenerateDebugInformation="true"
          419  +				SubSystem="1"
          420  +				OptimizeReferences="2"
          421  +				EnableCOMDATFolding="2"
          422  +				OptimizeForWindows98="1"
          423  +				TargetMachine="17"
          424  +			/>
          425  +			<Tool
          426  +				Name="VCALinkTool"
          427  +			/>
          428  +			<Tool
          429  +				Name="VCManifestTool"
          430  +			/>
          431  +			<Tool
          432  +				Name="VCXDCMakeTool"
          433  +			/>
          434  +			<Tool
          435  +				Name="VCBscMakeTool"
          436  +			/>
          437  +			<Tool
          438  +				Name="VCFxCopTool"
          439  +			/>
          440  +			<Tool
          441  +				Name="VCAppVerifierTool"
          442  +			/>
          443  +			<Tool
          444  +				Name="VCWebDeploymentTool"
          445  +			/>
          446  +			<Tool
          447  +				Name="VCPostBuildEventTool"
          448  +			/>
          449  +		</Configuration>
          450  +		<Configuration
          451  +			Name="Release|Itanium"
          452  +			OutputDirectory="ia64\TestZlibDll$(ConfigurationName)"
          453  +			IntermediateDirectory="ia64\TestZlibDll$(ConfigurationName)\Tmp"
          454  +			ConfigurationType="1"
          455  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          456  +			CharacterSet="2"
          457  +			>
          458  +			<Tool
          459  +				Name="VCPreBuildEventTool"
          460  +			/>
          461  +			<Tool
          462  +				Name="VCCustomBuildTool"
          463  +			/>
          464  +			<Tool
          465  +				Name="VCXMLDataGeneratorTool"
          466  +			/>
          467  +			<Tool
          468  +				Name="VCWebServiceProxyGeneratorTool"
          469  +			/>
          470  +			<Tool
          471  +				Name="VCMIDLTool"
          472  +				TargetEnvironment="2"
          473  +			/>
          474  +			<Tool
          475  +				Name="VCCLCompilerTool"
          476  +				Optimization="2"
          477  +				InlineFunctionExpansion="1"
          478  +				OmitFramePointers="true"
          479  +				AdditionalIncludeDirectories="..\..\..;..\..\minizip"
          480  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64"
          481  +				StringPooling="true"
          482  +				BasicRuntimeChecks="0"
          483  +				RuntimeLibrary="2"
          484  +				BufferSecurityCheck="false"
          485  +				EnableFunctionLevelLinking="true"
          486  +				UsePrecompiledHeader="0"
          487  +				AssemblerListingLocation="$(IntDir)\"
          488  +				WarningLevel="3"
          489  +				Detect64BitPortabilityProblems="true"
          490  +				DebugInformationFormat="3"
          491  +			/>
          492  +			<Tool
          493  +				Name="VCManagedResourceCompilerTool"
          494  +			/>
          495  +			<Tool
          496  +				Name="VCResourceCompilerTool"
          497  +			/>
          498  +			<Tool
          499  +				Name="VCPreLinkEventTool"
          500  +			/>
          501  +			<Tool
          502  +				Name="VCLinkerTool"
          503  +				AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib"
          504  +				OutputFile="$(OutDir)/testzlib.exe"
          505  +				LinkIncremental="1"
          506  +				GenerateManifest="false"
          507  +				GenerateDebugInformation="true"
          508  +				SubSystem="1"
          509  +				OptimizeReferences="2"
          510  +				EnableCOMDATFolding="2"
          511  +				OptimizeForWindows98="1"
          512  +				TargetMachine="5"
          513  +			/>
          514  +			<Tool
          515  +				Name="VCALinkTool"
          516  +			/>
          517  +			<Tool
          518  +				Name="VCManifestTool"
          519  +			/>
          520  +			<Tool
          521  +				Name="VCXDCMakeTool"
          522  +			/>
          523  +			<Tool
          524  +				Name="VCBscMakeTool"
          525  +			/>
          526  +			<Tool
          527  +				Name="VCFxCopTool"
          528  +			/>
          529  +			<Tool
          530  +				Name="VCAppVerifierTool"
          531  +			/>
          532  +			<Tool
          533  +				Name="VCWebDeploymentTool"
          534  +			/>
          535  +			<Tool
          536  +				Name="VCPostBuildEventTool"
          537  +			/>
          538  +		</Configuration>
          539  +	</Configurations>
          540  +	<References>
          541  +	</References>
          542  +	<Files>
          543  +		<Filter
          544  +			Name="Source Files"
          545  +			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
          546  +			>
          547  +			<File
          548  +				RelativePath="..\..\testzlib\testzlib.c"
          549  +				>
          550  +			</File>
          551  +		</Filter>
          552  +		<Filter
          553  +			Name="Header Files"
          554  +			Filter="h;hpp;hxx;hm;inl;inc"
          555  +			>
          556  +		</Filter>
          557  +		<Filter
          558  +			Name="Resource Files"
          559  +			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
          560  +			>
          561  +		</Filter>
          562  +	</Files>
          563  +	<Globals>
          564  +	</Globals>
          565  +</VisualStudioProject>

Added compat/zlib/contrib/vstudio/vc9/zlib.rc.

            1  +#include <windows.h>
            2  +
            3  +#define IDR_VERSION1  1
            4  +IDR_VERSION1	VERSIONINFO	MOVEABLE IMPURE LOADONCALL DISCARDABLE
            5  +  FILEVERSION	 1.2.7,0
            6  +  PRODUCTVERSION 1.2.7,0
            7  +  FILEFLAGSMASK	VS_FFI_FILEFLAGSMASK
            8  +  FILEFLAGS	0
            9  +  FILEOS	VOS_DOS_WINDOWS32
           10  +  FILETYPE	VFT_DLL
           11  +  FILESUBTYPE	0	// not used
           12  +BEGIN
           13  +  BLOCK "StringFileInfo"
           14  +  BEGIN
           15  +    BLOCK "040904E4"
           16  +    //language ID = U.S. English, char set = Windows, Multilingual
           17  +
           18  +    BEGIN
           19  +      VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
           20  +      VALUE "FileVersion",	"1.2.7\0"
           21  +      VALUE "InternalName",	"zlib\0"
           22  +      VALUE "OriginalFilename",	"zlib.dll\0"
           23  +      VALUE "ProductName",	"ZLib.DLL\0"
           24  +      VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
           25  +      VALUE "LegalCopyright", "(C) 1995-2012 Jean-loup Gailly & Mark Adler\0"
           26  +    END
           27  +  END
           28  +  BLOCK "VarFileInfo"
           29  +  BEGIN
           30  +    VALUE "Translation", 0x0409, 1252
           31  +  END
           32  +END

Added compat/zlib/contrib/vstudio/vc9/zlibstat.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9,00"
            5  +	Name="zlibstat"
            6  +	ProjectGUID="{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}"
            7  +	TargetFrameworkVersion="131072"
            8  +	>
            9  +	<Platforms>
           10  +		<Platform
           11  +			Name="Win32"
           12  +		/>
           13  +		<Platform
           14  +			Name="x64"
           15  +		/>
           16  +		<Platform
           17  +			Name="Itanium"
           18  +		/>
           19  +	</Platforms>
           20  +	<ToolFiles>
           21  +	</ToolFiles>
           22  +	<Configurations>
           23  +		<Configuration
           24  +			Name="Debug|Win32"
           25  +			OutputDirectory="x86\ZlibStat$(ConfigurationName)"
           26  +			IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp"
           27  +			ConfigurationType="4"
           28  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
           29  +			UseOfMFC="0"
           30  +			ATLMinimizesCRunTimeLibraryUsage="false"
           31  +			>
           32  +			<Tool
           33  +				Name="VCPreBuildEventTool"
           34  +			/>
           35  +			<Tool
           36  +				Name="VCCustomBuildTool"
           37  +			/>
           38  +			<Tool
           39  +				Name="VCXMLDataGeneratorTool"
           40  +			/>
           41  +			<Tool
           42  +				Name="VCWebServiceProxyGeneratorTool"
           43  +			/>
           44  +			<Tool
           45  +				Name="VCMIDLTool"
           46  +			/>
           47  +			<Tool
           48  +				Name="VCCLCompilerTool"
           49  +				Optimization="0"
           50  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
           51  +				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
           52  +				ExceptionHandling="0"
           53  +				RuntimeLibrary="1"
           54  +				BufferSecurityCheck="false"
           55  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
           56  +				AssemblerListingLocation="$(IntDir)\"
           57  +				ObjectFile="$(IntDir)\"
           58  +				ProgramDataBaseFileName="$(OutDir)\"
           59  +				WarningLevel="3"
           60  +				SuppressStartupBanner="true"
           61  +				Detect64BitPortabilityProblems="true"
           62  +				DebugInformationFormat="1"
           63  +			/>
           64  +			<Tool
           65  +				Name="VCManagedResourceCompilerTool"
           66  +			/>
           67  +			<Tool
           68  +				Name="VCResourceCompilerTool"
           69  +				Culture="1036"
           70  +			/>
           71  +			<Tool
           72  +				Name="VCPreLinkEventTool"
           73  +			/>
           74  +			<Tool
           75  +				Name="VCLibrarianTool"
           76  +				AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB"
           77  +				OutputFile="$(OutDir)\zlibstat.lib"
           78  +				SuppressStartupBanner="true"
           79  +			/>
           80  +			<Tool
           81  +				Name="VCALinkTool"
           82  +			/>
           83  +			<Tool
           84  +				Name="VCXDCMakeTool"
           85  +			/>
           86  +			<Tool
           87  +				Name="VCBscMakeTool"
           88  +			/>
           89  +			<Tool
           90  +				Name="VCFxCopTool"
           91  +			/>
           92  +			<Tool
           93  +				Name="VCPostBuildEventTool"
           94  +			/>
           95  +		</Configuration>
           96  +		<Configuration
           97  +			Name="Debug|x64"
           98  +			OutputDirectory="x64\ZlibStat$(ConfigurationName)"
           99  +			IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp"
          100  +			ConfigurationType="4"
          101  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          102  +			UseOfMFC="0"
          103  +			ATLMinimizesCRunTimeLibraryUsage="false"
          104  +			>
          105  +			<Tool
          106  +				Name="VCPreBuildEventTool"
          107  +			/>
          108  +			<Tool
          109  +				Name="VCCustomBuildTool"
          110  +			/>
          111  +			<Tool
          112  +				Name="VCXMLDataGeneratorTool"
          113  +			/>
          114  +			<Tool
          115  +				Name="VCWebServiceProxyGeneratorTool"
          116  +			/>
          117  +			<Tool
          118  +				Name="VCMIDLTool"
          119  +				TargetEnvironment="3"
          120  +			/>
          121  +			<Tool
          122  +				Name="VCCLCompilerTool"
          123  +				Optimization="0"
          124  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          125  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          126  +				ExceptionHandling="0"
          127  +				RuntimeLibrary="3"
          128  +				BufferSecurityCheck="false"
          129  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          130  +				AssemblerListingLocation="$(IntDir)\"
          131  +				ObjectFile="$(IntDir)\"
          132  +				ProgramDataBaseFileName="$(OutDir)\"
          133  +				WarningLevel="3"
          134  +				SuppressStartupBanner="true"
          135  +				Detect64BitPortabilityProblems="true"
          136  +				DebugInformationFormat="1"
          137  +			/>
          138  +			<Tool
          139  +				Name="VCManagedResourceCompilerTool"
          140  +			/>
          141  +			<Tool
          142  +				Name="VCResourceCompilerTool"
          143  +				Culture="1036"
          144  +			/>
          145  +			<Tool
          146  +				Name="VCPreLinkEventTool"
          147  +			/>
          148  +			<Tool
          149  +				Name="VCLibrarianTool"
          150  +				AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB"
          151  +				OutputFile="$(OutDir)\zlibstat.lib"
          152  +				SuppressStartupBanner="true"
          153  +			/>
          154  +			<Tool
          155  +				Name="VCALinkTool"
          156  +			/>
          157  +			<Tool
          158  +				Name="VCXDCMakeTool"
          159  +			/>
          160  +			<Tool
          161  +				Name="VCBscMakeTool"
          162  +			/>
          163  +			<Tool
          164  +				Name="VCFxCopTool"
          165  +			/>
          166  +			<Tool
          167  +				Name="VCPostBuildEventTool"
          168  +			/>
          169  +		</Configuration>
          170  +		<Configuration
          171  +			Name="Debug|Itanium"
          172  +			OutputDirectory="ia64\ZlibStat$(ConfigurationName)"
          173  +			IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp"
          174  +			ConfigurationType="4"
          175  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          176  +			UseOfMFC="0"
          177  +			ATLMinimizesCRunTimeLibraryUsage="false"
          178  +			>
          179  +			<Tool
          180  +				Name="VCPreBuildEventTool"
          181  +			/>
          182  +			<Tool
          183  +				Name="VCCustomBuildTool"
          184  +			/>
          185  +			<Tool
          186  +				Name="VCXMLDataGeneratorTool"
          187  +			/>
          188  +			<Tool
          189  +				Name="VCWebServiceProxyGeneratorTool"
          190  +			/>
          191  +			<Tool
          192  +				Name="VCMIDLTool"
          193  +				TargetEnvironment="2"
          194  +			/>
          195  +			<Tool
          196  +				Name="VCCLCompilerTool"
          197  +				Optimization="0"
          198  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          199  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          200  +				ExceptionHandling="0"
          201  +				RuntimeLibrary="3"
          202  +				BufferSecurityCheck="false"
          203  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          204  +				AssemblerListingLocation="$(IntDir)\"
          205  +				ObjectFile="$(IntDir)\"
          206  +				ProgramDataBaseFileName="$(OutDir)\"
          207  +				WarningLevel="3"
          208  +				SuppressStartupBanner="true"
          209  +				Detect64BitPortabilityProblems="true"
          210  +				DebugInformationFormat="1"
          211  +			/>
          212  +			<Tool
          213  +				Name="VCManagedResourceCompilerTool"
          214  +			/>
          215  +			<Tool
          216  +				Name="VCResourceCompilerTool"
          217  +				Culture="1036"
          218  +			/>
          219  +			<Tool
          220  +				Name="VCPreLinkEventTool"
          221  +			/>
          222  +			<Tool
          223  +				Name="VCLibrarianTool"
          224  +				AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB"
          225  +				OutputFile="$(OutDir)\zlibstat.lib"
          226  +				SuppressStartupBanner="true"
          227  +			/>
          228  +			<Tool
          229  +				Name="VCALinkTool"
          230  +			/>
          231  +			<Tool
          232  +				Name="VCXDCMakeTool"
          233  +			/>
          234  +			<Tool
          235  +				Name="VCBscMakeTool"
          236  +			/>
          237  +			<Tool
          238  +				Name="VCFxCopTool"
          239  +			/>
          240  +			<Tool
          241  +				Name="VCPostBuildEventTool"
          242  +			/>
          243  +		</Configuration>
          244  +		<Configuration
          245  +			Name="Release|Win32"
          246  +			OutputDirectory="x86\ZlibStat$(ConfigurationName)"
          247  +			IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp"
          248  +			ConfigurationType="4"
          249  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          250  +			UseOfMFC="0"
          251  +			ATLMinimizesCRunTimeLibraryUsage="false"
          252  +			>
          253  +			<Tool
          254  +				Name="VCPreBuildEventTool"
          255  +			/>
          256  +			<Tool
          257  +				Name="VCCustomBuildTool"
          258  +			/>
          259  +			<Tool
          260  +				Name="VCXMLDataGeneratorTool"
          261  +			/>
          262  +			<Tool
          263  +				Name="VCWebServiceProxyGeneratorTool"
          264  +			/>
          265  +			<Tool
          266  +				Name="VCMIDLTool"
          267  +			/>
          268  +			<Tool
          269  +				Name="VCCLCompilerTool"
          270  +				InlineFunctionExpansion="1"
          271  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          272  +				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF"
          273  +				StringPooling="true"
          274  +				ExceptionHandling="0"
          275  +				RuntimeLibrary="0"
          276  +				BufferSecurityCheck="false"
          277  +				EnableFunctionLevelLinking="true"
          278  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          279  +				AssemblerListingLocation="$(IntDir)\"
          280  +				ObjectFile="$(IntDir)\"
          281  +				ProgramDataBaseFileName="$(OutDir)\"
          282  +				WarningLevel="3"
          283  +				SuppressStartupBanner="true"
          284  +			/>
          285  +			<Tool
          286  +				Name="VCManagedResourceCompilerTool"
          287  +			/>
          288  +			<Tool
          289  +				Name="VCResourceCompilerTool"
          290  +				Culture="1036"
          291  +			/>
          292  +			<Tool
          293  +				Name="VCPreLinkEventTool"
          294  +			/>
          295  +			<Tool
          296  +				Name="VCLibrarianTool"
          297  +				AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB"
          298  +				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj "
          299  +				OutputFile="$(OutDir)\zlibstat.lib"
          300  +				SuppressStartupBanner="true"
          301  +			/>
          302  +			<Tool
          303  +				Name="VCALinkTool"
          304  +			/>
          305  +			<Tool
          306  +				Name="VCXDCMakeTool"
          307  +			/>
          308  +			<Tool
          309  +				Name="VCBscMakeTool"
          310  +			/>
          311  +			<Tool
          312  +				Name="VCFxCopTool"
          313  +			/>
          314  +			<Tool
          315  +				Name="VCPostBuildEventTool"
          316  +			/>
          317  +		</Configuration>
          318  +		<Configuration
          319  +			Name="Release|x64"
          320  +			OutputDirectory="x64\ZlibStat$(ConfigurationName)"
          321  +			IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp"
          322  +			ConfigurationType="4"
          323  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          324  +			UseOfMFC="0"
          325  +			ATLMinimizesCRunTimeLibraryUsage="false"
          326  +			>
          327  +			<Tool
          328  +				Name="VCPreBuildEventTool"
          329  +			/>
          330  +			<Tool
          331  +				Name="VCCustomBuildTool"
          332  +			/>
          333  +			<Tool
          334  +				Name="VCXMLDataGeneratorTool"
          335  +			/>
          336  +			<Tool
          337  +				Name="VCWebServiceProxyGeneratorTool"
          338  +			/>
          339  +			<Tool
          340  +				Name="VCMIDLTool"
          341  +				TargetEnvironment="3"
          342  +			/>
          343  +			<Tool
          344  +				Name="VCCLCompilerTool"
          345  +				InlineFunctionExpansion="1"
          346  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          347  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64"
          348  +				StringPooling="true"
          349  +				ExceptionHandling="0"
          350  +				RuntimeLibrary="2"
          351  +				BufferSecurityCheck="false"
          352  +				EnableFunctionLevelLinking="true"
          353  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          354  +				AssemblerListingLocation="$(IntDir)\"
          355  +				ObjectFile="$(IntDir)\"
          356  +				ProgramDataBaseFileName="$(OutDir)\"
          357  +				WarningLevel="3"
          358  +				SuppressStartupBanner="true"
          359  +			/>
          360  +			<Tool
          361  +				Name="VCManagedResourceCompilerTool"
          362  +			/>
          363  +			<Tool
          364  +				Name="VCResourceCompilerTool"
          365  +				Culture="1036"
          366  +			/>
          367  +			<Tool
          368  +				Name="VCPreLinkEventTool"
          369  +			/>
          370  +			<Tool
          371  +				Name="VCLibrarianTool"
          372  +				AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB"
          373  +				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
          374  +				OutputFile="$(OutDir)\zlibstat.lib"
          375  +				SuppressStartupBanner="true"
          376  +			/>
          377  +			<Tool
          378  +				Name="VCALinkTool"
          379  +			/>
          380  +			<Tool
          381  +				Name="VCXDCMakeTool"
          382  +			/>
          383  +			<Tool
          384  +				Name="VCBscMakeTool"
          385  +			/>
          386  +			<Tool
          387  +				Name="VCFxCopTool"
          388  +			/>
          389  +			<Tool
          390  +				Name="VCPostBuildEventTool"
          391  +			/>
          392  +		</Configuration>
          393  +		<Configuration
          394  +			Name="Release|Itanium"
          395  +			OutputDirectory="ia64\ZlibStat$(ConfigurationName)"
          396  +			IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp"
          397  +			ConfigurationType="4"
          398  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          399  +			UseOfMFC="0"
          400  +			ATLMinimizesCRunTimeLibraryUsage="false"
          401  +			>
          402  +			<Tool
          403  +				Name="VCPreBuildEventTool"
          404  +			/>
          405  +			<Tool
          406  +				Name="VCCustomBuildTool"
          407  +			/>
          408  +			<Tool
          409  +				Name="VCXMLDataGeneratorTool"
          410  +			/>
          411  +			<Tool
          412  +				Name="VCWebServiceProxyGeneratorTool"
          413  +			/>
          414  +			<Tool
          415  +				Name="VCMIDLTool"
          416  +				TargetEnvironment="2"
          417  +			/>
          418  +			<Tool
          419  +				Name="VCCLCompilerTool"
          420  +				InlineFunctionExpansion="1"
          421  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          422  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          423  +				StringPooling="true"
          424  +				ExceptionHandling="0"
          425  +				RuntimeLibrary="2"
          426  +				BufferSecurityCheck="false"
          427  +				EnableFunctionLevelLinking="true"
          428  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          429  +				AssemblerListingLocation="$(IntDir)\"
          430  +				ObjectFile="$(IntDir)\"
          431  +				ProgramDataBaseFileName="$(OutDir)\"
          432  +				WarningLevel="3"
          433  +				SuppressStartupBanner="true"
          434  +			/>
          435  +			<Tool
          436  +				Name="VCManagedResourceCompilerTool"
          437  +			/>
          438  +			<Tool
          439  +				Name="VCResourceCompilerTool"
          440  +				Culture="1036"
          441  +			/>
          442  +			<Tool
          443  +				Name="VCPreLinkEventTool"
          444  +			/>
          445  +			<Tool
          446  +				Name="VCLibrarianTool"
          447  +				AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB"
          448  +				OutputFile="$(OutDir)\zlibstat.lib"
          449  +				SuppressStartupBanner="true"
          450  +			/>
          451  +			<Tool
          452  +				Name="VCALinkTool"
          453  +			/>
          454  +			<Tool
          455  +				Name="VCXDCMakeTool"
          456  +			/>
          457  +			<Tool
          458  +				Name="VCBscMakeTool"
          459  +			/>
          460  +			<Tool
          461  +				Name="VCFxCopTool"
          462  +			/>
          463  +			<Tool
          464  +				Name="VCPostBuildEventTool"
          465  +			/>
          466  +		</Configuration>
          467  +		<Configuration
          468  +			Name="ReleaseWithoutAsm|Win32"
          469  +			OutputDirectory="x86\ZlibStat$(ConfigurationName)"
          470  +			IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp"
          471  +			ConfigurationType="4"
          472  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          473  +			UseOfMFC="0"
          474  +			ATLMinimizesCRunTimeLibraryUsage="false"
          475  +			>
          476  +			<Tool
          477  +				Name="VCPreBuildEventTool"
          478  +			/>
          479  +			<Tool
          480  +				Name="VCCustomBuildTool"
          481  +			/>
          482  +			<Tool
          483  +				Name="VCXMLDataGeneratorTool"
          484  +			/>
          485  +			<Tool
          486  +				Name="VCWebServiceProxyGeneratorTool"
          487  +			/>
          488  +			<Tool
          489  +				Name="VCMIDLTool"
          490  +			/>
          491  +			<Tool
          492  +				Name="VCCLCompilerTool"
          493  +				InlineFunctionExpansion="1"
          494  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          495  +				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
          496  +				StringPooling="true"
          497  +				ExceptionHandling="0"
          498  +				RuntimeLibrary="0"
          499  +				BufferSecurityCheck="false"
          500  +				EnableFunctionLevelLinking="true"
          501  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          502  +				AssemblerListingLocation="$(IntDir)\"
          503  +				ObjectFile="$(IntDir)\"
          504  +				ProgramDataBaseFileName="$(OutDir)\"
          505  +				WarningLevel="3"
          506  +				SuppressStartupBanner="true"
          507  +			/>
          508  +			<Tool
          509  +				Name="VCManagedResourceCompilerTool"
          510  +			/>
          511  +			<Tool
          512  +				Name="VCResourceCompilerTool"
          513  +				Culture="1036"
          514  +			/>
          515  +			<Tool
          516  +				Name="VCPreLinkEventTool"
          517  +			/>
          518  +			<Tool
          519  +				Name="VCLibrarianTool"
          520  +				AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB"
          521  +				OutputFile="$(OutDir)\zlibstat.lib"
          522  +				SuppressStartupBanner="true"
          523  +			/>
          524  +			<Tool
          525  +				Name="VCALinkTool"
          526  +			/>
          527  +			<Tool
          528  +				Name="VCXDCMakeTool"
          529  +			/>
          530  +			<Tool
          531  +				Name="VCBscMakeTool"
          532  +			/>
          533  +			<Tool
          534  +				Name="VCFxCopTool"
          535  +			/>
          536  +			<Tool
          537  +				Name="VCPostBuildEventTool"
          538  +			/>
          539  +		</Configuration>
          540  +		<Configuration
          541  +			Name="ReleaseWithoutAsm|x64"
          542  +			OutputDirectory="x64\ZlibStat$(ConfigurationName)"
          543  +			IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp"
          544  +			ConfigurationType="4"
          545  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          546  +			UseOfMFC="0"
          547  +			ATLMinimizesCRunTimeLibraryUsage="false"
          548  +			>
          549  +			<Tool
          550  +				Name="VCPreBuildEventTool"
          551  +			/>
          552  +			<Tool
          553  +				Name="VCCustomBuildTool"
          554  +			/>
          555  +			<Tool
          556  +				Name="VCXMLDataGeneratorTool"
          557  +			/>
          558  +			<Tool
          559  +				Name="VCWebServiceProxyGeneratorTool"
          560  +			/>
          561  +			<Tool
          562  +				Name="VCMIDLTool"
          563  +				TargetEnvironment="3"
          564  +			/>
          565  +			<Tool
          566  +				Name="VCCLCompilerTool"
          567  +				InlineFunctionExpansion="1"
          568  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          569  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          570  +				StringPooling="true"
          571  +				ExceptionHandling="0"
          572  +				RuntimeLibrary="2"
          573  +				BufferSecurityCheck="false"
          574  +				EnableFunctionLevelLinking="true"
          575  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          576  +				AssemblerListingLocation="$(IntDir)\"
          577  +				ObjectFile="$(IntDir)\"
          578  +				ProgramDataBaseFileName="$(OutDir)\"
          579  +				WarningLevel="3"
          580  +				SuppressStartupBanner="true"
          581  +			/>
          582  +			<Tool
          583  +				Name="VCManagedResourceCompilerTool"
          584  +			/>
          585  +			<Tool
          586  +				Name="VCResourceCompilerTool"
          587  +				Culture="1036"
          588  +			/>
          589  +			<Tool
          590  +				Name="VCPreLinkEventTool"
          591  +			/>
          592  +			<Tool
          593  +				Name="VCLibrarianTool"
          594  +				AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB"
          595  +				OutputFile="$(OutDir)\zlibstat.lib"
          596  +				SuppressStartupBanner="true"
          597  +			/>
          598  +			<Tool
          599  +				Name="VCALinkTool"
          600  +			/>
          601  +			<Tool
          602  +				Name="VCXDCMakeTool"
          603  +			/>
          604  +			<Tool
          605  +				Name="VCBscMakeTool"
          606  +			/>
          607  +			<Tool
          608  +				Name="VCFxCopTool"
          609  +			/>
          610  +			<Tool
          611  +				Name="VCPostBuildEventTool"
          612  +			/>
          613  +		</Configuration>
          614  +		<Configuration
          615  +			Name="ReleaseWithoutAsm|Itanium"
          616  +			OutputDirectory="ia64\ZlibStat$(ConfigurationName)"
          617  +			IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp"
          618  +			ConfigurationType="4"
          619  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          620  +			UseOfMFC="0"
          621  +			ATLMinimizesCRunTimeLibraryUsage="false"
          622  +			>
          623  +			<Tool
          624  +				Name="VCPreBuildEventTool"
          625  +			/>
          626  +			<Tool
          627  +				Name="VCCustomBuildTool"
          628  +			/>
          629  +			<Tool
          630  +				Name="VCXMLDataGeneratorTool"
          631  +			/>
          632  +			<Tool
          633  +				Name="VCWebServiceProxyGeneratorTool"
          634  +			/>
          635  +			<Tool
          636  +				Name="VCMIDLTool"
          637  +				TargetEnvironment="2"
          638  +			/>
          639  +			<Tool
          640  +				Name="VCCLCompilerTool"
          641  +				InlineFunctionExpansion="1"
          642  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          643  +				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64"
          644  +				StringPooling="true"
          645  +				ExceptionHandling="0"
          646  +				RuntimeLibrary="2"
          647  +				BufferSecurityCheck="false"
          648  +				EnableFunctionLevelLinking="true"
          649  +				PrecompiledHeaderFile="$(IntDir)/zlibstat.pch"
          650  +				AssemblerListingLocation="$(IntDir)\"
          651  +				ObjectFile="$(IntDir)\"
          652  +				ProgramDataBaseFileName="$(OutDir)\"
          653  +				WarningLevel="3"
          654  +				SuppressStartupBanner="true"
          655  +			/>
          656  +			<Tool
          657  +				Name="VCManagedResourceCompilerTool"
          658  +			/>
          659  +			<Tool
          660  +				Name="VCResourceCompilerTool"
          661  +				Culture="1036"
          662  +			/>
          663  +			<Tool
          664  +				Name="VCPreLinkEventTool"
          665  +			/>
          666  +			<Tool
          667  +				Name="VCLibrarianTool"
          668  +				AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB"
          669  +				OutputFile="$(OutDir)\zlibstat.lib"
          670  +				SuppressStartupBanner="true"
          671  +			/>
          672  +			<Tool
          673  +				Name="VCALinkTool"
          674  +			/>
          675  +			<Tool
          676  +				Name="VCXDCMakeTool"
          677  +			/>
          678  +			<Tool
          679  +				Name="VCBscMakeTool"
          680  +			/>
          681  +			<Tool
          682  +				Name="VCFxCopTool"
          683  +			/>
          684  +			<Tool
          685  +				Name="VCPostBuildEventTool"
          686  +			/>
          687  +		</Configuration>
          688  +	</Configurations>
          689  +	<References>
          690  +	</References>
          691  +	<Files>
          692  +		<Filter
          693  +			Name="Source Files"
          694  +			>
          695  +			<File
          696  +				RelativePath="..\..\..\adler32.c"
          697  +				>
          698  +			</File>
          699  +			<File
          700  +				RelativePath="..\..\..\compress.c"
          701  +				>
          702  +			</File>
          703  +			<File
          704  +				RelativePath="..\..\..\crc32.c"
          705  +				>
          706  +			</File>
          707  +			<File
          708  +				RelativePath="..\..\..\deflate.c"
          709  +				>
          710  +			</File>
          711  +			<File
          712  +				RelativePath="..\..\..\gzclose.c"
          713  +				>
          714  +			</File>
          715  +			<File
          716  +				RelativePath="..\..\..\gzguts.h"
          717  +				>
          718  +			</File>
          719  +			<File
          720  +				RelativePath="..\..\..\gzlib.c"
          721  +				>
          722  +			</File>
          723  +			<File
          724  +				RelativePath="..\..\..\gzread.c"
          725  +				>
          726  +			</File>
          727  +			<File
          728  +				RelativePath="..\..\..\gzwrite.c"
          729  +				>
          730  +			</File>
          731  +			<File
          732  +				RelativePath="..\..\..\infback.c"
          733  +				>
          734  +			</File>
          735  +			<File
          736  +				RelativePath="..\..\masmx64\inffas8664.c"
          737  +				>
          738  +				<FileConfiguration
          739  +					Name="Debug|Win32"
          740  +					ExcludedFromBuild="true"
          741  +					>
          742  +					<Tool
          743  +						Name="VCCLCompilerTool"
          744  +					/>
          745  +				</FileConfiguration>
          746  +				<FileConfiguration
          747  +					Name="Debug|Itanium"
          748  +					ExcludedFromBuild="true"
          749  +					>
          750  +					<Tool
          751  +						Name="VCCLCompilerTool"
          752  +					/>
          753  +				</FileConfiguration>
          754  +				<FileConfiguration
          755  +					Name="Release|Win32"
          756  +					ExcludedFromBuild="true"
          757  +					>
          758  +					<Tool
          759  +						Name="VCCLCompilerTool"
          760  +					/>
          761  +				</FileConfiguration>
          762  +				<FileConfiguration
          763  +					Name="Release|Itanium"
          764  +					ExcludedFromBuild="true"
          765  +					>
          766  +					<Tool
          767  +						Name="VCCLCompilerTool"
          768  +					/>
          769  +				</FileConfiguration>
          770  +				<FileConfiguration
          771  +					Name="ReleaseWithoutAsm|Win32"
          772  +					ExcludedFromBuild="true"
          773  +					>
          774  +					<Tool
          775  +						Name="VCCLCompilerTool"
          776  +					/>
          777  +				</FileConfiguration>
          778  +				<FileConfiguration
          779  +					Name="ReleaseWithoutAsm|Itanium"
          780  +					ExcludedFromBuild="true"
          781  +					>
          782  +					<Tool
          783  +						Name="VCCLCompilerTool"
          784  +					/>
          785  +				</FileConfiguration>
          786  +			</File>
          787  +			<File
          788  +				RelativePath="..\..\..\inffast.c"
          789  +				>
          790  +			</File>
          791  +			<File
          792  +				RelativePath="..\..\..\inflate.c"
          793  +				>
          794  +			</File>
          795  +			<File
          796  +				RelativePath="..\..\..\inftrees.c"
          797  +				>
          798  +			</File>
          799  +			<File
          800  +				RelativePath="..\..\minizip\ioapi.c"
          801  +				>
          802  +			</File>
          803  +			<File
          804  +				RelativePath="..\..\..\trees.c"
          805  +				>
          806  +			</File>
          807  +			<File
          808  +				RelativePath="..\..\..\uncompr.c"
          809  +				>
          810  +			</File>
          811  +			<File
          812  +				RelativePath="..\..\minizip\unzip.c"
          813  +				>
          814  +			</File>
          815  +			<File
          816  +				RelativePath="..\..\minizip\zip.c"
          817  +				>
          818  +			</File>
          819  +			<File
          820  +				RelativePath=".\zlib.rc"
          821  +				>
          822  +			</File>
          823  +			<File
          824  +				RelativePath=".\zlibvc.def"
          825  +				>
          826  +			</File>
          827  +			<File
          828  +				RelativePath="..\..\..\zutil.c"
          829  +				>
          830  +			</File>
          831  +		</Filter>
          832  +	</Files>
          833  +	<Globals>
          834  +	</Globals>
          835  +</VisualStudioProject>

Added compat/zlib/contrib/vstudio/vc9/zlibvc.def.

            1  +LIBRARY
            2  +; zlib data compression and ZIP file I/O library
            3  +
            4  +VERSION		1.2.7
            5  +
            6  +EXPORTS
            7  +        adler32                                  @1
            8  +        compress                                 @2
            9  +        crc32                                    @3
           10  +        deflate                                  @4
           11  +        deflateCopy                              @5
           12  +        deflateEnd                               @6
           13  +        deflateInit2_                            @7
           14  +        deflateInit_                             @8
           15  +        deflateParams                            @9
           16  +        deflateReset                             @10
           17  +        deflateSetDictionary                     @11
           18  +        gzclose                                  @12
           19  +        gzdopen                                  @13
           20  +        gzerror                                  @14
           21  +        gzflush                                  @15
           22  +        gzopen                                   @16
           23  +        gzread                                   @17
           24  +        gzwrite                                  @18
           25  +        inflate                                  @19
           26  +        inflateEnd                               @20
           27  +        inflateInit2_                            @21
           28  +        inflateInit_                             @22
           29  +        inflateReset                             @23
           30  +        inflateSetDictionary                     @24
           31  +        inflateSync                              @25
           32  +        uncompress                               @26
           33  +        zlibVersion                              @27
           34  +        gzprintf                                 @28
           35  +        gzputc                                   @29
           36  +        gzgetc                                   @30
           37  +        gzseek                                   @31
           38  +        gzrewind                                 @32
           39  +        gztell                                   @33
           40  +        gzeof                                    @34
           41  +        gzsetparams                              @35
           42  +        zError                                   @36
           43  +        inflateSyncPoint                         @37
           44  +        get_crc_table                            @38
           45  +        compress2                                @39
           46  +        gzputs                                   @40
           47  +        gzgets                                   @41
           48  +        inflateCopy                              @42
           49  +        inflateBackInit_                         @43
           50  +        inflateBack                              @44
           51  +        inflateBackEnd                           @45
           52  +        compressBound                            @46
           53  +        deflateBound                             @47
           54  +        gzclearerr                               @48
           55  +        gzungetc                                 @49
           56  +        zlibCompileFlags                         @50
           57  +        deflatePrime                             @51
           58  +        deflatePending                           @52
           59  +
           60  +        unzOpen                                  @61
           61  +        unzClose                                 @62
           62  +        unzGetGlobalInfo                         @63
           63  +        unzGetCurrentFileInfo                    @64
           64  +        unzGoToFirstFile                         @65
           65  +        unzGoToNextFile                          @66
           66  +        unzOpenCurrentFile                       @67
           67  +        unzReadCurrentFile                       @68
           68  +        unzOpenCurrentFile3                      @69
           69  +        unztell                                  @70
           70  +        unzeof                                   @71
           71  +        unzCloseCurrentFile                      @72
           72  +        unzGetGlobalComment                      @73
           73  +        unzStringFileNameCompare                 @74
           74  +        unzLocateFile                            @75
           75  +        unzGetLocalExtrafield                    @76
           76  +        unzOpen2                                 @77
           77  +        unzOpenCurrentFile2                      @78
           78  +        unzOpenCurrentFilePassword               @79
           79  +
           80  +        zipOpen                                  @80
           81  +        zipOpenNewFileInZip                      @81
           82  +        zipWriteInFileInZip                      @82
           83  +        zipCloseFileInZip                        @83
           84  +        zipClose                                 @84
           85  +        zipOpenNewFileInZip2                     @86
           86  +        zipCloseFileInZipRaw                     @87
           87  +        zipOpen2                                 @88
           88  +        zipOpenNewFileInZip3                     @89
           89  +
           90  +        unzGetFilePos                            @100
           91  +        unzGoToFilePos                           @101
           92  +
           93  +        fill_win32_filefunc                      @110
           94  +
           95  +; zlibwapi v1.2.4 added:
           96  +        fill_win32_filefunc64                   @111
           97  +        fill_win32_filefunc64A                  @112
           98  +        fill_win32_filefunc64W                  @113
           99  +
          100  +        unzOpen64                               @120
          101  +        unzOpen2_64                             @121
          102  +        unzGetGlobalInfo64                      @122
          103  +        unzGetCurrentFileInfo64                 @124
          104  +        unzGetCurrentFileZStreamPos64           @125
          105  +        unztell64                               @126
          106  +        unzGetFilePos64                         @127
          107  +        unzGoToFilePos64                        @128
          108  +
          109  +        zipOpen64                               @130
          110  +        zipOpen2_64                             @131
          111  +        zipOpenNewFileInZip64                   @132
          112  +        zipOpenNewFileInZip2_64                 @133
          113  +        zipOpenNewFileInZip3_64                 @134
          114  +        zipOpenNewFileInZip4_64                 @135
          115  +        zipCloseFileInZipRaw64                  @136
          116  +
          117  +; zlib1 v1.2.4 added:
          118  +        adler32_combine                         @140
          119  +        crc32_combine                           @142
          120  +        deflateSetHeader                        @144
          121  +        deflateTune                             @145
          122  +        gzbuffer                                @146
          123  +        gzclose_r                               @147
          124  +        gzclose_w                               @148
          125  +        gzdirect                                @149
          126  +        gzoffset                                @150
          127  +        inflateGetHeader                        @156
          128  +        inflateMark                             @157
          129  +        inflatePrime                            @158
          130  +        inflateReset2                           @159
          131  +        inflateUndermine                        @160
          132  +
          133  +; zlib1 v1.2.6 added:
          134  +        gzgetc_                                 @161
          135  +        inflateResetKeep                        @163
          136  +        deflateResetKeep                        @164
          137  +
          138  +; zlib1 v1.2.7 added:
          139  +        gzopen_w                                @165

Added compat/zlib/contrib/vstudio/vc9/zlibvc.sln.

            1  +
            2  +Microsoft Visual Studio Solution File, Format Version 10.00
            3  +# Visual Studio 2008
            4  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}"
            5  +EndProject
            6  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}"
            7  +EndProject
            8  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}"
            9  +EndProject
           10  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestZlibDll", "testzlibdll.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}"
           11  +	ProjectSection(ProjectDependencies) = postProject
           12  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D}
           13  +	EndProjectSection
           14  +EndProject
           15  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}"
           16  +	ProjectSection(ProjectDependencies) = postProject
           17  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D}
           18  +	EndProjectSection
           19  +EndProject
           20  +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}"
           21  +	ProjectSection(ProjectDependencies) = postProject
           22  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D}
           23  +	EndProjectSection
           24  +EndProject
           25  +Global
           26  +	GlobalSection(SolutionConfigurationPlatforms) = preSolution
           27  +		Debug|Itanium = Debug|Itanium
           28  +		Debug|Win32 = Debug|Win32
           29  +		Debug|x64 = Debug|x64
           30  +		Release|Itanium = Release|Itanium
           31  +		Release|Win32 = Release|Win32
           32  +		Release|x64 = Release|x64
           33  +		ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium
           34  +		ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32
           35  +		ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64
           36  +	EndGlobalSection
           37  +	GlobalSection(ProjectConfigurationPlatforms) = postSolution
           38  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium
           39  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium
           40  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32
           41  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32
           42  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64
           43  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64
           44  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium
           45  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium
           46  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32
           47  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32
           48  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64
           49  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64
           50  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           51  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           52  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           53  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           54  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           55  +		{8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           56  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium
           57  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium
           58  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32
           59  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32
           60  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64
           61  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64
           62  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium
           63  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium
           64  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32
           65  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32
           66  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64
           67  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64
           68  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           69  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           70  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           71  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           72  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           73  +		{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           74  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium
           75  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium
           76  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32
           77  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32
           78  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64
           79  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64
           80  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium
           81  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium
           82  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32
           83  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32
           84  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64
           85  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64
           86  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium
           87  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium
           88  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32
           89  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32
           90  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64
           91  +		{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64
           92  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium
           93  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium
           94  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32
           95  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32
           96  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64
           97  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64
           98  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium
           99  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium
          100  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32
          101  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32
          102  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64
          103  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64
          104  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
          105  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
          106  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
          107  +		{C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
          108  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium
          109  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium
          110  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32
          111  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32
          112  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64
          113  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64
          114  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium
          115  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium
          116  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32
          117  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32
          118  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64
          119  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64
          120  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
          121  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
          122  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
          123  +		{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
          124  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium
          125  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium
          126  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32
          127  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32
          128  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64
          129  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64
          130  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium
          131  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium
          132  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32
          133  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32
          134  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64
          135  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64
          136  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium
          137  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium
          138  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32
          139  +		{C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64
          140  +	EndGlobalSection
          141  +	GlobalSection(SolutionProperties) = preSolution
          142  +		HideSolutionNode = FALSE
          143  +	EndGlobalSection
          144  +EndGlobal

Added compat/zlib/contrib/vstudio/vc9/zlibvc.vcproj.

            1  +<?xml version="1.0" encoding="Windows-1252"?>
            2  +<VisualStudioProject
            3  +	ProjectType="Visual C++"
            4  +	Version="9,00"
            5  +	Name="zlibvc"
            6  +	ProjectGUID="{8FD826F8-3739-44E6-8CC8-997122E53B8D}"
            7  +	RootNamespace="zlibvc"
            8  +	TargetFrameworkVersion="131072"
            9  +	>
           10  +	<Platforms>
           11  +		<Platform
           12  +			Name="Win32"
           13  +		/>
           14  +		<Platform
           15  +			Name="x64"
           16  +		/>
           17  +		<Platform
           18  +			Name="Itanium"
           19  +		/>
           20  +	</Platforms>
           21  +	<ToolFiles>
           22  +	</ToolFiles>
           23  +	<Configurations>
           24  +		<Configuration
           25  +			Name="Debug|Win32"
           26  +			OutputDirectory="x86\ZlibDll$(ConfigurationName)"
           27  +			IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp"
           28  +			ConfigurationType="2"
           29  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
           30  +			UseOfMFC="0"
           31  +			ATLMinimizesCRunTimeLibraryUsage="false"
           32  +			>
           33  +			<Tool
           34  +				Name="VCPreBuildEventTool"
           35  +			/>
           36  +			<Tool
           37  +				Name="VCCustomBuildTool"
           38  +			/>
           39  +			<Tool
           40  +				Name="VCXMLDataGeneratorTool"
           41  +			/>
           42  +			<Tool
           43  +				Name="VCWebServiceProxyGeneratorTool"
           44  +			/>
           45  +			<Tool
           46  +				Name="VCMIDLTool"
           47  +				PreprocessorDefinitions="_DEBUG"
           48  +				MkTypLibCompatible="true"
           49  +				SuppressStartupBanner="true"
           50  +				TargetEnvironment="1"
           51  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
           52  +			/>
           53  +			<Tool
           54  +				Name="VCCLCompilerTool"
           55  +				Optimization="0"
           56  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
           57  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF"
           58  +				ExceptionHandling="0"
           59  +				RuntimeLibrary="1"
           60  +				BufferSecurityCheck="false"
           61  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
           62  +				AssemblerListingLocation="$(IntDir)\"
           63  +				ObjectFile="$(IntDir)\"
           64  +				ProgramDataBaseFileName="$(OutDir)\"
           65  +				BrowseInformation="0"
           66  +				WarningLevel="3"
           67  +				SuppressStartupBanner="true"
           68  +				DebugInformationFormat="4"
           69  +			/>
           70  +			<Tool
           71  +				Name="VCManagedResourceCompilerTool"
           72  +			/>
           73  +			<Tool
           74  +				Name="VCResourceCompilerTool"
           75  +				PreprocessorDefinitions="_DEBUG"
           76  +				Culture="1036"
           77  +			/>
           78  +			<Tool
           79  +				Name="VCPreLinkEventTool"
           80  +			/>
           81  +			<Tool
           82  +				Name="VCLinkerTool"
           83  +				AdditionalOptions="/MACHINE:I386"
           84  +				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
           85  +				OutputFile="$(OutDir)\zlibwapi.dll"
           86  +				LinkIncremental="2"
           87  +				SuppressStartupBanner="true"
           88  +				GenerateManifest="false"
           89  +				ModuleDefinitionFile=".\zlibvc.def"
           90  +				GenerateDebugInformation="true"
           91  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
           92  +				GenerateMapFile="true"
           93  +				MapFileName="$(OutDir)/zlibwapi.map"
           94  +				SubSystem="2"
           95  +				RandomizedBaseAddress="1"
           96  +				DataExecutionPrevention="0"
           97  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
           98  +			/>
           99  +			<Tool
          100  +				Name="VCALinkTool"
          101  +			/>
          102  +			<Tool
          103  +				Name="VCManifestTool"
          104  +			/>
          105  +			<Tool
          106  +				Name="VCXDCMakeTool"
          107  +			/>
          108  +			<Tool
          109  +				Name="VCBscMakeTool"
          110  +			/>
          111  +			<Tool
          112  +				Name="VCFxCopTool"
          113  +			/>
          114  +			<Tool
          115  +				Name="VCAppVerifierTool"
          116  +			/>
          117  +			<Tool
          118  +				Name="VCPostBuildEventTool"
          119  +			/>
          120  +		</Configuration>
          121  +		<Configuration
          122  +			Name="Debug|x64"
          123  +			OutputDirectory="x64\ZlibDll$(ConfigurationName)"
          124  +			IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp"
          125  +			ConfigurationType="2"
          126  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          127  +			UseOfMFC="0"
          128  +			ATLMinimizesCRunTimeLibraryUsage="false"
          129  +			>
          130  +			<Tool
          131  +				Name="VCPreBuildEventTool"
          132  +			/>
          133  +			<Tool
          134  +				Name="VCCustomBuildTool"
          135  +			/>
          136  +			<Tool
          137  +				Name="VCXMLDataGeneratorTool"
          138  +			/>
          139  +			<Tool
          140  +				Name="VCWebServiceProxyGeneratorTool"
          141  +			/>
          142  +			<Tool
          143  +				Name="VCMIDLTool"
          144  +				PreprocessorDefinitions="_DEBUG"
          145  +				MkTypLibCompatible="true"
          146  +				SuppressStartupBanner="true"
          147  +				TargetEnvironment="3"
          148  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          149  +			/>
          150  +			<Tool
          151  +				Name="VCCLCompilerTool"
          152  +				Optimization="0"
          153  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          154  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64"
          155  +				ExceptionHandling="0"
          156  +				RuntimeLibrary="3"
          157  +				BufferSecurityCheck="false"
          158  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          159  +				AssemblerListingLocation="$(IntDir)\"
          160  +				ObjectFile="$(IntDir)\"
          161  +				ProgramDataBaseFileName="$(OutDir)\"
          162  +				BrowseInformation="0"
          163  +				WarningLevel="3"
          164  +				SuppressStartupBanner="true"
          165  +				DebugInformationFormat="3"
          166  +			/>
          167  +			<Tool
          168  +				Name="VCManagedResourceCompilerTool"
          169  +			/>
          170  +			<Tool
          171  +				Name="VCResourceCompilerTool"
          172  +				PreprocessorDefinitions="_DEBUG"
          173  +				Culture="1036"
          174  +			/>
          175  +			<Tool
          176  +				Name="VCPreLinkEventTool"
          177  +			/>
          178  +			<Tool
          179  +				Name="VCLinkerTool"
          180  +				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
          181  +				OutputFile="$(OutDir)\zlibwapi.dll"
          182  +				LinkIncremental="2"
          183  +				SuppressStartupBanner="true"
          184  +				GenerateManifest="false"
          185  +				ModuleDefinitionFile=".\zlibvc.def"
          186  +				GenerateDebugInformation="true"
          187  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          188  +				GenerateMapFile="true"
          189  +				MapFileName="$(OutDir)/zlibwapi.map"
          190  +				SubSystem="2"
          191  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          192  +				TargetMachine="17"
          193  +			/>
          194  +			<Tool
          195  +				Name="VCALinkTool"
          196  +			/>
          197  +			<Tool
          198  +				Name="VCManifestTool"
          199  +			/>
          200  +			<Tool
          201  +				Name="VCXDCMakeTool"
          202  +			/>
          203  +			<Tool
          204  +				Name="VCBscMakeTool"
          205  +			/>
          206  +			<Tool
          207  +				Name="VCFxCopTool"
          208  +			/>
          209  +			<Tool
          210  +				Name="VCAppVerifierTool"
          211  +			/>
          212  +			<Tool
          213  +				Name="VCPostBuildEventTool"
          214  +			/>
          215  +		</Configuration>
          216  +		<Configuration
          217  +			Name="Debug|Itanium"
          218  +			OutputDirectory="ia64\ZlibDll$(ConfigurationName)"
          219  +			IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp"
          220  +			ConfigurationType="2"
          221  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          222  +			UseOfMFC="0"
          223  +			ATLMinimizesCRunTimeLibraryUsage="false"
          224  +			>
          225  +			<Tool
          226  +				Name="VCPreBuildEventTool"
          227  +			/>
          228  +			<Tool
          229  +				Name="VCCustomBuildTool"
          230  +			/>
          231  +			<Tool
          232  +				Name="VCXMLDataGeneratorTool"
          233  +			/>
          234  +			<Tool
          235  +				Name="VCWebServiceProxyGeneratorTool"
          236  +			/>
          237  +			<Tool
          238  +				Name="VCMIDLTool"
          239  +				PreprocessorDefinitions="_DEBUG"
          240  +				MkTypLibCompatible="true"
          241  +				SuppressStartupBanner="true"
          242  +				TargetEnvironment="2"
          243  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          244  +			/>
          245  +			<Tool
          246  +				Name="VCCLCompilerTool"
          247  +				Optimization="0"
          248  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          249  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64"
          250  +				ExceptionHandling="0"
          251  +				RuntimeLibrary="3"
          252  +				BufferSecurityCheck="false"
          253  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          254  +				AssemblerListingLocation="$(IntDir)\"
          255  +				ObjectFile="$(IntDir)\"
          256  +				ProgramDataBaseFileName="$(OutDir)\"
          257  +				BrowseInformation="0"
          258  +				WarningLevel="3"
          259  +				SuppressStartupBanner="true"
          260  +				DebugInformationFormat="3"
          261  +			/>
          262  +			<Tool
          263  +				Name="VCManagedResourceCompilerTool"
          264  +			/>
          265  +			<Tool
          266  +				Name="VCResourceCompilerTool"
          267  +				PreprocessorDefinitions="_DEBUG"
          268  +				Culture="1036"
          269  +			/>
          270  +			<Tool
          271  +				Name="VCPreLinkEventTool"
          272  +			/>
          273  +			<Tool
          274  +				Name="VCLinkerTool"
          275  +				OutputFile="$(OutDir)\zlibwapi.dll"
          276  +				LinkIncremental="2"
          277  +				SuppressStartupBanner="true"
          278  +				GenerateManifest="false"
          279  +				ModuleDefinitionFile=".\zlibvc.def"
          280  +				GenerateDebugInformation="true"
          281  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          282  +				GenerateMapFile="true"
          283  +				MapFileName="$(OutDir)/zlibwapi.map"
          284  +				SubSystem="2"
          285  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          286  +				TargetMachine="5"
          287  +			/>
          288  +			<Tool
          289  +				Name="VCALinkTool"
          290  +			/>
          291  +			<Tool
          292  +				Name="VCManifestTool"
          293  +			/>
          294  +			<Tool
          295  +				Name="VCXDCMakeTool"
          296  +			/>
          297  +			<Tool
          298  +				Name="VCBscMakeTool"
          299  +			/>
          300  +			<Tool
          301  +				Name="VCFxCopTool"
          302  +			/>
          303  +			<Tool
          304  +				Name="VCAppVerifierTool"
          305  +			/>
          306  +			<Tool
          307  +				Name="VCPostBuildEventTool"
          308  +			/>
          309  +		</Configuration>
          310  +		<Configuration
          311  +			Name="ReleaseWithoutAsm|Win32"
          312  +			OutputDirectory="x86\ZlibDll$(ConfigurationName)"
          313  +			IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp"
          314  +			ConfigurationType="2"
          315  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          316  +			UseOfMFC="0"
          317  +			ATLMinimizesCRunTimeLibraryUsage="false"
          318  +			WholeProgramOptimization="1"
          319  +			>
          320  +			<Tool
          321  +				Name="VCPreBuildEventTool"
          322  +			/>
          323  +			<Tool
          324  +				Name="VCCustomBuildTool"
          325  +			/>
          326  +			<Tool
          327  +				Name="VCXMLDataGeneratorTool"
          328  +			/>
          329  +			<Tool
          330  +				Name="VCWebServiceProxyGeneratorTool"
          331  +			/>
          332  +			<Tool
          333  +				Name="VCMIDLTool"
          334  +				PreprocessorDefinitions="NDEBUG"
          335  +				MkTypLibCompatible="true"
          336  +				SuppressStartupBanner="true"
          337  +				TargetEnvironment="1"
          338  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          339  +			/>
          340  +			<Tool
          341  +				Name="VCCLCompilerTool"
          342  +				InlineFunctionExpansion="1"
          343  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          344  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI"
          345  +				StringPooling="true"
          346  +				ExceptionHandling="0"
          347  +				RuntimeLibrary="2"
          348  +				BufferSecurityCheck="false"
          349  +				EnableFunctionLevelLinking="true"
          350  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          351  +				AssemblerOutput="2"
          352  +				AssemblerListingLocation="$(IntDir)\"
          353  +				ObjectFile="$(IntDir)\"
          354  +				ProgramDataBaseFileName="$(OutDir)\"
          355  +				BrowseInformation="0"
          356  +				WarningLevel="3"
          357  +				SuppressStartupBanner="true"
          358  +			/>
          359  +			<Tool
          360  +				Name="VCManagedResourceCompilerTool"
          361  +			/>
          362  +			<Tool
          363  +				Name="VCResourceCompilerTool"
          364  +				PreprocessorDefinitions="NDEBUG"
          365  +				Culture="1036"
          366  +			/>
          367  +			<Tool
          368  +				Name="VCPreLinkEventTool"
          369  +			/>
          370  +			<Tool
          371  +				Name="VCLinkerTool"
          372  +				AdditionalOptions="/MACHINE:I386"
          373  +				OutputFile="$(OutDir)\zlibwapi.dll"
          374  +				LinkIncremental="1"
          375  +				SuppressStartupBanner="true"
          376  +				GenerateManifest="false"
          377  +				IgnoreAllDefaultLibraries="false"
          378  +				ModuleDefinitionFile=".\zlibvc.def"
          379  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          380  +				GenerateMapFile="true"
          381  +				MapFileName="$(OutDir)/zlibwapi.map"
          382  +				SubSystem="2"
          383  +				OptimizeForWindows98="1"
          384  +				RandomizedBaseAddress="1"
          385  +				DataExecutionPrevention="0"
          386  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          387  +			/>
          388  +			<Tool
          389  +				Name="VCALinkTool"
          390  +			/>
          391  +			<Tool
          392  +				Name="VCManifestTool"
          393  +			/>
          394  +			<Tool
          395  +				Name="VCXDCMakeTool"
          396  +			/>
          397  +			<Tool
          398  +				Name="VCBscMakeTool"
          399  +			/>
          400  +			<Tool
          401  +				Name="VCFxCopTool"
          402  +			/>
          403  +			<Tool
          404  +				Name="VCAppVerifierTool"
          405  +			/>
          406  +			<Tool
          407  +				Name="VCPostBuildEventTool"
          408  +			/>
          409  +		</Configuration>
          410  +		<Configuration
          411  +			Name="ReleaseWithoutAsm|x64"
          412  +			OutputDirectory="x64\ZlibDll$(ConfigurationName)"
          413  +			IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp"
          414  +			ConfigurationType="2"
          415  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          416  +			UseOfMFC="0"
          417  +			ATLMinimizesCRunTimeLibraryUsage="false"
          418  +			WholeProgramOptimization="1"
          419  +			>
          420  +			<Tool
          421  +				Name="VCPreBuildEventTool"
          422  +			/>
          423  +			<Tool
          424  +				Name="VCCustomBuildTool"
          425  +			/>
          426  +			<Tool
          427  +				Name="VCXMLDataGeneratorTool"
          428  +			/>
          429  +			<Tool
          430  +				Name="VCWebServiceProxyGeneratorTool"
          431  +			/>
          432  +			<Tool
          433  +				Name="VCMIDLTool"
          434  +				PreprocessorDefinitions="NDEBUG"
          435  +				MkTypLibCompatible="true"
          436  +				SuppressStartupBanner="true"
          437  +				TargetEnvironment="3"
          438  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          439  +			/>
          440  +			<Tool
          441  +				Name="VCCLCompilerTool"
          442  +				InlineFunctionExpansion="1"
          443  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          444  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64"
          445  +				StringPooling="true"
          446  +				ExceptionHandling="0"
          447  +				RuntimeLibrary="2"
          448  +				BufferSecurityCheck="false"
          449  +				EnableFunctionLevelLinking="true"
          450  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          451  +				AssemblerOutput="2"
          452  +				AssemblerListingLocation="$(IntDir)\"
          453  +				ObjectFile="$(IntDir)\"
          454  +				ProgramDataBaseFileName="$(OutDir)\"
          455  +				BrowseInformation="0"
          456  +				WarningLevel="3"
          457  +				SuppressStartupBanner="true"
          458  +			/>
          459  +			<Tool
          460  +				Name="VCManagedResourceCompilerTool"
          461  +			/>
          462  +			<Tool
          463  +				Name="VCResourceCompilerTool"
          464  +				PreprocessorDefinitions="NDEBUG"
          465  +				Culture="1036"
          466  +			/>
          467  +			<Tool
          468  +				Name="VCPreLinkEventTool"
          469  +			/>
          470  +			<Tool
          471  +				Name="VCLinkerTool"
          472  +				OutputFile="$(OutDir)\zlibwapi.dll"
          473  +				LinkIncremental="1"
          474  +				SuppressStartupBanner="true"
          475  +				GenerateManifest="false"
          476  +				IgnoreAllDefaultLibraries="false"
          477  +				ModuleDefinitionFile=".\zlibvc.def"
          478  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          479  +				GenerateMapFile="true"
          480  +				MapFileName="$(OutDir)/zlibwapi.map"
          481  +				SubSystem="2"
          482  +				OptimizeForWindows98="1"
          483  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          484  +				TargetMachine="17"
          485  +			/>
          486  +			<Tool
          487  +				Name="VCALinkTool"
          488  +			/>
          489  +			<Tool
          490  +				Name="VCManifestTool"
          491  +			/>
          492  +			<Tool
          493  +				Name="VCXDCMakeTool"
          494  +			/>
          495  +			<Tool
          496  +				Name="VCBscMakeTool"
          497  +			/>
          498  +			<Tool
          499  +				Name="VCFxCopTool"
          500  +			/>
          501  +			<Tool
          502  +				Name="VCAppVerifierTool"
          503  +			/>
          504  +			<Tool
          505  +				Name="VCPostBuildEventTool"
          506  +			/>
          507  +		</Configuration>
          508  +		<Configuration
          509  +			Name="ReleaseWithoutAsm|Itanium"
          510  +			OutputDirectory="ia64\ZlibDll$(ConfigurationName)"
          511  +			IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp"
          512  +			ConfigurationType="2"
          513  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          514  +			UseOfMFC="0"
          515  +			ATLMinimizesCRunTimeLibraryUsage="false"
          516  +			WholeProgramOptimization="1"
          517  +			>
          518  +			<Tool
          519  +				Name="VCPreBuildEventTool"
          520  +			/>
          521  +			<Tool
          522  +				Name="VCCustomBuildTool"
          523  +			/>
          524  +			<Tool
          525  +				Name="VCXMLDataGeneratorTool"
          526  +			/>
          527  +			<Tool
          528  +				Name="VCWebServiceProxyGeneratorTool"
          529  +			/>
          530  +			<Tool
          531  +				Name="VCMIDLTool"
          532  +				PreprocessorDefinitions="NDEBUG"
          533  +				MkTypLibCompatible="true"
          534  +				SuppressStartupBanner="true"
          535  +				TargetEnvironment="2"
          536  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          537  +			/>
          538  +			<Tool
          539  +				Name="VCCLCompilerTool"
          540  +				InlineFunctionExpansion="1"
          541  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          542  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64"
          543  +				StringPooling="true"
          544  +				ExceptionHandling="0"
          545  +				RuntimeLibrary="2"
          546  +				BufferSecurityCheck="false"
          547  +				EnableFunctionLevelLinking="true"
          548  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          549  +				AssemblerOutput="2"
          550  +				AssemblerListingLocation="$(IntDir)\"
          551  +				ObjectFile="$(IntDir)\"
          552  +				ProgramDataBaseFileName="$(OutDir)\"
          553  +				BrowseInformation="0"
          554  +				WarningLevel="3"
          555  +				SuppressStartupBanner="true"
          556  +			/>
          557  +			<Tool
          558  +				Name="VCManagedResourceCompilerTool"
          559  +			/>
          560  +			<Tool
          561  +				Name="VCResourceCompilerTool"
          562  +				PreprocessorDefinitions="NDEBUG"
          563  +				Culture="1036"
          564  +			/>
          565  +			<Tool
          566  +				Name="VCPreLinkEventTool"
          567  +			/>
          568  +			<Tool
          569  +				Name="VCLinkerTool"
          570  +				OutputFile="$(OutDir)\zlibwapi.dll"
          571  +				LinkIncremental="1"
          572  +				SuppressStartupBanner="true"
          573  +				GenerateManifest="false"
          574  +				IgnoreAllDefaultLibraries="false"
          575  +				ModuleDefinitionFile=".\zlibvc.def"
          576  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          577  +				GenerateMapFile="true"
          578  +				MapFileName="$(OutDir)/zlibwapi.map"
          579  +				SubSystem="2"
          580  +				OptimizeForWindows98="1"
          581  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          582  +				TargetMachine="5"
          583  +			/>
          584  +			<Tool
          585  +				Name="VCALinkTool"
          586  +			/>
          587  +			<Tool
          588  +				Name="VCManifestTool"
          589  +			/>
          590  +			<Tool
          591  +				Name="VCXDCMakeTool"
          592  +			/>
          593  +			<Tool
          594  +				Name="VCBscMakeTool"
          595  +			/>
          596  +			<Tool
          597  +				Name="VCFxCopTool"
          598  +			/>
          599  +			<Tool
          600  +				Name="VCAppVerifierTool"
          601  +			/>
          602  +			<Tool
          603  +				Name="VCPostBuildEventTool"
          604  +			/>
          605  +		</Configuration>
          606  +		<Configuration
          607  +			Name="Release|Win32"
          608  +			OutputDirectory="x86\ZlibDll$(ConfigurationName)"
          609  +			IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp"
          610  +			ConfigurationType="2"
          611  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          612  +			UseOfMFC="0"
          613  +			ATLMinimizesCRunTimeLibraryUsage="false"
          614  +			WholeProgramOptimization="1"
          615  +			>
          616  +			<Tool
          617  +				Name="VCPreBuildEventTool"
          618  +			/>
          619  +			<Tool
          620  +				Name="VCCustomBuildTool"
          621  +			/>
          622  +			<Tool
          623  +				Name="VCXMLDataGeneratorTool"
          624  +			/>
          625  +			<Tool
          626  +				Name="VCWebServiceProxyGeneratorTool"
          627  +			/>
          628  +			<Tool
          629  +				Name="VCMIDLTool"
          630  +				PreprocessorDefinitions="NDEBUG"
          631  +				MkTypLibCompatible="true"
          632  +				SuppressStartupBanner="true"
          633  +				TargetEnvironment="1"
          634  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          635  +			/>
          636  +			<Tool
          637  +				Name="VCCLCompilerTool"
          638  +				InlineFunctionExpansion="1"
          639  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          640  +				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF"
          641  +				StringPooling="true"
          642  +				ExceptionHandling="0"
          643  +				RuntimeLibrary="0"
          644  +				BufferSecurityCheck="false"
          645  +				EnableFunctionLevelLinking="true"
          646  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          647  +				AssemblerOutput="2"
          648  +				AssemblerListingLocation="$(IntDir)\"
          649  +				ObjectFile="$(IntDir)\"
          650  +				ProgramDataBaseFileName="$(OutDir)\"
          651  +				BrowseInformation="0"
          652  +				WarningLevel="3"
          653  +				SuppressStartupBanner="true"
          654  +			/>
          655  +			<Tool
          656  +				Name="VCManagedResourceCompilerTool"
          657  +			/>
          658  +			<Tool
          659  +				Name="VCResourceCompilerTool"
          660  +				PreprocessorDefinitions="NDEBUG"
          661  +				Culture="1036"
          662  +			/>
          663  +			<Tool
          664  +				Name="VCPreLinkEventTool"
          665  +			/>
          666  +			<Tool
          667  +				Name="VCLinkerTool"
          668  +				AdditionalOptions="/MACHINE:I386"
          669  +				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj "
          670  +				OutputFile="$(OutDir)\zlibwapi.dll"
          671  +				LinkIncremental="1"
          672  +				SuppressStartupBanner="true"
          673  +				GenerateManifest="false"
          674  +				IgnoreAllDefaultLibraries="false"
          675  +				ModuleDefinitionFile=".\zlibvc.def"
          676  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          677  +				GenerateMapFile="true"
          678  +				MapFileName="$(OutDir)/zlibwapi.map"
          679  +				SubSystem="2"
          680  +				OptimizeForWindows98="1"
          681  +				RandomizedBaseAddress="1"
          682  +				DataExecutionPrevention="0"
          683  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          684  +			/>
          685  +			<Tool
          686  +				Name="VCALinkTool"
          687  +			/>
          688  +			<Tool
          689  +				Name="VCManifestTool"
          690  +			/>
          691  +			<Tool
          692  +				Name="VCXDCMakeTool"
          693  +			/>
          694  +			<Tool
          695  +				Name="VCBscMakeTool"
          696  +			/>
          697  +			<Tool
          698  +				Name="VCFxCopTool"
          699  +			/>
          700  +			<Tool
          701  +				Name="VCAppVerifierTool"
          702  +			/>
          703  +			<Tool
          704  +				Name="VCPostBuildEventTool"
          705  +			/>
          706  +		</Configuration>
          707  +		<Configuration
          708  +			Name="Release|x64"
          709  +			OutputDirectory="x64\ZlibDll$(ConfigurationName)"
          710  +			IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp"
          711  +			ConfigurationType="2"
          712  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          713  +			UseOfMFC="0"
          714  +			ATLMinimizesCRunTimeLibraryUsage="false"
          715  +			WholeProgramOptimization="1"
          716  +			>
          717  +			<Tool
          718  +				Name="VCPreBuildEventTool"
          719  +			/>
          720  +			<Tool
          721  +				Name="VCCustomBuildTool"
          722  +			/>
          723  +			<Tool
          724  +				Name="VCXMLDataGeneratorTool"
          725  +			/>
          726  +			<Tool
          727  +				Name="VCWebServiceProxyGeneratorTool"
          728  +			/>
          729  +			<Tool
          730  +				Name="VCMIDLTool"
          731  +				PreprocessorDefinitions="NDEBUG"
          732  +				MkTypLibCompatible="true"
          733  +				SuppressStartupBanner="true"
          734  +				TargetEnvironment="3"
          735  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          736  +			/>
          737  +			<Tool
          738  +				Name="VCCLCompilerTool"
          739  +				InlineFunctionExpansion="1"
          740  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          741  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64"
          742  +				StringPooling="true"
          743  +				ExceptionHandling="0"
          744  +				RuntimeLibrary="2"
          745  +				BufferSecurityCheck="false"
          746  +				EnableFunctionLevelLinking="true"
          747  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          748  +				AssemblerOutput="2"
          749  +				AssemblerListingLocation="$(IntDir)\"
          750  +				ObjectFile="$(IntDir)\"
          751  +				ProgramDataBaseFileName="$(OutDir)\"
          752  +				BrowseInformation="0"
          753  +				WarningLevel="3"
          754  +				SuppressStartupBanner="true"
          755  +			/>
          756  +			<Tool
          757  +				Name="VCManagedResourceCompilerTool"
          758  +			/>
          759  +			<Tool
          760  +				Name="VCResourceCompilerTool"
          761  +				PreprocessorDefinitions="NDEBUG"
          762  +				Culture="1036"
          763  +			/>
          764  +			<Tool
          765  +				Name="VCPreLinkEventTool"
          766  +			/>
          767  +			<Tool
          768  +				Name="VCLinkerTool"
          769  +				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
          770  +				OutputFile="$(OutDir)\zlibwapi.dll"
          771  +				LinkIncremental="1"
          772  +				SuppressStartupBanner="true"
          773  +				GenerateManifest="false"
          774  +				IgnoreAllDefaultLibraries="false"
          775  +				ModuleDefinitionFile=".\zlibvc.def"
          776  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          777  +				GenerateMapFile="true"
          778  +				MapFileName="$(OutDir)/zlibwapi.map"
          779  +				SubSystem="2"
          780  +				OptimizeForWindows98="1"
          781  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          782  +				TargetMachine="17"
          783  +			/>
          784  +			<Tool
          785  +				Name="VCALinkTool"
          786  +			/>
          787  +			<Tool
          788  +				Name="VCManifestTool"
          789  +			/>
          790  +			<Tool
          791  +				Name="VCXDCMakeTool"
          792  +			/>
          793  +			<Tool
          794  +				Name="VCBscMakeTool"
          795  +			/>
          796  +			<Tool
          797  +				Name="VCFxCopTool"
          798  +			/>
          799  +			<Tool
          800  +				Name="VCAppVerifierTool"
          801  +			/>
          802  +			<Tool
          803  +				Name="VCPostBuildEventTool"
          804  +			/>
          805  +		</Configuration>
          806  +		<Configuration
          807  +			Name="Release|Itanium"
          808  +			OutputDirectory="ia64\ZlibDll$(ConfigurationName)"
          809  +			IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp"
          810  +			ConfigurationType="2"
          811  +			InheritedPropertySheets="UpgradeFromVC70.vsprops"
          812  +			UseOfMFC="0"
          813  +			ATLMinimizesCRunTimeLibraryUsage="false"
          814  +			WholeProgramOptimization="1"
          815  +			>
          816  +			<Tool
          817  +				Name="VCPreBuildEventTool"
          818  +			/>
          819  +			<Tool
          820  +				Name="VCCustomBuildTool"
          821  +			/>
          822  +			<Tool
          823  +				Name="VCXMLDataGeneratorTool"
          824  +			/>
          825  +			<Tool
          826  +				Name="VCWebServiceProxyGeneratorTool"
          827  +			/>
          828  +			<Tool
          829  +				Name="VCMIDLTool"
          830  +				PreprocessorDefinitions="NDEBUG"
          831  +				MkTypLibCompatible="true"
          832  +				SuppressStartupBanner="true"
          833  +				TargetEnvironment="2"
          834  +				TypeLibraryName="$(OutDir)/zlibvc.tlb"
          835  +			/>
          836  +			<Tool
          837  +				Name="VCCLCompilerTool"
          838  +				InlineFunctionExpansion="1"
          839  +				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
          840  +				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64"
          841  +				StringPooling="true"
          842  +				ExceptionHandling="0"
          843  +				RuntimeLibrary="2"
          844  +				BufferSecurityCheck="false"
          845  +				EnableFunctionLevelLinking="true"
          846  +				PrecompiledHeaderFile="$(IntDir)/zlibvc.pch"
          847  +				AssemblerOutput="2"
          848  +				AssemblerListingLocation="$(IntDir)\"
          849  +				ObjectFile="$(IntDir)\"
          850  +				ProgramDataBaseFileName="$(OutDir)\"
          851  +				BrowseInformation="0"
          852  +				WarningLevel="3"
          853  +				SuppressStartupBanner="true"
          854  +			/>
          855  +			<Tool
          856  +				Name="VCManagedResourceCompilerTool"
          857  +			/>
          858  +			<Tool
          859  +				Name="VCResourceCompilerTool"
          860  +				PreprocessorDefinitions="NDEBUG"
          861  +				Culture="1036"
          862  +			/>
          863  +			<Tool
          864  +				Name="VCPreLinkEventTool"
          865  +			/>
          866  +			<Tool
          867  +				Name="VCLinkerTool"
          868  +				OutputFile="$(OutDir)\zlibwapi.dll"
          869  +				LinkIncremental="1"
          870  +				SuppressStartupBanner="true"
          871  +				GenerateManifest="false"
          872  +				IgnoreAllDefaultLibraries="false"
          873  +				ModuleDefinitionFile=".\zlibvc.def"
          874  +				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
          875  +				GenerateMapFile="true"
          876  +				MapFileName="$(OutDir)/zlibwapi.map"
          877  +				SubSystem="2"
          878  +				OptimizeForWindows98="1"
          879  +				ImportLibrary="$(OutDir)/zlibwapi.lib"
          880  +				TargetMachine="5"
          881  +			/>
          882  +			<Tool
          883  +				Name="VCALinkTool"
          884  +			/>
          885  +			<Tool
          886  +				Name="VCManifestTool"
          887  +			/>
          888  +			<Tool
          889  +				Name="VCXDCMakeTool"
          890  +			/>
          891  +			<Tool
          892  +				Name="VCBscMakeTool"
          893  +			/>
          894  +			<Tool
          895  +				Name="VCFxCopTool"
          896  +			/>
          897  +			<Tool
          898  +				Name="VCAppVerifierTool"
          899  +			/>
          900  +			<Tool
          901  +				Name="VCPostBuildEventTool"
          902  +			/>
          903  +		</Configuration>
          904  +	</Configurations>
          905  +	<References>
          906  +	</References>
          907  +	<Files>
          908  +		<Filter
          909  +			Name="Source Files"
          910  +			Filter="cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
          911  +			>
          912  +			<File
          913  +				RelativePath="..\..\..\adler32.c"
          914  +				>
          915  +			</File>
          916  +			<File
          917  +				RelativePath="..\..\..\compress.c"
          918  +				>
          919  +			</File>
          920  +			<File
          921  +				RelativePath="..\..\..\crc32.c"
          922  +				>
          923  +			</File>
          924  +			<File
          925  +				RelativePath="..\..\..\deflate.c"
          926  +				>
          927  +			</File>
          928  +			<File
          929  +				RelativePath="..\..\..\gzclose.c"
          930  +				>
          931  +			</File>
          932  +			<File
          933  +				RelativePath="..\..\..\gzguts.h"
          934  +				>
          935  +			</File>
          936  +			<File
          937  +				RelativePath="..\..\..\gzlib.c"
          938  +				>
          939  +			</File>
          940  +			<File
          941  +				RelativePath="..\..\..\gzread.c"
          942  +				>
          943  +			</File>
          944  +			<File
          945  +				RelativePath="..\..\..\gzwrite.c"
          946  +				>
          947  +			</File>
          948  +			<File
          949  +				RelativePath="..\..\..\infback.c"
          950  +				>
          951  +			</File>
          952  +			<File
          953  +				RelativePath="..\..\masmx64\inffas8664.c"
          954  +				>
          955  +				<FileConfiguration
          956  +					Name="Debug|Win32"
          957  +					ExcludedFromBuild="true"
          958  +					>
          959  +					<Tool
          960  +						Name="VCCLCompilerTool"
          961  +					/>
          962  +				</FileConfiguration>
          963  +				<FileConfiguration
          964  +					Name="Debug|Itanium"
          965  +					ExcludedFromBuild="true"
          966  +					>
          967  +					<Tool
          968  +						Name="VCCLCompilerTool"
          969  +					/>
          970  +				</FileConfiguration>
          971  +				<FileConfiguration
          972  +					Name="ReleaseWithoutAsm|Win32"
          973  +					ExcludedFromBuild="true"
          974  +					>
          975  +					<Tool
          976  +						Name="VCCLCompilerTool"
          977  +					/>
          978  +				</FileConfiguration>
          979  +				<FileConfiguration
          980  +					Name="ReleaseWithoutAsm|Itanium"
          981  +					ExcludedFromBuild="true"
          982  +					>
          983  +					<Tool
          984  +						Name="VCCLCompilerTool"
          985  +					/>
          986  +				</FileConfiguration>
          987  +				<FileConfiguration
          988  +					Name="Release|Win32"
          989  +					ExcludedFromBuild="true"
          990  +					>
          991  +					<Tool
          992  +						Name="VCCLCompilerTool"
          993  +					/>
          994  +				</FileConfiguration>
          995  +				<FileConfiguration
          996  +					Name="Release|Itanium"
          997  +					ExcludedFromBuild="true"
          998  +					>
          999  +					<Tool
         1000  +						Name="VCCLCompilerTool"
         1001  +					/>
         1002  +				</FileConfiguration>
         1003  +			</File>
         1004  +			<File
         1005  +				RelativePath="..\..\..\inffast.c"
         1006  +				>
         1007  +			</File>
         1008  +			<File
         1009  +				RelativePath="..\..\..\inflate.c"
         1010  +				>
         1011  +			</File>
         1012  +			<File
         1013  +				RelativePath="..\..\..\inftrees.c"
         1014  +				>
         1015  +			</File>
         1016  +			<File
         1017  +				RelativePath="..\..\minizip\ioapi.c"
         1018  +				>
         1019  +			</File>
         1020  +			<File
         1021  +				RelativePath="..\..\minizip\iowin32.c"
         1022  +				>
         1023  +			</File>
         1024  +			<File
         1025  +				RelativePath="..\..\..\trees.c"
         1026  +				>
         1027  +			</File>
         1028  +			<File
         1029  +				RelativePath="..\..\..\uncompr.c"
         1030  +				>
         1031  +			</File>
         1032  +			<File
         1033  +				RelativePath="..\..\minizip\unzip.c"
         1034  +				>
         1035  +				<FileConfiguration
         1036  +					Name="Release|Win32"
         1037  +					>
         1038  +					<Tool
         1039  +						Name="VCCLCompilerTool"
         1040  +						AdditionalIncludeDirectories=""
         1041  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1042  +					/>
         1043  +				</FileConfiguration>
         1044  +				<FileConfiguration
         1045  +					Name="Release|x64"
         1046  +					>
         1047  +					<Tool
         1048  +						Name="VCCLCompilerTool"
         1049  +						AdditionalIncludeDirectories=""
         1050  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1051  +					/>
         1052  +				</FileConfiguration>
         1053  +				<FileConfiguration
         1054  +					Name="Release|Itanium"
         1055  +					>
         1056  +					<Tool
         1057  +						Name="VCCLCompilerTool"
         1058  +						AdditionalIncludeDirectories=""
         1059  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1060  +					/>
         1061  +				</FileConfiguration>
         1062  +			</File>
         1063  +			<File
         1064  +				RelativePath="..\..\minizip\zip.c"
         1065  +				>
         1066  +				<FileConfiguration
         1067  +					Name="Release|Win32"
         1068  +					>
         1069  +					<Tool
         1070  +						Name="VCCLCompilerTool"
         1071  +						AdditionalIncludeDirectories=""
         1072  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1073  +					/>
         1074  +				</FileConfiguration>
         1075  +				<FileConfiguration
         1076  +					Name="Release|x64"
         1077  +					>
         1078  +					<Tool
         1079  +						Name="VCCLCompilerTool"
         1080  +						AdditionalIncludeDirectories=""
         1081  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1082  +					/>
         1083  +				</FileConfiguration>
         1084  +				<FileConfiguration
         1085  +					Name="Release|Itanium"
         1086  +					>
         1087  +					<Tool
         1088  +						Name="VCCLCompilerTool"
         1089  +						AdditionalIncludeDirectories=""
         1090  +						PreprocessorDefinitions="ZLIB_INTERNAL"
         1091  +					/>
         1092  +				</FileConfiguration>
         1093  +			</File>
         1094  +			<File
         1095  +				RelativePath=".\zlib.rc"
         1096  +				>
         1097  +			</File>
         1098  +			<File
         1099  +				RelativePath=".\zlibvc.def"
         1100  +				>
         1101  +			</File>
         1102  +			<File
         1103  +				RelativePath="..\..\..\zutil.c"
         1104  +				>
         1105  +			</File>
         1106  +		</Filter>
         1107  +		<Filter
         1108  +			Name="Header Files"
         1109  +			Filter="h;hpp;hxx;hm;inl;fi;fd"
         1110  +			>
         1111  +			<File
         1112  +				RelativePath="..\..\..\deflate.h"
         1113  +				>
         1114  +			</File>
         1115  +			<File
         1116  +				RelativePath="..\..\..\infblock.h"
         1117  +				>
         1118  +			</File>
         1119  +			<File
         1120  +				RelativePath="..\..\..\infcodes.h"
         1121  +				>
         1122  +			</File>
         1123  +			<File
         1124  +				RelativePath="..\..\..\inffast.h"
         1125  +				>
         1126  +			</File>
         1127  +			<File
         1128  +				RelativePath="..\..\..\inftrees.h"
         1129  +				>
         1130  +			</File>
         1131  +			<File
         1132  +				RelativePath="..\..\..\infutil.h"
         1133  +				>
         1134  +			</File>
         1135  +			<File
         1136  +				RelativePath="..\..\..\zconf.h"
         1137  +				>
         1138  +			</File>
         1139  +			<File
         1140  +				RelativePath="..\..\..\zlib.h"
         1141  +				>
         1142  +			</File>
         1143  +			<File
         1144  +				RelativePath="..\..\..\zutil.h"
         1145  +				>
         1146  +			</File>
         1147  +		</Filter>
         1148  +		<Filter
         1149  +			Name="Resource Files"
         1150  +			Filter="ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
         1151  +			>
         1152  +		</Filter>
         1153  +	</Files>
         1154  +	<Globals>
         1155  +	</Globals>
         1156  +</VisualStudioProject>

Added compat/zlib/crc32.c.

            1  +/* crc32.c -- compute the CRC-32 of a data stream
            2  + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + *
            5  + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
            6  + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
            7  + * tables for updating the shift register in one step with three exclusive-ors
            8  + * instead of four steps with four exclusive-ors.  This results in about a
            9  + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
           10  + */
           11  +
           12  +/* @(#) $Id$ */
           13  +
           14  +/*
           15  +  Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
           16  +  protection on the static variables used to control the first-use generation
           17  +  of the crc tables.  Therefore, if you #define DYNAMIC_CRC_TABLE, you should
           18  +  first call get_crc_table() to initialize the tables before allowing more than
           19  +  one thread to use crc32().
           20  +
           21  +  DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
           22  + */
           23  +
           24  +#ifdef MAKECRCH
           25  +#  include <stdio.h>
           26  +#  ifndef DYNAMIC_CRC_TABLE
           27  +#    define DYNAMIC_CRC_TABLE
           28  +#  endif /* !DYNAMIC_CRC_TABLE */
           29  +#endif /* MAKECRCH */
           30  +
           31  +#include "zutil.h"      /* for STDC and FAR definitions */
           32  +
           33  +#define local static
           34  +
           35  +/* Definitions for doing the crc four data bytes at a time. */
           36  +#if !defined(NOBYFOUR) && defined(Z_U4)
           37  +#  define BYFOUR
           38  +#endif
           39  +#ifdef BYFOUR
           40  +   local unsigned long crc32_little OF((unsigned long,
           41  +                        const unsigned char FAR *, unsigned));
           42  +   local unsigned long crc32_big OF((unsigned long,
           43  +                        const unsigned char FAR *, unsigned));
           44  +#  define TBLS 8
           45  +#else
           46  +#  define TBLS 1
           47  +#endif /* BYFOUR */
           48  +
           49  +/* Local functions for crc concatenation */
           50  +local unsigned long gf2_matrix_times OF((unsigned long *mat,
           51  +                                         unsigned long vec));
           52  +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
           53  +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));
           54  +
           55  +
           56  +#ifdef DYNAMIC_CRC_TABLE
           57  +
           58  +local volatile int crc_table_empty = 1;
           59  +local z_crc_t FAR crc_table[TBLS][256];
           60  +local void make_crc_table OF((void));
           61  +#ifdef MAKECRCH
           62  +   local void write_table OF((FILE *, const z_crc_t FAR *));
           63  +#endif /* MAKECRCH */
           64  +/*
           65  +  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
           66  +  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
           67  +
           68  +  Polynomials over GF(2) are represented in binary, one bit per coefficient,
           69  +  with the lowest powers in the most significant bit.  Then adding polynomials
           70  +  is just exclusive-or, and multiplying a polynomial by x is a right shift by
           71  +  one.  If we call the above polynomial p, and represent a byte as the
           72  +  polynomial q, also with the lowest power in the most significant bit (so the
           73  +  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
           74  +  where a mod b means the remainder after dividing a by b.
           75  +
           76  +  This calculation is done using the shift-register method of multiplying and
           77  +  taking the remainder.  The register is initialized to zero, and for each
           78  +  incoming bit, x^32 is added mod p to the register if the bit is a one (where
           79  +  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
           80  +  x (which is shifting right by one and adding x^32 mod p if the bit shifted
           81  +  out is a one).  We start with the highest power (least significant bit) of
           82  +  q and repeat for all eight bits of q.
           83  +
           84  +  The first table is simply the CRC of all possible eight bit values.  This is
           85  +  all the information needed to generate CRCs on data a byte at a time for all
           86  +  combinations of CRC register values and incoming bytes.  The remaining tables
           87  +  allow for word-at-a-time CRC calculation for both big-endian and little-
           88  +  endian machines, where a word is four bytes.
           89  +*/
           90  +local void make_crc_table()
           91  +{
           92  +    z_crc_t c;
           93  +    int n, k;
           94  +    z_crc_t poly;                       /* polynomial exclusive-or pattern */
           95  +    /* terms of polynomial defining this crc (except x^32): */
           96  +    static volatile int first = 1;      /* flag to limit concurrent making */
           97  +    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
           98  +
           99  +    /* See if another task is already doing this (not thread-safe, but better
          100  +       than nothing -- significantly reduces duration of vulnerability in
          101  +       case the advice about DYNAMIC_CRC_TABLE is ignored) */
          102  +    if (first) {
          103  +        first = 0;
          104  +
          105  +        /* make exclusive-or pattern from polynomial (0xedb88320UL) */
          106  +        poly = 0;
          107  +        for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
          108  +            poly |= (z_crc_t)1 << (31 - p[n]);
          109  +
          110  +        /* generate a crc for every 8-bit value */
          111  +        for (n = 0; n < 256; n++) {
          112  +            c = (z_crc_t)n;
          113  +            for (k = 0; k < 8; k++)
          114  +                c = c & 1 ? poly ^ (c >> 1) : c >> 1;
          115  +            crc_table[0][n] = c;
          116  +        }
          117  +
          118  +#ifdef BYFOUR
          119  +        /* generate crc for each value followed by one, two, and three zeros,
          120  +           and then the byte reversal of those as well as the first table */
          121  +        for (n = 0; n < 256; n++) {
          122  +            c = crc_table[0][n];
          123  +            crc_table[4][n] = ZSWAP32(c);
          124  +            for (k = 1; k < 4; k++) {
          125  +                c = crc_table[0][c & 0xff] ^ (c >> 8);
          126  +                crc_table[k][n] = c;
          127  +                crc_table[k + 4][n] = ZSWAP32(c);
          128  +            }
          129  +        }
          130  +#endif /* BYFOUR */
          131  +
          132  +        crc_table_empty = 0;
          133  +    }
          134  +    else {      /* not first */
          135  +        /* wait for the other guy to finish (not efficient, but rare) */
          136  +        while (crc_table_empty)
          137  +            ;
          138  +    }
          139  +
          140  +#ifdef MAKECRCH
          141  +    /* write out CRC tables to crc32.h */
          142  +    {
          143  +        FILE *out;
          144  +
          145  +        out = fopen("crc32.h", "w");
          146  +        if (out == NULL) return;
          147  +        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
          148  +        fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
          149  +        fprintf(out, "local const z_crc_t FAR ");
          150  +        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n");
          151  +        write_table(out, crc_table[0]);
          152  +#  ifdef BYFOUR
          153  +        fprintf(out, "#ifdef BYFOUR\n");
          154  +        for (k = 1; k < 8; k++) {
          155  +            fprintf(out, "  },\n  {\n");
          156  +            write_table(out, crc_table[k]);
          157  +        }
          158  +        fprintf(out, "#endif\n");
          159  +#  endif /* BYFOUR */
          160  +        fprintf(out, "  }\n};\n");
          161  +        fclose(out);
          162  +    }
          163  +#endif /* MAKECRCH */
          164  +}
          165  +
          166  +#ifdef MAKECRCH
          167  +local void write_table(out, table)
          168  +    FILE *out;
          169  +    const z_crc_t FAR *table;
          170  +{
          171  +    int n;
          172  +
          173  +    for (n = 0; n < 256; n++)
          174  +        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ",
          175  +                (unsigned long)(table[n]),
          176  +                n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
          177  +}
          178  +#endif /* MAKECRCH */
          179  +
          180  +#else /* !DYNAMIC_CRC_TABLE */
          181  +/* ========================================================================
          182  + * Tables of CRC-32s of all single-byte values, made by make_crc_table().
          183  + */
          184  +#include "crc32.h"
          185  +#endif /* DYNAMIC_CRC_TABLE */
          186  +
          187  +/* =========================================================================
          188  + * This function can be used by asm versions of crc32()
          189  + */
          190  +const z_crc_t FAR * ZEXPORT get_crc_table()
          191  +{
          192  +#ifdef DYNAMIC_CRC_TABLE
          193  +    if (crc_table_empty)
          194  +        make_crc_table();
          195  +#endif /* DYNAMIC_CRC_TABLE */
          196  +    return (const z_crc_t FAR *)crc_table;
          197  +}
          198  +
          199  +/* ========================================================================= */
          200  +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
          201  +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
          202  +
          203  +/* ========================================================================= */
          204  +unsigned long ZEXPORT crc32(crc, buf, len)
          205  +    unsigned long crc;
          206  +    const unsigned char FAR *buf;
          207  +    uInt len;
          208  +{
          209  +    if (buf == Z_NULL) return 0UL;
          210  +
          211  +#ifdef DYNAMIC_CRC_TABLE
          212  +    if (crc_table_empty)
          213  +        make_crc_table();
          214  +#endif /* DYNAMIC_CRC_TABLE */
          215  +
          216  +#ifdef BYFOUR
          217  +    if (sizeof(void *) == sizeof(ptrdiff_t)) {
          218  +        z_crc_t endian;
          219  +
          220  +        endian = 1;
          221  +        if (*((unsigned char *)(&endian)))
          222  +            return crc32_little(crc, buf, len);
          223  +        else
          224  +            return crc32_big(crc, buf, len);
          225  +    }
          226  +#endif /* BYFOUR */
          227  +    crc = crc ^ 0xffffffffUL;
          228  +    while (len >= 8) {
          229  +        DO8;
          230  +        len -= 8;
          231  +    }
          232  +    if (len) do {
          233  +        DO1;
          234  +    } while (--len);
          235  +    return crc ^ 0xffffffffUL;
          236  +}
          237  +
          238  +#ifdef BYFOUR
          239  +
          240  +/* ========================================================================= */
          241  +#define DOLIT4 c ^= *buf4++; \
          242  +        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
          243  +            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
          244  +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
          245  +
          246  +/* ========================================================================= */
          247  +local unsigned long crc32_little(crc, buf, len)
          248  +    unsigned long crc;
          249  +    const unsigned char FAR *buf;
          250  +    unsigned len;
          251  +{
          252  +    register z_crc_t c;
          253  +    register const z_crc_t FAR *buf4;
          254  +
          255  +    c = (z_crc_t)crc;
          256  +    c = ~c;
          257  +    while (len && ((ptrdiff_t)buf & 3)) {
          258  +        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
          259  +        len--;
          260  +    }
          261  +
          262  +    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
          263  +    while (len >= 32) {
          264  +        DOLIT32;
          265  +        len -= 32;
          266  +    }
          267  +    while (len >= 4) {
          268  +        DOLIT4;
          269  +        len -= 4;
          270  +    }
          271  +    buf = (const unsigned char FAR *)buf4;
          272  +
          273  +    if (len) do {
          274  +        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
          275  +    } while (--len);
          276  +    c = ~c;
          277  +    return (unsigned long)c;
          278  +}
          279  +
          280  +/* ========================================================================= */
          281  +#define DOBIG4 c ^= *++buf4; \
          282  +        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
          283  +            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
          284  +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
          285  +
          286  +/* ========================================================================= */
          287  +local unsigned long crc32_big(crc, buf, len)
          288  +    unsigned long crc;
          289  +    const unsigned char FAR *buf;
          290  +    unsigned len;
          291  +{
          292  +    register z_crc_t c;
          293  +    register const z_crc_t FAR *buf4;
          294  +
          295  +    c = ZSWAP32((z_crc_t)crc);
          296  +    c = ~c;
          297  +    while (len && ((ptrdiff_t)buf & 3)) {
          298  +        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
          299  +        len--;
          300  +    }
          301  +
          302  +    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
          303  +    buf4--;
          304  +    while (len >= 32) {
          305  +        DOBIG32;
          306  +        len -= 32;
          307  +    }
          308  +    while (len >= 4) {
          309  +        DOBIG4;
          310  +        len -= 4;
          311  +    }
          312  +    buf4++;
          313  +    buf = (const unsigned char FAR *)buf4;
          314  +
          315  +    if (len) do {
          316  +        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
          317  +    } while (--len);
          318  +    c = ~c;
          319  +    return (unsigned long)(ZSWAP32(c));
          320  +}
          321  +
          322  +#endif /* BYFOUR */
          323  +
          324  +#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */
          325  +
          326  +/* ========================================================================= */
          327  +local unsigned long gf2_matrix_times(mat, vec)
          328  +    unsigned long *mat;
          329  +    unsigned long vec;
          330  +{
          331  +    unsigned long sum;
          332  +
          333  +    sum = 0;
          334  +    while (vec) {
          335  +        if (vec & 1)
          336  +            sum ^= *mat;
          337  +        vec >>= 1;
          338  +        mat++;
          339  +    }
          340  +    return sum;
          341  +}
          342  +
          343  +/* ========================================================================= */
          344  +local void gf2_matrix_square(square, mat)
          345  +    unsigned long *square;
          346  +    unsigned long *mat;
          347  +{
          348  +    int n;
          349  +
          350  +    for (n = 0; n < GF2_DIM; n++)
          351  +        square[n] = gf2_matrix_times(mat, mat[n]);
          352  +}
          353  +
          354  +/* ========================================================================= */
          355  +local uLong crc32_combine_(crc1, crc2, len2)
          356  +    uLong crc1;
          357  +    uLong crc2;
          358  +    z_off64_t len2;
          359  +{
          360  +    int n;
          361  +    unsigned long row;
          362  +    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */
          363  +    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */
          364  +
          365  +    /* degenerate case (also disallow negative lengths) */
          366  +    if (len2 <= 0)
          367  +        return crc1;
          368  +
          369  +    /* put operator for one zero bit in odd */
          370  +    odd[0] = 0xedb88320UL;          /* CRC-32 polynomial */
          371  +    row = 1;
          372  +    for (n = 1; n < GF2_DIM; n++) {
          373  +        odd[n] = row;
          374  +        row <<= 1;
          375  +    }
          376  +
          377  +    /* put operator for two zero bits in even */
          378  +    gf2_matrix_square(even, odd);
          379  +
          380  +    /* put operator for four zero bits in odd */
          381  +    gf2_matrix_square(odd, even);
          382  +
          383  +    /* apply len2 zeros to crc1 (first square will put the operator for one
          384  +       zero byte, eight zero bits, in even) */
          385  +    do {
          386  +        /* apply zeros operator for this bit of len2 */
          387  +        gf2_matrix_square(even, odd);
          388  +        if (len2 & 1)
          389  +            crc1 = gf2_matrix_times(even, crc1);
          390  +        len2 >>= 1;
          391  +
          392  +        /* if no more bits set, then done */
          393  +        if (len2 == 0)
          394  +            break;
          395  +
          396  +        /* another iteration of the loop with odd and even swapped */
          397  +        gf2_matrix_square(odd, even);
          398  +        if (len2 & 1)
          399  +            crc1 = gf2_matrix_times(odd, crc1);
          400  +        len2 >>= 1;
          401  +
          402  +        /* if no more bits set, then done */
          403  +    } while (len2 != 0);
          404  +
          405  +    /* return combined crc */
          406  +    crc1 ^= crc2;
          407  +    return crc1;
          408  +}
          409  +
          410  +/* ========================================================================= */
          411  +uLong ZEXPORT crc32_combine(crc1, crc2, len2)
          412  +    uLong crc1;
          413  +    uLong crc2;
          414  +    z_off_t len2;
          415  +{
          416  +    return crc32_combine_(crc1, crc2, len2);
          417  +}
          418  +
          419  +uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
          420  +    uLong crc1;
          421  +    uLong crc2;
          422  +    z_off64_t len2;
          423  +{
          424  +    return crc32_combine_(crc1, crc2, len2);
          425  +}

Added compat/zlib/crc32.h.

            1  +/* crc32.h -- tables for rapid CRC calculation
            2  + * Generated automatically by crc32.c
            3  + */
            4  +
            5  +local const z_crc_t FAR crc_table[TBLS][256] =
            6  +{
            7  +  {
            8  +    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
            9  +    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
           10  +    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
           11  +    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
           12  +    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
           13  +    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
           14  +    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
           15  +    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
           16  +    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
           17  +    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
           18  +    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
           19  +    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
           20  +    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
           21  +    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
           22  +    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
           23  +    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
           24  +    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
           25  +    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
           26  +    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
           27  +    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
           28  +    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
           29  +    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
           30  +    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
           31  +    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
           32  +    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
           33  +    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
           34  +    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
           35  +    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
           36  +    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
           37  +    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
           38  +    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
           39  +    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
           40  +    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
           41  +    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
           42  +    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
           43  +    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
           44  +    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
           45  +    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
           46  +    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
           47  +    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
           48  +    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
           49  +    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
           50  +    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
           51  +    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
           52  +    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
           53  +    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
           54  +    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
           55  +    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
           56  +    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
           57  +    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
           58  +    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
           59  +    0x2d02ef8dUL
           60  +#ifdef BYFOUR
           61  +  },
           62  +  {
           63  +    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
           64  +    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
           65  +    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
           66  +    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
           67  +    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
           68  +    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
           69  +    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
           70  +    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
           71  +    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
           72  +    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
           73  +    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
           74  +    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
           75  +    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
           76  +    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
           77  +    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
           78  +    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
           79  +    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
           80  +    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
           81  +    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
           82  +    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
           83  +    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
           84  +    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
           85  +    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
           86  +    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
           87  +    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
           88  +    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
           89  +    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
           90  +    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
           91  +    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
           92  +    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
           93  +    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
           94  +    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
           95  +    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
           96  +    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
           97  +    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
           98  +    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
           99  +    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
          100  +    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
          101  +    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
          102  +    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
          103  +    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
          104  +    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
          105  +    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
          106  +    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
          107  +    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
          108  +    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
          109  +    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
          110  +    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
          111  +    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
          112  +    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
          113  +    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
          114  +    0x9324fd72UL
          115  +  },
          116  +  {
          117  +    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
          118  +    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
          119  +    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
          120  +    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
          121  +    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
          122  +    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
          123  +    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
          124  +    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
          125  +    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
          126  +    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
          127  +    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
          128  +    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
          129  +    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
          130  +    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
          131  +    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
          132  +    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
          133  +    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
          134  +    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
          135  +    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
          136  +    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
          137  +    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
          138  +    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
          139  +    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
          140  +    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
          141  +    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
          142  +    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
          143  +    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
          144  +    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
          145  +    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
          146  +    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
          147  +    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
          148  +    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
          149  +    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
          150  +    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
          151  +    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
          152  +    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
          153  +    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
          154  +    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
          155  +    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
          156  +    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
          157  +    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
          158  +    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
          159  +    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
          160  +    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
          161  +    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
          162  +    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
          163  +    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
          164  +    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
          165  +    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
          166  +    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
          167  +    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
          168  +    0xbe9834edUL
          169  +  },
          170  +  {
          171  +    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
          172  +    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
          173  +    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
          174  +    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
          175  +    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
          176  +    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
          177  +    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
          178  +    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
          179  +    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
          180  +    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
          181  +    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
          182  +    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
          183  +    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
          184  +    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
          185  +    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
          186  +    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
          187  +    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
          188  +    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
          189  +    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
          190  +    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
          191  +    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
          192  +    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
          193  +    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
          194  +    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
          195  +    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
          196  +    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
          197  +    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
          198  +    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
          199  +    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
          200  +    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
          201  +    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
          202  +    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
          203  +    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
          204  +    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
          205  +    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
          206  +    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
          207  +    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
          208  +    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
          209  +    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
          210  +    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
          211  +    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
          212  +    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
          213  +    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
          214  +    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
          215  +    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
          216  +    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
          217  +    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
          218  +    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
          219  +    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
          220  +    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
          221  +    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
          222  +    0xde0506f1UL
          223  +  },
          224  +  {
          225  +    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
          226  +    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
          227  +    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
          228  +    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
          229  +    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
          230  +    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
          231  +    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
          232  +    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
          233  +    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
          234  +    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
          235  +    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
          236  +    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
          237  +    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
          238  +    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
          239  +    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
          240  +    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
          241  +    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
          242  +    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
          243  +    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
          244  +    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
          245  +    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
          246  +    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
          247  +    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
          248  +    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
          249  +    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
          250  +    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
          251  +    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
          252  +    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
          253  +    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
          254  +    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
          255  +    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
          256  +    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
          257  +    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
          258  +    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
          259  +    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
          260  +    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
          261  +    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
          262  +    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
          263  +    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
          264  +    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
          265  +    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
          266  +    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
          267  +    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
          268  +    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
          269  +    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
          270  +    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
          271  +    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
          272  +    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
          273  +    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
          274  +    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
          275  +    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
          276  +    0x8def022dUL
          277  +  },
          278  +  {
          279  +    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
          280  +    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
          281  +    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
          282  +    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
          283  +    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
          284  +    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
          285  +    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
          286  +    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
          287  +    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
          288  +    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
          289  +    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
          290  +    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
          291  +    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
          292  +    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
          293  +    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
          294  +    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
          295  +    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
          296  +    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
          297  +    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
          298  +    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
          299  +    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
          300  +    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
          301  +    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
          302  +    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
          303  +    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
          304  +    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
          305  +    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
          306  +    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
          307  +    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
          308  +    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
          309  +    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
          310  +    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
          311  +    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
          312  +    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
          313  +    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
          314  +    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
          315  +    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
          316  +    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
          317  +    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
          318  +    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
          319  +    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
          320  +    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
          321  +    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
          322  +    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
          323  +    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
          324  +    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
          325  +    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
          326  +    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
          327  +    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
          328  +    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
          329  +    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
          330  +    0x72fd2493UL
          331  +  },
          332  +  {
          333  +    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
          334  +    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
          335  +    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
          336  +    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
          337  +    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
          338  +    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
          339  +    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
          340  +    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
          341  +    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
          342  +    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
          343  +    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
          344  +    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
          345  +    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
          346  +    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
          347  +    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
          348  +    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
          349  +    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
          350  +    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
          351  +    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
          352  +    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
          353  +    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
          354  +    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
          355  +    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
          356  +    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
          357  +    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
          358  +    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
          359  +    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
          360  +    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
          361  +    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
          362  +    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
          363  +    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
          364  +    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
          365  +    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
          366  +    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
          367  +    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
          368  +    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
          369  +    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
          370  +    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
          371  +    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
          372  +    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
          373  +    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
          374  +    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
          375  +    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
          376  +    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
          377  +    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
          378  +    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
          379  +    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
          380  +    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
          381  +    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
          382  +    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
          383  +    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
          384  +    0xed3498beUL
          385  +  },
          386  +  {
          387  +    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
          388  +    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
          389  +    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
          390  +    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
          391  +    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
          392  +    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
          393  +    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
          394  +    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
          395  +    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
          396  +    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
          397  +    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
          398  +    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
          399  +    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
          400  +    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
          401  +    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
          402  +    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
          403  +    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
          404  +    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
          405  +    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
          406  +    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
          407  +    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
          408  +    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
          409  +    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
          410  +    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
          411  +    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
          412  +    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
          413  +    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
          414  +    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
          415  +    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
          416  +    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
          417  +    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
          418  +    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
          419  +    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
          420  +    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
          421  +    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
          422  +    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
          423  +    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
          424  +    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
          425  +    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
          426  +    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
          427  +    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
          428  +    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
          429  +    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
          430  +    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
          431  +    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
          432  +    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
          433  +    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
          434  +    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
          435  +    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
          436  +    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
          437  +    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
          438  +    0xf10605deUL
          439  +#endif
          440  +  }
          441  +};

Added compat/zlib/deflate.c.

            1  +/* deflate.c -- compress data using the deflation algorithm
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/*
            7  + *  ALGORITHM
            8  + *
            9  + *      The "deflation" process depends on being able to identify portions
           10  + *      of the input text which are identical to earlier input (within a
           11  + *      sliding window trailing behind the input currently being processed).
           12  + *
           13  + *      The most straightforward technique turns out to be the fastest for
           14  + *      most input files: try all possible matches and select the longest.
           15  + *      The key feature of this algorithm is that insertions into the string
           16  + *      dictionary are very simple and thus fast, and deletions are avoided
           17  + *      completely. Insertions are performed at each input character, whereas
           18  + *      string matches are performed only when the previous match ends. So it
           19  + *      is preferable to spend more time in matches to allow very fast string
           20  + *      insertions and avoid deletions. The matching algorithm for small
           21  + *      strings is inspired from that of Rabin & Karp. A brute force approach
           22  + *      is used to find longer strings when a small match has been found.
           23  + *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
           24  + *      (by Leonid Broukhis).
           25  + *         A previous version of this file used a more sophisticated algorithm
           26  + *      (by Fiala and Greene) which is guaranteed to run in linear amortized
           27  + *      time, but has a larger average cost, uses more memory and is patented.
           28  + *      However the F&G algorithm may be faster for some highly redundant
           29  + *      files if the parameter max_chain_length (described below) is too large.
           30  + *
           31  + *  ACKNOWLEDGEMENTS
           32  + *
           33  + *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
           34  + *      I found it in 'freeze' written by Leonid Broukhis.
           35  + *      Thanks to many people for bug reports and testing.
           36  + *
           37  + *  REFERENCES
           38  + *
           39  + *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
           40  + *      Available in http://tools.ietf.org/html/rfc1951
           41  + *
           42  + *      A description of the Rabin and Karp algorithm is given in the book
           43  + *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
           44  + *
           45  + *      Fiala,E.R., and Greene,D.H.
           46  + *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
           47  + *
           48  + */
           49  +
           50  +/* @(#) $Id$ */
           51  +
           52  +#include "deflate.h"
           53  +
           54  +const char deflate_copyright[] =
           55  +   " deflate 1.2.7 Copyright 1995-2012 Jean-loup Gailly and Mark Adler ";
           56  +/*
           57  +  If you use the zlib library in a product, an acknowledgment is welcome
           58  +  in the documentation of your product. If for some reason you cannot
           59  +  include such an acknowledgment, I would appreciate that you keep this
           60  +  copyright string in the executable of your product.
           61  + */
           62  +
           63  +/* ===========================================================================
           64  + *  Function prototypes.
           65  + */
           66  +typedef enum {
           67  +    need_more,      /* block not completed, need more input or more output */
           68  +    block_done,     /* block flush performed */
           69  +    finish_started, /* finish started, need only more output at next deflate */
           70  +    finish_done     /* finish done, accept no more input or output */
           71  +} block_state;
           72  +
           73  +typedef block_state (*compress_func) OF((deflate_state *s, int flush));
           74  +/* Compression function. Returns the block state after the call. */
           75  +
           76  +local void fill_window    OF((deflate_state *s));
           77  +local block_state deflate_stored OF((deflate_state *s, int flush));
           78  +local block_state deflate_fast   OF((deflate_state *s, int flush));
           79  +#ifndef FASTEST
           80  +local block_state deflate_slow   OF((deflate_state *s, int flush));
           81  +#endif
           82  +local block_state deflate_rle    OF((deflate_state *s, int flush));
           83  +local block_state deflate_huff   OF((deflate_state *s, int flush));
           84  +local void lm_init        OF((deflate_state *s));
           85  +local void putShortMSB    OF((deflate_state *s, uInt b));
           86  +local void flush_pending  OF((z_streamp strm));
           87  +local int read_buf        OF((z_streamp strm, Bytef *buf, unsigned size));
           88  +#ifdef ASMV
           89  +      void match_init OF((void)); /* asm code initialization */
           90  +      uInt longest_match  OF((deflate_state *s, IPos cur_match));
           91  +#else
           92  +local uInt longest_match  OF((deflate_state *s, IPos cur_match));
           93  +#endif
           94  +
           95  +#ifdef DEBUG
           96  +local  void check_match OF((deflate_state *s, IPos start, IPos match,
           97  +                            int length));
           98  +#endif
           99  +
          100  +/* ===========================================================================
          101  + * Local data
          102  + */
          103  +
          104  +#define NIL 0
          105  +/* Tail of hash chains */
          106  +
          107  +#ifndef TOO_FAR
          108  +#  define TOO_FAR 4096
          109  +#endif
          110  +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
          111  +
          112  +/* Values for max_lazy_match, good_match and max_chain_length, depending on
          113  + * the desired pack level (0..9). The values given below have been tuned to
          114  + * exclude worst case performance for pathological files. Better values may be
          115  + * found for specific files.
          116  + */
          117  +typedef struct config_s {
          118  +   ush good_length; /* reduce lazy search above this match length */
          119  +   ush max_lazy;    /* do not perform lazy search above this match length */
          120  +   ush nice_length; /* quit search above this match length */
          121  +   ush max_chain;
          122  +   compress_func func;
          123  +} config;
          124  +
          125  +#ifdef FASTEST
          126  +local const config configuration_table[2] = {
          127  +/*      good lazy nice chain */
          128  +/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
          129  +/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */
          130  +#else
          131  +local const config configuration_table[10] = {
          132  +/*      good lazy nice chain */
          133  +/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
          134  +/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */
          135  +/* 2 */ {4,    5, 16,    8, deflate_fast},
          136  +/* 3 */ {4,    6, 32,   32, deflate_fast},
          137  +
          138  +/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
          139  +/* 5 */ {8,   16, 32,   32, deflate_slow},
          140  +/* 6 */ {8,   16, 128, 128, deflate_slow},
          141  +/* 7 */ {8,   32, 128, 256, deflate_slow},
          142  +/* 8 */ {32, 128, 258, 1024, deflate_slow},
          143  +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
          144  +#endif
          145  +
          146  +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
          147  + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
          148  + * meaning.
          149  + */
          150  +
          151  +#define EQUAL 0
          152  +/* result of memcmp for equal strings */
          153  +
          154  +#ifndef NO_DUMMY_DECL
          155  +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
          156  +#endif
          157  +
          158  +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
          159  +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0))
          160  +
          161  +/* ===========================================================================
          162  + * Update a hash value with the given input byte
          163  + * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
          164  + *    input characters, so that a running hash key can be computed from the
          165  + *    previous key instead of complete recalculation each time.
          166  + */
          167  +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
          168  +
          169  +
          170  +/* ===========================================================================
          171  + * Insert string str in the dictionary and set match_head to the previous head
          172  + * of the hash chain (the most recent string with same hash key). Return
          173  + * the previous length of the hash chain.
          174  + * If this file is compiled with -DFASTEST, the compression level is forced
          175  + * to 1, and no hash chains are maintained.
          176  + * IN  assertion: all calls to to INSERT_STRING are made with consecutive
          177  + *    input characters and the first MIN_MATCH bytes of str are valid
          178  + *    (except for the last MIN_MATCH-1 bytes of the input file).
          179  + */
          180  +#ifdef FASTEST
          181  +#define INSERT_STRING(s, str, match_head) \
          182  +   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
          183  +    match_head = s->head[s->ins_h], \
          184  +    s->head[s->ins_h] = (Pos)(str))
          185  +#else
          186  +#define INSERT_STRING(s, str, match_head) \
          187  +   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
          188  +    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
          189  +    s->head[s->ins_h] = (Pos)(str))
          190  +#endif
          191  +
          192  +/* ===========================================================================
          193  + * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
          194  + * prev[] will be initialized on the fly.
          195  + */
          196  +#define CLEAR_HASH(s) \
          197  +    s->head[s->hash_size-1] = NIL; \
          198  +    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
          199  +
          200  +/* ========================================================================= */
          201  +int ZEXPORT deflateInit_(strm, level, version, stream_size)
          202  +    z_streamp strm;
          203  +    int level;
          204  +    const char *version;
          205  +    int stream_size;
          206  +{
          207  +    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
          208  +                         Z_DEFAULT_STRATEGY, version, stream_size);
          209  +    /* To do: ignore strm->next_in if we use it as window */
          210  +}
          211  +
          212  +/* ========================================================================= */
          213  +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
          214  +                  version, stream_size)
          215  +    z_streamp strm;
          216  +    int  level;
          217  +    int  method;
          218  +    int  windowBits;
          219  +    int  memLevel;
          220  +    int  strategy;
          221  +    const char *version;
          222  +    int stream_size;
          223  +{
          224  +    deflate_state *s;
          225  +    int wrap = 1;
          226  +    static const char my_version[] = ZLIB_VERSION;
          227  +
          228  +    ushf *overlay;
          229  +    /* We overlay pending_buf and d_buf+l_buf. This works since the average
          230  +     * output size for (length,distance) codes is <= 24 bits.
          231  +     */
          232  +
          233  +    if (version == Z_NULL || version[0] != my_version[0] ||
          234  +        stream_size != sizeof(z_stream)) {
          235  +        return Z_VERSION_ERROR;
          236  +    }
          237  +    if (strm == Z_NULL) return Z_STREAM_ERROR;
          238  +
          239  +    strm->msg = Z_NULL;
          240  +    if (strm->zalloc == (alloc_func)0) {
          241  +#ifdef Z_SOLO
          242  +        return Z_STREAM_ERROR;
          243  +#else
          244  +        strm->zalloc = zcalloc;
          245  +        strm->opaque = (voidpf)0;
          246  +#endif
          247  +    }
          248  +    if (strm->zfree == (free_func)0)
          249  +#ifdef Z_SOLO
          250  +        return Z_STREAM_ERROR;
          251  +#else
          252  +        strm->zfree = zcfree;
          253  +#endif
          254  +
          255  +#ifdef FASTEST
          256  +    if (level != 0) level = 1;
          257  +#else
          258  +    if (level == Z_DEFAULT_COMPRESSION) level = 6;
          259  +#endif
          260  +
          261  +    if (windowBits < 0) { /* suppress zlib wrapper */
          262  +        wrap = 0;
          263  +        windowBits = -windowBits;
          264  +    }
          265  +#ifdef GZIP
          266  +    else if (windowBits > 15) {
          267  +        wrap = 2;       /* write gzip wrapper instead */
          268  +        windowBits -= 16;
          269  +    }
          270  +#endif
          271  +    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
          272  +        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
          273  +        strategy < 0 || strategy > Z_FIXED) {
          274  +        return Z_STREAM_ERROR;
          275  +    }
          276  +    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
          277  +    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
          278  +    if (s == Z_NULL) return Z_MEM_ERROR;
          279  +    strm->state = (struct internal_state FAR *)s;
          280  +    s->strm = strm;
          281  +
          282  +    s->wrap = wrap;
          283  +    s->gzhead = Z_NULL;
          284  +    s->w_bits = windowBits;
          285  +    s->w_size = 1 << s->w_bits;
          286  +    s->w_mask = s->w_size - 1;
          287  +
          288  +    s->hash_bits = memLevel + 7;
          289  +    s->hash_size = 1 << s->hash_bits;
          290  +    s->hash_mask = s->hash_size - 1;
          291  +    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
          292  +
          293  +    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
          294  +    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));
          295  +    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));
          296  +
          297  +    s->high_water = 0;      /* nothing written to s->window yet */
          298  +
          299  +    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
          300  +
          301  +    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
          302  +    s->pending_buf = (uchf *) overlay;
          303  +    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
          304  +
          305  +    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
          306  +        s->pending_buf == Z_NULL) {
          307  +        s->status = FINISH_STATE;
          308  +        strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
          309  +        deflateEnd (strm);
          310  +        return Z_MEM_ERROR;
          311  +    }
          312  +    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
          313  +    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
          314  +
          315  +    s->level = level;
          316  +    s->strategy = strategy;
          317  +    s->method = (Byte)method;
          318  +
          319  +    return deflateReset(strm);
          320  +}
          321  +
          322  +/* ========================================================================= */
          323  +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
          324  +    z_streamp strm;
          325  +    const Bytef *dictionary;
          326  +    uInt  dictLength;
          327  +{
          328  +    deflate_state *s;
          329  +    uInt str, n;
          330  +    int wrap;
          331  +    unsigned avail;
          332  +    unsigned char *next;
          333  +
          334  +    if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL)
          335  +        return Z_STREAM_ERROR;
          336  +    s = strm->state;
          337  +    wrap = s->wrap;
          338  +    if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
          339  +        return Z_STREAM_ERROR;
          340  +
          341  +    /* when using zlib wrappers, compute Adler-32 for provided dictionary */
          342  +    if (wrap == 1)
          343  +        strm->adler = adler32(strm->adler, dictionary, dictLength);
          344  +    s->wrap = 0;                    /* avoid computing Adler-32 in read_buf */
          345  +
          346  +    /* if dictionary would fill window, just replace the history */
          347  +    if (dictLength >= s->w_size) {
          348  +        if (wrap == 0) {            /* already empty otherwise */
          349  +            CLEAR_HASH(s);
          350  +            s->strstart = 0;
          351  +            s->block_start = 0L;
          352  +            s->insert = 0;
          353  +        }
          354  +        dictionary += dictLength - s->w_size;  /* use the tail */
          355  +        dictLength = s->w_size;
          356  +    }
          357  +
          358  +    /* insert dictionary into window and hash */
          359  +    avail = strm->avail_in;
          360  +    next = strm->next_in;
          361  +    strm->avail_in = dictLength;
          362  +    strm->next_in = (Bytef *)dictionary;
          363  +    fill_window(s);
          364  +    while (s->lookahead >= MIN_MATCH) {
          365  +        str = s->strstart;
          366  +        n = s->lookahead - (MIN_MATCH-1);
          367  +        do {
          368  +            UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
          369  +#ifndef FASTEST
          370  +            s->prev[str & s->w_mask] = s->head[s->ins_h];
          371  +#endif
          372  +            s->head[s->ins_h] = (Pos)str;
          373  +            str++;
          374  +        } while (--n);
          375  +        s->strstart = str;
          376  +        s->lookahead = MIN_MATCH-1;
          377  +        fill_window(s);
          378  +    }
          379  +    s->strstart += s->lookahead;
          380  +    s->block_start = (long)s->strstart;
          381  +    s->insert = s->lookahead;
          382  +    s->lookahead = 0;
          383  +    s->match_length = s->prev_length = MIN_MATCH-1;
          384  +    s->match_available = 0;
          385  +    strm->next_in = next;
          386  +    strm->avail_in = avail;
          387  +    s->wrap = wrap;
          388  +    return Z_OK;
          389  +}
          390  +
          391  +/* ========================================================================= */
          392  +int ZEXPORT deflateResetKeep (strm)
          393  +    z_streamp strm;
          394  +{
          395  +    deflate_state *s;
          396  +
          397  +    if (strm == Z_NULL || strm->state == Z_NULL ||
          398  +        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
          399  +        return Z_STREAM_ERROR;
          400  +    }
          401  +
          402  +    strm->total_in = strm->total_out = 0;
          403  +    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
          404  +    strm->data_type = Z_UNKNOWN;
          405  +
          406  +    s = (deflate_state *)strm->state;
          407  +    s->pending = 0;
          408  +    s->pending_out = s->pending_buf;
          409  +
          410  +    if (s->wrap < 0) {
          411  +        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
          412  +    }
          413  +    s->status = s->wrap ? INIT_STATE : BUSY_STATE;
          414  +    strm->adler =
          415  +#ifdef GZIP
          416  +        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
          417  +#endif
          418  +        adler32(0L, Z_NULL, 0);
          419  +    s->last_flush = Z_NO_FLUSH;
          420  +
          421  +    _tr_init(s);
          422  +
          423  +    return Z_OK;
          424  +}
          425  +
          426  +/* ========================================================================= */
          427  +int ZEXPORT deflateReset (strm)
          428  +    z_streamp strm;
          429  +{
          430  +    int ret;
          431  +
          432  +    ret = deflateResetKeep(strm);
          433  +    if (ret == Z_OK)
          434  +        lm_init(strm->state);
          435  +    return ret;
          436  +}
          437  +
          438  +/* ========================================================================= */
          439  +int ZEXPORT deflateSetHeader (strm, head)
          440  +    z_streamp strm;
          441  +    gz_headerp head;
          442  +{
          443  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          444  +    if (strm->state->wrap != 2) return Z_STREAM_ERROR;
          445  +    strm->state->gzhead = head;
          446  +    return Z_OK;
          447  +}
          448  +
          449  +/* ========================================================================= */
          450  +int ZEXPORT deflatePending (strm, pending, bits)
          451  +    unsigned *pending;
          452  +    int *bits;
          453  +    z_streamp strm;
          454  +{
          455  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          456  +    if (pending != Z_NULL)
          457  +        *pending = strm->state->pending;
          458  +    if (bits != Z_NULL)
          459  +        *bits = strm->state->bi_valid;
          460  +    return Z_OK;
          461  +}
          462  +
          463  +/* ========================================================================= */
          464  +int ZEXPORT deflatePrime (strm, bits, value)
          465  +    z_streamp strm;
          466  +    int bits;
          467  +    int value;
          468  +{
          469  +    deflate_state *s;
          470  +    int put;
          471  +
          472  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          473  +    s = strm->state;
          474  +    if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
          475  +        return Z_BUF_ERROR;
          476  +    do {
          477  +        put = Buf_size - s->bi_valid;
          478  +        if (put > bits)
          479  +            put = bits;
          480  +        s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
          481  +        s->bi_valid += put;
          482  +        _tr_flush_bits(s);
          483  +        value >>= put;
          484  +        bits -= put;
          485  +    } while (bits);
          486  +    return Z_OK;
          487  +}
          488  +
          489  +/* ========================================================================= */
          490  +int ZEXPORT deflateParams(strm, level, strategy)
          491  +    z_streamp strm;
          492  +    int level;
          493  +    int strategy;
          494  +{
          495  +    deflate_state *s;
          496  +    compress_func func;
          497  +    int err = Z_OK;
          498  +
          499  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          500  +    s = strm->state;
          501  +
          502  +#ifdef FASTEST
          503  +    if (level != 0) level = 1;
          504  +#else
          505  +    if (level == Z_DEFAULT_COMPRESSION) level = 6;
          506  +#endif
          507  +    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
          508  +        return Z_STREAM_ERROR;
          509  +    }
          510  +    func = configuration_table[s->level].func;
          511  +
          512  +    if ((strategy != s->strategy || func != configuration_table[level].func) &&
          513  +        strm->total_in != 0) {
          514  +        /* Flush the last buffer: */
          515  +        err = deflate(strm, Z_BLOCK);
          516  +    }
          517  +    if (s->level != level) {
          518  +        s->level = level;
          519  +        s->max_lazy_match   = configuration_table[level].max_lazy;
          520  +        s->good_match       = configuration_table[level].good_length;
          521  +        s->nice_match       = configuration_table[level].nice_length;
          522  +        s->max_chain_length = configuration_table[level].max_chain;
          523  +    }
          524  +    s->strategy = strategy;
          525  +    return err;
          526  +}
          527  +
          528  +/* ========================================================================= */
          529  +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
          530  +    z_streamp strm;
          531  +    int good_length;
          532  +    int max_lazy;
          533  +    int nice_length;
          534  +    int max_chain;
          535  +{
          536  +    deflate_state *s;
          537  +
          538  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          539  +    s = strm->state;
          540  +    s->good_match = good_length;
          541  +    s->max_lazy_match = max_lazy;
          542  +    s->nice_match = nice_length;
          543  +    s->max_chain_length = max_chain;
          544  +    return Z_OK;
          545  +}
          546  +
          547  +/* =========================================================================
          548  + * For the default windowBits of 15 and memLevel of 8, this function returns
          549  + * a close to exact, as well as small, upper bound on the compressed size.
          550  + * They are coded as constants here for a reason--if the #define's are
          551  + * changed, then this function needs to be changed as well.  The return
          552  + * value for 15 and 8 only works for those exact settings.
          553  + *
          554  + * For any setting other than those defaults for windowBits and memLevel,
          555  + * the value returned is a conservative worst case for the maximum expansion
          556  + * resulting from using fixed blocks instead of stored blocks, which deflate
          557  + * can emit on compressed data for some combinations of the parameters.
          558  + *
          559  + * This function could be more sophisticated to provide closer upper bounds for
          560  + * every combination of windowBits and memLevel.  But even the conservative
          561  + * upper bound of about 14% expansion does not seem onerous for output buffer
          562  + * allocation.
          563  + */
          564  +uLong ZEXPORT deflateBound(strm, sourceLen)
          565  +    z_streamp strm;
          566  +    uLong sourceLen;
          567  +{
          568  +    deflate_state *s;
          569  +    uLong complen, wraplen;
          570  +    Bytef *str;
          571  +
          572  +    /* conservative upper bound for compressed data */
          573  +    complen = sourceLen +
          574  +              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
          575  +
          576  +    /* if can't get parameters, return conservative bound plus zlib wrapper */
          577  +    if (strm == Z_NULL || strm->state == Z_NULL)
          578  +        return complen + 6;
          579  +
          580  +    /* compute wrapper length */
          581  +    s = strm->state;
          582  +    switch (s->wrap) {
          583  +    case 0:                                 /* raw deflate */
          584  +        wraplen = 0;
          585  +        break;
          586  +    case 1:                                 /* zlib wrapper */
          587  +        wraplen = 6 + (s->strstart ? 4 : 0);
          588  +        break;
          589  +    case 2:                                 /* gzip wrapper */
          590  +        wraplen = 18;
          591  +        if (s->gzhead != Z_NULL) {          /* user-supplied gzip header */
          592  +            if (s->gzhead->extra != Z_NULL)
          593  +                wraplen += 2 + s->gzhead->extra_len;
          594  +            str = s->gzhead->name;
          595  +            if (str != Z_NULL)
          596  +                do {
          597  +                    wraplen++;
          598  +                } while (*str++);
          599  +            str = s->gzhead->comment;
          600  +            if (str != Z_NULL)
          601  +                do {
          602  +                    wraplen++;
          603  +                } while (*str++);
          604  +            if (s->gzhead->hcrc)
          605  +                wraplen += 2;
          606  +        }
          607  +        break;
          608  +    default:                                /* for compiler happiness */
          609  +        wraplen = 6;
          610  +    }
          611  +
          612  +    /* if not default parameters, return conservative bound */
          613  +    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
          614  +        return complen + wraplen;
          615  +
          616  +    /* default settings: return tight bound for that case */
          617  +    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
          618  +           (sourceLen >> 25) + 13 - 6 + wraplen;
          619  +}
          620  +
          621  +/* =========================================================================
          622  + * Put a short in the pending buffer. The 16-bit value is put in MSB order.
          623  + * IN assertion: the stream state is correct and there is enough room in
          624  + * pending_buf.
          625  + */
          626  +local void putShortMSB (s, b)
          627  +    deflate_state *s;
          628  +    uInt b;
          629  +{
          630  +    put_byte(s, (Byte)(b >> 8));
          631  +    put_byte(s, (Byte)(b & 0xff));
          632  +}
          633  +
          634  +/* =========================================================================
          635  + * Flush as much pending output as possible. All deflate() output goes
          636  + * through this function so some applications may wish to modify it
          637  + * to avoid allocating a large strm->next_out buffer and copying into it.
          638  + * (See also read_buf()).
          639  + */
          640  +local void flush_pending(strm)
          641  +    z_streamp strm;
          642  +{
          643  +    unsigned len;
          644  +    deflate_state *s = strm->state;
          645  +
          646  +    _tr_flush_bits(s);
          647  +    len = s->pending;
          648  +    if (len > strm->avail_out) len = strm->avail_out;
          649  +    if (len == 0) return;
          650  +
          651  +    zmemcpy(strm->next_out, s->pending_out, len);
          652  +    strm->next_out  += len;
          653  +    s->pending_out  += len;
          654  +    strm->total_out += len;
          655  +    strm->avail_out  -= len;
          656  +    s->pending -= len;
          657  +    if (s->pending == 0) {
          658  +        s->pending_out = s->pending_buf;
          659  +    }
          660  +}
          661  +
          662  +/* ========================================================================= */
          663  +int ZEXPORT deflate (strm, flush)
          664  +    z_streamp strm;
          665  +    int flush;
          666  +{
          667  +    int old_flush; /* value of flush param for previous deflate call */
          668  +    deflate_state *s;
          669  +
          670  +    if (strm == Z_NULL || strm->state == Z_NULL ||
          671  +        flush > Z_BLOCK || flush < 0) {
          672  +        return Z_STREAM_ERROR;
          673  +    }
          674  +    s = strm->state;
          675  +
          676  +    if (strm->next_out == Z_NULL ||
          677  +        (strm->next_in == Z_NULL && strm->avail_in != 0) ||
          678  +        (s->status == FINISH_STATE && flush != Z_FINISH)) {
          679  +        ERR_RETURN(strm, Z_STREAM_ERROR);
          680  +    }
          681  +    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
          682  +
          683  +    s->strm = strm; /* just in case */
          684  +    old_flush = s->last_flush;
          685  +    s->last_flush = flush;
          686  +
          687  +    /* Write the header */
          688  +    if (s->status == INIT_STATE) {
          689  +#ifdef GZIP
          690  +        if (s->wrap == 2) {
          691  +            strm->adler = crc32(0L, Z_NULL, 0);
          692  +            put_byte(s, 31);
          693  +            put_byte(s, 139);
          694  +            put_byte(s, 8);
          695  +            if (s->gzhead == Z_NULL) {
          696  +                put_byte(s, 0);
          697  +                put_byte(s, 0);
          698  +                put_byte(s, 0);
          699  +                put_byte(s, 0);
          700  +                put_byte(s, 0);
          701  +                put_byte(s, s->level == 9 ? 2 :
          702  +                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
          703  +                             4 : 0));
          704  +                put_byte(s, OS_CODE);
          705  +                s->status = BUSY_STATE;
          706  +            }
          707  +            else {
          708  +                put_byte(s, (s->gzhead->text ? 1 : 0) +
          709  +                            (s->gzhead->hcrc ? 2 : 0) +
          710  +                            (s->gzhead->extra == Z_NULL ? 0 : 4) +
          711  +                            (s->gzhead->name == Z_NULL ? 0 : 8) +
          712  +                            (s->gzhead->comment == Z_NULL ? 0 : 16)
          713  +                        );
          714  +                put_byte(s, (Byte)(s->gzhead->time & 0xff));
          715  +                put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
          716  +                put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
          717  +                put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
          718  +                put_byte(s, s->level == 9 ? 2 :
          719  +                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
          720  +                             4 : 0));
          721  +                put_byte(s, s->gzhead->os & 0xff);
          722  +                if (s->gzhead->extra != Z_NULL) {
          723  +                    put_byte(s, s->gzhead->extra_len & 0xff);
          724  +                    put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
          725  +                }
          726  +                if (s->gzhead->hcrc)
          727  +                    strm->adler = crc32(strm->adler, s->pending_buf,
          728  +                                        s->pending);
          729  +                s->gzindex = 0;
          730  +                s->status = EXTRA_STATE;
          731  +            }
          732  +        }
          733  +        else
          734  +#endif
          735  +        {
          736  +            uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
          737  +            uInt level_flags;
          738  +
          739  +            if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
          740  +                level_flags = 0;
          741  +            else if (s->level < 6)
          742  +                level_flags = 1;
          743  +            else if (s->level == 6)
          744  +                level_flags = 2;
          745  +            else
          746  +                level_flags = 3;
          747  +            header |= (level_flags << 6);
          748  +            if (s->strstart != 0) header |= PRESET_DICT;
          749  +            header += 31 - (header % 31);
          750  +
          751  +            s->status = BUSY_STATE;
          752  +            putShortMSB(s, header);
          753  +
          754  +            /* Save the adler32 of the preset dictionary: */
          755  +            if (s->strstart != 0) {
          756  +                putShortMSB(s, (uInt)(strm->adler >> 16));
          757  +                putShortMSB(s, (uInt)(strm->adler & 0xffff));
          758  +            }
          759  +            strm->adler = adler32(0L, Z_NULL, 0);
          760  +        }
          761  +    }
          762  +#ifdef GZIP
          763  +    if (s->status == EXTRA_STATE) {
          764  +        if (s->gzhead->extra != Z_NULL) {
          765  +            uInt beg = s->pending;  /* start of bytes to update crc */
          766  +
          767  +            while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
          768  +                if (s->pending == s->pending_buf_size) {
          769  +                    if (s->gzhead->hcrc && s->pending > beg)
          770  +                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
          771  +                                            s->pending - beg);
          772  +                    flush_pending(strm);
          773  +                    beg = s->pending;
          774  +                    if (s->pending == s->pending_buf_size)
          775  +                        break;
          776  +                }
          777  +                put_byte(s, s->gzhead->extra[s->gzindex]);
          778  +                s->gzindex++;
          779  +            }
          780  +            if (s->gzhead->hcrc && s->pending > beg)
          781  +                strm->adler = crc32(strm->adler, s->pending_buf + beg,
          782  +                                    s->pending - beg);
          783  +            if (s->gzindex == s->gzhead->extra_len) {
          784  +                s->gzindex = 0;
          785  +                s->status = NAME_STATE;
          786  +            }
          787  +        }
          788  +        else
          789  +            s->status = NAME_STATE;
          790  +    }
          791  +    if (s->status == NAME_STATE) {
          792  +        if (s->gzhead->name != Z_NULL) {
          793  +            uInt beg = s->pending;  /* start of bytes to update crc */
          794  +            int val;
          795  +
          796  +            do {
          797  +                if (s->pending == s->pending_buf_size) {
          798  +                    if (s->gzhead->hcrc && s->pending > beg)
          799  +                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
          800  +                                            s->pending - beg);
          801  +                    flush_pending(strm);
          802  +                    beg = s->pending;
          803  +                    if (s->pending == s->pending_buf_size) {
          804  +                        val = 1;
          805  +                        break;
          806  +                    }
          807  +                }
          808  +                val = s->gzhead->name[s->gzindex++];
          809  +                put_byte(s, val);
          810  +            } while (val != 0);
          811  +            if (s->gzhead->hcrc && s->pending > beg)
          812  +                strm->adler = crc32(strm->adler, s->pending_buf + beg,
          813  +                                    s->pending - beg);
          814  +            if (val == 0) {
          815  +                s->gzindex = 0;
          816  +                s->status = COMMENT_STATE;
          817  +            }
          818  +        }
          819  +        else
          820  +            s->status = COMMENT_STATE;
          821  +    }
          822  +    if (s->status == COMMENT_STATE) {
          823  +        if (s->gzhead->comment != Z_NULL) {
          824  +            uInt beg = s->pending;  /* start of bytes to update crc */
          825  +            int val;
          826  +
          827  +            do {
          828  +                if (s->pending == s->pending_buf_size) {
          829  +                    if (s->gzhead->hcrc && s->pending > beg)
          830  +                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
          831  +                                            s->pending - beg);
          832  +                    flush_pending(strm);
          833  +                    beg = s->pending;
          834  +                    if (s->pending == s->pending_buf_size) {
          835  +                        val = 1;
          836  +                        break;
          837  +                    }
          838  +                }
          839  +                val = s->gzhead->comment[s->gzindex++];
          840  +                put_byte(s, val);
          841  +            } while (val != 0);
          842  +            if (s->gzhead->hcrc && s->pending > beg)
          843  +                strm->adler = crc32(strm->adler, s->pending_buf + beg,
          844  +                                    s->pending - beg);
          845  +            if (val == 0)
          846  +                s->status = HCRC_STATE;
          847  +        }
          848  +        else
          849  +            s->status = HCRC_STATE;
          850  +    }
          851  +    if (s->status == HCRC_STATE) {
          852  +        if (s->gzhead->hcrc) {
          853  +            if (s->pending + 2 > s->pending_buf_size)
          854  +                flush_pending(strm);
          855  +            if (s->pending + 2 <= s->pending_buf_size) {
          856  +                put_byte(s, (Byte)(strm->adler & 0xff));
          857  +                put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
          858  +                strm->adler = crc32(0L, Z_NULL, 0);
          859  +                s->status = BUSY_STATE;
          860  +            }
          861  +        }
          862  +        else
          863  +            s->status = BUSY_STATE;
          864  +    }
          865  +#endif
          866  +
          867  +    /* Flush as much pending output as possible */
          868  +    if (s->pending != 0) {
          869  +        flush_pending(strm);
          870  +        if (strm->avail_out == 0) {
          871  +            /* Since avail_out is 0, deflate will be called again with
          872  +             * more output space, but possibly with both pending and
          873  +             * avail_in equal to zero. There won't be anything to do,
          874  +             * but this is not an error situation so make sure we
          875  +             * return OK instead of BUF_ERROR at next call of deflate:
          876  +             */
          877  +            s->last_flush = -1;
          878  +            return Z_OK;
          879  +        }
          880  +
          881  +    /* Make sure there is something to do and avoid duplicate consecutive
          882  +     * flushes. For repeated and useless calls with Z_FINISH, we keep
          883  +     * returning Z_STREAM_END instead of Z_BUF_ERROR.
          884  +     */
          885  +    } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
          886  +               flush != Z_FINISH) {
          887  +        ERR_RETURN(strm, Z_BUF_ERROR);
          888  +    }
          889  +
          890  +    /* User must not provide more input after the first FINISH: */
          891  +    if (s->status == FINISH_STATE && strm->avail_in != 0) {
          892  +        ERR_RETURN(strm, Z_BUF_ERROR);
          893  +    }
          894  +
          895  +    /* Start a new block or continue the current one.
          896  +     */
          897  +    if (strm->avail_in != 0 || s->lookahead != 0 ||
          898  +        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
          899  +        block_state bstate;
          900  +
          901  +        bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :
          902  +                    (s->strategy == Z_RLE ? deflate_rle(s, flush) :
          903  +                        (*(configuration_table[s->level].func))(s, flush));
          904  +
          905  +        if (bstate == finish_started || bstate == finish_done) {
          906  +            s->status = FINISH_STATE;
          907  +        }
          908  +        if (bstate == need_more || bstate == finish_started) {
          909  +            if (strm->avail_out == 0) {
          910  +                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
          911  +            }
          912  +            return Z_OK;
          913  +            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
          914  +             * of deflate should use the same flush parameter to make sure
          915  +             * that the flush is complete. So we don't have to output an
          916  +             * empty block here, this will be done at next call. This also
          917  +             * ensures that for a very small output buffer, we emit at most
          918  +             * one empty block.
          919  +             */
          920  +        }
          921  +        if (bstate == block_done) {
          922  +            if (flush == Z_PARTIAL_FLUSH) {
          923  +                _tr_align(s);
          924  +            } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
          925  +                _tr_stored_block(s, (char*)0, 0L, 0);
          926  +                /* For a full flush, this empty block will be recognized
          927  +                 * as a special marker by inflate_sync().
          928  +                 */
          929  +                if (flush == Z_FULL_FLUSH) {
          930  +                    CLEAR_HASH(s);             /* forget history */
          931  +                    if (s->lookahead == 0) {
          932  +                        s->strstart = 0;
          933  +                        s->block_start = 0L;
          934  +                        s->insert = 0;
          935  +                    }
          936  +                }
          937  +            }
          938  +            flush_pending(strm);
          939  +            if (strm->avail_out == 0) {
          940  +              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
          941  +              return Z_OK;
          942  +            }
          943  +        }
          944  +    }
          945  +    Assert(strm->avail_out > 0, "bug2");
          946  +
          947  +    if (flush != Z_FINISH) return Z_OK;
          948  +    if (s->wrap <= 0) return Z_STREAM_END;
          949  +
          950  +    /* Write the trailer */
          951  +#ifdef GZIP
          952  +    if (s->wrap == 2) {
          953  +        put_byte(s, (Byte)(strm->adler & 0xff));
          954  +        put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
          955  +        put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
          956  +        put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
          957  +        put_byte(s, (Byte)(strm->total_in & 0xff));
          958  +        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
          959  +        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
          960  +        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
          961  +    }
          962  +    else
          963  +#endif
          964  +    {
          965  +        putShortMSB(s, (uInt)(strm->adler >> 16));
          966  +        putShortMSB(s, (uInt)(strm->adler & 0xffff));
          967  +    }
          968  +    flush_pending(strm);
          969  +    /* If avail_out is zero, the application will call deflate again
          970  +     * to flush the rest.
          971  +     */
          972  +    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
          973  +    return s->pending != 0 ? Z_OK : Z_STREAM_END;
          974  +}
          975  +
          976  +/* ========================================================================= */
          977  +int ZEXPORT deflateEnd (strm)
          978  +    z_streamp strm;
          979  +{
          980  +    int status;
          981  +
          982  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          983  +
          984  +    status = strm->state->status;
          985  +    if (status != INIT_STATE &&
          986  +        status != EXTRA_STATE &&
          987  +        status != NAME_STATE &&
          988  +        status != COMMENT_STATE &&
          989  +        status != HCRC_STATE &&
          990  +        status != BUSY_STATE &&
          991  +        status != FINISH_STATE) {
          992  +      return Z_STREAM_ERROR;
          993  +    }
          994  +
          995  +    /* Deallocate in reverse order of allocations: */
          996  +    TRY_FREE(strm, strm->state->pending_buf);
          997  +    TRY_FREE(strm, strm->state->head);
          998  +    TRY_FREE(strm, strm->state->prev);
          999  +    TRY_FREE(strm, strm->state->window);
         1000  +
         1001  +    ZFREE(strm, strm->state);
         1002  +    strm->state = Z_NULL;
         1003  +
         1004  +    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
         1005  +}
         1006  +
         1007  +/* =========================================================================
         1008  + * Copy the source state to the destination state.
         1009  + * To simplify the source, this is not supported for 16-bit MSDOS (which
         1010  + * doesn't have enough memory anyway to duplicate compression states).
         1011  + */
         1012  +int ZEXPORT deflateCopy (dest, source)
         1013  +    z_streamp dest;
         1014  +    z_streamp source;
         1015  +{
         1016  +#ifdef MAXSEG_64K
         1017  +    return Z_STREAM_ERROR;
         1018  +#else
         1019  +    deflate_state *ds;
         1020  +    deflate_state *ss;
         1021  +    ushf *overlay;
         1022  +
         1023  +
         1024  +    if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
         1025  +        return Z_STREAM_ERROR;
         1026  +    }
         1027  +
         1028  +    ss = source->state;
         1029  +
         1030  +    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
         1031  +
         1032  +    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
         1033  +    if (ds == Z_NULL) return Z_MEM_ERROR;
         1034  +    dest->state = (struct internal_state FAR *) ds;
         1035  +    zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
         1036  +    ds->strm = dest;
         1037  +
         1038  +    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
         1039  +    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));
         1040  +    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));
         1041  +    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
         1042  +    ds->pending_buf = (uchf *) overlay;
         1043  +
         1044  +    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
         1045  +        ds->pending_buf == Z_NULL) {
         1046  +        deflateEnd (dest);
         1047  +        return Z_MEM_ERROR;
         1048  +    }
         1049  +    /* following zmemcpy do not work for 16-bit MSDOS */
         1050  +    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
         1051  +    zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
         1052  +    zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
         1053  +    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
         1054  +
         1055  +    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
         1056  +    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
         1057  +    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
         1058  +
         1059  +    ds->l_desc.dyn_tree = ds->dyn_ltree;
         1060  +    ds->d_desc.dyn_tree = ds->dyn_dtree;
         1061  +    ds->bl_desc.dyn_tree = ds->bl_tree;
         1062  +
         1063  +    return Z_OK;
         1064  +#endif /* MAXSEG_64K */
         1065  +}
         1066  +
         1067  +/* ===========================================================================
         1068  + * Read a new buffer from the current input stream, update the adler32
         1069  + * and total number of bytes read.  All deflate() input goes through
         1070  + * this function so some applications may wish to modify it to avoid
         1071  + * allocating a large strm->next_in buffer and copying from it.
         1072  + * (See also flush_pending()).
         1073  + */
         1074  +local int read_buf(strm, buf, size)
         1075  +    z_streamp strm;
         1076  +    Bytef *buf;
         1077  +    unsigned size;
         1078  +{
         1079  +    unsigned len = strm->avail_in;
         1080  +
         1081  +    if (len > size) len = size;
         1082  +    if (len == 0) return 0;
         1083  +
         1084  +    strm->avail_in  -= len;
         1085  +
         1086  +    zmemcpy(buf, strm->next_in, len);
         1087  +    if (strm->state->wrap == 1) {
         1088  +        strm->adler = adler32(strm->adler, buf, len);
         1089  +    }
         1090  +#ifdef GZIP
         1091  +    else if (strm->state->wrap == 2) {
         1092  +        strm->adler = crc32(strm->adler, buf, len);
         1093  +    }
         1094  +#endif
         1095  +    strm->next_in  += len;
         1096  +    strm->total_in += len;
         1097  +
         1098  +    return (int)len;
         1099  +}
         1100  +
         1101  +/* ===========================================================================
         1102  + * Initialize the "longest match" routines for a new zlib stream
         1103  + */
         1104  +local void lm_init (s)
         1105  +    deflate_state *s;
         1106  +{
         1107  +    s->window_size = (ulg)2L*s->w_size;
         1108  +
         1109  +    CLEAR_HASH(s);
         1110  +
         1111  +    /* Set the default configuration parameters:
         1112  +     */
         1113  +    s->max_lazy_match   = configuration_table[s->level].max_lazy;
         1114  +    s->good_match       = configuration_table[s->level].good_length;
         1115  +    s->nice_match       = configuration_table[s->level].nice_length;
         1116  +    s->max_chain_length = configuration_table[s->level].max_chain;
         1117  +
         1118  +    s->strstart = 0;
         1119  +    s->block_start = 0L;
         1120  +    s->lookahead = 0;
         1121  +    s->insert = 0;
         1122  +    s->match_length = s->prev_length = MIN_MATCH-1;
         1123  +    s->match_available = 0;
         1124  +    s->ins_h = 0;
         1125  +#ifndef FASTEST
         1126  +#ifdef ASMV
         1127  +    match_init(); /* initialize the asm code */
         1128  +#endif
         1129  +#endif
         1130  +}
         1131  +
         1132  +#ifndef FASTEST
         1133  +/* ===========================================================================
         1134  + * Set match_start to the longest match starting at the given string and
         1135  + * return its length. Matches shorter or equal to prev_length are discarded,
         1136  + * in which case the result is equal to prev_length and match_start is
         1137  + * garbage.
         1138  + * IN assertions: cur_match is the head of the hash chain for the current
         1139  + *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
         1140  + * OUT assertion: the match length is not greater than s->lookahead.
         1141  + */
         1142  +#ifndef ASMV
         1143  +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
         1144  + * match.S. The code will be functionally equivalent.
         1145  + */
         1146  +local uInt longest_match(s, cur_match)
         1147  +    deflate_state *s;
         1148  +    IPos cur_match;                             /* current match */
         1149  +{
         1150  +    unsigned chain_length = s->max_chain_length;/* max hash chain length */
         1151  +    register Bytef *scan = s->window + s->strstart; /* current string */
         1152  +    register Bytef *match;                       /* matched string */
         1153  +    register int len;                           /* length of current match */
         1154  +    int best_len = s->prev_length;              /* best match length so far */
         1155  +    int nice_match = s->nice_match;             /* stop if match long enough */
         1156  +    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
         1157  +        s->strstart - (IPos)MAX_DIST(s) : NIL;
         1158  +    /* Stop when cur_match becomes <= limit. To simplify the code,
         1159  +     * we prevent matches with the string of window index 0.
         1160  +     */
         1161  +    Posf *prev = s->prev;
         1162  +    uInt wmask = s->w_mask;
         1163  +
         1164  +#ifdef UNALIGNED_OK
         1165  +    /* Compare two bytes at a time. Note: this is not always beneficial.
         1166  +     * Try with and without -DUNALIGNED_OK to check.
         1167  +     */
         1168  +    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
         1169  +    register ush scan_start = *(ushf*)scan;
         1170  +    register ush scan_end   = *(ushf*)(scan+best_len-1);
         1171  +#else
         1172  +    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
         1173  +    register Byte scan_end1  = scan[best_len-1];
         1174  +    register Byte scan_end   = scan[best_len];
         1175  +#endif
         1176  +
         1177  +    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
         1178  +     * It is easy to get rid of this optimization if necessary.
         1179  +     */
         1180  +    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
         1181  +
         1182  +    /* Do not waste too much time if we already have a good match: */
         1183  +    if (s->prev_length >= s->good_match) {
         1184  +        chain_length >>= 2;
         1185  +    }
         1186  +    /* Do not look for matches beyond the end of the input. This is necessary
         1187  +     * to make deflate deterministic.
         1188  +     */
         1189  +    if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
         1190  +
         1191  +    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
         1192  +
         1193  +    do {
         1194  +        Assert(cur_match < s->strstart, "no future");
         1195  +        match = s->window + cur_match;
         1196  +
         1197  +        /* Skip to next match if the match length cannot increase
         1198  +         * or if the match length is less than 2.  Note that the checks below
         1199  +         * for insufficient lookahead only occur occasionally for performance
         1200  +         * reasons.  Therefore uninitialized memory will be accessed, and
         1201  +         * conditional jumps will be made that depend on those values.
         1202  +         * However the length of the match is limited to the lookahead, so
         1203  +         * the output of deflate is not affected by the uninitialized values.
         1204  +         */
         1205  +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
         1206  +        /* This code assumes sizeof(unsigned short) == 2. Do not use
         1207  +         * UNALIGNED_OK if your compiler uses a different size.
         1208  +         */
         1209  +        if (*(ushf*)(match+best_len-1) != scan_end ||
         1210  +            *(ushf*)match != scan_start) continue;
         1211  +
         1212  +        /* It is not necessary to compare scan[2] and match[2] since they are
         1213  +         * always equal when the other bytes match, given that the hash keys
         1214  +         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
         1215  +         * strstart+3, +5, ... up to strstart+257. We check for insufficient
         1216  +         * lookahead only every 4th comparison; the 128th check will be made
         1217  +         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
         1218  +         * necessary to put more guard bytes at the end of the window, or
         1219  +         * to check more often for insufficient lookahead.
         1220  +         */
         1221  +        Assert(scan[2] == match[2], "scan[2]?");
         1222  +        scan++, match++;
         1223  +        do {
         1224  +        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         1225  +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         1226  +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         1227  +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         1228  +                 scan < strend);
         1229  +        /* The funny "do {}" generates better code on most compilers */
         1230  +
         1231  +        /* Here, scan <= window+strstart+257 */
         1232  +        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
         1233  +        if (*scan == *match) scan++;
         1234  +
         1235  +        len = (MAX_MATCH - 1) - (int)(strend-scan);
         1236  +        scan = strend - (MAX_MATCH-1);
         1237  +
         1238  +#else /* UNALIGNED_OK */
         1239  +
         1240  +        if (match[best_len]   != scan_end  ||
         1241  +            match[best_len-1] != scan_end1 ||
         1242  +            *match            != *scan     ||
         1243  +            *++match          != scan[1])      continue;
         1244  +
         1245  +        /* The check at best_len-1 can be removed because it will be made
         1246  +         * again later. (This heuristic is not always a win.)
         1247  +         * It is not necessary to compare scan[2] and match[2] since they
         1248  +         * are always equal when the other bytes match, given that
         1249  +         * the hash keys are equal and that HASH_BITS >= 8.
         1250  +         */
         1251  +        scan += 2, match++;
         1252  +        Assert(*scan == *match, "match[2]?");
         1253  +
         1254  +        /* We check for insufficient lookahead only every 8th comparison;
         1255  +         * the 256th check will be made at strstart+258.
         1256  +         */
         1257  +        do {
         1258  +        } while (*++scan == *++match && *++scan == *++match &&
         1259  +                 *++scan == *++match && *++scan == *++match &&
         1260  +                 *++scan == *++match && *++scan == *++match &&
         1261  +                 *++scan == *++match && *++scan == *++match &&
         1262  +                 scan < strend);
         1263  +
         1264  +        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
         1265  +
         1266  +        len = MAX_MATCH - (int)(strend - scan);
         1267  +        scan = strend - MAX_MATCH;
         1268  +
         1269  +#endif /* UNALIGNED_OK */
         1270  +
         1271  +        if (len > best_len) {
         1272  +            s->match_start = cur_match;
         1273  +            best_len = len;
         1274  +            if (len >= nice_match) break;
         1275  +#ifdef UNALIGNED_OK
         1276  +            scan_end = *(ushf*)(scan+best_len-1);
         1277  +#else
         1278  +            scan_end1  = scan[best_len-1];
         1279  +            scan_end   = scan[best_len];
         1280  +#endif
         1281  +        }
         1282  +    } while ((cur_match = prev[cur_match & wmask]) > limit
         1283  +             && --chain_length != 0);
         1284  +
         1285  +    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
         1286  +    return s->lookahead;
         1287  +}
         1288  +#endif /* ASMV */
         1289  +
         1290  +#else /* FASTEST */
         1291  +
         1292  +/* ---------------------------------------------------------------------------
         1293  + * Optimized version for FASTEST only
         1294  + */
         1295  +local uInt longest_match(s, cur_match)
         1296  +    deflate_state *s;
         1297  +    IPos cur_match;                             /* current match */
         1298  +{
         1299  +    register Bytef *scan = s->window + s->strstart; /* current string */
         1300  +    register Bytef *match;                       /* matched string */
         1301  +    register int len;                           /* length of current match */
         1302  +    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
         1303  +
         1304  +    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
         1305  +     * It is easy to get rid of this optimization if necessary.
         1306  +     */
         1307  +    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
         1308  +
         1309  +    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
         1310  +
         1311  +    Assert(cur_match < s->strstart, "no future");
         1312  +
         1313  +    match = s->window + cur_match;
         1314  +
         1315  +    /* Return failure if the match length is less than 2:
         1316  +     */
         1317  +    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
         1318  +
         1319  +    /* The check at best_len-1 can be removed because it will be made
         1320  +     * again later. (This heuristic is not always a win.)
         1321  +     * It is not necessary to compare scan[2] and match[2] since they
         1322  +     * are always equal when the other bytes match, given that
         1323  +     * the hash keys are equal and that HASH_BITS >= 8.
         1324  +     */
         1325  +    scan += 2, match += 2;
         1326  +    Assert(*scan == *match, "match[2]?");
         1327  +
         1328  +    /* We check for insufficient lookahead only every 8th comparison;
         1329  +     * the 256th check will be made at strstart+258.
         1330  +     */
         1331  +    do {
         1332  +    } while (*++scan == *++match && *++scan == *++match &&
         1333  +             *++scan == *++match && *++scan == *++match &&
         1334  +             *++scan == *++match && *++scan == *++match &&
         1335  +             *++scan == *++match && *++scan == *++match &&
         1336  +             scan < strend);
         1337  +
         1338  +    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
         1339  +
         1340  +    len = MAX_MATCH - (int)(strend - scan);
         1341  +
         1342  +    if (len < MIN_MATCH) return MIN_MATCH - 1;
         1343  +
         1344  +    s->match_start = cur_match;
         1345  +    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
         1346  +}
         1347  +
         1348  +#endif /* FASTEST */
         1349  +
         1350  +#ifdef DEBUG
         1351  +/* ===========================================================================
         1352  + * Check that the match at match_start is indeed a match.
         1353  + */
         1354  +local void check_match(s, start, match, length)
         1355  +    deflate_state *s;
         1356  +    IPos start, match;
         1357  +    int length;
         1358  +{
         1359  +    /* check that the match is indeed a match */
         1360  +    if (zmemcmp(s->window + match,
         1361  +                s->window + start, length) != EQUAL) {
         1362  +        fprintf(stderr, " start %u, match %u, length %d\n",
         1363  +                start, match, length);
         1364  +        do {
         1365  +            fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
         1366  +        } while (--length != 0);
         1367  +        z_error("invalid match");
         1368  +    }
         1369  +    if (z_verbose > 1) {
         1370  +        fprintf(stderr,"\\[%d,%d]", start-match, length);
         1371  +        do { putc(s->window[start++], stderr); } while (--length != 0);
         1372  +    }
         1373  +}
         1374  +#else
         1375  +#  define check_match(s, start, match, length)
         1376  +#endif /* DEBUG */
         1377  +
         1378  +/* ===========================================================================
         1379  + * Fill the window when the lookahead becomes insufficient.
         1380  + * Updates strstart and lookahead.
         1381  + *
         1382  + * IN assertion: lookahead < MIN_LOOKAHEAD
         1383  + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
         1384  + *    At least one byte has been read, or avail_in == 0; reads are
         1385  + *    performed for at least two bytes (required for the zip translate_eol
         1386  + *    option -- not supported here).
         1387  + */
         1388  +local void fill_window(s)
         1389  +    deflate_state *s;
         1390  +{
         1391  +    register unsigned n, m;
         1392  +    register Posf *p;
         1393  +    unsigned more;    /* Amount of free space at the end of the window. */
         1394  +    uInt wsize = s->w_size;
         1395  +
         1396  +    Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
         1397  +
         1398  +    do {
         1399  +        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
         1400  +
         1401  +        /* Deal with !@#$% 64K limit: */
         1402  +        if (sizeof(int) <= 2) {
         1403  +            if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
         1404  +                more = wsize;
         1405  +
         1406  +            } else if (more == (unsigned)(-1)) {
         1407  +                /* Very unlikely, but possible on 16 bit machine if
         1408  +                 * strstart == 0 && lookahead == 1 (input done a byte at time)
         1409  +                 */
         1410  +                more--;
         1411  +            }
         1412  +        }
         1413  +
         1414  +        /* If the window is almost full and there is insufficient lookahead,
         1415  +         * move the upper half to the lower one to make room in the upper half.
         1416  +         */
         1417  +        if (s->strstart >= wsize+MAX_DIST(s)) {
         1418  +
         1419  +            zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
         1420  +            s->match_start -= wsize;
         1421  +            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */
         1422  +            s->block_start -= (long) wsize;
         1423  +
         1424  +            /* Slide the hash table (could be avoided with 32 bit values
         1425  +               at the expense of memory usage). We slide even when level == 0
         1426  +               to keep the hash table consistent if we switch back to level > 0
         1427  +               later. (Using level 0 permanently is not an optimal usage of
         1428  +               zlib, so we don't care about this pathological case.)
         1429  +             */
         1430  +            n = s->hash_size;
         1431  +            p = &s->head[n];
         1432  +            do {
         1433  +                m = *--p;
         1434  +                *p = (Pos)(m >= wsize ? m-wsize : NIL);
         1435  +            } while (--n);
         1436  +
         1437  +            n = wsize;
         1438  +#ifndef FASTEST
         1439  +            p = &s->prev[n];
         1440  +            do {
         1441  +                m = *--p;
         1442  +                *p = (Pos)(m >= wsize ? m-wsize : NIL);
         1443  +                /* If n is not on any hash chain, prev[n] is garbage but
         1444  +                 * its value will never be used.
         1445  +                 */
         1446  +            } while (--n);
         1447  +#endif
         1448  +            more += wsize;
         1449  +        }
         1450  +        if (s->strm->avail_in == 0) break;
         1451  +
         1452  +        /* If there was no sliding:
         1453  +         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
         1454  +         *    more == window_size - lookahead - strstart
         1455  +         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
         1456  +         * => more >= window_size - 2*WSIZE + 2
         1457  +         * In the BIG_MEM or MMAP case (not yet supported),
         1458  +         *   window_size == input_size + MIN_LOOKAHEAD  &&
         1459  +         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
         1460  +         * Otherwise, window_size == 2*WSIZE so more >= 2.
         1461  +         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
         1462  +         */
         1463  +        Assert(more >= 2, "more < 2");
         1464  +
         1465  +        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
         1466  +        s->lookahead += n;
         1467  +
         1468  +        /* Initialize the hash value now that we have some input: */
         1469  +        if (s->lookahead + s->insert >= MIN_MATCH) {
         1470  +            uInt str = s->strstart - s->insert;
         1471  +            s->ins_h = s->window[str];
         1472  +            UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
         1473  +#if MIN_MATCH != 3
         1474  +            Call UPDATE_HASH() MIN_MATCH-3 more times
         1475  +#endif
         1476  +            while (s->insert) {
         1477  +                UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
         1478  +#ifndef FASTEST
         1479  +                s->prev[str & s->w_mask] = s->head[s->ins_h];
         1480  +#endif
         1481  +                s->head[s->ins_h] = (Pos)str;
         1482  +                str++;
         1483  +                s->insert--;
         1484  +                if (s->lookahead + s->insert < MIN_MATCH)
         1485  +                    break;
         1486  +            }
         1487  +        }
         1488  +        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
         1489  +         * but this is not important since only literal bytes will be emitted.
         1490  +         */
         1491  +
         1492  +    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
         1493  +
         1494  +    /* If the WIN_INIT bytes after the end of the current data have never been
         1495  +     * written, then zero those bytes in order to avoid memory check reports of
         1496  +     * the use of uninitialized (or uninitialised as Julian writes) bytes by
         1497  +     * the longest match routines.  Update the high water mark for the next
         1498  +     * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
         1499  +     * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
         1500  +     */
         1501  +    if (s->high_water < s->window_size) {
         1502  +        ulg curr = s->strstart + (ulg)(s->lookahead);
         1503  +        ulg init;
         1504  +
         1505  +        if (s->high_water < curr) {
         1506  +            /* Previous high water mark below current data -- zero WIN_INIT
         1507  +             * bytes or up to end of window, whichever is less.
         1508  +             */
         1509  +            init = s->window_size - curr;
         1510  +            if (init > WIN_INIT)
         1511  +                init = WIN_INIT;
         1512  +            zmemzero(s->window + curr, (unsigned)init);
         1513  +            s->high_water = curr + init;
         1514  +        }
         1515  +        else if (s->high_water < (ulg)curr + WIN_INIT) {
         1516  +            /* High water mark at or above current data, but below current data
         1517  +             * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
         1518  +             * to end of window, whichever is less.
         1519  +             */
         1520  +            init = (ulg)curr + WIN_INIT - s->high_water;
         1521  +            if (init > s->window_size - s->high_water)
         1522  +                init = s->window_size - s->high_water;
         1523  +            zmemzero(s->window + s->high_water, (unsigned)init);
         1524  +            s->high_water += init;
         1525  +        }
         1526  +    }
         1527  +
         1528  +    Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
         1529  +           "not enough room for search");
         1530  +}
         1531  +
         1532  +/* ===========================================================================
         1533  + * Flush the current block, with given end-of-file flag.
         1534  + * IN assertion: strstart is set to the end of the current match.
         1535  + */
         1536  +#define FLUSH_BLOCK_ONLY(s, last) { \
         1537  +   _tr_flush_block(s, (s->block_start >= 0L ? \
         1538  +                   (charf *)&s->window[(unsigned)s->block_start] : \
         1539  +                   (charf *)Z_NULL), \
         1540  +                (ulg)((long)s->strstart - s->block_start), \
         1541  +                (last)); \
         1542  +   s->block_start = s->strstart; \
         1543  +   flush_pending(s->strm); \
         1544  +   Tracev((stderr,"[FLUSH]")); \
         1545  +}
         1546  +
         1547  +/* Same but force premature exit if necessary. */
         1548  +#define FLUSH_BLOCK(s, last) { \
         1549  +   FLUSH_BLOCK_ONLY(s, last); \
         1550  +   if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \
         1551  +}
         1552  +
         1553  +/* ===========================================================================
         1554  + * Copy without compression as much as possible from the input stream, return
         1555  + * the current block state.
         1556  + * This function does not insert new strings in the dictionary since
         1557  + * uncompressible data is probably not useful. This function is used
         1558  + * only for the level=0 compression option.
         1559  + * NOTE: this function should be optimized to avoid extra copying from
         1560  + * window to pending_buf.
         1561  + */
         1562  +local block_state deflate_stored(s, flush)
         1563  +    deflate_state *s;
         1564  +    int flush;
         1565  +{
         1566  +    /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
         1567  +     * to pending_buf_size, and each stored block has a 5 byte header:
         1568  +     */
         1569  +    ulg max_block_size = 0xffff;
         1570  +    ulg max_start;
         1571  +
         1572  +    if (max_block_size > s->pending_buf_size - 5) {
         1573  +        max_block_size = s->pending_buf_size - 5;
         1574  +    }
         1575  +
         1576  +    /* Copy as much as possible from input to output: */
         1577  +    for (;;) {
         1578  +        /* Fill the window as much as possible: */
         1579  +        if (s->lookahead <= 1) {
         1580  +
         1581  +            Assert(s->strstart < s->w_size+MAX_DIST(s) ||
         1582  +                   s->block_start >= (long)s->w_size, "slide too late");
         1583  +
         1584  +            fill_window(s);
         1585  +            if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
         1586  +
         1587  +            if (s->lookahead == 0) break; /* flush the current block */
         1588  +        }
         1589  +        Assert(s->block_start >= 0L, "block gone");
         1590  +
         1591  +        s->strstart += s->lookahead;
         1592  +        s->lookahead = 0;
         1593  +
         1594  +        /* Emit a stored block if pending_buf will be full: */
         1595  +        max_start = s->block_start + max_block_size;
         1596  +        if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
         1597  +            /* strstart == 0 is possible when wraparound on 16-bit machine */
         1598  +            s->lookahead = (uInt)(s->strstart - max_start);
         1599  +            s->strstart = (uInt)max_start;
         1600  +            FLUSH_BLOCK(s, 0);
         1601  +        }
         1602  +        /* Flush if we may have to slide, otherwise block_start may become
         1603  +         * negative and the data will be gone:
         1604  +         */
         1605  +        if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
         1606  +            FLUSH_BLOCK(s, 0);
         1607  +        }
         1608  +    }
         1609  +    s->insert = 0;
         1610  +    if (flush == Z_FINISH) {
         1611  +        FLUSH_BLOCK(s, 1);
         1612  +        return finish_done;
         1613  +    }
         1614  +    if ((long)s->strstart > s->block_start)
         1615  +        FLUSH_BLOCK(s, 0);
         1616  +    return block_done;
         1617  +}
         1618  +
         1619  +/* ===========================================================================
         1620  + * Compress as much as possible from the input stream, return the current
         1621  + * block state.
         1622  + * This function does not perform lazy evaluation of matches and inserts
         1623  + * new strings in the dictionary only for unmatched strings or for short
         1624  + * matches. It is used only for the fast compression options.
         1625  + */
         1626  +local block_state deflate_fast(s, flush)
         1627  +    deflate_state *s;
         1628  +    int flush;
         1629  +{
         1630  +    IPos hash_head;       /* head of the hash chain */
         1631  +    int bflush;           /* set if current block must be flushed */
         1632  +
         1633  +    for (;;) {
         1634  +        /* Make sure that we always have enough lookahead, except
         1635  +         * at the end of the input file. We need MAX_MATCH bytes
         1636  +         * for the next match, plus MIN_MATCH bytes to insert the
         1637  +         * string following the next match.
         1638  +         */
         1639  +        if (s->lookahead < MIN_LOOKAHEAD) {
         1640  +            fill_window(s);
         1641  +            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
         1642  +                return need_more;
         1643  +            }
         1644  +            if (s->lookahead == 0) break; /* flush the current block */
         1645  +        }
         1646  +
         1647  +        /* Insert the string window[strstart .. strstart+2] in the
         1648  +         * dictionary, and set hash_head to the head of the hash chain:
         1649  +         */
         1650  +        hash_head = NIL;
         1651  +        if (s->lookahead >= MIN_MATCH) {
         1652  +            INSERT_STRING(s, s->strstart, hash_head);
         1653  +        }
         1654  +
         1655  +        /* Find the longest match, discarding those <= prev_length.
         1656  +         * At this point we have always match_length < MIN_MATCH
         1657  +         */
         1658  +        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
         1659  +            /* To simplify the code, we prevent matches with the string
         1660  +             * of window index 0 (in particular we have to avoid a match
         1661  +             * of the string with itself at the start of the input file).
         1662  +             */
         1663  +            s->match_length = longest_match (s, hash_head);
         1664  +            /* longest_match() sets match_start */
         1665  +        }
         1666  +        if (s->match_length >= MIN_MATCH) {
         1667  +            check_match(s, s->strstart, s->match_start, s->match_length);
         1668  +
         1669  +            _tr_tally_dist(s, s->strstart - s->match_start,
         1670  +                           s->match_length - MIN_MATCH, bflush);
         1671  +
         1672  +            s->lookahead -= s->match_length;
         1673  +
         1674  +            /* Insert new strings in the hash table only if the match length
         1675  +             * is not too large. This saves time but degrades compression.
         1676  +             */
         1677  +#ifndef FASTEST
         1678  +            if (s->match_length <= s->max_insert_length &&
         1679  +                s->lookahead >= MIN_MATCH) {
         1680  +                s->match_length--; /* string at strstart already in table */
         1681  +                do {
         1682  +                    s->strstart++;
         1683  +                    INSERT_STRING(s, s->strstart, hash_head);
         1684  +                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
         1685  +                     * always MIN_MATCH bytes ahead.
         1686  +                     */
         1687  +                } while (--s->match_length != 0);
         1688  +                s->strstart++;
         1689  +            } else
         1690  +#endif
         1691  +            {
         1692  +                s->strstart += s->match_length;
         1693  +                s->match_length = 0;
         1694  +                s->ins_h = s->window[s->strstart];
         1695  +                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
         1696  +#if MIN_MATCH != 3
         1697  +                Call UPDATE_HASH() MIN_MATCH-3 more times
         1698  +#endif
         1699  +                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
         1700  +                 * matter since it will be recomputed at next deflate call.
         1701  +                 */
         1702  +            }
         1703  +        } else {
         1704  +            /* No match, output a literal byte */
         1705  +            Tracevv((stderr,"%c", s->window[s->strstart]));
         1706  +            _tr_tally_lit (s, s->window[s->strstart], bflush);
         1707  +            s->lookahead--;
         1708  +            s->strstart++;
         1709  +        }
         1710  +        if (bflush) FLUSH_BLOCK(s, 0);
         1711  +    }
         1712  +    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
         1713  +    if (flush == Z_FINISH) {
         1714  +        FLUSH_BLOCK(s, 1);
         1715  +        return finish_done;
         1716  +    }
         1717  +    if (s->last_lit)
         1718  +        FLUSH_BLOCK(s, 0);
         1719  +    return block_done;
         1720  +}
         1721  +
         1722  +#ifndef FASTEST
         1723  +/* ===========================================================================
         1724  + * Same as above, but achieves better compression. We use a lazy
         1725  + * evaluation for matches: a match is finally adopted only if there is
         1726  + * no better match at the next window position.
         1727  + */
         1728  +local block_state deflate_slow(s, flush)
         1729  +    deflate_state *s;
         1730  +    int flush;
         1731  +{
         1732  +    IPos hash_head;          /* head of hash chain */
         1733  +    int bflush;              /* set if current block must be flushed */
         1734  +
         1735  +    /* Process the input block. */
         1736  +    for (;;) {
         1737  +        /* Make sure that we always have enough lookahead, except
         1738  +         * at the end of the input file. We need MAX_MATCH bytes
         1739  +         * for the next match, plus MIN_MATCH bytes to insert the
         1740  +         * string following the next match.
         1741  +         */
         1742  +        if (s->lookahead < MIN_LOOKAHEAD) {
         1743  +            fill_window(s);
         1744  +            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
         1745  +                return need_more;
         1746  +            }
         1747  +            if (s->lookahead == 0) break; /* flush the current block */
         1748  +        }
         1749  +
         1750  +        /* Insert the string window[strstart .. strstart+2] in the
         1751  +         * dictionary, and set hash_head to the head of the hash chain:
         1752  +         */
         1753  +        hash_head = NIL;
         1754  +        if (s->lookahead >= MIN_MATCH) {
         1755  +            INSERT_STRING(s, s->strstart, hash_head);
         1756  +        }
         1757  +
         1758  +        /* Find the longest match, discarding those <= prev_length.
         1759  +         */
         1760  +        s->prev_length = s->match_length, s->prev_match = s->match_start;
         1761  +        s->match_length = MIN_MATCH-1;
         1762  +
         1763  +        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
         1764  +            s->strstart - hash_head <= MAX_DIST(s)) {
         1765  +            /* To simplify the code, we prevent matches with the string
         1766  +             * of window index 0 (in particular we have to avoid a match
         1767  +             * of the string with itself at the start of the input file).
         1768  +             */
         1769  +            s->match_length = longest_match (s, hash_head);
         1770  +            /* longest_match() sets match_start */
         1771  +
         1772  +            if (s->match_length <= 5 && (s->strategy == Z_FILTERED
         1773  +#if TOO_FAR <= 32767
         1774  +                || (s->match_length == MIN_MATCH &&
         1775  +                    s->strstart - s->match_start > TOO_FAR)
         1776  +#endif
         1777  +                )) {
         1778  +
         1779  +                /* If prev_match is also MIN_MATCH, match_start is garbage
         1780  +                 * but we will ignore the current match anyway.
         1781  +                 */
         1782  +                s->match_length = MIN_MATCH-1;
         1783  +            }
         1784  +        }
         1785  +        /* If there was a match at the previous step and the current
         1786  +         * match is not better, output the previous match:
         1787  +         */
         1788  +        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
         1789  +            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
         1790  +            /* Do not insert strings in hash table beyond this. */
         1791  +
         1792  +            check_match(s, s->strstart-1, s->prev_match, s->prev_length);
         1793  +
         1794  +            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
         1795  +                           s->prev_length - MIN_MATCH, bflush);
         1796  +
         1797  +            /* Insert in hash table all strings up to the end of the match.
         1798  +             * strstart-1 and strstart are already inserted. If there is not
         1799  +             * enough lookahead, the last two strings are not inserted in
         1800  +             * the hash table.
         1801  +             */
         1802  +            s->lookahead -= s->prev_length-1;
         1803  +            s->prev_length -= 2;
         1804  +            do {
         1805  +                if (++s->strstart <= max_insert) {
         1806  +                    INSERT_STRING(s, s->strstart, hash_head);
         1807  +                }
         1808  +            } while (--s->prev_length != 0);
         1809  +            s->match_available = 0;
         1810  +            s->match_length = MIN_MATCH-1;
         1811  +            s->strstart++;
         1812  +
         1813  +            if (bflush) FLUSH_BLOCK(s, 0);
         1814  +
         1815  +        } else if (s->match_available) {
         1816  +            /* If there was no match at the previous position, output a
         1817  +             * single literal. If there was a match but the current match
         1818  +             * is longer, truncate the previous match to a single literal.
         1819  +             */
         1820  +            Tracevv((stderr,"%c", s->window[s->strstart-1]));
         1821  +            _tr_tally_lit(s, s->window[s->strstart-1], bflush);
         1822  +            if (bflush) {
         1823  +                FLUSH_BLOCK_ONLY(s, 0);
         1824  +            }
         1825  +            s->strstart++;
         1826  +            s->lookahead--;
         1827  +            if (s->strm->avail_out == 0) return need_more;
         1828  +        } else {
         1829  +            /* There is no previous match to compare with, wait for
         1830  +             * the next step to decide.
         1831  +             */
         1832  +            s->match_available = 1;
         1833  +            s->strstart++;
         1834  +            s->lookahead--;
         1835  +        }
         1836  +    }
         1837  +    Assert (flush != Z_NO_FLUSH, "no flush?");
         1838  +    if (s->match_available) {
         1839  +        Tracevv((stderr,"%c", s->window[s->strstart-1]));
         1840  +        _tr_tally_lit(s, s->window[s->strstart-1], bflush);
         1841  +        s->match_available = 0;
         1842  +    }
         1843  +    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
         1844  +    if (flush == Z_FINISH) {
         1845  +        FLUSH_BLOCK(s, 1);
         1846  +        return finish_done;
         1847  +    }
         1848  +    if (s->last_lit)
         1849  +        FLUSH_BLOCK(s, 0);
         1850  +    return block_done;
         1851  +}
         1852  +#endif /* FASTEST */
         1853  +
         1854  +/* ===========================================================================
         1855  + * For Z_RLE, simply look for runs of bytes, generate matches only of distance
         1856  + * one.  Do not maintain a hash table.  (It will be regenerated if this run of
         1857  + * deflate switches away from Z_RLE.)
         1858  + */
         1859  +local block_state deflate_rle(s, flush)
         1860  +    deflate_state *s;
         1861  +    int flush;
         1862  +{
         1863  +    int bflush;             /* set if current block must be flushed */
         1864  +    uInt prev;              /* byte at distance one to match */
         1865  +    Bytef *scan, *strend;   /* scan goes up to strend for length of run */
         1866  +
         1867  +    for (;;) {
         1868  +        /* Make sure that we always have enough lookahead, except
         1869  +         * at the end of the input file. We need MAX_MATCH bytes
         1870  +         * for the longest run, plus one for the unrolled loop.
         1871  +         */
         1872  +        if (s->lookahead <= MAX_MATCH) {
         1873  +            fill_window(s);
         1874  +            if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
         1875  +                return need_more;
         1876  +            }
         1877  +            if (s->lookahead == 0) break; /* flush the current block */
         1878  +        }
         1879  +
         1880  +        /* See how many times the previous byte repeats */
         1881  +        s->match_length = 0;
         1882  +        if (s->lookahead >= MIN_MATCH && s->strstart > 0) {
         1883  +            scan = s->window + s->strstart - 1;
         1884  +            prev = *scan;
         1885  +            if (prev == *++scan && prev == *++scan && prev == *++scan) {
         1886  +                strend = s->window + s->strstart + MAX_MATCH;
         1887  +                do {
         1888  +                } while (prev == *++scan && prev == *++scan &&
         1889  +                         prev == *++scan && prev == *++scan &&
         1890  +                         prev == *++scan && prev == *++scan &&
         1891  +                         prev == *++scan && prev == *++scan &&
         1892  +                         scan < strend);
         1893  +                s->match_length = MAX_MATCH - (int)(strend - scan);
         1894  +                if (s->match_length > s->lookahead)
         1895  +                    s->match_length = s->lookahead;
         1896  +            }
         1897  +            Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
         1898  +        }
         1899  +
         1900  +        /* Emit match if have run of MIN_MATCH or longer, else emit literal */
         1901  +        if (s->match_length >= MIN_MATCH) {
         1902  +            check_match(s, s->strstart, s->strstart - 1, s->match_length);
         1903  +
         1904  +            _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
         1905  +
         1906  +            s->lookahead -= s->match_length;
         1907  +            s->strstart += s->match_length;
         1908  +            s->match_length = 0;
         1909  +        } else {
         1910  +            /* No match, output a literal byte */
         1911  +            Tracevv((stderr,"%c", s->window[s->strstart]));
         1912  +            _tr_tally_lit (s, s->window[s->strstart], bflush);
         1913  +            s->lookahead--;
         1914  +            s->strstart++;
         1915  +        }
         1916  +        if (bflush) FLUSH_BLOCK(s, 0);
         1917  +    }
         1918  +    s->insert = 0;
         1919  +    if (flush == Z_FINISH) {
         1920  +        FLUSH_BLOCK(s, 1);
         1921  +        return finish_done;
         1922  +    }
         1923  +    if (s->last_lit)
         1924  +        FLUSH_BLOCK(s, 0);
         1925  +    return block_done;
         1926  +}
         1927  +
         1928  +/* ===========================================================================
         1929  + * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
         1930  + * (It will be regenerated if this run of deflate switches away from Huffman.)
         1931  + */
         1932  +local block_state deflate_huff(s, flush)
         1933  +    deflate_state *s;
         1934  +    int flush;
         1935  +{
         1936  +    int bflush;             /* set if current block must be flushed */
         1937  +
         1938  +    for (;;) {
         1939  +        /* Make sure that we have a literal to write. */
         1940  +        if (s->lookahead == 0) {
         1941  +            fill_window(s);
         1942  +            if (s->lookahead == 0) {
         1943  +                if (flush == Z_NO_FLUSH)
         1944  +                    return need_more;
         1945  +                break;      /* flush the current block */
         1946  +            }
         1947  +        }
         1948  +
         1949  +        /* Output a literal byte */
         1950  +        s->match_length = 0;
         1951  +        Tracevv((stderr,"%c", s->window[s->strstart]));
         1952  +        _tr_tally_lit (s, s->window[s->strstart], bflush);
         1953  +        s->lookahead--;
         1954  +        s->strstart++;
         1955  +        if (bflush) FLUSH_BLOCK(s, 0);
         1956  +    }
         1957  +    s->insert = 0;
         1958  +    if (flush == Z_FINISH) {
         1959  +        FLUSH_BLOCK(s, 1);
         1960  +        return finish_done;
         1961  +    }
         1962  +    if (s->last_lit)
         1963  +        FLUSH_BLOCK(s, 0);
         1964  +    return block_done;
         1965  +}

Added compat/zlib/deflate.h.

            1  +/* deflate.h -- internal compression state
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* @(#) $Id$ */
           12  +
           13  +#ifndef DEFLATE_H
           14  +#define DEFLATE_H
           15  +
           16  +#include "zutil.h"
           17  +
           18  +/* define NO_GZIP when compiling if you want to disable gzip header and
           19  +   trailer creation by deflate().  NO_GZIP would be used to avoid linking in
           20  +   the crc code when it is not needed.  For shared libraries, gzip encoding
           21  +   should be left enabled. */
           22  +#ifndef NO_GZIP
           23  +#  define GZIP
           24  +#endif
           25  +
           26  +/* ===========================================================================
           27  + * Internal compression state.
           28  + */
           29  +
           30  +#define LENGTH_CODES 29
           31  +/* number of length codes, not counting the special END_BLOCK code */
           32  +
           33  +#define LITERALS  256
           34  +/* number of literal bytes 0..255 */
           35  +
           36  +#define L_CODES (LITERALS+1+LENGTH_CODES)
           37  +/* number of Literal or Length codes, including the END_BLOCK code */
           38  +
           39  +#define D_CODES   30
           40  +/* number of distance codes */
           41  +
           42  +#define BL_CODES  19
           43  +/* number of codes used to transfer the bit lengths */
           44  +
           45  +#define HEAP_SIZE (2*L_CODES+1)
           46  +/* maximum heap size */
           47  +
           48  +#define MAX_BITS 15
           49  +/* All codes must not exceed MAX_BITS bits */
           50  +
           51  +#define Buf_size 16
           52  +/* size of bit buffer in bi_buf */
           53  +
           54  +#define INIT_STATE    42
           55  +#define EXTRA_STATE   69
           56  +#define NAME_STATE    73
           57  +#define COMMENT_STATE 91
           58  +#define HCRC_STATE   103
           59  +#define BUSY_STATE   113
           60  +#define FINISH_STATE 666
           61  +/* Stream status */
           62  +
           63  +
           64  +/* Data structure describing a single value and its code string. */
           65  +typedef struct ct_data_s {
           66  +    union {
           67  +        ush  freq;       /* frequency count */
           68  +        ush  code;       /* bit string */
           69  +    } fc;
           70  +    union {
           71  +        ush  dad;        /* father node in Huffman tree */
           72  +        ush  len;        /* length of bit string */
           73  +    } dl;
           74  +} FAR ct_data;
           75  +
           76  +#define Freq fc.freq
           77  +#define Code fc.code
           78  +#define Dad  dl.dad
           79  +#define Len  dl.len
           80  +
           81  +typedef struct static_tree_desc_s  static_tree_desc;
           82  +
           83  +typedef struct tree_desc_s {
           84  +    ct_data *dyn_tree;           /* the dynamic tree */
           85  +    int     max_code;            /* largest code with non zero frequency */
           86  +    static_tree_desc *stat_desc; /* the corresponding static tree */
           87  +} FAR tree_desc;
           88  +
           89  +typedef ush Pos;
           90  +typedef Pos FAR Posf;
           91  +typedef unsigned IPos;
           92  +
           93  +/* A Pos is an index in the character window. We use short instead of int to
           94  + * save space in the various tables. IPos is used only for parameter passing.
           95  + */
           96  +
           97  +typedef struct internal_state {
           98  +    z_streamp strm;      /* pointer back to this zlib stream */
           99  +    int   status;        /* as the name implies */
          100  +    Bytef *pending_buf;  /* output still pending */
          101  +    ulg   pending_buf_size; /* size of pending_buf */
          102  +    Bytef *pending_out;  /* next pending byte to output to the stream */
          103  +    uInt   pending;      /* nb of bytes in the pending buffer */
          104  +    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */
          105  +    gz_headerp  gzhead;  /* gzip header information to write */
          106  +    uInt   gzindex;      /* where in extra, name, or comment */
          107  +    Byte  method;        /* STORED (for zip only) or DEFLATED */
          108  +    int   last_flush;    /* value of flush param for previous deflate call */
          109  +
          110  +                /* used by deflate.c: */
          111  +
          112  +    uInt  w_size;        /* LZ77 window size (32K by default) */
          113  +    uInt  w_bits;        /* log2(w_size)  (8..16) */
          114  +    uInt  w_mask;        /* w_size - 1 */
          115  +
          116  +    Bytef *window;
          117  +    /* Sliding window. Input bytes are read into the second half of the window,
          118  +     * and move to the first half later to keep a dictionary of at least wSize
          119  +     * bytes. With this organization, matches are limited to a distance of
          120  +     * wSize-MAX_MATCH bytes, but this ensures that IO is always
          121  +     * performed with a length multiple of the block size. Also, it limits
          122  +     * the window size to 64K, which is quite useful on MSDOS.
          123  +     * To do: use the user input buffer as sliding window.
          124  +     */
          125  +
          126  +    ulg window_size;
          127  +    /* Actual size of window: 2*wSize, except when the user input buffer
          128  +     * is directly used as sliding window.
          129  +     */
          130  +
          131  +    Posf *prev;
          132  +    /* Link to older string with same hash index. To limit the size of this
          133  +     * array to 64K, this link is maintained only for the last 32K strings.
          134  +     * An index in this array is thus a window index modulo 32K.
          135  +     */
          136  +
          137  +    Posf *head; /* Heads of the hash chains or NIL. */
          138  +
          139  +    uInt  ins_h;          /* hash index of string to be inserted */
          140  +    uInt  hash_size;      /* number of elements in hash table */
          141  +    uInt  hash_bits;      /* log2(hash_size) */
          142  +    uInt  hash_mask;      /* hash_size-1 */
          143  +
          144  +    uInt  hash_shift;
          145  +    /* Number of bits by which ins_h must be shifted at each input
          146  +     * step. It must be such that after MIN_MATCH steps, the oldest
          147  +     * byte no longer takes part in the hash key, that is:
          148  +     *   hash_shift * MIN_MATCH >= hash_bits
          149  +     */
          150  +
          151  +    long block_start;
          152  +    /* Window position at the beginning of the current output block. Gets
          153  +     * negative when the window is moved backwards.
          154  +     */
          155  +
          156  +    uInt match_length;           /* length of best match */
          157  +    IPos prev_match;             /* previous match */
          158  +    int match_available;         /* set if previous match exists */
          159  +    uInt strstart;               /* start of string to insert */
          160  +    uInt match_start;            /* start of matching string */
          161  +    uInt lookahead;              /* number of valid bytes ahead in window */
          162  +
          163  +    uInt prev_length;
          164  +    /* Length of the best match at previous step. Matches not greater than this
          165  +     * are discarded. This is used in the lazy match evaluation.
          166  +     */
          167  +
          168  +    uInt max_chain_length;
          169  +    /* To speed up deflation, hash chains are never searched beyond this
          170  +     * length.  A higher limit improves compression ratio but degrades the
          171  +     * speed.
          172  +     */
          173  +
          174  +    uInt max_lazy_match;
          175  +    /* Attempt to find a better match only when the current match is strictly
          176  +     * smaller than this value. This mechanism is used only for compression
          177  +     * levels >= 4.
          178  +     */
          179  +#   define max_insert_length  max_lazy_match
          180  +    /* Insert new strings in the hash table only if the match length is not
          181  +     * greater than this length. This saves time but degrades compression.
          182  +     * max_insert_length is used only for compression levels <= 3.
          183  +     */
          184  +
          185  +    int level;    /* compression level (1..9) */
          186  +    int strategy; /* favor or force Huffman coding*/
          187  +
          188  +    uInt good_match;
          189  +    /* Use a faster search when the previous match is longer than this */
          190  +
          191  +    int nice_match; /* Stop searching when current match exceeds this */
          192  +
          193  +                /* used by trees.c: */
          194  +    /* Didn't use ct_data typedef below to suppress compiler warning */
          195  +    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
          196  +    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
          197  +    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
          198  +
          199  +    struct tree_desc_s l_desc;               /* desc. for literal tree */
          200  +    struct tree_desc_s d_desc;               /* desc. for distance tree */
          201  +    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
          202  +
          203  +    ush bl_count[MAX_BITS+1];
          204  +    /* number of codes at each bit length for an optimal tree */
          205  +
          206  +    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
          207  +    int heap_len;               /* number of elements in the heap */
          208  +    int heap_max;               /* element of largest frequency */
          209  +    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
          210  +     * The same heap array is used to build all trees.
          211  +     */
          212  +
          213  +    uch depth[2*L_CODES+1];
          214  +    /* Depth of each subtree used as tie breaker for trees of equal frequency
          215  +     */
          216  +
          217  +    uchf *l_buf;          /* buffer for literals or lengths */
          218  +
          219  +    uInt  lit_bufsize;
          220  +    /* Size of match buffer for literals/lengths.  There are 4 reasons for
          221  +     * limiting lit_bufsize to 64K:
          222  +     *   - frequencies can be kept in 16 bit counters
          223  +     *   - if compression is not successful for the first block, all input
          224  +     *     data is still in the window so we can still emit a stored block even
          225  +     *     when input comes from standard input.  (This can also be done for
          226  +     *     all blocks if lit_bufsize is not greater than 32K.)
          227  +     *   - if compression is not successful for a file smaller than 64K, we can
          228  +     *     even emit a stored file instead of a stored block (saving 5 bytes).
          229  +     *     This is applicable only for zip (not gzip or zlib).
          230  +     *   - creating new Huffman trees less frequently may not provide fast
          231  +     *     adaptation to changes in the input data statistics. (Take for
          232  +     *     example a binary file with poorly compressible code followed by
          233  +     *     a highly compressible string table.) Smaller buffer sizes give
          234  +     *     fast adaptation but have of course the overhead of transmitting
          235  +     *     trees more frequently.
          236  +     *   - I can't count above 4
          237  +     */
          238  +
          239  +    uInt last_lit;      /* running index in l_buf */
          240  +
          241  +    ushf *d_buf;
          242  +    /* Buffer for distances. To simplify the code, d_buf and l_buf have
          243  +     * the same number of elements. To use different lengths, an extra flag
          244  +     * array would be necessary.
          245  +     */
          246  +
          247  +    ulg opt_len;        /* bit length of current block with optimal trees */
          248  +    ulg static_len;     /* bit length of current block with static trees */
          249  +    uInt matches;       /* number of string matches in current block */
          250  +    uInt insert;        /* bytes at end of window left to insert */
          251  +
          252  +#ifdef DEBUG
          253  +    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
          254  +    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
          255  +#endif
          256  +
          257  +    ush bi_buf;
          258  +    /* Output buffer. bits are inserted starting at the bottom (least
          259  +     * significant bits).
          260  +     */
          261  +    int bi_valid;
          262  +    /* Number of valid bits in bi_buf.  All bits above the last valid bit
          263  +     * are always zero.
          264  +     */
          265  +
          266  +    ulg high_water;
          267  +    /* High water mark offset in window for initialized bytes -- bytes above
          268  +     * this are set to zero in order to avoid memory check warnings when
          269  +     * longest match routines access bytes past the input.  This is then
          270  +     * updated to the new high water mark.
          271  +     */
          272  +
          273  +} FAR deflate_state;
          274  +
          275  +/* Output a byte on the stream.
          276  + * IN assertion: there is enough room in pending_buf.
          277  + */
          278  +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
          279  +
          280  +
          281  +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
          282  +/* Minimum amount of lookahead, except at the end of the input file.
          283  + * See deflate.c for comments about the MIN_MATCH+1.
          284  + */
          285  +
          286  +#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
          287  +/* In order to simplify the code, particularly on 16 bit machines, match
          288  + * distances are limited to MAX_DIST instead of WSIZE.
          289  + */
          290  +
          291  +#define WIN_INIT MAX_MATCH
          292  +/* Number of bytes after end of data in window to initialize in order to avoid
          293  +   memory checker errors from longest match routines */
          294  +
          295  +        /* in trees.c */
          296  +void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
          297  +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
          298  +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
          299  +                        ulg stored_len, int last));
          300  +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
          301  +void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
          302  +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
          303  +                        ulg stored_len, int last));
          304  +
          305  +#define d_code(dist) \
          306  +   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
          307  +/* Mapping from a distance to a distance code. dist is the distance - 1 and
          308  + * must not have side effects. _dist_code[256] and _dist_code[257] are never
          309  + * used.
          310  + */
          311  +
          312  +#ifndef DEBUG
          313  +/* Inline versions of _tr_tally for speed: */
          314  +
          315  +#if defined(GEN_TREES_H) || !defined(STDC)
          316  +  extern uch ZLIB_INTERNAL _length_code[];
          317  +  extern uch ZLIB_INTERNAL _dist_code[];
          318  +#else
          319  +  extern const uch ZLIB_INTERNAL _length_code[];
          320  +  extern const uch ZLIB_INTERNAL _dist_code[];
          321  +#endif
          322  +
          323  +# define _tr_tally_lit(s, c, flush) \
          324  +  { uch cc = (c); \
          325  +    s->d_buf[s->last_lit] = 0; \
          326  +    s->l_buf[s->last_lit++] = cc; \
          327  +    s->dyn_ltree[cc].Freq++; \
          328  +    flush = (s->last_lit == s->lit_bufsize-1); \
          329  +   }
          330  +# define _tr_tally_dist(s, distance, length, flush) \
          331  +  { uch len = (length); \
          332  +    ush dist = (distance); \
          333  +    s->d_buf[s->last_lit] = dist; \
          334  +    s->l_buf[s->last_lit++] = len; \
          335  +    dist--; \
          336  +    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
          337  +    s->dyn_dtree[d_code(dist)].Freq++; \
          338  +    flush = (s->last_lit == s->lit_bufsize-1); \
          339  +  }
          340  +#else
          341  +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
          342  +# define _tr_tally_dist(s, distance, length, flush) \
          343  +              flush = _tr_tally(s, distance, length)
          344  +#endif
          345  +
          346  +#endif /* DEFLATE_H */

Added compat/zlib/doc/algorithm.txt.

            1  +1. Compression algorithm (deflate)
            2  +
            3  +The deflation algorithm used by gzip (also zip and zlib) is a variation of
            4  +LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
            5  +the input data.  The second occurrence of a string is replaced by a
            6  +pointer to the previous string, in the form of a pair (distance,
            7  +length).  Distances are limited to 32K bytes, and lengths are limited
            8  +to 258 bytes. When a string does not occur anywhere in the previous
            9  +32K bytes, it is emitted as a sequence of literal bytes.  (In this
           10  +description, `string' must be taken as an arbitrary sequence of bytes,
           11  +and is not restricted to printable characters.)
           12  +
           13  +Literals or match lengths are compressed with one Huffman tree, and
           14  +match distances are compressed with another tree. The trees are stored
           15  +in a compact form at the start of each block. The blocks can have any
           16  +size (except that the compressed data for one block must fit in
           17  +available memory). A block is terminated when deflate() determines that
           18  +it would be useful to start another block with fresh trees. (This is
           19  +somewhat similar to the behavior of LZW-based _compress_.)
           20  +
           21  +Duplicated strings are found using a hash table. All input strings of
           22  +length 3 are inserted in the hash table. A hash index is computed for
           23  +the next 3 bytes. If the hash chain for this index is not empty, all
           24  +strings in the chain are compared with the current input string, and
           25  +the longest match is selected.
           26  +
           27  +The hash chains are searched starting with the most recent strings, to
           28  +favor small distances and thus take advantage of the Huffman encoding.
           29  +The hash chains are singly linked. There are no deletions from the
           30  +hash chains, the algorithm simply discards matches that are too old.
           31  +
           32  +To avoid a worst-case situation, very long hash chains are arbitrarily
           33  +truncated at a certain length, determined by a runtime option (level
           34  +parameter of deflateInit). So deflate() does not always find the longest
           35  +possible match but generally finds a match which is long enough.
           36  +
           37  +deflate() also defers the selection of matches with a lazy evaluation
           38  +mechanism. After a match of length N has been found, deflate() searches for
           39  +a longer match at the next input byte. If a longer match is found, the
           40  +previous match is truncated to a length of one (thus producing a single
           41  +literal byte) and the process of lazy evaluation begins again. Otherwise,
           42  +the original match is kept, and the next match search is attempted only N
           43  +steps later.
           44  +
           45  +The lazy match evaluation is also subject to a runtime parameter. If
           46  +the current match is long enough, deflate() reduces the search for a longer
           47  +match, thus speeding up the whole process. If compression ratio is more
           48  +important than speed, deflate() attempts a complete second search even if
           49  +the first match is already long enough.
           50  +
           51  +The lazy match evaluation is not performed for the fastest compression
           52  +modes (level parameter 1 to 3). For these fast modes, new strings
           53  +are inserted in the hash table only when no match was found, or
           54  +when the match is not too long. This degrades the compression ratio
           55  +but saves time since there are both fewer insertions and fewer searches.
           56  +
           57  +
           58  +2. Decompression algorithm (inflate)
           59  +
           60  +2.1 Introduction
           61  +
           62  +The key question is how to represent a Huffman code (or any prefix code) so
           63  +that you can decode fast.  The most important characteristic is that shorter
           64  +codes are much more common than longer codes, so pay attention to decoding the
           65  +short codes fast, and let the long codes take longer to decode.
           66  +
           67  +inflate() sets up a first level table that covers some number of bits of
           68  +input less than the length of longest code.  It gets that many bits from the
           69  +stream, and looks it up in the table.  The table will tell if the next
           70  +code is that many bits or less and how many, and if it is, it will tell
           71  +the value, else it will point to the next level table for which inflate()
           72  +grabs more bits and tries to decode a longer code.
           73  +
           74  +How many bits to make the first lookup is a tradeoff between the time it
           75  +takes to decode and the time it takes to build the table.  If building the
           76  +table took no time (and if you had infinite memory), then there would only
           77  +be a first level table to cover all the way to the longest code.  However,
           78  +building the table ends up taking a lot longer for more bits since short
           79  +codes are replicated many times in such a table.  What inflate() does is
           80  +simply to make the number of bits in the first table a variable, and  then
           81  +to set that variable for the maximum speed.
           82  +
           83  +For inflate, which has 286 possible codes for the literal/length tree, the size
           84  +of the first table is nine bits.  Also the distance trees have 30 possible
           85  +values, and the size of the first table is six bits.  Note that for each of
           86  +those cases, the table ended up one bit longer than the ``average'' code
           87  +length, i.e. the code length of an approximately flat code which would be a
           88  +little more than eight bits for 286 symbols and a little less than five bits
           89  +for 30 symbols.
           90  +
           91  +
           92  +2.2 More details on the inflate table lookup
           93  +
           94  +Ok, you want to know what this cleverly obfuscated inflate tree actually
           95  +looks like.  You are correct that it's not a Huffman tree.  It is simply a
           96  +lookup table for the first, let's say, nine bits of a Huffman symbol.  The
           97  +symbol could be as short as one bit or as long as 15 bits.  If a particular
           98  +symbol is shorter than nine bits, then that symbol's translation is duplicated
           99  +in all those entries that start with that symbol's bits.  For example, if the
          100  +symbol is four bits, then it's duplicated 32 times in a nine-bit table.  If a
          101  +symbol is nine bits long, it appears in the table once.
          102  +
          103  +If the symbol is longer than nine bits, then that entry in the table points
          104  +to another similar table for the remaining bits.  Again, there are duplicated
          105  +entries as needed.  The idea is that most of the time the symbol will be short
          106  +and there will only be one table look up.  (That's whole idea behind data
          107  +compression in the first place.)  For the less frequent long symbols, there
          108  +will be two lookups.  If you had a compression method with really long
          109  +symbols, you could have as many levels of lookups as is efficient.  For
          110  +inflate, two is enough.
          111  +
          112  +So a table entry either points to another table (in which case nine bits in
          113  +the above example are gobbled), or it contains the translation for the symbol
          114  +and the number of bits to gobble.  Then you start again with the next
          115  +ungobbled bit.
          116  +
          117  +You may wonder: why not just have one lookup table for how ever many bits the
          118  +longest symbol is?  The reason is that if you do that, you end up spending
          119  +more time filling in duplicate symbol entries than you do actually decoding.
          120  +At least for deflate's output that generates new trees every several 10's of
          121  +kbytes.  You can imagine that filling in a 2^15 entry table for a 15-bit code
          122  +would take too long if you're only decoding several thousand symbols.  At the
          123  +other extreme, you could make a new table for every bit in the code.  In fact,
          124  +that's essentially a Huffman tree.  But then you spend too much time
          125  +traversing the tree while decoding, even for short symbols.
          126  +
          127  +So the number of bits for the first lookup table is a trade of the time to
          128  +fill out the table vs. the time spent looking at the second level and above of
          129  +the table.
          130  +
          131  +Here is an example, scaled down:
          132  +
          133  +The code being decoded, with 10 symbols, from 1 to 6 bits long:
          134  +
          135  +A: 0
          136  +B: 10
          137  +C: 1100
          138  +D: 11010
          139  +E: 11011
          140  +F: 11100
          141  +G: 11101
          142  +H: 11110
          143  +I: 111110
          144  +J: 111111
          145  +
          146  +Let's make the first table three bits long (eight entries):
          147  +
          148  +000: A,1
          149  +001: A,1
          150  +010: A,1
          151  +011: A,1
          152  +100: B,2
          153  +101: B,2
          154  +110: -> table X (gobble 3 bits)
          155  +111: -> table Y (gobble 3 bits)
          156  +
          157  +Each entry is what the bits decode as and how many bits that is, i.e. how
          158  +many bits to gobble.  Or the entry points to another table, with the number of
          159  +bits to gobble implicit in the size of the table.
          160  +
          161  +Table X is two bits long since the longest code starting with 110 is five bits
          162  +long:
          163  +
          164  +00: C,1
          165  +01: C,1
          166  +10: D,2
          167  +11: E,2
          168  +
          169  +Table Y is three bits long since the longest code starting with 111 is six
          170  +bits long:
          171  +
          172  +000: F,2
          173  +001: F,2
          174  +010: G,2
          175  +011: G,2
          176  +100: H,2
          177  +101: H,2
          178  +110: I,3
          179  +111: J,3
          180  +
          181  +So what we have here are three tables with a total of 20 entries that had to
          182  +be constructed.  That's compared to 64 entries for a single table.  Or
          183  +compared to 16 entries for a Huffman tree (six two entry tables and one four
          184  +entry table).  Assuming that the code ideally represents the probability of
          185  +the symbols, it takes on the average 1.25 lookups per symbol.  That's compared
          186  +to one lookup for the single table, or 1.66 lookups per symbol for the
          187  +Huffman tree.
          188  +
          189  +There, I think that gives you a picture of what's going on.  For inflate, the
          190  +meaning of a particular symbol is often more than just a letter.  It can be a
          191  +byte (a "literal"), or it can be either a length or a distance which
          192  +indicates a base value and a number of bits to fetch after the code that is
          193  +added to the base value.  Or it might be the special end-of-block code.  The
          194  +data structures created in inftrees.c try to encode all that information
          195  +compactly in the tables.
          196  +
          197  +
          198  +Jean-loup Gailly        Mark Adler
          199  +jloup@gzip.org          madler@alumni.caltech.edu
          200  +
          201  +
          202  +References:
          203  +
          204  +[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
          205  +Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
          206  +pp. 337-343.
          207  +
          208  +``DEFLATE Compressed Data Format Specification'' available in
          209  +http://tools.ietf.org/html/rfc1951

Added compat/zlib/doc/rfc1950.txt.

            1  +
            2  +
            3  +
            4  +
            5  +
            6  +
            7  +Network Working Group                                         P. Deutsch
            8  +Request for Comments: 1950                           Aladdin Enterprises
            9  +Category: Informational                                      J-L. Gailly
           10  +                                                                Info-ZIP
           11  +                                                                May 1996
           12  +
           13  +
           14  +         ZLIB Compressed Data Format Specification version 3.3
           15  +
           16  +Status of This Memo
           17  +
           18  +   This memo provides information for the Internet community.  This memo
           19  +   does not specify an Internet standard of any kind.  Distribution of
           20  +   this memo is unlimited.
           21  +
           22  +IESG Note:
           23  +
           24  +   The IESG takes no position on the validity of any Intellectual
           25  +   Property Rights statements contained in this document.
           26  +
           27  +Notices
           28  +
           29  +   Copyright (c) 1996 L. Peter Deutsch and Jean-Loup Gailly
           30  +
           31  +   Permission is granted to copy and distribute this document for any
           32  +   purpose and without charge, including translations into other
           33  +   languages and incorporation into compilations, provided that the
           34  +   copyright notice and this notice are preserved, and that any
           35  +   substantive changes or deletions from the original are clearly
           36  +   marked.
           37  +
           38  +   A pointer to the latest version of this and related documentation in
           39  +   HTML format can be found at the URL
           40  +   <ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html>.
           41  +
           42  +Abstract
           43  +
           44  +   This specification defines a lossless compressed data format.  The
           45  +   data can be produced or consumed, even for an arbitrarily long
           46  +   sequentially presented input data stream, using only an a priori
           47  +   bounded amount of intermediate storage.  The format presently uses
           48  +   the DEFLATE compression method but can be easily extended to use
           49  +   other compression methods.  It can be implemented readily in a manner
           50  +   not covered by patents.  This specification also defines the ADLER-32
           51  +   checksum (an extension and improvement of the Fletcher checksum),
           52  +   used for detection of data corruption, and provides an algorithm for
           53  +   computing it.
           54  +
           55  +
           56  +
           57  +
           58  +Deutsch & Gailly             Informational                      [Page 1]
           59  +
           60  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
           61  +
           62  +
           63  +Table of Contents
           64  +
           65  +   1. Introduction ................................................... 2
           66  +      1.1. Purpose ................................................... 2
           67  +      1.2. Intended audience ......................................... 3
           68  +      1.3. Scope ..................................................... 3
           69  +      1.4. Compliance ................................................ 3
           70  +      1.5.  Definitions of terms and conventions used ................ 3
           71  +      1.6. Changes from previous versions ............................ 3
           72  +   2. Detailed specification ......................................... 3
           73  +      2.1. Overall conventions ....................................... 3
           74  +      2.2. Data format ............................................... 4
           75  +      2.3. Compliance ................................................ 7
           76  +   3. References ..................................................... 7
           77  +   4. Source code .................................................... 8
           78  +   5. Security Considerations ........................................ 8
           79  +   6. Acknowledgements ............................................... 8
           80  +   7. Authors' Addresses ............................................. 8
           81  +   8. Appendix: Rationale ............................................ 9
           82  +   9. Appendix: Sample code ..........................................10
           83  +
           84  +1. Introduction
           85  +
           86  +   1.1. Purpose
           87  +
           88  +      The purpose of this specification is to define a lossless
           89  +      compressed data format that:
           90  +
           91  +          * Is independent of CPU type, operating system, file system,
           92  +            and character set, and hence can be used for interchange;
           93  +
           94  +          * Can be produced or consumed, even for an arbitrarily long
           95  +            sequentially presented input data stream, using only an a
           96  +            priori bounded amount of intermediate storage, and hence can
           97  +            be used in data communications or similar structures such as
           98  +            Unix filters;
           99  +
          100  +          * Can use a number of different compression methods;
          101  +
          102  +          * Can be implemented readily in a manner not covered by
          103  +            patents, and hence can be practiced freely.
          104  +
          105  +      The data format defined by this specification does not attempt to
          106  +      allow random access to compressed data.
          107  +
          108  +
          109  +
          110  +
          111  +
          112  +
          113  +
          114  +Deutsch & Gailly             Informational                      [Page 2]
          115  +
          116  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          117  +
          118  +
          119  +   1.2. Intended audience
          120  +
          121  +      This specification is intended for use by implementors of software
          122  +      to compress data into zlib format and/or decompress data from zlib
          123  +      format.
          124  +
          125  +      The text of the specification assumes a basic background in
          126  +      programming at the level of bits and other primitive data
          127  +      representations.
          128  +
          129  +   1.3. Scope
          130  +
          131  +      The specification specifies a compressed data format that can be
          132  +      used for in-memory compression of a sequence of arbitrary bytes.
          133  +
          134  +   1.4. Compliance
          135  +
          136  +      Unless otherwise indicated below, a compliant decompressor must be
          137  +      able to accept and decompress any data set that conforms to all
          138  +      the specifications presented here; a compliant compressor must
          139  +      produce data sets that conform to all the specifications presented
          140  +      here.
          141  +
          142  +   1.5.  Definitions of terms and conventions used
          143  +
          144  +      byte: 8 bits stored or transmitted as a unit (same as an octet).
          145  +      (For this specification, a byte is exactly 8 bits, even on
          146  +      machines which store a character on a number of bits different
          147  +      from 8.) See below, for the numbering of bits within a byte.
          148  +
          149  +   1.6. Changes from previous versions
          150  +
          151  +      Version 3.1 was the first public release of this specification.
          152  +      In version 3.2, some terminology was changed and the Adler-32
          153  +      sample code was rewritten for clarity.  In version 3.3, the
          154  +      support for a preset dictionary was introduced, and the
          155  +      specification was converted to RFC style.
          156  +
          157  +2. Detailed specification
          158  +
          159  +   2.1. Overall conventions
          160  +
          161  +      In the diagrams below, a box like this:
          162  +
          163  +         +---+
          164  +         |   | <-- the vertical bars might be missing
          165  +         +---+
          166  +
          167  +
          168  +
          169  +
          170  +Deutsch & Gailly             Informational                      [Page 3]
          171  +
          172  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          173  +
          174  +
          175  +      represents one byte; a box like this:
          176  +
          177  +         +==============+
          178  +         |              |
          179  +         +==============+
          180  +
          181  +      represents a variable number of bytes.
          182  +
          183  +      Bytes stored within a computer do not have a "bit order", since
          184  +      they are always treated as a unit.  However, a byte considered as
          185  +      an integer between 0 and 255 does have a most- and least-
          186  +      significant bit, and since we write numbers with the most-
          187  +      significant digit on the left, we also write bytes with the most-
          188  +      significant bit on the left.  In the diagrams below, we number the
          189  +      bits of a byte so that bit 0 is the least-significant bit, i.e.,
          190  +      the bits are numbered:
          191  +
          192  +         +--------+
          193  +         |76543210|
          194  +         +--------+
          195  +
          196  +      Within a computer, a number may occupy multiple bytes.  All
          197  +      multi-byte numbers in the format described here are stored with
          198  +      the MOST-significant byte first (at the lower memory address).
          199  +      For example, the decimal number 520 is stored as:
          200  +
          201  +             0     1
          202  +         +--------+--------+
          203  +         |00000010|00001000|
          204  +         +--------+--------+
          205  +          ^        ^
          206  +          |        |
          207  +          |        + less significant byte = 8
          208  +          + more significant byte = 2 x 256
          209  +
          210  +   2.2. Data format
          211  +
          212  +      A zlib stream has the following structure:
          213  +
          214  +           0   1
          215  +         +---+---+
          216  +         |CMF|FLG|   (more-->)
          217  +         +---+---+
          218  +
          219  +
          220  +
          221  +
          222  +
          223  +
          224  +
          225  +
          226  +Deutsch & Gailly             Informational                      [Page 4]
          227  +
          228  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          229  +
          230  +
          231  +      (if FLG.FDICT set)
          232  +
          233  +           0   1   2   3
          234  +         +---+---+---+---+
          235  +         |     DICTID    |   (more-->)
          236  +         +---+---+---+---+
          237  +
          238  +         +=====================+---+---+---+---+
          239  +         |...compressed data...|    ADLER32    |
          240  +         +=====================+---+---+---+---+
          241  +
          242  +      Any data which may appear after ADLER32 are not part of the zlib
          243  +      stream.
          244  +
          245  +      CMF (Compression Method and flags)
          246  +         This byte is divided into a 4-bit compression method and a 4-
          247  +         bit information field depending on the compression method.
          248  +
          249  +            bits 0 to 3  CM     Compression method
          250  +            bits 4 to 7  CINFO  Compression info
          251  +
          252  +      CM (Compression method)
          253  +         This identifies the compression method used in the file. CM = 8
          254  +         denotes the "deflate" compression method with a window size up
          255  +         to 32K.  This is the method used by gzip and PNG (see
          256  +         references [1] and [2] in Chapter 3, below, for the reference
          257  +         documents).  CM = 15 is reserved.  It might be used in a future
          258  +         version of this specification to indicate the presence of an
          259  +         extra field before the compressed data.
          260  +
          261  +      CINFO (Compression info)
          262  +         For CM = 8, CINFO is the base-2 logarithm of the LZ77 window
          263  +         size, minus eight (CINFO=7 indicates a 32K window size). Values
          264  +         of CINFO above 7 are not allowed in this version of the
          265  +         specification.  CINFO is not defined in this specification for
          266  +         CM not equal to 8.
          267  +
          268  +      FLG (FLaGs)
          269  +         This flag byte is divided as follows:
          270  +
          271  +            bits 0 to 4  FCHECK  (check bits for CMF and FLG)
          272  +            bit  5       FDICT   (preset dictionary)
          273  +            bits 6 to 7  FLEVEL  (compression level)
          274  +
          275  +         The FCHECK value must be such that CMF and FLG, when viewed as
          276  +         a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG),
          277  +         is a multiple of 31.
          278  +
          279  +
          280  +
          281  +
          282  +Deutsch & Gailly             Informational                      [Page 5]
          283  +
          284  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          285  +
          286  +
          287  +      FDICT (Preset dictionary)
          288  +         If FDICT is set, a DICT dictionary identifier is present
          289  +         immediately after the FLG byte. The dictionary is a sequence of
          290  +         bytes which are initially fed to the compressor without
          291  +         producing any compressed output. DICT is the Adler-32 checksum
          292  +         of this sequence of bytes (see the definition of ADLER32
          293  +         below).  The decompressor can use this identifier to determine
          294  +         which dictionary has been used by the compressor.
          295  +
          296  +      FLEVEL (Compression level)
          297  +         These flags are available for use by specific compression
          298  +         methods.  The "deflate" method (CM = 8) sets these flags as
          299  +         follows:
          300  +
          301  +            0 - compressor used fastest algorithm
          302  +            1 - compressor used fast algorithm
          303  +            2 - compressor used default algorithm
          304  +            3 - compressor used maximum compression, slowest algorithm
          305  +
          306  +         The information in FLEVEL is not needed for decompression; it
          307  +         is there to indicate if recompression might be worthwhile.
          308  +
          309  +      compressed data
          310  +         For compression method 8, the compressed data is stored in the
          311  +         deflate compressed data format as described in the document
          312  +         "DEFLATE Compressed Data Format Specification" by L. Peter
          313  +         Deutsch. (See reference [3] in Chapter 3, below)
          314  +
          315  +         Other compressed data formats are not specified in this version
          316  +         of the zlib specification.
          317  +
          318  +      ADLER32 (Adler-32 checksum)
          319  +         This contains a checksum value of the uncompressed data
          320  +         (excluding any dictionary data) computed according to Adler-32
          321  +         algorithm. This algorithm is a 32-bit extension and improvement
          322  +         of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
          323  +         standard. See references [4] and [5] in Chapter 3, below)
          324  +
          325  +         Adler-32 is composed of two sums accumulated per byte: s1 is
          326  +         the sum of all bytes, s2 is the sum of all s1 values. Both sums
          327  +         are done modulo 65521. s1 is initialized to 1, s2 to zero.  The
          328  +         Adler-32 checksum is stored as s2*65536 + s1 in most-
          329  +         significant-byte first (network) order.
          330  +
          331  +
          332  +
          333  +
          334  +
          335  +
          336  +
          337  +
          338  +Deutsch & Gailly             Informational                      [Page 6]
          339  +
          340  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          341  +
          342  +
          343  +   2.3. Compliance
          344  +
          345  +      A compliant compressor must produce streams with correct CMF, FLG
          346  +      and ADLER32, but need not support preset dictionaries.  When the
          347  +      zlib data format is used as part of another standard data format,
          348  +      the compressor may use only preset dictionaries that are specified
          349  +      by this other data format.  If this other format does not use the
          350  +      preset dictionary feature, the compressor must not set the FDICT
          351  +      flag.
          352  +
          353  +      A compliant decompressor must check CMF, FLG, and ADLER32, and
          354  +      provide an error indication if any of these have incorrect values.
          355  +      A compliant decompressor must give an error indication if CM is
          356  +      not one of the values defined in this specification (only the
          357  +      value 8 is permitted in this version), since another value could
          358  +      indicate the presence of new features that would cause subsequent
          359  +      data to be interpreted incorrectly.  A compliant decompressor must
          360  +      give an error indication if FDICT is set and DICTID is not the
          361  +      identifier of a known preset dictionary.  A decompressor may
          362  +      ignore FLEVEL and still be compliant.  When the zlib data format
          363  +      is being used as a part of another standard format, a compliant
          364  +      decompressor must support all the preset dictionaries specified by
          365  +      the other format. When the other format does not use the preset
          366  +      dictionary feature, a compliant decompressor must reject any
          367  +      stream in which the FDICT flag is set.
          368  +
          369  +3. References
          370  +
          371  +   [1] Deutsch, L.P.,"GZIP Compressed Data Format Specification",
          372  +       available in ftp://ftp.uu.net/pub/archiving/zip/doc/
          373  +
          374  +   [2] Thomas Boutell, "PNG (Portable Network Graphics) specification",
          375  +       available in ftp://ftp.uu.net/graphics/png/documents/
          376  +
          377  +   [3] Deutsch, L.P.,"DEFLATE Compressed Data Format Specification",
          378  +       available in ftp://ftp.uu.net/pub/archiving/zip/doc/
          379  +
          380  +   [4] Fletcher, J. G., "An Arithmetic Checksum for Serial
          381  +       Transmissions," IEEE Transactions on Communications, Vol. COM-30,
          382  +       No. 1, January 1982, pp. 247-252.
          383  +
          384  +   [5] ITU-T Recommendation X.224, Annex D, "Checksum Algorithms,"
          385  +       November, 1993, pp. 144, 145. (Available from
          386  +       gopher://info.itu.ch). ITU-T X.244 is also the same as ISO 8073.
          387  +
          388  +
          389  +
          390  +
          391  +
          392  +
          393  +
          394  +Deutsch & Gailly             Informational                      [Page 7]
          395  +
          396  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          397  +
          398  +
          399  +4. Source code
          400  +
          401  +   Source code for a C language implementation of a "zlib" compliant
          402  +   library is available at ftp://ftp.uu.net/pub/archiving/zip/zlib/.
          403  +
          404  +5. Security Considerations
          405  +
          406  +   A decoder that fails to check the ADLER32 checksum value may be
          407  +   subject to undetected data corruption.
          408  +
          409  +6. Acknowledgements
          410  +
          411  +   Trademarks cited in this document are the property of their
          412  +   respective owners.
          413  +
          414  +   Jean-Loup Gailly and Mark Adler designed the zlib format and wrote
          415  +   the related software described in this specification.  Glenn
          416  +   Randers-Pehrson converted this document to RFC and HTML format.
          417  +
          418  +7. Authors' Addresses
          419  +
          420  +   L. Peter Deutsch
          421  +   Aladdin Enterprises
          422  +   203 Santa Margarita Ave.
          423  +   Menlo Park, CA 94025
          424  +
          425  +   Phone: (415) 322-0103 (AM only)
          426  +   FAX:   (415) 322-1734
          427  +   EMail: <ghost@aladdin.com>
          428  +
          429  +
          430  +   Jean-Loup Gailly
          431  +
          432  +   EMail: <gzip@prep.ai.mit.edu>
          433  +
          434  +   Questions about the technical content of this specification can be
          435  +   sent by email to
          436  +
          437  +   Jean-Loup Gailly <gzip@prep.ai.mit.edu> and
          438  +   Mark Adler <madler@alumni.caltech.edu>
          439  +
          440  +   Editorial comments on this specification can be sent by email to
          441  +
          442  +   L. Peter Deutsch <ghost@aladdin.com> and
          443  +   Glenn Randers-Pehrson <randeg@alumni.rpi.edu>
          444  +
          445  +
          446  +
          447  +
          448  +
          449  +
          450  +Deutsch & Gailly             Informational                      [Page 8]
          451  +
          452  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          453  +
          454  +
          455  +8. Appendix: Rationale
          456  +
          457  +   8.1. Preset dictionaries
          458  +
          459  +      A preset dictionary is specially useful to compress short input
          460  +      sequences. The compressor can take advantage of the dictionary
          461  +      context to encode the input in a more compact manner. The
          462  +      decompressor can be initialized with the appropriate context by
          463  +      virtually decompressing a compressed version of the dictionary
          464  +      without producing any output. However for certain compression
          465  +      algorithms such as the deflate algorithm this operation can be
          466  +      achieved without actually performing any decompression.
          467  +
          468  +      The compressor and the decompressor must use exactly the same
          469  +      dictionary. The dictionary may be fixed or may be chosen among a
          470  +      certain number of predefined dictionaries, according to the kind
          471  +      of input data. The decompressor can determine which dictionary has
          472  +      been chosen by the compressor by checking the dictionary
          473  +      identifier. This document does not specify the contents of
          474  +      predefined dictionaries, since the optimal dictionaries are
          475  +      application specific. Standard data formats using this feature of
          476  +      the zlib specification must precisely define the allowed
          477  +      dictionaries.
          478  +
          479  +   8.2. The Adler-32 algorithm
          480  +
          481  +      The Adler-32 algorithm is much faster than the CRC32 algorithm yet
          482  +      still provides an extremely low probability of undetected errors.
          483  +
          484  +      The modulo on unsigned long accumulators can be delayed for 5552
          485  +      bytes, so the modulo operation time is negligible.  If the bytes
          486  +      are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
          487  +      and order sensitive, unlike the first sum, which is just a
          488  +      checksum.  That 65521 is prime is important to avoid a possible
          489  +      large class of two-byte errors that leave the check unchanged.
          490  +      (The Fletcher checksum uses 255, which is not prime and which also
          491  +      makes the Fletcher check insensitive to single byte changes 0 <->
          492  +      255.)
          493  +
          494  +      The sum s1 is initialized to 1 instead of zero to make the length
          495  +      of the sequence part of s2, so that the length does not have to be
          496  +      checked separately. (Any sequence of zeroes has a Fletcher
          497  +      checksum of zero.)
          498  +
          499  +
          500  +
          501  +
          502  +
          503  +
          504  +
          505  +
          506  +Deutsch & Gailly             Informational                      [Page 9]
          507  +
          508  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          509  +
          510  +
          511  +9. Appendix: Sample code
          512  +
          513  +   The following C code computes the Adler-32 checksum of a data buffer.
          514  +   It is written for clarity, not for speed.  The sample code is in the
          515  +   ANSI C programming language. Non C users may find it easier to read
          516  +   with these hints:
          517  +
          518  +      &      Bitwise AND operator.
          519  +      >>     Bitwise right shift operator. When applied to an
          520  +             unsigned quantity, as here, right shift inserts zero bit(s)
          521  +             at the left.
          522  +      <<     Bitwise left shift operator. Left shift inserts zero
          523  +             bit(s) at the right.
          524  +      ++     "n++" increments the variable n.
          525  +      %      modulo operator: a % b is the remainder of a divided by b.
          526  +
          527  +      #define BASE 65521 /* largest prime smaller than 65536 */
          528  +
          529  +      /*
          530  +         Update a running Adler-32 checksum with the bytes buf[0..len-1]
          531  +       and return the updated checksum. The Adler-32 checksum should be
          532  +       initialized to 1.
          533  +
          534  +       Usage example:
          535  +
          536  +         unsigned long adler = 1L;
          537  +
          538  +         while (read_buffer(buffer, length) != EOF) {
          539  +           adler = update_adler32(adler, buffer, length);
          540  +         }
          541  +         if (adler != original_adler) error();
          542  +      */
          543  +      unsigned long update_adler32(unsigned long adler,
          544  +         unsigned char *buf, int len)
          545  +      {
          546  +        unsigned long s1 = adler & 0xffff;
          547  +        unsigned long s2 = (adler >> 16) & 0xffff;
          548  +        int n;
          549  +
          550  +        for (n = 0; n < len; n++) {
          551  +          s1 = (s1 + buf[n]) % BASE;
          552  +          s2 = (s2 + s1)     % BASE;
          553  +        }
          554  +        return (s2 << 16) + s1;
          555  +      }
          556  +
          557  +      /* Return the adler32 of the bytes buf[0..len-1] */
          558  +
          559  +
          560  +
          561  +
          562  +Deutsch & Gailly             Informational                     [Page 10]
          563  +
          564  +RFC 1950       ZLIB Compressed Data Format Specification        May 1996
          565  +
          566  +
          567  +      unsigned long adler32(unsigned char *buf, int len)
          568  +      {
          569  +        return update_adler32(1L, buf, len);
          570  +      }
          571  +
          572  +
          573  +
          574  +
          575  +
          576  +
          577  +
          578  +
          579  +
          580  +
          581  +
          582  +
          583  +
          584  +
          585  +
          586  +
          587  +
          588  +
          589  +
          590  +
          591  +
          592  +
          593  +
          594  +
          595  +
          596  +
          597  +
          598  +
          599  +
          600  +
          601  +
          602  +
          603  +
          604  +
          605  +
          606  +
          607  +
          608  +
          609  +
          610  +
          611  +
          612  +
          613  +
          614  +
          615  +
          616  +
          617  +
          618  +Deutsch & Gailly             Informational                     [Page 11]
          619  +

Added compat/zlib/doc/rfc1951.txt.

            1  +
            2  +
            3  +
            4  +
            5  +
            6  +
            7  +Network Working Group                                         P. Deutsch
            8  +Request for Comments: 1951                           Aladdin Enterprises
            9  +Category: Informational                                         May 1996
           10  +
           11  +
           12  +        DEFLATE Compressed Data Format Specification version 1.3
           13  +
           14  +Status of This Memo
           15  +
           16  +   This memo provides information for the Internet community.  This memo
           17  +   does not specify an Internet standard of any kind.  Distribution of
           18  +   this memo is unlimited.
           19  +
           20  +IESG Note:
           21  +
           22  +   The IESG takes no position on the validity of any Intellectual
           23  +   Property Rights statements contained in this document.
           24  +
           25  +Notices
           26  +
           27  +   Copyright (c) 1996 L. Peter Deutsch
           28  +
           29  +   Permission is granted to copy and distribute this document for any
           30  +   purpose and without charge, including translations into other
           31  +   languages and incorporation into compilations, provided that the
           32  +   copyright notice and this notice are preserved, and that any
           33  +   substantive changes or deletions from the original are clearly
           34  +   marked.
           35  +
           36  +   A pointer to the latest version of this and related documentation in
           37  +   HTML format can be found at the URL
           38  +   <ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html>.
           39  +
           40  +Abstract
           41  +
           42  +   This specification defines a lossless compressed data format that
           43  +   compresses data using a combination of the LZ77 algorithm and Huffman
           44  +   coding, with efficiency comparable to the best currently available
           45  +   general-purpose compression methods.  The data can be produced or
           46  +   consumed, even for an arbitrarily long sequentially presented input
           47  +   data stream, using only an a priori bounded amount of intermediate
           48  +   storage.  The format can be implemented readily in a manner not
           49  +   covered by patents.
           50  +
           51  +
           52  +
           53  +
           54  +
           55  +
           56  +
           57  +
           58  +Deutsch                      Informational                      [Page 1]
           59  +
           60  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
           61  +
           62  +
           63  +Table of Contents
           64  +
           65  +   1. Introduction ................................................... 2
           66  +      1.1. Purpose ................................................... 2
           67  +      1.2. Intended audience ......................................... 3
           68  +      1.3. Scope ..................................................... 3
           69  +      1.4. Compliance ................................................ 3
           70  +      1.5.  Definitions of terms and conventions used ................ 3
           71  +      1.6. Changes from previous versions ............................ 4
           72  +   2. Compressed representation overview ............................. 4
           73  +   3. Detailed specification ......................................... 5
           74  +      3.1. Overall conventions ....................................... 5
           75  +          3.1.1. Packing into bytes .................................. 5
           76  +      3.2. Compressed block format ................................... 6
           77  +          3.2.1. Synopsis of prefix and Huffman coding ............... 6
           78  +          3.2.2. Use of Huffman coding in the "deflate" format ....... 7
           79  +          3.2.3. Details of block format ............................. 9
           80  +          3.2.4. Non-compressed blocks (BTYPE=00) ................... 11
           81  +          3.2.5. Compressed blocks (length and distance codes) ...... 11
           82  +          3.2.6. Compression with fixed Huffman codes (BTYPE=01) .... 12
           83  +          3.2.7. Compression with dynamic Huffman codes (BTYPE=10) .. 13
           84  +      3.3. Compliance ............................................... 14
           85  +   4. Compression algorithm details ................................. 14
           86  +   5. References .................................................... 16
           87  +   6. Security Considerations ....................................... 16
           88  +   7. Source code ................................................... 16
           89  +   8. Acknowledgements .............................................. 16
           90  +   9. Author's Address .............................................. 17
           91  +
           92  +1. Introduction
           93  +
           94  +   1.1. Purpose
           95  +
           96  +      The purpose of this specification is to define a lossless
           97  +      compressed data format that:
           98  +          * Is independent of CPU type, operating system, file system,
           99  +            and character set, and hence can be used for interchange;
          100  +          * Can be produced or consumed, even for an arbitrarily long
          101  +            sequentially presented input data stream, using only an a
          102  +            priori bounded amount of intermediate storage, and hence
          103  +            can be used in data communications or similar structures
          104  +            such as Unix filters;
          105  +          * Compresses data with efficiency comparable to the best
          106  +            currently available general-purpose compression methods,
          107  +            and in particular considerably better than the "compress"
          108  +            program;
          109  +          * Can be implemented readily in a manner not covered by
          110  +            patents, and hence can be practiced freely;
          111  +
          112  +
          113  +
          114  +Deutsch                      Informational                      [Page 2]
          115  +
          116  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          117  +
          118  +
          119  +          * Is compatible with the file format produced by the current
          120  +            widely used gzip utility, in that conforming decompressors
          121  +            will be able to read data produced by the existing gzip
          122  +            compressor.
          123  +
          124  +      The data format defined by this specification does not attempt to:
          125  +
          126  +          * Allow random access to compressed data;
          127  +          * Compress specialized data (e.g., raster graphics) as well
          128  +            as the best currently available specialized algorithms.
          129  +
          130  +      A simple counting argument shows that no lossless compression
          131  +      algorithm can compress every possible input data set.  For the
          132  +      format defined here, the worst case expansion is 5 bytes per 32K-
          133  +      byte block, i.e., a size increase of 0.015% for large data sets.
          134  +      English text usually compresses by a factor of 2.5 to 3;
          135  +      executable files usually compress somewhat less; graphical data
          136  +      such as raster images may compress much more.
          137  +
          138  +   1.2. Intended audience
          139  +
          140  +      This specification is intended for use by implementors of software
          141  +      to compress data into "deflate" format and/or decompress data from
          142  +      "deflate" format.
          143  +
          144  +      The text of the specification assumes a basic background in
          145  +      programming at the level of bits and other primitive data
          146  +      representations.  Familiarity with the technique of Huffman coding
          147  +      is helpful but not required.
          148  +
          149  +   1.3. Scope
          150  +
          151  +      The specification specifies a method for representing a sequence
          152  +      of bytes as a (usually shorter) sequence of bits, and a method for
          153  +      packing the latter bit sequence into bytes.
          154  +
          155  +   1.4. Compliance
          156  +
          157  +      Unless otherwise indicated below, a compliant decompressor must be
          158  +      able to accept and decompress any data set that conforms to all
          159  +      the specifications presented here; a compliant compressor must
          160  +      produce data sets that conform to all the specifications presented
          161  +      here.
          162  +
          163  +   1.5.  Definitions of terms and conventions used
          164  +
          165  +      Byte: 8 bits stored or transmitted as a unit (same as an octet).
          166  +      For this specification, a byte is exactly 8 bits, even on machines
          167  +
          168  +
          169  +
          170  +Deutsch                      Informational                      [Page 3]
          171  +
          172  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          173  +
          174  +
          175  +      which store a character on a number of bits different from eight.
          176  +      See below, for the numbering of bits within a byte.
          177  +
          178  +      String: a sequence of arbitrary bytes.
          179  +
          180  +   1.6. Changes from previous versions
          181  +
          182  +      There have been no technical changes to the deflate format since
          183  +      version 1.1 of this specification.  In version 1.2, some
          184  +      terminology was changed.  Version 1.3 is a conversion of the
          185  +      specification to RFC style.
          186  +
          187  +2. Compressed representation overview
          188  +
          189  +   A compressed data set consists of a series of blocks, corresponding
          190  +   to successive blocks of input data.  The block sizes are arbitrary,
          191  +   except that non-compressible blocks are limited to 65,535 bytes.
          192  +
          193  +   Each block is compressed using a combination of the LZ77 algorithm
          194  +   and Huffman coding. The Huffman trees for each block are independent
          195  +   of those for previous or subsequent blocks; the LZ77 algorithm may
          196  +   use a reference to a duplicated string occurring in a previous block,
          197  +   up to 32K input bytes before.
          198  +
          199  +   Each block consists of two parts: a pair of Huffman code trees that
          200  +   describe the representation of the compressed data part, and a
          201  +   compressed data part.  (The Huffman trees themselves are compressed
          202  +   using Huffman encoding.)  The compressed data consists of a series of
          203  +   elements of two types: literal bytes (of strings that have not been
          204  +   detected as duplicated within the previous 32K input bytes), and
          205  +   pointers to duplicated strings, where a pointer is represented as a
          206  +   pair <length, backward distance>.  The representation used in the
          207  +   "deflate" format limits distances to 32K bytes and lengths to 258
          208  +   bytes, but does not limit the size of a block, except for
          209  +   uncompressible blocks, which are limited as noted above.
          210  +
          211  +   Each type of value (literals, distances, and lengths) in the
          212  +   compressed data is represented using a Huffman code, using one code
          213  +   tree for literals and lengths and a separate code tree for distances.
          214  +   The code trees for each block appear in a compact form just before
          215  +   the compressed data for that block.
          216  +
          217  +
          218  +
          219  +
          220  +
          221  +
          222  +
          223  +
          224  +
          225  +
          226  +Deutsch                      Informational                      [Page 4]
          227  +
          228  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          229  +
          230  +
          231  +3. Detailed specification
          232  +
          233  +   3.1. Overall conventions In the diagrams below, a box like this:
          234  +
          235  +         +---+
          236  +         |   | <-- the vertical bars might be missing
          237  +         +---+
          238  +
          239  +      represents one byte; a box like this:
          240  +
          241  +         +==============+
          242  +         |              |
          243  +         +==============+
          244  +
          245  +      represents a variable number of bytes.
          246  +
          247  +      Bytes stored within a computer do not have a "bit order", since
          248  +      they are always treated as a unit.  However, a byte considered as
          249  +      an integer between 0 and 255 does have a most- and least-
          250  +      significant bit, and since we write numbers with the most-
          251  +      significant digit on the left, we also write bytes with the most-
          252  +      significant bit on the left.  In the diagrams below, we number the
          253  +      bits of a byte so that bit 0 is the least-significant bit, i.e.,
          254  +      the bits are numbered:
          255  +
          256  +         +--------+
          257  +         |76543210|
          258  +         +--------+
          259  +
          260  +      Within a computer, a number may occupy multiple bytes.  All
          261  +      multi-byte numbers in the format described here are stored with
          262  +      the least-significant byte first (at the lower memory address).
          263  +      For example, the decimal number 520 is stored as:
          264  +
          265  +             0        1
          266  +         +--------+--------+
          267  +         |00001000|00000010|
          268  +         +--------+--------+
          269  +          ^        ^
          270  +          |        |
          271  +          |        + more significant byte = 2 x 256
          272  +          + less significant byte = 8
          273  +
          274  +      3.1.1. Packing into bytes
          275  +
          276  +         This document does not address the issue of the order in which
          277  +         bits of a byte are transmitted on a bit-sequential medium,
          278  +         since the final data format described here is byte- rather than
          279  +
          280  +
          281  +
          282  +Deutsch                      Informational                      [Page 5]
          283  +
          284  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          285  +
          286  +
          287  +         bit-oriented.  However, we describe the compressed block format
          288  +         in below, as a sequence of data elements of various bit
          289  +         lengths, not a sequence of bytes.  We must therefore specify
          290  +         how to pack these data elements into bytes to form the final
          291  +         compressed byte sequence:
          292  +
          293  +             * Data elements are packed into bytes in order of
          294  +               increasing bit number within the byte, i.e., starting
          295  +               with the least-significant bit of the byte.
          296  +             * Data elements other than Huffman codes are packed
          297  +               starting with the least-significant bit of the data
          298  +               element.
          299  +             * Huffman codes are packed starting with the most-
          300  +               significant bit of the code.
          301  +
          302  +         In other words, if one were to print out the compressed data as
          303  +         a sequence of bytes, starting with the first byte at the
          304  +         *right* margin and proceeding to the *left*, with the most-
          305  +         significant bit of each byte on the left as usual, one would be
          306  +         able to parse the result from right to left, with fixed-width
          307  +         elements in the correct MSB-to-LSB order and Huffman codes in
          308  +         bit-reversed order (i.e., with the first bit of the code in the
          309  +         relative LSB position).
          310  +
          311  +   3.2. Compressed block format
          312  +
          313  +      3.2.1. Synopsis of prefix and Huffman coding
          314  +
          315  +         Prefix coding represents symbols from an a priori known
          316  +         alphabet by bit sequences (codes), one code for each symbol, in
          317  +         a manner such that different symbols may be represented by bit
          318  +         sequences of different lengths, but a parser can always parse
          319  +         an encoded string unambiguously symbol-by-symbol.
          320  +
          321  +         We define a prefix code in terms of a binary tree in which the
          322  +         two edges descending from each non-leaf node are labeled 0 and
          323  +         1 and in which the leaf nodes correspond one-for-one with (are
          324  +         labeled with) the symbols of the alphabet; then the code for a
          325  +         symbol is the sequence of 0's and 1's on the edges leading from
          326  +         the root to the leaf labeled with that symbol.  For example:
          327  +
          328  +
          329  +
          330  +
          331  +
          332  +
          333  +
          334  +
          335  +
          336  +
          337  +
          338  +Deutsch                      Informational                      [Page 6]
          339  +
          340  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          341  +
          342  +
          343  +                          /\              Symbol    Code
          344  +                         0  1             ------    ----
          345  +                        /    \                A      00
          346  +                       /\     B               B       1
          347  +                      0  1                    C     011
          348  +                     /    \                   D     010
          349  +                    A     /\
          350  +                         0  1
          351  +                        /    \
          352  +                       D      C
          353  +
          354  +         A parser can decode the next symbol from an encoded input
          355  +         stream by walking down the tree from the root, at each step
          356  +         choosing the edge corresponding to the next input bit.
          357  +
          358  +         Given an alphabet with known symbol frequencies, the Huffman
          359  +         algorithm allows the construction of an optimal prefix code
          360  +         (one which represents strings with those symbol frequencies
          361  +         using the fewest bits of any possible prefix codes for that
          362  +         alphabet).  Such a code is called a Huffman code.  (See
          363  +         reference [1] in Chapter 5, references for additional
          364  +         information on Huffman codes.)
          365  +
          366  +         Note that in the "deflate" format, the Huffman codes for the
          367  +         various alphabets must not exceed certain maximum code lengths.
          368  +         This constraint complicates the algorithm for computing code
          369  +         lengths from symbol frequencies.  Again, see Chapter 5,
          370  +         references for details.
          371  +
          372  +      3.2.2. Use of Huffman coding in the "deflate" format
          373  +
          374  +         The Huffman codes used for each alphabet in the "deflate"
          375  +         format have two additional rules:
          376  +
          377  +             * All codes of a given bit length have lexicographically
          378  +               consecutive values, in the same order as the symbols
          379  +               they represent;
          380  +
          381  +             * Shorter codes lexicographically precede longer codes.
          382  +
          383  +
          384  +
          385  +
          386  +
          387  +
          388  +
          389  +
          390  +
          391  +
          392  +
          393  +
          394  +Deutsch                      Informational                      [Page 7]
          395  +
          396  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          397  +
          398  +
          399  +         We could recode the example above to follow this rule as
          400  +         follows, assuming that the order of the alphabet is ABCD:
          401  +
          402  +            Symbol  Code
          403  +            ------  ----
          404  +            A       10
          405  +            B       0
          406  +            C       110
          407  +            D       111
          408  +
          409  +         I.e., 0 precedes 10 which precedes 11x, and 110 and 111 are
          410  +         lexicographically consecutive.
          411  +
          412  +         Given this rule, we can define the Huffman code for an alphabet
          413  +         just by giving the bit lengths of the codes for each symbol of
          414  +         the alphabet in order; this is sufficient to determine the
          415  +         actual codes.  In our example, the code is completely defined
          416  +         by the sequence of bit lengths (2, 1, 3, 3).  The following
          417  +         algorithm generates the codes as integers, intended to be read
          418  +         from most- to least-significant bit.  The code lengths are
          419  +         initially in tree[I].Len; the codes are produced in
          420  +         tree[I].Code.
          421  +
          422  +         1)  Count the number of codes for each code length.  Let
          423  +             bl_count[N] be the number of codes of length N, N >= 1.
          424  +
          425  +         2)  Find the numerical value of the smallest code for each
          426  +             code length:
          427  +
          428  +                code = 0;
          429  +                bl_count[0] = 0;
          430  +                for (bits = 1; bits <= MAX_BITS; bits++) {
          431  +                    code = (code + bl_count[bits-1]) << 1;
          432  +                    next_code[bits] = code;
          433  +                }
          434  +
          435  +         3)  Assign numerical values to all codes, using consecutive
          436  +             values for all codes of the same length with the base
          437  +             values determined at step 2. Codes that are never used
          438  +             (which have a bit length of zero) must not be assigned a
          439  +             value.
          440  +
          441  +                for (n = 0;  n <= max_code; n++) {
          442  +                    len = tree[n].Len;
          443  +                    if (len != 0) {
          444  +                        tree[n].Code = next_code[len];
          445  +                        next_code[len]++;
          446  +                    }
          447  +
          448  +
          449  +
          450  +Deutsch                      Informational                      [Page 8]
          451  +
          452  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          453  +
          454  +
          455  +                }
          456  +
          457  +         Example:
          458  +
          459  +         Consider the alphabet ABCDEFGH, with bit lengths (3, 3, 3, 3,
          460  +         3, 2, 4, 4).  After step 1, we have:
          461  +
          462  +            N      bl_count[N]
          463  +            -      -----------
          464  +            2      1
          465  +            3      5
          466  +            4      2
          467  +
          468  +         Step 2 computes the following next_code values:
          469  +
          470  +            N      next_code[N]
          471  +            -      ------------
          472  +            1      0
          473  +            2      0
          474  +            3      2
          475  +            4      14
          476  +
          477  +         Step 3 produces the following code values:
          478  +
          479  +            Symbol Length   Code
          480  +            ------ ------   ----
          481  +            A       3        010
          482  +            B       3        011
          483  +            C       3        100
          484  +            D       3        101
          485  +            E       3        110
          486  +            F       2         00
          487  +            G       4       1110
          488  +            H       4       1111
          489  +
          490  +      3.2.3. Details of block format
          491  +
          492  +         Each block of compressed data begins with 3 header bits
          493  +         containing the following data:
          494  +
          495  +            first bit       BFINAL
          496  +            next 2 bits     BTYPE
          497  +
          498  +         Note that the header bits do not necessarily begin on a byte
          499  +         boundary, since a block does not necessarily occupy an integral
          500  +         number of bytes.
          501  +
          502  +
          503  +
          504  +
          505  +
          506  +Deutsch                      Informational                      [Page 9]
          507  +
          508  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          509  +
          510  +
          511  +         BFINAL is set if and only if this is the last block of the data
          512  +         set.
          513  +
          514  +         BTYPE specifies how the data are compressed, as follows:
          515  +
          516  +            00 - no compression
          517  +            01 - compressed with fixed Huffman codes
          518  +            10 - compressed with dynamic Huffman codes
          519  +            11 - reserved (error)
          520  +
          521  +         The only difference between the two compressed cases is how the
          522  +         Huffman codes for the literal/length and distance alphabets are
          523  +         defined.
          524  +
          525  +         In all cases, the decoding algorithm for the actual data is as
          526  +         follows:
          527  +
          528  +            do
          529  +               read block header from input stream.
          530  +               if stored with no compression
          531  +                  skip any remaining bits in current partially
          532  +                     processed byte
          533  +                  read LEN and NLEN (see next section)
          534  +                  copy LEN bytes of data to output
          535  +               otherwise
          536  +                  if compressed with dynamic Huffman codes
          537  +                     read representation of code trees (see
          538  +                        subsection below)
          539  +                  loop (until end of block code recognized)
          540  +                     decode literal/length value from input stream
          541  +                     if value < 256
          542  +                        copy value (literal byte) to output stream
          543  +                     otherwise
          544  +                        if value = end of block (256)
          545  +                           break from loop
          546  +                        otherwise (value = 257..285)
          547  +                           decode distance from input stream
          548  +
          549  +                           move backwards distance bytes in the output
          550  +                           stream, and copy length bytes from this
          551  +                           position to the output stream.
          552  +                  end loop
          553  +            while not last block
          554  +
          555  +         Note that a duplicated string reference may refer to a string
          556  +         in a previous block; i.e., the backward distance may cross one
          557  +         or more block boundaries.  However a distance cannot refer past
          558  +         the beginning of the output stream.  (An application using a
          559  +
          560  +
          561  +
          562  +Deutsch                      Informational                     [Page 10]
          563  +
          564  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          565  +
          566  +
          567  +         preset dictionary might discard part of the output stream; a
          568  +         distance can refer to that part of the output stream anyway)
          569  +         Note also that the referenced string may overlap the current
          570  +         position; for example, if the last 2 bytes decoded have values
          571  +         X and Y, a string reference with <length = 5, distance = 2>
          572  +         adds X,Y,X,Y,X to the output stream.
          573  +
          574  +         We now specify each compression method in turn.
          575  +
          576  +      3.2.4. Non-compressed blocks (BTYPE=00)
          577  +
          578  +         Any bits of input up to the next byte boundary are ignored.
          579  +         The rest of the block consists of the following information:
          580  +
          581  +              0   1   2   3   4...
          582  +            +---+---+---+---+================================+
          583  +            |  LEN  | NLEN  |... LEN bytes of literal data...|
          584  +            +---+---+---+---+================================+
          585  +
          586  +         LEN is the number of data bytes in the block.  NLEN is the
          587  +         one's complement of LEN.
          588  +
          589  +      3.2.5. Compressed blocks (length and distance codes)
          590  +
          591  +         As noted above, encoded data blocks in the "deflate" format
          592  +         consist of sequences of symbols drawn from three conceptually
          593  +         distinct alphabets: either literal bytes, from the alphabet of
          594  +         byte values (0..255), or <length, backward distance> pairs,
          595  +         where the length is drawn from (3..258) and the distance is
          596  +         drawn from (1..32,768).  In fact, the literal and length
          597  +         alphabets are merged into a single alphabet (0..285), where
          598  +         values 0..255 represent literal bytes, the value 256 indicates
          599  +         end-of-block, and values 257..285 represent length codes
          600  +         (possibly in conjunction with extra bits following the symbol
          601  +         code) as follows:
          602  +
          603  +
          604  +
          605  +
          606  +
          607  +
          608  +
          609  +
          610  +
          611  +
          612  +
          613  +
          614  +
          615  +
          616  +
          617  +
          618  +Deutsch                      Informational                     [Page 11]
          619  +
          620  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          621  +
          622  +
          623  +                 Extra               Extra               Extra
          624  +            Code Bits Length(s) Code Bits Lengths   Code Bits Length(s)
          625  +            ---- ---- ------     ---- ---- -------   ---- ---- -------
          626  +             257   0     3       267   1   15,16     277   4   67-82
          627  +             258   0     4       268   1   17,18     278   4   83-98
          628  +             259   0     5       269   2   19-22     279   4   99-114
          629  +             260   0     6       270   2   23-26     280   4  115-130
          630  +             261   0     7       271   2   27-30     281   5  131-162
          631  +             262   0     8       272   2   31-34     282   5  163-194
          632  +             263   0     9       273   3   35-42     283   5  195-226
          633  +             264   0    10       274   3   43-50     284   5  227-257
          634  +             265   1  11,12      275   3   51-58     285   0    258
          635  +             266   1  13,14      276   3   59-66
          636  +
          637  +         The extra bits should be interpreted as a machine integer
          638  +         stored with the most-significant bit first, e.g., bits 1110
          639  +         represent the value 14.
          640  +
          641  +                  Extra           Extra               Extra
          642  +             Code Bits Dist  Code Bits   Dist     Code Bits Distance
          643  +             ---- ---- ----  ---- ----  ------    ---- ---- --------
          644  +               0   0    1     10   4     33-48    20    9   1025-1536
          645  +               1   0    2     11   4     49-64    21    9   1537-2048
          646  +               2   0    3     12   5     65-96    22   10   2049-3072
          647  +               3   0    4     13   5     97-128   23   10   3073-4096
          648  +               4   1   5,6    14   6    129-192   24   11   4097-6144
          649  +               5   1   7,8    15   6    193-256   25   11   6145-8192
          650  +               6   2   9-12   16   7    257-384   26   12  8193-12288
          651  +               7   2  13-16   17   7    385-512   27   12 12289-16384
          652  +               8   3  17-24   18   8    513-768   28   13 16385-24576
          653  +               9   3  25-32   19   8   769-1024   29   13 24577-32768
          654  +
          655  +      3.2.6. Compression with fixed Huffman codes (BTYPE=01)
          656  +
          657  +         The Huffman codes for the two alphabets are fixed, and are not
          658  +         represented explicitly in the data.  The Huffman code lengths
          659  +         for the literal/length alphabet are:
          660  +
          661  +                   Lit Value    Bits        Codes
          662  +                   ---------    ----        -----
          663  +                     0 - 143     8          00110000 through
          664  +                                            10111111
          665  +                   144 - 255     9          110010000 through
          666  +                                            111111111
          667  +                   256 - 279     7          0000000 through
          668  +                                            0010111
          669  +                   280 - 287     8          11000000 through
          670  +                                            11000111
          671  +
          672  +
          673  +
          674  +Deutsch                      Informational                     [Page 12]
          675  +
          676  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          677  +
          678  +
          679  +         The code lengths are sufficient to generate the actual codes,
          680  +         as described above; we show the codes in the table for added
          681  +         clarity.  Literal/length values 286-287 will never actually
          682  +         occur in the compressed data, but participate in the code
          683  +         construction.
          684  +
          685  +         Distance codes 0-31 are represented by (fixed-length) 5-bit
          686  +         codes, with possible additional bits as shown in the table
          687  +         shown in Paragraph 3.2.5, above.  Note that distance codes 30-
          688  +         31 will never actually occur in the compressed data.
          689  +
          690  +      3.2.7. Compression with dynamic Huffman codes (BTYPE=10)
          691  +
          692  +         The Huffman codes for the two alphabets appear in the block
          693  +         immediately after the header bits and before the actual
          694  +         compressed data, first the literal/length code and then the
          695  +         distance code.  Each code is defined by a sequence of code
          696  +         lengths, as discussed in Paragraph 3.2.2, above.  For even
          697  +         greater compactness, the code length sequences themselves are
          698  +         compressed using a Huffman code.  The alphabet for code lengths
          699  +         is as follows:
          700  +
          701  +               0 - 15: Represent code lengths of 0 - 15
          702  +                   16: Copy the previous code length 3 - 6 times.
          703  +                       The next 2 bits indicate repeat length
          704  +                             (0 = 3, ... , 3 = 6)
          705  +                          Example:  Codes 8, 16 (+2 bits 11),
          706  +                                    16 (+2 bits 10) will expand to
          707  +                                    12 code lengths of 8 (1 + 6 + 5)
          708  +                   17: Repeat a code length of 0 for 3 - 10 times.
          709  +                       (3 bits of length)
          710  +                   18: Repeat a code length of 0 for 11 - 138 times
          711  +                       (7 bits of length)
          712  +
          713  +         A code length of 0 indicates that the corresponding symbol in
          714  +         the literal/length or distance alphabet will not occur in the
          715  +         block, and should not participate in the Huffman code
          716  +         construction algorithm given earlier.  If only one distance
          717  +         code is used, it is encoded using one bit, not zero bits; in
          718  +         this case there is a single code length of one, with one unused
          719  +         code.  One distance code of zero bits means that there are no
          720  +         distance codes used at all (the data is all literals).
          721  +
          722  +         We can now define the format of the block:
          723  +
          724  +               5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
          725  +               5 Bits: HDIST, # of Distance codes - 1        (1 - 32)
          726  +               4 Bits: HCLEN, # of Code Length codes - 4     (4 - 19)
          727  +
          728  +
          729  +
          730  +Deutsch                      Informational                     [Page 13]
          731  +
          732  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          733  +
          734  +
          735  +               (HCLEN + 4) x 3 bits: code lengths for the code length
          736  +                  alphabet given just above, in the order: 16, 17, 18,
          737  +                  0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
          738  +
          739  +                  These code lengths are interpreted as 3-bit integers
          740  +                  (0-7); as above, a code length of 0 means the
          741  +                  corresponding symbol (literal/length or distance code
          742  +                  length) is not used.
          743  +
          744  +               HLIT + 257 code lengths for the literal/length alphabet,
          745  +                  encoded using the code length Huffman code
          746  +
          747  +               HDIST + 1 code lengths for the distance alphabet,
          748  +                  encoded using the code length Huffman code
          749  +
          750  +               The actual compressed data of the block,
          751  +                  encoded using the literal/length and distance Huffman
          752  +                  codes
          753  +
          754  +               The literal/length symbol 256 (end of data),
          755  +                  encoded using the literal/length Huffman code
          756  +
          757  +         The code length repeat codes can cross from HLIT + 257 to the
          758  +         HDIST + 1 code lengths.  In other words, all code lengths form
          759  +         a single sequence of HLIT + HDIST + 258 values.
          760  +
          761  +   3.3. Compliance
          762  +
          763  +      A compressor may limit further the ranges of values specified in
          764  +      the previous section and still be compliant; for example, it may
          765  +      limit the range of backward pointers to some value smaller than
          766  +      32K.  Similarly, a compressor may limit the size of blocks so that
          767  +      a compressible block fits in memory.
          768  +
          769  +      A compliant decompressor must accept the full range of possible
          770  +      values defined in the previous section, and must accept blocks of
          771  +      arbitrary size.
          772  +
          773  +4. Compression algorithm details
          774  +
          775  +   While it is the intent of this document to define the "deflate"
          776  +   compressed data format without reference to any particular
          777  +   compression algorithm, the format is related to the compressed
          778  +   formats produced by LZ77 (Lempel-Ziv 1977, see reference [2] below);
          779  +   since many variations of LZ77 are patented, it is strongly
          780  +   recommended that the implementor of a compressor follow the general
          781  +   algorithm presented here, which is known not to be patented per se.
          782  +   The material in this section is not part of the definition of the
          783  +
          784  +
          785  +
          786  +Deutsch                      Informational                     [Page 14]
          787  +
          788  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          789  +
          790  +
          791  +   specification per se, and a compressor need not follow it in order to
          792  +   be compliant.
          793  +
          794  +   The compressor terminates a block when it determines that starting a
          795  +   new block with fresh trees would be useful, or when the block size
          796  +   fills up the compressor's block buffer.
          797  +
          798  +   The compressor uses a chained hash table to find duplicated strings,
          799  +   using a hash function that operates on 3-byte sequences.  At any
          800  +   given point during compression, let XYZ be the next 3 input bytes to
          801  +   be examined (not necessarily all different, of course).  First, the
          802  +   compressor examines the hash chain for XYZ.  If the chain is empty,
          803  +   the compressor simply writes out X as a literal byte and advances one
          804  +   byte in the input.  If the hash chain is not empty, indicating that
          805  +   the sequence XYZ (or, if we are unlucky, some other 3 bytes with the
          806  +   same hash function value) has occurred recently, the compressor
          807  +   compares all strings on the XYZ hash chain with the actual input data
          808  +   sequence starting at the current point, and selects the longest
          809  +   match.
          810  +
          811  +   The compressor searches the hash chains starting with the most recent
          812  +   strings, to favor small distances and thus take advantage of the
          813  +   Huffman encoding.  The hash chains are singly linked. There are no
          814  +   deletions from the hash chains; the algorithm simply discards matches
          815  +   that are too old.  To avoid a worst-case situation, very long hash
          816  +   chains are arbitrarily truncated at a certain length, determined by a
          817  +   run-time parameter.
          818  +
          819  +   To improve overall compression, the compressor optionally defers the
          820  +   selection of matches ("lazy matching"): after a match of length N has
          821  +   been found, the compressor searches for a longer match starting at
          822  +   the next input byte.  If it finds a longer match, it truncates the
          823  +   previous match to a length of one (thus producing a single literal
          824  +   byte) and then emits the longer match.  Otherwise, it emits the
          825  +   original match, and, as described above, advances N bytes before
          826  +   continuing.
          827  +
          828  +   Run-time parameters also control this "lazy match" procedure.  If
          829  +   compression ratio is most important, the compressor attempts a
          830  +   complete second search regardless of the length of the first match.
          831  +   In the normal case, if the current match is "long enough", the
          832  +   compressor reduces the search for a longer match, thus speeding up
          833  +   the process.  If speed is most important, the compressor inserts new
          834  +   strings in the hash table only when no match was found, or when the
          835  +   match is not "too long".  This degrades the compression ratio but
          836  +   saves time since there are both fewer insertions and fewer searches.
          837  +
          838  +
          839  +
          840  +
          841  +
          842  +Deutsch                      Informational                     [Page 15]
          843  +
          844  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          845  +
          846  +
          847  +5. References
          848  +
          849  +   [1] Huffman, D. A., "A Method for the Construction of Minimum
          850  +       Redundancy Codes", Proceedings of the Institute of Radio
          851  +       Engineers, September 1952, Volume 40, Number 9, pp. 1098-1101.
          852  +
          853  +   [2] Ziv J., Lempel A., "A Universal Algorithm for Sequential Data
          854  +       Compression", IEEE Transactions on Information Theory, Vol. 23,
          855  +       No. 3, pp. 337-343.
          856  +
          857  +   [3] Gailly, J.-L., and Adler, M., ZLIB documentation and sources,
          858  +       available in ftp://ftp.uu.net/pub/archiving/zip/doc/
          859  +
          860  +   [4] Gailly, J.-L., and Adler, M., GZIP documentation and sources,
          861  +       available as gzip-*.tar in ftp://prep.ai.mit.edu/pub/gnu/
          862  +
          863  +   [5] Schwartz, E. S., and Kallick, B. "Generating a canonical prefix
          864  +       encoding." Comm. ACM, 7,3 (Mar. 1964), pp. 166-169.
          865  +
          866  +   [6] Hirschberg and Lelewer, "Efficient decoding of prefix codes,"
          867  +       Comm. ACM, 33,4, April 1990, pp. 449-459.
          868  +
          869  +6. Security Considerations
          870  +
          871  +   Any data compression method involves the reduction of redundancy in
          872  +   the data.  Consequently, any corruption of the data is likely to have
          873  +   severe effects and be difficult to correct.  Uncompressed text, on
          874  +   the other hand, will probably still be readable despite the presence
          875  +   of some corrupted bytes.
          876  +
          877  +   It is recommended that systems using this data format provide some
          878  +   means of validating the integrity of the compressed data.  See
          879  +   reference [3], for example.
          880  +
          881  +7. Source code
          882  +
          883  +   Source code for a C language implementation of a "deflate" compliant
          884  +   compressor and decompressor is available within the zlib package at
          885  +   ftp://ftp.uu.net/pub/archiving/zip/zlib/.
          886  +
          887  +8. Acknowledgements
          888  +
          889  +   Trademarks cited in this document are the property of their
          890  +   respective owners.
          891  +
          892  +   Phil Katz designed the deflate format.  Jean-Loup Gailly and Mark
          893  +   Adler wrote the related software described in this specification.
          894  +   Glenn Randers-Pehrson converted this document to RFC and HTML format.
          895  +
          896  +
          897  +
          898  +Deutsch                      Informational                     [Page 16]
          899  +
          900  +RFC 1951      DEFLATE Compressed Data Format Specification      May 1996
          901  +
          902  +
          903  +9. Author's Address
          904  +
          905  +   L. Peter Deutsch
          906  +   Aladdin Enterprises
          907  +   203 Santa Margarita Ave.
          908  +   Menlo Park, CA 94025
          909  +
          910  +   Phone: (415) 322-0103 (AM only)
          911  +   FAX:   (415) 322-1734
          912  +   EMail: <ghost@aladdin.com>
          913  +
          914  +   Questions about the technical content of this specification can be
          915  +   sent by email to:
          916  +
          917  +   Jean-Loup Gailly <gzip@prep.ai.mit.edu> and
          918  +   Mark Adler <madler@alumni.caltech.edu>
          919  +
          920  +   Editorial comments on this specification can be sent by email to:
          921  +
          922  +   L. Peter Deutsch <ghost@aladdin.com> and
          923  +   Glenn Randers-Pehrson <randeg@alumni.rpi.edu>
          924  +
          925  +
          926  +
          927  +
          928  +
          929  +
          930  +
          931  +
          932  +
          933  +
          934  +
          935  +
          936  +
          937  +
          938  +
          939  +
          940  +
          941  +
          942  +
          943  +
          944  +
          945  +
          946  +
          947  +
          948  +
          949  +
          950  +
          951  +
          952  +
          953  +
          954  +Deutsch                      Informational                     [Page 17]
          955  +

Added compat/zlib/doc/rfc1952.txt.

            1  +
            2  +
            3  +
            4  +
            5  +
            6  +
            7  +Network Working Group                                         P. Deutsch
            8  +Request for Comments: 1952                           Aladdin Enterprises
            9  +Category: Informational                                         May 1996
           10  +
           11  +
           12  +               GZIP file format specification version 4.3
           13  +
           14  +Status of This Memo
           15  +
           16  +   This memo provides information for the Internet community.  This memo
           17  +   does not specify an Internet standard of any kind.  Distribution of
           18  +   this memo is unlimited.
           19  +
           20  +IESG Note:
           21  +
           22  +   The IESG takes no position on the validity of any Intellectual
           23  +   Property Rights statements contained in this document.
           24  +
           25  +Notices
           26  +
           27  +   Copyright (c) 1996 L. Peter Deutsch
           28  +
           29  +   Permission is granted to copy and distribute this document for any
           30  +   purpose and without charge, including translations into other
           31  +   languages and incorporation into compilations, provided that the
           32  +   copyright notice and this notice are preserved, and that any
           33  +   substantive changes or deletions from the original are clearly
           34  +   marked.
           35  +
           36  +   A pointer to the latest version of this and related documentation in
           37  +   HTML format can be found at the URL
           38  +   <ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html>.
           39  +
           40  +Abstract
           41  +
           42  +   This specification defines a lossless compressed data format that is
           43  +   compatible with the widely used GZIP utility.  The format includes a
           44  +   cyclic redundancy check value for detecting data corruption.  The
           45  +   format presently uses the DEFLATE method of compression but can be
           46  +   easily extended to use other compression methods.  The format can be
           47  +   implemented readily in a manner not covered by patents.
           48  +
           49  +
           50  +
           51  +
           52  +
           53  +
           54  +
           55  +
           56  +
           57  +
           58  +Deutsch                      Informational                      [Page 1]
           59  +
           60  +RFC 1952             GZIP File Format Specification             May 1996
           61  +
           62  +
           63  +Table of Contents
           64  +
           65  +   1. Introduction ................................................... 2
           66  +      1.1. Purpose ................................................... 2
           67  +      1.2. Intended audience ......................................... 3
           68  +      1.3. Scope ..................................................... 3
           69  +      1.4. Compliance ................................................ 3
           70  +      1.5. Definitions of terms and conventions used ................. 3
           71  +      1.6. Changes from previous versions ............................ 3
           72  +   2. Detailed specification ......................................... 4
           73  +      2.1. Overall conventions ....................................... 4
           74  +      2.2. File format ............................................... 5
           75  +      2.3. Member format ............................................. 5
           76  +          2.3.1. Member header and trailer ........................... 6
           77  +              2.3.1.1. Extra field ................................... 8
           78  +              2.3.1.2. Compliance .................................... 9
           79  +      3. References .................................................. 9
           80  +      4. Security Considerations .................................... 10
           81  +      5. Acknowledgements ........................................... 10
           82  +      6. Author's Address ........................................... 10
           83  +      7. Appendix: Jean-Loup Gailly's gzip utility .................. 11
           84  +      8. Appendix: Sample CRC Code .................................. 11
           85  +
           86  +1. Introduction
           87  +
           88  +   1.1. Purpose
           89  +
           90  +      The purpose of this specification is to define a lossless
           91  +      compressed data format that:
           92  +
           93  +          * Is independent of CPU type, operating system, file system,
           94  +            and character set, and hence can be used for interchange;
           95  +          * Can compress or decompress a data stream (as opposed to a
           96  +            randomly accessible file) to produce another data stream,
           97  +            using only an a priori bounded amount of intermediate
           98  +            storage, and hence can be used in data communications or
           99  +            similar structures such as Unix filters;
          100  +          * Compresses data with efficiency comparable to the best
          101  +            currently available general-purpose compression methods,
          102  +            and in particular considerably better than the "compress"
          103  +            program;
          104  +          * Can be implemented readily in a manner not covered by
          105  +            patents, and hence can be practiced freely;
          106  +          * Is compatible with the file format produced by the current
          107  +            widely used gzip utility, in that conforming decompressors
          108  +            will be able to read data produced by the existing gzip
          109  +            compressor.
          110  +
          111  +
          112  +
          113  +
          114  +Deutsch                      Informational                      [Page 2]
          115  +
          116  +RFC 1952             GZIP File Format Specification             May 1996
          117  +
          118  +
          119  +      The data format defined by this specification does not attempt to:
          120  +
          121  +          * Provide random access to compressed data;
          122  +          * Compress specialized data (e.g., raster graphics) as well as
          123  +            the best currently available specialized algorithms.
          124  +
          125  +   1.2. Intended audience
          126  +
          127  +      This specification is intended for use by implementors of software
          128  +      to compress data into gzip format and/or decompress data from gzip
          129  +      format.
          130  +
          131  +      The text of the specification assumes a basic background in
          132  +      programming at the level of bits and other primitive data
          133  +      representations.
          134  +
          135  +   1.3. Scope
          136  +
          137  +      The specification specifies a compression method and a file format
          138  +      (the latter assuming only that a file can store a sequence of
          139  +      arbitrary bytes).  It does not specify any particular interface to
          140  +      a file system or anything about character sets or encodings
          141  +      (except for file names and comments, which are optional).
          142  +
          143  +   1.4. Compliance
          144  +
          145  +      Unless otherwise indicated below, a compliant decompressor must be
          146  +      able to accept and decompress any file that conforms to all the
          147  +      specifications presented here; a compliant compressor must produce
          148  +      files that conform to all the specifications presented here.  The
          149  +      material in the appendices is not part of the specification per se
          150  +      and is not relevant to compliance.
          151  +
          152  +   1.5. Definitions of terms and conventions used
          153  +
          154  +      byte: 8 bits stored or transmitted as a unit (same as an octet).
          155  +      (For this specification, a byte is exactly 8 bits, even on
          156  +      machines which store a character on a number of bits different
          157  +      from 8.)  See below for the numbering of bits within a byte.
          158  +
          159  +   1.6. Changes from previous versions
          160  +
          161  +      There have been no technical changes to the gzip format since
          162  +      version 4.1 of this specification.  In version 4.2, some
          163  +      terminology was changed, and the sample CRC code was rewritten for
          164  +      clarity and to eliminate the requirement for the caller to do pre-
          165  +      and post-conditioning.  Version 4.3 is a conversion of the
          166  +      specification to RFC style.
          167  +
          168  +
          169  +
          170  +Deutsch                      Informational                      [Page 3]
          171  +
          172  +RFC 1952             GZIP File Format Specification             May 1996
          173  +
          174  +
          175  +2. Detailed specification
          176  +
          177  +   2.1. Overall conventions
          178  +
          179  +      In the diagrams below, a box like this:
          180  +
          181  +         +---+
          182  +         |   | <-- the vertical bars might be missing
          183  +         +---+
          184  +
          185  +      represents one byte; a box like this:
          186  +
          187  +         +==============+
          188  +         |              |
          189  +         +==============+
          190  +
          191  +      represents a variable number of bytes.
          192  +
          193  +      Bytes stored within a computer do not have a "bit order", since
          194  +      they are always treated as a unit.  However, a byte considered as
          195  +      an integer between 0 and 255 does have a most- and least-
          196  +      significant bit, and since we write numbers with the most-
          197  +      significant digit on the left, we also write bytes with the most-
          198  +      significant bit on the left.  In the diagrams below, we number the
          199  +      bits of a byte so that bit 0 is the least-significant bit, i.e.,
          200  +      the bits are numbered:
          201  +
          202  +         +--------+
          203  +         |76543210|
          204  +         +--------+
          205  +
          206  +      This document does not address the issue of the order in which
          207  +      bits of a byte are transmitted on a bit-sequential medium, since
          208  +      the data format described here is byte- rather than bit-oriented.
          209  +
          210  +      Within a computer, a number may occupy multiple bytes.  All
          211  +      multi-byte numbers in the format described here are stored with
          212  +      the least-significant byte first (at the lower memory address).
          213  +      For example, the decimal number 520 is stored as:
          214  +
          215  +             0        1
          216  +         +--------+--------+
          217  +         |00001000|00000010|
          218  +         +--------+--------+
          219  +          ^        ^
          220  +          |        |
          221  +          |        + more significant byte = 2 x 256
          222  +          + less significant byte = 8
          223  +
          224  +
          225  +
          226  +Deutsch                      Informational                      [Page 4]
          227  +
          228  +RFC 1952             GZIP File Format Specification             May 1996
          229  +
          230  +
          231  +   2.2. File format
          232  +
          233  +      A gzip file consists of a series of "members" (compressed data
          234  +      sets).  The format of each member is specified in the following
          235  +      section.  The members simply appear one after another in the file,
          236  +      with no additional information before, between, or after them.
          237  +
          238  +   2.3. Member format
          239  +
          240  +      Each member has the following structure:
          241  +
          242  +         +---+---+---+---+---+---+---+---+---+---+
          243  +         |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)
          244  +         +---+---+---+---+---+---+---+---+---+---+
          245  +
          246  +      (if FLG.FEXTRA set)
          247  +
          248  +         +---+---+=================================+
          249  +         | XLEN  |...XLEN bytes of "extra field"...| (more-->)
          250  +         +---+---+=================================+
          251  +
          252  +      (if FLG.FNAME set)
          253  +
          254  +         +=========================================+
          255  +         |...original file name, zero-terminated...| (more-->)
          256  +         +=========================================+
          257  +
          258  +      (if FLG.FCOMMENT set)
          259  +
          260  +         +===================================+
          261  +         |...file comment, zero-terminated...| (more-->)
          262  +         +===================================+
          263  +
          264  +      (if FLG.FHCRC set)
          265  +
          266  +         +---+---+
          267  +         | CRC16 |
          268  +         +---+---+
          269  +
          270  +         +=======================+
          271  +         |...compressed blocks...| (more-->)
          272  +         +=======================+
          273  +
          274  +           0   1   2   3   4   5   6   7
          275  +         +---+---+---+---+---+---+---+---+
          276  +         |     CRC32     |     ISIZE     |
          277  +         +---+---+---+---+---+---+---+---+
          278  +
          279  +
          280  +
          281  +
          282  +Deutsch                      Informational                      [Page 5]
          283  +
          284  +RFC 1952             GZIP File Format Specification             May 1996
          285  +
          286  +
          287  +      2.3.1. Member header and trailer
          288  +
          289  +         ID1 (IDentification 1)
          290  +         ID2 (IDentification 2)
          291  +            These have the fixed values ID1 = 31 (0x1f, \037), ID2 = 139
          292  +            (0x8b, \213), to identify the file as being in gzip format.
          293  +
          294  +         CM (Compression Method)
          295  +            This identifies the compression method used in the file.  CM
          296  +            = 0-7 are reserved.  CM = 8 denotes the "deflate"
          297  +            compression method, which is the one customarily used by
          298  +            gzip and which is documented elsewhere.
          299  +
          300  +         FLG (FLaGs)
          301  +            This flag byte is divided into individual bits as follows:
          302  +
          303  +               bit 0   FTEXT
          304  +               bit 1   FHCRC
          305  +               bit 2   FEXTRA
          306  +               bit 3   FNAME
          307  +               bit 4   FCOMMENT
          308  +               bit 5   reserved
          309  +               bit 6   reserved
          310  +               bit 7   reserved
          311  +
          312  +            If FTEXT is set, the file is probably ASCII text.  This is
          313  +            an optional indication, which the compressor may set by
          314  +            checking a small amount of the input data to see whether any
          315  +            non-ASCII characters are present.  In case of doubt, FTEXT
          316  +            is cleared, indicating binary data. For systems which have
          317  +            different file formats for ascii text and binary data, the
          318  +            decompressor can use FTEXT to choose the appropriate format.
          319  +            We deliberately do not specify the algorithm used to set
          320  +            this bit, since a compressor always has the option of
          321  +            leaving it cleared and a decompressor always has the option
          322  +            of ignoring it and letting some other program handle issues
          323  +            of data conversion.
          324  +
          325  +            If FHCRC is set, a CRC16 for the gzip header is present,
          326  +            immediately before the compressed data. The CRC16 consists
          327  +            of the two least significant bytes of the CRC32 for all
          328  +            bytes of the gzip header up to and not including the CRC16.
          329  +            [The FHCRC bit was never set by versions of gzip up to
          330  +            1.2.4, even though it was documented with a different
          331  +            meaning in gzip 1.2.4.]
          332  +
          333  +            If FEXTRA is set, optional extra fields are present, as
          334  +            described in a following section.
          335  +
          336  +
          337  +
          338  +Deutsch                      Informational                      [Page 6]
          339  +
          340  +RFC 1952             GZIP File Format Specification             May 1996
          341  +
          342  +
          343  +            If FNAME is set, an original file name is present,
          344  +            terminated by a zero byte.  The name must consist of ISO
          345  +            8859-1 (LATIN-1) characters; on operating systems using
          346  +            EBCDIC or any other character set for file names, the name
          347  +            must be translated to the ISO LATIN-1 character set.  This
          348  +            is the original name of the file being compressed, with any
          349  +            directory components removed, and, if the file being
          350  +            compressed is on a file system with case insensitive names,
          351  +            forced to lower case. There is no original file name if the
          352  +            data was compressed from a source other than a named file;
          353  +            for example, if the source was stdin on a Unix system, there
          354  +            is no file name.
          355  +
          356  +            If FCOMMENT is set, a zero-terminated file comment is
          357  +            present.  This comment is not interpreted; it is only
          358  +            intended for human consumption.  The comment must consist of
          359  +            ISO 8859-1 (LATIN-1) characters.  Line breaks should be
          360  +            denoted by a single line feed character (10 decimal).
          361  +
          362  +            Reserved FLG bits must be zero.
          363  +
          364  +         MTIME (Modification TIME)
          365  +            This gives the most recent modification time of the original
          366  +            file being compressed.  The time is in Unix format, i.e.,
          367  +            seconds since 00:00:00 GMT, Jan.  1, 1970.  (Note that this
          368  +            may cause problems for MS-DOS and other systems that use
          369  +            local rather than Universal time.)  If the compressed data
          370  +            did not come from a file, MTIME is set to the time at which
          371  +            compression started.  MTIME = 0 means no time stamp is
          372  +            available.
          373  +
          374  +         XFL (eXtra FLags)
          375  +            These flags are available for use by specific compression
          376  +            methods.  The "deflate" method (CM = 8) sets these flags as
          377  +            follows:
          378  +
          379  +               XFL = 2 - compressor used maximum compression,
          380  +                         slowest algorithm
          381  +               XFL = 4 - compressor used fastest algorithm
          382  +
          383  +         OS (Operating System)
          384  +            This identifies the type of file system on which compression
          385  +            took place.  This may be useful in determining end-of-line
          386  +            convention for text files.  The currently defined values are
          387  +            as follows:
          388  +
          389  +
          390  +
          391  +
          392  +
          393  +
          394  +Deutsch                      Informational                      [Page 7]
          395  +
          396  +RFC 1952             GZIP File Format Specification             May 1996
          397  +
          398  +
          399  +                 0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
          400  +                 1 - Amiga
          401  +                 2 - VMS (or OpenVMS)
          402  +                 3 - Unix
          403  +                 4 - VM/CMS
          404  +                 5 - Atari TOS
          405  +                 6 - HPFS filesystem (OS/2, NT)
          406  +                 7 - Macintosh
          407  +                 8 - Z-System
          408  +                 9 - CP/M
          409  +                10 - TOPS-20
          410  +                11 - NTFS filesystem (NT)
          411  +                12 - QDOS
          412  +                13 - Acorn RISCOS
          413  +               255 - unknown
          414  +
          415  +         XLEN (eXtra LENgth)
          416  +            If FLG.FEXTRA is set, this gives the length of the optional
          417  +            extra field.  See below for details.
          418  +
          419  +         CRC32 (CRC-32)
          420  +            This contains a Cyclic Redundancy Check value of the
          421  +            uncompressed data computed according to CRC-32 algorithm
          422  +            used in the ISO 3309 standard and in section 8.1.1.6.2 of
          423  +            ITU-T recommendation V.42.  (See http://www.iso.ch for
          424  +            ordering ISO documents. See gopher://info.itu.ch for an
          425  +            online version of ITU-T V.42.)
          426  +
          427  +         ISIZE (Input SIZE)
          428  +            This contains the size of the original (uncompressed) input
          429  +            data modulo 2^32.
          430  +
          431  +      2.3.1.1. Extra field
          432  +
          433  +         If the FLG.FEXTRA bit is set, an "extra field" is present in
          434  +         the header, with total length XLEN bytes.  It consists of a
          435  +         series of subfields, each of the form:
          436  +
          437  +            +---+---+---+---+==================================+
          438  +            |SI1|SI2|  LEN  |... LEN bytes of subfield data ...|
          439  +            +---+---+---+---+==================================+
          440  +
          441  +         SI1 and SI2 provide a subfield ID, typically two ASCII letters
          442  +         with some mnemonic value.  Jean-Loup Gailly
          443  +         <gzip@prep.ai.mit.edu> is maintaining a registry of subfield
          444  +         IDs; please send him any subfield ID you wish to use.  Subfield
          445  +         IDs with SI2 = 0 are reserved for future use.  The following
          446  +         IDs are currently defined:
          447  +
          448  +
          449  +
          450  +Deutsch                      Informational                      [Page 8]
          451  +
          452  +RFC 1952             GZIP File Format Specification             May 1996
          453  +
          454  +
          455  +            SI1         SI2         Data
          456  +            ----------  ----------  ----
          457  +            0x41 ('A')  0x70 ('P')  Apollo file type information
          458  +
          459  +         LEN gives the length of the subfield data, excluding the 4
          460  +         initial bytes.
          461  +
          462  +      2.3.1.2. Compliance
          463  +
          464  +         A compliant compressor must produce files with correct ID1,
          465  +         ID2, CM, CRC32, and ISIZE, but may set all the other fields in
          466  +         the fixed-length part of the header to default values (255 for
          467  +         OS, 0 for all others).  The compressor must set all reserved
          468  +         bits to zero.
          469  +
          470  +         A compliant decompressor must check ID1, ID2, and CM, and
          471  +         provide an error indication if any of these have incorrect
          472  +         values.  It must examine FEXTRA/XLEN, FNAME, FCOMMENT and FHCRC
          473  +         at least so it can skip over the optional fields if they are
          474  +         present.  It need not examine any other part of the header or
          475  +         trailer; in particular, a decompressor may ignore FTEXT and OS
          476  +         and always produce binary output, and still be compliant.  A
          477  +         compliant decompressor must give an error indication if any
          478  +         reserved bit is non-zero, since such a bit could indicate the
          479  +         presence of a new field that would cause subsequent data to be
          480  +         interpreted incorrectly.
          481  +
          482  +3. References
          483  +
          484  +   [1] "Information Processing - 8-bit single-byte coded graphic
          485  +       character sets - Part 1: Latin alphabet No.1" (ISO 8859-1:1987).
          486  +       The ISO 8859-1 (Latin-1) character set is a superset of 7-bit
          487  +       ASCII. Files defining this character set are available as
          488  +       iso_8859-1.* in ftp://ftp.uu.net/graphics/png/documents/
          489  +
          490  +   [2] ISO 3309
          491  +
          492  +   [3] ITU-T recommendation V.42
          493  +
          494  +   [4] Deutsch, L.P.,"DEFLATE Compressed Data Format Specification",
          495  +       available in ftp://ftp.uu.net/pub/archiving/zip/doc/
          496  +
          497  +   [5] Gailly, J.-L., GZIP documentation, available as gzip-*.tar in
          498  +       ftp://prep.ai.mit.edu/pub/gnu/
          499  +
          500  +   [6] Sarwate, D.V., "Computation of Cyclic Redundancy Checks via Table
          501  +       Look-Up", Communications of the ACM, 31(8), pp.1008-1013.
          502  +
          503  +
          504  +
          505  +
          506  +Deutsch                      Informational                      [Page 9]
          507  +
          508  +RFC 1952             GZIP File Format Specification             May 1996
          509  +
          510  +
          511  +   [7] Schwaderer, W.D., "CRC Calculation", April 85 PC Tech Journal,
          512  +       pp.118-133.
          513  +
          514  +   [8] ftp://ftp.adelaide.edu.au/pub/rocksoft/papers/crc_v3.txt,
          515  +       describing the CRC concept.
          516  +
          517  +4. Security Considerations
          518  +
          519  +   Any data compression method involves the reduction of redundancy in
          520  +   the data.  Consequently, any corruption of the data is likely to have
          521  +   severe effects and be difficult to correct.  Uncompressed text, on
          522  +   the other hand, will probably still be readable despite the presence
          523  +   of some corrupted bytes.
          524  +
          525  +   It is recommended that systems using this data format provide some
          526  +   means of validating the integrity of the compressed data, such as by
          527  +   setting and checking the CRC-32 check value.
          528  +
          529  +5. Acknowledgements
          530  +
          531  +   Trademarks cited in this document are the property of their
          532  +   respective owners.
          533  +
          534  +   Jean-Loup Gailly designed the gzip format and wrote, with Mark Adler,
          535  +   the related software described in this specification.  Glenn
          536  +   Randers-Pehrson converted this document to RFC and HTML format.
          537  +
          538  +6. Author's Address
          539  +
          540  +   L. Peter Deutsch
          541  +   Aladdin Enterprises
          542  +   203 Santa Margarita Ave.
          543  +   Menlo Park, CA 94025
          544  +
          545  +   Phone: (415) 322-0103 (AM only)
          546  +   FAX:   (415) 322-1734
          547  +   EMail: <ghost@aladdin.com>
          548  +
          549  +   Questions about the technical content of this specification can be
          550  +   sent by email to:
          551  +
          552  +   Jean-Loup Gailly <gzip@prep.ai.mit.edu> and
          553  +   Mark Adler <madler@alumni.caltech.edu>
          554  +
          555  +   Editorial comments on this specification can be sent by email to:
          556  +
          557  +   L. Peter Deutsch <ghost@aladdin.com> and
          558  +   Glenn Randers-Pehrson <randeg@alumni.rpi.edu>
          559  +
          560  +
          561  +
          562  +Deutsch                      Informational                     [Page 10]
          563  +
          564  +RFC 1952             GZIP File Format Specification             May 1996
          565  +
          566  +
          567  +7. Appendix: Jean-Loup Gailly's gzip utility
          568  +
          569  +   The most widely used implementation of gzip compression, and the
          570  +   original documentation on which this specification is based, were
          571  +   created by Jean-Loup Gailly <gzip@prep.ai.mit.edu>.  Since this
          572  +   implementation is a de facto standard, we mention some more of its
          573  +   features here.  Again, the material in this section is not part of
          574  +   the specification per se, and implementations need not follow it to
          575  +   be compliant.
          576  +
          577  +   When compressing or decompressing a file, gzip preserves the
          578  +   protection, ownership, and modification time attributes on the local
          579  +   file system, since there is no provision for representing protection
          580  +   attributes in the gzip file format itself.  Since the file format
          581  +   includes a modification time, the gzip decompressor provides a
          582  +   command line switch that assigns the modification time from the file,
          583  +   rather than the local modification time of the compressed input, to
          584  +   the decompressed output.
          585  +
          586  +8. Appendix: Sample CRC Code
          587  +
          588  +   The following sample code represents a practical implementation of
          589  +   the CRC (Cyclic Redundancy Check). (See also ISO 3309 and ITU-T V.42
          590  +   for a formal specification.)
          591  +
          592  +   The sample code is in the ANSI C programming language. Non C users
          593  +   may find it easier to read with these hints:
          594  +
          595  +      &      Bitwise AND operator.
          596  +      ^      Bitwise exclusive-OR operator.
          597  +      >>     Bitwise right shift operator. When applied to an
          598  +             unsigned quantity, as here, right shift inserts zero
          599  +             bit(s) at the left.
          600  +      !      Logical NOT operator.
          601  +      ++     "n++" increments the variable n.
          602  +      0xNNN  0x introduces a hexadecimal (base 16) constant.
          603  +             Suffix L indicates a long value (at least 32 bits).
          604  +
          605  +      /* Table of CRCs of all 8-bit messages. */
          606  +      unsigned long crc_table[256];
          607  +
          608  +      /* Flag: has the table been computed? Initially false. */
          609  +      int crc_table_computed = 0;
          610  +
          611  +      /* Make the table for a fast CRC. */
          612  +      void make_crc_table(void)
          613  +      {
          614  +        unsigned long c;
          615  +
          616  +
          617  +
          618  +Deutsch                      Informational                     [Page 11]
          619  +
          620  +RFC 1952             GZIP File Format Specification             May 1996
          621  +
          622  +
          623  +        int n, k;
          624  +        for (n = 0; n < 256; n++) {
          625  +          c = (unsigned long) n;
          626  +          for (k = 0; k < 8; k++) {
          627  +            if (c & 1) {
          628  +              c = 0xedb88320L ^ (c >> 1);
          629  +            } else {
          630  +              c = c >> 1;
          631  +            }
          632  +          }
          633  +          crc_table[n] = c;
          634  +        }
          635  +        crc_table_computed = 1;
          636  +      }
          637  +
          638  +      /*
          639  +         Update a running crc with the bytes buf[0..len-1] and return
          640  +       the updated crc. The crc should be initialized to zero. Pre- and
          641  +       post-conditioning (one's complement) is performed within this
          642  +       function so it shouldn't be done by the caller. Usage example:
          643  +
          644  +         unsigned long crc = 0L;
          645  +
          646  +         while (read_buffer(buffer, length) != EOF) {
          647  +           crc = update_crc(crc, buffer, length);
          648  +         }
          649  +         if (crc != original_crc) error();
          650  +      */
          651  +      unsigned long update_crc(unsigned long crc,
          652  +                      unsigned char *buf, int len)
          653  +      {
          654  +        unsigned long c = crc ^ 0xffffffffL;
          655  +        int n;
          656  +
          657  +        if (!crc_table_computed)
          658  +          make_crc_table();
          659  +        for (n = 0; n < len; n++) {
          660  +          c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
          661  +        }
          662  +        return c ^ 0xffffffffL;
          663  +      }
          664  +
          665  +      /* Return the CRC of the bytes buf[0..len-1]. */
          666  +      unsigned long crc(unsigned char *buf, int len)
          667  +      {
          668  +        return update_crc(0L, buf, len);
          669  +      }
          670  +
          671  +
          672  +
          673  +
          674  +Deutsch                      Informational                     [Page 12]
          675  +

Added compat/zlib/doc/txtvsbin.txt.

            1  +A Fast Method for Identifying Plain Text Files
            2  +==============================================
            3  +
            4  +
            5  +Introduction
            6  +------------
            7  +
            8  +Given a file coming from an unknown source, it is sometimes desirable
            9  +to find out whether the format of that file is plain text.  Although
           10  +this may appear like a simple task, a fully accurate detection of the
           11  +file type requires heavy-duty semantic analysis on the file contents.
           12  +It is, however, possible to obtain satisfactory results by employing
           13  +various heuristics.
           14  +
           15  +Previous versions of PKZip and other zip-compatible compression tools
           16  +were using a crude detection scheme: if more than 80% (4/5) of the bytes
           17  +found in a certain buffer are within the range [7..127], the file is
           18  +labeled as plain text, otherwise it is labeled as binary.  A prominent
           19  +limitation of this scheme is the restriction to Latin-based alphabets.
           20  +Other alphabets, like Greek, Cyrillic or Asian, make extensive use of
           21  +the bytes within the range [128..255], and texts using these alphabets
           22  +are most often misidentified by this scheme; in other words, the rate
           23  +of false negatives is sometimes too high, which means that the recall
           24  +is low.  Another weakness of this scheme is a reduced precision, due to
           25  +the false positives that may occur when binary files containing large
           26  +amounts of textual characters are misidentified as plain text.
           27  +
           28  +In this article we propose a new, simple detection scheme that features
           29  +a much increased precision and a near-100% recall.  This scheme is
           30  +designed to work on ASCII, Unicode and other ASCII-derived alphabets,
           31  +and it handles single-byte encodings (ISO-8859, MacRoman, KOI8, etc.)
           32  +and variable-sized encodings (ISO-2022, UTF-8, etc.).  Wider encodings
           33  +(UCS-2/UTF-16 and UCS-4/UTF-32) are not handled, however.
           34  +
           35  +
           36  +The Algorithm
           37  +-------------
           38  +
           39  +The algorithm works by dividing the set of bytecodes [0..255] into three
           40  +categories:
           41  +- The white list of textual bytecodes:
           42  +  9 (TAB), 10 (LF), 13 (CR), 32 (SPACE) to 255.
           43  +- The gray list of tolerated bytecodes:
           44  +  7 (BEL), 8 (BS), 11 (VT), 12 (FF), 26 (SUB), 27 (ESC).
           45  +- The black list of undesired, non-textual bytecodes:
           46  +  0 (NUL) to 6, 14 to 31.
           47  +
           48  +If a file contains at least one byte that belongs to the white list and
           49  +no byte that belongs to the black list, then the file is categorized as
           50  +plain text; otherwise, it is categorized as binary.  (The boundary case,
           51  +when the file is empty, automatically falls into the latter category.)
           52  +
           53  +
           54  +Rationale
           55  +---------
           56  +
           57  +The idea behind this algorithm relies on two observations.
           58  +
           59  +The first observation is that, although the full range of 7-bit codes
           60  +[0..127] is properly specified by the ASCII standard, most control
           61  +characters in the range [0..31] are not used in practice.  The only
           62  +widely-used, almost universally-portable control codes are 9 (TAB),
           63  +10 (LF) and 13 (CR).  There are a few more control codes that are
           64  +recognized on a reduced range of platforms and text viewers/editors:
           65  +7 (BEL), 8 (BS), 11 (VT), 12 (FF), 26 (SUB) and 27 (ESC); but these
           66  +codes are rarely (if ever) used alone, without being accompanied by
           67  +some printable text.  Even the newer, portable text formats such as
           68  +XML avoid using control characters outside the list mentioned here.
           69  +
           70  +The second observation is that most of the binary files tend to contain
           71  +control characters, especially 0 (NUL).  Even though the older text
           72  +detection schemes observe the presence of non-ASCII codes from the range
           73  +[128..255], the precision rarely has to suffer if this upper range is
           74  +labeled as textual, because the files that are genuinely binary tend to
           75  +contain both control characters and codes from the upper range.  On the
           76  +other hand, the upper range needs to be labeled as textual, because it
           77  +is used by virtually all ASCII extensions.  In particular, this range is
           78  +used for encoding non-Latin scripts.
           79  +
           80  +Since there is no counting involved, other than simply observing the
           81  +presence or the absence of some byte values, the algorithm produces
           82  +consistent results, regardless what alphabet encoding is being used.
           83  +(If counting were involved, it could be possible to obtain different
           84  +results on a text encoded, say, using ISO-8859-16 versus UTF-8.)
           85  +
           86  +There is an extra category of plain text files that are "polluted" with
           87  +one or more black-listed codes, either by mistake or by peculiar design
           88  +considerations.  In such cases, a scheme that tolerates a small fraction
           89  +of black-listed codes would provide an increased recall (i.e. more true
           90  +positives).  This, however, incurs a reduced precision overall, since
           91  +false positives are more likely to appear in binary files that contain
           92  +large chunks of textual data.  Furthermore, "polluted" plain text should
           93  +be regarded as binary by general-purpose text detection schemes, because
           94  +general-purpose text processing algorithms might not be applicable.
           95  +Under this premise, it is safe to say that our detection method provides
           96  +a near-100% recall.
           97  +
           98  +Experiments have been run on many files coming from various platforms
           99  +and applications.  We tried plain text files, system logs, source code,
          100  +formatted office documents, compiled object code, etc.  The results
          101  +confirm the optimistic assumptions about the capabilities of this
          102  +algorithm.
          103  +
          104  +
          105  +--
          106  +Cosmin Truta
          107  +Last updated: 2006-May-28

Added compat/zlib/examples/README.examples.

            1  +This directory contains examples of the use of zlib and other relevant
            2  +programs and documentation.
            3  +
            4  +enough.c
            5  +    calculation and justification of ENOUGH parameter in inftrees.h
            6  +    - calculates the maximum table space used in inflate tree
            7  +      construction over all possible Huffman codes
            8  +
            9  +fitblk.c
           10  +    compress just enough input to nearly fill a requested output size
           11  +    - zlib isn't designed to do this, but fitblk does it anyway
           12  +
           13  +gun.c
           14  +    uncompress a gzip file
           15  +    - illustrates the use of inflateBack() for high speed file-to-file
           16  +      decompression using call-back functions
           17  +    - is approximately twice as fast as gzip -d
           18  +    - also provides Unix uncompress functionality, again twice as fast
           19  +
           20  +gzappend.c
           21  +    append to a gzip file
           22  +    - illustrates the use of the Z_BLOCK flush parameter for inflate()
           23  +    - illustrates the use of deflatePrime() to start at any bit
           24  +
           25  +gzjoin.c
           26  +    join gzip files without recalculating the crc or recompressing
           27  +    - illustrates the use of the Z_BLOCK flush parameter for inflate()
           28  +    - illustrates the use of crc32_combine()
           29  +
           30  +gzlog.c
           31  +gzlog.h
           32  +    efficiently and robustly maintain a message log file in gzip format
           33  +    - illustrates use of raw deflate, Z_PARTIAL_FLUSH, deflatePrime(),
           34  +      and deflateSetDictionary()
           35  +    - illustrates use of a gzip header extra field
           36  +
           37  +zlib_how.html
           38  +    painfully comprehensive description of zpipe.c (see below)
           39  +    - describes in excruciating detail the use of deflate() and inflate()
           40  +
           41  +zpipe.c
           42  +    reads and writes zlib streams from stdin to stdout
           43  +    - illustrates the proper use of deflate() and inflate()
           44  +    - deeply commented in zlib_how.html (see above)
           45  +
           46  +zran.c
           47  +    index a zlib or gzip stream and randomly access it
           48  +    - illustrates the use of Z_BLOCK, inflatePrime(), and
           49  +      inflateSetDictionary() to provide random access

Added compat/zlib/examples/enough.c.

            1  +/* enough.c -- determine the maximum size of inflate's Huffman code tables over
            2  + * all possible valid and complete Huffman codes, subject to a length limit.
            3  + * Copyright (C) 2007, 2008 Mark Adler
            4  + * Version 1.3  17 February 2008  Mark Adler
            5  + */
            6  +
            7  +/* Version history:
            8  +   1.0   3 Jan 2007  First version (derived from codecount.c version 1.4)
            9  +   1.1   4 Jan 2007  Use faster incremental table usage computation
           10  +                     Prune examine() search on previously visited states
           11  +   1.2   5 Jan 2007  Comments clean up
           12  +                     As inflate does, decrease root for short codes
           13  +                     Refuse cases where inflate would increase root
           14  +   1.3  17 Feb 2008  Add argument for initial root table size
           15  +                     Fix bug for initial root table size == max - 1
           16  +                     Use a macro to compute the history index
           17  + */
           18  +
           19  +/*
           20  +   Examine all possible Huffman codes for a given number of symbols and a
           21  +   maximum code length in bits to determine the maximum table size for zilb's
           22  +   inflate.  Only complete Huffman codes are counted.
           23  +
           24  +   Two codes are considered distinct if the vectors of the number of codes per
           25  +   length are not identical.  So permutations of the symbol assignments result
           26  +   in the same code for the counting, as do permutations of the assignments of
           27  +   the bit values to the codes (i.e. only canonical codes are counted).
           28  +
           29  +   We build a code from shorter to longer lengths, determining how many symbols
           30  +   are coded at each length.  At each step, we have how many symbols remain to
           31  +   be coded, what the last code length used was, and how many bit patterns of
           32  +   that length remain unused. Then we add one to the code length and double the
           33  +   number of unused patterns to graduate to the next code length.  We then
           34  +   assign all portions of the remaining symbols to that code length that
           35  +   preserve the properties of a correct and eventually complete code.  Those
           36  +   properties are: we cannot use more bit patterns than are available; and when
           37  +   all the symbols are used, there are exactly zero possible bit patterns
           38  +   remaining.
           39  +
           40  +   The inflate Huffman decoding algorithm uses two-level lookup tables for
           41  +   speed.  There is a single first-level table to decode codes up to root bits
           42  +   in length (root == 9 in the current inflate implementation).  The table
           43  +   has 1 << root entries and is indexed by the next root bits of input.  Codes
           44  +   shorter than root bits have replicated table entries, so that the correct
           45  +   entry is pointed to regardless of the bits that follow the short code.  If
           46  +   the code is longer than root bits, then the table entry points to a second-
           47  +   level table.  The size of that table is determined by the longest code with
           48  +   that root-bit prefix.  If that longest code has length len, then the table
           49  +   has size 1 << (len - root), to index the remaining bits in that set of
           50  +   codes.  Each subsequent root-bit prefix then has its own sub-table.  The
           51  +   total number of table entries required by the code is calculated
           52  +   incrementally as the number of codes at each bit length is populated.  When
           53  +   all of the codes are shorter than root bits, then root is reduced to the
           54  +   longest code length, resulting in a single, smaller, one-level table.
           55  +
           56  +   The inflate algorithm also provides for small values of root (relative to
           57  +   the log2 of the number of symbols), where the shortest code has more bits
           58  +   than root.  In that case, root is increased to the length of the shortest
           59  +   code.  This program, by design, does not handle that case, so it is verified
           60  +   that the number of symbols is less than 2^(root + 1).
           61  +
           62  +   In order to speed up the examination (by about ten orders of magnitude for
           63  +   the default arguments), the intermediate states in the build-up of a code
           64  +   are remembered and previously visited branches are pruned.  The memory
           65  +   required for this will increase rapidly with the total number of symbols and
           66  +   the maximum code length in bits.  However this is a very small price to pay
           67  +   for the vast speedup.
           68  +
           69  +   First, all of the possible Huffman codes are counted, and reachable
           70  +   intermediate states are noted by a non-zero count in a saved-results array.
           71  +   Second, the intermediate states that lead to (root + 1) bit or longer codes
           72  +   are used to look at all sub-codes from those junctures for their inflate
           73  +   memory usage.  (The amount of memory used is not affected by the number of
           74  +   codes of root bits or less in length.)  Third, the visited states in the
           75  +   construction of those sub-codes and the associated calculation of the table
           76  +   size is recalled in order to avoid recalculating from the same juncture.
           77  +   Beginning the code examination at (root + 1) bit codes, which is enabled by
           78  +   identifying the reachable nodes, accounts for about six of the orders of
           79  +   magnitude of improvement for the default arguments.  About another four
           80  +   orders of magnitude come from not revisiting previous states.  Out of
           81  +   approximately 2x10^16 possible Huffman codes, only about 2x10^6 sub-codes
           82  +   need to be examined to cover all of the possible table memory usage cases
           83  +   for the default arguments of 286 symbols limited to 15-bit codes.
           84  +
           85  +   Note that an unsigned long long type is used for counting.  It is quite easy
           86  +   to exceed the capacity of an eight-byte integer with a large number of
           87  +   symbols and a large maximum code length, so multiple-precision arithmetic
           88  +   would need to replace the unsigned long long arithmetic in that case.  This
           89  +   program will abort if an overflow occurs.  The big_t type identifies where
           90  +   the counting takes place.
           91  +
           92  +   An unsigned long long type is also used for calculating the number of
           93  +   possible codes remaining at the maximum length.  This limits the maximum
           94  +   code length to the number of bits in a long long minus the number of bits
           95  +   needed to represent the symbols in a flat code.  The code_t type identifies
           96  +   where the bit pattern counting takes place.
           97  + */
           98  +
           99  +#include <stdio.h>
          100  +#include <stdlib.h>
          101  +#include <string.h>
          102  +#include <assert.h>
          103  +
          104  +#define local static
          105  +
          106  +/* special data types */
          107  +typedef unsigned long long big_t;   /* type for code counting */
          108  +typedef unsigned long long code_t;  /* type for bit pattern counting */
          109  +struct tab {                        /* type for been here check */
          110  +    size_t len;         /* length of bit vector in char's */
          111  +    char *vec;          /* allocated bit vector */
          112  +};
          113  +
          114  +/* The array for saving results, num[], is indexed with this triplet:
          115  +
          116  +      syms: number of symbols remaining to code
          117  +      left: number of available bit patterns at length len
          118  +      len: number of bits in the codes currently being assigned
          119  +
          120  +   Those indices are constrained thusly when saving results:
          121  +
          122  +      syms: 3..totsym (totsym == total symbols to code)
          123  +      left: 2..syms - 1, but only the evens (so syms == 8 -> 2, 4, 6)
          124  +      len: 1..max - 1 (max == maximum code length in bits)
          125  +
          126  +   syms == 2 is not saved since that immediately leads to a single code.  left
          127  +   must be even, since it represents the number of available bit patterns at
          128  +   the current length, which is double the number at the previous length.
          129  +   left ends at syms-1 since left == syms immediately results in a single code.
          130  +   (left > sym is not allowed since that would result in an incomplete code.)
          131  +   len is less than max, since the code completes immediately when len == max.
          132  +
          133  +   The offset into the array is calculated for the three indices with the
          134  +   first one (syms) being outermost, and the last one (len) being innermost.
          135  +   We build the array with length max-1 lists for the len index, with syms-3
          136  +   of those for each symbol.  There are totsym-2 of those, with each one
          137  +   varying in length as a function of sym.  See the calculation of index in
          138  +   count() for the index, and the calculation of size in main() for the size
          139  +   of the array.
          140  +
          141  +   For the deflate example of 286 symbols limited to 15-bit codes, the array
          142  +   has 284,284 entries, taking up 2.17 MB for an 8-byte big_t.  More than
          143  +   half of the space allocated for saved results is actually used -- not all
          144  +   possible triplets are reached in the generation of valid Huffman codes.
          145  + */
          146  +
          147  +/* The array for tracking visited states, done[], is itself indexed identically
          148  +   to the num[] array as described above for the (syms, left, len) triplet.
          149  +   Each element in the array is further indexed by the (mem, rem) doublet,
          150  +   where mem is the amount of inflate table space used so far, and rem is the
          151  +   remaining unused entries in the current inflate sub-table.  Each indexed
          152  +   element is simply one bit indicating whether the state has been visited or
          153  +   not.  Since the ranges for mem and rem are not known a priori, each bit
          154  +   vector is of a variable size, and grows as needed to accommodate the visited
          155  +   states.  mem and rem are used to calculate a single index in a triangular
          156  +   array.  Since the range of mem is expected in the default case to be about
          157  +   ten times larger than the range of rem, the array is skewed to reduce the
          158  +   memory usage, with eight times the range for mem than for rem.  See the
          159  +   calculations for offset and bit in beenhere() for the details.
          160  +
          161  +   For the deflate example of 286 symbols limited to 15-bit codes, the bit
          162  +   vectors grow to total approximately 21 MB, in addition to the 4.3 MB done[]
          163  +   array itself.
          164  + */
          165  +
          166  +/* Globals to avoid propagating constants or constant pointers recursively */
          167  +local int max;          /* maximum allowed bit length for the codes */
          168  +local int root;         /* size of base code table in bits */
          169  +local int large;        /* largest code table so far */
          170  +local size_t size;      /* number of elements in num and done */
          171  +local int *code;        /* number of symbols assigned to each bit length */
          172  +local big_t *num;       /* saved results array for code counting */
          173  +local struct tab *done; /* states already evaluated array */
          174  +
          175  +/* Index function for num[] and done[] */
          176  +#define INDEX(i,j,k) (((size_t)((i-1)>>1)*((i-2)>>1)+(j>>1)-1)*(max-1)+k-1)
          177  +
          178  +/* Free allocated space.  Uses globals code, num, and done. */
          179  +local void cleanup(void)
          180  +{
          181  +    size_t n;
          182  +
          183  +    if (done != NULL) {
          184  +        for (n = 0; n < size; n++)
          185  +            if (done[n].len)
          186  +                free(done[n].vec);
          187  +        free(done);
          188  +    }
          189  +    if (num != NULL)
          190  +        free(num);
          191  +    if (code != NULL)
          192  +        free(code);
          193  +}
          194  +
          195  +/* Return the number of possible Huffman codes using bit patterns of lengths
          196  +   len through max inclusive, coding syms symbols, with left bit patterns of
          197  +   length len unused -- return -1 if there is an overflow in the counting.
          198  +   Keep a record of previous results in num to prevent repeating the same
          199  +   calculation.  Uses the globals max and num. */
          200  +local big_t count(int syms, int len, int left)
          201  +{
          202  +    big_t sum;          /* number of possible codes from this juncture */
          203  +    big_t got;          /* value returned from count() */
          204  +    int least;          /* least number of syms to use at this juncture */
          205  +    int most;           /* most number of syms to use at this juncture */
          206  +    int use;            /* number of bit patterns to use in next call */
          207  +    size_t index;       /* index of this case in *num */
          208  +
          209  +    /* see if only one possible code */
          210  +    if (syms == left)
          211  +        return 1;
          212  +
          213  +    /* note and verify the expected state */
          214  +    assert(syms > left && left > 0 && len < max);
          215  +
          216  +    /* see if we've done this one already */
          217  +    index = INDEX(syms, left, len);
          218  +    got = num[index];
          219  +    if (got)
          220  +        return got;         /* we have -- return the saved result */
          221  +
          222  +    /* we need to use at least this many bit patterns so that the code won't be
          223  +       incomplete at the next length (more bit patterns than symbols) */
          224  +    least = (left << 1) - syms;
          225  +    if (least < 0)
          226  +        least = 0;
          227  +
          228  +    /* we can use at most this many bit patterns, lest there not be enough
          229  +       available for the remaining symbols at the maximum length (if there were
          230  +       no limit to the code length, this would become: most = left - 1) */
          231  +    most = (((code_t)left << (max - len)) - syms) /
          232  +            (((code_t)1 << (max - len)) - 1);
          233  +
          234  +    /* count all possible codes from this juncture and add them up */
          235  +    sum = 0;
          236  +    for (use = least; use <= most; use++) {
          237  +        got = count(syms - use, len + 1, (left - use) << 1);
          238  +        sum += got;
          239  +        if (got == -1 || sum < got)         /* overflow */
          240  +            return -1;
          241  +    }
          242  +
          243  +    /* verify that all recursive calls are productive */
          244  +    assert(sum != 0);
          245  +
          246  +    /* save the result and return it */
          247  +    num[index] = sum;
          248  +    return sum;
          249  +}
          250  +
          251  +/* Return true if we've been here before, set to true if not.  Set a bit in a
          252  +   bit vector to indicate visiting this state.  Each (syms,len,left) state
          253  +   has a variable size bit vector indexed by (mem,rem).  The bit vector is
          254  +   lengthened if needed to allow setting the (mem,rem) bit. */
          255  +local int beenhere(int syms, int len, int left, int mem, int rem)
          256  +{
          257  +    size_t index;       /* index for this state's bit vector */
          258  +    size_t offset;      /* offset in this state's bit vector */
          259  +    int bit;            /* mask for this state's bit */
          260  +    size_t length;      /* length of the bit vector in bytes */
          261  +    char *vector;       /* new or enlarged bit vector */
          262  +
          263  +    /* point to vector for (syms,left,len), bit in vector for (mem,rem) */
          264  +    index = INDEX(syms, left, len);
          265  +    mem -= 1 << root;
          266  +    offset = (mem >> 3) + rem;
          267  +    offset = ((offset * (offset + 1)) >> 1) + rem;
          268  +    bit = 1 << (mem & 7);
          269  +
          270  +    /* see if we've been here */
          271  +    length = done[index].len;
          272  +    if (offset < length && (done[index].vec[offset] & bit) != 0)
          273  +        return 1;       /* done this! */
          274  +
          275  +    /* we haven't been here before -- set the bit to show we have now */
          276  +
          277  +    /* see if we need to lengthen the vector in order to set the bit */
          278  +    if (length <= offset) {
          279  +        /* if we have one already, enlarge it, zero out the appended space */
          280  +        if (length) {
          281  +            do {
          282  +                length <<= 1;
          283  +            } while (length <= offset);
          284  +            vector = realloc(done[index].vec, length);
          285  +            if (vector != NULL)
          286  +                memset(vector + done[index].len, 0, length - done[index].len);
          287  +        }
          288  +
          289  +        /* otherwise we need to make a new vector and zero it out */
          290  +        else {
          291  +            length = 1 << (len - root);
          292  +            while (length <= offset)
          293  +                length <<= 1;
          294  +            vector = calloc(length, sizeof(char));
          295  +        }
          296  +
          297  +        /* in either case, bail if we can't get the memory */
          298  +        if (vector == NULL) {
          299  +            fputs("abort: unable to allocate enough memory\n", stderr);
          300  +            cleanup();
          301  +            exit(1);
          302  +        }
          303  +
          304  +        /* install the new vector */
          305  +        done[index].len = length;
          306  +        done[index].vec = vector;
          307  +    }
          308  +
          309  +    /* set the bit */
          310  +    done[index].vec[offset] |= bit;
          311  +    return 0;
          312  +}
          313  +
          314  +/* Examine all possible codes from the given node (syms, len, left).  Compute
          315  +   the amount of memory required to build inflate's decoding tables, where the
          316  +   number of code structures used so far is mem, and the number remaining in
          317  +   the current sub-table is rem.  Uses the globals max, code, root, large, and
          318  +   done. */
          319  +local void examine(int syms, int len, int left, int mem, int rem)
          320  +{
          321  +    int least;          /* least number of syms to use at this juncture */
          322  +    int most;           /* most number of syms to use at this juncture */
          323  +    int use;            /* number of bit patterns to use in next call */
          324  +
          325  +    /* see if we have a complete code */
          326  +    if (syms == left) {
          327  +        /* set the last code entry */
          328  +        code[len] = left;
          329  +
          330  +        /* complete computation of memory used by this code */
          331  +        while (rem < left) {
          332  +            left -= rem;
          333  +            rem = 1 << (len - root);
          334  +            mem += rem;
          335  +        }
          336  +        assert(rem == left);
          337  +
          338  +        /* if this is a new maximum, show the entries used and the sub-code */
          339  +        if (mem > large) {
          340  +            large = mem;
          341  +            printf("max %d: ", mem);
          342  +            for (use = root + 1; use <= max; use++)
          343  +                if (code[use])
          344  +                    printf("%d[%d] ", code[use], use);
          345  +            putchar('\n');
          346  +            fflush(stdout);
          347  +        }
          348  +
          349  +        /* remove entries as we drop back down in the recursion */
          350  +        code[len] = 0;
          351  +        return;
          352  +    }
          353  +
          354  +    /* prune the tree if we can */
          355  +    if (beenhere(syms, len, left, mem, rem))
          356  +        return;
          357  +
          358  +    /* we need to use at least this many bit patterns so that the code won't be
          359  +       incomplete at the next length (more bit patterns than symbols) */
          360  +    least = (left << 1) - syms;
          361  +    if (least < 0)
          362  +        least = 0;
          363  +
          364  +    /* we can use at most this many bit patterns, lest there not be enough
          365  +       available for the remaining symbols at the maximum length (if there were
          366  +       no limit to the code length, this would become: most = left - 1) */
          367  +    most = (((code_t)left << (max - len)) - syms) /
          368  +            (((code_t)1 << (max - len)) - 1);
          369  +
          370  +    /* occupy least table spaces, creating new sub-tables as needed */
          371  +    use = least;
          372  +    while (rem < use) {
          373  +        use -= rem;
          374  +        rem = 1 << (len - root);
          375  +        mem += rem;
          376  +    }
          377  +    rem -= use;
          378  +
          379  +    /* examine codes from here, updating table space as we go */
          380  +    for (use = least; use <= most; use++) {
          381  +        code[len] = use;
          382  +        examine(syms - use, len + 1, (left - use) << 1,
          383  +                mem + (rem ? 1 << (len - root) : 0), rem << 1);
          384  +        if (rem == 0) {
          385  +            rem = 1 << (len - root);
          386  +            mem += rem;
          387  +        }
          388  +        rem--;
          389  +    }
          390  +
          391  +    /* remove entries as we drop back down in the recursion */
          392  +    code[len] = 0;
          393  +}
          394  +
          395  +/* Look at all sub-codes starting with root + 1 bits.  Look at only the valid
          396  +   intermediate code states (syms, left, len).  For each completed code,
          397  +   calculate the amount of memory required by inflate to build the decoding
          398  +   tables. Find the maximum amount of memory required and show the code that
          399  +   requires that maximum.  Uses the globals max, root, and num. */
          400  +local void enough(int syms)
          401  +{
          402  +    int n;              /* number of remaing symbols for this node */
          403  +    int left;           /* number of unused bit patterns at this length */
          404  +    size_t index;       /* index of this case in *num */
          405  +
          406  +    /* clear code */
          407  +    for (n = 0; n <= max; n++)
          408  +        code[n] = 0;
          409  +
          410  +    /* look at all (root + 1) bit and longer codes */
          411  +    large = 1 << root;              /* base table */
          412  +    if (root < max)                 /* otherwise, there's only a base table */
          413  +        for (n = 3; n <= syms; n++)
          414  +            for (left = 2; left < n; left += 2)
          415  +            {
          416  +                /* look at all reachable (root + 1) bit nodes, and the
          417  +                   resulting codes (complete at root + 2 or more) */
          418  +                index = INDEX(n, left, root + 1);
          419  +                if (root + 1 < max && num[index])       /* reachable node */
          420  +                    examine(n, root + 1, left, 1 << root, 0);
          421  +
          422  +                /* also look at root bit codes with completions at root + 1
          423  +                   bits (not saved in num, since complete), just in case */
          424  +                if (num[index - 1] && n <= left << 1)
          425  +                    examine((n - left) << 1, root + 1, (n - left) << 1,
          426  +                            1 << root, 0);
          427  +            }
          428  +
          429  +    /* done */
          430  +    printf("done: maximum of %d table entries\n", large);
          431  +}
          432  +
          433  +/*
          434  +   Examine and show the total number of possible Huffman codes for a given
          435  +   maximum number of symbols, initial root table size, and maximum code length
          436  +   in bits -- those are the command arguments in that order.  The default
          437  +   values are 286, 9, and 15 respectively, for the deflate literal/length code.
          438  +   The possible codes are counted for each number of coded symbols from two to
          439  +   the maximum.  The counts for each of those and the total number of codes are
          440  +   shown.  The maximum number of inflate table entires is then calculated
          441  +   across all possible codes.  Each new maximum number of table entries and the
          442  +   associated sub-code (starting at root + 1 == 10 bits) is shown.
          443  +
          444  +   To count and examine Huffman codes that are not length-limited, provide a
          445  +   maximum length equal to the number of symbols minus one.
          446  +
          447  +   For the deflate literal/length code, use "enough".  For the deflate distance
          448  +   code, use "enough 30 6".
          449  +
          450  +   This uses the %llu printf format to print big_t numbers, which assumes that
          451  +   big_t is an unsigned long long.  If the big_t type is changed (for example
          452  +   to a multiple precision type), the method of printing will also need to be
          453  +   updated.
          454  + */
          455  +int main(int argc, char **argv)
          456  +{
          457  +    int syms;           /* total number of symbols to code */
          458  +    int n;              /* number of symbols to code for this run */
          459  +    big_t got;          /* return value of count() */
          460  +    big_t sum;          /* accumulated number of codes over n */
          461  +
          462  +    /* set up globals for cleanup() */
          463  +    code = NULL;
          464  +    num = NULL;
          465  +    done = NULL;
          466  +
          467  +    /* get arguments -- default to the deflate literal/length code */
          468  +    syms = 286;
          469  +        root = 9;
          470  +    max = 15;
          471  +    if (argc > 1) {
          472  +        syms = atoi(argv[1]);
          473  +        if (argc > 2) {
          474  +            root = atoi(argv[2]);
          475  +                        if (argc > 3)
          476  +                                max = atoi(argv[3]);
          477  +                }
          478  +    }
          479  +    if (argc > 4 || syms < 2 || root < 1 || max < 1) {
          480  +        fputs("invalid arguments, need: [sym >= 2 [root >= 1 [max >= 1]]]\n",
          481  +                          stderr);
          482  +        return 1;
          483  +    }
          484  +
          485  +    /* if not restricting the code length, the longest is syms - 1 */
          486  +    if (max > syms - 1)
          487  +        max = syms - 1;
          488  +
          489  +    /* determine the number of bits in a code_t */
          490  +    n = 0;
          491  +    while (((code_t)1 << n) != 0)
          492  +        n++;
          493  +
          494  +    /* make sure that the calculation of most will not overflow */
          495  +    if (max > n || syms - 2 >= (((code_t)0 - 1) >> (max - 1))) {
          496  +        fputs("abort: code length too long for internal types\n", stderr);
          497  +        return 1;
          498  +    }
          499  +
          500  +    /* reject impossible code requests */
          501  +    if (syms - 1 > ((code_t)1 << max) - 1) {
          502  +        fprintf(stderr, "%d symbols cannot be coded in %d bits\n",
          503  +                syms, max);
          504  +        return 1;
          505  +    }
          506  +
          507  +    /* allocate code vector */
          508  +    code = calloc(max + 1, sizeof(int));
          509  +    if (code == NULL) {
          510  +        fputs("abort: unable to allocate enough memory\n", stderr);
          511  +        return 1;
          512  +    }
          513  +
          514  +    /* determine size of saved results array, checking for overflows,
          515  +       allocate and clear the array (set all to zero with calloc()) */
          516  +    if (syms == 2)              /* iff max == 1 */
          517  +        num = NULL;             /* won't be saving any results */
          518  +    else {
          519  +        size = syms >> 1;
          520  +        if (size > ((size_t)0 - 1) / (n = (syms - 1) >> 1) ||
          521  +                (size *= n, size > ((size_t)0 - 1) / (n = max - 1)) ||
          522  +                (size *= n, size > ((size_t)0 - 1) / sizeof(big_t)) ||
          523  +                (num = calloc(size, sizeof(big_t))) == NULL) {
          524  +            fputs("abort: unable to allocate enough memory\n", stderr);
          525  +            cleanup();
          526  +            return 1;
          527  +        }
          528  +    }
          529  +
          530  +    /* count possible codes for all numbers of symbols, add up counts */
          531  +    sum = 0;
          532  +    for (n = 2; n <= syms; n++) {
          533  +        got = count(n, 1, 2);
          534  +        sum += got;
          535  +        if (got == -1 || sum < got) {       /* overflow */
          536  +            fputs("abort: can't count that high!\n", stderr);
          537  +            cleanup();
          538  +            return 1;
          539  +        }
          540  +        printf("%llu %d-codes\n", got, n);
          541  +    }
          542  +    printf("%llu total codes for 2 to %d symbols", sum, syms);
          543  +    if (max < syms - 1)
          544  +        printf(" (%d-bit length limit)\n", max);
          545  +    else
          546  +        puts(" (no length limit)");
          547  +
          548  +    /* allocate and clear done array for beenhere() */
          549  +    if (syms == 2)
          550  +        done = NULL;
          551  +    else if (size > ((size_t)0 - 1) / sizeof(struct tab) ||
          552  +             (done = calloc(size, sizeof(struct tab))) == NULL) {
          553  +        fputs("abort: unable to allocate enough memory\n", stderr);
          554  +        cleanup();
          555  +        return 1;
          556  +    }
          557  +
          558  +    /* find and show maximum inflate table usage */
          559  +        if (root > max)                 /* reduce root to max length */
          560  +                root = max;
          561  +    if (syms < ((code_t)1 << (root + 1)))
          562  +        enough(syms);
          563  +    else
          564  +        puts("cannot handle minimum code lengths > root");
          565  +
          566  +    /* done */
          567  +    cleanup();
          568  +    return 0;
          569  +}

Added compat/zlib/examples/fitblk.c.

            1  +/* fitblk.c: example of fitting compressed output to a specified size
            2  +   Not copyrighted -- provided to the public domain
            3  +   Version 1.1  25 November 2004  Mark Adler */
            4  +
            5  +/* Version history:
            6  +   1.0  24 Nov 2004  First version
            7  +   1.1  25 Nov 2004  Change deflateInit2() to deflateInit()
            8  +                     Use fixed-size, stack-allocated raw buffers
            9  +                     Simplify code moving compression to subroutines
           10  +                     Use assert() for internal errors
           11  +                     Add detailed description of approach
           12  + */
           13  +
           14  +/* Approach to just fitting a requested compressed size:
           15  +
           16  +   fitblk performs three compression passes on a portion of the input
           17  +   data in order to determine how much of that input will compress to
           18  +   nearly the requested output block size.  The first pass generates
           19  +   enough deflate blocks to produce output to fill the requested
           20  +   output size plus a specfied excess amount (see the EXCESS define
           21  +   below).  The last deflate block may go quite a bit past that, but
           22  +   is discarded.  The second pass decompresses and recompresses just
           23  +   the compressed data that fit in the requested plus excess sized
           24  +   buffer.  The deflate process is terminated after that amount of
           25  +   input, which is less than the amount consumed on the first pass.
           26  +   The last deflate block of the result will be of a comparable size
           27  +   to the final product, so that the header for that deflate block and
           28  +   the compression ratio for that block will be about the same as in
           29  +   the final product.  The third compression pass decompresses the
           30  +   result of the second step, but only the compressed data up to the
           31  +   requested size minus an amount to allow the compressed stream to
           32  +   complete (see the MARGIN define below).  That will result in a
           33  +   final compressed stream whose length is less than or equal to the
           34  +   requested size.  Assuming sufficient input and a requested size
           35  +   greater than a few hundred bytes, the shortfall will typically be
           36  +   less than ten bytes.
           37  +
           38  +   If the input is short enough that the first compression completes
           39  +   before filling the requested output size, then that compressed
           40  +   stream is return with no recompression.
           41  +
           42  +   EXCESS is chosen to be just greater than the shortfall seen in a
           43  +   two pass approach similar to the above.  That shortfall is due to
           44  +   the last deflate block compressing more efficiently with a smaller
           45  +   header on the second pass.  EXCESS is set to be large enough so
           46  +   that there is enough uncompressed data for the second pass to fill
           47  +   out the requested size, and small enough so that the final deflate
           48  +   block of the second pass will be close in size to the final deflate
           49  +   block of the third and final pass.  MARGIN is chosen to be just
           50  +   large enough to assure that the final compression has enough room
           51  +   to complete in all cases.
           52  + */
           53  +
           54  +#include <stdio.h>
           55  +#include <stdlib.h>
           56  +#include <assert.h>
           57  +#include "zlib.h"
           58  +
           59  +#define local static
           60  +
           61  +/* print nastygram and leave */
           62  +local void quit(char *why)
           63  +{
           64  +    fprintf(stderr, "fitblk abort: %s\n", why);
           65  +    exit(1);
           66  +}
           67  +
           68  +#define RAWLEN 4096    /* intermediate uncompressed buffer size */
           69  +
           70  +/* compress from file to def until provided buffer is full or end of
           71  +   input reached; return last deflate() return value, or Z_ERRNO if
           72  +   there was read error on the file */
           73  +local int partcompress(FILE *in, z_streamp def)
           74  +{
           75  +    int ret, flush;
           76  +    unsigned char raw[RAWLEN];
           77  +
           78  +    flush = Z_NO_FLUSH;
           79  +    do {
           80  +        def->avail_in = fread(raw, 1, RAWLEN, in);
           81  +        if (ferror(in))
           82  +            return Z_ERRNO;
           83  +        def->next_in = raw;
           84  +        if (feof(in))
           85  +            flush = Z_FINISH;
           86  +        ret = deflate(def, flush);
           87  +        assert(ret != Z_STREAM_ERROR);
           88  +    } while (def->avail_out != 0 && flush == Z_NO_FLUSH);
           89  +    return ret;
           90  +}
           91  +
           92  +/* recompress from inf's input to def's output; the input for inf and
           93  +   the output for def are set in those structures before calling;
           94  +   return last deflate() return value, or Z_MEM_ERROR if inflate()
           95  +   was not able to allocate enough memory when it needed to */
           96  +local int recompress(z_streamp inf, z_streamp def)
           97  +{
           98  +    int ret, flush;
           99  +    unsigned char raw[RAWLEN];
          100  +
          101  +    flush = Z_NO_FLUSH;
          102  +    do {
          103  +        /* decompress */
          104  +        inf->avail_out = RAWLEN;
          105  +        inf->next_out = raw;
          106  +        ret = inflate(inf, Z_NO_FLUSH);
          107  +        assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR &&
          108  +               ret != Z_NEED_DICT);
          109  +        if (ret == Z_MEM_ERROR)
          110  +            return ret;
          111  +
          112  +        /* compress what was decompresed until done or no room */
          113  +        def->avail_in = RAWLEN - inf->avail_out;
          114  +        def->next_in = raw;
          115  +        if (inf->avail_out != 0)
          116  +            flush = Z_FINISH;
          117  +        ret = deflate(def, flush);
          118  +        assert(ret != Z_STREAM_ERROR);
          119  +    } while (ret != Z_STREAM_END && def->avail_out != 0);
          120  +    return ret;
          121  +}
          122  +
          123  +#define EXCESS 256      /* empirically determined stream overage */
          124  +#define MARGIN 8        /* amount to back off for completion */
          125  +
          126  +/* compress from stdin to fixed-size block on stdout */
          127  +int main(int argc, char **argv)
          128  +{
          129  +    int ret;                /* return code */
          130  +    unsigned size;          /* requested fixed output block size */
          131  +    unsigned have;          /* bytes written by deflate() call */
          132  +    unsigned char *blk;     /* intermediate and final stream */
          133  +    unsigned char *tmp;     /* close to desired size stream */
          134  +    z_stream def, inf;      /* zlib deflate and inflate states */
          135  +
          136  +    /* get requested output size */
          137  +    if (argc != 2)
          138  +        quit("need one argument: size of output block");
          139  +    ret = strtol(argv[1], argv + 1, 10);
          140  +    if (argv[1][0] != 0)
          141  +        quit("argument must be a number");
          142  +    if (ret < 8)            /* 8 is minimum zlib stream size */
          143  +        quit("need positive size of 8 or greater");
          144  +    size = (unsigned)ret;
          145  +
          146  +    /* allocate memory for buffers and compression engine */
          147  +    blk = malloc(size + EXCESS);
          148  +    def.zalloc = Z_NULL;
          149  +    def.zfree = Z_NULL;
          150  +    def.opaque = Z_NULL;
          151  +    ret = deflateInit(&def, Z_DEFAULT_COMPRESSION);
          152  +    if (ret != Z_OK || blk == NULL)
          153  +        quit("out of memory");
          154  +
          155  +    /* compress from stdin until output full, or no more input */
          156  +    def.avail_out = size + EXCESS;
          157  +    def.next_out = blk;
          158  +    ret = partcompress(stdin, &def);
          159  +    if (ret == Z_ERRNO)
          160  +        quit("error reading input");
          161  +
          162  +    /* if it all fit, then size was undersubscribed -- done! */
          163  +    if (ret == Z_STREAM_END && def.avail_out >= EXCESS) {
          164  +        /* write block to stdout */
          165  +        have = size + EXCESS - def.avail_out;
          166  +        if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
          167  +            quit("error writing output");
          168  +
          169  +        /* clean up and print results to stderr */
          170  +        ret = deflateEnd(&def);
          171  +        assert(ret != Z_STREAM_ERROR);
          172  +        free(blk);
          173  +        fprintf(stderr,
          174  +                "%u bytes unused out of %u requested (all input)\n",
          175  +                size - have, size);
          176  +        return 0;
          177  +    }
          178  +
          179  +    /* it didn't all fit -- set up for recompression */
          180  +    inf.zalloc = Z_NULL;
          181  +    inf.zfree = Z_NULL;
          182  +    inf.opaque = Z_NULL;
          183  +    inf.avail_in = 0;
          184  +    inf.next_in = Z_NULL;
          185  +    ret = inflateInit(&inf);
          186  +    tmp = malloc(size + EXCESS);
          187  +    if (ret != Z_OK || tmp == NULL)
          188  +        quit("out of memory");
          189  +    ret = deflateReset(&def);
          190  +    assert(ret != Z_STREAM_ERROR);
          191  +
          192  +    /* do first recompression close to the right amount */
          193  +    inf.avail_in = size + EXCESS;
          194  +    inf.next_in = blk;
          195  +    def.avail_out = size + EXCESS;
          196  +    def.next_out = tmp;
          197  +    ret = recompress(&inf, &def);
          198  +    if (ret == Z_MEM_ERROR)
          199  +        quit("out of memory");
          200  +
          201  +    /* set up for next reocmpression */
          202  +    ret = inflateReset(&inf);
          203  +    assert(ret != Z_STREAM_ERROR);
          204  +    ret = deflateReset(&def);
          205  +    assert(ret != Z_STREAM_ERROR);
          206  +
          207  +    /* do second and final recompression (third compression) */
          208  +    inf.avail_in = size - MARGIN;   /* assure stream will complete */
          209  +    inf.next_in = tmp;
          210  +    def.avail_out = size;
          211  +    def.next_out = blk;
          212  +    ret = recompress(&inf, &def);
          213  +    if (ret == Z_MEM_ERROR)
          214  +        quit("out of memory");
          215  +    assert(ret == Z_STREAM_END);    /* otherwise MARGIN too small */
          216  +
          217  +    /* done -- write block to stdout */
          218  +    have = size - def.avail_out;
          219  +    if (fwrite(blk, 1, have, stdout) != have || ferror(stdout))
          220  +        quit("error writing output");
          221  +
          222  +    /* clean up and print results to stderr */
          223  +    free(tmp);
          224  +    ret = inflateEnd(&inf);
          225  +    assert(ret != Z_STREAM_ERROR);
          226  +    ret = deflateEnd(&def);
          227  +    assert(ret != Z_STREAM_ERROR);
          228  +    free(blk);
          229  +    fprintf(stderr,
          230  +            "%u bytes unused out of %u requested (%lu input)\n",
          231  +            size - have, size, def.total_in);
          232  +    return 0;
          233  +}

Added compat/zlib/examples/gun.c.

            1  +/* gun.c -- simple gunzip to give an example of the use of inflateBack()
            2  + * Copyright (C) 2003, 2005, 2008, 2010 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  +   Version 1.6  17 January 2010  Mark Adler */
            5  +
            6  +/* Version history:
            7  +   1.0  16 Feb 2003  First version for testing of inflateBack()
            8  +   1.1  21 Feb 2005  Decompress concatenated gzip streams
            9  +                     Remove use of "this" variable (C++ keyword)
           10  +                     Fix return value for in()
           11  +                     Improve allocation failure checking
           12  +                     Add typecasting for void * structures
           13  +                     Add -h option for command version and usage
           14  +                     Add a bunch of comments
           15  +   1.2  20 Mar 2005  Add Unix compress (LZW) decompression
           16  +                     Copy file attributes from input file to output file
           17  +   1.3  12 Jun 2005  Add casts for error messages [Oberhumer]
           18  +   1.4   8 Dec 2006  LZW decompression speed improvements
           19  +   1.5   9 Feb 2008  Avoid warning in latest version of gcc
           20  +   1.6  17 Jan 2010  Avoid signed/unsigned comparison warnings
           21  + */
           22  +
           23  +/*
           24  +   gun [ -t ] [ name ... ]
           25  +
           26  +   decompresses the data in the named gzip files.  If no arguments are given,
           27  +   gun will decompress from stdin to stdout.  The names must end in .gz, -gz,
           28  +   .z, -z, _z, or .Z.  The uncompressed data will be written to a file name
           29  +   with the suffix stripped.  On success, the original file is deleted.  On
           30  +   failure, the output file is deleted.  For most failures, the command will
           31  +   continue to process the remaining names on the command line.  A memory
           32  +   allocation failure will abort the command.  If -t is specified, then the
           33  +   listed files or stdin will be tested as gzip files for integrity (without
           34  +   checking for a proper suffix), no output will be written, and no files
           35  +   will be deleted.
           36  +
           37  +   Like gzip, gun allows concatenated gzip streams and will decompress them,
           38  +   writing all of the uncompressed data to the output.  Unlike gzip, gun allows
           39  +   an empty file on input, and will produce no error writing an empty output
           40  +   file.
           41  +
           42  +   gun will also decompress files made by Unix compress, which uses LZW
           43  +   compression.  These files are automatically detected by virtue of their
           44  +   magic header bytes.  Since the end of Unix compress stream is marked by the
           45  +   end-of-file, they cannot be concantenated.  If a Unix compress stream is
           46  +   encountered in an input file, it is the last stream in that file.
           47  +
           48  +   Like gunzip and uncompress, the file attributes of the orignal compressed
           49  +   file are maintained in the final uncompressed file, to the extent that the
           50  +   user permissions allow it.
           51  +
           52  +   On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
           53  +   1.2.4) is on the same file, when gun is linked with zlib 1.2.2.  Also the
           54  +   LZW decompression provided by gun is about twice as fast as the standard
           55  +   Unix uncompress command.
           56  + */
           57  +
           58  +/* external functions and related types and constants */
           59  +#include <stdio.h>          /* fprintf() */
           60  +#include <stdlib.h>         /* malloc(), free() */
           61  +#include <string.h>         /* strerror(), strcmp(), strlen(), memcpy() */
           62  +#include <errno.h>          /* errno */
           63  +#include <fcntl.h>          /* open() */
           64  +#include <unistd.h>         /* read(), write(), close(), chown(), unlink() */
           65  +#include <sys/types.h>
           66  +#include <sys/stat.h>       /* stat(), chmod() */
           67  +#include <utime.h>          /* utime() */
           68  +#include "zlib.h"           /* inflateBackInit(), inflateBack(), */
           69  +                            /* inflateBackEnd(), crc32() */
           70  +
           71  +/* function declaration */
           72  +#define local static
           73  +
           74  +/* buffer constants */
           75  +#define SIZE 32768U         /* input and output buffer sizes */
           76  +#define PIECE 16384         /* limits i/o chunks for 16-bit int case */
           77  +
           78  +/* structure for infback() to pass to input function in() -- it maintains the
           79  +   input file and a buffer of size SIZE */
           80  +struct ind {
           81  +    int infile;
           82  +    unsigned char *inbuf;
           83  +};
           84  +
           85  +/* Load input buffer, assumed to be empty, and return bytes loaded and a
           86  +   pointer to them.  read() is called until the buffer is full, or until it
           87  +   returns end-of-file or error.  Return 0 on error. */
           88  +local unsigned in(void *in_desc, unsigned char **buf)
           89  +{
           90  +    int ret;
           91  +    unsigned len;
           92  +    unsigned char *next;
           93  +    struct ind *me = (struct ind *)in_desc;
           94  +
           95  +    next = me->inbuf;
           96  +    *buf = next;
           97  +    len = 0;
           98  +    do {
           99  +        ret = PIECE;
          100  +        if ((unsigned)ret > SIZE - len)
          101  +            ret = (int)(SIZE - len);
          102  +        ret = (int)read(me->infile, next, ret);
          103  +        if (ret == -1) {
          104  +            len = 0;
          105  +            break;
          106  +        }
          107  +        next += ret;
          108  +        len += ret;
          109  +    } while (ret != 0 && len < SIZE);
          110  +    return len;
          111  +}
          112  +
          113  +/* structure for infback() to pass to output function out() -- it maintains the
          114  +   output file, a running CRC-32 check on the output and the total number of
          115  +   bytes output, both for checking against the gzip trailer.  (The length in
          116  +   the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
          117  +   the output is greater than 4 GB.) */
          118  +struct outd {
          119  +    int outfile;
          120  +    int check;                  /* true if checking crc and total */
          121  +    unsigned long crc;
          122  +    unsigned long total;
          123  +};
          124  +
          125  +/* Write output buffer and update the CRC-32 and total bytes written.  write()
          126  +   is called until all of the output is written or an error is encountered.
          127  +   On success out() returns 0.  For a write failure, out() returns 1.  If the
          128  +   output file descriptor is -1, then nothing is written.
          129  + */
          130  +local int out(void *out_desc, unsigned char *buf, unsigned len)
          131  +{
          132  +    int ret;
          133  +    struct outd *me = (struct outd *)out_desc;
          134  +
          135  +    if (me->check) {
          136  +        me->crc = crc32(me->crc, buf, len);
          137  +        me->total += len;
          138  +    }
          139  +    if (me->outfile != -1)
          140  +        do {
          141  +            ret = PIECE;
          142  +            if ((unsigned)ret > len)
          143  +                ret = (int)len;
          144  +            ret = (int)write(me->outfile, buf, ret);
          145  +            if (ret == -1)
          146  +                return 1;
          147  +            buf += ret;
          148  +            len -= ret;
          149  +        } while (len != 0);
          150  +    return 0;
          151  +}
          152  +
          153  +/* next input byte macro for use inside lunpipe() and gunpipe() */
          154  +#define NEXT() (have ? 0 : (have = in(indp, &next)), \
          155  +                last = have ? (have--, (int)(*next++)) : -1)
          156  +
          157  +/* memory for gunpipe() and lunpipe() --
          158  +   the first 256 entries of prefix[] and suffix[] are never used, could
          159  +   have offset the index, but it's faster to waste the memory */
          160  +unsigned char inbuf[SIZE];              /* input buffer */
          161  +unsigned char outbuf[SIZE];             /* output buffer */
          162  +unsigned short prefix[65536];           /* index to LZW prefix string */
          163  +unsigned char suffix[65536];            /* one-character LZW suffix */
          164  +unsigned char match[65280 + 2];         /* buffer for reversed match or gzip
          165  +                                           32K sliding window */
          166  +
          167  +/* throw out what's left in the current bits byte buffer (this is a vestigial
          168  +   aspect of the compressed data format derived from an implementation that
          169  +   made use of a special VAX machine instruction!) */
          170  +#define FLUSHCODE() \
          171  +    do { \
          172  +        left = 0; \
          173  +        rem = 0; \
          174  +        if (chunk > have) { \
          175  +            chunk -= have; \
          176  +            have = 0; \
          177  +            if (NEXT() == -1) \
          178  +                break; \
          179  +            chunk--; \
          180  +            if (chunk > have) { \
          181  +                chunk = have = 0; \
          182  +                break; \
          183  +            } \
          184  +        } \
          185  +        have -= chunk; \
          186  +        next += chunk; \
          187  +        chunk = 0; \
          188  +    } while (0)
          189  +
          190  +/* Decompress a compress (LZW) file from indp to outfile.  The compress magic
          191  +   header (two bytes) has already been read and verified.  There are have bytes
          192  +   of buffered input at next.  strm is used for passing error information back
          193  +   to gunpipe().
          194  +
          195  +   lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
          196  +   file, read error, or write error (a write error indicated by strm->next_in
          197  +   not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
          198  + */
          199  +local int lunpipe(unsigned have, unsigned char *next, struct ind *indp,
          200  +                  int outfile, z_stream *strm)
          201  +{
          202  +    int last;                   /* last byte read by NEXT(), or -1 if EOF */
          203  +    unsigned chunk;             /* bytes left in current chunk */
          204  +    int left;                   /* bits left in rem */
          205  +    unsigned rem;               /* unused bits from input */
          206  +    int bits;                   /* current bits per code */
          207  +    unsigned code;              /* code, table traversal index */
          208  +    unsigned mask;              /* mask for current bits codes */
          209  +    int max;                    /* maximum bits per code for this stream */
          210  +    unsigned flags;             /* compress flags, then block compress flag */
          211  +    unsigned end;               /* last valid entry in prefix/suffix tables */
          212  +    unsigned temp;              /* current code */
          213  +    unsigned prev;              /* previous code */
          214  +    unsigned final;             /* last character written for previous code */
          215  +    unsigned stack;             /* next position for reversed string */
          216  +    unsigned outcnt;            /* bytes in output buffer */
          217  +    struct outd outd;           /* output structure */
          218  +    unsigned char *p;
          219  +
          220  +    /* set up output */
          221  +    outd.outfile = outfile;
          222  +    outd.check = 0;
          223  +
          224  +    /* process remainder of compress header -- a flags byte */
          225  +    flags = NEXT();
          226  +    if (last == -1)
          227  +        return Z_BUF_ERROR;
          228  +    if (flags & 0x60) {
          229  +        strm->msg = (char *)"unknown lzw flags set";
          230  +        return Z_DATA_ERROR;
          231  +    }
          232  +    max = flags & 0x1f;
          233  +    if (max < 9 || max > 16) {
          234  +        strm->msg = (char *)"lzw bits out of range";
          235  +        return Z_DATA_ERROR;
          236  +    }
          237  +    if (max == 9)                           /* 9 doesn't really mean 9 */
          238  +        max = 10;
          239  +    flags &= 0x80;                          /* true if block compress */
          240  +
          241  +    /* clear table */
          242  +    bits = 9;
          243  +    mask = 0x1ff;
          244  +    end = flags ? 256 : 255;
          245  +
          246  +    /* set up: get first 9-bit code, which is the first decompressed byte, but
          247  +       don't create a table entry until the next code */
          248  +    if (NEXT() == -1)                       /* no compressed data is ok */
          249  +        return Z_OK;
          250  +    final = prev = (unsigned)last;          /* low 8 bits of code */
          251  +    if (NEXT() == -1)                       /* missing a bit */
          252  +        return Z_BUF_ERROR;
          253  +    if (last & 1) {                         /* code must be < 256 */
          254  +        strm->msg = (char *)"invalid lzw code";
          255  +        return Z_DATA_ERROR;
          256  +    }
          257  +    rem = (unsigned)last >> 1;              /* remaining 7 bits */
          258  +    left = 7;
          259  +    chunk = bits - 2;                       /* 7 bytes left in this chunk */
          260  +    outbuf[0] = (unsigned char)final;       /* write first decompressed byte */
          261  +    outcnt = 1;
          262  +
          263  +    /* decode codes */
          264  +    stack = 0;
          265  +    for (;;) {
          266  +        /* if the table will be full after this, increment the code size */
          267  +        if (end >= mask && bits < max) {
          268  +            FLUSHCODE();
          269  +            bits++;
          270  +            mask <<= 1;
          271  +            mask++;
          272  +        }
          273  +
          274  +        /* get a code of length bits */
          275  +        if (chunk == 0)                     /* decrement chunk modulo bits */
          276  +            chunk = bits;
          277  +        code = rem;                         /* low bits of code */
          278  +        if (NEXT() == -1) {                 /* EOF is end of compressed data */
          279  +            /* write remaining buffered output */
          280  +            if (outcnt && out(&outd, outbuf, outcnt)) {
          281  +                strm->next_in = outbuf;     /* signal write error */
          282  +                return Z_BUF_ERROR;
          283  +            }
          284  +            return Z_OK;
          285  +        }
          286  +        code += (unsigned)last << left;     /* middle (or high) bits of code */
          287  +        left += 8;
          288  +        chunk--;
          289  +        if (bits > left) {                  /* need more bits */
          290  +            if (NEXT() == -1)               /* can't end in middle of code */
          291  +                return Z_BUF_ERROR;
          292  +            code += (unsigned)last << left; /* high bits of code */
          293  +            left += 8;
          294  +            chunk--;
          295  +        }
          296  +        code &= mask;                       /* mask to current code length */
          297  +        left -= bits;                       /* number of unused bits */
          298  +        rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
          299  +
          300  +        /* process clear code (256) */
          301  +        if (code == 256 && flags) {
          302  +            FLUSHCODE();
          303  +            bits = 9;                       /* initialize bits and mask */
          304  +            mask = 0x1ff;
          305  +            end = 255;                      /* empty table */
          306  +            continue;                       /* get next code */
          307  +        }
          308  +
          309  +        /* special code to reuse last match */
          310  +        temp = code;                        /* save the current code */
          311  +        if (code > end) {
          312  +            /* Be picky on the allowed code here, and make sure that the code
          313  +               we drop through (prev) will be a valid index so that random
          314  +               input does not cause an exception.  The code != end + 1 check is
          315  +               empirically derived, and not checked in the original uncompress
          316  +               code.  If this ever causes a problem, that check could be safely
          317  +               removed.  Leaving this check in greatly improves gun's ability
          318  +               to detect random or corrupted input after a compress header.
          319  +               In any case, the prev > end check must be retained. */
          320  +            if (code != end + 1 || prev > end) {
          321  +                strm->msg = (char *)"invalid lzw code";
          322  +                return Z_DATA_ERROR;
          323  +            }
          324  +            match[stack++] = (unsigned char)final;
          325  +            code = prev;
          326  +        }
          327  +
          328  +        /* walk through linked list to generate output in reverse order */
          329  +        p = match + stack;
          330  +        while (code >= 256) {
          331  +            *p++ = suffix[code];
          332  +            code = prefix[code];
          333  +        }
          334  +        stack = p - match;
          335  +        match[stack++] = (unsigned char)code;
          336  +        final = code;
          337  +
          338  +        /* link new table entry */
          339  +        if (end < mask) {
          340  +            end++;
          341  +            prefix[end] = (unsigned short)prev;
          342  +            suffix[end] = (unsigned char)final;
          343  +        }
          344  +
          345  +        /* set previous code for next iteration */
          346  +        prev = temp;
          347  +
          348  +        /* write output in forward order */
          349  +        while (stack > SIZE - outcnt) {
          350  +            while (outcnt < SIZE)
          351  +                outbuf[outcnt++] = match[--stack];
          352  +            if (out(&outd, outbuf, outcnt)) {
          353  +                strm->next_in = outbuf; /* signal write error */
          354  +                return Z_BUF_ERROR;
          355  +            }
          356  +            outcnt = 0;
          357  +        }
          358  +        p = match + stack;
          359  +        do {
          360  +            outbuf[outcnt++] = *--p;
          361  +        } while (p > match);
          362  +        stack = 0;
          363  +
          364  +        /* loop for next code with final and prev as the last match, rem and
          365  +           left provide the first 0..7 bits of the next code, end is the last
          366  +           valid table entry */
          367  +    }
          368  +}
          369  +
          370  +/* Decompress a gzip file from infile to outfile.  strm is assumed to have been
          371  +   successfully initialized with inflateBackInit().  The input file may consist
          372  +   of a series of gzip streams, in which case all of them will be decompressed
          373  +   to the output file.  If outfile is -1, then the gzip stream(s) integrity is
          374  +   checked and nothing is written.
          375  +
          376  +   The return value is a zlib error code: Z_MEM_ERROR if out of memory,
          377  +   Z_DATA_ERROR if the header or the compressed data is invalid, or if the
          378  +   trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
          379  +   prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
          380  +   stream) follows a valid gzip stream.
          381  + */
          382  +local int gunpipe(z_stream *strm, int infile, int outfile)
          383  +{
          384  +    int ret, first, last;
          385  +    unsigned have, flags, len;
          386  +    unsigned char *next = NULL;
          387  +    struct ind ind, *indp;
          388  +    struct outd outd;
          389  +
          390  +    /* setup input buffer */
          391  +    ind.infile = infile;
          392  +    ind.inbuf = inbuf;
          393  +    indp = &ind;
          394  +
          395  +    /* decompress concatenated gzip streams */
          396  +    have = 0;                               /* no input data read in yet */
          397  +    first = 1;                              /* looking for first gzip header */
          398  +    strm->next_in = Z_NULL;                 /* so Z_BUF_ERROR means EOF */
          399  +    for (;;) {
          400  +        /* look for the two magic header bytes for a gzip stream */
          401  +        if (NEXT() == -1) {
          402  +            ret = Z_OK;
          403  +            break;                          /* empty gzip stream is ok */
          404  +        }
          405  +        if (last != 31 || (NEXT() != 139 && last != 157)) {
          406  +            strm->msg = (char *)"incorrect header check";
          407  +            ret = first ? Z_DATA_ERROR : Z_ERRNO;
          408  +            break;                          /* not a gzip or compress header */
          409  +        }
          410  +        first = 0;                          /* next non-header is junk */
          411  +
          412  +        /* process a compress (LZW) file -- can't be concatenated after this */
          413  +        if (last == 157) {
          414  +            ret = lunpipe(have, next, indp, outfile, strm);
          415  +            break;
          416  +        }
          417  +
          418  +        /* process remainder of gzip header */
          419  +        ret = Z_BUF_ERROR;
          420  +        if (NEXT() != 8) {                  /* only deflate method allowed */
          421  +            if (last == -1) break;
          422  +            strm->msg = (char *)"unknown compression method";
          423  +            ret = Z_DATA_ERROR;
          424  +            break;
          425  +        }
          426  +        flags = NEXT();                     /* header flags */
          427  +        NEXT();                             /* discard mod time, xflgs, os */
          428  +        NEXT();
          429  +        NEXT();
          430  +        NEXT();
          431  +        NEXT();
          432  +        NEXT();
          433  +        if (last == -1) break;
          434  +        if (flags & 0xe0) {
          435  +            strm->msg = (char *)"unknown header flags set";
          436  +            ret = Z_DATA_ERROR;
          437  +            break;
          438  +        }
          439  +        if (flags & 4) {                    /* extra field */
          440  +            len = NEXT();
          441  +            len += (unsigned)(NEXT()) << 8;
          442  +            if (last == -1) break;
          443  +            while (len > have) {
          444  +                len -= have;
          445  +                have = 0;
          446  +                if (NEXT() == -1) break;
          447  +                len--;
          448  +            }
          449  +            if (last == -1) break;
          450  +            have -= len;
          451  +            next += len;
          452  +        }
          453  +        if (flags & 8)                      /* file name */
          454  +            while (NEXT() != 0 && last != -1)
          455  +                ;
          456  +        if (flags & 16)                     /* comment */
          457  +            while (NEXT() != 0 && last != -1)
          458  +                ;
          459  +        if (flags & 2) {                    /* header crc */
          460  +            NEXT();
          461  +            NEXT();
          462  +        }
          463  +        if (last == -1) break;
          464  +
          465  +        /* set up output */
          466  +        outd.outfile = outfile;
          467  +        outd.check = 1;
          468  +        outd.crc = crc32(0L, Z_NULL, 0);
          469  +        outd.total = 0;
          470  +
          471  +        /* decompress data to output */
          472  +        strm->next_in = next;
          473  +        strm->avail_in = have;
          474  +        ret = inflateBack(strm, in, indp, out, &outd);
          475  +        if (ret != Z_STREAM_END) break;
          476  +        next = strm->next_in;
          477  +        have = strm->avail_in;
          478  +        strm->next_in = Z_NULL;             /* so Z_BUF_ERROR means EOF */
          479  +
          480  +        /* check trailer */
          481  +        ret = Z_BUF_ERROR;
          482  +        if (NEXT() != (int)(outd.crc & 0xff) ||
          483  +            NEXT() != (int)((outd.crc >> 8) & 0xff) ||
          484  +            NEXT() != (int)((outd.crc >> 16) & 0xff) ||
          485  +            NEXT() != (int)((outd.crc >> 24) & 0xff)) {
          486  +            /* crc error */
          487  +            if (last != -1) {
          488  +                strm->msg = (char *)"incorrect data check";
          489  +                ret = Z_DATA_ERROR;
          490  +            }
          491  +            break;
          492  +        }
          493  +        if (NEXT() != (int)(outd.total & 0xff) ||
          494  +            NEXT() != (int)((outd.total >> 8) & 0xff) ||
          495  +            NEXT() != (int)((outd.total >> 16) & 0xff) ||
          496  +            NEXT() != (int)((outd.total >> 24) & 0xff)) {
          497  +            /* length error */
          498  +            if (last != -1) {
          499  +                strm->msg = (char *)"incorrect length check";
          500  +                ret = Z_DATA_ERROR;
          501  +            }
          502  +            break;
          503  +        }
          504  +
          505  +        /* go back and look for another gzip stream */
          506  +    }
          507  +
          508  +    /* clean up and return */
          509  +    return ret;
          510  +}
          511  +
          512  +/* Copy file attributes, from -> to, as best we can.  This is best effort, so
          513  +   no errors are reported.  The mode bits, including suid, sgid, and the sticky
          514  +   bit are copied (if allowed), the owner's user id and group id are copied
          515  +   (again if allowed), and the access and modify times are copied. */
          516  +local void copymeta(char *from, char *to)
          517  +{
          518  +    struct stat was;
          519  +    struct utimbuf when;
          520  +
          521  +    /* get all of from's Unix meta data, return if not a regular file */
          522  +    if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
          523  +        return;
          524  +
          525  +    /* set to's mode bits, ignore errors */
          526  +    (void)chmod(to, was.st_mode & 07777);
          527  +
          528  +    /* copy owner's user and group, ignore errors */
          529  +    (void)chown(to, was.st_uid, was.st_gid);
          530  +
          531  +    /* copy access and modify times, ignore errors */
          532  +    when.actime = was.st_atime;
          533  +    when.modtime = was.st_mtime;
          534  +    (void)utime(to, &when);
          535  +}
          536  +
          537  +/* Decompress the file inname to the file outnname, of if test is true, just
          538  +   decompress without writing and check the gzip trailer for integrity.  If
          539  +   inname is NULL or an empty string, read from stdin.  If outname is NULL or
          540  +   an empty string, write to stdout.  strm is a pre-initialized inflateBack
          541  +   structure.  When appropriate, copy the file attributes from inname to
          542  +   outname.
          543  +
          544  +   gunzip() returns 1 if there is an out-of-memory error or an unexpected
          545  +   return code from gunpipe().  Otherwise it returns 0.
          546  + */
          547  +local int gunzip(z_stream *strm, char *inname, char *outname, int test)
          548  +{
          549  +    int ret;
          550  +    int infile, outfile;
          551  +
          552  +    /* open files */
          553  +    if (inname == NULL || *inname == 0) {
          554  +        inname = "-";
          555  +        infile = 0;     /* stdin */
          556  +    }
          557  +    else {
          558  +        infile = open(inname, O_RDONLY, 0);
          559  +        if (infile == -1) {
          560  +            fprintf(stderr, "gun cannot open %s\n", inname);
          561  +            return 0;
          562  +        }
          563  +    }
          564  +    if (test)
          565  +        outfile = -1;
          566  +    else if (outname == NULL || *outname == 0) {
          567  +        outname = "-";
          568  +        outfile = 1;    /* stdout */
          569  +    }
          570  +    else {
          571  +        outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
          572  +        if (outfile == -1) {
          573  +            close(infile);
          574  +            fprintf(stderr, "gun cannot create %s\n", outname);
          575  +            return 0;
          576  +        }
          577  +    }
          578  +    errno = 0;
          579  +
          580  +    /* decompress */
          581  +    ret = gunpipe(strm, infile, outfile);
          582  +    if (outfile > 2) close(outfile);
          583  +    if (infile > 2) close(infile);
          584  +
          585  +    /* interpret result */
          586  +    switch (ret) {
          587  +    case Z_OK:
          588  +    case Z_ERRNO:
          589  +        if (infile > 2 && outfile > 2) {
          590  +            copymeta(inname, outname);          /* copy attributes */
          591  +            unlink(inname);
          592  +        }
          593  +        if (ret == Z_ERRNO)
          594  +            fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
          595  +                    inname);
          596  +        break;
          597  +    case Z_DATA_ERROR:
          598  +        if (outfile > 2) unlink(outname);
          599  +        fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
          600  +        break;
          601  +    case Z_MEM_ERROR:
          602  +        if (outfile > 2) unlink(outname);
          603  +        fprintf(stderr, "gun out of memory error--aborting\n");
          604  +        return 1;
          605  +    case Z_BUF_ERROR:
          606  +        if (outfile > 2) unlink(outname);
          607  +        if (strm->next_in != Z_NULL) {
          608  +            fprintf(stderr, "gun write error on %s: %s\n",
          609  +                    outname, strerror(errno));
          610  +        }
          611  +        else if (errno) {
          612  +            fprintf(stderr, "gun read error on %s: %s\n",
          613  +                    inname, strerror(errno));
          614  +        }
          615  +        else {
          616  +            fprintf(stderr, "gun unexpected end of file on %s\n",
          617  +                    inname);
          618  +        }
          619  +        break;
          620  +    default:
          621  +        if (outfile > 2) unlink(outname);
          622  +        fprintf(stderr, "gun internal error--aborting\n");
          623  +        return 1;
          624  +    }
          625  +    return 0;
          626  +}
          627  +
          628  +/* Process the gun command line arguments.  See the command syntax near the
          629  +   beginning of this source file. */
          630  +int main(int argc, char **argv)
          631  +{
          632  +    int ret, len, test;
          633  +    char *outname;
          634  +    unsigned char *window;
          635  +    z_stream strm;
          636  +
          637  +    /* initialize inflateBack state for repeated use */
          638  +    window = match;                         /* reuse LZW match buffer */
          639  +    strm.zalloc = Z_NULL;
          640  +    strm.zfree = Z_NULL;
          641  +    strm.opaque = Z_NULL;
          642  +    ret = inflateBackInit(&strm, 15, window);
          643  +    if (ret != Z_OK) {
          644  +        fprintf(stderr, "gun out of memory error--aborting\n");
          645  +        return 1;
          646  +    }
          647  +
          648  +    /* decompress each file to the same name with the suffix removed */
          649  +    argc--;
          650  +    argv++;
          651  +    test = 0;
          652  +    if (argc && strcmp(*argv, "-h") == 0) {
          653  +        fprintf(stderr, "gun 1.6 (17 Jan 2010)\n");
          654  +        fprintf(stderr, "Copyright (C) 2003-2010 Mark Adler\n");
          655  +        fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n");
          656  +        return 0;
          657  +    }
          658  +    if (argc && strcmp(*argv, "-t") == 0) {
          659  +        test = 1;
          660  +        argc--;
          661  +        argv++;
          662  +    }
          663  +    if (argc)
          664  +        do {
          665  +            if (test)
          666  +                outname = NULL;
          667  +            else {
          668  +                len = (int)strlen(*argv);
          669  +                if (strcmp(*argv + len - 3, ".gz") == 0 ||
          670  +                    strcmp(*argv + len - 3, "-gz") == 0)
          671  +                    len -= 3;
          672  +                else if (strcmp(*argv + len - 2, ".z") == 0 ||
          673  +                    strcmp(*argv + len - 2, "-z") == 0 ||
          674  +                    strcmp(*argv + len - 2, "_z") == 0 ||
          675  +                    strcmp(*argv + len - 2, ".Z") == 0)
          676  +                    len -= 2;
          677  +                else {
          678  +                    fprintf(stderr, "gun error: no gz type on %s--skipping\n",
          679  +                            *argv);
          680  +                    continue;
          681  +                }
          682  +                outname = malloc(len + 1);
          683  +                if (outname == NULL) {
          684  +                    fprintf(stderr, "gun out of memory error--aborting\n");
          685  +                    ret = 1;
          686  +                    break;
          687  +                }
          688  +                memcpy(outname, *argv, len);
          689  +                outname[len] = 0;
          690  +            }
          691  +            ret = gunzip(&strm, *argv, outname, test);
          692  +            if (outname != NULL) free(outname);
          693  +            if (ret) break;
          694  +        } while (argv++, --argc);
          695  +    else
          696  +        ret = gunzip(&strm, NULL, NULL, test);
          697  +
          698  +    /* clean up */
          699  +    inflateBackEnd(&strm);
          700  +    return ret;
          701  +}

Added compat/zlib/examples/gzappend.c.

            1  +/* gzappend -- command to append to a gzip file
            2  +
            3  +  Copyright (C) 2003 Mark Adler, all rights reserved
            4  +  version 1.1, 4 Nov 2003
            5  +
            6  +  This software is provided 'as-is', without any express or implied
            7  +  warranty.  In no event will the author be held liable for any damages
            8  +  arising from the use of this software.
            9  +
           10  +  Permission is granted to anyone to use this software for any purpose,
           11  +  including commercial applications, and to alter it and redistribute it
           12  +  freely, subject to the following restrictions:
           13  +
           14  +  1. The origin of this software must not be misrepresented; you must not
           15  +     claim that you wrote the original software. If you use this software
           16  +     in a product, an acknowledgment in the product documentation would be
           17  +     appreciated but is not required.
           18  +  2. Altered source versions must be plainly marked as such, and must not be
           19  +     misrepresented as being the original software.
           20  +  3. This notice may not be removed or altered from any source distribution.
           21  +
           22  +  Mark Adler    madler@alumni.caltech.edu
           23  + */
           24  +
           25  +/*
           26  + * Change history:
           27  + *
           28  + * 1.0  19 Oct 2003     - First version
           29  + * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
           30  + *                      - Add version and copyright to help
           31  + *                      - Send help to stdout instead of stderr
           32  + *                      - Add some preemptive typecasts
           33  + *                      - Add L to constants in lseek() calls
           34  + *                      - Remove some debugging information in error messages
           35  + *                      - Use new data_type definition for zlib 1.2.1
           36  + *                      - Simplfy and unify file operations
           37  + *                      - Finish off gzip file in gztack()
           38  + *                      - Use deflatePrime() instead of adding empty blocks
           39  + *                      - Keep gzip file clean on appended file read errors
           40  + *                      - Use in-place rotate instead of auxiliary buffer
           41  + *                        (Why you ask?  Because it was fun to write!)
           42  + */
           43  +
           44  +/*
           45  +   gzappend takes a gzip file and appends to it, compressing files from the
           46  +   command line or data from stdin.  The gzip file is written to directly, to
           47  +   avoid copying that file, in case it's large.  Note that this results in the
           48  +   unfriendly behavior that if gzappend fails, the gzip file is corrupted.
           49  +
           50  +   This program was written to illustrate the use of the new Z_BLOCK option of
           51  +   zlib 1.2.x's inflate() function.  This option returns from inflate() at each
           52  +   block boundary to facilitate locating and modifying the last block bit at
           53  +   the start of the final deflate block.  Also whether using Z_BLOCK or not,
           54  +   another required feature of zlib 1.2.x is that inflate() now provides the
           55  +   number of unusued bits in the last input byte used.  gzappend will not work
           56  +   with versions of zlib earlier than 1.2.1.
           57  +
           58  +   gzappend first decompresses the gzip file internally, discarding all but
           59  +   the last 32K of uncompressed data, and noting the location of the last block
           60  +   bit and the number of unused bits in the last byte of the compressed data.
           61  +   The gzip trailer containing the CRC-32 and length of the uncompressed data
           62  +   is verified.  This trailer will be later overwritten.
           63  +
           64  +   Then the last block bit is cleared by seeking back in the file and rewriting
           65  +   the byte that contains it.  Seeking forward, the last byte of the compressed
           66  +   data is saved along with the number of unused bits to initialize deflate.
           67  +
           68  +   A deflate process is initialized, using the last 32K of the uncompressed
           69  +   data from the gzip file to initialize the dictionary.  If the total
           70  +   uncompressed data was less than 32K, then all of it is used to initialize
           71  +   the dictionary.  The deflate output bit buffer is also initialized with the
           72  +   last bits from the original deflate stream.  From here on, the data to
           73  +   append is simply compressed using deflate, and written to the gzip file.
           74  +   When that is complete, the new CRC-32 and uncompressed length are written
           75  +   as the trailer of the gzip file.
           76  + */
           77  +
           78  +#include <stdio.h>
           79  +#include <stdlib.h>
           80  +#include <string.h>
           81  +#include <fcntl.h>
           82  +#include <unistd.h>
           83  +#include "zlib.h"
           84  +
           85  +#define local static
           86  +#define LGCHUNK 14
           87  +#define CHUNK (1U << LGCHUNK)
           88  +#define DSIZE 32768U
           89  +
           90  +/* print an error message and terminate with extreme prejudice */
           91  +local void bye(char *msg1, char *msg2)
           92  +{
           93  +    fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
           94  +    exit(1);
           95  +}
           96  +
           97  +/* return the greatest common divisor of a and b using Euclid's algorithm,
           98  +   modified to be fast when one argument much greater than the other, and
           99  +   coded to avoid unnecessary swapping */
          100  +local unsigned gcd(unsigned a, unsigned b)
          101  +{
          102  +    unsigned c;
          103  +
          104  +    while (a && b)
          105  +        if (a > b) {
          106  +            c = b;
          107  +            while (a - c >= c)
          108  +                c <<= 1;
          109  +            a -= c;
          110  +        }
          111  +        else {
          112  +            c = a;
          113  +            while (b - c >= c)
          114  +                c <<= 1;
          115  +            b -= c;
          116  +        }
          117  +    return a + b;
          118  +}
          119  +
          120  +/* rotate list[0..len-1] left by rot positions, in place */
          121  +local void rotate(unsigned char *list, unsigned len, unsigned rot)
          122  +{
          123  +    unsigned char tmp;
          124  +    unsigned cycles;
          125  +    unsigned char *start, *last, *to, *from;
          126  +
          127  +    /* normalize rot and handle degenerate cases */
          128  +    if (len < 2) return;
          129  +    if (rot >= len) rot %= len;
          130  +    if (rot == 0) return;
          131  +
          132  +    /* pointer to last entry in list */
          133  +    last = list + (len - 1);
          134  +
          135  +    /* do simple left shift by one */
          136  +    if (rot == 1) {
          137  +        tmp = *list;
          138  +        memcpy(list, list + 1, len - 1);
          139  +        *last = tmp;
          140  +        return;
          141  +    }
          142  +
          143  +    /* do simple right shift by one */
          144  +    if (rot == len - 1) {
          145  +        tmp = *last;
          146  +        memmove(list + 1, list, len - 1);
          147  +        *list = tmp;
          148  +        return;
          149  +    }
          150  +
          151  +    /* otherwise do rotate as a set of cycles in place */
          152  +    cycles = gcd(len, rot);             /* number of cycles */
          153  +    do {
          154  +        start = from = list + cycles;   /* start index is arbitrary */
          155  +        tmp = *from;                    /* save entry to be overwritten */
          156  +        for (;;) {
          157  +            to = from;                  /* next step in cycle */
          158  +            from += rot;                /* go right rot positions */
          159  +            if (from > last) from -= len;   /* (pointer better not wrap) */
          160  +            if (from == start) break;   /* all but one shifted */
          161  +            *to = *from;                /* shift left */
          162  +        }
          163  +        *to = tmp;                      /* complete the circle */
          164  +    } while (--cycles);
          165  +}
          166  +
          167  +/* structure for gzip file read operations */
          168  +typedef struct {
          169  +    int fd;                     /* file descriptor */
          170  +    int size;                   /* 1 << size is bytes in buf */
          171  +    unsigned left;              /* bytes available at next */
          172  +    unsigned char *buf;         /* buffer */
          173  +    unsigned char *next;        /* next byte in buffer */
          174  +    char *name;                 /* file name for error messages */
          175  +} file;
          176  +
          177  +/* reload buffer */
          178  +local int readin(file *in)
          179  +{
          180  +    int len;
          181  +
          182  +    len = read(in->fd, in->buf, 1 << in->size);
          183  +    if (len == -1) bye("error reading ", in->name);
          184  +    in->left = (unsigned)len;
          185  +    in->next = in->buf;
          186  +    return len;
          187  +}
          188  +
          189  +/* read from file in, exit if end-of-file */
          190  +local int readmore(file *in)
          191  +{
          192  +    if (readin(in) == 0) bye("unexpected end of ", in->name);
          193  +    return 0;
          194  +}
          195  +
          196  +#define read1(in) (in->left == 0 ? readmore(in) : 0, \
          197  +                   in->left--, *(in->next)++)
          198  +
          199  +/* skip over n bytes of in */
          200  +local void skip(file *in, unsigned n)
          201  +{
          202  +    unsigned bypass;
          203  +
          204  +    if (n > in->left) {
          205  +        n -= in->left;
          206  +        bypass = n & ~((1U << in->size) - 1);
          207  +        if (bypass) {
          208  +            if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
          209  +                bye("seeking ", in->name);
          210  +            n -= bypass;
          211  +        }
          212  +        readmore(in);
          213  +        if (n > in->left)
          214  +            bye("unexpected end of ", in->name);
          215  +    }
          216  +    in->left -= n;
          217  +    in->next += n;
          218  +}
          219  +
          220  +/* read a four-byte unsigned integer, little-endian, from in */
          221  +unsigned long read4(file *in)
          222  +{
          223  +    unsigned long val;
          224  +
          225  +    val = read1(in);
          226  +    val += (unsigned)read1(in) << 8;
          227  +    val += (unsigned long)read1(in) << 16;
          228  +    val += (unsigned long)read1(in) << 24;
          229  +    return val;
          230  +}
          231  +
          232  +/* skip over gzip header */
          233  +local void gzheader(file *in)
          234  +{
          235  +    int flags;
          236  +    unsigned n;
          237  +
          238  +    if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
          239  +    if (read1(in) != 8) bye("unknown compression method in", in->name);
          240  +    flags = read1(in);
          241  +    if (flags & 0xe0) bye("unknown header flags set in", in->name);
          242  +    skip(in, 6);
          243  +    if (flags & 4) {
          244  +        n = read1(in);
          245  +        n += (unsigned)(read1(in)) << 8;
          246  +        skip(in, n);
          247  +    }
          248  +    if (flags & 8) while (read1(in) != 0) ;
          249  +    if (flags & 16) while (read1(in) != 0) ;
          250  +    if (flags & 2) skip(in, 2);
          251  +}
          252  +
          253  +/* decompress gzip file "name", return strm with a deflate stream ready to
          254  +   continue compression of the data in the gzip file, and return a file
          255  +   descriptor pointing to where to write the compressed data -- the deflate
          256  +   stream is initialized to compress using level "level" */
          257  +local int gzscan(char *name, z_stream *strm, int level)
          258  +{
          259  +    int ret, lastbit, left, full;
          260  +    unsigned have;
          261  +    unsigned long crc, tot;
          262  +    unsigned char *window;
          263  +    off_t lastoff, end;
          264  +    file gz;
          265  +
          266  +    /* open gzip file */
          267  +    gz.name = name;
          268  +    gz.fd = open(name, O_RDWR, 0);
          269  +    if (gz.fd == -1) bye("cannot open ", name);
          270  +    gz.buf = malloc(CHUNK);
          271  +    if (gz.buf == NULL) bye("out of memory", "");
          272  +    gz.size = LGCHUNK;
          273  +    gz.left = 0;
          274  +
          275  +    /* skip gzip header */
          276  +    gzheader(&gz);
          277  +
          278  +    /* prepare to decompress */
          279  +    window = malloc(DSIZE);
          280  +    if (window == NULL) bye("out of memory", "");
          281  +    strm->zalloc = Z_NULL;
          282  +    strm->zfree = Z_NULL;
          283  +    strm->opaque = Z_NULL;
          284  +    ret = inflateInit2(strm, -15);
          285  +    if (ret != Z_OK) bye("out of memory", " or library mismatch");
          286  +
          287  +    /* decompress the deflate stream, saving append information */
          288  +    lastbit = 0;
          289  +    lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
          290  +    left = 0;
          291  +    strm->avail_in = gz.left;
          292  +    strm->next_in = gz.next;
          293  +    crc = crc32(0L, Z_NULL, 0);
          294  +    have = full = 0;
          295  +    do {
          296  +        /* if needed, get more input */
          297  +        if (strm->avail_in == 0) {
          298  +            readmore(&gz);
          299  +            strm->avail_in = gz.left;
          300  +            strm->next_in = gz.next;
          301  +        }
          302  +
          303  +        /* set up output to next available section of sliding window */
          304  +        strm->avail_out = DSIZE - have;
          305  +        strm->next_out = window + have;
          306  +
          307  +        /* inflate and check for errors */
          308  +        ret = inflate(strm, Z_BLOCK);
          309  +        if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
          310  +        if (ret == Z_MEM_ERROR) bye("out of memory", "");
          311  +        if (ret == Z_DATA_ERROR)
          312  +            bye("invalid compressed data--format violated in", name);
          313  +
          314  +        /* update crc and sliding window pointer */
          315  +        crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
          316  +        if (strm->avail_out)
          317  +            have = DSIZE - strm->avail_out;
          318  +        else {
          319  +            have = 0;
          320  +            full = 1;
          321  +        }
          322  +
          323  +        /* process end of block */
          324  +        if (strm->data_type & 128) {
          325  +            if (strm->data_type & 64)
          326  +                left = strm->data_type & 0x1f;
          327  +            else {
          328  +                lastbit = strm->data_type & 0x1f;
          329  +                lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
          330  +            }
          331  +        }
          332  +    } while (ret != Z_STREAM_END);
          333  +    inflateEnd(strm);
          334  +    gz.left = strm->avail_in;
          335  +    gz.next = strm->next_in;
          336  +
          337  +    /* save the location of the end of the compressed data */
          338  +    end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
          339  +
          340  +    /* check gzip trailer and save total for deflate */
          341  +    if (crc != read4(&gz))
          342  +        bye("invalid compressed data--crc mismatch in ", name);
          343  +    tot = strm->total_out;
          344  +    if ((tot & 0xffffffffUL) != read4(&gz))
          345  +        bye("invalid compressed data--length mismatch in", name);
          346  +
          347  +    /* if not at end of file, warn */
          348  +    if (gz.left || readin(&gz))
          349  +        fprintf(stderr,
          350  +            "gzappend warning: junk at end of gzip file overwritten\n");
          351  +
          352  +    /* clear last block bit */
          353  +    lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
          354  +    if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
          355  +    *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
          356  +    lseek(gz.fd, -1L, SEEK_CUR);
          357  +    if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
          358  +
          359  +    /* if window wrapped, build dictionary from window by rotating */
          360  +    if (full) {
          361  +        rotate(window, DSIZE, have);
          362  +        have = DSIZE;
          363  +    }
          364  +
          365  +    /* set up deflate stream with window, crc, total_in, and leftover bits */
          366  +    ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
          367  +    if (ret != Z_OK) bye("out of memory", "");
          368  +    deflateSetDictionary(strm, window, have);
          369  +    strm->adler = crc;
          370  +    strm->total_in = tot;
          371  +    if (left) {
          372  +        lseek(gz.fd, --end, SEEK_SET);
          373  +        if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
          374  +        deflatePrime(strm, 8 - left, *gz.buf);
          375  +    }
          376  +    lseek(gz.fd, end, SEEK_SET);
          377  +
          378  +    /* clean up and return */
          379  +    free(window);
          380  +    free(gz.buf);
          381  +    return gz.fd;
          382  +}
          383  +
          384  +/* append file "name" to gzip file gd using deflate stream strm -- if last
          385  +   is true, then finish off the deflate stream at the end */
          386  +local void gztack(char *name, int gd, z_stream *strm, int last)
          387  +{
          388  +    int fd, len, ret;
          389  +    unsigned left;
          390  +    unsigned char *in, *out;
          391  +
          392  +    /* open file to compress and append */
          393  +    fd = 0;
          394  +    if (name != NULL) {
          395  +        fd = open(name, O_RDONLY, 0);
          396  +        if (fd == -1)
          397  +            fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
          398  +                    name);
          399  +    }
          400  +
          401  +    /* allocate buffers */
          402  +    in = fd == -1 ? NULL : malloc(CHUNK);
          403  +    out = malloc(CHUNK);
          404  +    if (out == NULL) bye("out of memory", "");
          405  +
          406  +    /* compress input file and append to gzip file */
          407  +    do {
          408  +        /* get more input */
          409  +        len = fd == -1 ? 0 : read(fd, in, CHUNK);
          410  +        if (len == -1) {
          411  +            fprintf(stderr,
          412  +                    "gzappend warning: error reading %s, skipping rest ...\n",
          413  +                    name);
          414  +            len = 0;
          415  +        }
          416  +        strm->avail_in = (unsigned)len;
          417  +        strm->next_in = in;
          418  +        if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
          419  +
          420  +        /* compress and write all available output */
          421  +        do {
          422  +            strm->avail_out = CHUNK;
          423  +            strm->next_out = out;
          424  +            ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
          425  +            left = CHUNK - strm->avail_out;
          426  +            while (left) {
          427  +                len = write(gd, out + CHUNK - strm->avail_out - left, left);
          428  +                if (len == -1) bye("writing gzip file", "");
          429  +                left -= (unsigned)len;
          430  +            }
          431  +        } while (strm->avail_out == 0 && ret != Z_STREAM_END);
          432  +    } while (len != 0);
          433  +
          434  +    /* write trailer after last entry */
          435  +    if (last) {
          436  +        deflateEnd(strm);
          437  +        out[0] = (unsigned char)(strm->adler);
          438  +        out[1] = (unsigned char)(strm->adler >> 8);
          439  +        out[2] = (unsigned char)(strm->adler >> 16);
          440  +        out[3] = (unsigned char)(strm->adler >> 24);
          441  +        out[4] = (unsigned char)(strm->total_in);
          442  +        out[5] = (unsigned char)(strm->total_in >> 8);
          443  +        out[6] = (unsigned char)(strm->total_in >> 16);
          444  +        out[7] = (unsigned char)(strm->total_in >> 24);
          445  +        len = 8;
          446  +        do {
          447  +            ret = write(gd, out + 8 - len, len);
          448  +            if (ret == -1) bye("writing gzip file", "");
          449  +            len -= ret;
          450  +        } while (len);
          451  +        close(gd);
          452  +    }
          453  +
          454  +    /* clean up and return */
          455  +    free(out);
          456  +    if (in != NULL) free(in);
          457  +    if (fd > 0) close(fd);
          458  +}
          459  +
          460  +/* process the compression level option if present, scan the gzip file, and
          461  +   append the specified files, or append the data from stdin if no other file
          462  +   names are provided on the command line -- the gzip file must be writable
          463  +   and seekable */
          464  +int main(int argc, char **argv)
          465  +{
          466  +    int gd, level;
          467  +    z_stream strm;
          468  +
          469  +    /* ignore command name */
          470  +    argv++;
          471  +
          472  +    /* provide usage if no arguments */
          473  +    if (*argv == NULL) {
          474  +        printf("gzappend 1.1 (4 Nov 2003) Copyright (C) 2003 Mark Adler\n");
          475  +        printf(
          476  +            "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
          477  +        return 0;
          478  +    }
          479  +
          480  +    /* set compression level */
          481  +    level = Z_DEFAULT_COMPRESSION;
          482  +    if (argv[0][0] == '-') {
          483  +        if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
          484  +            bye("invalid compression level", "");
          485  +        level = argv[0][1] - '0';
          486  +        if (*++argv == NULL) bye("no gzip file name after options", "");
          487  +    }
          488  +
          489  +    /* prepare to append to gzip file */
          490  +    gd = gzscan(*argv++, &strm, level);
          491  +
          492  +    /* append files on command line, or from stdin if none */
          493  +    if (*argv == NULL)
          494  +        gztack(NULL, gd, &strm, 1);
          495  +    else
          496  +        do {
          497  +            gztack(*argv, gd, &strm, argv[1] == NULL);
          498  +        } while (*++argv != NULL);
          499  +    return 0;
          500  +}

Added compat/zlib/examples/gzjoin.c.

            1  +/* gzjoin -- command to join gzip files into one gzip file
            2  +
            3  +  Copyright (C) 2004 Mark Adler, all rights reserved
            4  +  version 1.0, 11 Dec 2004
            5  +
            6  +  This software is provided 'as-is', without any express or implied
            7  +  warranty.  In no event will the author be held liable for any damages
            8  +  arising from the use of this software.
            9  +
           10  +  Permission is granted to anyone to use this software for any purpose,
           11  +  including commercial applications, and to alter it and redistribute it
           12  +  freely, subject to the following restrictions:
           13  +
           14  +  1. The origin of this software must not be misrepresented; you must not
           15  +     claim that you wrote the original software. If you use this software
           16  +     in a product, an acknowledgment in the product documentation would be
           17  +     appreciated but is not required.
           18  +  2. Altered source versions must be plainly marked as such, and must not be
           19  +     misrepresented as being the original software.
           20  +  3. This notice may not be removed or altered from any source distribution.
           21  +
           22  +  Mark Adler    madler@alumni.caltech.edu
           23  + */
           24  +
           25  +/*
           26  + * Change history:
           27  + *
           28  + * 1.0  11 Dec 2004     - First version
           29  + * 1.1  12 Jun 2005     - Changed ssize_t to long for portability
           30  + */
           31  +
           32  +/*
           33  +   gzjoin takes one or more gzip files on the command line and writes out a
           34  +   single gzip file that will uncompress to the concatenation of the
           35  +   uncompressed data from the individual gzip files.  gzjoin does this without
           36  +   having to recompress any of the data and without having to calculate a new
           37  +   crc32 for the concatenated uncompressed data.  gzjoin does however have to
           38  +   decompress all of the input data in order to find the bits in the compressed
           39  +   data that need to be modified to concatenate the streams.
           40  +
           41  +   gzjoin does not do an integrity check on the input gzip files other than
           42  +   checking the gzip header and decompressing the compressed data.  They are
           43  +   otherwise assumed to be complete and correct.
           44  +
           45  +   Each joint between gzip files removes at least 18 bytes of previous trailer
           46  +   and subsequent header, and inserts an average of about three bytes to the
           47  +   compressed data in order to connect the streams.  The output gzip file
           48  +   has a minimal ten-byte gzip header with no file name or modification time.
           49  +
           50  +   This program was written to illustrate the use of the Z_BLOCK option of
           51  +   inflate() and the crc32_combine() function.  gzjoin will not compile with
           52  +   versions of zlib earlier than 1.2.3.
           53  + */
           54  +
           55  +#include <stdio.h>      /* fputs(), fprintf(), fwrite(), putc() */
           56  +#include <stdlib.h>     /* exit(), malloc(), free() */
           57  +#include <fcntl.h>      /* open() */
           58  +#include <unistd.h>     /* close(), read(), lseek() */
           59  +#include "zlib.h"
           60  +    /* crc32(), crc32_combine(), inflateInit2(), inflate(), inflateEnd() */
           61  +
           62  +#define local static
           63  +
           64  +/* exit with an error (return a value to allow use in an expression) */
           65  +local int bail(char *why1, char *why2)
           66  +{
           67  +    fprintf(stderr, "gzjoin error: %s%s, output incomplete\n", why1, why2);
           68  +    exit(1);
           69  +    return 0;
           70  +}
           71  +
           72  +/* -- simple buffered file input with access to the buffer -- */
           73  +
           74  +#define CHUNK 32768         /* must be a power of two and fit in unsigned */
           75  +
           76  +/* bin buffered input file type */
           77  +typedef struct {
           78  +    char *name;             /* name of file for error messages */
           79  +    int fd;                 /* file descriptor */
           80  +    unsigned left;          /* bytes remaining at next */
           81  +    unsigned char *next;    /* next byte to read */
           82  +    unsigned char *buf;     /* allocated buffer of length CHUNK */
           83  +} bin;
           84  +
           85  +/* close a buffered file and free allocated memory */
           86  +local void bclose(bin *in)
           87  +{
           88  +    if (in != NULL) {
           89  +        if (in->fd != -1)
           90  +            close(in->fd);
           91  +        if (in->buf != NULL)
           92  +            free(in->buf);
           93  +        free(in);
           94  +    }
           95  +}
           96  +
           97  +/* open a buffered file for input, return a pointer to type bin, or NULL on
           98  +   failure */
           99  +local bin *bopen(char *name)
          100  +{
          101  +    bin *in;
          102  +
          103  +    in = malloc(sizeof(bin));
          104  +    if (in == NULL)
          105  +        return NULL;
          106  +    in->buf = malloc(CHUNK);
          107  +    in->fd = open(name, O_RDONLY, 0);
          108  +    if (in->buf == NULL || in->fd == -1) {
          109  +        bclose(in);
          110  +        return NULL;
          111  +    }
          112  +    in->left = 0;
          113  +    in->next = in->buf;
          114  +    in->name = name;
          115  +    return in;
          116  +}
          117  +
          118  +/* load buffer from file, return -1 on read error, 0 or 1 on success, with
          119  +   1 indicating that end-of-file was reached */
          120  +local int bload(bin *in)
          121  +{
          122  +    long len;
          123  +
          124  +    if (in == NULL)
          125  +        return -1;
          126  +    if (in->left != 0)
          127  +        return 0;
          128  +    in->next = in->buf;
          129  +    do {
          130  +        len = (long)read(in->fd, in->buf + in->left, CHUNK - in->left);
          131  +        if (len < 0)
          132  +            return -1;
          133  +        in->left += (unsigned)len;
          134  +    } while (len != 0 && in->left < CHUNK);
          135  +    return len == 0 ? 1 : 0;
          136  +}
          137  +
          138  +/* get a byte from the file, bail if end of file */
          139  +#define bget(in) (in->left ? 0 : bload(in), \
          140  +                  in->left ? (in->left--, *(in->next)++) : \
          141  +                    bail("unexpected end of file on ", in->name))
          142  +
          143  +/* get a four-byte little-endian unsigned integer from file */
          144  +local unsigned long bget4(bin *in)
          145  +{
          146  +    unsigned long val;
          147  +
          148  +    val = bget(in);
          149  +    val += (unsigned long)(bget(in)) << 8;
          150  +    val += (unsigned long)(bget(in)) << 16;
          151  +    val += (unsigned long)(bget(in)) << 24;
          152  +    return val;
          153  +}
          154  +
          155  +/* skip bytes in file */
          156  +local void bskip(bin *in, unsigned skip)
          157  +{
          158  +    /* check pointer */
          159  +    if (in == NULL)
          160  +        return;
          161  +
          162  +    /* easy case -- skip bytes in buffer */
          163  +    if (skip <= in->left) {
          164  +        in->left -= skip;
          165  +        in->next += skip;
          166  +        return;
          167  +    }
          168  +
          169  +    /* skip what's in buffer, discard buffer contents */
          170  +    skip -= in->left;
          171  +    in->left = 0;
          172  +
          173  +    /* seek past multiples of CHUNK bytes */
          174  +    if (skip > CHUNK) {
          175  +        unsigned left;
          176  +
          177  +        left = skip & (CHUNK - 1);
          178  +        if (left == 0) {
          179  +            /* exact number of chunks: seek all the way minus one byte to check
          180  +               for end-of-file with a read */
          181  +            lseek(in->fd, skip - 1, SEEK_CUR);
          182  +            if (read(in->fd, in->buf, 1) != 1)
          183  +                bail("unexpected end of file on ", in->name);
          184  +            return;
          185  +        }
          186  +
          187  +        /* skip the integral chunks, update skip with remainder */
          188  +        lseek(in->fd, skip - left, SEEK_CUR);
          189  +        skip = left;
          190  +    }
          191  +
          192  +    /* read more input and skip remainder */
          193  +    bload(in);
          194  +    if (skip > in->left)
          195  +        bail("unexpected end of file on ", in->name);
          196  +    in->left -= skip;
          197  +    in->next += skip;
          198  +}
          199  +
          200  +/* -- end of buffered input functions -- */
          201  +
          202  +/* skip the gzip header from file in */
          203  +local void gzhead(bin *in)
          204  +{
          205  +    int flags;
          206  +
          207  +    /* verify gzip magic header and compression method */
          208  +    if (bget(in) != 0x1f || bget(in) != 0x8b || bget(in) != 8)
          209  +        bail(in->name, " is not a valid gzip file");
          210  +
          211  +    /* get and verify flags */
          212  +    flags = bget(in);
          213  +    if ((flags & 0xe0) != 0)
          214  +        bail("unknown reserved bits set in ", in->name);
          215  +
          216  +    /* skip modification time, extra flags, and os */
          217  +    bskip(in, 6);
          218  +
          219  +    /* skip extra field if present */
          220  +    if (flags & 4) {
          221  +        unsigned len;
          222  +
          223  +        len = bget(in);
          224  +        len += (unsigned)(bget(in)) << 8;
          225  +        bskip(in, len);
          226  +    }
          227  +
          228  +    /* skip file name if present */
          229  +    if (flags & 8)
          230  +        while (bget(in) != 0)
          231  +            ;
          232  +
          233  +    /* skip comment if present */
          234  +    if (flags & 16)
          235  +        while (bget(in) != 0)
          236  +            ;
          237  +
          238  +    /* skip header crc if present */
          239  +    if (flags & 2)
          240  +        bskip(in, 2);
          241  +}
          242  +
          243  +/* write a four-byte little-endian unsigned integer to out */
          244  +local void put4(unsigned long val, FILE *out)
          245  +{
          246  +    putc(val & 0xff, out);
          247  +    putc((val >> 8) & 0xff, out);
          248  +    putc((val >> 16) & 0xff, out);
          249  +    putc((val >> 24) & 0xff, out);
          250  +}
          251  +
          252  +/* Load up zlib stream from buffered input, bail if end of file */
          253  +local void zpull(z_streamp strm, bin *in)
          254  +{
          255  +    if (in->left == 0)
          256  +        bload(in);
          257  +    if (in->left == 0)
          258  +        bail("unexpected end of file on ", in->name);
          259  +    strm->avail_in = in->left;
          260  +    strm->next_in = in->next;
          261  +}
          262  +
          263  +/* Write header for gzip file to out and initialize trailer. */
          264  +local void gzinit(unsigned long *crc, unsigned long *tot, FILE *out)
          265  +{
          266  +    fwrite("\x1f\x8b\x08\0\0\0\0\0\0\xff", 1, 10, out);
          267  +    *crc = crc32(0L, Z_NULL, 0);
          268  +    *tot = 0;
          269  +}
          270  +
          271  +/* Copy the compressed data from name, zeroing the last block bit of the last
          272  +   block if clr is true, and adding empty blocks as needed to get to a byte
          273  +   boundary.  If clr is false, then the last block becomes the last block of
          274  +   the output, and the gzip trailer is written.  crc and tot maintains the
          275  +   crc and length (modulo 2^32) of the output for the trailer.  The resulting
          276  +   gzip file is written to out.  gzinit() must be called before the first call
          277  +   of gzcopy() to write the gzip header and to initialize crc and tot. */
          278  +local void gzcopy(char *name, int clr, unsigned long *crc, unsigned long *tot,
          279  +                  FILE *out)
          280  +{
          281  +    int ret;                /* return value from zlib functions */
          282  +    int pos;                /* where the "last block" bit is in byte */
          283  +    int last;               /* true if processing the last block */
          284  +    bin *in;                /* buffered input file */
          285  +    unsigned char *start;   /* start of compressed data in buffer */
          286  +    unsigned char *junk;    /* buffer for uncompressed data -- discarded */
          287  +    z_off_t len;            /* length of uncompressed data (support > 4 GB) */
          288  +    z_stream strm;          /* zlib inflate stream */
          289  +
          290  +    /* open gzip file and skip header */
          291  +    in = bopen(name);
          292  +    if (in == NULL)
          293  +        bail("could not open ", name);
          294  +    gzhead(in);
          295  +
          296  +    /* allocate buffer for uncompressed data and initialize raw inflate
          297  +       stream */
          298  +    junk = malloc(CHUNK);
          299  +    strm.zalloc = Z_NULL;
          300  +    strm.zfree = Z_NULL;
          301  +    strm.opaque = Z_NULL;
          302  +    strm.avail_in = 0;
          303  +    strm.next_in = Z_NULL;
          304  +    ret = inflateInit2(&strm, -15);
          305  +    if (junk == NULL || ret != Z_OK)
          306  +        bail("out of memory", "");
          307  +
          308  +    /* inflate and copy compressed data, clear last-block bit if requested */
          309  +    len = 0;
          310  +    zpull(&strm, in);
          311  +    start = strm.next_in;
          312  +    last = start[0] & 1;
          313  +    if (last && clr)
          314  +        start[0] &= ~1;
          315  +    strm.avail_out = 0;
          316  +    for (;;) {
          317  +        /* if input used and output done, write used input and get more */
          318  +        if (strm.avail_in == 0 && strm.avail_out != 0) {
          319  +            fwrite(start, 1, strm.next_in - start, out);
          320  +            start = in->buf;
          321  +            in->left = 0;
          322  +            zpull(&strm, in);
          323  +        }
          324  +
          325  +        /* decompress -- return early when end-of-block reached */
          326  +        strm.avail_out = CHUNK;
          327  +        strm.next_out = junk;
          328  +        ret = inflate(&strm, Z_BLOCK);
          329  +        switch (ret) {
          330  +        case Z_MEM_ERROR:
          331  +            bail("out of memory", "");
          332  +        case Z_DATA_ERROR:
          333  +            bail("invalid compressed data in ", in->name);
          334  +        }
          335  +
          336  +        /* update length of uncompressed data */
          337  +        len += CHUNK - strm.avail_out;
          338  +
          339  +        /* check for block boundary (only get this when block copied out) */
          340  +        if (strm.data_type & 128) {
          341  +            /* if that was the last block, then done */
          342  +            if (last)
          343  +                break;
          344  +
          345  +            /* number of unused bits in last byte */
          346  +            pos = strm.data_type & 7;
          347  +
          348  +            /* find the next last-block bit */
          349  +            if (pos != 0) {
          350  +                /* next last-block bit is in last used byte */
          351  +                pos = 0x100 >> pos;
          352  +                last = strm.next_in[-1] & pos;
          353  +                if (last && clr)
          354  +                    strm.next_in[-1] &= ~pos;
          355  +            }
          356  +            else {
          357  +                /* next last-block bit is in next unused byte */
          358  +                if (strm.avail_in == 0) {
          359  +                    /* don't have that byte yet -- get it */
          360  +                    fwrite(start, 1, strm.next_in - start, out);
          361  +                    start = in->buf;
          362  +                    in->left = 0;
          363  +                    zpull(&strm, in);
          364  +                }
          365  +                last = strm.next_in[0] & 1;
          366  +                if (last && clr)
          367  +                    strm.next_in[0] &= ~1;
          368  +            }
          369  +        }
          370  +    }
          371  +
          372  +    /* update buffer with unused input */
          373  +    in->left = strm.avail_in;
          374  +    in->next = strm.next_in;
          375  +
          376  +    /* copy used input, write empty blocks to get to byte boundary */
          377  +    pos = strm.data_type & 7;
          378  +    fwrite(start, 1, in->next - start - 1, out);
          379  +    last = in->next[-1];
          380  +    if (pos == 0 || !clr)
          381  +        /* already at byte boundary, or last file: write last byte */
          382  +        putc(last, out);
          383  +    else {
          384  +        /* append empty blocks to last byte */
          385  +        last &= ((0x100 >> pos) - 1);       /* assure unused bits are zero */
          386  +        if (pos & 1) {
          387  +            /* odd -- append an empty stored block */
          388  +            putc(last, out);
          389  +            if (pos == 1)
          390  +                putc(0, out);               /* two more bits in block header */
          391  +            fwrite("\0\0\xff\xff", 1, 4, out);
          392  +        }
          393  +        else {
          394  +            /* even -- append 1, 2, or 3 empty fixed blocks */
          395  +            switch (pos) {
          396  +            case 6:
          397  +                putc(last | 8, out);
          398  +                last = 0;
          399  +            case 4:
          400  +                putc(last | 0x20, out);
          401  +                last = 0;
          402  +            case 2:
          403  +                putc(last | 0x80, out);
          404  +                putc(0, out);
          405  +            }
          406  +        }
          407  +    }
          408  +
          409  +    /* update crc and tot */
          410  +    *crc = crc32_combine(*crc, bget4(in), len);
          411  +    *tot += (unsigned long)len;
          412  +
          413  +    /* clean up */
          414  +    inflateEnd(&strm);
          415  +    free(junk);
          416  +    bclose(in);
          417  +
          418  +    /* write trailer if this is the last gzip file */
          419  +    if (!clr) {
          420  +        put4(*crc, out);
          421  +        put4(*tot, out);
          422  +    }
          423  +}
          424  +
          425  +/* join the gzip files on the command line, write result to stdout */
          426  +int main(int argc, char **argv)
          427  +{
          428  +    unsigned long crc, tot;     /* running crc and total uncompressed length */
          429  +
          430  +    /* skip command name */
          431  +    argc--;
          432  +    argv++;
          433  +
          434  +    /* show usage if no arguments */
          435  +    if (argc == 0) {
          436  +        fputs("gzjoin usage: gzjoin f1.gz [f2.gz [f3.gz ...]] > fjoin.gz\n",
          437  +              stderr);
          438  +        return 0;
          439  +    }
          440  +
          441  +    /* join gzip files on command line and write to stdout */
          442  +    gzinit(&crc, &tot, stdout);
          443  +    while (argc--)
          444  +        gzcopy(*argv++, argc, &crc, &tot, stdout);
          445  +
          446  +    /* done */
          447  +    return 0;
          448  +}

Added compat/zlib/examples/gzlog.c.

            1  +/*
            2  + * gzlog.c
            3  + * Copyright (C) 2004, 2008 Mark Adler, all rights reserved
            4  + * For conditions of distribution and use, see copyright notice in gzlog.h
            5  + * version 2.0, 25 Apr 2008
            6  + */
            7  +
            8  +/*
            9  +   gzlog provides a mechanism for frequently appending short strings to a gzip
           10  +   file that is efficient both in execution time and compression ratio.  The
           11  +   strategy is to write the short strings in an uncompressed form to the end of
           12  +   the gzip file, only compressing when the amount of uncompressed data has
           13  +   reached a given threshold.
           14  +
           15  +   gzlog also provides protection against interruptions in the process due to
           16  +   system crashes.  The status of the operation is recorded in an extra field
           17  +   in the gzip file, and is only updated once the gzip file is brought to a
           18  +   valid state.  The last data to be appended or compressed is saved in an
           19  +   auxiliary file, so that if the operation is interrupted, it can be completed
           20  +   the next time an append operation is attempted.
           21  +
           22  +   gzlog maintains another auxiliary file with the last 32K of data from the
           23  +   compressed portion, which is preloaded for the compression of the subsequent
           24  +   data.  This minimizes the impact to the compression ratio of appending.
           25  + */
           26  +
           27  +/*
           28  +   Operations Concept:
           29  +
           30  +   Files (log name "foo"):
           31  +   foo.gz -- gzip file with the complete log
           32  +   foo.add -- last message to append or last data to compress
           33  +   foo.dict -- dictionary of the last 32K of data for next compression
           34  +   foo.temp -- temporary dictionary file for compression after this one
           35  +   foo.lock -- lock file for reading and writing the other files
           36  +   foo.repairs -- log file for log file recovery operations (not compressed)
           37  +
           38  +   gzip file structure:
           39  +   - fixed-length (no file name) header with extra field (see below)
           40  +   - compressed data ending initially with empty stored block
           41  +   - uncompressed data filling out originally empty stored block and
           42  +     subsequent stored blocks as needed (16K max each)
           43  +   - gzip trailer
           44  +   - no junk at end (no other gzip streams)
           45  +
           46  +   When appending data, the information in the first three items above plus the
           47  +   foo.add file are sufficient to recover an interrupted append operation.  The
           48  +   extra field has the necessary information to restore the start of the last
           49  +   stored block and determine where to append the data in the foo.add file, as
           50  +   well as the crc and length of the gzip data before the append operation.
           51  +
           52  +   The foo.add file is created before the gzip file is marked for append, and
           53  +   deleted after the gzip file is marked as complete.  So if the append
           54  +   operation is interrupted, the data to add will still be there.  If due to
           55  +   some external force, the foo.add file gets deleted between when the append
           56  +   operation was interrupted and when recovery is attempted, the gzip file will
           57  +   still be restored, but without the appended data.
           58  +
           59  +   When compressing data, the information in the first two items above plus the
           60  +   foo.add file are sufficient to recover an interrupted compress operation.
           61  +   The extra field has the necessary information to find the end of the
           62  +   compressed data, and contains both the crc and length of just the compressed
           63  +   data and of the complete set of data including the contents of the foo.add
           64  +   file.
           65  +
           66  +   Again, the foo.add file is maintained during the compress operation in case
           67  +   of an interruption.  If in the unlikely event the foo.add file with the data
           68  +   to be compressed is missing due to some external force, a gzip file with
           69  +   just the previous compressed data will be reconstructed.  In this case, all
           70  +   of the data that was to be compressed is lost (approximately one megabyte).
           71  +   This will not occur if all that happened was an interruption of the compress
           72  +   operation.
           73  +
           74  +   The third state that is marked is the replacement of the old dictionary with
           75  +   the new dictionary after a compress operation.  Once compression is
           76  +   complete, the gzip file is marked as being in the replace state.  This
           77  +   completes the gzip file, so an interrupt after being so marked does not
           78  +   result in recompression.  Then the dictionary file is replaced, and the gzip
           79  +   file is marked as completed.  This state prevents the possibility of
           80  +   restarting compression with the wrong dictionary file.
           81  +
           82  +   All three operations are wrapped by a lock/unlock procedure.  In order to
           83  +   gain exclusive access to the log files, first a foo.lock file must be
           84  +   exclusively created.  When all operations are complete, the lock is
           85  +   released by deleting the foo.lock file.  If when attempting to create the
           86  +   lock file, it already exists and the modify time of the lock file is more
           87  +   than five minutes old (set by the PATIENCE define below), then the old
           88  +   lock file is considered stale and deleted, and the exclusive creation of
           89  +   the lock file is retried.  To assure that there are no false assessments
           90  +   of the staleness of the lock file, the operations periodically touch the
           91  +   lock file to update the modified date.
           92  +
           93  +   Following is the definition of the extra field with all of the information
           94  +   required to enable the above append and compress operations and their
           95  +   recovery if interrupted.  Multi-byte values are stored little endian
           96  +   (consistent with the gzip format).  File pointers are eight bytes long.
           97  +   The crc's and lengths for the gzip trailer are four bytes long.  (Note that
           98  +   the length at the end of a gzip file is used for error checking only, and
           99  +   for large files is actually the length modulo 2^32.)  The stored block
          100  +   length is two bytes long.  The gzip extra field two-byte identification is
          101  +   "ap" for append.  It is assumed that writing the extra field to the file is
          102  +   an "atomic" operation.  That is, either all of the extra field is written
          103  +   to the file, or none of it is, if the operation is interrupted right at the
          104  +   point of updating the extra field.  This is a reasonable assumption, since
          105  +   the extra field is within the first 52 bytes of the file, which is smaller
          106  +   than any expected block size for a mass storage device (usually 512 bytes or
          107  +   larger).
          108  +
          109  +   Extra field (35 bytes):
          110  +   - Pointer to first stored block length -- this points to the two-byte length
          111  +     of the first stored block, which is followed by the two-byte, one's
          112  +     complement of that length.  The stored block length is preceded by the
          113  +     three-bit header of the stored block, which is the actual start of the
          114  +     stored block in the deflate format.  See the bit offset field below.
          115  +   - Pointer to the last stored block length.  This is the same as above, but
          116  +     for the last stored block of the uncompressed data in the gzip file.
          117  +     Initially this is the same as the first stored block length pointer.
          118  +     When the stored block gets to 16K (see the MAX_STORE define), then a new
          119  +     stored block as added, at which point the last stored block length pointer
          120  +     is different from the first stored block length pointer.  When they are
          121  +     different, the first bit of the last stored block header is eight bits, or
          122  +     one byte back from the block length.
          123  +   - Compressed data crc and length.  This is the crc and length of the data
          124  +     that is in the compressed portion of the deflate stream.  These are used
          125  +     only in the event that the foo.add file containing the data to compress is
          126  +     lost after a compress operation is interrupted.
          127  +   - Total data crc and length.  This is the crc and length of all of the data
          128  +     stored in the gzip file, compressed and uncompressed.  It is used to
          129  +     reconstruct the gzip trailer when compressing, as well as when recovering
          130  +     interrupted operations.
          131  +   - Final stored block length.  This is used to quickly find where to append,
          132  +     and allows the restoration of the original final stored block state when
          133  +     an append operation is interrupted.
          134  +   - First stored block start as the number of bits back from the final stored
          135  +     block first length byte.  This value is in the range of 3..10, and is
          136  +     stored as the low three bits of the final byte of the extra field after
          137  +     subtracting three (0..7).  This allows the last-block bit of the stored
          138  +     block header to be updated when a new stored block is added, for the case
          139  +     when the first stored block and the last stored block are the same.  (When
          140  +     they are different, the numbers of bits back is known to be eight.)  This
          141  +     also allows for new compressed data to be appended to the old compressed
          142  +     data in the compress operation, overwriting the previous first stored
          143  +     block, or for the compressed data to be terminated and a valid gzip file
          144  +     reconstructed on the off chance that a compression operation was
          145  +     interrupted and the data to compress in the foo.add file was deleted.
          146  +   - The operation in process.  This is the next two bits in the last byte (the
          147  +     bits under the mask 0x18).  The are interpreted as 0: nothing in process,
          148  +     1: append in process, 2: compress in process, 3: replace in process.
          149  +   - The top three bits of the last byte in the extra field are reserved and
          150  +     are currently set to zero.
          151  +
          152  +   Main procedure:
          153  +   - Exclusively create the foo.lock file using the O_CREAT and O_EXCL modes of
          154  +     the system open() call.  If the modify time of an existing lock file is
          155  +     more than PATIENCE seconds old, then the lock file is deleted and the
          156  +     exclusive create is retried.
          157  +   - Load the extra field from the foo.gz file, and see if an operation was in
          158  +     progress but not completed.  If so, apply the recovery procedure below.
          159  +   - Perform the append procedure with the provided data.
          160  +   - If the uncompressed data in the foo.gz file is 1MB or more, apply the
          161  +     compress procedure.
          162  +   - Delete the foo.lock file.
          163  +
          164  +   Append procedure:
          165  +   - Put what to append in the foo.add file so that the operation can be
          166  +     restarted if this procedure is interrupted.
          167  +   - Mark the foo.gz extra field with the append operation in progress.
          168  +   + Restore the original last-block bit and stored block length of the last
          169  +     stored block from the information in the extra field, in case a previous
          170  +     append operation was interrupted.
          171  +   - Append the provided data to the last stored block, creating new stored
          172  +     blocks as needed and updating the stored blocks last-block bits and
          173  +     lengths.
          174  +   - Update the crc and length with the new data, and write the gzip trailer.
          175  +   - Write over the extra field (with a single write operation) with the new
          176  +     pointers, lengths, and crc's, and mark the gzip file as not in process.
          177  +     Though there is still a foo.add file, it will be ignored since nothing
          178  +     is in process.  If a foo.add file is leftover from a previously
          179  +     completed operation, it is truncated when writing new data to it.
          180  +   - Delete the foo.add file.
          181  +
          182  +   Compress and replace procedures:
          183  +   - Read all of the uncompressed data in the stored blocks in foo.gz and write
          184  +     it to foo.add.  Also write foo.temp with the last 32K of that data to
          185  +     provide a dictionary for the next invocation of this procedure.
          186  +   - Rewrite the extra field marking foo.gz with a compression in process.
          187  +   * If there is no data provided to compress (due to a missing foo.add file
          188  +     when recovering), reconstruct and truncate the foo.gz file to contain
          189  +     only the previous compressed data and proceed to the step after the next
          190  +     one.  Otherwise ...
          191  +   - Compress the data with the dictionary in foo.dict, and write to the
          192  +     foo.gz file starting at the bit immediately following the last previously
          193  +     compressed block.  If there is no foo.dict, proceed anyway with the
          194  +     compression at slightly reduced efficiency.  (For the foo.dict file to be
          195  +     missing requires some external failure beyond simply the interruption of
          196  +     a compress operation.)  During this process, the foo.lock file is
          197  +     periodically touched to assure that that file is not considered stale by
          198  +     another process before we're done.  The deflation is terminated with a
          199  +     non-last empty static block (10 bits long), that is then located and
          200  +     written over by a last-bit-set empty stored block.
          201  +   - Append the crc and length of the data in the gzip file (previously
          202  +     calculated during the append operations).
          203  +   - Write over the extra field with the updated stored block offsets, bits
          204  +     back, crc's, and lengths, and mark foo.gz as in process for a replacement
          205  +     of the dictionary.
          206  +   @ Delete the foo.add file.
          207  +   - Replace foo.dict with foo.temp.
          208  +   - Write over the extra field, marking foo.gz as complete.
          209  +
          210  +   Recovery procedure:
          211  +   - If not a replace recovery, read in the foo.add file, and provide that data
          212  +     to the appropriate recovery below.  If there is no foo.add file, provide
          213  +     a zero data length to the recovery.  In that case, the append recovery
          214  +     restores the foo.gz to the previous compressed + uncompressed data state.
          215  +     For the the compress recovery, a missing foo.add file results in foo.gz
          216  +     being restored to the previous compressed-only data state.
          217  +   - Append recovery:
          218  +     - Pick up append at + step above
          219  +   - Compress recovery:
          220  +     - Pick up compress at * step above
          221  +   - Replace recovery:
          222  +     - Pick up compress at @ step above
          223  +   - Log the repair with a date stamp in foo.repairs
          224  + */
          225  +
          226  +#include <sys/types.h>
          227  +#include <stdio.h>      /* rename, fopen, fprintf, fclose */
          228  +#include <stdlib.h>     /* malloc, free */
          229  +#include <string.h>     /* strlen, strrchr, strcpy, strncpy, strcmp */
          230  +#include <fcntl.h>      /* open */
          231  +#include <unistd.h>     /* lseek, read, write, close, unlink, sleep, */
          232  +                        /* ftruncate, fsync */
          233  +#include <errno.h>      /* errno */
          234  +#include <time.h>       /* time, ctime */
          235  +#include <sys/stat.h>   /* stat */
          236  +#include <sys/time.h>   /* utimes */
          237  +#include "zlib.h"       /* crc32 */
          238  +
          239  +#include "gzlog.h"      /* header for external access */
          240  +
          241  +#define local static
          242  +typedef unsigned int uint;
          243  +typedef unsigned long ulong;
          244  +
          245  +/* Macro for debugging to deterministically force recovery operations */
          246  +#ifdef DEBUG
          247  +    #include <setjmp.h>         /* longjmp */
          248  +    jmp_buf gzlog_jump;         /* where to go back to */
          249  +    int gzlog_bail = 0;         /* which point to bail at (1..8) */
          250  +    int gzlog_count = -1;       /* number of times through to wait */
          251  +#   define BAIL(n) do { if (n == gzlog_bail && gzlog_count-- == 0) \
          252  +                            longjmp(gzlog_jump, gzlog_bail); } while (0)
          253  +#else
          254  +#   define BAIL(n)
          255  +#endif
          256  +
          257  +/* how old the lock file can be in seconds before considering it stale */
          258  +#define PATIENCE 300
          259  +
          260  +/* maximum stored block size in Kbytes -- must be in 1..63 */
          261  +#define MAX_STORE 16
          262  +
          263  +/* number of stored Kbytes to trigger compression (must be >= 32 to allow
          264  +   dictionary construction, and <= 204 * MAX_STORE, in order for >> 10 to
          265  +   discard the stored block headers contribution of five bytes each) */
          266  +#define TRIGGER 1024
          267  +
          268  +/* size of a deflate dictionary (this cannot be changed) */
          269  +#define DICT 32768U
          270  +
          271  +/* values for the operation (2 bits) */
          272  +#define NO_OP 0
          273  +#define APPEND_OP 1
          274  +#define COMPRESS_OP 2
          275  +#define REPLACE_OP 3
          276  +
          277  +/* macros to extract little-endian integers from an unsigned byte buffer */
          278  +#define PULL2(p) ((p)[0]+((uint)((p)[1])<<8))
          279  +#define PULL4(p) (PULL2(p)+((ulong)PULL2(p+2)<<16))
          280  +#define PULL8(p) (PULL4(p)+((off_t)PULL4(p+4)<<32))
          281  +
          282  +/* macros to store integers into a byte buffer in little-endian order */
          283  +#define PUT2(p,a) do {(p)[0]=a;(p)[1]=(a)>>8;} while(0)
          284  +#define PUT4(p,a) do {PUT2(p,a);PUT2(p+2,a>>16);} while(0)
          285  +#define PUT8(p,a) do {PUT4(p,a);PUT4(p+4,a>>32);} while(0)
          286  +
          287  +/* internal structure for log information */
          288  +#define LOGID "\106\035\172"    /* should be three non-zero characters */
          289  +struct log {
          290  +    char id[4];     /* contains LOGID to detect inadvertent overwrites */
          291  +    int fd;         /* file descriptor for .gz file, opened read/write */
          292  +    char *path;     /* allocated path, e.g. "/var/log/foo" or "foo" */
          293  +    char *end;      /* end of path, for appending suffices such as ".gz" */
          294  +    off_t first;    /* offset of first stored block first length byte */
          295  +    int back;       /* location of first block id in bits back from first */
          296  +    uint stored;    /* bytes currently in last stored block */
          297  +    off_t last;     /* offset of last stored block first length byte */
          298  +    ulong ccrc;     /* crc of compressed data */
          299  +    ulong clen;     /* length (modulo 2^32) of compressed data */
          300  +    ulong tcrc;     /* crc of total data */
          301  +    ulong tlen;     /* length (modulo 2^32) of total data */
          302  +    time_t lock;    /* last modify time of our lock file */
          303  +};
          304  +
          305  +/* gzip header for gzlog */
          306  +local unsigned char log_gzhead[] = {
          307  +    0x1f, 0x8b,                 /* magic gzip id */
          308  +    8,                          /* compression method is deflate */
          309  +    4,                          /* there is an extra field (no file name) */
          310  +    0, 0, 0, 0,                 /* no modification time provided */
          311  +    0, 0xff,                    /* no extra flags, no OS specified */
          312  +    39, 0, 'a', 'p', 35, 0      /* extra field with "ap" subfield */
          313  +                                /* 35 is EXTRA, 39 is EXTRA + 4 */
          314  +};
          315  +
          316  +#define HEAD sizeof(log_gzhead)     /* should be 16 */
          317  +
          318  +/* initial gzip extra field content (52 == HEAD + EXTRA + 1) */
          319  +local unsigned char log_gzext[] = {
          320  +    52, 0, 0, 0, 0, 0, 0, 0,    /* offset of first stored block length */
          321  +    52, 0, 0, 0, 0, 0, 0, 0,    /* offset of last stored block length */
          322  +    0, 0, 0, 0, 0, 0, 0, 0,     /* compressed data crc and length */
          323  +    0, 0, 0, 0, 0, 0, 0, 0,     /* total data crc and length */
          324  +    0, 0,                       /* final stored block data length */
          325  +    5                           /* op is NO_OP, last bit 8 bits back */
          326  +};
          327  +
          328  +#define EXTRA sizeof(log_gzext)     /* should be 35 */
          329  +
          330  +/* initial gzip data and trailer */
          331  +local unsigned char log_gzbody[] = {
          332  +    1, 0, 0, 0xff, 0xff,        /* empty stored block (last) */
          333  +    0, 0, 0, 0,                 /* crc */
          334  +    0, 0, 0, 0                  /* uncompressed length */
          335  +};
          336  +
          337  +#define BODY sizeof(log_gzbody)
          338  +
          339  +/* Exclusively create foo.lock in order to negotiate exclusive access to the
          340  +   foo.* files.  If the modify time of an existing lock file is greater than
          341  +   PATIENCE seconds in the past, then consider the lock file to have been
          342  +   abandoned, delete it, and try the exclusive create again.  Save the lock
          343  +   file modify time for verification of ownership.  Return 0 on success, or -1
          344  +   on failure, usually due to an access restriction or invalid path.  Note that
          345  +   if stat() or unlink() fails, it may be due to another process noticing the
          346  +   abandoned lock file a smidge sooner and deleting it, so those are not
          347  +   flagged as an error. */
          348  +local int log_lock(struct log *log)
          349  +{
          350  +    int fd;
          351  +    struct stat st;
          352  +
          353  +    strcpy(log->end, ".lock");
          354  +    while ((fd = open(log->path, O_CREAT | O_EXCL, 0644)) < 0) {
          355  +        if (errno != EEXIST)
          356  +            return -1;
          357  +        if (stat(log->path, &st) == 0 && time(NULL) - st.st_mtime > PATIENCE) {
          358  +            unlink(log->path);
          359  +            continue;
          360  +        }
          361  +        sleep(2);       /* relinquish the CPU for two seconds while waiting */
          362  +    }
          363  +    close(fd);
          364  +    if (stat(log->path, &st) == 0)
          365  +        log->lock = st.st_mtime;
          366  +    return 0;
          367  +}
          368  +
          369  +/* Update the modify time of the lock file to now, in order to prevent another
          370  +   task from thinking that the lock is stale.  Save the lock file modify time
          371  +   for verification of ownership. */
          372  +local void log_touch(struct log *log)
          373  +{
          374  +    struct stat st;
          375  +
          376  +    strcpy(log->end, ".lock");
          377  +    utimes(log->path, NULL);
          378  +    if (stat(log->path, &st) == 0)
          379  +        log->lock = st.st_mtime;
          380  +}
          381  +
          382  +/* Check the log file modify time against what is expected.  Return true if
          383  +   this is not our lock.  If it is our lock, touch it to keep it. */
          384  +local int log_check(struct log *log)
          385  +{
          386  +    struct stat st;
          387  +
          388  +    strcpy(log->end, ".lock");
          389  +    if (stat(log->path, &st) || st.st_mtime != log->lock)
          390  +        return 1;
          391  +    log_touch(log);
          392  +    return 0;
          393  +}
          394  +
          395  +/* Unlock a previously acquired lock, but only if it's ours. */
          396  +local void log_unlock(struct log *log)
          397  +{
          398  +    if (log_check(log))
          399  +        return;
          400  +    strcpy(log->end, ".lock");
          401  +    unlink(log->path);
          402  +    log->lock = 0;
          403  +}
          404  +
          405  +/* Check the gzip header and read in the extra field, filling in the values in
          406  +   the log structure.  Return op on success or -1 if the gzip header was not as
          407  +   expected.  op is the current operation in progress last written to the extra
          408  +   field.  This assumes that the gzip file has already been opened, with the
          409  +   file descriptor log->fd. */
          410  +local int log_head(struct log *log)
          411  +{
          412  +    int op;
          413  +    unsigned char buf[HEAD + EXTRA];
          414  +
          415  +    if (lseek(log->fd, 0, SEEK_SET) < 0 ||
          416  +        read(log->fd, buf, HEAD + EXTRA) != HEAD + EXTRA ||
          417  +        memcmp(buf, log_gzhead, HEAD)) {
          418  +        return -1;
          419  +    }
          420  +    log->first = PULL8(buf + HEAD);
          421  +    log->last = PULL8(buf + HEAD + 8);
          422  +    log->ccrc = PULL4(buf + HEAD + 16);
          423  +    log->clen = PULL4(buf + HEAD + 20);
          424  +    log->tcrc = PULL4(buf + HEAD + 24);
          425  +    log->tlen = PULL4(buf + HEAD + 28);
          426  +    log->stored = PULL2(buf + HEAD + 32);
          427  +    log->back = 3 + (buf[HEAD + 34] & 7);
          428  +    op = (buf[HEAD + 34] >> 3) & 3;
          429  +    return op;
          430  +}
          431  +
          432  +/* Write over the extra field contents, marking the operation as op.  Use fsync
          433  +   to assure that the device is written to, and in the requested order.  This
          434  +   operation, and only this operation, is assumed to be atomic in order to
          435  +   assure that the log is recoverable in the event of an interruption at any
          436  +   point in the process.  Return -1 if the write to foo.gz failed. */
          437  +local int log_mark(struct log *log, int op)
          438  +{
          439  +    int ret;
          440  +    unsigned char ext[EXTRA];
          441  +
          442  +    PUT8(ext, log->first);
          443  +    PUT8(ext + 8, log->last);
          444  +    PUT4(ext + 16, log->ccrc);
          445  +    PUT4(ext + 20, log->clen);
          446  +    PUT4(ext + 24, log->tcrc);
          447  +    PUT4(ext + 28, log->tlen);
          448  +    PUT2(ext + 32, log->stored);
          449  +    ext[34] = log->back - 3 + (op << 3);
          450  +    fsync(log->fd);
          451  +    ret = lseek(log->fd, HEAD, SEEK_SET) < 0 ||
          452  +          write(log->fd, ext, EXTRA) != EXTRA ? -1 : 0;
          453  +    fsync(log->fd);
          454  +    return ret;
          455  +}
          456  +
          457  +/* Rewrite the last block header bits and subsequent zero bits to get to a byte
          458  +   boundary, setting the last block bit if last is true, and then write the
          459  +   remainder of the stored block header (length and one's complement).  Leave
          460  +   the file pointer after the end of the last stored block data.  Return -1 if
          461  +   there is a read or write failure on the foo.gz file */
          462  +local int log_last(struct log *log, int last)
          463  +{
          464  +    int back, len, mask;
          465  +    unsigned char buf[6];
          466  +
          467  +    /* determine the locations of the bytes and bits to modify */
          468  +    back = log->last == log->first ? log->back : 8;
          469  +    len = back > 8 ? 2 : 1;                 /* bytes back from log->last */
          470  +    mask = 0x80 >> ((back - 1) & 7);        /* mask for block last-bit */
          471  +
          472  +    /* get the byte to modify (one or two back) into buf[0] -- don't need to
          473  +       read the byte if the last-bit is eight bits back, since in that case
          474  +       the entire byte will be modified */
          475  +    buf[0] = 0;
          476  +    if (back != 8 && (lseek(log->fd, log->last - len, SEEK_SET) < 0 ||
          477  +                      read(log->fd, buf, 1) != 1))
          478  +        return -1;
          479  +
          480  +    /* change the last-bit of the last stored block as requested -- note
          481  +       that all bits above the last-bit are set to zero, per the type bits
          482  +       of a stored block being 00 and per the convention that the bits to
          483  +       bring the stream to a byte boundary are also zeros */
          484  +    buf[1] = 0;
          485  +    buf[2 - len] = (*buf & (mask - 1)) + (last ? mask : 0);
          486  +
          487  +    /* write the modified stored block header and lengths, move the file
          488  +       pointer to after the last stored block data */
          489  +    PUT2(buf + 2, log->stored);
          490  +    PUT2(buf + 4, log->stored ^ 0xffff);
          491  +    return lseek(log->fd, log->last - len, SEEK_SET) < 0 ||
          492  +           write(log->fd, buf + 2 - len, len + 4) != len + 4 ||
          493  +           lseek(log->fd, log->stored, SEEK_CUR) < 0 ? -1 : 0;
          494  +}
          495  +
          496  +/* Append len bytes from data to the locked and open log file.  len may be zero
          497  +   if recovering and no .add file was found.  In that case, the previous state
          498  +   of the foo.gz file is restored.  The data is appended uncompressed in
          499  +   deflate stored blocks.  Return -1 if there was an error reading or writing
          500  +   the foo.gz file. */
          501  +local int log_append(struct log *log, unsigned char *data, size_t len)
          502  +{
          503  +    uint put;
          504  +    off_t end;
          505  +    unsigned char buf[8];
          506  +
          507  +    /* set the last block last-bit and length, in case recovering an
          508  +       interrupted append, then position the file pointer to append to the
          509  +       block */
          510  +    if (log_last(log, 1))
          511  +        return -1;
          512  +
          513  +    /* append, adding stored blocks and updating the offset of the last stored
          514  +       block as needed, and update the total crc and length */
          515  +    while (len) {
          516  +        /* append as much as we can to the last block */
          517  +        put = (MAX_STORE << 10) - log->stored;
          518  +        if (put > len)
          519  +            put = (uint)len;
          520  +        if (put) {
          521  +            if (write(log->fd, data, put) != put)
          522  +                return -1;
          523  +            BAIL(1);
          524  +            log->tcrc = crc32(log->tcrc, data, put);
          525  +            log->tlen += put;
          526  +            log->stored += put;
          527  +            data += put;
          528  +            len -= put;
          529  +        }
          530  +
          531  +        /* if we need to, add a new empty stored block */
          532  +        if (len) {
          533  +            /* mark current block as not last */
          534  +            if (log_last(log, 0))
          535  +                return -1;
          536  +
          537  +            /* point to new, empty stored block */
          538  +            log->last += 4 + log->stored + 1;
          539  +            log->stored = 0;
          540  +        }
          541  +
          542  +        /* mark last block as last, update its length */
          543  +        if (log_last(log, 1))
          544  +            return -1;
          545  +        BAIL(2);
          546  +    }
          547  +
          548  +    /* write the new crc and length trailer, and truncate just in case (could
          549  +       be recovering from partial append with a missing foo.add file) */
          550  +    PUT4(buf, log->tcrc);
          551  +    PUT4(buf + 4, log->tlen);
          552  +    if (write(log->fd, buf, 8) != 8 ||
          553  +        (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end))
          554  +        return -1;
          555  +
          556  +    /* write the extra field, marking the log file as done, delete .add file */
          557  +    if (log_mark(log, NO_OP))
          558  +        return -1;
          559  +    strcpy(log->end, ".add");
          560  +    unlink(log->path);          /* ignore error, since may not exist */
          561  +    return 0;
          562  +}
          563  +
          564  +/* Replace the foo.dict file with the foo.temp file.  Also delete the foo.add
          565  +   file, since the compress operation may have been interrupted before that was
          566  +   done.  Returns 1 if memory could not be allocated, or -1 if reading or
          567  +   writing foo.gz fails, or if the rename fails for some reason other than
          568  +   foo.temp not existing.  foo.temp not existing is a permitted error, since
          569  +   the replace operation may have been interrupted after the rename is done,
          570  +   but before foo.gz is marked as complete. */
          571  +local int log_replace(struct log *log)
          572  +{
          573  +    int ret;
          574  +    char *dest;
          575  +
          576  +    /* delete foo.add file */
          577  +    strcpy(log->end, ".add");
          578  +    unlink(log->path);         /* ignore error, since may not exist */
          579  +    BAIL(3);
          580  +
          581  +    /* rename foo.name to foo.dict, replacing foo.dict if it exists */
          582  +    strcpy(log->end, ".dict");
          583  +    dest = malloc(strlen(log->path) + 1);
          584  +    if (dest == NULL)
          585  +        return -2;
          586  +    strcpy(dest, log->path);
          587  +    strcpy(log->end, ".temp");
          588  +    ret = rename(log->path, dest);
          589  +    free(dest);
          590  +    if (ret && errno != ENOENT)
          591  +        return -1;
          592  +    BAIL(4);
          593  +
          594  +    /* mark the foo.gz file as done */
          595  +    return log_mark(log, NO_OP);
          596  +}
          597  +
          598  +/* Compress the len bytes at data and append the compressed data to the
          599  +   foo.gz deflate data immediately after the previous compressed data.  This
          600  +   overwrites the previous uncompressed data, which was stored in foo.add
          601  +   and is the data provided in data[0..len-1].  If this operation is
          602  +   interrupted, it picks up at the start of this routine, with the foo.add
          603  +   file read in again.  If there is no data to compress (len == 0), then we
          604  +   simply terminate the foo.gz file after the previously compressed data,
          605  +   appending a final empty stored block and the gzip trailer.  Return -1 if
          606  +   reading or writing the log.gz file failed, or -2 if there was a memory
          607  +   allocation failure. */
          608  +local int log_compress(struct log *log, unsigned char *data, size_t len)
          609  +{
          610  +    int fd;
          611  +    uint got, max;
          612  +    ssize_t dict;
          613  +    off_t end;
          614  +    z_stream strm;
          615  +    unsigned char buf[DICT];
          616  +
          617  +    /* compress and append compressed data */
          618  +    if (len) {
          619  +        /* set up for deflate, allocating memory */
          620  +        strm.zalloc = Z_NULL;
          621  +        strm.zfree = Z_NULL;
          622  +        strm.opaque = Z_NULL;
          623  +        if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
          624  +                         Z_DEFAULT_STRATEGY) != Z_OK)
          625  +            return -2;
          626  +
          627  +        /* read in dictionary (last 32K of data that was compressed) */
          628  +        strcpy(log->end, ".dict");
          629  +        fd = open(log->path, O_RDONLY, 0);
          630  +        if (fd >= 0) {
          631  +            dict = read(fd, buf, DICT);
          632  +            close(fd);
          633  +            if (dict < 0) {
          634  +                deflateEnd(&strm);
          635  +                return -1;
          636  +            }
          637  +            if (dict)
          638  +                deflateSetDictionary(&strm, buf, (uint)dict);
          639  +        }
          640  +        log_touch(log);
          641  +
          642  +        /* prime deflate with last bits of previous block, position write
          643  +           pointer to write those bits and overwrite what follows */
          644  +        if (lseek(log->fd, log->first - (log->back > 8 ? 2 : 1),
          645  +                SEEK_SET) < 0 ||
          646  +            read(log->fd, buf, 1) != 1 || lseek(log->fd, -1, SEEK_CUR) < 0) {
          647  +            deflateEnd(&strm);
          648  +            return -1;
          649  +        }
          650  +        deflatePrime(&strm, (8 - log->back) & 7, *buf);
          651  +
          652  +        /* compress, finishing with a partial non-last empty static block */
          653  +        strm.next_in = data;
          654  +        max = (((uint)0 - 1) >> 1) + 1; /* in case int smaller than size_t */
          655  +        do {
          656  +            strm.avail_in = len > max ? max : (uint)len;
          657  +            len -= strm.avail_in;
          658  +            do {
          659  +                strm.avail_out = DICT;
          660  +                strm.next_out = buf;
          661  +                deflate(&strm, len ? Z_NO_FLUSH : Z_PARTIAL_FLUSH);
          662  +                got = DICT - strm.avail_out;
          663  +                if (got && write(log->fd, buf, got) != got) {
          664  +                    deflateEnd(&strm);
          665  +                    return -1;
          666  +                }
          667  +                log_touch(log);
          668  +            } while (strm.avail_out == 0);
          669  +        } while (len);
          670  +        deflateEnd(&strm);
          671  +        BAIL(5);
          672  +
          673  +        /* find start of empty static block -- scanning backwards the first one
          674  +           bit is the second bit of the block, if the last byte is zero, then
          675  +           we know the byte before that has a one in the top bit, since an
          676  +           empty static block is ten bits long */
          677  +        if ((log->first = lseek(log->fd, -1, SEEK_CUR)) < 0 ||
          678  +            read(log->fd, buf, 1) != 1)
          679  +            return -1;
          680  +        log->first++;
          681  +        if (*buf) {
          682  +            log->back = 1;
          683  +            while ((*buf & ((uint)1 << (8 - log->back++))) == 0)
          684  +                ;       /* guaranteed to terminate, since *buf != 0 */
          685  +        }
          686  +        else
          687  +            log->back = 10;
          688  +
          689  +        /* update compressed crc and length */
          690  +        log->ccrc = log->tcrc;
          691  +        log->clen = log->tlen;
          692  +    }
          693  +    else {
          694  +        /* no data to compress -- fix up existing gzip stream */
          695  +        log->tcrc = log->ccrc;
          696  +        log->tlen = log->clen;
          697  +    }
          698  +
          699  +    /* complete and truncate gzip stream */
          700  +    log->last = log->first;
          701  +    log->stored = 0;
          702  +    PUT4(buf, log->tcrc);
          703  +    PUT4(buf + 4, log->tlen);
          704  +    if (log_last(log, 1) || write(log->fd, buf, 8) != 8 ||
          705  +        (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end))
          706  +        return -1;
          707  +    BAIL(6);
          708  +
          709  +    /* mark as being in the replace operation */
          710  +    if (log_mark(log, REPLACE_OP))
          711  +        return -1;
          712  +
          713  +    /* execute the replace operation and mark the file as done */
          714  +    return log_replace(log);
          715  +}
          716  +
          717  +/* log a repair record to the .repairs file */
          718  +local void log_log(struct log *log, int op, char *record)
          719  +{
          720  +    time_t now;
          721  +    FILE *rec;
          722  +
          723  +    now = time(NULL);
          724  +    strcpy(log->end, ".repairs");
          725  +    rec = fopen(log->path, "a");
          726  +    if (rec == NULL)
          727  +        return;
          728  +    fprintf(rec, "%.24s %s recovery: %s\n", ctime(&now), op == APPEND_OP ?
          729  +            "append" : (op == COMPRESS_OP ? "compress" : "replace"), record);
          730  +    fclose(rec);
          731  +    return;
          732  +}
          733  +
          734  +/* Recover the interrupted operation op.  First read foo.add for recovering an
          735  +   append or compress operation.  Return -1 if there was an error reading or
          736  +   writing foo.gz or reading an existing foo.add, or -2 if there was a memory
          737  +   allocation failure. */
          738  +local int log_recover(struct log *log, int op)
          739  +{
          740  +    int fd, ret = 0;
          741  +    unsigned char *data = NULL;
          742  +    size_t len = 0;
          743  +    struct stat st;
          744  +
          745  +    /* log recovery */
          746  +    log_log(log, op, "start");
          747  +
          748  +    /* load foo.add file if expected and present */
          749  +    if (op == APPEND_OP || op == COMPRESS_OP) {
          750  +        strcpy(log->end, ".add");
          751  +        if (stat(log->path, &st) == 0 && st.st_size) {
          752  +            len = (size_t)(st.st_size);
          753  +            if (len != st.st_size || (data = malloc(st.st_size)) == NULL) {
          754  +                log_log(log, op, "allocation failure");
          755  +                return -2;
          756  +            }
          757  +            if ((fd = open(log->path, O_RDONLY, 0)) < 0) {
          758  +                log_log(log, op, ".add file read failure");
          759  +                return -1;
          760  +            }
          761  +            ret = read(fd, data, len) != len;
          762  +            close(fd);
          763  +            if (ret) {
          764  +                log_log(log, op, ".add file read failure");
          765  +                return -1;
          766  +            }
          767  +            log_log(log, op, "loaded .add file");
          768  +        }
          769  +        else
          770  +            log_log(log, op, "missing .add file!");
          771  +    }
          772  +
          773  +    /* recover the interrupted operation */
          774  +    switch (op) {
          775  +    case APPEND_OP:
          776  +        ret = log_append(log, data, len);
          777  +        break;
          778  +    case COMPRESS_OP:
          779  +        ret = log_compress(log, data, len);
          780  +        break;
          781  +    case REPLACE_OP:
          782  +        ret = log_replace(log);
          783  +    }
          784  +
          785  +    /* log status */
          786  +    log_log(log, op, ret ? "failure" : "complete");
          787  +
          788  +    /* clean up */
          789  +    if (data != NULL)
          790  +        free(data);
          791  +    return ret;
          792  +}
          793  +
          794  +/* Close the foo.gz file (if open) and release the lock. */
          795  +local void log_close(struct log *log)
          796  +{
          797  +    if (log->fd >= 0)
          798  +        close(log->fd);
          799  +    log->fd = -1;
          800  +    log_unlock(log);
          801  +}
          802  +
          803  +/* Open foo.gz, verify the header, and load the extra field contents, after
          804  +   first creating the foo.lock file to gain exclusive access to the foo.*
          805  +   files.  If foo.gz does not exist or is empty, then write the initial header,
          806  +   extra, and body content of an empty foo.gz log file.  If there is an error
          807  +   creating the lock file due to access restrictions, or an error reading or
          808  +   writing the foo.gz file, or if the foo.gz file is not a proper log file for
          809  +   this object (e.g. not a gzip file or does not contain the expected extra
          810  +   field), then return true.  If there is an error, the lock is released.
          811  +   Otherwise, the lock is left in place. */
          812  +local int log_open(struct log *log)
          813  +{
          814  +    int op;
          815  +
          816  +    /* release open file resource if left over -- can occur if lock lost
          817  +       between gzlog_open() and gzlog_write() */
          818  +    if (log->fd >= 0)
          819  +        close(log->fd);
          820  +    log->fd = -1;
          821  +
          822  +    /* negotiate exclusive access */
          823  +    if (log_lock(log) < 0)
          824  +        return -1;
          825  +
          826  +    /* open the log file, foo.gz */
          827  +    strcpy(log->end, ".gz");
          828  +    log->fd = open(log->path, O_RDWR | O_CREAT, 0644);
          829  +    if (log->fd < 0) {
          830  +        log_close(log);
          831  +        return -1;
          832  +    }
          833  +
          834  +    /* if new, initialize foo.gz with an empty log, delete old dictionary */
          835  +    if (lseek(log->fd, 0, SEEK_END) == 0) {
          836  +        if (write(log->fd, log_gzhead, HEAD) != HEAD ||
          837  +            write(log->fd, log_gzext, EXTRA) != EXTRA ||
          838  +            write(log->fd, log_gzbody, BODY) != BODY) {
          839  +            log_close(log);
          840  +            return -1;
          841  +        }
          842  +        strcpy(log->end, ".dict");
          843  +        unlink(log->path);
          844  +    }
          845  +
          846  +    /* verify log file and load extra field information */
          847  +    if ((op = log_head(log)) < 0) {
          848  +        log_close(log);
          849  +        return -1;
          850  +    }
          851  +
          852  +    /* check for interrupted process and if so, recover */
          853  +    if (op != NO_OP && log_recover(log, op)) {
          854  +        log_close(log);
          855  +        return -1;
          856  +    }
          857  +
          858  +    /* touch the lock file to prevent another process from grabbing it */
          859  +    log_touch(log);
          860  +    return 0;
          861  +}
          862  +
          863  +/* See gzlog.h for the description of the external methods below */
          864  +gzlog *gzlog_open(char *path)
          865  +{
          866  +    size_t n;
          867  +    struct log *log;
          868  +
          869  +    /* check arguments */
          870  +    if (path == NULL || *path == 0)
          871  +        return NULL;
          872  +
          873  +    /* allocate and initialize log structure */
          874  +    log = malloc(sizeof(struct log));
          875  +    if (log == NULL)
          876  +        return NULL;
          877  +    strcpy(log->id, LOGID);
          878  +    log->fd = -1;
          879  +
          880  +    /* save path and end of path for name construction */
          881  +    n = strlen(path);
          882  +    log->path = malloc(n + 9);              /* allow for ".repairs" */
          883  +    if (log->path == NULL) {
          884  +        free(log);
          885  +        return NULL;
          886  +    }
          887  +    strcpy(log->path, path);
          888  +    log->end = log->path + n;
          889  +
          890  +    /* gain exclusive access and verify log file -- may perform a
          891  +       recovery operation if needed */
          892  +    if (log_open(log)) {
          893  +        free(log->path);
          894  +        free(log);
          895  +        return NULL;
          896  +    }
          897  +
          898  +    /* return pointer to log structure */
          899  +    return log;
          900  +}
          901  +
          902  +/* gzlog_compress() return values:
          903  +    0: all good
          904  +   -1: file i/o error (usually access issue)
          905  +   -2: memory allocation failure
          906  +   -3: invalid log pointer argument */
          907  +int gzlog_compress(gzlog *logd)
          908  +{
          909  +    int fd, ret;
          910  +    uint block;
          911  +    size_t len, next;
          912  +    unsigned char *data, buf[5];
          913  +    struct log *log = logd;
          914  +
          915  +    /* check arguments */
          916  +    if (log == NULL || strcmp(log->id, LOGID) || len < 0)
          917  +        return -3;
          918  +
          919  +    /* see if we lost the lock -- if so get it again and reload the extra
          920  +       field information (it probably changed), recover last operation if
          921  +       necessary */
          922  +    if (log_check(log) && log_open(log))
          923  +        return -1;
          924  +
          925  +    /* create space for uncompressed data */
          926  +    len = ((size_t)(log->last - log->first) & ~(((size_t)1 << 10) - 1)) +
          927  +          log->stored;
          928  +    if ((data = malloc(len)) == NULL)
          929  +        return -2;
          930  +
          931  +    /* do statement here is just a cheap trick for error handling */
          932  +    do {
          933  +        /* read in the uncompressed data */
          934  +        if (lseek(log->fd, log->first - 1, SEEK_SET) < 0)
          935  +            break;
          936  +        next = 0;
          937  +        while (next < len) {
          938  +            if (read(log->fd, buf, 5) != 5)
          939  +                break;
          940  +            block = PULL2(buf + 1);
          941  +            if (next + block > len ||
          942  +                read(log->fd, (char *)data + next, block) != block)
          943  +                break;
          944  +            next += block;
          945  +        }
          946  +        if (lseek(log->fd, 0, SEEK_CUR) != log->last + 4 + log->stored)
          947  +            break;
          948  +        log_touch(log);
          949  +
          950  +        /* write the uncompressed data to the .add file */
          951  +        strcpy(log->end, ".add");
          952  +        fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
          953  +        if (fd < 0)
          954  +            break;
          955  +        ret = write(fd, data, len) != len;
          956  +        if (ret | close(fd))
          957  +            break;
          958  +        log_touch(log);
          959  +
          960  +        /* write the dictionary for the next compress to the .temp file */
          961  +        strcpy(log->end, ".temp");
          962  +        fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
          963  +        if (fd < 0)
          964  +            break;
          965  +        next = DICT > len ? len : DICT;
          966  +        ret = write(fd, (char *)data + len - next, next) != next;
          967  +        if (ret | close(fd))
          968  +            break;
          969  +        log_touch(log);
          970  +
          971  +        /* roll back to compressed data, mark the compress in progress */
          972  +        log->last = log->first;
          973  +        log->stored = 0;
          974  +        if (log_mark(log, COMPRESS_OP))
          975  +            break;
          976  +        BAIL(7);
          977  +
          978  +        /* compress and append the data (clears mark) */
          979  +        ret = log_compress(log, data, len);
          980  +        free(data);
          981  +        return ret;
          982  +    } while (0);
          983  +
          984  +    /* broke out of do above on i/o error */
          985  +    free(data);
          986  +    return -1;
          987  +}
          988  +
          989  +/* gzlog_write() return values:
          990  +    0: all good
          991  +   -1: file i/o error (usually access issue)
          992  +   -2: memory allocation failure
          993  +   -3: invalid log pointer argument */
          994  +int gzlog_write(gzlog *logd, void *data, size_t len)
          995  +{
          996  +    int fd, ret;
          997  +    struct log *log = logd;
          998  +
          999  +    /* check arguments */
         1000  +    if (log == NULL || strcmp(log->id, LOGID) || len < 0)
         1001  +        return -3;
         1002  +    if (data == NULL || len == 0)
         1003  +        return 0;
         1004  +
         1005  +    /* see if we lost the lock -- if so get it again and reload the extra
         1006  +       field information (it probably changed), recover last operation if
         1007  +       necessary */
         1008  +    if (log_check(log) && log_open(log))
         1009  +        return -1;
         1010  +
         1011  +    /* create and write .add file */
         1012  +    strcpy(log->end, ".add");
         1013  +    fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
         1014  +    if (fd < 0)
         1015  +        return -1;
         1016  +    ret = write(fd, data, len) != len;
         1017  +    if (ret | close(fd))
         1018  +        return -1;
         1019  +    log_touch(log);
         1020  +
         1021  +    /* mark log file with append in progress */
         1022  +    if (log_mark(log, APPEND_OP))
         1023  +        return -1;
         1024  +    BAIL(8);
         1025  +
         1026  +    /* append data (clears mark) */
         1027  +    if (log_append(log, data, len))
         1028  +        return -1;
         1029  +
         1030  +    /* check to see if it's time to compress -- if not, then done */
         1031  +    if (((log->last - log->first) >> 10) + (log->stored >> 10) < TRIGGER)
         1032  +        return 0;
         1033  +
         1034  +    /* time to compress */
         1035  +    return gzlog_compress(log);
         1036  +}
         1037  +
         1038  +/* gzlog_close() return values:
         1039  +    0: ok
         1040  +   -3: invalid log pointer argument */
         1041  +int gzlog_close(gzlog *logd)
         1042  +{
         1043  +    struct log *log = logd;
         1044  +
         1045  +    /* check arguments */
         1046  +    if (log == NULL || strcmp(log->id, LOGID))
         1047  +        return -3;
         1048  +
         1049  +    /* close the log file and release the lock */
         1050  +    log_close(log);
         1051  +
         1052  +    /* free structure and return */
         1053  +    if (log->path != NULL)
         1054  +        free(log->path);
         1055  +    strcpy(log->id, "bad");
         1056  +    free(log);
         1057  +    return 0;
         1058  +}

Added compat/zlib/examples/gzlog.h.

            1  +/* gzlog.h
            2  +  Copyright (C) 2004, 2008 Mark Adler, all rights reserved
            3  +  version 2.0, 25 Apr 2008
            4  +
            5  +  This software is provided 'as-is', without any express or implied
            6  +  warranty.  In no event will the author be held liable for any damages
            7  +  arising from the use of this software.
            8  +
            9  +  Permission is granted to anyone to use this software for any purpose,
           10  +  including commercial applications, and to alter it and redistribute it
           11  +  freely, subject to the following restrictions:
           12  +
           13  +  1. The origin of this software must not be misrepresented; you must not
           14  +     claim that you wrote the original software. If you use this software
           15  +     in a product, an acknowledgment in the product documentation would be
           16  +     appreciated but is not required.
           17  +  2. Altered source versions must be plainly marked as such, and must not be
           18  +     misrepresented as being the original software.
           19  +  3. This notice may not be removed or altered from any source distribution.
           20  +
           21  +  Mark Adler    madler@alumni.caltech.edu
           22  + */
           23  +
           24  +/* Version History:
           25  +   1.0  26 Nov 2004  First version
           26  +   2.0  25 Apr 2008  Complete redesign for recovery of interrupted operations
           27  +                     Interface changed slightly in that now path is a prefix
           28  +                     Compression now occurs as needed during gzlog_write()
           29  +                     gzlog_write() now always leaves the log file as valid gzip
           30  + */
           31  +
           32  +/*
           33  +   The gzlog object allows writing short messages to a gzipped log file,
           34  +   opening the log file locked for small bursts, and then closing it.  The log
           35  +   object works by appending stored (uncompressed) data to the gzip file until
           36  +   1 MB has been accumulated.  At that time, the stored data is compressed, and
           37  +   replaces the uncompressed data in the file.  The log file is truncated to
           38  +   its new size at that time.  After each write operation, the log file is a
           39  +   valid gzip file that can decompressed to recover what was written.
           40  +
           41  +   The gzlog operations can be interupted at any point due to an application or
           42  +   system crash, and the log file will be recovered the next time the log is
           43  +   opened with gzlog_open().
           44  + */
           45  +
           46  +#ifndef GZLOG_H
           47  +#define GZLOG_H
           48  +
           49  +/* gzlog object type */
           50  +typedef void gzlog;
           51  +
           52  +/* Open a gzlog object, creating the log file if it does not exist.  Return
           53  +   NULL on error.  Note that gzlog_open() could take a while to complete if it
           54  +   has to wait to verify that a lock is stale (possibly for five minutes), or
           55  +   if there is significant contention with other instantiations of this object
           56  +   when locking the resource.  path is the prefix of the file names created by
           57  +   this object.  If path is "foo", then the log file will be "foo.gz", and
           58  +   other auxiliary files will be created and destroyed during the process:
           59  +   "foo.dict" for a compression dictionary, "foo.temp" for a temporary (next)
           60  +   dictionary, "foo.add" for data being added or compressed, "foo.lock" for the
           61  +   lock file, and "foo.repairs" to log recovery operations performed due to
           62  +   interrupted gzlog operations.  A gzlog_open() followed by a gzlog_close()
           63  +   will recover a previously interrupted operation, if any. */
           64  +gzlog *gzlog_open(char *path);
           65  +
           66  +/* Write to a gzlog object.  Return zero on success, -1 if there is a file i/o
           67  +   error on any of the gzlog files (this should not happen if gzlog_open()
           68  +   succeeded, unless the device has run out of space or leftover auxiliary
           69  +   files have permissions or ownership that prevent their use), -2 if there is
           70  +   a memory allocation failure, or -3 if the log argument is invalid (e.g. if
           71  +   it was not created by gzlog_open()).  This function will write data to the
           72  +   file uncompressed, until 1 MB has been accumulated, at which time that data
           73  +   will be compressed.  The log file will be a valid gzip file upon successful
           74  +   return. */
           75  +int gzlog_write(gzlog *log, void *data, size_t len);
           76  +
           77  +/* Force compression of any uncompressed data in the log.  This should be used
           78  +   sparingly, if at all.  The main application would be when a log file will
           79  +   not be appended to again.  If this is used to compress frequently while
           80  +   appending, it will both significantly increase the execution time and
           81  +   reduce the compression ratio.  The return codes are the same as for
           82  +   gzlog_write(). */
           83  +int gzlog_compress(gzlog *log);
           84  +
           85  +/* Close a gzlog object.  Return zero on success, -3 if the log argument is
           86  +   invalid.  The log object is freed, and so cannot be referenced again. */
           87  +int gzlog_close(gzlog *log);
           88  +
           89  +#endif

Added compat/zlib/examples/zlib_how.html.

            1  +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
            2  +  "http://www.w3.org/TR/REC-html40/loose.dtd">
            3  +<html>
            4  +<head>
            5  +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
            6  +<title>zlib Usage Example</title>
            7  +<!--  Copyright (c) 2004, 2005 Mark Adler.  -->
            8  +</head>
            9  +<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#00A000">
           10  +<h2 align="center"> zlib Usage Example </h2>
           11  +We often get questions about how the <tt>deflate()</tt> and <tt>inflate()</tt> functions should be used.
           12  +Users wonder when they should provide more input, when they should use more output,
           13  +what to do with a <tt>Z_BUF_ERROR</tt>, how to make sure the process terminates properly, and
           14  +so on.  So for those who have read <tt>zlib.h</tt> (a few times), and
           15  +would like further edification, below is an annotated example in C of simple routines to compress and decompress
           16  +from an input file to an output file using <tt>deflate()</tt> and <tt>inflate()</tt> respectively.  The
           17  +annotations are interspersed between lines of the code.  So please read between the lines.
           18  +We hope this helps explain some of the intricacies of <em>zlib</em>.
           19  +<p>
           20  +Without further adieu, here is the program <a href="zpipe.c"><tt>zpipe.c</tt></a>:
           21  +<pre><b>
           22  +/* zpipe.c: example of proper use of zlib's inflate() and deflate()
           23  +   Not copyrighted -- provided to the public domain
           24  +   Version 1.4  11 December 2005  Mark Adler */
           25  +
           26  +/* Version history:
           27  +   1.0  30 Oct 2004  First version
           28  +   1.1   8 Nov 2004  Add void casting for unused return values
           29  +                     Use switch statement for inflate() return values
           30  +   1.2   9 Nov 2004  Add assertions to document zlib guarantees
           31  +   1.3   6 Apr 2005  Remove incorrect assertion in inf()
           32  +   1.4  11 Dec 2005  Add hack to avoid MSDOS end-of-line conversions
           33  +                     Avoid some compiler warnings for input and output buffers
           34  + */
           35  +</b></pre><!-- -->
           36  +We now include the header files for the required definitions.  From
           37  +<tt>stdio.h</tt> we use <tt>fopen()</tt>, <tt>fread()</tt>, <tt>fwrite()</tt>,
           38  +<tt>feof()</tt>, <tt>ferror()</tt>, and <tt>fclose()</tt> for file i/o, and
           39  +<tt>fputs()</tt> for error messages.  From <tt>string.h</tt> we use
           40  +<tt>strcmp()</tt> for command line argument processing.
           41  +From <tt>assert.h</tt> we use the <tt>assert()</tt> macro.
           42  +From <tt>zlib.h</tt>
           43  +we use the basic compression functions <tt>deflateInit()</tt>,
           44  +<tt>deflate()</tt>, and <tt>deflateEnd()</tt>, and the basic decompression
           45  +functions <tt>inflateInit()</tt>, <tt>inflate()</tt>, and
           46  +<tt>inflateEnd()</tt>.
           47  +<pre><b>
           48  +#include &lt;stdio.h&gt;
           49  +#include &lt;string.h&gt;
           50  +#include &lt;assert.h&gt;
           51  +#include "zlib.h"
           52  +</b></pre><!-- -->
           53  +This is an ugly hack required to avoid corruption of the input and output data on
           54  +Windows/MS-DOS systems.  Without this, those systems would assume that the input and output
           55  +files are text, and try to convert the end-of-line characters from one standard to
           56  +another.  That would corrupt binary data, and in particular would render the compressed data unusable.
           57  +This sets the input and output to binary which suppresses the end-of-line conversions.
           58  +<tt>SET_BINARY_MODE()</tt> will be used later on <tt>stdin</tt> and <tt>stdout</tt>, at the beginning of <tt>main()</tt>.
           59  +<pre><b>
           60  +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
           61  +#  include &lt;fcntl.h&gt;
           62  +#  include &lt;io.h&gt;
           63  +#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
           64  +#else
           65  +#  define SET_BINARY_MODE(file)
           66  +#endif
           67  +</b></pre><!-- -->
           68  +<tt>CHUNK</tt> is simply the buffer size for feeding data to and pulling data
           69  +from the <em>zlib</em> routines.  Larger buffer sizes would be more efficient,
           70  +especially for <tt>inflate()</tt>.  If the memory is available, buffers sizes
           71  +on the order of 128K or 256K bytes should be used.
           72  +<pre><b>
           73  +#define CHUNK 16384
           74  +</b></pre><!-- -->
           75  +The <tt>def()</tt> routine compresses data from an input file to an output file.  The output data
           76  +will be in the <em>zlib</em> format, which is different from the <em>gzip</em> or <em>zip</em>
           77  +formats.  The <em>zlib</em> format has a very small header of only two bytes to identify it as
           78  +a <em>zlib</em> stream and to provide decoding information, and a four-byte trailer with a fast
           79  +check value to verify the integrity of the uncompressed data after decoding.
           80  +<pre><b>
           81  +/* Compress from file source to file dest until EOF on source.
           82  +   def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
           83  +   allocated for processing, Z_STREAM_ERROR if an invalid compression
           84  +   level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
           85  +   version of the library linked do not match, or Z_ERRNO if there is
           86  +   an error reading or writing the files. */
           87  +int def(FILE *source, FILE *dest, int level)
           88  +{
           89  +</b></pre>
           90  +Here are the local variables for <tt>def()</tt>.  <tt>ret</tt> will be used for <em>zlib</em>
           91  +return codes.  <tt>flush</tt> will keep track of the current flushing state for <tt>deflate()</tt>,
           92  +which is either no flushing, or flush to completion after the end of the input file is reached.
           93  +<tt>have</tt> is the amount of data returned from <tt>deflate()</tt>.  The <tt>strm</tt> structure
           94  +is used to pass information to and from the <em>zlib</em> routines, and to maintain the
           95  +<tt>deflate()</tt> state.  <tt>in</tt> and <tt>out</tt> are the input and output buffers for
           96  +<tt>deflate()</tt>.
           97  +<pre><b>
           98  +    int ret, flush;
           99  +    unsigned have;
          100  +    z_stream strm;
          101  +    unsigned char in[CHUNK];
          102  +    unsigned char out[CHUNK];
          103  +</b></pre><!-- -->
          104  +The first thing we do is to initialize the <em>zlib</em> state for compression using
          105  +<tt>deflateInit()</tt>.  This must be done before the first use of <tt>deflate()</tt>.
          106  +The <tt>zalloc</tt>, <tt>zfree</tt>, and <tt>opaque</tt> fields in the <tt>strm</tt>
          107  +structure must be initialized before calling <tt>deflateInit()</tt>.  Here they are
          108  +set to the <em>zlib</em> constant <tt>Z_NULL</tt> to request that <em>zlib</em> use
          109  +the default memory allocation routines.  An application may also choose to provide
          110  +custom memory allocation routines here.  <tt>deflateInit()</tt> will allocate on the
          111  +order of 256K bytes for the internal state.
          112  +(See <a href="zlib_tech.html"><em>zlib Technical Details</em></a>.)
          113  +<p>
          114  +<tt>deflateInit()</tt> is called with a pointer to the structure to be initialized and
          115  +the compression level, which is an integer in the range of -1 to 9.  Lower compression
          116  +levels result in faster execution, but less compression.  Higher levels result in
          117  +greater compression, but slower execution.  The <em>zlib</em> constant Z_DEFAULT_COMPRESSION,
          118  +equal to -1,
          119  +provides a good compromise between compression and speed and is equivalent to level 6.
          120  +Level 0 actually does no compression at all, and in fact expands the data slightly to produce
          121  +the <em>zlib</em> format (it is not a byte-for-byte copy of the input).
          122  +More advanced applications of <em>zlib</em>
          123  +may use <tt>deflateInit2()</tt> here instead.  Such an application may want to reduce how
          124  +much memory will be used, at some price in compression.  Or it may need to request a
          125  +<em>gzip</em> header and trailer instead of a <em>zlib</em> header and trailer, or raw
          126  +encoding with no header or trailer at all.
          127  +<p>
          128  +We must check the return value of <tt>deflateInit()</tt> against the <em>zlib</em> constant
          129  +<tt>Z_OK</tt> to make sure that it was able to
          130  +allocate memory for the internal state, and that the provided arguments were valid.
          131  +<tt>deflateInit()</tt> will also check that the version of <em>zlib</em> that the <tt>zlib.h</tt>
          132  +file came from matches the version of <em>zlib</em> actually linked with the program.  This
          133  +is especially important for environments in which <em>zlib</em> is a shared library.
          134  +<p>
          135  +Note that an application can initialize multiple, independent <em>zlib</em> streams, which can
          136  +operate in parallel.  The state information maintained in the structure allows the <em>zlib</em>
          137  +routines to be reentrant.
          138  +<pre><b>
          139  +    /* allocate deflate state */
          140  +    strm.zalloc = Z_NULL;
          141  +    strm.zfree = Z_NULL;
          142  +    strm.opaque = Z_NULL;
          143  +    ret = deflateInit(&amp;strm, level);
          144  +    if (ret != Z_OK)
          145  +        return ret;
          146  +</b></pre><!-- -->
          147  +With the pleasantries out of the way, now we can get down to business.  The outer <tt>do</tt>-loop
          148  +reads all of the input file and exits at the bottom of the loop once end-of-file is reached.
          149  +This loop contains the only call of <tt>deflate()</tt>.  So we must make sure that all of the
          150  +input data has been processed and that all of the output data has been generated and consumed
          151  +before we fall out of the loop at the bottom.
          152  +<pre><b>
          153  +    /* compress until end of file */
          154  +    do {
          155  +</b></pre>
          156  +We start off by reading data from the input file.  The number of bytes read is put directly
          157  +into <tt>avail_in</tt>, and a pointer to those bytes is put into <tt>next_in</tt>.  We also
          158  +check to see if end-of-file on the input has been reached.  If we are at the end of file, then <tt>flush</tt> is set to the
          159  +<em>zlib</em> constant <tt>Z_FINISH</tt>, which is later passed to <tt>deflate()</tt> to
          160  +indicate that this is the last chunk of input data to compress.  We need to use <tt>feof()</tt>
          161  +to check for end-of-file as opposed to seeing if fewer than <tt>CHUNK</tt> bytes have been read.  The
          162  +reason is that if the input file length is an exact multiple of <tt>CHUNK</tt>, we will miss
          163  +the fact that we got to the end-of-file, and not know to tell <tt>deflate()</tt> to finish
          164  +up the compressed stream.  If we are not yet at the end of the input, then the <em>zlib</em>
          165  +constant <tt>Z_NO_FLUSH</tt> will be passed to <tt>deflate</tt> to indicate that we are still
          166  +in the middle of the uncompressed data.
          167  +<p>
          168  +If there is an error in reading from the input file, the process is aborted with
          169  +<tt>deflateEnd()</tt> being called to free the allocated <em>zlib</em> state before returning
          170  +the error.  We wouldn't want a memory leak, now would we?  <tt>deflateEnd()</tt> can be called
          171  +at any time after the state has been initialized.  Once that's done, <tt>deflateInit()</tt> (or
          172  +<tt>deflateInit2()</tt>) would have to be called to start a new compression process.  There is
          173  +no point here in checking the <tt>deflateEnd()</tt> return code.  The deallocation can't fail.
          174  +<pre><b>
          175  +        strm.avail_in = fread(in, 1, CHUNK, source);
          176  +        if (ferror(source)) {
          177  +            (void)deflateEnd(&amp;strm);
          178  +            return Z_ERRNO;
          179  +        }
          180  +        flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
          181  +        strm.next_in = in;
          182  +</b></pre><!-- -->
          183  +The inner <tt>do</tt>-loop passes our chunk of input data to <tt>deflate()</tt>, and then
          184  +keeps calling <tt>deflate()</tt> until it is done producing output.  Once there is no more
          185  +new output, <tt>deflate()</tt> is guaranteed to have consumed all of the input, i.e.,
          186  +<tt>avail_in</tt> will be zero.
          187  +<pre><b>
          188  +        /* run deflate() on input until output buffer not full, finish
          189  +           compression if all of source has been read in */
          190  +        do {
          191  +</b></pre>
          192  +Output space is provided to <tt>deflate()</tt> by setting <tt>avail_out</tt> to the number
          193  +of available output bytes and <tt>next_out</tt> to a pointer to that space.
          194  +<pre><b>
          195  +            strm.avail_out = CHUNK;
          196  +            strm.next_out = out;
          197  +</b></pre>
          198  +Now we call the compression engine itself, <tt>deflate()</tt>.  It takes as many of the
          199  +<tt>avail_in</tt> bytes at <tt>next_in</tt> as it can process, and writes as many as
          200  +<tt>avail_out</tt> bytes to <tt>next_out</tt>.  Those counters and pointers are then
          201  +updated past the input data consumed and the output data written.  It is the amount of
          202  +output space available that may limit how much input is consumed.
          203  +Hence the inner loop to make sure that
          204  +all of the input is consumed by providing more output space each time.  Since <tt>avail_in</tt>
          205  +and <tt>next_in</tt> are updated by <tt>deflate()</tt>, we don't have to mess with those
          206  +between <tt>deflate()</tt> calls until it's all used up.
          207  +<p>
          208  +The parameters to <tt>deflate()</tt> are a pointer to the <tt>strm</tt> structure containing
          209  +the input and output information and the internal compression engine state, and a parameter
          210  +indicating whether and how to flush data to the output.  Normally <tt>deflate</tt> will consume
          211  +several K bytes of input data before producing any output (except for the header), in order
          212  +to accumulate statistics on the data for optimum compression.  It will then put out a burst of
          213  +compressed data, and proceed to consume more input before the next burst.  Eventually,
          214  +<tt>deflate()</tt>
          215  +must be told to terminate the stream, complete the compression with provided input data, and
          216  +write out the trailer check value.  <tt>deflate()</tt> will continue to compress normally as long
          217  +as the flush parameter is <tt>Z_NO_FLUSH</tt>.  Once the <tt>Z_FINISH</tt> parameter is provided,
          218  +<tt>deflate()</tt> will begin to complete the compressed output stream.  However depending on how
          219  +much output space is provided, <tt>deflate()</tt> may have to be called several times until it
          220  +has provided the complete compressed stream, even after it has consumed all of the input.  The flush
          221  +parameter must continue to be <tt>Z_FINISH</tt> for those subsequent calls.
          222  +<p>
          223  +There are other values of the flush parameter that are used in more advanced applications.  You can
          224  +force <tt>deflate()</tt> to produce a burst of output that encodes all of the input data provided
          225  +so far, even if it wouldn't have otherwise, for example to control data latency on a link with
          226  +compressed data.  You can also ask that <tt>deflate()</tt> do that as well as erase any history up to
          227  +that point so that what follows can be decompressed independently, for example for random access
          228  +applications.  Both requests will degrade compression by an amount depending on how often such
          229  +requests are made.
          230  +<p>
          231  +<tt>deflate()</tt> has a return value that can indicate errors, yet we do not check it here.  Why
          232  +not?  Well, it turns out that <tt>deflate()</tt> can do no wrong here.  Let's go through
          233  +<tt>deflate()</tt>'s return values and dispense with them one by one.  The possible values are
          234  +<tt>Z_OK</tt>, <tt>Z_STREAM_END</tt>, <tt>Z_STREAM_ERROR</tt>, or <tt>Z_BUF_ERROR</tt>.  <tt>Z_OK</tt>
          235  +is, well, ok.  <tt>Z_STREAM_END</tt> is also ok and will be returned for the last call of
          236  +<tt>deflate()</tt>.  This is already guaranteed by calling <tt>deflate()</tt> with <tt>Z_FINISH</tt>
          237  +until it has no more output.  <tt>Z_STREAM_ERROR</tt> is only possible if the stream is not
          238  +initialized properly, but we did initialize it properly.  There is no harm in checking for
          239  +<tt>Z_STREAM_ERROR</tt> here, for example to check for the possibility that some
          240  +other part of the application inadvertently clobbered the memory containing the <em>zlib</em> state.
          241  +<tt>Z_BUF_ERROR</tt> will be explained further below, but
          242  +suffice it to say that this is simply an indication that <tt>deflate()</tt> could not consume
          243  +more input or produce more output.  <tt>deflate()</tt> can be called again with more output space
          244  +or more available input, which it will be in this code.
          245  +<pre><b>
          246  +            ret = deflate(&amp;strm, flush);    /* no bad return value */
          247  +            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
          248  +</b></pre>
          249  +Now we compute how much output <tt>deflate()</tt> provided on the last call, which is the
          250  +difference between how much space was provided before the call, and how much output space
          251  +is still available after the call.  Then that data, if any, is written to the output file.
          252  +We can then reuse the output buffer for the next call of <tt>deflate()</tt>.  Again if there
          253  +is a file i/o error, we call <tt>deflateEnd()</tt> before returning to avoid a memory leak.
          254  +<pre><b>
          255  +            have = CHUNK - strm.avail_out;
          256  +            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
          257  +                (void)deflateEnd(&amp;strm);
          258  +                return Z_ERRNO;
          259  +            }
          260  +</b></pre>
          261  +The inner <tt>do</tt>-loop is repeated until the last <tt>deflate()</tt> call fails to fill the
          262  +provided output buffer.  Then we know that <tt>deflate()</tt> has done as much as it can with
          263  +the provided input, and that all of that input has been consumed.  We can then fall out of this
          264  +loop and reuse the input buffer.
          265  +<p>
          266  +The way we tell that <tt>deflate()</tt> has no more output is by seeing that it did not fill
          267  +the output buffer, leaving <tt>avail_out</tt> greater than zero.  However suppose that
          268  +<tt>deflate()</tt> has no more output, but just so happened to exactly fill the output buffer!
          269  +<tt>avail_out</tt> is zero, and we can't tell that <tt>deflate()</tt> has done all it can.
          270  +As far as we know, <tt>deflate()</tt>
          271  +has more output for us.  So we call it again.  But now <tt>deflate()</tt> produces no output
          272  +at all, and <tt>avail_out</tt> remains unchanged as <tt>CHUNK</tt>.  That <tt>deflate()</tt> call
          273  +wasn't able to do anything, either consume input or produce output, and so it returns
          274  +<tt>Z_BUF_ERROR</tt>.  (See, I told you I'd cover this later.)  However this is not a problem at
          275  +all.  Now we finally have the desired indication that <tt>deflate()</tt> is really done,
          276  +and so we drop out of the inner loop to provide more input to <tt>deflate()</tt>.
          277  +<p>
          278  +With <tt>flush</tt> set to <tt>Z_FINISH</tt>, this final set of <tt>deflate()</tt> calls will
          279  +complete the output stream.  Once that is done, subsequent calls of <tt>deflate()</tt> would return
          280  +<tt>Z_STREAM_ERROR</tt> if the flush parameter is not <tt>Z_FINISH</tt>, and do no more processing
          281  +until the state is reinitialized.
          282  +<p>
          283  +Some applications of <em>zlib</em> have two loops that call <tt>deflate()</tt>
          284  +instead of the single inner loop we have here.  The first loop would call
          285  +without flushing and feed all of the data to <tt>deflate()</tt>.  The second loop would call
          286  +<tt>deflate()</tt> with no more
          287  +data and the <tt>Z_FINISH</tt> parameter to complete the process.  As you can see from this
          288  +example, that can be avoided by simply keeping track of the current flush state.
          289  +<pre><b>
          290  +        } while (strm.avail_out == 0);
          291  +        assert(strm.avail_in == 0);     /* all input will be used */
          292  +</b></pre><!-- -->
          293  +Now we check to see if we have already processed all of the input file.  That information was
          294  +saved in the <tt>flush</tt> variable, so we see if that was set to <tt>Z_FINISH</tt>.  If so,
          295  +then we're done and we fall out of the outer loop.  We're guaranteed to get <tt>Z_STREAM_END</tt>
          296  +from the last <tt>deflate()</tt> call, since we ran it until the last chunk of input was
          297  +consumed and all of the output was generated.
          298  +<pre><b>
          299  +        /* done when last data in file processed */
          300  +    } while (flush != Z_FINISH);
          301  +    assert(ret == Z_STREAM_END);        /* stream will be complete */
          302  +</b></pre><!-- -->
          303  +The process is complete, but we still need to deallocate the state to avoid a memory leak
          304  +(or rather more like a memory hemorrhage if you didn't do this).  Then
          305  +finally we can return with a happy return value.
          306  +<pre><b>
          307  +    /* clean up and return */
          308  +    (void)deflateEnd(&amp;strm);
          309  +    return Z_OK;
          310  +}
          311  +</b></pre><!-- -->
          312  +Now we do the same thing for decompression in the <tt>inf()</tt> routine. <tt>inf()</tt>
          313  +decompresses what is hopefully a valid <em>zlib</em> stream from the input file and writes the
          314  +uncompressed data to the output file.  Much of the discussion above for <tt>def()</tt>
          315  +applies to <tt>inf()</tt> as well, so the discussion here will focus on the differences between
          316  +the two.
          317  +<pre><b>
          318  +/* Decompress from file source to file dest until stream ends or EOF.
          319  +   inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
          320  +   allocated for processing, Z_DATA_ERROR if the deflate data is
          321  +   invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
          322  +   the version of the library linked do not match, or Z_ERRNO if there
          323  +   is an error reading or writing the files. */
          324  +int inf(FILE *source, FILE *dest)
          325  +{
          326  +</b></pre>
          327  +The local variables have the same functionality as they do for <tt>def()</tt>.  The
          328  +only difference is that there is no <tt>flush</tt> variable, since <tt>inflate()</tt>
          329  +can tell from the <em>zlib</em> stream itself when the stream is complete.
          330  +<pre><b>
          331  +    int ret;
          332  +    unsigned have;
          333  +    z_stream strm;
          334  +    unsigned char in[CHUNK];
          335  +    unsigned char out[CHUNK];
          336  +</b></pre><!-- -->
          337  +The initialization of the state is the same, except that there is no compression level,
          338  +of course, and two more elements of the structure are initialized.  <tt>avail_in</tt>
          339  +and <tt>next_in</tt> must be initialized before calling <tt>inflateInit()</tt>.  This
          340  +is because the application has the option to provide the start of the zlib stream in
          341  +order for <tt>inflateInit()</tt> to have access to information about the compression
          342  +method to aid in memory allocation.  In the current implementation of <em>zlib</em>
          343  +(up through versions 1.2.x), the method-dependent memory allocations are deferred to the first call of
          344  +<tt>inflate()</tt> anyway.  However those fields must be initialized since later versions
          345  +of <em>zlib</em> that provide more compression methods may take advantage of this interface.
          346  +In any case, no decompression is performed by <tt>inflateInit()</tt>, so the
          347  +<tt>avail_out</tt> and <tt>next_out</tt> fields do not need to be initialized before calling.
          348  +<p>
          349  +Here <tt>avail_in</tt> is set to zero and <tt>next_in</tt> is set to <tt>Z_NULL</tt> to
          350  +indicate that no input data is being provided.
          351  +<pre><b>
          352  +    /* allocate inflate state */
          353  +    strm.zalloc = Z_NULL;
          354  +    strm.zfree = Z_NULL;
          355  +    strm.opaque = Z_NULL;
          356  +    strm.avail_in = 0;
          357  +    strm.next_in = Z_NULL;
          358  +    ret = inflateInit(&amp;strm);
          359  +    if (ret != Z_OK)
          360  +        return ret;
          361  +</b></pre><!-- -->
          362  +The outer <tt>do</tt>-loop decompresses input until <tt>inflate()</tt> indicates
          363  +that it has reached the end of the compressed data and has produced all of the uncompressed
          364  +output.  This is in contrast to <tt>def()</tt> which processes all of the input file.
          365  +If end-of-file is reached before the compressed data self-terminates, then the compressed
          366  +data is incomplete and an error is returned.
          367  +<pre><b>
          368  +    /* decompress until deflate stream ends or end of file */
          369  +    do {
          370  +</b></pre>
          371  +We read input data and set the <tt>strm</tt> structure accordingly.  If we've reached the
          372  +end of the input file, then we leave the outer loop and report an error, since the
          373  +compressed data is incomplete.  Note that we may read more data than is eventually consumed
          374  +by <tt>inflate()</tt>, if the input file continues past the <em>zlib</em> stream.
          375  +For applications where <em>zlib</em> streams are embedded in other data, this routine would
          376  +need to be modified to return the unused data, or at least indicate how much of the input
          377  +data was not used, so the application would know where to pick up after the <em>zlib</em> stream.
          378  +<pre><b>
          379  +        strm.avail_in = fread(in, 1, CHUNK, source);
          380  +        if (ferror(source)) {
          381  +            (void)inflateEnd(&amp;strm);
          382  +            return Z_ERRNO;
          383  +        }
          384  +        if (strm.avail_in == 0)
          385  +            break;
          386  +        strm.next_in = in;
          387  +</b></pre><!-- -->
          388  +The inner <tt>do</tt>-loop has the same function it did in <tt>def()</tt>, which is to
          389  +keep calling <tt>inflate()</tt> until has generated all of the output it can with the
          390  +provided input.
          391  +<pre><b>
          392  +        /* run inflate() on input until output buffer not full */
          393  +        do {
          394  +</b></pre>
          395  +Just like in <tt>def()</tt>, the same output space is provided for each call of <tt>inflate()</tt>.
          396  +<pre><b>
          397  +            strm.avail_out = CHUNK;
          398  +            strm.next_out = out;
          399  +</b></pre>
          400  +Now we run the decompression engine itself.  There is no need to adjust the flush parameter, since
          401  +the <em>zlib</em> format is self-terminating. The main difference here is that there are
          402  +return values that we need to pay attention to.  <tt>Z_DATA_ERROR</tt>
          403  +indicates that <tt>inflate()</tt> detected an error in the <em>zlib</em> compressed data format,
          404  +which means that either the data is not a <em>zlib</em> stream to begin with, or that the data was
          405  +corrupted somewhere along the way since it was compressed.  The other error to be processed is
          406  +<tt>Z_MEM_ERROR</tt>, which can occur since memory allocation is deferred until <tt>inflate()</tt>
          407  +needs it, unlike <tt>deflate()</tt>, whose memory is allocated at the start by <tt>deflateInit()</tt>.
          408  +<p>
          409  +Advanced applications may use
          410  +<tt>deflateSetDictionary()</tt> to prime <tt>deflate()</tt> with a set of likely data to improve the
          411  +first 32K or so of compression.  This is noted in the <em>zlib</em> header, so <tt>inflate()</tt>
          412  +requests that that dictionary be provided before it can start to decompress.  Without the dictionary,
          413  +correct decompression is not possible.  For this routine, we have no idea what the dictionary is,
          414  +so the <tt>Z_NEED_DICT</tt> indication is converted to a <tt>Z_DATA_ERROR</tt>.
          415  +<p>
          416  +<tt>inflate()</tt> can also return <tt>Z_STREAM_ERROR</tt>, which should not be possible here,
          417  +but could be checked for as noted above for <tt>def()</tt>.  <tt>Z_BUF_ERROR</tt> does not need to be
          418  +checked for here, for the same reasons noted for <tt>def()</tt>.  <tt>Z_STREAM_END</tt> will be
          419  +checked for later.
          420  +<pre><b>
          421  +            ret = inflate(&amp;strm, Z_NO_FLUSH);
          422  +            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
          423  +            switch (ret) {
          424  +            case Z_NEED_DICT:
          425  +                ret = Z_DATA_ERROR;     /* and fall through */
          426  +            case Z_DATA_ERROR:
          427  +            case Z_MEM_ERROR:
          428  +                (void)inflateEnd(&amp;strm);
          429  +                return ret;
          430  +            }
          431  +</b></pre>
          432  +The output of <tt>inflate()</tt> is handled identically to that of <tt>deflate()</tt>.
          433  +<pre><b>
          434  +            have = CHUNK - strm.avail_out;
          435  +            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
          436  +                (void)inflateEnd(&amp;strm);
          437  +                return Z_ERRNO;
          438  +            }
          439  +</b></pre>
          440  +The inner <tt>do</tt>-loop ends when <tt>inflate()</tt> has no more output as indicated
          441  +by not filling the output buffer, just as for <tt>deflate()</tt>.  In this case, we cannot
          442  +assert that <tt>strm.avail_in</tt> will be zero, since the deflate stream may end before the file
          443  +does.
          444  +<pre><b>
          445  +        } while (strm.avail_out == 0);
          446  +</b></pre><!-- -->
          447  +The outer <tt>do</tt>-loop ends when <tt>inflate()</tt> reports that it has reached the
          448  +end of the input <em>zlib</em> stream, has completed the decompression and integrity
          449  +check, and has provided all of the output.  This is indicated by the <tt>inflate()</tt>
          450  +return value <tt>Z_STREAM_END</tt>.  The inner loop is guaranteed to leave <tt>ret</tt>
          451  +equal to <tt>Z_STREAM_END</tt> if the last chunk of the input file read contained the end
          452  +of the <em>zlib</em> stream.  So if the return value is not <tt>Z_STREAM_END</tt>, the
          453  +loop continues to read more input.
          454  +<pre><b>
          455  +        /* done when inflate() says it's done */
          456  +    } while (ret != Z_STREAM_END);
          457  +</b></pre><!-- -->
          458  +At this point, decompression successfully completed, or we broke out of the loop due to no
          459  +more data being available from the input file.  If the last <tt>inflate()</tt> return value
          460  +is not <tt>Z_STREAM_END</tt>, then the <em>zlib</em> stream was incomplete and a data error
          461  +is returned.  Otherwise, we return with a happy return value.  Of course, <tt>inflateEnd()</tt>
          462  +is called first to avoid a memory leak.
          463  +<pre><b>
          464  +    /* clean up and return */
          465  +    (void)inflateEnd(&amp;strm);
          466  +    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
          467  +}
          468  +</b></pre><!-- -->
          469  +That ends the routines that directly use <em>zlib</em>.  The following routines make this
          470  +a command-line program by running data through the above routines from <tt>stdin</tt> to
          471  +<tt>stdout</tt>, and handling any errors reported by <tt>def()</tt> or <tt>inf()</tt>.
          472  +<p>
          473  +<tt>zerr()</tt> is used to interpret the possible error codes from <tt>def()</tt>
          474  +and <tt>inf()</tt>, as detailed in their comments above, and print out an error message.
          475  +Note that these are only a subset of the possible return values from <tt>deflate()</tt>
          476  +and <tt>inflate()</tt>.
          477  +<pre><b>
          478  +/* report a zlib or i/o error */
          479  +void zerr(int ret)
          480  +{
          481  +    fputs("zpipe: ", stderr);
          482  +    switch (ret) {
          483  +    case Z_ERRNO:
          484  +        if (ferror(stdin))
          485  +            fputs("error reading stdin\n", stderr);
          486  +        if (ferror(stdout))
          487  +            fputs("error writing stdout\n", stderr);
          488  +        break;
          489  +    case Z_STREAM_ERROR:
          490  +        fputs("invalid compression level\n", stderr);
          491  +        break;
          492  +    case Z_DATA_ERROR:
          493  +        fputs("invalid or incomplete deflate data\n", stderr);
          494  +        break;
          495  +    case Z_MEM_ERROR:
          496  +        fputs("out of memory\n", stderr);
          497  +        break;
          498  +    case Z_VERSION_ERROR:
          499  +        fputs("zlib version mismatch!\n", stderr);
          500  +    }
          501  +}
          502  +</b></pre><!-- -->
          503  +Here is the <tt>main()</tt> routine used to test <tt>def()</tt> and <tt>inf()</tt>.  The
          504  +<tt>zpipe</tt> command is simply a compression pipe from <tt>stdin</tt> to <tt>stdout</tt>, if
          505  +no arguments are given, or it is a decompression pipe if <tt>zpipe -d</tt> is used.  If any other
          506  +arguments are provided, no compression or decompression is performed.  Instead a usage
          507  +message is displayed.  Examples are <tt>zpipe < foo.txt > foo.txt.z</tt> to compress, and
          508  +<tt>zpipe -d < foo.txt.z > foo.txt</tt> to decompress.
          509  +<pre><b>
          510  +/* compress or decompress from stdin to stdout */
          511  +int main(int argc, char **argv)
          512  +{
          513  +    int ret;
          514  +
          515  +    /* avoid end-of-line conversions */
          516  +    SET_BINARY_MODE(stdin);
          517  +    SET_BINARY_MODE(stdout);
          518  +
          519  +    /* do compression if no arguments */
          520  +    if (argc == 1) {
          521  +        ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
          522  +        if (ret != Z_OK)
          523  +            zerr(ret);
          524  +        return ret;
          525  +    }
          526  +
          527  +    /* do decompression if -d specified */
          528  +    else if (argc == 2 &amp;&amp; strcmp(argv[1], "-d") == 0) {
          529  +        ret = inf(stdin, stdout);
          530  +        if (ret != Z_OK)
          531  +            zerr(ret);
          532  +        return ret;
          533  +    }
          534  +
          535  +    /* otherwise, report usage */
          536  +    else {
          537  +        fputs("zpipe usage: zpipe [-d] &lt; source &gt; dest\n", stderr);
          538  +        return 1;
          539  +    }
          540  +}
          541  +</b></pre>
          542  +<hr>
          543  +<i>Copyright (c) 2004, 2005 by Mark Adler<br>Last modified 11 December 2005</i>
          544  +</body>
          545  +</html>

Added compat/zlib/examples/zpipe.c.

            1  +/* zpipe.c: example of proper use of zlib's inflate() and deflate()
            2  +   Not copyrighted -- provided to the public domain
            3  +   Version 1.4  11 December 2005  Mark Adler */
            4  +
            5  +/* Version history:
            6  +   1.0  30 Oct 2004  First version
            7  +   1.1   8 Nov 2004  Add void casting for unused return values
            8  +                     Use switch statement for inflate() return values
            9  +   1.2   9 Nov 2004  Add assertions to document zlib guarantees
           10  +   1.3   6 Apr 2005  Remove incorrect assertion in inf()
           11  +   1.4  11 Dec 2005  Add hack to avoid MSDOS end-of-line conversions
           12  +                     Avoid some compiler warnings for input and output buffers
           13  + */
           14  +
           15  +#include <stdio.h>
           16  +#include <string.h>
           17  +#include <assert.h>
           18  +#include "zlib.h"
           19  +
           20  +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
           21  +#  include <fcntl.h>
           22  +#  include <io.h>
           23  +#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
           24  +#else
           25  +#  define SET_BINARY_MODE(file)
           26  +#endif
           27  +
           28  +#define CHUNK 16384
           29  +
           30  +/* Compress from file source to file dest until EOF on source.
           31  +   def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
           32  +   allocated for processing, Z_STREAM_ERROR if an invalid compression
           33  +   level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
           34  +   version of the library linked do not match, or Z_ERRNO if there is
           35  +   an error reading or writing the files. */
           36  +int def(FILE *source, FILE *dest, int level)
           37  +{
           38  +    int ret, flush;
           39  +    unsigned have;
           40  +    z_stream strm;
           41  +    unsigned char in[CHUNK];
           42  +    unsigned char out[CHUNK];
           43  +
           44  +    /* allocate deflate state */
           45  +    strm.zalloc = Z_NULL;
           46  +    strm.zfree = Z_NULL;
           47  +    strm.opaque = Z_NULL;
           48  +    ret = deflateInit(&strm, level);
           49  +    if (ret != Z_OK)
           50  +        return ret;
           51  +
           52  +    /* compress until end of file */
           53  +    do {
           54  +        strm.avail_in = fread(in, 1, CHUNK, source);
           55  +        if (ferror(source)) {
           56  +            (void)deflateEnd(&strm);
           57  +            return Z_ERRNO;
           58  +        }
           59  +        flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
           60  +        strm.next_in = in;
           61  +
           62  +        /* run deflate() on input until output buffer not full, finish
           63  +           compression if all of source has been read in */
           64  +        do {
           65  +            strm.avail_out = CHUNK;
           66  +            strm.next_out = out;
           67  +            ret = deflate(&strm, flush);    /* no bad return value */
           68  +            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
           69  +            have = CHUNK - strm.avail_out;
           70  +            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
           71  +                (void)deflateEnd(&strm);
           72  +                return Z_ERRNO;
           73  +            }
           74  +        } while (strm.avail_out == 0);
           75  +        assert(strm.avail_in == 0);     /* all input will be used */
           76  +
           77  +        /* done when last data in file processed */
           78  +    } while (flush != Z_FINISH);
           79  +    assert(ret == Z_STREAM_END);        /* stream will be complete */
           80  +
           81  +    /* clean up and return */
           82  +    (void)deflateEnd(&strm);
           83  +    return Z_OK;
           84  +}
           85  +
           86  +/* Decompress from file source to file dest until stream ends or EOF.
           87  +   inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
           88  +   allocated for processing, Z_DATA_ERROR if the deflate data is
           89  +   invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
           90  +   the version of the library linked do not match, or Z_ERRNO if there
           91  +   is an error reading or writing the files. */
           92  +int inf(FILE *source, FILE *dest)
           93  +{
           94  +    int ret;
           95  +    unsigned have;
           96  +    z_stream strm;
           97  +    unsigned char in[CHUNK];
           98  +    unsigned char out[CHUNK];
           99  +
          100  +    /* allocate inflate state */
          101  +    strm.zalloc = Z_NULL;
          102  +    strm.zfree = Z_NULL;
          103  +    strm.opaque = Z_NULL;
          104  +    strm.avail_in = 0;
          105  +    strm.next_in = Z_NULL;
          106  +    ret = inflateInit(&strm);
          107  +    if (ret != Z_OK)
          108  +        return ret;
          109  +
          110  +    /* decompress until deflate stream ends or end of file */
          111  +    do {
          112  +        strm.avail_in = fread(in, 1, CHUNK, source);
          113  +        if (ferror(source)) {
          114  +            (void)inflateEnd(&strm);
          115  +            return Z_ERRNO;
          116  +        }
          117  +        if (strm.avail_in == 0)
          118  +            break;
          119  +        strm.next_in = in;
          120  +
          121  +        /* run inflate() on input until output buffer not full */
          122  +        do {
          123  +            strm.avail_out = CHUNK;
          124  +            strm.next_out = out;
          125  +            ret = inflate(&strm, Z_NO_FLUSH);
          126  +            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
          127  +            switch (ret) {
          128  +            case Z_NEED_DICT:
          129  +                ret = Z_DATA_ERROR;     /* and fall through */
          130  +            case Z_DATA_ERROR:
          131  +            case Z_MEM_ERROR:
          132  +                (void)inflateEnd(&strm);
          133  +                return ret;
          134  +            }
          135  +            have = CHUNK - strm.avail_out;
          136  +            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
          137  +                (void)inflateEnd(&strm);
          138  +                return Z_ERRNO;
          139  +            }
          140  +        } while (strm.avail_out == 0);
          141  +
          142  +        /* done when inflate() says it's done */
          143  +    } while (ret != Z_STREAM_END);
          144  +
          145  +    /* clean up and return */
          146  +    (void)inflateEnd(&strm);
          147  +    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
          148  +}
          149  +
          150  +/* report a zlib or i/o error */
          151  +void zerr(int ret)
          152  +{
          153  +    fputs("zpipe: ", stderr);
          154  +    switch (ret) {
          155  +    case Z_ERRNO:
          156  +        if (ferror(stdin))
          157  +            fputs("error reading stdin\n", stderr);
          158  +        if (ferror(stdout))
          159  +            fputs("error writing stdout\n", stderr);
          160  +        break;
          161  +    case Z_STREAM_ERROR:
          162  +        fputs("invalid compression level\n", stderr);
          163  +        break;
          164  +    case Z_DATA_ERROR:
          165  +        fputs("invalid or incomplete deflate data\n", stderr);
          166  +        break;
          167  +    case Z_MEM_ERROR:
          168  +        fputs("out of memory\n", stderr);
          169  +        break;
          170  +    case Z_VERSION_ERROR:
          171  +        fputs("zlib version mismatch!\n", stderr);
          172  +    }
          173  +}
          174  +
          175  +/* compress or decompress from stdin to stdout */
          176  +int main(int argc, char **argv)
          177  +{
          178  +    int ret;
          179  +
          180  +    /* avoid end-of-line conversions */
          181  +    SET_BINARY_MODE(stdin);
          182  +    SET_BINARY_MODE(stdout);
          183  +
          184  +    /* do compression if no arguments */
          185  +    if (argc == 1) {
          186  +        ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
          187  +        if (ret != Z_OK)
          188  +            zerr(ret);
          189  +        return ret;
          190  +    }
          191  +
          192  +    /* do decompression if -d specified */
          193  +    else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
          194  +        ret = inf(stdin, stdout);
          195  +        if (ret != Z_OK)
          196  +            zerr(ret);
          197  +        return ret;
          198  +    }
          199  +
          200  +    /* otherwise, report usage */
          201  +    else {
          202  +        fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);
          203  +        return 1;
          204  +    }
          205  +}

Added compat/zlib/examples/zran.c.

            1  +/* zran.c -- example of zlib/gzip stream indexing and random access
            2  + * Copyright (C) 2005 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  +   Version 1.0  29 May 2005  Mark Adler */
            5  +
            6  +/* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
            7  +   for random access of a compressed file.  A file containing a zlib or gzip
            8  +   stream is provided on the command line.  The compressed stream is decoded in
            9  +   its entirety, and an index built with access points about every SPAN bytes
           10  +   in the uncompressed output.  The compressed file is left open, and can then
           11  +   be read randomly, having to decompress on the average SPAN/2 uncompressed
           12  +   bytes before getting to the desired block of data.
           13  +
           14  +   An access point can be created at the start of any deflate block, by saving
           15  +   the starting file offset and bit of that block, and the 32K bytes of
           16  +   uncompressed data that precede that block.  Also the uncompressed offset of
           17  +   that block is saved to provide a referece for locating a desired starting
           18  +   point in the uncompressed stream.  build_index() works by decompressing the
           19  +   input zlib or gzip stream a block at a time, and at the end of each block
           20  +   deciding if enough uncompressed data has gone by to justify the creation of
           21  +   a new access point.  If so, that point is saved in a data structure that
           22  +   grows as needed to accommodate the points.
           23  +
           24  +   To use the index, an offset in the uncompressed data is provided, for which
           25  +   the latest accees point at or preceding that offset is located in the index.
           26  +   The input file is positioned to the specified location in the index, and if
           27  +   necessary the first few bits of the compressed data is read from the file.
           28  +   inflate is initialized with those bits and the 32K of uncompressed data, and
           29  +   the decompression then proceeds until the desired offset in the file is
           30  +   reached.  Then the decompression continues to read the desired uncompressed
           31  +   data from the file.
           32  +
           33  +   Another approach would be to generate the index on demand.  In that case,
           34  +   requests for random access reads from the compressed data would try to use
           35  +   the index, but if a read far enough past the end of the index is required,
           36  +   then further index entries would be generated and added.
           37  +
           38  +   There is some fair bit of overhead to starting inflation for the random
           39  +   access, mainly copying the 32K byte dictionary.  So if small pieces of the
           40  +   file are being accessed, it would make sense to implement a cache to hold
           41  +   some lookahead and avoid many calls to extract() for small lengths.
           42  +
           43  +   Another way to build an index would be to use inflateCopy().  That would
           44  +   not be constrained to have access points at block boundaries, but requires
           45  +   more memory per access point, and also cannot be saved to file due to the
           46  +   use of pointers in the state.  The approach here allows for storage of the
           47  +   index in a file.
           48  + */
           49  +
           50  +#include <stdio.h>
           51  +#include <stdlib.h>
           52  +#include <string.h>
           53  +#include "zlib.h"
           54  +
           55  +#define local static
           56  +
           57  +#define SPAN 1048576L       /* desired distance between access points */
           58  +#define WINSIZE 32768U      /* sliding window size */
           59  +#define CHUNK 16384         /* file input buffer size */
           60  +
           61  +/* access point entry */
           62  +struct point {
           63  +    off_t out;          /* corresponding offset in uncompressed data */
           64  +    off_t in;           /* offset in input file of first full byte */
           65  +    int bits;           /* number of bits (1-7) from byte at in - 1, or 0 */
           66  +    unsigned char window[WINSIZE];  /* preceding 32K of uncompressed data */
           67  +};
           68  +
           69  +/* access point list */
           70  +struct access {
           71  +    int have;           /* number of list entries filled in */
           72  +    int size;           /* number of list entries allocated */
           73  +    struct point *list; /* allocated list */
           74  +};
           75  +
           76  +/* Deallocate an index built by build_index() */
           77  +local void free_index(struct access *index)
           78  +{
           79  +    if (index != NULL) {
           80  +        free(index->list);
           81  +        free(index);
           82  +    }
           83  +}
           84  +
           85  +/* Add an entry to the access point list.  If out of memory, deallocate the
           86  +   existing list and return NULL. */
           87  +local struct access *addpoint(struct access *index, int bits,
           88  +    off_t in, off_t out, unsigned left, unsigned char *window)
           89  +{
           90  +    struct point *next;
           91  +
           92  +    /* if list is empty, create it (start with eight points) */
           93  +    if (index == NULL) {
           94  +        index = malloc(sizeof(struct access));
           95  +        if (index == NULL) return NULL;
           96  +        index->list = malloc(sizeof(struct point) << 3);
           97  +        if (index->list == NULL) {
           98  +            free(index);
           99  +            return NULL;
          100  +        }
          101  +        index->size = 8;
          102  +        index->have = 0;
          103  +    }
          104  +
          105  +    /* if list is full, make it bigger */
          106  +    else if (index->have == index->size) {
          107  +        index->size <<= 1;
          108  +        next = realloc(index->list, sizeof(struct point) * index->size);
          109  +        if (next == NULL) {
          110  +            free_index(index);
          111  +            return NULL;
          112  +        }
          113  +        index->list = next;
          114  +    }
          115  +
          116  +    /* fill in entry and increment how many we have */
          117  +    next = index->list + index->have;
          118  +    next->bits = bits;
          119  +    next->in = in;
          120  +    next->out = out;
          121  +    if (left)
          122  +        memcpy(next->window, window + WINSIZE - left, left);
          123  +    if (left < WINSIZE)
          124  +        memcpy(next->window + left, window, WINSIZE - left);
          125  +    index->have++;
          126  +
          127  +    /* return list, possibly reallocated */
          128  +    return index;
          129  +}
          130  +
          131  +/* Make one entire pass through the compressed stream and build an index, with
          132  +   access points about every span bytes of uncompressed output -- span is
          133  +   chosen to balance the speed of random access against the memory requirements
          134  +   of the list, about 32K bytes per access point.  Note that data after the end
          135  +   of the first zlib or gzip stream in the file is ignored.  build_index()
          136  +   returns the number of access points on success (>= 1), Z_MEM_ERROR for out
          137  +   of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
          138  +   file read error.  On success, *built points to the resulting index. */
          139  +local int build_index(FILE *in, off_t span, struct access **built)
          140  +{
          141  +    int ret;
          142  +    off_t totin, totout;        /* our own total counters to avoid 4GB limit */
          143  +    off_t last;                 /* totout value of last access point */
          144  +    struct access *index;       /* access points being generated */
          145  +    z_stream strm;
          146  +    unsigned char input[CHUNK];
          147  +    unsigned char window[WINSIZE];
          148  +
          149  +    /* initialize inflate */
          150  +    strm.zalloc = Z_NULL;
          151  +    strm.zfree = Z_NULL;
          152  +    strm.opaque = Z_NULL;
          153  +    strm.avail_in = 0;
          154  +    strm.next_in = Z_NULL;
          155  +    ret = inflateInit2(&strm, 47);      /* automatic zlib or gzip decoding */
          156  +    if (ret != Z_OK)
          157  +        return ret;
          158  +
          159  +    /* inflate the input, maintain a sliding window, and build an index -- this
          160  +       also validates the integrity of the compressed data using the check
          161  +       information at the end of the gzip or zlib stream */
          162  +    totin = totout = last = 0;
          163  +    index = NULL;               /* will be allocated by first addpoint() */
          164  +    strm.avail_out = 0;
          165  +    do {
          166  +        /* get some compressed data from input file */
          167  +        strm.avail_in = fread(input, 1, CHUNK, in);
          168  +        if (ferror(in)) {
          169  +            ret = Z_ERRNO;
          170  +            goto build_index_error;
          171  +        }
          172  +        if (strm.avail_in == 0) {
          173  +            ret = Z_DATA_ERROR;
          174  +            goto build_index_error;
          175  +        }
          176  +        strm.next_in = input;
          177  +
          178  +        /* process all of that, or until end of stream */
          179  +        do {
          180  +            /* reset sliding window if necessary */
          181  +            if (strm.avail_out == 0) {
          182  +                strm.avail_out = WINSIZE;
          183  +                strm.next_out = window;
          184  +            }
          185  +
          186  +            /* inflate until out of input, output, or at end of block --
          187  +               update the total input and output counters */
          188  +            totin += strm.avail_in;
          189  +            totout += strm.avail_out;
          190  +            ret = inflate(&strm, Z_BLOCK);      /* return at end of block */
          191  +            totin -= strm.avail_in;
          192  +            totout -= strm.avail_out;
          193  +            if (ret == Z_NEED_DICT)
          194  +                ret = Z_DATA_ERROR;
          195  +            if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
          196  +                goto build_index_error;
          197  +            if (ret == Z_STREAM_END)
          198  +                break;
          199  +
          200  +            /* if at end of block, consider adding an index entry (note that if
          201  +               data_type indicates an end-of-block, then all of the
          202  +               uncompressed data from that block has been delivered, and none
          203  +               of the compressed data after that block has been consumed,
          204  +               except for up to seven bits) -- the totout == 0 provides an
          205  +               entry point after the zlib or gzip header, and assures that the
          206  +               index always has at least one access point; we avoid creating an
          207  +               access point after the last block by checking bit 6 of data_type
          208  +             */
          209  +            if ((strm.data_type & 128) && !(strm.data_type & 64) &&
          210  +                (totout == 0 || totout - last > span)) {
          211  +                index = addpoint(index, strm.data_type & 7, totin,
          212  +                                 totout, strm.avail_out, window);
          213  +                if (index == NULL) {
          214  +                    ret = Z_MEM_ERROR;
          215  +                    goto build_index_error;
          216  +                }
          217  +                last = totout;
          218  +            }
          219  +        } while (strm.avail_in != 0);
          220  +    } while (ret != Z_STREAM_END);
          221  +
          222  +    /* clean up and return index (release unused entries in list) */
          223  +    (void)inflateEnd(&strm);
          224  +    index = realloc(index, sizeof(struct point) * index->have);
          225  +    index->size = index->have;
          226  +    *built = index;
          227  +    return index->size;
          228  +
          229  +    /* return error */
          230  +  build_index_error:
          231  +    (void)inflateEnd(&strm);
          232  +    if (index != NULL)
          233  +        free_index(index);
          234  +    return ret;
          235  +}
          236  +
          237  +/* Use the index to read len bytes from offset into buf, return bytes read or
          238  +   negative for error (Z_DATA_ERROR or Z_MEM_ERROR).  If data is requested past
          239  +   the end of the uncompressed data, then extract() will return a value less
          240  +   than len, indicating how much as actually read into buf.  This function
          241  +   should not return a data error unless the file was modified since the index
          242  +   was generated.  extract() may also return Z_ERRNO if there is an error on
          243  +   reading or seeking the input file. */
          244  +local int extract(FILE *in, struct access *index, off_t offset,
          245  +                  unsigned char *buf, int len)
          246  +{
          247  +    int ret, skip;
          248  +    z_stream strm;
          249  +    struct point *here;
          250  +    unsigned char input[CHUNK];
          251  +    unsigned char discard[WINSIZE];
          252  +
          253  +    /* proceed only if something reasonable to do */
          254  +    if (len < 0)
          255  +        return 0;
          256  +
          257  +    /* find where in stream to start */
          258  +    here = index->list;
          259  +    ret = index->have;
          260  +    while (--ret && here[1].out <= offset)
          261  +        here++;
          262  +
          263  +    /* initialize file and inflate state to start there */
          264  +    strm.zalloc = Z_NULL;
          265  +    strm.zfree = Z_NULL;
          266  +    strm.opaque = Z_NULL;
          267  +    strm.avail_in = 0;
          268  +    strm.next_in = Z_NULL;
          269  +    ret = inflateInit2(&strm, -15);         /* raw inflate */
          270  +    if (ret != Z_OK)
          271  +        return ret;
          272  +    ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
          273  +    if (ret == -1)
          274  +        goto extract_ret;
          275  +    if (here->bits) {
          276  +        ret = getc(in);
          277  +        if (ret == -1) {
          278  +            ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
          279  +            goto extract_ret;
          280  +        }
          281  +        (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
          282  +    }
          283  +    (void)inflateSetDictionary(&strm, here->window, WINSIZE);
          284  +
          285  +    /* skip uncompressed bytes until offset reached, then satisfy request */
          286  +    offset -= here->out;
          287  +    strm.avail_in = 0;
          288  +    skip = 1;                               /* while skipping to offset */
          289  +    do {
          290  +        /* define where to put uncompressed data, and how much */
          291  +        if (offset == 0 && skip) {          /* at offset now */
          292  +            strm.avail_out = len;
          293  +            strm.next_out = buf;
          294  +            skip = 0;                       /* only do this once */
          295  +        }
          296  +        if (offset > WINSIZE) {             /* skip WINSIZE bytes */
          297  +            strm.avail_out = WINSIZE;
          298  +            strm.next_out = discard;
          299  +            offset -= WINSIZE;
          300  +        }
          301  +        else if (offset != 0) {             /* last skip */
          302  +            strm.avail_out = (unsigned)offset;
          303  +            strm.next_out = discard;
          304  +            offset = 0;
          305  +        }
          306  +
          307  +        /* uncompress until avail_out filled, or end of stream */
          308  +        do {
          309  +            if (strm.avail_in == 0) {
          310  +                strm.avail_in = fread(input, 1, CHUNK, in);
          311  +                if (ferror(in)) {
          312  +                    ret = Z_ERRNO;
          313  +                    goto extract_ret;
          314  +                }
          315  +                if (strm.avail_in == 0) {
          316  +                    ret = Z_DATA_ERROR;
          317  +                    goto extract_ret;
          318  +                }
          319  +                strm.next_in = input;
          320  +            }
          321  +            ret = inflate(&strm, Z_NO_FLUSH);       /* normal inflate */
          322  +            if (ret == Z_NEED_DICT)
          323  +                ret = Z_DATA_ERROR;
          324  +            if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
          325  +                goto extract_ret;
          326  +            if (ret == Z_STREAM_END)
          327  +                break;
          328  +        } while (strm.avail_out != 0);
          329  +
          330  +        /* if reach end of stream, then don't keep trying to get more */
          331  +        if (ret == Z_STREAM_END)
          332  +            break;
          333  +
          334  +        /* do until offset reached and requested data read, or stream ends */
          335  +    } while (skip);
          336  +
          337  +    /* compute number of uncompressed bytes read after offset */
          338  +    ret = skip ? 0 : len - strm.avail_out;
          339  +
          340  +    /* clean up and return bytes read or error */
          341  +  extract_ret:
          342  +    (void)inflateEnd(&strm);
          343  +    return ret;
          344  +}
          345  +
          346  +/* Demonstrate the use of build_index() and extract() by processing the file
          347  +   provided on the command line, and the extracting 16K from about 2/3rds of
          348  +   the way through the uncompressed output, and writing that to stdout. */
          349  +int main(int argc, char **argv)
          350  +{
          351  +    int len;
          352  +    off_t offset;
          353  +    FILE *in;
          354  +    struct access *index = NULL;
          355  +    unsigned char buf[CHUNK];
          356  +
          357  +    /* open input file */
          358  +    if (argc != 2) {
          359  +        fprintf(stderr, "usage: zran file.gz\n");
          360  +        return 1;
          361  +    }
          362  +    in = fopen(argv[1], "rb");
          363  +    if (in == NULL) {
          364  +        fprintf(stderr, "zran: could not open %s for reading\n", argv[1]);
          365  +        return 1;
          366  +    }
          367  +
          368  +    /* build index */
          369  +    len = build_index(in, SPAN, &index);
          370  +    if (len < 0) {
          371  +        fclose(in);
          372  +        switch (len) {
          373  +        case Z_MEM_ERROR:
          374  +            fprintf(stderr, "zran: out of memory\n");
          375  +            break;
          376  +        case Z_DATA_ERROR:
          377  +            fprintf(stderr, "zran: compressed data error in %s\n", argv[1]);
          378  +            break;
          379  +        case Z_ERRNO:
          380  +            fprintf(stderr, "zran: read error on %s\n", argv[1]);
          381  +            break;
          382  +        default:
          383  +            fprintf(stderr, "zran: error %d while building index\n", len);
          384  +        }
          385  +        return 1;
          386  +    }
          387  +    fprintf(stderr, "zran: built index with %d access points\n", len);
          388  +
          389  +    /* use index by reading some bytes from an arbitrary offset */
          390  +    offset = (index->list[index->have - 1].out << 1) / 3;
          391  +    len = extract(in, index, offset, buf, CHUNK);
          392  +    if (len < 0)
          393  +        fprintf(stderr, "zran: extraction failed: %s error\n",
          394  +                len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
          395  +    else {
          396  +        fwrite(buf, 1, len, stdout);
          397  +        fprintf(stderr, "zran: extracted %d bytes at %llu\n", len, offset);
          398  +    }
          399  +
          400  +    /* clean up and exit */
          401  +    free_index(index);
          402  +    fclose(in);
          403  +    return 0;
          404  +}

Added compat/zlib/gzclose.c.

            1  +/* gzclose.c -- zlib gzclose() function
            2  + * Copyright (C) 2004, 2010 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "gzguts.h"
            7  +
            8  +/* gzclose() is in a separate file so that it is linked in only if it is used.
            9  +   That way the other gzclose functions can be used instead to avoid linking in
           10  +   unneeded compression or decompression routines. */
           11  +int ZEXPORT gzclose(file)
           12  +    gzFile file;
           13  +{
           14  +#ifndef NO_GZCOMPRESS
           15  +    gz_statep state;
           16  +
           17  +    if (file == NULL)
           18  +        return Z_STREAM_ERROR;
           19  +    state = (gz_statep)file;
           20  +
           21  +    return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file);
           22  +#else
           23  +    return gzclose_r(file);
           24  +#endif
           25  +}

Added compat/zlib/gzguts.h.

            1  +/* gzguts.h -- zlib internal header definitions for gz* operations
            2  + * Copyright (C) 2004, 2005, 2010, 2011, 2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#ifdef _LARGEFILE64_SOURCE
            7  +#  ifndef _LARGEFILE_SOURCE
            8  +#    define _LARGEFILE_SOURCE 1
            9  +#  endif
           10  +#  ifdef _FILE_OFFSET_BITS
           11  +#    undef _FILE_OFFSET_BITS
           12  +#  endif
           13  +#endif
           14  +
           15  +#ifdef HAVE_HIDDEN
           16  +#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
           17  +#else
           18  +#  define ZLIB_INTERNAL
           19  +#endif
           20  +
           21  +#include <stdio.h>
           22  +#include "zlib.h"
           23  +#ifdef STDC
           24  +#  include <string.h>
           25  +#  include <stdlib.h>
           26  +#  include <limits.h>
           27  +#endif
           28  +#include <fcntl.h>
           29  +
           30  +#ifdef _WIN32
           31  +#  include <stddef.h>
           32  +#endif
           33  +
           34  +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
           35  +#  include <io.h>
           36  +#endif
           37  +
           38  +#ifdef NO_DEFLATE       /* for compatibility with old definition */
           39  +#  define NO_GZCOMPRESS
           40  +#endif
           41  +
           42  +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
           43  +#  ifndef HAVE_VSNPRINTF
           44  +#    define HAVE_VSNPRINTF
           45  +#  endif
           46  +#endif
           47  +
           48  +#if defined(__CYGWIN__)
           49  +#  ifndef HAVE_VSNPRINTF
           50  +#    define HAVE_VSNPRINTF
           51  +#  endif
           52  +#endif
           53  +
           54  +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410)
           55  +#  ifndef HAVE_VSNPRINTF
           56  +#    define HAVE_VSNPRINTF
           57  +#  endif
           58  +#endif
           59  +
           60  +#ifndef HAVE_VSNPRINTF
           61  +#  ifdef MSDOS
           62  +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
           63  + but for now we just assume it doesn't. */
           64  +#    define NO_vsnprintf
           65  +#  endif
           66  +#  ifdef __TURBOC__
           67  +#    define NO_vsnprintf
           68  +#  endif
           69  +#  ifdef WIN32
           70  +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
           71  +#    if !defined(vsnprintf) && !defined(NO_vsnprintf)
           72  +#      if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
           73  +#         define vsnprintf _vsnprintf
           74  +#      endif
           75  +#    endif
           76  +#  endif
           77  +#  ifdef __SASC
           78  +#    define NO_vsnprintf
           79  +#  endif
           80  +#  ifdef VMS
           81  +#    define NO_vsnprintf
           82  +#  endif
           83  +#  ifdef __OS400__
           84  +#    define NO_vsnprintf
           85  +#  endif
           86  +#  ifdef __MVS__
           87  +#    define NO_vsnprintf
           88  +#  endif
           89  +#endif
           90  +
           91  +#ifndef local
           92  +#  define local static
           93  +#endif
           94  +/* compile with -Dlocal if your debugger can't find static symbols */
           95  +
           96  +/* gz* functions always use library allocation functions */
           97  +#ifndef STDC
           98  +  extern voidp  malloc OF((uInt size));
           99  +  extern void   free   OF((voidpf ptr));
          100  +#endif
          101  +
          102  +/* get errno and strerror definition */
          103  +#if defined UNDER_CE
          104  +#  include <windows.h>
          105  +#  define zstrerror() gz_strwinerror((DWORD)GetLastError())
          106  +#else
          107  +#  ifndef NO_STRERROR
          108  +#    include <errno.h>
          109  +#    define zstrerror() strerror(errno)
          110  +#  else
          111  +#    define zstrerror() "stdio error (consult errno)"
          112  +#  endif
          113  +#endif
          114  +
          115  +/* provide prototypes for these when building zlib without LFS */
          116  +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
          117  +    ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
          118  +    ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
          119  +    ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
          120  +    ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
          121  +#endif
          122  +
          123  +/* default memLevel */
          124  +#if MAX_MEM_LEVEL >= 8
          125  +#  define DEF_MEM_LEVEL 8
          126  +#else
          127  +#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
          128  +#endif
          129  +
          130  +/* default i/o buffer size -- double this for output when reading */
          131  +#define GZBUFSIZE 8192
          132  +
          133  +/* gzip modes, also provide a little integrity check on the passed structure */
          134  +#define GZ_NONE 0
          135  +#define GZ_READ 7247
          136  +#define GZ_WRITE 31153
          137  +#define GZ_APPEND 1     /* mode set to GZ_WRITE after the file is opened */
          138  +
          139  +/* values for gz_state how */
          140  +#define LOOK 0      /* look for a gzip header */
          141  +#define COPY 1      /* copy input directly */
          142  +#define GZIP 2      /* decompress a gzip stream */
          143  +
          144  +/* internal gzip file state data structure */
          145  +typedef struct {
          146  +        /* exposed contents for gzgetc() macro */
          147  +    struct gzFile_s x;      /* "x" for exposed */
          148  +                            /* x.have: number of bytes available at x.next */
          149  +                            /* x.next: next output data to deliver or write */
          150  +                            /* x.pos: current position in uncompressed data */
          151  +        /* used for both reading and writing */
          152  +    int mode;               /* see gzip modes above */
          153  +    int fd;                 /* file descriptor */
          154  +    char *path;             /* path or fd for error messages */
          155  +    unsigned size;          /* buffer size, zero if not allocated yet */
          156  +    unsigned want;          /* requested buffer size, default is GZBUFSIZE */
          157  +    unsigned char *in;      /* input buffer */
          158  +    unsigned char *out;     /* output buffer (double-sized when reading) */
          159  +    int direct;             /* 0 if processing gzip, 1 if transparent */
          160  +        /* just for reading */
          161  +    int how;                /* 0: get header, 1: copy, 2: decompress */
          162  +    z_off64_t start;        /* where the gzip data started, for rewinding */
          163  +    int eof;                /* true if end of input file reached */
          164  +    int past;               /* true if read requested past end */
          165  +        /* just for writing */
          166  +    int level;              /* compression level */
          167  +    int strategy;           /* compression strategy */
          168  +        /* seek request */
          169  +    z_off64_t skip;         /* amount to skip (already rewound if backwards) */
          170  +    int seek;               /* true if seek request pending */
          171  +        /* error information */
          172  +    int err;                /* error code */
          173  +    char *msg;              /* error message */
          174  +        /* zlib inflate or deflate stream */
          175  +    z_stream strm;          /* stream structure in-place (not a pointer) */
          176  +} gz_state;
          177  +typedef gz_state FAR *gz_statep;
          178  +
          179  +/* shared functions */
          180  +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
          181  +#if defined UNDER_CE
          182  +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
          183  +#endif
          184  +
          185  +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
          186  +   value -- needed when comparing unsigned to z_off64_t, which is signed
          187  +   (possible z_off64_t types off_t, off64_t, and long are all signed) */
          188  +#ifdef INT_MAX
          189  +#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
          190  +#else
          191  +unsigned ZLIB_INTERNAL gz_intmax OF((void));
          192  +#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
          193  +#endif

Added compat/zlib/gzlib.c.

            1  +/* gzlib.c -- zlib functions common to reading and writing gzip files
            2  + * Copyright (C) 2004, 2010, 2011, 2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "gzguts.h"
            7  +
            8  +#if defined(_WIN32) && !defined(__BORLANDC__)
            9  +#  define LSEEK _lseeki64
           10  +#else
           11  +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
           12  +#  define LSEEK lseek64
           13  +#else
           14  +#  define LSEEK lseek
           15  +#endif
           16  +#endif
           17  +
           18  +/* Local functions */
           19  +local void gz_reset OF((gz_statep));
           20  +local gzFile gz_open OF((const void *, int, const char *));
           21  +
           22  +#if defined UNDER_CE
           23  +
           24  +/* Map the Windows error number in ERROR to a locale-dependent error message
           25  +   string and return a pointer to it.  Typically, the values for ERROR come
           26  +   from GetLastError.
           27  +
           28  +   The string pointed to shall not be modified by the application, but may be
           29  +   overwritten by a subsequent call to gz_strwinerror
           30  +
           31  +   The gz_strwinerror function does not change the current setting of
           32  +   GetLastError. */
           33  +char ZLIB_INTERNAL *gz_strwinerror (error)
           34  +     DWORD error;
           35  +{
           36  +    static char buf[1024];
           37  +
           38  +    wchar_t *msgbuf;
           39  +    DWORD lasterr = GetLastError();
           40  +    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
           41  +        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
           42  +        NULL,
           43  +        error,
           44  +        0, /* Default language */
           45  +        (LPVOID)&msgbuf,
           46  +        0,
           47  +        NULL);
           48  +    if (chars != 0) {
           49  +        /* If there is an \r\n appended, zap it.  */
           50  +        if (chars >= 2
           51  +            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
           52  +            chars -= 2;
           53  +            msgbuf[chars] = 0;
           54  +        }
           55  +
           56  +        if (chars > sizeof (buf) - 1) {
           57  +            chars = sizeof (buf) - 1;
           58  +            msgbuf[chars] = 0;
           59  +        }
           60  +
           61  +        wcstombs(buf, msgbuf, chars + 1);
           62  +        LocalFree(msgbuf);
           63  +    }
           64  +    else {
           65  +        sprintf(buf, "unknown win32 error (%ld)", error);
           66  +    }
           67  +
           68  +    SetLastError(lasterr);
           69  +    return buf;
           70  +}
           71  +
           72  +#endif /* UNDER_CE */
           73  +
           74  +/* Reset gzip file state */
           75  +local void gz_reset(state)
           76  +    gz_statep state;
           77  +{
           78  +    state->x.have = 0;              /* no output data available */
           79  +    if (state->mode == GZ_READ) {   /* for reading ... */
           80  +        state->eof = 0;             /* not at end of file */
           81  +        state->past = 0;            /* have not read past end yet */
           82  +        state->how = LOOK;          /* look for gzip header */
           83  +    }
           84  +    state->seek = 0;                /* no seek request pending */
           85  +    gz_error(state, Z_OK, NULL);    /* clear error */
           86  +    state->x.pos = 0;               /* no uncompressed data yet */
           87  +    state->strm.avail_in = 0;       /* no input data yet */
           88  +}
           89  +
           90  +/* Open a gzip file either by name or file descriptor. */
           91  +local gzFile gz_open(path, fd, mode)
           92  +    const void *path;
           93  +    int fd;
           94  +    const char *mode;
           95  +{
           96  +    gz_statep state;
           97  +    size_t len;
           98  +    int oflag;
           99  +#ifdef O_CLOEXEC
          100  +    int cloexec = 0;
          101  +#endif
          102  +#ifdef O_EXCL
          103  +    int exclusive = 0;
          104  +#endif
          105  +
          106  +    /* check input */
          107  +    if (path == NULL)
          108  +        return NULL;
          109  +
          110  +    /* allocate gzFile structure to return */
          111  +    state = malloc(sizeof(gz_state));
          112  +    if (state == NULL)
          113  +        return NULL;
          114  +    state->size = 0;            /* no buffers allocated yet */
          115  +    state->want = GZBUFSIZE;    /* requested buffer size */
          116  +    state->msg = NULL;          /* no error message yet */
          117  +
          118  +    /* interpret mode */
          119  +    state->mode = GZ_NONE;
          120  +    state->level = Z_DEFAULT_COMPRESSION;
          121  +    state->strategy = Z_DEFAULT_STRATEGY;
          122  +    state->direct = 0;
          123  +    while (*mode) {
          124  +        if (*mode >= '0' && *mode <= '9')
          125  +            state->level = *mode - '0';
          126  +        else
          127  +            switch (*mode) {
          128  +            case 'r':
          129  +                state->mode = GZ_READ;
          130  +                break;
          131  +#ifndef NO_GZCOMPRESS
          132  +            case 'w':
          133  +                state->mode = GZ_WRITE;
          134  +                break;
          135  +            case 'a':
          136  +                state->mode = GZ_APPEND;
          137  +                break;
          138  +#endif
          139  +            case '+':       /* can't read and write at the same time */
          140  +                free(state);
          141  +                return NULL;
          142  +            case 'b':       /* ignore -- will request binary anyway */
          143  +                break;
          144  +#ifdef O_CLOEXEC
          145  +            case 'e':
          146  +                cloexec = 1;
          147  +                break;
          148  +#endif
          149  +#ifdef O_EXCL
          150  +            case 'x':
          151  +                exclusive = 1;
          152  +                break;
          153  +#endif
          154  +            case 'f':
          155  +                state->strategy = Z_FILTERED;
          156  +                break;
          157  +            case 'h':
          158  +                state->strategy = Z_HUFFMAN_ONLY;
          159  +                break;
          160  +            case 'R':
          161  +                state->strategy = Z_RLE;
          162  +                break;
          163  +            case 'F':
          164  +                state->strategy = Z_FIXED;
          165  +            case 'T':
          166  +                state->direct = 1;
          167  +            default:        /* could consider as an error, but just ignore */
          168  +                ;
          169  +            }
          170  +        mode++;
          171  +    }
          172  +
          173  +    /* must provide an "r", "w", or "a" */
          174  +    if (state->mode == GZ_NONE) {
          175  +        free(state);
          176  +        return NULL;
          177  +    }
          178  +
          179  +    /* can't force transparent read */
          180  +    if (state->mode == GZ_READ) {
          181  +        if (state->direct) {
          182  +            free(state);
          183  +            return NULL;
          184  +        }
          185  +        state->direct = 1;      /* for empty file */
          186  +    }
          187  +
          188  +    /* save the path name for error messages */
          189  +#ifdef _WIN32
          190  +    if (fd == -2) {
          191  +        len = wcstombs(NULL, path, 0);
          192  +        if (len == (size_t)-1)
          193  +            len = 0;
          194  +    }
          195  +    else
          196  +#endif
          197  +        len = strlen(path);
          198  +    state->path = malloc(len + 1);
          199  +    if (state->path == NULL) {
          200  +        free(state);
          201  +        return NULL;
          202  +    }
          203  +#ifdef _WIN32
          204  +    if (fd == -2)
          205  +        if (len)
          206  +            wcstombs(state->path, path, len + 1);
          207  +        else
          208  +            *(state->path) = 0;
          209  +    else
          210  +#endif
          211  +        strcpy(state->path, path);
          212  +
          213  +    /* compute the flags for open() */
          214  +    oflag =
          215  +#ifdef O_LARGEFILE
          216  +        O_LARGEFILE |
          217  +#endif
          218  +#ifdef O_BINARY
          219  +        O_BINARY |
          220  +#endif
          221  +#ifdef O_CLOEXEC
          222  +        (cloexec ? O_CLOEXEC : 0) |
          223  +#endif
          224  +        (state->mode == GZ_READ ?
          225  +         O_RDONLY :
          226  +         (O_WRONLY | O_CREAT |
          227  +#ifdef O_EXCL
          228  +          (exclusive ? O_EXCL : 0) |
          229  +#endif
          230  +          (state->mode == GZ_WRITE ?
          231  +           O_TRUNC :
          232  +           O_APPEND)));
          233  +
          234  +    /* open the file with the appropriate flags (or just use fd) */
          235  +    state->fd = fd > -1 ? fd : (
          236  +#ifdef _WIN32
          237  +        fd == -2 ? _wopen(path, oflag, 0666) :
          238  +#endif
          239  +        open(path, oflag, 0666));
          240  +    if (state->fd == -1) {
          241  +        free(state->path);
          242  +        free(state);
          243  +        return NULL;
          244  +    }
          245  +    if (state->mode == GZ_APPEND)
          246  +        state->mode = GZ_WRITE;         /* simplify later checks */
          247  +
          248  +    /* save the current position for rewinding (only if reading) */
          249  +    if (state->mode == GZ_READ) {
          250  +        state->start = LSEEK(state->fd, 0, SEEK_CUR);
          251  +        if (state->start == -1) state->start = 0;
          252  +    }
          253  +
          254  +    /* initialize stream */
          255  +    gz_reset(state);
          256  +
          257  +    /* return stream */
          258  +    return (gzFile)state;
          259  +}
          260  +
          261  +/* -- see zlib.h -- */
          262  +gzFile ZEXPORT gzopen(path, mode)
          263  +    const char *path;
          264  +    const char *mode;
          265  +{
          266  +    return gz_open(path, -1, mode);
          267  +}
          268  +
          269  +/* -- see zlib.h -- */
          270  +gzFile ZEXPORT gzopen64(path, mode)
          271  +    const char *path;
          272  +    const char *mode;
          273  +{
          274  +    return gz_open(path, -1, mode);
          275  +}
          276  +
          277  +/* -- see zlib.h -- */
          278  +gzFile ZEXPORT gzdopen(fd, mode)
          279  +    int fd;
          280  +    const char *mode;
          281  +{
          282  +    char *path;         /* identifier for error messages */
          283  +    gzFile gz;
          284  +
          285  +    if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL)
          286  +        return NULL;
          287  +    sprintf(path, "<fd:%d>", fd);   /* for debugging */
          288  +    gz = gz_open(path, fd, mode);
          289  +    free(path);
          290  +    return gz;
          291  +}
          292  +
          293  +/* -- see zlib.h -- */
          294  +#ifdef _WIN32
          295  +gzFile ZEXPORT gzopen_w(path, mode)
          296  +    const wchar_t *path;
          297  +    const char *mode;
          298  +{
          299  +    return gz_open(path, -2, mode);
          300  +}
          301  +#endif
          302  +
          303  +/* -- see zlib.h -- */
          304  +int ZEXPORT gzbuffer(file, size)
          305  +    gzFile file;
          306  +    unsigned size;
          307  +{
          308  +    gz_statep state;
          309  +
          310  +    /* get internal structure and check integrity */
          311  +    if (file == NULL)
          312  +        return -1;
          313  +    state = (gz_statep)file;
          314  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          315  +        return -1;
          316  +
          317  +    /* make sure we haven't already allocated memory */
          318  +    if (state->size != 0)
          319  +        return -1;
          320  +
          321  +    /* check and set requested size */
          322  +    if (size < 2)
          323  +        size = 2;               /* need two bytes to check magic header */
          324  +    state->want = size;
          325  +    return 0;
          326  +}
          327  +
          328  +/* -- see zlib.h -- */
          329  +int ZEXPORT gzrewind(file)
          330  +    gzFile file;
          331  +{
          332  +    gz_statep state;
          333  +
          334  +    /* get internal structure */
          335  +    if (file == NULL)
          336  +        return -1;
          337  +    state = (gz_statep)file;
          338  +
          339  +    /* check that we're reading and that there's no error */
          340  +    if (state->mode != GZ_READ ||
          341  +            (state->err != Z_OK && state->err != Z_BUF_ERROR))
          342  +        return -1;
          343  +
          344  +    /* back up and start over */
          345  +    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
          346  +        return -1;
          347  +    gz_reset(state);
          348  +    return 0;
          349  +}
          350  +
          351  +/* -- see zlib.h -- */
          352  +z_off64_t ZEXPORT gzseek64(file, offset, whence)
          353  +    gzFile file;
          354  +    z_off64_t offset;
          355  +    int whence;
          356  +{
          357  +    unsigned n;
          358  +    z_off64_t ret;
          359  +    gz_statep state;
          360  +
          361  +    /* get internal structure and check integrity */
          362  +    if (file == NULL)
          363  +        return -1;
          364  +    state = (gz_statep)file;
          365  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          366  +        return -1;
          367  +
          368  +    /* check that there's no error */
          369  +    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
          370  +        return -1;
          371  +
          372  +    /* can only seek from start or relative to current position */
          373  +    if (whence != SEEK_SET && whence != SEEK_CUR)
          374  +        return -1;
          375  +
          376  +    /* normalize offset to a SEEK_CUR specification */
          377  +    if (whence == SEEK_SET)
          378  +        offset -= state->x.pos;
          379  +    else if (state->seek)
          380  +        offset += state->skip;
          381  +    state->seek = 0;
          382  +
          383  +    /* if within raw area while reading, just go there */
          384  +    if (state->mode == GZ_READ && state->how == COPY &&
          385  +            state->x.pos + offset >= 0) {
          386  +        ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);
          387  +        if (ret == -1)
          388  +            return -1;
          389  +        state->x.have = 0;
          390  +        state->eof = 0;
          391  +        state->past = 0;
          392  +        state->seek = 0;
          393  +        gz_error(state, Z_OK, NULL);
          394  +        state->strm.avail_in = 0;
          395  +        state->x.pos += offset;
          396  +        return state->x.pos;
          397  +    }
          398  +
          399  +    /* calculate skip amount, rewinding if needed for back seek when reading */
          400  +    if (offset < 0) {
          401  +        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
          402  +            return -1;
          403  +        offset += state->x.pos;
          404  +        if (offset < 0)                     /* before start of file! */
          405  +            return -1;
          406  +        if (gzrewind(file) == -1)           /* rewind, then skip to offset */
          407  +            return -1;
          408  +    }
          409  +
          410  +    /* if reading, skip what's in output buffer (one less gzgetc() check) */
          411  +    if (state->mode == GZ_READ) {
          412  +        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
          413  +            (unsigned)offset : state->x.have;
          414  +        state->x.have -= n;
          415  +        state->x.next += n;
          416  +        state->x.pos += n;
          417  +        offset -= n;
          418  +    }
          419  +
          420  +    /* request skip (if not zero) */
          421  +    if (offset) {
          422  +        state->seek = 1;
          423  +        state->skip = offset;
          424  +    }
          425  +    return state->x.pos + offset;
          426  +}
          427  +
          428  +/* -- see zlib.h -- */
          429  +z_off_t ZEXPORT gzseek(file, offset, whence)
          430  +    gzFile file;
          431  +    z_off_t offset;
          432  +    int whence;
          433  +{
          434  +    z_off64_t ret;
          435  +
          436  +    ret = gzseek64(file, (z_off64_t)offset, whence);
          437  +    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
          438  +}
          439  +
          440  +/* -- see zlib.h -- */
          441  +z_off64_t ZEXPORT gztell64(file)
          442  +    gzFile file;
          443  +{
          444  +    gz_statep state;
          445  +
          446  +    /* get internal structure and check integrity */
          447  +    if (file == NULL)
          448  +        return -1;
          449  +    state = (gz_statep)file;
          450  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          451  +        return -1;
          452  +
          453  +    /* return position */
          454  +    return state->x.pos + (state->seek ? state->skip : 0);
          455  +}
          456  +
          457  +/* -- see zlib.h -- */
          458  +z_off_t ZEXPORT gztell(file)
          459  +    gzFile file;
          460  +{
          461  +    z_off64_t ret;
          462  +
          463  +    ret = gztell64(file);
          464  +    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
          465  +}
          466  +
          467  +/* -- see zlib.h -- */
          468  +z_off64_t ZEXPORT gzoffset64(file)
          469  +    gzFile file;
          470  +{
          471  +    z_off64_t offset;
          472  +    gz_statep state;
          473  +
          474  +    /* get internal structure and check integrity */
          475  +    if (file == NULL)
          476  +        return -1;
          477  +    state = (gz_statep)file;
          478  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          479  +        return -1;
          480  +
          481  +    /* compute and return effective offset in file */
          482  +    offset = LSEEK(state->fd, 0, SEEK_CUR);
          483  +    if (offset == -1)
          484  +        return -1;
          485  +    if (state->mode == GZ_READ)             /* reading */
          486  +        offset -= state->strm.avail_in;     /* don't count buffered input */
          487  +    return offset;
          488  +}
          489  +
          490  +/* -- see zlib.h -- */
          491  +z_off_t ZEXPORT gzoffset(file)
          492  +    gzFile file;
          493  +{
          494  +    z_off64_t ret;
          495  +
          496  +    ret = gzoffset64(file);
          497  +    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
          498  +}
          499  +
          500  +/* -- see zlib.h -- */
          501  +int ZEXPORT gzeof(file)
          502  +    gzFile file;
          503  +{
          504  +    gz_statep state;
          505  +
          506  +    /* get internal structure and check integrity */
          507  +    if (file == NULL)
          508  +        return 0;
          509  +    state = (gz_statep)file;
          510  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          511  +        return 0;
          512  +
          513  +    /* return end-of-file state */
          514  +    return state->mode == GZ_READ ? state->past : 0;
          515  +}
          516  +
          517  +/* -- see zlib.h -- */
          518  +const char * ZEXPORT gzerror(file, errnum)
          519  +    gzFile file;
          520  +    int *errnum;
          521  +{
          522  +    gz_statep state;
          523  +
          524  +    /* get internal structure and check integrity */
          525  +    if (file == NULL)
          526  +        return NULL;
          527  +    state = (gz_statep)file;
          528  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          529  +        return NULL;
          530  +
          531  +    /* return error information */
          532  +    if (errnum != NULL)
          533  +        *errnum = state->err;
          534  +    return state->msg == NULL ? "" : state->msg;
          535  +}
          536  +
          537  +/* -- see zlib.h -- */
          538  +void ZEXPORT gzclearerr(file)
          539  +    gzFile file;
          540  +{
          541  +    gz_statep state;
          542  +
          543  +    /* get internal structure and check integrity */
          544  +    if (file == NULL)
          545  +        return;
          546  +    state = (gz_statep)file;
          547  +    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
          548  +        return;
          549  +
          550  +    /* clear error and end-of-file */
          551  +    if (state->mode == GZ_READ) {
          552  +        state->eof = 0;
          553  +        state->past = 0;
          554  +    }
          555  +    gz_error(state, Z_OK, NULL);
          556  +}
          557  +
          558  +/* Create an error message in allocated memory and set state->err and
          559  +   state->msg accordingly.  Free any previous error message already there.  Do
          560  +   not try to free or allocate space if the error is Z_MEM_ERROR (out of
          561  +   memory).  Simply save the error message as a static string.  If there is an
          562  +   allocation failure constructing the error message, then convert the error to
          563  +   out of memory. */
          564  +void ZLIB_INTERNAL gz_error(state, err, msg)
          565  +    gz_statep state;
          566  +    int err;
          567  +    const char *msg;
          568  +{
          569  +    /* free previously allocated message and clear */
          570  +    if (state->msg != NULL) {
          571  +        if (state->err != Z_MEM_ERROR)
          572  +            free(state->msg);
          573  +        state->msg = NULL;
          574  +    }
          575  +
          576  +    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
          577  +    if (err != Z_OK && err != Z_BUF_ERROR)
          578  +        state->x.have = 0;
          579  +
          580  +    /* set error code, and if no message, then done */
          581  +    state->err = err;
          582  +    if (msg == NULL)
          583  +        return;
          584  +
          585  +    /* for an out of memory error, save as static string */
          586  +    if (err == Z_MEM_ERROR) {
          587  +        state->msg = (char *)msg;
          588  +        return;
          589  +    }
          590  +
          591  +    /* construct error message with path */
          592  +    if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
          593  +        state->err = Z_MEM_ERROR;
          594  +        state->msg = (char *)"out of memory";
          595  +        return;
          596  +    }
          597  +    strcpy(state->msg, state->path);
          598  +    strcat(state->msg, ": ");
          599  +    strcat(state->msg, msg);
          600  +    return;
          601  +}
          602  +
          603  +#ifndef INT_MAX
          604  +/* portably return maximum value for an int (when limits.h presumed not
          605  +   available) -- we need to do this to cover cases where 2's complement not
          606  +   used, since C standard permits 1's complement and sign-bit representations,
          607  +   otherwise we could just use ((unsigned)-1) >> 1 */
          608  +unsigned ZLIB_INTERNAL gz_intmax()
          609  +{
          610  +    unsigned p, q;
          611  +
          612  +    p = 1;
          613  +    do {
          614  +        q = p;
          615  +        p <<= 1;
          616  +        p++;
          617  +    } while (p > q);
          618  +    return q >> 1;
          619  +}
          620  +#endif

Added compat/zlib/gzread.c.

            1  +/* gzread.c -- zlib functions for reading gzip files
            2  + * Copyright (C) 2004, 2005, 2010, 2011, 2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "gzguts.h"
            7  +
            8  +/* Local functions */
            9  +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
           10  +local int gz_avail OF((gz_statep));
           11  +local int gz_look OF((gz_statep));
           12  +local int gz_decomp OF((gz_statep));
           13  +local int gz_fetch OF((gz_statep));
           14  +local int gz_skip OF((gz_statep, z_off64_t));
           15  +
           16  +/* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from
           17  +   state->fd, and update state->eof, state->err, and state->msg as appropriate.
           18  +   This function needs to loop on read(), since read() is not guaranteed to
           19  +   read the number of bytes requested, depending on the type of descriptor. */
           20  +local int gz_load(state, buf, len, have)
           21  +    gz_statep state;
           22  +    unsigned char *buf;
           23  +    unsigned len;
           24  +    unsigned *have;
           25  +{
           26  +    int ret;
           27  +
           28  +    *have = 0;
           29  +    do {
           30  +        ret = read(state->fd, buf + *have, len - *have);
           31  +        if (ret <= 0)
           32  +            break;
           33  +        *have += ret;
           34  +    } while (*have < len);
           35  +    if (ret < 0) {
           36  +        gz_error(state, Z_ERRNO, zstrerror());
           37  +        return -1;
           38  +    }
           39  +    if (ret == 0)
           40  +        state->eof = 1;
           41  +    return 0;
           42  +}
           43  +
           44  +/* Load up input buffer and set eof flag if last data loaded -- return -1 on
           45  +   error, 0 otherwise.  Note that the eof flag is set when the end of the input
           46  +   file is reached, even though there may be unused data in the buffer.  Once
           47  +   that data has been used, no more attempts will be made to read the file.
           48  +   If strm->avail_in != 0, then the current data is moved to the beginning of
           49  +   the input buffer, and then the remainder of the buffer is loaded with the
           50  +   available data from the input file. */
           51  +local int gz_avail(state)
           52  +    gz_statep state;
           53  +{
           54  +    unsigned got;
           55  +    z_streamp strm = &(state->strm);
           56  +
           57  +    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
           58  +        return -1;
           59  +    if (state->eof == 0) {
           60  +        if (strm->avail_in) {       /* copy what's there to the start */
           61  +            unsigned char *p = state->in, *q = strm->next_in;
           62  +            unsigned n = strm->avail_in;
           63  +            do {
           64  +                *p++ = *q++;
           65  +            } while (--n);
           66  +        }
           67  +        if (gz_load(state, state->in + strm->avail_in,
           68  +                    state->size - strm->avail_in, &got) == -1)
           69  +            return -1;
           70  +        strm->avail_in += got;
           71  +        strm->next_in = state->in;
           72  +    }
           73  +    return 0;
           74  +}
           75  +
           76  +/* Look for gzip header, set up for inflate or copy.  state->x.have must be 0.
           77  +   If this is the first time in, allocate required memory.  state->how will be
           78  +   left unchanged if there is no more input data available, will be set to COPY
           79  +   if there is no gzip header and direct copying will be performed, or it will
           80  +   be set to GZIP for decompression.  If direct copying, then leftover input
           81  +   data from the input buffer will be copied to the output buffer.  In that
           82  +   case, all further file reads will be directly to either the output buffer or
           83  +   a user buffer.  If decompressing, the inflate state will be initialized.
           84  +   gz_look() will return 0 on success or -1 on failure. */
           85  +local int gz_look(state)
           86  +    gz_statep state;
           87  +{
           88  +    z_streamp strm = &(state->strm);
           89  +
           90  +    /* allocate read buffers and inflate memory */
           91  +    if (state->size == 0) {
           92  +        /* allocate buffers */
           93  +        state->in = malloc(state->want);
           94  +        state->out = malloc(state->want << 1);
           95  +        if (state->in == NULL || state->out == NULL) {
           96  +            if (state->out != NULL)
           97  +                free(state->out);
           98  +            if (state->in != NULL)
           99  +                free(state->in);
          100  +            gz_error(state, Z_MEM_ERROR, "out of memory");
          101  +            return -1;
          102  +        }
          103  +        state->size = state->want;
          104  +
          105  +        /* allocate inflate memory */
          106  +        state->strm.zalloc = Z_NULL;
          107  +        state->strm.zfree = Z_NULL;
          108  +        state->strm.opaque = Z_NULL;
          109  +        state->strm.avail_in = 0;
          110  +        state->strm.next_in = Z_NULL;
          111  +        if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) {    /* gunzip */
          112  +            free(state->out);
          113  +            free(state->in);
          114  +            state->size = 0;
          115  +            gz_error(state, Z_MEM_ERROR, "out of memory");
          116  +            return -1;
          117  +        }
          118  +    }
          119  +
          120  +    /* get at least the magic bytes in the input buffer */
          121  +    if (strm->avail_in < 2) {
          122  +        if (gz_avail(state) == -1)
          123  +            return -1;
          124  +        if (strm->avail_in == 0)
          125  +            return 0;
          126  +    }
          127  +
          128  +    /* look for gzip magic bytes -- if there, do gzip decoding (note: there is
          129  +       a logical dilemma here when considering the case of a partially written
          130  +       gzip file, to wit, if a single 31 byte is written, then we cannot tell
          131  +       whether this is a single-byte file, or just a partially written gzip
          132  +       file -- for here we assume that if a gzip file is being written, then
          133  +       the header will be written in a single operation, so that reading a
          134  +       single byte is sufficient indication that it is not a gzip file) */
          135  +    if (strm->avail_in > 1 &&
          136  +            strm->next_in[0] == 31 && strm->next_in[1] == 139) {
          137  +        inflateReset(strm);
          138  +        state->how = GZIP;
          139  +        state->direct = 0;
          140  +        return 0;
          141  +    }
          142  +
          143  +    /* no gzip header -- if we were decoding gzip before, then this is trailing
          144  +       garbage.  Ignore the trailing garbage and finish. */
          145  +    if (state->direct == 0) {
          146  +        strm->avail_in = 0;
          147  +        state->eof = 1;
          148  +        state->x.have = 0;
          149  +        return 0;
          150  +    }
          151  +
          152  +    /* doing raw i/o, copy any leftover input to output -- this assumes that
          153  +       the output buffer is larger than the input buffer, which also assures
          154  +       space for gzungetc() */
          155  +    state->x.next = state->out;
          156  +    if (strm->avail_in) {
          157  +        memcpy(state->x.next, strm->next_in, strm->avail_in);
          158  +        state->x.have = strm->avail_in;
          159  +        strm->avail_in = 0;
          160  +    }
          161  +    state->how = COPY;
          162  +    state->direct = 1;
          163  +    return 0;
          164  +}
          165  +
          166  +/* Decompress from input to the provided next_out and avail_out in the state.
          167  +   On return, state->x.have and state->x.next point to the just decompressed
          168  +   data.  If the gzip stream completes, state->how is reset to LOOK to look for
          169  +   the next gzip stream or raw data, once state->x.have is depleted.  Returns 0
          170  +   on success, -1 on failure. */
          171  +local int gz_decomp(state)
          172  +    gz_statep state;
          173  +{
          174  +    int ret = Z_OK;
          175  +    unsigned had;
          176  +    z_streamp strm = &(state->strm);
          177  +
          178  +    /* fill output buffer up to end of deflate stream */
          179  +    had = strm->avail_out;
          180  +    do {
          181  +        /* get more input for inflate() */
          182  +        if (strm->avail_in == 0 && gz_avail(state) == -1)
          183  +            return -1;
          184  +        if (strm->avail_in == 0) {
          185  +            gz_error(state, Z_BUF_ERROR, "unexpected end of file");
          186  +            break;
          187  +        }
          188  +
          189  +        /* decompress and handle errors */
          190  +        ret = inflate(strm, Z_NO_FLUSH);
          191  +        if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
          192  +            gz_error(state, Z_STREAM_ERROR,
          193  +                     "internal error: inflate stream corrupt");
          194  +            return -1;
          195  +        }
          196  +        if (ret == Z_MEM_ERROR) {
          197  +            gz_error(state, Z_MEM_ERROR, "out of memory");
          198  +            return -1;
          199  +        }
          200  +        if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
          201  +            gz_error(state, Z_DATA_ERROR,
          202  +                     strm->msg == NULL ? "compressed data error" : strm->msg);
          203  +            return -1;
          204  +        }
          205  +    } while (strm->avail_out && ret != Z_STREAM_END);
          206  +
          207  +    /* update available output */
          208  +    state->x.have = had - strm->avail_out;
          209  +    state->x.next = strm->next_out - state->x.have;
          210  +
          211  +    /* if the gzip stream completed successfully, look for another */
          212  +    if (ret == Z_STREAM_END)
          213  +        state->how = LOOK;
          214  +
          215  +    /* good decompression */
          216  +    return 0;
          217  +}
          218  +
          219  +/* Fetch data and put it in the output buffer.  Assumes state->x.have is 0.
          220  +   Data is either copied from the input file or decompressed from the input
          221  +   file depending on state->how.  If state->how is LOOK, then a gzip header is
          222  +   looked for to determine whether to copy or decompress.  Returns -1 on error,
          223  +   otherwise 0.  gz_fetch() will leave state->how as COPY or GZIP unless the
          224  +   end of the input file has been reached and all data has been processed.  */
          225  +local int gz_fetch(state)
          226  +    gz_statep state;
          227  +{
          228  +    z_streamp strm = &(state->strm);
          229  +
          230  +    do {
          231  +        switch(state->how) {
          232  +        case LOOK:      /* -> LOOK, COPY (only if never GZIP), or GZIP */
          233  +            if (gz_look(state) == -1)
          234  +                return -1;
          235  +            if (state->how == LOOK)
          236  +                return 0;
          237  +            break;
          238  +        case COPY:      /* -> COPY */
          239  +            if (gz_load(state, state->out, state->size << 1, &(state->x.have))
          240  +                    == -1)
          241  +                return -1;
          242  +            state->x.next = state->out;
          243  +            return 0;
          244  +        case GZIP:      /* -> GZIP or LOOK (if end of gzip stream) */
          245  +            strm->avail_out = state->size << 1;
          246  +            strm->next_out = state->out;
          247  +            if (gz_decomp(state) == -1)
          248  +                return -1;
          249  +        }
          250  +    } while (state->x.have == 0 && (!state->eof || strm->avail_in));
          251  +    return 0;
          252  +}
          253  +
          254  +/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */
          255  +local int gz_skip(state, len)
          256  +    gz_statep state;
          257  +    z_off64_t len;
          258  +{
          259  +    unsigned n;
          260  +
          261  +    /* skip over len bytes or reach end-of-file, whichever comes first */
          262  +    while (len)
          263  +        /* skip over whatever is in output buffer */
          264  +        if (state->x.have) {
          265  +            n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?
          266  +                (unsigned)len : state->x.have;
          267  +            state->x.have -= n;
          268  +            state->x.next += n;
          269  +            state->x.pos += n;
          270  +            len -= n;
          271  +        }
          272  +
          273  +        /* output buffer empty -- return if we're at the end of the input */
          274  +        else if (state->eof && state->strm.avail_in == 0)
          275  +            break;
          276  +
          277  +        /* need more data to skip -- load up output buffer */
          278  +        else {
          279  +            /* get more output, looking for header if required */
          280  +            if (gz_fetch(state) == -1)
          281  +                return -1;
          282  +        }
          283  +    return 0;
          284  +}
          285  +
          286  +/* -- see zlib.h -- */
          287  +int ZEXPORT gzread(file, buf, len)
          288  +    gzFile file;
          289  +    voidp buf;
          290  +    unsigned len;
          291  +{
          292  +    unsigned got, n;
          293  +    gz_statep state;
          294  +    z_streamp strm;
          295  +
          296  +    /* get internal structure */
          297  +    if (file == NULL)
          298  +        return -1;
          299  +    state = (gz_statep)file;
          300  +    strm = &(state->strm);
          301  +
          302  +    /* check that we're reading and that there's no (serious) error */
          303  +    if (state->mode != GZ_READ ||
          304  +            (state->err != Z_OK && state->err != Z_BUF_ERROR))
          305  +        return -1;
          306  +
          307  +    /* since an int is returned, make sure len fits in one, otherwise return
          308  +       with an error (this avoids the flaw in the interface) */
          309  +    if ((int)len < 0) {
          310  +        gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
          311  +        return -1;
          312  +    }
          313  +
          314  +    /* if len is zero, avoid unnecessary operations */
          315  +    if (len == 0)
          316  +        return 0;
          317  +
          318  +    /* process a skip request */
          319  +    if (state->seek) {
          320  +        state->seek = 0;
          321  +        if (gz_skip(state, state->skip) == -1)
          322  +            return -1;
          323  +    }
          324  +
          325  +    /* get len bytes to buf, or less than len if at the end */
          326  +    got = 0;
          327  +    do {
          328  +        /* first just try copying data from the output buffer */
          329  +        if (state->x.have) {
          330  +            n = state->x.have > len ? len : state->x.have;
          331  +            memcpy(buf, state->x.next, n);
          332  +            state->x.next += n;
          333  +            state->x.have -= n;
          334  +        }
          335  +
          336  +        /* output buffer empty -- return if we're at the end of the input */
          337  +        else if (state->eof && strm->avail_in == 0) {
          338  +            state->past = 1;        /* tried to read past end */
          339  +            break;
          340  +        }
          341  +
          342  +        /* need output data -- for small len or new stream load up our output
          343  +           buffer */
          344  +        else if (state->how == LOOK || len < (state->size << 1)) {
          345  +            /* get more output, looking for header if required */
          346  +            if (gz_fetch(state) == -1)
          347  +                return -1;
          348  +            continue;       /* no progress yet -- go back to copy above */
          349  +            /* the copy above assures that we will leave with space in the
          350  +               output buffer, allowing at least one gzungetc() to succeed */
          351  +        }
          352  +
          353  +        /* large len -- read directly into user buffer */
          354  +        else if (state->how == COPY) {      /* read directly */
          355  +            if (gz_load(state, buf, len, &n) == -1)
          356  +                return -1;
          357  +        }
          358  +
          359  +        /* large len -- decompress directly into user buffer */
          360  +        else {  /* state->how == GZIP */
          361  +            strm->avail_out = len;
          362  +            strm->next_out = buf;
          363  +            if (gz_decomp(state) == -1)
          364  +                return -1;
          365  +            n = state->x.have;
          366  +            state->x.have = 0;
          367  +        }
          368  +
          369  +        /* update progress */
          370  +        len -= n;
          371  +        buf = (char *)buf + n;
          372  +        got += n;
          373  +        state->x.pos += n;
          374  +    } while (len);
          375  +
          376  +    /* return number of bytes read into user buffer (will fit in int) */
          377  +    return (int)got;
          378  +}
          379  +
          380  +/* -- see zlib.h -- */
          381  +#undef gzgetc
          382  +int ZEXPORT gzgetc(file)
          383  +    gzFile file;
          384  +{
          385  +    int ret;
          386  +    unsigned char buf[1];
          387  +    gz_statep state;
          388  +
          389  +    /* get internal structure */
          390  +    if (file == NULL)
          391  +        return -1;
          392  +    state = (gz_statep)file;
          393  +
          394  +    /* check that we're reading and that there's no (serious) error */
          395  +    if (state->mode != GZ_READ ||
          396  +        (state->err != Z_OK && state->err != Z_BUF_ERROR))
          397  +        return -1;
          398  +
          399  +    /* try output buffer (no need to check for skip request) */
          400  +    if (state->x.have) {
          401  +        state->x.have--;
          402  +        state->x.pos++;
          403  +        return *(state->x.next)++;
          404  +    }
          405  +
          406  +    /* nothing there -- try gzread() */
          407  +    ret = gzread(file, buf, 1);
          408  +    return ret < 1 ? -1 : buf[0];
          409  +}
          410  +
          411  +int ZEXPORT gzgetc_(file)
          412  +gzFile file;
          413  +{
          414  +    return gzgetc(file);
          415  +}
          416  +
          417  +/* -- see zlib.h -- */
          418  +int ZEXPORT gzungetc(c, file)
          419  +    int c;
          420  +    gzFile file;
          421  +{
          422  +    gz_statep state;
          423  +
          424  +    /* get internal structure */
          425  +    if (file == NULL)
          426  +        return -1;
          427  +    state = (gz_statep)file;
          428  +
          429  +    /* check that we're reading and that there's no (serious) error */
          430  +    if (state->mode != GZ_READ ||
          431  +        (state->err != Z_OK && state->err != Z_BUF_ERROR))
          432  +        return -1;
          433  +
          434  +    /* process a skip request */
          435  +    if (state->seek) {
          436  +        state->seek = 0;
          437  +        if (gz_skip(state, state->skip) == -1)
          438  +            return -1;
          439  +    }
          440  +
          441  +    /* can't push EOF */
          442  +    if (c < 0)
          443  +        return -1;
          444  +
          445  +    /* if output buffer empty, put byte at end (allows more pushing) */
          446  +    if (state->x.have == 0) {
          447  +        state->x.have = 1;
          448  +        state->x.next = state->out + (state->size << 1) - 1;
          449  +        state->x.next[0] = c;
          450  +        state->x.pos--;
          451  +        state->past = 0;
          452  +        return c;
          453  +    }
          454  +
          455  +    /* if no room, give up (must have already done a gzungetc()) */
          456  +    if (state->x.have == (state->size << 1)) {
          457  +        gz_error(state, Z_DATA_ERROR, "out of room to push characters");
          458  +        return -1;
          459  +    }
          460  +
          461  +    /* slide output data if needed and insert byte before existing data */
          462  +    if (state->x.next == state->out) {
          463  +        unsigned char *src = state->out + state->x.have;
          464  +        unsigned char *dest = state->out + (state->size << 1);
          465  +        while (src > state->out)
          466  +            *--dest = *--src;
          467  +        state->x.next = dest;
          468  +    }
          469  +    state->x.have++;
          470  +    state->x.next--;
          471  +    state->x.next[0] = c;
          472  +    state->x.pos--;
          473  +    state->past = 0;
          474  +    return c;
          475  +}
          476  +
          477  +/* -- see zlib.h -- */
          478  +char * ZEXPORT gzgets(file, buf, len)
          479  +    gzFile file;
          480  +    char *buf;
          481  +    int len;
          482  +{
          483  +    unsigned left, n;
          484  +    char *str;
          485  +    unsigned char *eol;
          486  +    gz_statep state;
          487  +
          488  +    /* check parameters and get internal structure */
          489  +    if (file == NULL || buf == NULL || len < 1)
          490  +        return NULL;
          491  +    state = (gz_statep)file;
          492  +
          493  +    /* check that we're reading and that there's no (serious) error */
          494  +    if (state->mode != GZ_READ ||
          495  +        (state->err != Z_OK && state->err != Z_BUF_ERROR))
          496  +        return NULL;
          497  +
          498  +    /* process a skip request */
          499  +    if (state->seek) {
          500  +        state->seek = 0;
          501  +        if (gz_skip(state, state->skip) == -1)
          502  +            return NULL;
          503  +    }
          504  +
          505  +    /* copy output bytes up to new line or len - 1, whichever comes first --
          506  +       append a terminating zero to the string (we don't check for a zero in
          507  +       the contents, let the user worry about that) */
          508  +    str = buf;
          509  +    left = (unsigned)len - 1;
          510  +    if (left) do {
          511  +        /* assure that something is in the output buffer */
          512  +        if (state->x.have == 0 && gz_fetch(state) == -1)
          513  +            return NULL;                /* error */
          514  +        if (state->x.have == 0) {       /* end of file */
          515  +            state->past = 1;            /* read past end */
          516  +            break;                      /* return what we have */
          517  +        }
          518  +
          519  +        /* look for end-of-line in current output buffer */
          520  +        n = state->x.have > left ? left : state->x.have;
          521  +        eol = memchr(state->x.next, '\n', n);
          522  +        if (eol != NULL)
          523  +            n = (unsigned)(eol - state->x.next) + 1;
          524  +
          525  +        /* copy through end-of-line, or remainder if not found */
          526  +        memcpy(buf, state->x.next, n);
          527  +        state->x.have -= n;
          528  +        state->x.next += n;
          529  +        state->x.pos += n;
          530  +        left -= n;
          531  +        buf += n;
          532  +    } while (left && eol == NULL);
          533  +
          534  +    /* return terminated string, or if nothing, end of file */
          535  +    if (buf == str)
          536  +        return NULL;
          537  +    buf[0] = 0;
          538  +    return str;
          539  +}
          540  +
          541  +/* -- see zlib.h -- */
          542  +int ZEXPORT gzdirect(file)
          543  +    gzFile file;
          544  +{
          545  +    gz_statep state;
          546  +
          547  +    /* get internal structure */
          548  +    if (file == NULL)
          549  +        return 0;
          550  +    state = (gz_statep)file;
          551  +
          552  +    /* if the state is not known, but we can find out, then do so (this is
          553  +       mainly for right after a gzopen() or gzdopen()) */
          554  +    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
          555  +        (void)gz_look(state);
          556  +
          557  +    /* return 1 if transparent, 0 if processing a gzip stream */
          558  +    return state->direct;
          559  +}
          560  +
          561  +/* -- see zlib.h -- */
          562  +int ZEXPORT gzclose_r(file)
          563  +    gzFile file;
          564  +{
          565  +    int ret, err;
          566  +    gz_statep state;
          567  +
          568  +    /* get internal structure */
          569  +    if (file == NULL)
          570  +        return Z_STREAM_ERROR;
          571  +    state = (gz_statep)file;
          572  +
          573  +    /* check that we're reading */
          574  +    if (state->mode != GZ_READ)
          575  +        return Z_STREAM_ERROR;
          576  +
          577  +    /* free memory and close file */
          578  +    if (state->size) {
          579  +        inflateEnd(&(state->strm));
          580  +        free(state->out);
          581  +        free(state->in);
          582  +    }
          583  +    err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;
          584  +    gz_error(state, Z_OK, NULL);
          585  +    free(state->path);
          586  +    ret = close(state->fd);
          587  +    free(state);
          588  +    return ret ? Z_ERRNO : err;
          589  +}

Added compat/zlib/gzwrite.c.

            1  +/* gzwrite.c -- zlib functions for writing gzip files
            2  + * Copyright (C) 2004, 2005, 2010, 2011, 2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "gzguts.h"
            7  +
            8  +/* Local functions */
            9  +local int gz_init OF((gz_statep));
           10  +local int gz_comp OF((gz_statep, int));
           11  +local int gz_zero OF((gz_statep, z_off64_t));
           12  +
           13  +/* Initialize state for writing a gzip file.  Mark initialization by setting
           14  +   state->size to non-zero.  Return -1 on failure or 0 on success. */
           15  +local int gz_init(state)
           16  +    gz_statep state;
           17  +{
           18  +    int ret;
           19  +    z_streamp strm = &(state->strm);
           20  +
           21  +    /* allocate input buffer */
           22  +    state->in = malloc(state->want);
           23  +    if (state->in == NULL) {
           24  +        gz_error(state, Z_MEM_ERROR, "out of memory");
           25  +        return -1;
           26  +    }
           27  +
           28  +    /* only need output buffer and deflate state if compressing */
           29  +    if (!state->direct) {
           30  +        /* allocate output buffer */
           31  +        state->out = malloc(state->want);
           32  +        if (state->out == NULL) {
           33  +            free(state->in);
           34  +            gz_error(state, Z_MEM_ERROR, "out of memory");
           35  +            return -1;
           36  +        }
           37  +
           38  +        /* allocate deflate memory, set up for gzip compression */
           39  +        strm->zalloc = Z_NULL;
           40  +        strm->zfree = Z_NULL;
           41  +        strm->opaque = Z_NULL;
           42  +        ret = deflateInit2(strm, state->level, Z_DEFLATED,
           43  +                           MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
           44  +        if (ret != Z_OK) {
           45  +            free(state->out);
           46  +            free(state->in);
           47  +            gz_error(state, Z_MEM_ERROR, "out of memory");
           48  +            return -1;
           49  +        }
           50  +    }
           51  +
           52  +    /* mark state as initialized */
           53  +    state->size = state->want;
           54  +
           55  +    /* initialize write buffer if compressing */
           56  +    if (!state->direct) {
           57  +        strm->avail_out = state->size;
           58  +        strm->next_out = state->out;
           59  +        state->x.next = strm->next_out;
           60  +    }
           61  +    return 0;
           62  +}
           63  +
           64  +/* Compress whatever is at avail_in and next_in and write to the output file.
           65  +   Return -1 if there is an error writing to the output file, otherwise 0.
           66  +   flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
           67  +   then the deflate() state is reset to start a new gzip stream.  If gz->direct
           68  +   is true, then simply write to the output file without compressing, and
           69  +   ignore flush. */
           70  +local int gz_comp(state, flush)
           71  +    gz_statep state;
           72  +    int flush;
           73  +{
           74  +    int ret, got;
           75  +    unsigned have;
           76  +    z_streamp strm = &(state->strm);
           77  +
           78  +    /* allocate memory if this is the first time through */
           79  +    if (state->size == 0 && gz_init(state) == -1)
           80  +        return -1;
           81  +
           82  +    /* write directly if requested */
           83  +    if (state->direct) {
           84  +        got = write(state->fd, strm->next_in, strm->avail_in);
           85  +        if (got < 0 || (unsigned)got != strm->avail_in) {
           86  +            gz_error(state, Z_ERRNO, zstrerror());
           87  +            return -1;
           88  +        }
           89  +        strm->avail_in = 0;
           90  +        return 0;
           91  +    }
           92  +
           93  +    /* run deflate() on provided input until it produces no more output */
           94  +    ret = Z_OK;
           95  +    do {
           96  +        /* write out current buffer contents if full, or if flushing, but if
           97  +           doing Z_FINISH then don't write until we get to Z_STREAM_END */
           98  +        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
           99  +            (flush != Z_FINISH || ret == Z_STREAM_END))) {
          100  +            have = (unsigned)(strm->next_out - state->x.next);
          101  +            if (have && ((got = write(state->fd, state->x.next, have)) < 0 ||
          102  +                         (unsigned)got != have)) {
          103  +                gz_error(state, Z_ERRNO, zstrerror());
          104  +                return -1;
          105  +            }
          106  +            if (strm->avail_out == 0) {
          107  +                strm->avail_out = state->size;
          108  +                strm->next_out = state->out;
          109  +            }
          110  +            state->x.next = strm->next_out;
          111  +        }
          112  +
          113  +        /* compress */
          114  +        have = strm->avail_out;
          115  +        ret = deflate(strm, flush);
          116  +        if (ret == Z_STREAM_ERROR) {
          117  +            gz_error(state, Z_STREAM_ERROR,
          118  +                      "internal error: deflate stream corrupt");
          119  +            return -1;
          120  +        }
          121  +        have -= strm->avail_out;
          122  +    } while (have);
          123  +
          124  +    /* if that completed a deflate stream, allow another to start */
          125  +    if (flush == Z_FINISH)
          126  +        deflateReset(strm);
          127  +
          128  +    /* all done, no errors */
          129  +    return 0;
          130  +}
          131  +
          132  +/* Compress len zeros to output.  Return -1 on error, 0 on success. */
          133  +local int gz_zero(state, len)
          134  +    gz_statep state;
          135  +    z_off64_t len;
          136  +{
          137  +    int first;
          138  +    unsigned n;
          139  +    z_streamp strm = &(state->strm);
          140  +
          141  +    /* consume whatever's left in the input buffer */
          142  +    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
          143  +        return -1;
          144  +
          145  +    /* compress len zeros (len guaranteed > 0) */
          146  +    first = 1;
          147  +    while (len) {
          148  +        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
          149  +            (unsigned)len : state->size;
          150  +        if (first) {
          151  +            memset(state->in, 0, n);
          152  +            first = 0;
          153  +        }
          154  +        strm->avail_in = n;
          155  +        strm->next_in = state->in;
          156  +        state->x.pos += n;
          157  +        if (gz_comp(state, Z_NO_FLUSH) == -1)
          158  +            return -1;
          159  +        len -= n;
          160  +    }
          161  +    return 0;
          162  +}
          163  +
          164  +/* -- see zlib.h -- */
          165  +int ZEXPORT gzwrite(file, buf, len)
          166  +    gzFile file;
          167  +    voidpc buf;
          168  +    unsigned len;
          169  +{
          170  +    unsigned put = len;
          171  +    unsigned n;
          172  +    gz_statep state;
          173  +    z_streamp strm;
          174  +
          175  +    /* get internal structure */
          176  +    if (file == NULL)
          177  +        return 0;
          178  +    state = (gz_statep)file;
          179  +    strm = &(state->strm);
          180  +
          181  +    /* check that we're writing and that there's no error */
          182  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          183  +        return 0;
          184  +
          185  +    /* since an int is returned, make sure len fits in one, otherwise return
          186  +       with an error (this avoids the flaw in the interface) */
          187  +    if ((int)len < 0) {
          188  +        gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
          189  +        return 0;
          190  +    }
          191  +
          192  +    /* if len is zero, avoid unnecessary operations */
          193  +    if (len == 0)
          194  +        return 0;
          195  +
          196  +    /* allocate memory if this is the first time through */
          197  +    if (state->size == 0 && gz_init(state) == -1)
          198  +        return 0;
          199  +
          200  +    /* check for seek request */
          201  +    if (state->seek) {
          202  +        state->seek = 0;
          203  +        if (gz_zero(state, state->skip) == -1)
          204  +            return 0;
          205  +    }
          206  +
          207  +    /* for small len, copy to input buffer, otherwise compress directly */
          208  +    if (len < state->size) {
          209  +        /* copy to input buffer, compress when full */
          210  +        do {
          211  +            if (strm->avail_in == 0)
          212  +                strm->next_in = state->in;
          213  +            n = state->size - strm->avail_in;
          214  +            if (n > len)
          215  +                n = len;
          216  +            memcpy(strm->next_in + strm->avail_in, buf, n);
          217  +            strm->avail_in += n;
          218  +            state->x.pos += n;
          219  +            buf = (char *)buf + n;
          220  +            len -= n;
          221  +            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
          222  +                return 0;
          223  +        } while (len);
          224  +    }
          225  +    else {
          226  +        /* consume whatever's left in the input buffer */
          227  +        if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
          228  +            return 0;
          229  +
          230  +        /* directly compress user buffer to file */
          231  +        strm->avail_in = len;
          232  +        strm->next_in = (voidp)buf;
          233  +        state->x.pos += len;
          234  +        if (gz_comp(state, Z_NO_FLUSH) == -1)
          235  +            return 0;
          236  +    }
          237  +
          238  +    /* input was all buffered or compressed (put will fit in int) */
          239  +    return (int)put;
          240  +}
          241  +
          242  +/* -- see zlib.h -- */
          243  +int ZEXPORT gzputc(file, c)
          244  +    gzFile file;
          245  +    int c;
          246  +{
          247  +    unsigned char buf[1];
          248  +    gz_statep state;
          249  +    z_streamp strm;
          250  +
          251  +    /* get internal structure */
          252  +    if (file == NULL)
          253  +        return -1;
          254  +    state = (gz_statep)file;
          255  +    strm = &(state->strm);
          256  +
          257  +    /* check that we're writing and that there's no error */
          258  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          259  +        return -1;
          260  +
          261  +    /* check for seek request */
          262  +    if (state->seek) {
          263  +        state->seek = 0;
          264  +        if (gz_zero(state, state->skip) == -1)
          265  +            return -1;
          266  +    }
          267  +
          268  +    /* try writing to input buffer for speed (state->size == 0 if buffer not
          269  +       initialized) */
          270  +    if (strm->avail_in < state->size) {
          271  +        if (strm->avail_in == 0)
          272  +            strm->next_in = state->in;
          273  +        strm->next_in[strm->avail_in++] = c;
          274  +        state->x.pos++;
          275  +        return c & 0xff;
          276  +    }
          277  +
          278  +    /* no room in buffer or not initialized, use gz_write() */
          279  +    buf[0] = c;
          280  +    if (gzwrite(file, buf, 1) != 1)
          281  +        return -1;
          282  +    return c & 0xff;
          283  +}
          284  +
          285  +/* -- see zlib.h -- */
          286  +int ZEXPORT gzputs(file, str)
          287  +    gzFile file;
          288  +    const char *str;
          289  +{
          290  +    int ret;
          291  +    unsigned len;
          292  +
          293  +    /* write string */
          294  +    len = (unsigned)strlen(str);
          295  +    ret = gzwrite(file, str, len);
          296  +    return ret == 0 && len != 0 ? -1 : ret;
          297  +}
          298  +
          299  +#if defined(STDC) || defined(Z_HAVE_STDARG_H)
          300  +#include <stdarg.h>
          301  +
          302  +/* -- see zlib.h -- */
          303  +int ZEXPORTVA gzprintf (gzFile file, const char *format, ...)
          304  +{
          305  +    int size, len;
          306  +    gz_statep state;
          307  +    z_streamp strm;
          308  +    va_list va;
          309  +
          310  +    /* get internal structure */
          311  +    if (file == NULL)
          312  +        return -1;
          313  +    state = (gz_statep)file;
          314  +    strm = &(state->strm);
          315  +
          316  +    /* check that we're writing and that there's no error */
          317  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          318  +        return 0;
          319  +
          320  +    /* make sure we have some buffer space */
          321  +    if (state->size == 0 && gz_init(state) == -1)
          322  +        return 0;
          323  +
          324  +    /* check for seek request */
          325  +    if (state->seek) {
          326  +        state->seek = 0;
          327  +        if (gz_zero(state, state->skip) == -1)
          328  +            return 0;
          329  +    }
          330  +
          331  +    /* consume whatever's left in the input buffer */
          332  +    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
          333  +        return 0;
          334  +
          335  +    /* do the printf() into the input buffer, put length in len */
          336  +    size = (int)(state->size);
          337  +    state->in[size - 1] = 0;
          338  +    va_start(va, format);
          339  +#ifdef NO_vsnprintf
          340  +#  ifdef HAS_vsprintf_void
          341  +    (void)vsprintf((char *)(state->in), format, va);
          342  +    va_end(va);
          343  +    for (len = 0; len < size; len++)
          344  +        if (state->in[len] == 0) break;
          345  +#  else
          346  +    len = vsprintf((char *)(state->in), format, va);
          347  +    va_end(va);
          348  +#  endif
          349  +#else
          350  +#  ifdef HAS_vsnprintf_void
          351  +    (void)vsnprintf((char *)(state->in), size, format, va);
          352  +    va_end(va);
          353  +    len = strlen((char *)(state->in));
          354  +#  else
          355  +    len = vsnprintf((char *)(state->in), size, format, va);
          356  +    va_end(va);
          357  +#  endif
          358  +#endif
          359  +
          360  +    /* check that printf() results fit in buffer */
          361  +    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
          362  +        return 0;
          363  +
          364  +    /* update buffer and position, defer compression until needed */
          365  +    strm->avail_in = (unsigned)len;
          366  +    strm->next_in = state->in;
          367  +    state->x.pos += len;
          368  +    return len;
          369  +}
          370  +
          371  +#else /* !STDC && !Z_HAVE_STDARG_H */
          372  +
          373  +/* -- see zlib.h -- */
          374  +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
          375  +                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
          376  +    gzFile file;
          377  +    const char *format;
          378  +    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
          379  +        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
          380  +{
          381  +    int size, len;
          382  +    gz_statep state;
          383  +    z_streamp strm;
          384  +
          385  +    /* get internal structure */
          386  +    if (file == NULL)
          387  +        return -1;
          388  +    state = (gz_statep)file;
          389  +    strm = &(state->strm);
          390  +
          391  +    /* check that can really pass pointer in ints */
          392  +    if (sizeof(int) != sizeof(void *))
          393  +        return 0;
          394  +
          395  +    /* check that we're writing and that there's no error */
          396  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          397  +        return 0;
          398  +
          399  +    /* make sure we have some buffer space */
          400  +    if (state->size == 0 && gz_init(state) == -1)
          401  +        return 0;
          402  +
          403  +    /* check for seek request */
          404  +    if (state->seek) {
          405  +        state->seek = 0;
          406  +        if (gz_zero(state, state->skip) == -1)
          407  +            return 0;
          408  +    }
          409  +
          410  +    /* consume whatever's left in the input buffer */
          411  +    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
          412  +        return 0;
          413  +
          414  +    /* do the printf() into the input buffer, put length in len */
          415  +    size = (int)(state->size);
          416  +    state->in[size - 1] = 0;
          417  +#ifdef NO_snprintf
          418  +#  ifdef HAS_sprintf_void
          419  +    sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8,
          420  +            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
          421  +    for (len = 0; len < size; len++)
          422  +        if (state->in[len] == 0) break;
          423  +#  else
          424  +    len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8,
          425  +                  a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
          426  +#  endif
          427  +#else
          428  +#  ifdef HAS_snprintf_void
          429  +    snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8,
          430  +             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
          431  +    len = strlen((char *)(state->in));
          432  +#  else
          433  +    len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6,
          434  +                   a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18,
          435  +                   a19, a20);
          436  +#  endif
          437  +#endif
          438  +
          439  +    /* check that printf() results fit in buffer */
          440  +    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
          441  +        return 0;
          442  +
          443  +    /* update buffer and position, defer compression until needed */
          444  +    strm->avail_in = (unsigned)len;
          445  +    strm->next_in = state->in;
          446  +    state->x.pos += len;
          447  +    return len;
          448  +}
          449  +
          450  +#endif
          451  +
          452  +/* -- see zlib.h -- */
          453  +int ZEXPORT gzflush(file, flush)
          454  +    gzFile file;
          455  +    int flush;
          456  +{
          457  +    gz_statep state;
          458  +
          459  +    /* get internal structure */
          460  +    if (file == NULL)
          461  +        return -1;
          462  +    state = (gz_statep)file;
          463  +
          464  +    /* check that we're writing and that there's no error */
          465  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          466  +        return Z_STREAM_ERROR;
          467  +
          468  +    /* check flush parameter */
          469  +    if (flush < 0 || flush > Z_FINISH)
          470  +        return Z_STREAM_ERROR;
          471  +
          472  +    /* check for seek request */
          473  +    if (state->seek) {
          474  +        state->seek = 0;
          475  +        if (gz_zero(state, state->skip) == -1)
          476  +            return -1;
          477  +    }
          478  +
          479  +    /* compress remaining data with requested flush */
          480  +    gz_comp(state, flush);
          481  +    return state->err;
          482  +}
          483  +
          484  +/* -- see zlib.h -- */
          485  +int ZEXPORT gzsetparams(file, level, strategy)
          486  +    gzFile file;
          487  +    int level;
          488  +    int strategy;
          489  +{
          490  +    gz_statep state;
          491  +    z_streamp strm;
          492  +
          493  +    /* get internal structure */
          494  +    if (file == NULL)
          495  +        return Z_STREAM_ERROR;
          496  +    state = (gz_statep)file;
          497  +    strm = &(state->strm);
          498  +
          499  +    /* check that we're writing and that there's no error */
          500  +    if (state->mode != GZ_WRITE || state->err != Z_OK)
          501  +        return Z_STREAM_ERROR;
          502  +
          503  +    /* if no change is requested, then do nothing */
          504  +    if (level == state->level && strategy == state->strategy)
          505  +        return Z_OK;
          506  +
          507  +    /* check for seek request */
          508  +    if (state->seek) {
          509  +        state->seek = 0;
          510  +        if (gz_zero(state, state->skip) == -1)
          511  +            return -1;
          512  +    }
          513  +
          514  +    /* change compression parameters for subsequent input */
          515  +    if (state->size) {
          516  +        /* flush previous input with previous parameters before changing */
          517  +        if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1)
          518  +            return state->err;
          519  +        deflateParams(strm, level, strategy);
          520  +    }
          521  +    state->level = level;
          522  +    state->strategy = strategy;
          523  +    return Z_OK;
          524  +}
          525  +
          526  +/* -- see zlib.h -- */
          527  +int ZEXPORT gzclose_w(file)
          528  +    gzFile file;
          529  +{
          530  +    int ret = Z_OK;
          531  +    gz_statep state;
          532  +
          533  +    /* get internal structure */
          534  +    if (file == NULL)
          535  +        return Z_STREAM_ERROR;
          536  +    state = (gz_statep)file;
          537  +
          538  +    /* check that we're writing */
          539  +    if (state->mode != GZ_WRITE)
          540  +        return Z_STREAM_ERROR;
          541  +
          542  +    /* check for seek request */
          543  +    if (state->seek) {
          544  +        state->seek = 0;
          545  +        if (gz_zero(state, state->skip) == -1)
          546  +            ret = state->err;
          547  +    }
          548  +
          549  +    /* flush, free memory, and close file */
          550  +    if (state->size) {
          551  +        if (gz_comp(state, Z_FINISH) == -1)
          552  +            ret = state->err;
          553  +        if (!state->direct) {
          554  +            (void)deflateEnd(&(state->strm));
          555  +            free(state->out);
          556  +        }
          557  +        free(state->in);
          558  +    }
          559  +    gz_error(state, Z_OK, NULL);
          560  +    free(state->path);
          561  +    if (close(state->fd) == -1)
          562  +        ret = Z_ERRNO;
          563  +    free(state);
          564  +    return ret;
          565  +}

Added compat/zlib/infback.c.

            1  +/* infback.c -- inflate using a call-back interface
            2  + * Copyright (C) 1995-2011 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/*
            7  +   This code is largely copied from inflate.c.  Normally either infback.o or
            8  +   inflate.o would be linked into an application--not both.  The interface
            9  +   with inffast.c is retained so that optimized assembler-coded versions of
           10  +   inflate_fast() can be used with either inflate.c or infback.c.
           11  + */
           12  +
           13  +#include "zutil.h"
           14  +#include "inftrees.h"
           15  +#include "inflate.h"
           16  +#include "inffast.h"
           17  +
           18  +/* function prototypes */
           19  +local void fixedtables OF((struct inflate_state FAR *state));
           20  +
           21  +/*
           22  +   strm provides memory allocation functions in zalloc and zfree, or
           23  +   Z_NULL to use the library memory allocation functions.
           24  +
           25  +   windowBits is in the range 8..15, and window is a user-supplied
           26  +   window and output buffer that is 2**windowBits bytes.
           27  + */
           28  +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
           29  +z_streamp strm;
           30  +int windowBits;
           31  +unsigned char FAR *window;
           32  +const char *version;
           33  +int stream_size;
           34  +{
           35  +    struct inflate_state FAR *state;
           36  +
           37  +    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
           38  +        stream_size != (int)(sizeof(z_stream)))
           39  +        return Z_VERSION_ERROR;
           40  +    if (strm == Z_NULL || window == Z_NULL ||
           41  +        windowBits < 8 || windowBits > 15)
           42  +        return Z_STREAM_ERROR;
           43  +    strm->msg = Z_NULL;                 /* in case we return an error */
           44  +    if (strm->zalloc == (alloc_func)0) {
           45  +#ifdef Z_SOLO
           46  +        return Z_STREAM_ERROR;
           47  +#else
           48  +        strm->zalloc = zcalloc;
           49  +        strm->opaque = (voidpf)0;
           50  +#endif
           51  +    }
           52  +    if (strm->zfree == (free_func)0)
           53  +#ifdef Z_SOLO
           54  +        return Z_STREAM_ERROR;
           55  +#else
           56  +    strm->zfree = zcfree;
           57  +#endif
           58  +    state = (struct inflate_state FAR *)ZALLOC(strm, 1,
           59  +                                               sizeof(struct inflate_state));
           60  +    if (state == Z_NULL) return Z_MEM_ERROR;
           61  +    Tracev((stderr, "inflate: allocated\n"));
           62  +    strm->state = (struct internal_state FAR *)state;
           63  +    state->dmax = 32768U;
           64  +    state->wbits = windowBits;
           65  +    state->wsize = 1U << windowBits;
           66  +    state->window = window;
           67  +    state->wnext = 0;
           68  +    state->whave = 0;
           69  +    return Z_OK;
           70  +}
           71  +
           72  +/*
           73  +   Return state with length and distance decoding tables and index sizes set to
           74  +   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
           75  +   If BUILDFIXED is defined, then instead this routine builds the tables the
           76  +   first time it's called, and returns those tables the first time and
           77  +   thereafter.  This reduces the size of the code by about 2K bytes, in
           78  +   exchange for a little execution time.  However, BUILDFIXED should not be
           79  +   used for threaded applications, since the rewriting of the tables and virgin
           80  +   may not be thread-safe.
           81  + */
           82  +local void fixedtables(state)
           83  +struct inflate_state FAR *state;
           84  +{
           85  +#ifdef BUILDFIXED
           86  +    static int virgin = 1;
           87  +    static code *lenfix, *distfix;
           88  +    static code fixed[544];
           89  +
           90  +    /* build fixed huffman tables if first call (may not be thread safe) */
           91  +    if (virgin) {
           92  +        unsigned sym, bits;
           93  +        static code *next;
           94  +
           95  +        /* literal/length table */
           96  +        sym = 0;
           97  +        while (sym < 144) state->lens[sym++] = 8;
           98  +        while (sym < 256) state->lens[sym++] = 9;
           99  +        while (sym < 280) state->lens[sym++] = 7;
          100  +        while (sym < 288) state->lens[sym++] = 8;
          101  +        next = fixed;
          102  +        lenfix = next;
          103  +        bits = 9;
          104  +        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
          105  +
          106  +        /* distance table */
          107  +        sym = 0;
          108  +        while (sym < 32) state->lens[sym++] = 5;
          109  +        distfix = next;
          110  +        bits = 5;
          111  +        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
          112  +
          113  +        /* do this just once */
          114  +        virgin = 0;
          115  +    }
          116  +#else /* !BUILDFIXED */
          117  +#   include "inffixed.h"
          118  +#endif /* BUILDFIXED */
          119  +    state->lencode = lenfix;
          120  +    state->lenbits = 9;
          121  +    state->distcode = distfix;
          122  +    state->distbits = 5;
          123  +}
          124  +
          125  +/* Macros for inflateBack(): */
          126  +
          127  +/* Load returned state from inflate_fast() */
          128  +#define LOAD() \
          129  +    do { \
          130  +        put = strm->next_out; \
          131  +        left = strm->avail_out; \
          132  +        next = strm->next_in; \
          133  +        have = strm->avail_in; \
          134  +        hold = state->hold; \
          135  +        bits = state->bits; \
          136  +    } while (0)
          137  +
          138  +/* Set state from registers for inflate_fast() */
          139  +#define RESTORE() \
          140  +    do { \
          141  +        strm->next_out = put; \
          142  +        strm->avail_out = left; \
          143  +        strm->next_in = next; \
          144  +        strm->avail_in = have; \
          145  +        state->hold = hold; \
          146  +        state->bits = bits; \
          147  +    } while (0)
          148  +
          149  +/* Clear the input bit accumulator */
          150  +#define INITBITS() \
          151  +    do { \
          152  +        hold = 0; \
          153  +        bits = 0; \
          154  +    } while (0)
          155  +
          156  +/* Assure that some input is available.  If input is requested, but denied,
          157  +   then return a Z_BUF_ERROR from inflateBack(). */
          158  +#define PULL() \
          159  +    do { \
          160  +        if (have == 0) { \
          161  +            have = in(in_desc, &next); \
          162  +            if (have == 0) { \
          163  +                next = Z_NULL; \
          164  +                ret = Z_BUF_ERROR; \
          165  +                goto inf_leave; \
          166  +            } \
          167  +        } \
          168  +    } while (0)
          169  +
          170  +/* Get a byte of input into the bit accumulator, or return from inflateBack()
          171  +   with an error if there is no input available. */
          172  +#define PULLBYTE() \
          173  +    do { \
          174  +        PULL(); \
          175  +        have--; \
          176  +        hold += (unsigned long)(*next++) << bits; \
          177  +        bits += 8; \
          178  +    } while (0)
          179  +
          180  +/* Assure that there are at least n bits in the bit accumulator.  If there is
          181  +   not enough available input to do that, then return from inflateBack() with
          182  +   an error. */
          183  +#define NEEDBITS(n) \
          184  +    do { \
          185  +        while (bits < (unsigned)(n)) \
          186  +            PULLBYTE(); \
          187  +    } while (0)
          188  +
          189  +/* Return the low n bits of the bit accumulator (n < 16) */
          190  +#define BITS(n) \
          191  +    ((unsigned)hold & ((1U << (n)) - 1))
          192  +
          193  +/* Remove n bits from the bit accumulator */
          194  +#define DROPBITS(n) \
          195  +    do { \
          196  +        hold >>= (n); \
          197  +        bits -= (unsigned)(n); \
          198  +    } while (0)
          199  +
          200  +/* Remove zero to seven bits as needed to go to a byte boundary */
          201  +#define BYTEBITS() \
          202  +    do { \
          203  +        hold >>= bits & 7; \
          204  +        bits -= bits & 7; \
          205  +    } while (0)
          206  +
          207  +/* Assure that some output space is available, by writing out the window
          208  +   if it's full.  If the write fails, return from inflateBack() with a
          209  +   Z_BUF_ERROR. */
          210  +#define ROOM() \
          211  +    do { \
          212  +        if (left == 0) { \
          213  +            put = state->window; \
          214  +            left = state->wsize; \
          215  +            state->whave = left; \
          216  +            if (out(out_desc, put, left)) { \
          217  +                ret = Z_BUF_ERROR; \
          218  +                goto inf_leave; \
          219  +            } \
          220  +        } \
          221  +    } while (0)
          222  +
          223  +/*
          224  +   strm provides the memory allocation functions and window buffer on input,
          225  +   and provides information on the unused input on return.  For Z_DATA_ERROR
          226  +   returns, strm will also provide an error message.
          227  +
          228  +   in() and out() are the call-back input and output functions.  When
          229  +   inflateBack() needs more input, it calls in().  When inflateBack() has
          230  +   filled the window with output, or when it completes with data in the
          231  +   window, it calls out() to write out the data.  The application must not
          232  +   change the provided input until in() is called again or inflateBack()
          233  +   returns.  The application must not change the window/output buffer until
          234  +   inflateBack() returns.
          235  +
          236  +   in() and out() are called with a descriptor parameter provided in the
          237  +   inflateBack() call.  This parameter can be a structure that provides the
          238  +   information required to do the read or write, as well as accumulated
          239  +   information on the input and output such as totals and check values.
          240  +
          241  +   in() should return zero on failure.  out() should return non-zero on
          242  +   failure.  If either in() or out() fails, than inflateBack() returns a
          243  +   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it
          244  +   was in() or out() that caused in the error.  Otherwise,  inflateBack()
          245  +   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
          246  +   error, or Z_MEM_ERROR if it could not allocate memory for the state.
          247  +   inflateBack() can also return Z_STREAM_ERROR if the input parameters
          248  +   are not correct, i.e. strm is Z_NULL or the state was not initialized.
          249  + */
          250  +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
          251  +z_streamp strm;
          252  +in_func in;
          253  +void FAR *in_desc;
          254  +out_func out;
          255  +void FAR *out_desc;
          256  +{
          257  +    struct inflate_state FAR *state;
          258  +    unsigned char FAR *next;    /* next input */
          259  +    unsigned char FAR *put;     /* next output */
          260  +    unsigned have, left;        /* available input and output */
          261  +    unsigned long hold;         /* bit buffer */
          262  +    unsigned bits;              /* bits in bit buffer */
          263  +    unsigned copy;              /* number of stored or match bytes to copy */
          264  +    unsigned char FAR *from;    /* where to copy match bytes from */
          265  +    code here;                  /* current decoding table entry */
          266  +    code last;                  /* parent table entry */
          267  +    unsigned len;               /* length to copy for repeats, bits to drop */
          268  +    int ret;                    /* return code */
          269  +    static const unsigned short order[19] = /* permutation of code lengths */
          270  +        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
          271  +
          272  +    /* Check that the strm exists and that the state was initialized */
          273  +    if (strm == Z_NULL || strm->state == Z_NULL)
          274  +        return Z_STREAM_ERROR;
          275  +    state = (struct inflate_state FAR *)strm->state;
          276  +
          277  +    /* Reset the state */
          278  +    strm->msg = Z_NULL;
          279  +    state->mode = TYPE;
          280  +    state->last = 0;
          281  +    state->whave = 0;
          282  +    next = strm->next_in;
          283  +    have = next != Z_NULL ? strm->avail_in : 0;
          284  +    hold = 0;
          285  +    bits = 0;
          286  +    put = state->window;
          287  +    left = state->wsize;
          288  +
          289  +    /* Inflate until end of block marked as last */
          290  +    for (;;)
          291  +        switch (state->mode) {
          292  +        case TYPE:
          293  +            /* determine and dispatch block type */
          294  +            if (state->last) {
          295  +                BYTEBITS();
          296  +                state->mode = DONE;
          297  +                break;
          298  +            }
          299  +            NEEDBITS(3);
          300  +            state->last = BITS(1);
          301  +            DROPBITS(1);
          302  +            switch (BITS(2)) {
          303  +            case 0:                             /* stored block */
          304  +                Tracev((stderr, "inflate:     stored block%s\n",
          305  +                        state->last ? " (last)" : ""));
          306  +                state->mode = STORED;
          307  +                break;
          308  +            case 1:                             /* fixed block */
          309  +                fixedtables(state);
          310  +                Tracev((stderr, "inflate:     fixed codes block%s\n",
          311  +                        state->last ? " (last)" : ""));
          312  +                state->mode = LEN;              /* decode codes */
          313  +                break;
          314  +            case 2:                             /* dynamic block */
          315  +                Tracev((stderr, "inflate:     dynamic codes block%s\n",
          316  +                        state->last ? " (last)" : ""));
          317  +                state->mode = TABLE;
          318  +                break;
          319  +            case 3:
          320  +                strm->msg = (char *)"invalid block type";
          321  +                state->mode = BAD;
          322  +            }
          323  +            DROPBITS(2);
          324  +            break;
          325  +
          326  +        case STORED:
          327  +            /* get and verify stored block length */
          328  +            BYTEBITS();                         /* go to byte boundary */
          329  +            NEEDBITS(32);
          330  +            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
          331  +                strm->msg = (char *)"invalid stored block lengths";
          332  +                state->mode = BAD;
          333  +                break;
          334  +            }
          335  +            state->length = (unsigned)hold & 0xffff;
          336  +            Tracev((stderr, "inflate:       stored length %u\n",
          337  +                    state->length));
          338  +            INITBITS();
          339  +
          340  +            /* copy stored block from input to output */
          341  +            while (state->length != 0) {
          342  +                copy = state->length;
          343  +                PULL();
          344  +                ROOM();
          345  +                if (copy > have) copy = have;
          346  +                if (copy > left) copy = left;
          347  +                zmemcpy(put, next, copy);
          348  +                have -= copy;
          349  +                next += copy;
          350  +                left -= copy;
          351  +                put += copy;
          352  +                state->length -= copy;
          353  +            }
          354  +            Tracev((stderr, "inflate:       stored end\n"));
          355  +            state->mode = TYPE;
          356  +            break;
          357  +
          358  +        case TABLE:
          359  +            /* get dynamic table entries descriptor */
          360  +            NEEDBITS(14);
          361  +            state->nlen = BITS(5) + 257;
          362  +            DROPBITS(5);
          363  +            state->ndist = BITS(5) + 1;
          364  +            DROPBITS(5);
          365  +            state->ncode = BITS(4) + 4;
          366  +            DROPBITS(4);
          367  +#ifndef PKZIP_BUG_WORKAROUND
          368  +            if (state->nlen > 286 || state->ndist > 30) {
          369  +                strm->msg = (char *)"too many length or distance symbols";
          370  +                state->mode = BAD;
          371  +                break;
          372  +            }
          373  +#endif
          374  +            Tracev((stderr, "inflate:       table sizes ok\n"));
          375  +
          376  +            /* get code length code lengths (not a typo) */
          377  +            state->have = 0;
          378  +            while (state->have < state->ncode) {
          379  +                NEEDBITS(3);
          380  +                state->lens[order[state->have++]] = (unsigned short)BITS(3);
          381  +                DROPBITS(3);
          382  +            }
          383  +            while (state->have < 19)
          384  +                state->lens[order[state->have++]] = 0;
          385  +            state->next = state->codes;
          386  +            state->lencode = (code const FAR *)(state->next);
          387  +            state->lenbits = 7;
          388  +            ret = inflate_table(CODES, state->lens, 19, &(state->next),
          389  +                                &(state->lenbits), state->work);
          390  +            if (ret) {
          391  +                strm->msg = (char *)"invalid code lengths set";
          392  +                state->mode = BAD;
          393  +                break;
          394  +            }
          395  +            Tracev((stderr, "inflate:       code lengths ok\n"));
          396  +
          397  +            /* get length and distance code code lengths */
          398  +            state->have = 0;
          399  +            while (state->have < state->nlen + state->ndist) {
          400  +                for (;;) {
          401  +                    here = state->lencode[BITS(state->lenbits)];
          402  +                    if ((unsigned)(here.bits) <= bits) break;
          403  +                    PULLBYTE();
          404  +                }
          405  +                if (here.val < 16) {
          406  +                    DROPBITS(here.bits);
          407  +                    state->lens[state->have++] = here.val;
          408  +                }
          409  +                else {
          410  +                    if (here.val == 16) {
          411  +                        NEEDBITS(here.bits + 2);
          412  +                        DROPBITS(here.bits);
          413  +                        if (state->have == 0) {
          414  +                            strm->msg = (char *)"invalid bit length repeat";
          415  +                            state->mode = BAD;
          416  +                            break;
          417  +                        }
          418  +                        len = (unsigned)(state->lens[state->have - 1]);
          419  +                        copy = 3 + BITS(2);
          420  +                        DROPBITS(2);
          421  +                    }
          422  +                    else if (here.val == 17) {
          423  +                        NEEDBITS(here.bits + 3);
          424  +                        DROPBITS(here.bits);
          425  +                        len = 0;
          426  +                        copy = 3 + BITS(3);
          427  +                        DROPBITS(3);
          428  +                    }
          429  +                    else {
          430  +                        NEEDBITS(here.bits + 7);
          431  +                        DROPBITS(here.bits);
          432  +                        len = 0;
          433  +                        copy = 11 + BITS(7);
          434  +                        DROPBITS(7);
          435  +                    }
          436  +                    if (state->have + copy > state->nlen + state->ndist) {
          437  +                        strm->msg = (char *)"invalid bit length repeat";
          438  +                        state->mode = BAD;
          439  +                        break;
          440  +                    }
          441  +                    while (copy--)
          442  +                        state->lens[state->have++] = (unsigned short)len;
          443  +                }
          444  +            }
          445  +
          446  +            /* handle error breaks in while */
          447  +            if (state->mode == BAD) break;
          448  +
          449  +            /* check for end-of-block code (better have one) */
          450  +            if (state->lens[256] == 0) {
          451  +                strm->msg = (char *)"invalid code -- missing end-of-block";
          452  +                state->mode = BAD;
          453  +                break;
          454  +            }
          455  +
          456  +            /* build code tables -- note: do not change the lenbits or distbits
          457  +               values here (9 and 6) without reading the comments in inftrees.h
          458  +               concerning the ENOUGH constants, which depend on those values */
          459  +            state->next = state->codes;
          460  +            state->lencode = (code const FAR *)(state->next);
          461  +            state->lenbits = 9;
          462  +            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
          463  +                                &(state->lenbits), state->work);
          464  +            if (ret) {
          465  +                strm->msg = (char *)"invalid literal/lengths set";
          466  +                state->mode = BAD;
          467  +                break;
          468  +            }
          469  +            state->distcode = (code const FAR *)(state->next);
          470  +            state->distbits = 6;
          471  +            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
          472  +                            &(state->next), &(state->distbits), state->work);
          473  +            if (ret) {
          474  +                strm->msg = (char *)"invalid distances set";
          475  +                state->mode = BAD;
          476  +                break;
          477  +            }
          478  +            Tracev((stderr, "inflate:       codes ok\n"));
          479  +            state->mode = LEN;
          480  +
          481  +        case LEN:
          482  +            /* use inflate_fast() if we have enough input and output */
          483  +            if (have >= 6 && left >= 258) {
          484  +                RESTORE();
          485  +                if (state->whave < state->wsize)
          486  +                    state->whave = state->wsize - left;
          487  +                inflate_fast(strm, state->wsize);
          488  +                LOAD();
          489  +                break;
          490  +            }
          491  +
          492  +            /* get a literal, length, or end-of-block code */
          493  +            for (;;) {
          494  +                here = state->lencode[BITS(state->lenbits)];
          495  +                if ((unsigned)(here.bits) <= bits) break;
          496  +                PULLBYTE();
          497  +            }
          498  +            if (here.op && (here.op & 0xf0) == 0) {
          499  +                last = here;
          500  +                for (;;) {
          501  +                    here = state->lencode[last.val +
          502  +                            (BITS(last.bits + last.op) >> last.bits)];
          503  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
          504  +                    PULLBYTE();
          505  +                }
          506  +                DROPBITS(last.bits);
          507  +            }
          508  +            DROPBITS(here.bits);
          509  +            state->length = (unsigned)here.val;
          510  +
          511  +            /* process literal */
          512  +            if (here.op == 0) {
          513  +                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
          514  +                        "inflate:         literal '%c'\n" :
          515  +                        "inflate:         literal 0x%02x\n", here.val));
          516  +                ROOM();
          517  +                *put++ = (unsigned char)(state->length);
          518  +                left--;
          519  +                state->mode = LEN;
          520  +                break;
          521  +            }
          522  +
          523  +            /* process end of block */
          524  +            if (here.op & 32) {
          525  +                Tracevv((stderr, "inflate:         end of block\n"));
          526  +                state->mode = TYPE;
          527  +                break;
          528  +            }
          529  +
          530  +            /* invalid code */
          531  +            if (here.op & 64) {
          532  +                strm->msg = (char *)"invalid literal/length code";
          533  +                state->mode = BAD;
          534  +                break;
          535  +            }
          536  +
          537  +            /* length code -- get extra bits, if any */
          538  +            state->extra = (unsigned)(here.op) & 15;
          539  +            if (state->extra != 0) {
          540  +                NEEDBITS(state->extra);
          541  +                state->length += BITS(state->extra);
          542  +                DROPBITS(state->extra);
          543  +            }
          544  +            Tracevv((stderr, "inflate:         length %u\n", state->length));
          545  +
          546  +            /* get distance code */
          547  +            for (;;) {
          548  +                here = state->distcode[BITS(state->distbits)];
          549  +                if ((unsigned)(here.bits) <= bits) break;
          550  +                PULLBYTE();
          551  +            }
          552  +            if ((here.op & 0xf0) == 0) {
          553  +                last = here;
          554  +                for (;;) {
          555  +                    here = state->distcode[last.val +
          556  +                            (BITS(last.bits + last.op) >> last.bits)];
          557  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
          558  +                    PULLBYTE();
          559  +                }
          560  +                DROPBITS(last.bits);
          561  +            }
          562  +            DROPBITS(here.bits);
          563  +            if (here.op & 64) {
          564  +                strm->msg = (char *)"invalid distance code";
          565  +                state->mode = BAD;
          566  +                break;
          567  +            }
          568  +            state->offset = (unsigned)here.val;
          569  +
          570  +            /* get distance extra bits, if any */
          571  +            state->extra = (unsigned)(here.op) & 15;
          572  +            if (state->extra != 0) {
          573  +                NEEDBITS(state->extra);
          574  +                state->offset += BITS(state->extra);
          575  +                DROPBITS(state->extra);
          576  +            }
          577  +            if (state->offset > state->wsize - (state->whave < state->wsize ?
          578  +                                                left : 0)) {
          579  +                strm->msg = (char *)"invalid distance too far back";
          580  +                state->mode = BAD;
          581  +                break;
          582  +            }
          583  +            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
          584  +
          585  +            /* copy match from window to output */
          586  +            do {
          587  +                ROOM();
          588  +                copy = state->wsize - state->offset;
          589  +                if (copy < left) {
          590  +                    from = put + copy;
          591  +                    copy = left - copy;
          592  +                }
          593  +                else {
          594  +                    from = put - state->offset;
          595  +                    copy = left;
          596  +                }
          597  +                if (copy > state->length) copy = state->length;
          598  +                state->length -= copy;
          599  +                left -= copy;
          600  +                do {
          601  +                    *put++ = *from++;
          602  +                } while (--copy);
          603  +            } while (state->length != 0);
          604  +            break;
          605  +
          606  +        case DONE:
          607  +            /* inflate stream terminated properly -- write leftover output */
          608  +            ret = Z_STREAM_END;
          609  +            if (left < state->wsize) {
          610  +                if (out(out_desc, state->window, state->wsize - left))
          611  +                    ret = Z_BUF_ERROR;
          612  +            }
          613  +            goto inf_leave;
          614  +
          615  +        case BAD:
          616  +            ret = Z_DATA_ERROR;
          617  +            goto inf_leave;
          618  +
          619  +        default:                /* can't happen, but makes compilers happy */
          620  +            ret = Z_STREAM_ERROR;
          621  +            goto inf_leave;
          622  +        }
          623  +
          624  +    /* Return unused input */
          625  +  inf_leave:
          626  +    strm->next_in = next;
          627  +    strm->avail_in = have;
          628  +    return ret;
          629  +}
          630  +
          631  +int ZEXPORT inflateBackEnd(strm)
          632  +z_streamp strm;
          633  +{
          634  +    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
          635  +        return Z_STREAM_ERROR;
          636  +    ZFREE(strm, strm->state);
          637  +    strm->state = Z_NULL;
          638  +    Tracev((stderr, "inflate: end\n"));
          639  +    return Z_OK;
          640  +}

Added compat/zlib/inffast.c.

            1  +/* inffast.c -- fast decoding
            2  + * Copyright (C) 1995-2008, 2010 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "zutil.h"
            7  +#include "inftrees.h"
            8  +#include "inflate.h"
            9  +#include "inffast.h"
           10  +
           11  +#ifndef ASMINF
           12  +
           13  +/* Allow machine dependent optimization for post-increment or pre-increment.
           14  +   Based on testing to date,
           15  +   Pre-increment preferred for:
           16  +   - PowerPC G3 (Adler)
           17  +   - MIPS R5000 (Randers-Pehrson)
           18  +   Post-increment preferred for:
           19  +   - none
           20  +   No measurable difference:
           21  +   - Pentium III (Anderson)
           22  +   - M68060 (Nikl)
           23  + */
           24  +#ifdef POSTINC
           25  +#  define OFF 0
           26  +#  define PUP(a) *(a)++
           27  +#else
           28  +#  define OFF 1
           29  +#  define PUP(a) *++(a)
           30  +#endif
           31  +
           32  +/*
           33  +   Decode literal, length, and distance codes and write out the resulting
           34  +   literal and match bytes until either not enough input or output is
           35  +   available, an end-of-block is encountered, or a data error is encountered.
           36  +   When large enough input and output buffers are supplied to inflate(), for
           37  +   example, a 16K input buffer and a 64K output buffer, more than 95% of the
           38  +   inflate execution time is spent in this routine.
           39  +
           40  +   Entry assumptions:
           41  +
           42  +        state->mode == LEN
           43  +        strm->avail_in >= 6
           44  +        strm->avail_out >= 258
           45  +        start >= strm->avail_out
           46  +        state->bits < 8
           47  +
           48  +   On return, state->mode is one of:
           49  +
           50  +        LEN -- ran out of enough output space or enough available input
           51  +        TYPE -- reached end of block code, inflate() to interpret next block
           52  +        BAD -- error in block data
           53  +
           54  +   Notes:
           55  +
           56  +    - The maximum input bits used by a length/distance pair is 15 bits for the
           57  +      length code, 5 bits for the length extra, 15 bits for the distance code,
           58  +      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
           59  +      Therefore if strm->avail_in >= 6, then there is enough input to avoid
           60  +      checking for available input while decoding.
           61  +
           62  +    - The maximum bytes that a single length/distance pair can output is 258
           63  +      bytes, which is the maximum length that can be coded.  inflate_fast()
           64  +      requires strm->avail_out >= 258 for each loop to avoid checking for
           65  +      output space.
           66  + */
           67  +void ZLIB_INTERNAL inflate_fast(strm, start)
           68  +z_streamp strm;
           69  +unsigned start;         /* inflate()'s starting value for strm->avail_out */
           70  +{
           71  +    struct inflate_state FAR *state;
           72  +    unsigned char FAR *in;      /* local strm->next_in */
           73  +    unsigned char FAR *last;    /* while in < last, enough input available */
           74  +    unsigned char FAR *out;     /* local strm->next_out */
           75  +    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
           76  +    unsigned char FAR *end;     /* while out < end, enough space available */
           77  +#ifdef INFLATE_STRICT
           78  +    unsigned dmax;              /* maximum distance from zlib header */
           79  +#endif
           80  +    unsigned wsize;             /* window size or zero if not using window */
           81  +    unsigned whave;             /* valid bytes in the window */
           82  +    unsigned wnext;             /* window write index */
           83  +    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
           84  +    unsigned long hold;         /* local strm->hold */
           85  +    unsigned bits;              /* local strm->bits */
           86  +    code const FAR *lcode;      /* local strm->lencode */
           87  +    code const FAR *dcode;      /* local strm->distcode */
           88  +    unsigned lmask;             /* mask for first level of length codes */
           89  +    unsigned dmask;             /* mask for first level of distance codes */
           90  +    code here;                  /* retrieved table entry */
           91  +    unsigned op;                /* code bits, operation, extra bits, or */
           92  +                                /*  window position, window bytes to copy */
           93  +    unsigned len;               /* match length, unused bytes */
           94  +    unsigned dist;              /* match distance */
           95  +    unsigned char FAR *from;    /* where to copy match from */
           96  +
           97  +    /* copy state to local variables */
           98  +    state = (struct inflate_state FAR *)strm->state;
           99  +    in = strm->next_in - OFF;
          100  +    last = in + (strm->avail_in - 5);
          101  +    out = strm->next_out - OFF;
          102  +    beg = out - (start - strm->avail_out);
          103  +    end = out + (strm->avail_out - 257);
          104  +#ifdef INFLATE_STRICT
          105  +    dmax = state->dmax;
          106  +#endif
          107  +    wsize = state->wsize;
          108  +    whave = state->whave;
          109  +    wnext = state->wnext;
          110  +    window = state->window;
          111  +    hold = state->hold;
          112  +    bits = state->bits;
          113  +    lcode = state->lencode;
          114  +    dcode = state->distcode;
          115  +    lmask = (1U << state->lenbits) - 1;
          116  +    dmask = (1U << state->distbits) - 1;
          117  +
          118  +    /* decode literals and length/distances until end-of-block or not enough
          119  +       input data or output space */
          120  +    do {
          121  +        if (bits < 15) {
          122  +            hold += (unsigned long)(PUP(in)) << bits;
          123  +            bits += 8;
          124  +            hold += (unsigned long)(PUP(in)) << bits;
          125  +            bits += 8;
          126  +        }
          127  +        here = lcode[hold & lmask];
          128  +      dolen:
          129  +        op = (unsigned)(here.bits);
          130  +        hold >>= op;
          131  +        bits -= op;
          132  +        op = (unsigned)(here.op);
          133  +        if (op == 0) {                          /* literal */
          134  +            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
          135  +                    "inflate:         literal '%c'\n" :
          136  +                    "inflate:         literal 0x%02x\n", here.val));
          137  +            PUP(out) = (unsigned char)(here.val);
          138  +        }
          139  +        else if (op & 16) {                     /* length base */
          140  +            len = (unsigned)(here.val);
          141  +            op &= 15;                           /* number of extra bits */
          142  +            if (op) {
          143  +                if (bits < op) {
          144  +                    hold += (unsigned long)(PUP(in)) << bits;
          145  +                    bits += 8;
          146  +                }
          147  +                len += (unsigned)hold & ((1U << op) - 1);
          148  +                hold >>= op;
          149  +                bits -= op;
          150  +            }
          151  +            Tracevv((stderr, "inflate:         length %u\n", len));
          152  +            if (bits < 15) {
          153  +                hold += (unsigned long)(PUP(in)) << bits;
          154  +                bits += 8;
          155  +                hold += (unsigned long)(PUP(in)) << bits;
          156  +                bits += 8;
          157  +            }
          158  +            here = dcode[hold & dmask];
          159  +          dodist:
          160  +            op = (unsigned)(here.bits);
          161  +            hold >>= op;
          162  +            bits -= op;
          163  +            op = (unsigned)(here.op);
          164  +            if (op & 16) {                      /* distance base */
          165  +                dist = (unsigned)(here.val);
          166  +                op &= 15;                       /* number of extra bits */
          167  +                if (bits < op) {
          168  +                    hold += (unsigned long)(PUP(in)) << bits;
          169  +                    bits += 8;
          170  +                    if (bits < op) {
          171  +                        hold += (unsigned long)(PUP(in)) << bits;
          172  +                        bits += 8;
          173  +                    }
          174  +                }
          175  +                dist += (unsigned)hold & ((1U << op) - 1);
          176  +#ifdef INFLATE_STRICT
          177  +                if (dist > dmax) {
          178  +                    strm->msg = (char *)"invalid distance too far back";
          179  +                    state->mode = BAD;
          180  +                    break;
          181  +                }
          182  +#endif
          183  +                hold >>= op;
          184  +                bits -= op;
          185  +                Tracevv((stderr, "inflate:         distance %u\n", dist));
          186  +                op = (unsigned)(out - beg);     /* max distance in output */
          187  +                if (dist > op) {                /* see if copy from window */
          188  +                    op = dist - op;             /* distance back in window */
          189  +                    if (op > whave) {
          190  +                        if (state->sane) {
          191  +                            strm->msg =
          192  +                                (char *)"invalid distance too far back";
          193  +                            state->mode = BAD;
          194  +                            break;
          195  +                        }
          196  +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
          197  +                        if (len <= op - whave) {
          198  +                            do {
          199  +                                PUP(out) = 0;
          200  +                            } while (--len);
          201  +                            continue;
          202  +                        }
          203  +                        len -= op - whave;
          204  +                        do {
          205  +                            PUP(out) = 0;
          206  +                        } while (--op > whave);
          207  +                        if (op == 0) {
          208  +                            from = out - dist;
          209  +                            do {
          210  +                                PUP(out) = PUP(from);
          211  +                            } while (--len);
          212  +                            continue;
          213  +                        }
          214  +#endif
          215  +                    }
          216  +                    from = window - OFF;
          217  +                    if (wnext == 0) {           /* very common case */
          218  +                        from += wsize - op;
          219  +                        if (op < len) {         /* some from window */
          220  +                            len -= op;
          221  +                            do {
          222  +                                PUP(out) = PUP(from);
          223  +                            } while (--op);
          224  +                            from = out - dist;  /* rest from output */
          225  +                        }
          226  +                    }
          227  +                    else if (wnext < op) {      /* wrap around window */
          228  +                        from += wsize + wnext - op;
          229  +                        op -= wnext;
          230  +                        if (op < len) {         /* some from end of window */
          231  +                            len -= op;
          232  +                            do {
          233  +                                PUP(out) = PUP(from);
          234  +                            } while (--op);
          235  +                            from = window - OFF;
          236  +                            if (wnext < len) {  /* some from start of window */
          237  +                                op = wnext;
          238  +                                len -= op;
          239  +                                do {
          240  +                                    PUP(out) = PUP(from);
          241  +                                } while (--op);
          242  +                                from = out - dist;      /* rest from output */
          243  +                            }
          244  +                        }
          245  +                    }
          246  +                    else {                      /* contiguous in window */
          247  +                        from += wnext - op;
          248  +                        if (op < len) {         /* some from window */
          249  +                            len -= op;
          250  +                            do {
          251  +                                PUP(out) = PUP(from);
          252  +                            } while (--op);
          253  +                            from = out - dist;  /* rest from output */
          254  +                        }
          255  +                    }
          256  +                    while (len > 2) {
          257  +                        PUP(out) = PUP(from);
          258  +                        PUP(out) = PUP(from);
          259  +                        PUP(out) = PUP(from);
          260  +                        len -= 3;
          261  +                    }
          262  +                    if (len) {
          263  +                        PUP(out) = PUP(from);
          264  +                        if (len > 1)
          265  +                            PUP(out) = PUP(from);
          266  +                    }
          267  +                }
          268  +                else {
          269  +                    from = out - dist;          /* copy direct from output */
          270  +                    do {                        /* minimum length is three */
          271  +                        PUP(out) = PUP(from);
          272  +                        PUP(out) = PUP(from);
          273  +                        PUP(out) = PUP(from);
          274  +                        len -= 3;
          275  +                    } while (len > 2);
          276  +                    if (len) {
          277  +                        PUP(out) = PUP(from);
          278  +                        if (len > 1)
          279  +                            PUP(out) = PUP(from);
          280  +                    }
          281  +                }
          282  +            }
          283  +            else if ((op & 64) == 0) {          /* 2nd level distance code */
          284  +                here = dcode[here.val + (hold & ((1U << op) - 1))];
          285  +                goto dodist;
          286  +            }
          287  +            else {
          288  +                strm->msg = (char *)"invalid distance code";
          289  +                state->mode = BAD;
          290  +                break;
          291  +            }
          292  +        }
          293  +        else if ((op & 64) == 0) {              /* 2nd level length code */
          294  +            here = lcode[here.val + (hold & ((1U << op) - 1))];
          295  +            goto dolen;
          296  +        }
          297  +        else if (op & 32) {                     /* end-of-block */
          298  +            Tracevv((stderr, "inflate:         end of block\n"));
          299  +            state->mode = TYPE;
          300  +            break;
          301  +        }
          302  +        else {
          303  +            strm->msg = (char *)"invalid literal/length code";
          304  +            state->mode = BAD;
          305  +            break;
          306  +        }
          307  +    } while (in < last && out < end);
          308  +
          309  +    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
          310  +    len = bits >> 3;
          311  +    in -= len;
          312  +    bits -= len << 3;
          313  +    hold &= (1U << bits) - 1;
          314  +
          315  +    /* update state and return */
          316  +    strm->next_in = in + OFF;
          317  +    strm->next_out = out + OFF;
          318  +    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
          319  +    strm->avail_out = (unsigned)(out < end ?
          320  +                                 257 + (end - out) : 257 - (out - end));
          321  +    state->hold = hold;
          322  +    state->bits = bits;
          323  +    return;
          324  +}
          325  +
          326  +/*
          327  +   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
          328  +   - Using bit fields for code structure
          329  +   - Different op definition to avoid & for extra bits (do & for table bits)
          330  +   - Three separate decoding do-loops for direct, window, and wnext == 0
          331  +   - Special case for distance > 1 copies to do overlapped load and store copy
          332  +   - Explicit branch predictions (based on measured branch probabilities)
          333  +   - Deferring match copy and interspersed it with decoding subsequent codes
          334  +   - Swapping literal/length else
          335  +   - Swapping window/direct else
          336  +   - Larger unrolled copy loops (three is about right)
          337  +   - Moving len -= 3 statement into middle of loop
          338  + */
          339  +
          340  +#endif /* !ASMINF */

Added compat/zlib/inffast.h.

            1  +/* inffast.h -- header to use inffast.c
            2  + * Copyright (C) 1995-2003, 2010 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));

Added compat/zlib/inffixed.h.

            1  +    /* inffixed.h -- table for decoding fixed codes
            2  +     * Generated automatically by makefixed().
            3  +     */
            4  +
            5  +    /* WARNING: this file should *not* be used by applications.
            6  +       It is part of the implementation of this library and is
            7  +       subject to change. Applications should only use zlib.h.
            8  +     */
            9  +
           10  +    static const code lenfix[512] = {
           11  +        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
           12  +        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
           13  +        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
           14  +        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
           15  +        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
           16  +        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
           17  +        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
           18  +        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
           19  +        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
           20  +        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
           21  +        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
           22  +        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
           23  +        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
           24  +        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
           25  +        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
           26  +        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
           27  +        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
           28  +        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
           29  +        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
           30  +        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
           31  +        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
           32  +        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
           33  +        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
           34  +        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
           35  +        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
           36  +        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
           37  +        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
           38  +        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
           39  +        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
           40  +        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
           41  +        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
           42  +        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
           43  +        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
           44  +        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
           45  +        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
           46  +        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
           47  +        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
           48  +        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
           49  +        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
           50  +        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
           51  +        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
           52  +        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
           53  +        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
           54  +        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
           55  +        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
           56  +        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
           57  +        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
           58  +        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
           59  +        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
           60  +        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
           61  +        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
           62  +        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
           63  +        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
           64  +        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
           65  +        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
           66  +        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
           67  +        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
           68  +        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
           69  +        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
           70  +        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
           71  +        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
           72  +        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
           73  +        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
           74  +        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
           75  +        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
           76  +        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
           77  +        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
           78  +        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
           79  +        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
           80  +        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
           81  +        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
           82  +        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
           83  +        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
           84  +        {0,9,255}
           85  +    };
           86  +
           87  +    static const code distfix[32] = {
           88  +        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
           89  +        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
           90  +        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
           91  +        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
           92  +        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
           93  +        {22,5,193},{64,5,0}
           94  +    };

Added compat/zlib/inflate.c.

            1  +/* inflate.c -- zlib decompression
            2  + * Copyright (C) 1995-2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/*
            7  + * Change history:
            8  + *
            9  + * 1.2.beta0    24 Nov 2002
           10  + * - First version -- complete rewrite of inflate to simplify code, avoid
           11  + *   creation of window when not needed, minimize use of window when it is
           12  + *   needed, make inffast.c even faster, implement gzip decoding, and to
           13  + *   improve code readability and style over the previous zlib inflate code
           14  + *
           15  + * 1.2.beta1    25 Nov 2002
           16  + * - Use pointers for available input and output checking in inffast.c
           17  + * - Remove input and output counters in inffast.c
           18  + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
           19  + * - Remove unnecessary second byte pull from length extra in inffast.c
           20  + * - Unroll direct copy to three copies per loop in inffast.c
           21  + *
           22  + * 1.2.beta2    4 Dec 2002
           23  + * - Change external routine names to reduce potential conflicts
           24  + * - Correct filename to inffixed.h for fixed tables in inflate.c
           25  + * - Make hbuf[] unsigned char to match parameter type in inflate.c
           26  + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
           27  + *   to avoid negation problem on Alphas (64 bit) in inflate.c
           28  + *
           29  + * 1.2.beta3    22 Dec 2002
           30  + * - Add comments on state->bits assertion in inffast.c
           31  + * - Add comments on op field in inftrees.h
           32  + * - Fix bug in reuse of allocated window after inflateReset()
           33  + * - Remove bit fields--back to byte structure for speed
           34  + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
           35  + * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
           36  + * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
           37  + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
           38  + * - Use local copies of stream next and avail values, as well as local bit
           39  + *   buffer and bit count in inflate()--for speed when inflate_fast() not used
           40  + *
           41  + * 1.2.beta4    1 Jan 2003
           42  + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
           43  + * - Move a comment on output buffer sizes from inffast.c to inflate.c
           44  + * - Add comments in inffast.c to introduce the inflate_fast() routine
           45  + * - Rearrange window copies in inflate_fast() for speed and simplification
           46  + * - Unroll last copy for window match in inflate_fast()
           47  + * - Use local copies of window variables in inflate_fast() for speed
           48  + * - Pull out common wnext == 0 case for speed in inflate_fast()
           49  + * - Make op and len in inflate_fast() unsigned for consistency
           50  + * - Add FAR to lcode and dcode declarations in inflate_fast()
           51  + * - Simplified bad distance check in inflate_fast()
           52  + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
           53  + *   source file infback.c to provide a call-back interface to inflate for
           54  + *   programs like gzip and unzip -- uses window as output buffer to avoid
           55  + *   window copying
           56  + *
           57  + * 1.2.beta5    1 Jan 2003
           58  + * - Improved inflateBack() interface to allow the caller to provide initial
           59  + *   input in strm.
           60  + * - Fixed stored blocks bug in inflateBack()
           61  + *
           62  + * 1.2.beta6    4 Jan 2003
           63  + * - Added comments in inffast.c on effectiveness of POSTINC
           64  + * - Typecasting all around to reduce compiler warnings
           65  + * - Changed loops from while (1) or do {} while (1) to for (;;), again to
           66  + *   make compilers happy
           67  + * - Changed type of window in inflateBackInit() to unsigned char *
           68  + *
           69  + * 1.2.beta7    27 Jan 2003
           70  + * - Changed many types to unsigned or unsigned short to avoid warnings
           71  + * - Added inflateCopy() function
           72  + *
           73  + * 1.2.0        9 Mar 2003
           74  + * - Changed inflateBack() interface to provide separate opaque descriptors
           75  + *   for the in() and out() functions
           76  + * - Changed inflateBack() argument and in_func typedef to swap the length
           77  + *   and buffer address return values for the input function
           78  + * - Check next_in and next_out for Z_NULL on entry to inflate()
           79  + *
           80  + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
           81  + */
           82  +
           83  +#include "zutil.h"
           84  +#include "inftrees.h"
           85  +#include "inflate.h"
           86  +#include "inffast.h"
           87  +
           88  +#ifdef MAKEFIXED
           89  +#  ifndef BUILDFIXED
           90  +#    define BUILDFIXED
           91  +#  endif
           92  +#endif
           93  +
           94  +/* function prototypes */
           95  +local void fixedtables OF((struct inflate_state FAR *state));
           96  +local int updatewindow OF((z_streamp strm, unsigned out));
           97  +#ifdef BUILDFIXED
           98  +   void makefixed OF((void));
           99  +#endif
          100  +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
          101  +                              unsigned len));
          102  +
          103  +int ZEXPORT inflateResetKeep(strm)
          104  +z_streamp strm;
          105  +{
          106  +    struct inflate_state FAR *state;
          107  +
          108  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          109  +    state = (struct inflate_state FAR *)strm->state;
          110  +    strm->total_in = strm->total_out = state->total = 0;
          111  +    strm->msg = Z_NULL;
          112  +    if (state->wrap)        /* to support ill-conceived Java test suite */
          113  +        strm->adler = state->wrap & 1;
          114  +    state->mode = HEAD;
          115  +    state->last = 0;
          116  +    state->havedict = 0;
          117  +    state->dmax = 32768U;
          118  +    state->head = Z_NULL;
          119  +    state->hold = 0;
          120  +    state->bits = 0;
          121  +    state->lencode = state->distcode = state->next = state->codes;
          122  +    state->sane = 1;
          123  +    state->back = -1;
          124  +    Tracev((stderr, "inflate: reset\n"));
          125  +    return Z_OK;
          126  +}
          127  +
          128  +int ZEXPORT inflateReset(strm)
          129  +z_streamp strm;
          130  +{
          131  +    struct inflate_state FAR *state;
          132  +
          133  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          134  +    state = (struct inflate_state FAR *)strm->state;
          135  +    state->wsize = 0;
          136  +    state->whave = 0;
          137  +    state->wnext = 0;
          138  +    return inflateResetKeep(strm);
          139  +}
          140  +
          141  +int ZEXPORT inflateReset2(strm, windowBits)
          142  +z_streamp strm;
          143  +int windowBits;
          144  +{
          145  +    int wrap;
          146  +    struct inflate_state FAR *state;
          147  +
          148  +    /* get the state */
          149  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          150  +    state = (struct inflate_state FAR *)strm->state;
          151  +
          152  +    /* extract wrap request from windowBits parameter */
          153  +    if (windowBits < 0) {
          154  +        wrap = 0;
          155  +        windowBits = -windowBits;
          156  +    }
          157  +    else {
          158  +        wrap = (windowBits >> 4) + 1;
          159  +#ifdef GUNZIP
          160  +        if (windowBits < 48)
          161  +            windowBits &= 15;
          162  +#endif
          163  +    }
          164  +
          165  +    /* set number of window bits, free window if different */
          166  +    if (windowBits && (windowBits < 8 || windowBits > 15))
          167  +        return Z_STREAM_ERROR;
          168  +    if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
          169  +        ZFREE(strm, state->window);
          170  +        state->window = Z_NULL;
          171  +    }
          172  +
          173  +    /* update state and reset the rest of it */
          174  +    state->wrap = wrap;
          175  +    state->wbits = (unsigned)windowBits;
          176  +    return inflateReset(strm);
          177  +}
          178  +
          179  +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
          180  +z_streamp strm;
          181  +int windowBits;
          182  +const char *version;
          183  +int stream_size;
          184  +{
          185  +    int ret;
          186  +    struct inflate_state FAR *state;
          187  +
          188  +    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
          189  +        stream_size != (int)(sizeof(z_stream)))
          190  +        return Z_VERSION_ERROR;
          191  +    if (strm == Z_NULL) return Z_STREAM_ERROR;
          192  +    strm->msg = Z_NULL;                 /* in case we return an error */
          193  +    if (strm->zalloc == (alloc_func)0) {
          194  +#ifdef Z_SOLO
          195  +        return Z_STREAM_ERROR;
          196  +#else
          197  +        strm->zalloc = zcalloc;
          198  +        strm->opaque = (voidpf)0;
          199  +#endif
          200  +    }
          201  +    if (strm->zfree == (free_func)0)
          202  +#ifdef Z_SOLO
          203  +        return Z_STREAM_ERROR;
          204  +#else
          205  +        strm->zfree = zcfree;
          206  +#endif
          207  +    state = (struct inflate_state FAR *)
          208  +            ZALLOC(strm, 1, sizeof(struct inflate_state));
          209  +    if (state == Z_NULL) return Z_MEM_ERROR;
          210  +    Tracev((stderr, "inflate: allocated\n"));
          211  +    strm->state = (struct internal_state FAR *)state;
          212  +    state->window = Z_NULL;
          213  +    ret = inflateReset2(strm, windowBits);
          214  +    if (ret != Z_OK) {
          215  +        ZFREE(strm, state);
          216  +        strm->state = Z_NULL;
          217  +    }
          218  +    return ret;
          219  +}
          220  +
          221  +int ZEXPORT inflateInit_(strm, version, stream_size)
          222  +z_streamp strm;
          223  +const char *version;
          224  +int stream_size;
          225  +{
          226  +    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
          227  +}
          228  +
          229  +int ZEXPORT inflatePrime(strm, bits, value)
          230  +z_streamp strm;
          231  +int bits;
          232  +int value;
          233  +{
          234  +    struct inflate_state FAR *state;
          235  +
          236  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
          237  +    state = (struct inflate_state FAR *)strm->state;
          238  +    if (bits < 0) {
          239  +        state->hold = 0;
          240  +        state->bits = 0;
          241  +        return Z_OK;
          242  +    }
          243  +    if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
          244  +    value &= (1L << bits) - 1;
          245  +    state->hold += value << state->bits;
          246  +    state->bits += bits;
          247  +    return Z_OK;
          248  +}
          249  +
          250  +/*
          251  +   Return state with length and distance decoding tables and index sizes set to
          252  +   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
          253  +   If BUILDFIXED is defined, then instead this routine builds the tables the
          254  +   first time it's called, and returns those tables the first time and
          255  +   thereafter.  This reduces the size of the code by about 2K bytes, in
          256  +   exchange for a little execution time.  However, BUILDFIXED should not be
          257  +   used for threaded applications, since the rewriting of the tables and virgin
          258  +   may not be thread-safe.
          259  + */
          260  +local void fixedtables(state)
          261  +struct inflate_state FAR *state;
          262  +{
          263  +#ifdef BUILDFIXED
          264  +    static int virgin = 1;
          265  +    static code *lenfix, *distfix;
          266  +    static code fixed[544];
          267  +
          268  +    /* build fixed huffman tables if first call (may not be thread safe) */
          269  +    if (virgin) {
          270  +        unsigned sym, bits;
          271  +        static code *next;
          272  +
          273  +        /* literal/length table */
          274  +        sym = 0;
          275  +        while (sym < 144) state->lens[sym++] = 8;
          276  +        while (sym < 256) state->lens[sym++] = 9;
          277  +        while (sym < 280) state->lens[sym++] = 7;
          278  +        while (sym < 288) state->lens[sym++] = 8;
          279  +        next = fixed;
          280  +        lenfix = next;
          281  +        bits = 9;
          282  +        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
          283  +
          284  +        /* distance table */
          285  +        sym = 0;
          286  +        while (sym < 32) state->lens[sym++] = 5;
          287  +        distfix = next;
          288  +        bits = 5;
          289  +        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
          290  +
          291  +        /* do this just once */
          292  +        virgin = 0;
          293  +    }
          294  +#else /* !BUILDFIXED */
          295  +#   include "inffixed.h"
          296  +#endif /* BUILDFIXED */
          297  +    state->lencode = lenfix;
          298  +    state->lenbits = 9;
          299  +    state->distcode = distfix;
          300  +    state->distbits = 5;
          301  +}
          302  +
          303  +#ifdef MAKEFIXED
          304  +#include <stdio.h>
          305  +
          306  +/*
          307  +   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
          308  +   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
          309  +   those tables to stdout, which would be piped to inffixed.h.  A small program
          310  +   can simply call makefixed to do this:
          311  +
          312  +    void makefixed(void);
          313  +
          314  +    int main(void)
          315  +    {
          316  +        makefixed();
          317  +        return 0;
          318  +    }
          319  +
          320  +   Then that can be linked with zlib built with MAKEFIXED defined and run:
          321  +
          322  +    a.out > inffixed.h
          323  + */
          324  +void makefixed()
          325  +{
          326  +    unsigned low, size;
          327  +    struct inflate_state state;
          328  +
          329  +    fixedtables(&state);
          330  +    puts("    /* inffixed.h -- table for decoding fixed codes");
          331  +    puts("     * Generated automatically by makefixed().");
          332  +    puts("     */");
          333  +    puts("");
          334  +    puts("    /* WARNING: this file should *not* be used by applications.");
          335  +    puts("       It is part of the implementation of this library and is");
          336  +    puts("       subject to change. Applications should only use zlib.h.");
          337  +    puts("     */");
          338  +    puts("");
          339  +    size = 1U << 9;
          340  +    printf("    static const code lenfix[%u] = {", size);
          341  +    low = 0;
          342  +    for (;;) {
          343  +        if ((low % 7) == 0) printf("\n        ");
          344  +        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
          345  +               state.lencode[low].bits, state.lencode[low].val);
          346  +        if (++low == size) break;
          347  +        putchar(',');
          348  +    }
          349  +    puts("\n    };");
          350  +    size = 1U << 5;
          351  +    printf("\n    static const code distfix[%u] = {", size);
          352  +    low = 0;
          353  +    for (;;) {
          354  +        if ((low % 6) == 0) printf("\n        ");
          355  +        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
          356  +               state.distcode[low].val);
          357  +        if (++low == size) break;
          358  +        putchar(',');
          359  +    }
          360  +    puts("\n    };");
          361  +}
          362  +#endif /* MAKEFIXED */
          363  +
          364  +/*
          365  +   Update the window with the last wsize (normally 32K) bytes written before
          366  +   returning.  If window does not exist yet, create it.  This is only called
          367  +   when a window is already in use, or when output has been written during this
          368  +   inflate call, but the end of the deflate stream has not been reached yet.
          369  +   It is also called to create a window for dictionary data when a dictionary
          370  +   is loaded.
          371  +
          372  +   Providing output buffers larger than 32K to inflate() should provide a speed
          373  +   advantage, since only the last 32K of output is copied to the sliding window
          374  +   upon return from inflate(), and since all distances after the first 32K of
          375  +   output will fall in the output data, making match copies simpler and faster.
          376  +   The advantage may be dependent on the size of the processor's data caches.
          377  + */
          378  +local int updatewindow(strm, out)
          379  +z_streamp strm;
          380  +unsigned out;
          381  +{
          382  +    struct inflate_state FAR *state;
          383  +    unsigned copy, dist;
          384  +
          385  +    state = (struct inflate_state FAR *)strm->state;
          386  +
          387  +    /* if it hasn't been done already, allocate space for the window */
          388  +    if (state->window == Z_NULL) {
          389  +        state->window = (unsigned char FAR *)
          390  +                        ZALLOC(strm, 1U << state->wbits,
          391  +                               sizeof(unsigned char));
          392  +        if (state->window == Z_NULL) return 1;
          393  +    }
          394  +
          395  +    /* if window not in use yet, initialize */
          396  +    if (state->wsize == 0) {
          397  +        state->wsize = 1U << state->wbits;
          398  +        state->wnext = 0;
          399  +        state->whave = 0;
          400  +    }
          401  +
          402  +    /* copy state->wsize or less output bytes into the circular window */
          403  +    copy = out - strm->avail_out;
          404  +    if (copy >= state->wsize) {
          405  +        zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
          406  +        state->wnext = 0;
          407  +        state->whave = state->wsize;
          408  +    }
          409  +    else {
          410  +        dist = state->wsize - state->wnext;
          411  +        if (dist > copy) dist = copy;
          412  +        zmemcpy(state->window + state->wnext, strm->next_out - copy, dist);
          413  +        copy -= dist;
          414  +        if (copy) {
          415  +            zmemcpy(state->window, strm->next_out - copy, copy);
          416  +            state->wnext = copy;
          417  +            state->whave = state->wsize;
          418  +        }
          419  +        else {
          420  +            state->wnext += dist;
          421  +            if (state->wnext == state->wsize) state->wnext = 0;
          422  +            if (state->whave < state->wsize) state->whave += dist;
          423  +        }
          424  +    }
          425  +    return 0;
          426  +}
          427  +
          428  +/* Macros for inflate(): */
          429  +
          430  +/* check function to use adler32() for zlib or crc32() for gzip */
          431  +#ifdef GUNZIP
          432  +#  define UPDATE(check, buf, len) \
          433  +    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
          434  +#else
          435  +#  define UPDATE(check, buf, len) adler32(check, buf, len)
          436  +#endif
          437  +
          438  +/* check macros for header crc */
          439  +#ifdef GUNZIP
          440  +#  define CRC2(check, word) \
          441  +    do { \
          442  +        hbuf[0] = (unsigned char)(word); \
          443  +        hbuf[1] = (unsigned char)((word) >> 8); \
          444  +        check = crc32(check, hbuf, 2); \
          445  +    } while (0)
          446  +
          447  +#  define CRC4(check, word) \
          448  +    do { \
          449  +        hbuf[0] = (unsigned char)(word); \
          450  +        hbuf[1] = (unsigned char)((word) >> 8); \
          451  +        hbuf[2] = (unsigned char)((word) >> 16); \
          452  +        hbuf[3] = (unsigned char)((word) >> 24); \
          453  +        check = crc32(check, hbuf, 4); \
          454  +    } while (0)
          455  +#endif
          456  +
          457  +/* Load registers with state in inflate() for speed */
          458  +#define LOAD() \
          459  +    do { \
          460  +        put = strm->next_out; \
          461  +        left = strm->avail_out; \
          462  +        next = strm->next_in; \
          463  +        have = strm->avail_in; \
          464  +        hold = state->hold; \
          465  +        bits = state->bits; \
          466  +    } while (0)
          467  +
          468  +/* Restore state from registers in inflate() */
          469  +#define RESTORE() \
          470  +    do { \
          471  +        strm->next_out = put; \
          472  +        strm->avail_out = left; \
          473  +        strm->next_in = next; \
          474  +        strm->avail_in = have; \
          475  +        state->hold = hold; \
          476  +        state->bits = bits; \
          477  +    } while (0)
          478  +
          479  +/* Clear the input bit accumulator */
          480  +#define INITBITS() \
          481  +    do { \
          482  +        hold = 0; \
          483  +        bits = 0; \
          484  +    } while (0)
          485  +
          486  +/* Get a byte of input into the bit accumulator, or return from inflate()
          487  +   if there is no input available. */
          488  +#define PULLBYTE() \
          489  +    do { \
          490  +        if (have == 0) goto inf_leave; \
          491  +        have--; \
          492  +        hold += (unsigned long)(*next++) << bits; \
          493  +        bits += 8; \
          494  +    } while (0)
          495  +
          496  +/* Assure that there are at least n bits in the bit accumulator.  If there is
          497  +   not enough available input to do that, then return from inflate(). */
          498  +#define NEEDBITS(n) \
          499  +    do { \
          500  +        while (bits < (unsigned)(n)) \
          501  +            PULLBYTE(); \
          502  +    } while (0)
          503  +
          504  +/* Return the low n bits of the bit accumulator (n < 16) */
          505  +#define BITS(n) \
          506  +    ((unsigned)hold & ((1U << (n)) - 1))
          507  +
          508  +/* Remove n bits from the bit accumulator */
          509  +#define DROPBITS(n) \
          510  +    do { \
          511  +        hold >>= (n); \
          512  +        bits -= (unsigned)(n); \
          513  +    } while (0)
          514  +
          515  +/* Remove zero to seven bits as needed to go to a byte boundary */
          516  +#define BYTEBITS() \
          517  +    do { \
          518  +        hold >>= bits & 7; \
          519  +        bits -= bits & 7; \
          520  +    } while (0)
          521  +
          522  +/*
          523  +   inflate() uses a state machine to process as much input data and generate as
          524  +   much output data as possible before returning.  The state machine is
          525  +   structured roughly as follows:
          526  +
          527  +    for (;;) switch (state) {
          528  +    ...
          529  +    case STATEn:
          530  +        if (not enough input data or output space to make progress)
          531  +            return;
          532  +        ... make progress ...
          533  +        state = STATEm;
          534  +        break;
          535  +    ...
          536  +    }
          537  +
          538  +   so when inflate() is called again, the same case is attempted again, and
          539  +   if the appropriate resources are provided, the machine proceeds to the
          540  +   next state.  The NEEDBITS() macro is usually the way the state evaluates
          541  +   whether it can proceed or should return.  NEEDBITS() does the return if
          542  +   the requested bits are not available.  The typical use of the BITS macros
          543  +   is:
          544  +
          545  +        NEEDBITS(n);
          546  +        ... do something with BITS(n) ...
          547  +        DROPBITS(n);
          548  +
          549  +   where NEEDBITS(n) either returns from inflate() if there isn't enough
          550  +   input left to load n bits into the accumulator, or it continues.  BITS(n)
          551  +   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
          552  +   the low n bits off the accumulator.  INITBITS() clears the accumulator
          553  +   and sets the number of available bits to zero.  BYTEBITS() discards just
          554  +   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
          555  +   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
          556  +
          557  +   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
          558  +   if there is no input available.  The decoding of variable length codes uses
          559  +   PULLBYTE() directly in order to pull just enough bytes to decode the next
          560  +   code, and no more.
          561  +
          562  +   Some states loop until they get enough input, making sure that enough
          563  +   state information is maintained to continue the loop where it left off
          564  +   if NEEDBITS() returns in the loop.  For example, want, need, and keep
          565  +   would all have to actually be part of the saved state in case NEEDBITS()
          566  +   returns:
          567  +
          568  +    case STATEw:
          569  +        while (want < need) {
          570  +            NEEDBITS(n);
          571  +            keep[want++] = BITS(n);
          572  +            DROPBITS(n);
          573  +        }
          574  +        state = STATEx;
          575  +    case STATEx:
          576  +
          577  +   As shown above, if the next state is also the next case, then the break
          578  +   is omitted.
          579  +
          580  +   A state may also return if there is not enough output space available to
          581  +   complete that state.  Those states are copying stored data, writing a
          582  +   literal byte, and copying a matching string.
          583  +
          584  +   When returning, a "goto inf_leave" is used to update the total counters,
          585  +   update the check value, and determine whether any progress has been made
          586  +   during that inflate() call in order to return the proper return code.
          587  +   Progress is defined as a change in either strm->avail_in or strm->avail_out.
          588  +   When there is a window, goto inf_leave will update the window with the last
          589  +   output written.  If a goto inf_leave occurs in the middle of decompression
          590  +   and there is no window currently, goto inf_leave will create one and copy
          591  +   output to the window for the next call of inflate().
          592  +
          593  +   In this implementation, the flush parameter of inflate() only affects the
          594  +   return code (per zlib.h).  inflate() always writes as much as possible to
          595  +   strm->next_out, given the space available and the provided input--the effect
          596  +   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
          597  +   the allocation of and copying into a sliding window until necessary, which
          598  +   provides the effect documented in zlib.h for Z_FINISH when the entire input
          599  +   stream available.  So the only thing the flush parameter actually does is:
          600  +   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
          601  +   will return Z_BUF_ERROR if it has not reached the end of the stream.
          602  + */
          603  +
          604  +int ZEXPORT inflate(strm, flush)
          605  +z_streamp strm;
          606  +int flush;
          607  +{
          608  +    struct inflate_state FAR *state;
          609  +    unsigned char FAR *next;    /* next input */
          610  +    unsigned char FAR *put;     /* next output */
          611  +    unsigned have, left;        /* available input and output */
          612  +    unsigned long hold;         /* bit buffer */
          613  +    unsigned bits;              /* bits in bit buffer */
          614  +    unsigned in, out;           /* save starting available input and output */
          615  +    unsigned copy;              /* number of stored or match bytes to copy */
          616  +    unsigned char FAR *from;    /* where to copy match bytes from */
          617  +    code here;                  /* current decoding table entry */
          618  +    code last;                  /* parent table entry */
          619  +    unsigned len;               /* length to copy for repeats, bits to drop */
          620  +    int ret;                    /* return code */
          621  +#ifdef GUNZIP
          622  +    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
          623  +#endif
          624  +    static const unsigned short order[19] = /* permutation of code lengths */
          625  +        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
          626  +
          627  +    if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
          628  +        (strm->next_in == Z_NULL && strm->avail_in != 0))
          629  +        return Z_STREAM_ERROR;
          630  +
          631  +    state = (struct inflate_state FAR *)strm->state;
          632  +    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
          633  +    LOAD();
          634  +    in = have;
          635  +    out = left;
          636  +    ret = Z_OK;
          637  +    for (;;)
          638  +        switch (state->mode) {
          639  +        case HEAD:
          640  +            if (state->wrap == 0) {
          641  +                state->mode = TYPEDO;
          642  +                break;
          643  +            }
          644  +            NEEDBITS(16);
          645  +#ifdef GUNZIP
          646  +            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
          647  +                state->check = crc32(0L, Z_NULL, 0);
          648  +                CRC2(state->check, hold);
          649  +                INITBITS();
          650  +                state->mode = FLAGS;
          651  +                break;
          652  +            }
          653  +            state->flags = 0;           /* expect zlib header */
          654  +            if (state->head != Z_NULL)
          655  +                state->head->done = -1;
          656  +            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
          657  +#else
          658  +            if (
          659  +#endif
          660  +                ((BITS(8) << 8) + (hold >> 8)) % 31) {
          661  +                strm->msg = (char *)"incorrect header check";
          662  +                state->mode = BAD;
          663  +                break;
          664  +            }
          665  +            if (BITS(4) != Z_DEFLATED) {
          666  +                strm->msg = (char *)"unknown compression method";
          667  +                state->mode = BAD;
          668  +                break;
          669  +            }
          670  +            DROPBITS(4);
          671  +            len = BITS(4) + 8;
          672  +            if (state->wbits == 0)
          673  +                state->wbits = len;
          674  +            else if (len > state->wbits) {
          675  +                strm->msg = (char *)"invalid window size";
          676  +                state->mode = BAD;
          677  +                break;
          678  +            }
          679  +            state->dmax = 1U << len;
          680  +            Tracev((stderr, "inflate:   zlib header ok\n"));
          681  +            strm->adler = state->check = adler32(0L, Z_NULL, 0);
          682  +            state->mode = hold & 0x200 ? DICTID : TYPE;
          683  +            INITBITS();
          684  +            break;
          685  +#ifdef GUNZIP
          686  +        case FLAGS:
          687  +            NEEDBITS(16);
          688  +            state->flags = (int)(hold);
          689  +            if ((state->flags & 0xff) != Z_DEFLATED) {
          690  +                strm->msg = (char *)"unknown compression method";
          691  +                state->mode = BAD;
          692  +                break;
          693  +            }
          694  +            if (state->flags & 0xe000) {
          695  +                strm->msg = (char *)"unknown header flags set";
          696  +                state->mode = BAD;
          697  +                break;
          698  +            }
          699  +            if (state->head != Z_NULL)
          700  +                state->head->text = (int)((hold >> 8) & 1);
          701  +            if (state->flags & 0x0200) CRC2(state->check, hold);
          702  +            INITBITS();
          703  +            state->mode = TIME;
          704  +        case TIME:
          705  +            NEEDBITS(32);
          706  +            if (state->head != Z_NULL)
          707  +                state->head->time = hold;
          708  +            if (state->flags & 0x0200) CRC4(state->check, hold);
          709  +            INITBITS();
          710  +            state->mode = OS;
          711  +        case OS:
          712  +            NEEDBITS(16);
          713  +            if (state->head != Z_NULL) {
          714  +                state->head->xflags = (int)(hold & 0xff);
          715  +                state->head->os = (int)(hold >> 8);
          716  +            }
          717  +            if (state->flags & 0x0200) CRC2(state->check, hold);
          718  +            INITBITS();
          719  +            state->mode = EXLEN;
          720  +        case EXLEN:
          721  +            if (state->flags & 0x0400) {
          722  +                NEEDBITS(16);
          723  +                state->length = (unsigned)(hold);
          724  +                if (state->head != Z_NULL)
          725  +                    state->head->extra_len = (unsigned)hold;
          726  +                if (state->flags & 0x0200) CRC2(state->check, hold);
          727  +                INITBITS();
          728  +            }
          729  +            else if (state->head != Z_NULL)
          730  +                state->head->extra = Z_NULL;
          731  +            state->mode = EXTRA;
          732  +        case EXTRA:
          733  +            if (state->flags & 0x0400) {
          734  +                copy = state->length;
          735  +                if (copy > have) copy = have;
          736  +                if (copy) {
          737  +                    if (state->head != Z_NULL &&
          738  +                        state->head->extra != Z_NULL) {
          739  +                        len = state->head->extra_len - state->length;
          740  +                        zmemcpy(state->head->extra + len, next,
          741  +                                len + copy > state->head->extra_max ?
          742  +                                state->head->extra_max - len : copy);
          743  +                    }
          744  +                    if (state->flags & 0x0200)
          745  +                        state->check = crc32(state->check, next, copy);
          746  +                    have -= copy;
          747  +                    next += copy;
          748  +                    state->length -= copy;
          749  +                }
          750  +                if (state->length) goto inf_leave;
          751  +            }
          752  +            state->length = 0;
          753  +            state->mode = NAME;
          754  +        case NAME:
          755  +            if (state->flags & 0x0800) {
          756  +                if (have == 0) goto inf_leave;
          757  +                copy = 0;
          758  +                do {
          759  +                    len = (unsigned)(next[copy++]);
          760  +                    if (state->head != Z_NULL &&
          761  +                            state->head->name != Z_NULL &&
          762  +                            state->length < state->head->name_max)
          763  +                        state->head->name[state->length++] = len;
          764  +                } while (len && copy < have);
          765  +                if (state->flags & 0x0200)
          766  +                    state->check = crc32(state->check, next, copy);
          767  +                have -= copy;
          768  +                next += copy;
          769  +                if (len) goto inf_leave;
          770  +            }
          771  +            else if (state->head != Z_NULL)
          772  +                state->head->name = Z_NULL;
          773  +            state->length = 0;
          774  +            state->mode = COMMENT;
          775  +        case COMMENT:
          776  +            if (state->flags & 0x1000) {
          777  +                if (have == 0) goto inf_leave;
          778  +                copy = 0;
          779  +                do {
          780  +                    len = (unsigned)(next[copy++]);
          781  +                    if (state->head != Z_NULL &&
          782  +                            state->head->comment != Z_NULL &&
          783  +                            state->length < state->head->comm_max)
          784  +                        state->head->comment[state->length++] = len;
          785  +                } while (len && copy < have);
          786  +                if (state->flags & 0x0200)
          787  +                    state->check = crc32(state->check, next, copy);
          788  +                have -= copy;
          789  +                next += copy;
          790  +                if (len) goto inf_leave;
          791  +            }
          792  +            else if (state->head != Z_NULL)
          793  +                state->head->comment = Z_NULL;
          794  +            state->mode = HCRC;
          795  +        case HCRC:
          796  +            if (state->flags & 0x0200) {
          797  +                NEEDBITS(16);
          798  +                if (hold != (state->check & 0xffff)) {
          799  +                    strm->msg = (char *)"header crc mismatch";
          800  +                    state->mode = BAD;
          801  +                    break;
          802  +                }
          803  +                INITBITS();
          804  +            }
          805  +            if (state->head != Z_NULL) {
          806  +                state->head->hcrc = (int)((state->flags >> 9) & 1);
          807  +                state->head->done = 1;
          808  +            }
          809  +            strm->adler = state->check = crc32(0L, Z_NULL, 0);
          810  +            state->mode = TYPE;
          811  +            break;
          812  +#endif
          813  +        case DICTID:
          814  +            NEEDBITS(32);
          815  +            strm->adler = state->check = ZSWAP32(hold);
          816  +            INITBITS();
          817  +            state->mode = DICT;
          818  +        case DICT:
          819  +            if (state->havedict == 0) {
          820  +                RESTORE();
          821  +                return Z_NEED_DICT;
          822  +            }
          823  +            strm->adler = state->check = adler32(0L, Z_NULL, 0);
          824  +            state->mode = TYPE;
          825  +        case TYPE:
          826  +            if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
          827  +        case TYPEDO:
          828  +            if (state->last) {
          829  +                BYTEBITS();
          830  +                state->mode = CHECK;
          831  +                break;
          832  +            }
          833  +            NEEDBITS(3);
          834  +            state->last = BITS(1);
          835  +            DROPBITS(1);
          836  +            switch (BITS(2)) {
          837  +            case 0:                             /* stored block */
          838  +                Tracev((stderr, "inflate:     stored block%s\n",
          839  +                        state->last ? " (last)" : ""));
          840  +                state->mode = STORED;
          841  +                break;
          842  +            case 1:                             /* fixed block */
          843  +                fixedtables(state);
          844  +                Tracev((stderr, "inflate:     fixed codes block%s\n",
          845  +                        state->last ? " (last)" : ""));
          846  +                state->mode = LEN_;             /* decode codes */
          847  +                if (flush == Z_TREES) {
          848  +                    DROPBITS(2);
          849  +                    goto inf_leave;
          850  +                }
          851  +                break;
          852  +            case 2:                             /* dynamic block */
          853  +                Tracev((stderr, "inflate:     dynamic codes block%s\n",
          854  +                        state->last ? " (last)" : ""));
          855  +                state->mode = TABLE;
          856  +                break;
          857  +            case 3:
          858  +                strm->msg = (char *)"invalid block type";
          859  +                state->mode = BAD;
          860  +            }
          861  +            DROPBITS(2);
          862  +            break;
          863  +        case STORED:
          864  +            BYTEBITS();                         /* go to byte boundary */
          865  +            NEEDBITS(32);
          866  +            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
          867  +                strm->msg = (char *)"invalid stored block lengths";
          868  +                state->mode = BAD;
          869  +                break;
          870  +            }
          871  +            state->length = (unsigned)hold & 0xffff;
          872  +            Tracev((stderr, "inflate:       stored length %u\n",
          873  +                    state->length));
          874  +            INITBITS();
          875  +            state->mode = COPY_;
          876  +            if (flush == Z_TREES) goto inf_leave;
          877  +        case COPY_:
          878  +            state->mode = COPY;
          879  +        case COPY:
          880  +            copy = state->length;
          881  +            if (copy) {
          882  +                if (copy > have) copy = have;
          883  +                if (copy > left) copy = left;
          884  +                if (copy == 0) goto inf_leave;
          885  +                zmemcpy(put, next, copy);
          886  +                have -= copy;
          887  +                next += copy;
          888  +                left -= copy;
          889  +                put += copy;
          890  +                state->length -= copy;
          891  +                break;
          892  +            }
          893  +            Tracev((stderr, "inflate:       stored end\n"));
          894  +            state->mode = TYPE;
          895  +            break;
          896  +        case TABLE:
          897  +            NEEDBITS(14);
          898  +            state->nlen = BITS(5) + 257;
          899  +            DROPBITS(5);
          900  +            state->ndist = BITS(5) + 1;
          901  +            DROPBITS(5);
          902  +            state->ncode = BITS(4) + 4;
          903  +            DROPBITS(4);
          904  +#ifndef PKZIP_BUG_WORKAROUND
          905  +            if (state->nlen > 286 || state->ndist > 30) {
          906  +                strm->msg = (char *)"too many length or distance symbols";
          907  +                state->mode = BAD;
          908  +                break;
          909  +            }
          910  +#endif
          911  +            Tracev((stderr, "inflate:       table sizes ok\n"));
          912  +            state->have = 0;
          913  +            state->mode = LENLENS;
          914  +        case LENLENS:
          915  +            while (state->have < state->ncode) {
          916  +                NEEDBITS(3);
          917  +                state->lens[order[state->have++]] = (unsigned short)BITS(3);
          918  +                DROPBITS(3);
          919  +            }
          920  +            while (state->have < 19)
          921  +                state->lens[order[state->have++]] = 0;
          922  +            state->next = state->codes;
          923  +            state->lencode = (code const FAR *)(state->next);
          924  +            state->lenbits = 7;
          925  +            ret = inflate_table(CODES, state->lens, 19, &(state->next),
          926  +                                &(state->lenbits), state->work);
          927  +            if (ret) {
          928  +                strm->msg = (char *)"invalid code lengths set";
          929  +                state->mode = BAD;
          930  +                break;
          931  +            }
          932  +            Tracev((stderr, "inflate:       code lengths ok\n"));
          933  +            state->have = 0;
          934  +            state->mode = CODELENS;
          935  +        case CODELENS:
          936  +            while (state->have < state->nlen + state->ndist) {
          937  +                for (;;) {
          938  +                    here = state->lencode[BITS(state->lenbits)];
          939  +                    if ((unsigned)(here.bits) <= bits) break;
          940  +                    PULLBYTE();
          941  +                }
          942  +                if (here.val < 16) {
          943  +                    DROPBITS(here.bits);
          944  +                    state->lens[state->have++] = here.val;
          945  +                }
          946  +                else {
          947  +                    if (here.val == 16) {
          948  +                        NEEDBITS(here.bits + 2);
          949  +                        DROPBITS(here.bits);
          950  +                        if (state->have == 0) {
          951  +                            strm->msg = (char *)"invalid bit length repeat";
          952  +                            state->mode = BAD;
          953  +                            break;
          954  +                        }
          955  +                        len = state->lens[state->have - 1];
          956  +                        copy = 3 + BITS(2);
          957  +                        DROPBITS(2);
          958  +                    }
          959  +                    else if (here.val == 17) {
          960  +                        NEEDBITS(here.bits + 3);
          961  +                        DROPBITS(here.bits);
          962  +                        len = 0;
          963  +                        copy = 3 + BITS(3);
          964  +                        DROPBITS(3);
          965  +                    }
          966  +                    else {
          967  +                        NEEDBITS(here.bits + 7);
          968  +                        DROPBITS(here.bits);
          969  +                        len = 0;
          970  +                        copy = 11 + BITS(7);
          971  +                        DROPBITS(7);
          972  +                    }
          973  +                    if (state->have + copy > state->nlen + state->ndist) {
          974  +                        strm->msg = (char *)"invalid bit length repeat";
          975  +                        state->mode = BAD;
          976  +                        break;
          977  +                    }
          978  +                    while (copy--)
          979  +                        state->lens[state->have++] = (unsigned short)len;
          980  +                }
          981  +            }
          982  +
          983  +            /* handle error breaks in while */
          984  +            if (state->mode == BAD) break;
          985  +
          986  +            /* check for end-of-block code (better have one) */
          987  +            if (state->lens[256] == 0) {
          988  +                strm->msg = (char *)"invalid code -- missing end-of-block";
          989  +                state->mode = BAD;
          990  +                break;
          991  +            }
          992  +
          993  +            /* build code tables -- note: do not change the lenbits or distbits
          994  +               values here (9 and 6) without reading the comments in inftrees.h
          995  +               concerning the ENOUGH constants, which depend on those values */
          996  +            state->next = state->codes;
          997  +            state->lencode = (code const FAR *)(state->next);
          998  +            state->lenbits = 9;
          999  +            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
         1000  +                                &(state->lenbits), state->work);
         1001  +            if (ret) {
         1002  +                strm->msg = (char *)"invalid literal/lengths set";
         1003  +                state->mode = BAD;
         1004  +                break;
         1005  +            }
         1006  +            state->distcode = (code const FAR *)(state->next);
         1007  +            state->distbits = 6;
         1008  +            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
         1009  +                            &(state->next), &(state->distbits), state->work);
         1010  +            if (ret) {
         1011  +                strm->msg = (char *)"invalid distances set";
         1012  +                state->mode = BAD;
         1013  +                break;
         1014  +            }
         1015  +            Tracev((stderr, "inflate:       codes ok\n"));
         1016  +            state->mode = LEN_;
         1017  +            if (flush == Z_TREES) goto inf_leave;
         1018  +        case LEN_:
         1019  +            state->mode = LEN;
         1020  +        case LEN:
         1021  +            if (have >= 6 && left >= 258) {
         1022  +                RESTORE();
         1023  +                inflate_fast(strm, out);
         1024  +                LOAD();
         1025  +                if (state->mode == TYPE)
         1026  +                    state->back = -1;
         1027  +                break;
         1028  +            }
         1029  +            state->back = 0;
         1030  +            for (;;) {
         1031  +                here = state->lencode[BITS(state->lenbits)];
         1032  +                if ((unsigned)(here.bits) <= bits) break;
         1033  +                PULLBYTE();
         1034  +            }
         1035  +            if (here.op && (here.op & 0xf0) == 0) {
         1036  +                last = here;
         1037  +                for (;;) {
         1038  +                    here = state->lencode[last.val +
         1039  +                            (BITS(last.bits + last.op) >> last.bits)];
         1040  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
         1041  +                    PULLBYTE();
         1042  +                }
         1043  +                DROPBITS(last.bits);
         1044  +                state->back += last.bits;
         1045  +            }
         1046  +            DROPBITS(here.bits);
         1047  +            state->back += here.bits;
         1048  +            state->length = (unsigned)here.val;
         1049  +            if ((int)(here.op) == 0) {
         1050  +                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
         1051  +                        "inflate:         literal '%c'\n" :
         1052  +                        "inflate:         literal 0x%02x\n", here.val));
         1053  +                state->mode = LIT;
         1054  +                break;
         1055  +            }
         1056  +            if (here.op & 32) {
         1057  +                Tracevv((stderr, "inflate:         end of block\n"));
         1058  +                state->back = -1;
         1059  +                state->mode = TYPE;
         1060  +                break;
         1061  +            }
         1062  +            if (here.op & 64) {
         1063  +                strm->msg = (char *)"invalid literal/length code";
         1064  +                state->mode = BAD;
         1065  +                break;
         1066  +            }
         1067  +            state->extra = (unsigned)(here.op) & 15;
         1068  +            state->mode = LENEXT;
         1069  +        case LENEXT:
         1070  +            if (state->extra) {
         1071  +                NEEDBITS(state->extra);
         1072  +                state->length += BITS(state->extra);
         1073  +                DROPBITS(state->extra);
         1074  +                state->back += state->extra;
         1075  +            }
         1076  +            Tracevv((stderr, "inflate:         length %u\n", state->length));
         1077  +            state->was = state->length;
         1078  +            state->mode = DIST;
         1079  +        case DIST:
         1080  +            for (;;) {
         1081  +                here = state->distcode[BITS(state->distbits)];
         1082  +                if ((unsigned)(here.bits) <= bits) break;
         1083  +                PULLBYTE();
         1084  +            }
         1085  +            if ((here.op & 0xf0) == 0) {
         1086  +                last = here;
         1087  +                for (;;) {
         1088  +                    here = state->distcode[last.val +
         1089  +                            (BITS(last.bits + last.op) >> last.bits)];
         1090  +                    if ((unsigned)(last.bits + here.bits) <= bits) break;
         1091  +                    PULLBYTE();
         1092  +                }
         1093  +                DROPBITS(last.bits);
         1094  +                state->back += last.bits;
         1095  +            }
         1096  +            DROPBITS(here.bits);
         1097  +            state->back += here.bits;
         1098  +            if (here.op & 64) {
         1099  +                strm->msg = (char *)"invalid distance code";
         1100  +                state->mode = BAD;
         1101  +                break;
         1102  +            }
         1103  +            state->offset = (unsigned)here.val;
         1104  +            state->extra = (unsigned)(here.op) & 15;
         1105  +            state->mode = DISTEXT;
         1106  +        case DISTEXT:
         1107  +            if (state->extra) {
         1108  +                NEEDBITS(state->extra);
         1109  +                state->offset += BITS(state->extra);
         1110  +                DROPBITS(state->extra);
         1111  +                state->back += state->extra;
         1112  +            }
         1113  +#ifdef INFLATE_STRICT
         1114  +            if (state->offset > state->dmax) {
         1115  +                strm->msg = (char *)"invalid distance too far back";
         1116  +                state->mode = BAD;
         1117  +                break;
         1118  +            }
         1119  +#endif
         1120  +            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
         1121  +            state->mode = MATCH;
         1122  +        case MATCH:
         1123  +            if (left == 0) goto inf_leave;
         1124  +            copy = out - left;
         1125  +            if (state->offset > copy) {         /* copy from window */
         1126  +                copy = state->offset - copy;
         1127  +                if (copy > state->whave) {
         1128  +                    if (state->sane) {
         1129  +                        strm->msg = (char *)"invalid distance too far back";
         1130  +                        state->mode = BAD;
         1131  +                        break;
         1132  +                    }
         1133  +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
         1134  +                    Trace((stderr, "inflate.c too far\n"));
         1135  +                    copy -= state->whave;
         1136  +                    if (copy > state->length) copy = state->length;
         1137  +                    if (copy > left) copy = left;
         1138  +                    left -= copy;
         1139  +                    state->length -= copy;
         1140  +                    do {
         1141  +                        *put++ = 0;
         1142  +                    } while (--copy);
         1143  +                    if (state->length == 0) state->mode = LEN;
         1144  +                    break;
         1145  +#endif
         1146  +                }
         1147  +                if (copy > state->wnext) {
         1148  +                    copy -= state->wnext;
         1149  +                    from = state->window + (state->wsize - copy);
         1150  +                }
         1151  +                else
         1152  +                    from = state->window + (state->wnext - copy);
         1153  +                if (copy > state->length) copy = state->length;
         1154  +            }
         1155  +            else {                              /* copy from output */
         1156  +                from = put - state->offset;
         1157  +                copy = state->length;
         1158  +            }
         1159  +            if (copy > left) copy = left;
         1160  +            left -= copy;
         1161  +            state->length -= copy;
         1162  +            do {
         1163  +                *put++ = *from++;
         1164  +            } while (--copy);
         1165  +            if (state->length == 0) state->mode = LEN;
         1166  +            break;
         1167  +        case LIT:
         1168  +            if (left == 0) goto inf_leave;
         1169  +            *put++ = (unsigned char)(state->length);
         1170  +            left--;
         1171  +            state->mode = LEN;
         1172  +            break;
         1173  +        case CHECK:
         1174  +            if (state->wrap) {
         1175  +                NEEDBITS(32);
         1176  +                out -= left;
         1177  +                strm->total_out += out;
         1178  +                state->total += out;
         1179  +                if (out)
         1180  +                    strm->adler = state->check =
         1181  +                        UPDATE(state->check, put - out, out);
         1182  +                out = left;
         1183  +                if ((
         1184  +#ifdef GUNZIP
         1185  +                     state->flags ? hold :
         1186  +#endif
         1187  +                     ZSWAP32(hold)) != state->check) {
         1188  +                    strm->msg = (char *)"incorrect data check";
         1189  +                    state->mode = BAD;
         1190  +                    break;
         1191  +                }
         1192  +                INITBITS();
         1193  +                Tracev((stderr, "inflate:   check matches trailer\n"));
         1194  +            }
         1195  +#ifdef GUNZIP
         1196  +            state->mode = LENGTH;
         1197  +        case LENGTH:
         1198  +            if (state->wrap && state->flags) {
         1199  +                NEEDBITS(32);
         1200  +                if (hold != (state->total & 0xffffffffUL)) {
         1201  +                    strm->msg = (char *)"incorrect length check";
         1202  +                    state->mode = BAD;
         1203  +                    break;
         1204  +                }
         1205  +                INITBITS();
         1206  +                Tracev((stderr, "inflate:   length matches trailer\n"));
         1207  +            }
         1208  +#endif
         1209  +            state->mode = DONE;
         1210  +        case DONE:
         1211  +            ret = Z_STREAM_END;
         1212  +            goto inf_leave;
         1213  +        case BAD:
         1214  +            ret = Z_DATA_ERROR;
         1215  +            goto inf_leave;
         1216  +        case MEM:
         1217  +            return Z_MEM_ERROR;
         1218  +        case SYNC:
         1219  +        default:
         1220  +            return Z_STREAM_ERROR;
         1221  +        }
         1222  +
         1223  +    /*
         1224  +       Return from inflate(), updating the total counts and the check value.
         1225  +       If there was no progress during the inflate() call, return a buffer
         1226  +       error.  Call updatewindow() to create and/or update the window state.
         1227  +       Note: a memory error from inflate() is non-recoverable.
         1228  +     */
         1229  +  inf_leave:
         1230  +    RESTORE();
         1231  +    if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
         1232  +            (state->mode < CHECK || flush != Z_FINISH)))
         1233  +        if (updatewindow(strm, out)) {
         1234  +            state->mode = MEM;
         1235  +            return Z_MEM_ERROR;
         1236  +        }
         1237  +    in -= strm->avail_in;
         1238  +    out -= strm->avail_out;
         1239  +    strm->total_in += in;
         1240  +    strm->total_out += out;
         1241  +    state->total += out;
         1242  +    if (state->wrap && out)
         1243  +        strm->adler = state->check =
         1244  +            UPDATE(state->check, strm->next_out - out, out);
         1245  +    strm->data_type = state->bits + (state->last ? 64 : 0) +
         1246  +                      (state->mode == TYPE ? 128 : 0) +
         1247  +                      (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
         1248  +    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
         1249  +        ret = Z_BUF_ERROR;
         1250  +    return ret;
         1251  +}
         1252  +
         1253  +int ZEXPORT inflateEnd(strm)
         1254  +z_streamp strm;
         1255  +{
         1256  +    struct inflate_state FAR *state;
         1257  +    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
         1258  +        return Z_STREAM_ERROR;
         1259  +    state = (struct inflate_state FAR *)strm->state;
         1260  +    if (state->window != Z_NULL) ZFREE(strm, state->window);
         1261  +    ZFREE(strm, strm->state);
         1262  +    strm->state = Z_NULL;
         1263  +    Tracev((stderr, "inflate: end\n"));
         1264  +    return Z_OK;
         1265  +}
         1266  +
         1267  +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
         1268  +z_streamp strm;
         1269  +const Bytef *dictionary;
         1270  +uInt dictLength;
         1271  +{
         1272  +    struct inflate_state FAR *state;
         1273  +    unsigned long dictid;
         1274  +    unsigned char *next;
         1275  +    unsigned avail;
         1276  +    int ret;
         1277  +
         1278  +    /* check state */
         1279  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
         1280  +    state = (struct inflate_state FAR *)strm->state;
         1281  +    if (state->wrap != 0 && state->mode != DICT)
         1282  +        return Z_STREAM_ERROR;
         1283  +
         1284  +    /* check for correct dictionary identifier */
         1285  +    if (state->mode == DICT) {
         1286  +        dictid = adler32(0L, Z_NULL, 0);
         1287  +        dictid = adler32(dictid, dictionary, dictLength);
         1288  +        if (dictid != state->check)
         1289  +            return Z_DATA_ERROR;
         1290  +    }
         1291  +
         1292  +    /* copy dictionary to window using updatewindow(), which will amend the
         1293  +       existing dictionary if appropriate */
         1294  +    next = strm->next_out;
         1295  +    avail = strm->avail_out;
         1296  +    strm->next_out = (Bytef *)dictionary + dictLength;
         1297  +    strm->avail_out = 0;
         1298  +    ret = updatewindow(strm, dictLength);
         1299  +    strm->avail_out = avail;
         1300  +    strm->next_out = next;
         1301  +    if (ret) {
         1302  +        state->mode = MEM;
         1303  +        return Z_MEM_ERROR;
         1304  +    }
         1305  +    state->havedict = 1;
         1306  +    Tracev((stderr, "inflate:   dictionary set\n"));
         1307  +    return Z_OK;
         1308  +}
         1309  +
         1310  +int ZEXPORT inflateGetHeader(strm, head)
         1311  +z_streamp strm;
         1312  +gz_headerp head;
         1313  +{
         1314  +    struct inflate_state FAR *state;
         1315  +
         1316  +    /* check state */
         1317  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
         1318  +    state = (struct inflate_state FAR *)strm->state;
         1319  +    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
         1320  +
         1321  +    /* save header structure */
         1322  +    state->head = head;
         1323  +    head->done = 0;
         1324  +    return Z_OK;
         1325  +}
         1326  +
         1327  +/*
         1328  +   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
         1329  +   or when out of input.  When called, *have is the number of pattern bytes
         1330  +   found in order so far, in 0..3.  On return *have is updated to the new
         1331  +   state.  If on return *have equals four, then the pattern was found and the
         1332  +   return value is how many bytes were read including the last byte of the
         1333  +   pattern.  If *have is less than four, then the pattern has not been found
         1334  +   yet and the return value is len.  In the latter case, syncsearch() can be
         1335  +   called again with more data and the *have state.  *have is initialized to
         1336  +   zero for the first call.
         1337  + */
         1338  +local unsigned syncsearch(have, buf, len)
         1339  +unsigned FAR *have;
         1340  +unsigned char FAR *buf;
         1341  +unsigned len;
         1342  +{
         1343  +    unsigned got;
         1344  +    unsigned next;
         1345  +
         1346  +    got = *have;
         1347  +    next = 0;
         1348  +    while (next < len && got < 4) {
         1349  +        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
         1350  +            got++;
         1351  +        else if (buf[next])
         1352  +            got = 0;
         1353  +        else
         1354  +            got = 4 - got;
         1355  +        next++;
         1356  +    }
         1357  +    *have = got;
         1358  +    return next;
         1359  +}
         1360  +
         1361  +int ZEXPORT inflateSync(strm)
         1362  +z_streamp strm;
         1363  +{
         1364  +    unsigned len;               /* number of bytes to look at or looked at */
         1365  +    unsigned long in, out;      /* temporary to save total_in and total_out */
         1366  +    unsigned char buf[4];       /* to restore bit buffer to byte string */
         1367  +    struct inflate_state FAR *state;
         1368  +
         1369  +    /* check parameters */
         1370  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
         1371  +    state = (struct inflate_state FAR *)strm->state;
         1372  +    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
         1373  +
         1374  +    /* if first time, start search in bit buffer */
         1375  +    if (state->mode != SYNC) {
         1376  +        state->mode = SYNC;
         1377  +        state->hold <<= state->bits & 7;
         1378  +        state->bits -= state->bits & 7;
         1379  +        len = 0;
         1380  +        while (state->bits >= 8) {
         1381  +            buf[len++] = (unsigned char)(state->hold);
         1382  +            state->hold >>= 8;
         1383  +            state->bits -= 8;
         1384  +        }
         1385  +        state->have = 0;
         1386  +        syncsearch(&(state->have), buf, len);
         1387  +    }
         1388  +
         1389  +    /* search available input */
         1390  +    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
         1391  +    strm->avail_in -= len;
         1392  +    strm->next_in += len;
         1393  +    strm->total_in += len;
         1394  +
         1395  +    /* return no joy or set up to restart inflate() on a new block */
         1396  +    if (state->have != 4) return Z_DATA_ERROR;
         1397  +    in = strm->total_in;  out = strm->total_out;
         1398  +    inflateReset(strm);
         1399  +    strm->total_in = in;  strm->total_out = out;
         1400  +    state->mode = TYPE;
         1401  +    return Z_OK;
         1402  +}
         1403  +
         1404  +/*
         1405  +   Returns true if inflate is currently at the end of a block generated by
         1406  +   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
         1407  +   implementation to provide an additional safety check. PPP uses
         1408  +   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
         1409  +   block. When decompressing, PPP checks that at the end of input packet,
         1410  +   inflate is waiting for these length bytes.
         1411  + */
         1412  +int ZEXPORT inflateSyncPoint(strm)
         1413  +z_streamp strm;
         1414  +{
         1415  +    struct inflate_state FAR *state;
         1416  +
         1417  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
         1418  +    state = (struct inflate_state FAR *)strm->state;
         1419  +    return state->mode == STORED && state->bits == 0;
         1420  +}
         1421  +
         1422  +int ZEXPORT inflateCopy(dest, source)
         1423  +z_streamp dest;
         1424  +z_streamp source;
         1425  +{
         1426  +    struct inflate_state FAR *state;
         1427  +    struct inflate_state FAR *copy;
         1428  +    unsigned char FAR *window;
         1429  +    unsigned wsize;
         1430  +
         1431  +    /* check input */
         1432  +    if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
         1433  +        source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
         1434  +        return Z_STREAM_ERROR;
         1435  +    state = (struct inflate_state FAR *)source->state;
         1436  +
         1437  +    /* allocate space */
         1438  +    copy = (struct inflate_state FAR *)
         1439  +           ZALLOC(source, 1, sizeof(struct inflate_state));
         1440  +    if (copy == Z_NULL) return Z_MEM_ERROR;
         1441  +    window = Z_NULL;
         1442  +    if (state->window != Z_NULL) {
         1443  +        window = (unsigned char FAR *)
         1444  +                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
         1445  +        if (window == Z_NULL) {
         1446  +            ZFREE(source, copy);
         1447  +            return Z_MEM_ERROR;
         1448  +        }
         1449  +    }
         1450  +
         1451  +    /* copy state */
         1452  +    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
         1453  +    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
         1454  +    if (state->lencode >= state->codes &&
         1455  +        state->lencode <= state->codes + ENOUGH - 1) {
         1456  +        copy->lencode = copy->codes + (state->lencode - state->codes);
         1457  +        copy->distcode = copy->codes + (state->distcode - state->codes);
         1458  +    }
         1459  +    copy->next = copy->codes + (state->next - state->codes);
         1460  +    if (window != Z_NULL) {
         1461  +        wsize = 1U << state->wbits;
         1462  +        zmemcpy(window, state->window, wsize);
         1463  +    }
         1464  +    copy->window = window;
         1465  +    dest->state = (struct internal_state FAR *)copy;
         1466  +    return Z_OK;
         1467  +}
         1468  +
         1469  +int ZEXPORT inflateUndermine(strm, subvert)
         1470  +z_streamp strm;
         1471  +int subvert;
         1472  +{
         1473  +    struct inflate_state FAR *state;
         1474  +
         1475  +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
         1476  +    state = (struct inflate_state FAR *)strm->state;
         1477  +    state->sane = !subvert;
         1478  +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
         1479  +    return Z_OK;
         1480  +#else
         1481  +    state->sane = 1;
         1482  +    return Z_DATA_ERROR;
         1483  +#endif
         1484  +}
         1485  +
         1486  +long ZEXPORT inflateMark(strm)
         1487  +z_streamp strm;
         1488  +{
         1489  +    struct inflate_state FAR *state;
         1490  +
         1491  +    if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16;
         1492  +    state = (struct inflate_state FAR *)strm->state;
         1493  +    return ((long)(state->back) << 16) +
         1494  +        (state->mode == COPY ? state->length :
         1495  +            (state->mode == MATCH ? state->was - state->length : 0));
         1496  +}

Added compat/zlib/inflate.h.

            1  +/* inflate.h -- internal inflate state definition
            2  + * Copyright (C) 1995-2009 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* define NO_GZIP when compiling if you want to disable gzip header and
           12  +   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in
           13  +   the crc code when it is not needed.  For shared libraries, gzip decoding
           14  +   should be left enabled. */
           15  +#ifndef NO_GZIP
           16  +#  define GUNZIP
           17  +#endif
           18  +
           19  +/* Possible inflate modes between inflate() calls */
           20  +typedef enum {
           21  +    HEAD,       /* i: waiting for magic header */
           22  +    FLAGS,      /* i: waiting for method and flags (gzip) */
           23  +    TIME,       /* i: waiting for modification time (gzip) */
           24  +    OS,         /* i: waiting for extra flags and operating system (gzip) */
           25  +    EXLEN,      /* i: waiting for extra length (gzip) */
           26  +    EXTRA,      /* i: waiting for extra bytes (gzip) */
           27  +    NAME,       /* i: waiting for end of file name (gzip) */
           28  +    COMMENT,    /* i: waiting for end of comment (gzip) */
           29  +    HCRC,       /* i: waiting for header crc (gzip) */
           30  +    DICTID,     /* i: waiting for dictionary check value */
           31  +    DICT,       /* waiting for inflateSetDictionary() call */
           32  +        TYPE,       /* i: waiting for type bits, including last-flag bit */
           33  +        TYPEDO,     /* i: same, but skip check to exit inflate on new block */
           34  +        STORED,     /* i: waiting for stored size (length and complement) */
           35  +        COPY_,      /* i/o: same as COPY below, but only first time in */
           36  +        COPY,       /* i/o: waiting for input or output to copy stored block */
           37  +        TABLE,      /* i: waiting for dynamic block table lengths */
           38  +        LENLENS,    /* i: waiting for code length code lengths */
           39  +        CODELENS,   /* i: waiting for length/lit and distance code lengths */
           40  +            LEN_,       /* i: same as LEN below, but only first time in */
           41  +            LEN,        /* i: waiting for length/lit/eob code */
           42  +            LENEXT,     /* i: waiting for length extra bits */
           43  +            DIST,       /* i: waiting for distance code */
           44  +            DISTEXT,    /* i: waiting for distance extra bits */
           45  +            MATCH,      /* o: waiting for output space to copy string */
           46  +            LIT,        /* o: waiting for output space to write literal */
           47  +    CHECK,      /* i: waiting for 32-bit check value */
           48  +    LENGTH,     /* i: waiting for 32-bit length (gzip) */
           49  +    DONE,       /* finished check, done -- remain here until reset */
           50  +    BAD,        /* got a data error -- remain here until reset */
           51  +    MEM,        /* got an inflate() memory error -- remain here until reset */
           52  +    SYNC        /* looking for synchronization bytes to restart inflate() */
           53  +} inflate_mode;
           54  +
           55  +/*
           56  +    State transitions between above modes -
           57  +
           58  +    (most modes can go to BAD or MEM on error -- not shown for clarity)
           59  +
           60  +    Process header:
           61  +        HEAD -> (gzip) or (zlib) or (raw)
           62  +        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
           63  +                  HCRC -> TYPE
           64  +        (zlib) -> DICTID or TYPE
           65  +        DICTID -> DICT -> TYPE
           66  +        (raw) -> TYPEDO
           67  +    Read deflate blocks:
           68  +            TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
           69  +            STORED -> COPY_ -> COPY -> TYPE
           70  +            TABLE -> LENLENS -> CODELENS -> LEN_
           71  +            LEN_ -> LEN
           72  +    Read deflate codes in fixed or dynamic block:
           73  +                LEN -> LENEXT or LIT or TYPE
           74  +                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
           75  +                LIT -> LEN
           76  +    Process trailer:
           77  +        CHECK -> LENGTH -> DONE
           78  + */
           79  +
           80  +/* state maintained between inflate() calls.  Approximately 10K bytes. */
           81  +struct inflate_state {
           82  +    inflate_mode mode;          /* current inflate mode */
           83  +    int last;                   /* true if processing last block */
           84  +    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip */
           85  +    int havedict;               /* true if dictionary provided */
           86  +    int flags;                  /* gzip header method and flags (0 if zlib) */
           87  +    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */
           88  +    unsigned long check;        /* protected copy of check value */
           89  +    unsigned long total;        /* protected copy of output count */
           90  +    gz_headerp head;            /* where to save gzip header information */
           91  +        /* sliding window */
           92  +    unsigned wbits;             /* log base 2 of requested window size */
           93  +    unsigned wsize;             /* window size or zero if not using window */
           94  +    unsigned whave;             /* valid bytes in the window */
           95  +    unsigned wnext;             /* window write index */
           96  +    unsigned char FAR *window;  /* allocated sliding window, if needed */
           97  +        /* bit accumulator */
           98  +    unsigned long hold;         /* input bit accumulator */
           99  +    unsigned bits;              /* number of bits in "in" */
          100  +        /* for string and stored block copying */
          101  +    unsigned length;            /* literal or length of data to copy */
          102  +    unsigned offset;            /* distance back to copy string from */
          103  +        /* for table and code decoding */
          104  +    unsigned extra;             /* extra bits needed */
          105  +        /* fixed and dynamic code tables */
          106  +    code const FAR *lencode;    /* starting table for length/literal codes */
          107  +    code const FAR *distcode;   /* starting table for distance codes */
          108  +    unsigned lenbits;           /* index bits for lencode */
          109  +    unsigned distbits;          /* index bits for distcode */
          110  +        /* dynamic table building */
          111  +    unsigned ncode;             /* number of code length code lengths */
          112  +    unsigned nlen;              /* number of length code lengths */
          113  +    unsigned ndist;             /* number of distance code lengths */
          114  +    unsigned have;              /* number of code lengths in lens[] */
          115  +    code FAR *next;             /* next available space in codes[] */
          116  +    unsigned short lens[320];   /* temporary storage for code lengths */
          117  +    unsigned short work[288];   /* work area for code table building */
          118  +    code codes[ENOUGH];         /* space for code tables */
          119  +    int sane;                   /* if false, allow invalid distance too far */
          120  +    int back;                   /* bits back of last unprocessed length/lit */
          121  +    unsigned was;               /* initial length of match */
          122  +};

Added compat/zlib/inftrees.c.

            1  +/* inftrees.c -- generate Huffman trees for efficient decoding
            2  + * Copyright (C) 1995-2012 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +#include "zutil.h"
            7  +#include "inftrees.h"
            8  +
            9  +#define MAXBITS 15
           10  +
           11  +const char inflate_copyright[] =
           12  +   " inflate 1.2.7 Copyright 1995-2012 Mark Adler ";
           13  +/*
           14  +  If you use the zlib library in a product, an acknowledgment is welcome
           15  +  in the documentation of your product. If for some reason you cannot
           16  +  include such an acknowledgment, I would appreciate that you keep this
           17  +  copyright string in the executable of your product.
           18  + */
           19  +
           20  +/*
           21  +   Build a set of tables to decode the provided canonical Huffman code.
           22  +   The code lengths are lens[0..codes-1].  The result starts at *table,
           23  +   whose indices are 0..2^bits-1.  work is a writable array of at least
           24  +   lens shorts, which is used as a work area.  type is the type of code
           25  +   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
           26  +   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
           27  +   on return points to the next available entry's address.  bits is the
           28  +   requested root table index bits, and on return it is the actual root
           29  +   table index bits.  It will differ if the request is greater than the
           30  +   longest code or if it is less than the shortest code.
           31  + */
           32  +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
           33  +codetype type;
           34  +unsigned short FAR *lens;
           35  +unsigned codes;
           36  +code FAR * FAR *table;
           37  +unsigned FAR *bits;
           38  +unsigned short FAR *work;
           39  +{
           40  +    unsigned len;               /* a code's length in bits */
           41  +    unsigned sym;               /* index of code symbols */
           42  +    unsigned min, max;          /* minimum and maximum code lengths */
           43  +    unsigned root;              /* number of index bits for root table */
           44  +    unsigned curr;              /* number of index bits for current table */
           45  +    unsigned drop;              /* code bits to drop for sub-table */
           46  +    int left;                   /* number of prefix codes available */
           47  +    unsigned used;              /* code entries in table used */
           48  +    unsigned huff;              /* Huffman code */
           49  +    unsigned incr;              /* for incrementing code, index */
           50  +    unsigned fill;              /* index for replicating entries */
           51  +    unsigned low;               /* low bits for current root entry */
           52  +    unsigned mask;              /* mask for low root bits */
           53  +    code here;                  /* table entry for duplication */
           54  +    code FAR *next;             /* next available space in table */
           55  +    const unsigned short FAR *base;     /* base value table to use */
           56  +    const unsigned short FAR *extra;    /* extra bits table to use */
           57  +    int end;                    /* use base and extra for symbol > end */
           58  +    unsigned short count[MAXBITS+1];    /* number of codes of each length */
           59  +    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
           60  +    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
           61  +        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
           62  +        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
           63  +    static const unsigned short lext[31] = { /* Length codes 257..285 extra */
           64  +        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
           65  +        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 78, 68};
           66  +    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
           67  +        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
           68  +        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
           69  +        8193, 12289, 16385, 24577, 0, 0};
           70  +    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
           71  +        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
           72  +        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
           73  +        28, 28, 29, 29, 64, 64};
           74  +
           75  +    /*
           76  +       Process a set of code lengths to create a canonical Huffman code.  The
           77  +       code lengths are lens[0..codes-1].  Each length corresponds to the
           78  +       symbols 0..codes-1.  The Huffman code is generated by first sorting the
           79  +       symbols by length from short to long, and retaining the symbol order
           80  +       for codes with equal lengths.  Then the code starts with all zero bits
           81  +       for the first code of the shortest length, and the codes are integer
           82  +       increments for the same length, and zeros are appended as the length
           83  +       increases.  For the deflate format, these bits are stored backwards
           84  +       from their more natural integer increment ordering, and so when the
           85  +       decoding tables are built in the large loop below, the integer codes
           86  +       are incremented backwards.
           87  +
           88  +       This routine assumes, but does not check, that all of the entries in
           89  +       lens[] are in the range 0..MAXBITS.  The caller must assure this.
           90  +       1..MAXBITS is interpreted as that code length.  zero means that that
           91  +       symbol does not occur in this code.
           92  +
           93  +       The codes are sorted by computing a count of codes for each length,
           94  +       creating from that a table of starting indices for each length in the
           95  +       sorted table, and then entering the symbols in order in the sorted
           96  +       table.  The sorted table is work[], with that space being provided by
           97  +       the caller.
           98  +
           99  +       The length counts are used for other purposes as well, i.e. finding
          100  +       the minimum and maximum length codes, determining if there are any
          101  +       codes at all, checking for a valid set of lengths, and looking ahead
          102  +       at length counts to determine sub-table sizes when building the
          103  +       decoding tables.
          104  +     */
          105  +
          106  +    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
          107  +    for (len = 0; len <= MAXBITS; len++)
          108  +        count[len] = 0;
          109  +    for (sym = 0; sym < codes; sym++)
          110  +        count[lens[sym]]++;
          111  +
          112  +    /* bound code lengths, force root to be within code lengths */
          113  +    root = *bits;
          114  +    for (max = MAXBITS; max >= 1; max--)
          115  +        if (count[max] != 0) break;
          116  +    if (root > max) root = max;
          117  +    if (max == 0) {                     /* no symbols to code at all */
          118  +        here.op = (unsigned char)64;    /* invalid code marker */
          119  +        here.bits = (unsigned char)1;
          120  +        here.val = (unsigned short)0;
          121  +        *(*table)++ = here;             /* make a table to force an error */
          122  +        *(*table)++ = here;
          123  +        *bits = 1;
          124  +        return 0;     /* no symbols, but wait for decoding to report error */
          125  +    }
          126  +    for (min = 1; min < max; min++)
          127  +        if (count[min] != 0) break;
          128  +    if (root < min) root = min;
          129  +
          130  +    /* check for an over-subscribed or incomplete set of lengths */
          131  +    left = 1;
          132  +    for (len = 1; len <= MAXBITS; len++) {
          133  +        left <<= 1;
          134  +        left -= count[len];
          135  +        if (left < 0) return -1;        /* over-subscribed */
          136  +    }
          137  +    if (left > 0 && (type == CODES || max != 1))
          138  +        return -1;                      /* incomplete set */
          139  +
          140  +    /* generate offsets into symbol table for each length for sorting */
          141  +    offs[1] = 0;
          142  +    for (len = 1; len < MAXBITS; len++)
          143  +        offs[len + 1] = offs[len] + count[len];
          144  +
          145  +    /* sort symbols by length, by symbol order within each length */
          146  +    for (sym = 0; sym < codes; sym++)
          147  +        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
          148  +
          149  +    /*
          150  +       Create and fill in decoding tables.  In this loop, the table being
          151  +       filled is at next and has curr index bits.  The code being used is huff
          152  +       with length len.  That code is converted to an index by dropping drop
          153  +       bits off of the bottom.  For codes where len is less than drop + curr,
          154  +       those top drop + curr - len bits are incremented through all values to
          155  +       fill the table with replicated entries.
          156  +
          157  +       root is the number of index bits for the root table.  When len exceeds
          158  +       root, sub-tables are created pointed to by the root entry with an index
          159  +       of the low root bits of huff.  This is saved in low to check for when a
          160  +       new sub-table should be started.  drop is zero when the root table is
          161  +       being filled, and drop is root when sub-tables are being filled.
          162  +
          163  +       When a new sub-table is needed, it is necessary to look ahead in the
          164  +       code lengths to determine what size sub-table is needed.  The length
          165  +       counts are used for this, and so count[] is decremented as codes are
          166  +       entered in the tables.
          167  +
          168  +       used keeps track of how many table entries have been allocated from the
          169  +       provided *table space.  It is checked for LENS and DIST tables against
          170  +       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
          171  +       the initial root table size constants.  See the comments in inftrees.h
          172  +       for more information.
          173  +
          174  +       sym increments through all symbols, and the loop terminates when
          175  +       all codes of length max, i.e. all codes, have been processed.  This
          176  +       routine permits incomplete codes, so another loop after this one fills
          177  +       in the rest of the decoding tables with invalid code markers.
          178  +     */
          179  +
          180  +    /* set up for code type */
          181  +    switch (type) {
          182  +    case CODES:
          183  +        base = extra = work;    /* dummy value--not used */
          184  +        end = 19;
          185  +        break;
          186  +    case LENS:
          187  +        base = lbase;
          188  +        base -= 257;
          189  +        extra = lext;
          190  +        extra -= 257;
          191  +        end = 256;
          192  +        break;
          193  +    default:            /* DISTS */
          194  +        base = dbase;
          195  +        extra = dext;
          196  +        end = -1;
          197  +    }
          198  +
          199  +    /* initialize state for loop */
          200  +    huff = 0;                   /* starting code */
          201  +    sym = 0;                    /* starting code symbol */
          202  +    len = min;                  /* starting code length */
          203  +    next = *table;              /* current table to fill in */
          204  +    curr = root;                /* current table index bits */
          205  +    drop = 0;                   /* current bits to drop from code for index */
          206  +    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
          207  +    used = 1U << root;          /* use root table entries */
          208  +    mask = used - 1;            /* mask for comparing low */
          209  +
          210  +    /* check available table space */
          211  +    if ((type == LENS && used >= ENOUGH_LENS) ||
          212  +        (type == DISTS && used >= ENOUGH_DISTS))
          213  +        return 1;
          214  +
          215  +    /* process all codes and make table entries */
          216  +    for (;;) {
          217  +        /* create table entry */
          218  +        here.bits = (unsigned char)(len - drop);
          219  +        if ((int)(work[sym]) < end) {
          220  +            here.op = (unsigned char)0;
          221  +            here.val = work[sym];
          222  +        }
          223  +        else if ((int)(work[sym]) > end) {
          224  +            here.op = (unsigned char)(extra[work[sym]]);
          225  +            here.val = base[work[sym]];
          226  +        }
          227  +        else {
          228  +            here.op = (unsigned char)(32 + 64);         /* end of block */
          229  +            here.val = 0;
          230  +        }
          231  +
          232  +        /* replicate for those indices with low len bits equal to huff */
          233  +        incr = 1U << (len - drop);
          234  +        fill = 1U << curr;
          235  +        min = fill;                 /* save offset to next table */
          236  +        do {
          237  +            fill -= incr;
          238  +            next[(huff >> drop) + fill] = here;
          239  +        } while (fill != 0);
          240  +
          241  +        /* backwards increment the len-bit code huff */
          242  +        incr = 1U << (len - 1);
          243  +        while (huff & incr)
          244  +            incr >>= 1;
          245  +        if (incr != 0) {
          246  +            huff &= incr - 1;
          247  +            huff += incr;
          248  +        }
          249  +        else
          250  +            huff = 0;
          251  +
          252  +        /* go to next symbol, update count, len */
          253  +        sym++;
          254  +        if (--(count[len]) == 0) {
          255  +            if (len == max) break;
          256  +            len = lens[work[sym]];
          257  +        }
          258  +
          259  +        /* create new sub-table if needed */
          260  +        if (len > root && (huff & mask) != low) {
          261  +            /* if first time, transition to sub-tables */
          262  +            if (drop == 0)
          263  +                drop = root;
          264  +
          265  +            /* increment past last table */
          266  +            next += min;            /* here min is 1 << curr */
          267  +
          268  +            /* determine length of next table */
          269  +            curr = len - drop;
          270  +            left = (int)(1 << curr);
          271  +            while (curr + drop < max) {
          272  +                left -= count[curr + drop];
          273  +                if (left <= 0) break;
          274  +                curr++;
          275  +                left <<= 1;
          276  +            }
          277  +
          278  +            /* check for enough space */
          279  +            used += 1U << curr;
          280  +            if ((type == LENS && used >= ENOUGH_LENS) ||
          281  +                (type == DISTS && used >= ENOUGH_DISTS))
          282  +                return 1;
          283  +
          284  +            /* point entry in root table to sub-table */
          285  +            low = huff & mask;
          286  +            (*table)[low].op = (unsigned char)curr;
          287  +            (*table)[low].bits = (unsigned char)root;
          288  +            (*table)[low].val = (unsigned short)(next - *table);
          289  +        }
          290  +    }
          291  +
          292  +    /* fill in remaining table entry if code is incomplete (guaranteed to have
          293  +       at most one remaining entry, since if the code is incomplete, the
          294  +       maximum code length that was allowed to get this far is one bit) */
          295  +    if (huff != 0) {
          296  +        here.op = (unsigned char)64;            /* invalid code marker */
          297  +        here.bits = (unsigned char)(len - drop);
          298  +        here.val = (unsigned short)0;
          299  +        next[huff] = here;
          300  +    }
          301  +
          302  +    /* set return parameters */
          303  +    *table += used;
          304  +    *bits = root;
          305  +    return 0;
          306  +}

Added compat/zlib/inftrees.h.

            1  +/* inftrees.h -- header to use inftrees.c
            2  + * Copyright (C) 1995-2005, 2010 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* Structure for decoding tables.  Each entry provides either the
           12  +   information needed to do the operation requested by the code that
           13  +   indexed that table entry, or it provides a pointer to another
           14  +   table that indexes more bits of the code.  op indicates whether
           15  +   the entry is a pointer to another table, a literal, a length or
           16  +   distance, an end-of-block, or an invalid code.  For a table
           17  +   pointer, the low four bits of op is the number of index bits of
           18  +   that table.  For a length or distance, the low four bits of op
           19  +   is the number of extra bits to get after the code.  bits is
           20  +   the number of bits in this code or part of the code to drop off
           21  +   of the bit buffer.  val is the actual byte to output in the case
           22  +   of a literal, the base length or distance, or the offset from
           23  +   the current table to the next table.  Each entry is four bytes. */
           24  +typedef struct {
           25  +    unsigned char op;           /* operation, extra bits, table bits */
           26  +    unsigned char bits;         /* bits in this part of the code */
           27  +    unsigned short val;         /* offset in table or code value */
           28  +} code;
           29  +
           30  +/* op values as set by inflate_table():
           31  +    00000000 - literal
           32  +    0000tttt - table link, tttt != 0 is the number of table index bits
           33  +    0001eeee - length or distance, eeee is the number of extra bits
           34  +    01100000 - end of block
           35  +    01000000 - invalid code
           36  + */
           37  +
           38  +/* Maximum size of the dynamic table.  The maximum number of code structures is
           39  +   1444, which is the sum of 852 for literal/length codes and 592 for distance
           40  +   codes.  These values were found by exhaustive searches using the program
           41  +   examples/enough.c found in the zlib distribtution.  The arguments to that
           42  +   program are the number of symbols, the initial root table size, and the
           43  +   maximum bit length of a code.  "enough 286 9 15" for literal/length codes
           44  +   returns returns 852, and "enough 30 6 15" for distance codes returns 592.
           45  +   The initial root table size (9 or 6) is found in the fifth argument of the
           46  +   inflate_table() calls in inflate.c and infback.c.  If the root table size is
           47  +   changed, then these maximum sizes would be need to be recalculated and
           48  +   updated. */
           49  +#define ENOUGH_LENS 852
           50  +#define ENOUGH_DISTS 592
           51  +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
           52  +
           53  +/* Type of code to build for inflate_table() */
           54  +typedef enum {
           55  +    CODES,
           56  +    LENS,
           57  +    DISTS
           58  +} codetype;
           59  +
           60  +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
           61  +                             unsigned codes, code FAR * FAR *table,
           62  +                             unsigned FAR *bits, unsigned short FAR *work));

Added compat/zlib/make_vms.com.

            1  +$! make libz under VMS written by
            2  +$! Martin P.J. Zinser
            3  +$!
            4  +$! In case of problems with the install you might contact me at
            5  +$! zinser@zinser.no-ip.info(preferred) or
            6  +$! martin.zinser@eurexchange.com (work)
            7  +$!
            8  +$! Make procedure history for Zlib
            9  +$!
           10  +$!------------------------------------------------------------------------------
           11  +$! Version history
           12  +$! 0.01 20060120 First version to receive a number
           13  +$! 0.02 20061008 Adapt to new Makefile.in
           14  +$! 0.03 20091224 Add support for large file check
           15  +$! 0.04 20100110 Add new gzclose, gzlib, gzread, gzwrite
           16  +$! 0.05 20100221 Exchange zlibdefs.h by zconf.h.in
           17  +$! 0.06 20120111 Fix missing amiss_err, update zconf_h.in, fix new exmples
           18  +$!               subdir path, update module search in makefile.in
           19  +$! 0.07 20120115 Triggered by work done by Alexey Chupahin completly redesigned
           20  +$!               shared image creation
           21  +$! 0.08 20120219 Make it work on VAX again, pre-load missing symbols to shared
           22  +$!               image
           23  +$! 0.09 20120305 SMS.  P1 sets builder ("MMK", "MMS", " " (built-in)).
           24  +$!               "" -> automatic, preference: MMK, MMS, built-in.
           25  +$!
           26  +$ on error then goto err_exit
           27  +$!
           28  +$ true  = 1
           29  +$ false = 0
           30  +$ tmpnam = "temp_" + f$getjpi("","pid")
           31  +$ tt = tmpnam + ".txt"
           32  +$ tc = tmpnam + ".c"
           33  +$ th = tmpnam + ".h"
           34  +$ define/nolog tconfig 'th'
           35  +$ its_decc = false
           36  +$ its_vaxc = false
           37  +$ its_gnuc = false
           38  +$ s_case   = False
           39  +$!
           40  +$! Setup variables holding "config" information
           41  +$!
           42  +$ Make    = "''p1'"
           43  +$ name     = "Zlib"
           44  +$ version  = "?.?.?"
           45  +$ v_string = "ZLIB_VERSION"
           46  +$ v_file   = "zlib.h"
           47  +$ ccopt   = "/include = []"
           48  +$ lopts   = ""
           49  +$ dnsrl   = ""
           50  +$ aconf_in_file = "zconf.h.in#zconf.h_in#zconf_h.in"
           51  +$ conf_check_string = ""
           52  +$ linkonly = false
           53  +$ optfile  = name + ".opt"
           54  +$ mapfile  = name + ".map"
           55  +$ libdefs  = ""
           56  +$ vax      = f$getsyi("HW_MODEL").lt.1024
           57  +$ axp      = f$getsyi("HW_MODEL").ge.1024 .and. f$getsyi("HW_MODEL").lt.4096
           58  +$ ia64     = f$getsyi("HW_MODEL").ge.4096
           59  +$!
           60  +$! 2012-03-05 SMS.
           61  +$! Why is this needed?  And if it is needed, why not simply ".not. vax"?
           62  +$!
           63  +$!!! if axp .or. ia64 then  set proc/parse=extended
           64  +$!
           65  +$ whoami = f$parse(f$environment("Procedure"),,,,"NO_CONCEAL")
           66  +$ mydef  = F$parse(whoami,,,"DEVICE")
           67  +$ mydir  = f$parse(whoami,,,"DIRECTORY") - "]["
           68  +$ myproc = f$parse(whoami,,,"Name") + f$parse(whoami,,,"type")
           69  +$!
           70  +$! Check for MMK/MMS
           71  +$!
           72  +$ if (Make .eqs. "")
           73  +$ then
           74  +$   If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS"
           75  +$   If F$Type (MMK) .eqs. "STRING" Then Make = "MMK"
           76  +$ else
           77  +$   Make = f$edit( Make, "trim")
           78  +$ endif
           79  +$!
           80  +$ gosub find_version
           81  +$!
           82  +$  open/write topt tmp.opt
           83  +$  open/write optf 'optfile'
           84  +$!
           85  +$ gosub check_opts
           86  +$!
           87  +$! Look for the compiler used
           88  +$!
           89  +$ gosub check_compiler
           90  +$ close topt
           91  +$ close optf
           92  +$!
           93  +$ if its_decc
           94  +$ then
           95  +$   ccopt = "/prefix=all" + ccopt
           96  +$   if f$trnlnm("SYS") .eqs. ""
           97  +$   then
           98  +$     if axp
           99  +$     then
          100  +$       define sys sys$library:
          101  +$     else
          102  +$       ccopt = "/decc" + ccopt
          103  +$       define sys decc$library_include:
          104  +$     endif
          105  +$   endif
          106  +$!
          107  +$! 2012-03-05 SMS.
          108  +$! Why /NAMES = AS_IS?  Why not simply ".not. vax"?  And why not on VAX?
          109  +$!
          110  +$   if axp .or. ia64
          111  +$   then
          112  +$       ccopt = ccopt + "/name=as_is/opt=(inline=speed)"
          113  +$       s_case = true
          114  +$   endif
          115  +$ endif
          116  +$ if its_vaxc .or. its_gnuc
          117  +$ then
          118  +$    if f$trnlnm("SYS").eqs."" then define sys sys$library:
          119  +$ endif
          120  +$!
          121  +$! Build a fake configure input header
          122  +$!
          123  +$ open/write conf_hin config.hin
          124  +$ write conf_hin "#undef _LARGEFILE64_SOURCE"
          125  +$ close conf_hin
          126  +$!
          127  +$!
          128  +$ i = 0
          129  +$FIND_ACONF:
          130  +$ fname = f$element(i,"#",aconf_in_file)
          131  +$ if fname .eqs. "#" then goto AMISS_ERR
          132  +$ if f$search(fname) .eqs. ""
          133  +$ then
          134  +$   i = i + 1
          135  +$   goto find_aconf
          136  +$ endif
          137  +$ open/read/err=aconf_err aconf_in 'fname'
          138  +$ open/write aconf zconf.h
          139  +$ACONF_LOOP:
          140  +$ read/end_of_file=aconf_exit aconf_in line
          141  +$ work = f$edit(line, "compress,trim")
          142  +$ if f$extract(0,6,work) .nes. "#undef"
          143  +$ then
          144  +$   if f$extract(0,12,work) .nes. "#cmakedefine"
          145  +$   then
          146  +$       write aconf line
          147  +$   endif
          148  +$ else
          149  +$   cdef = f$element(1," ",work)
          150  +$   gosub check_config
          151  +$ endif
          152  +$ goto aconf_loop
          153  +$ACONF_EXIT:
          154  +$ write aconf ""
          155  +$ write aconf "/* VMS specifics added by make_vms.com: */"
          156  +$ write aconf "#define VMS 1"
          157  +$ write aconf "#include <unistd.h>"
          158  +$ write aconf "#include <unixio.h>"
          159  +$ write aconf "#ifdef _LARGEFILE"
          160  +$ write aconf "# define off64_t __off64_t"
          161  +$ write aconf "# define fopen64 fopen"
          162  +$ write aconf "# define fseeko64 fseeko"
          163  +$ write aconf "# define lseek64 lseek"
          164  +$ write aconf "# define ftello64 ftell"
          165  +$ write aconf "#endif"
          166  +$ write aconf "#if !defined( __VAX) && (__CRTL_VER >= 70312000)"
          167  +$ write aconf "# define HAVE_VSNPRINTF"
          168  +$ write aconf "#endif"
          169  +$ close aconf_in
          170  +$ close aconf
          171  +$ if f$search("''th'") .nes. "" then delete 'th';*
          172  +$! Build the thing plain or with mms
          173  +$!
          174  +$ write sys$output "Compiling Zlib sources ..."
          175  +$ if make.eqs.""
          176  +$ then
          177  +$   if (f$search( "example.obj;*") .nes. "") then delete example.obj;*
          178  +$   if (f$search( "minigzip.obj;*") .nes. "") then delete minigzip.obj;*
          179  +$   CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" -
          180  +                adler32.c zlib.h zconf.h
          181  +$   CALL MAKE compress.OBJ "CC ''CCOPT' compress" -
          182  +                compress.c zlib.h zconf.h
          183  +$   CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" -
          184  +                crc32.c zlib.h zconf.h
          185  +$   CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" -
          186  +                deflate.c deflate.h zutil.h zlib.h zconf.h
          187  +$   CALL MAKE gzclose.OBJ "CC ''CCOPT' gzclose" -
          188  +                gzclose.c zutil.h zlib.h zconf.h
          189  +$   CALL MAKE gzlib.OBJ "CC ''CCOPT' gzlib" -
          190  +                gzlib.c zutil.h zlib.h zconf.h
          191  +$   CALL MAKE gzread.OBJ "CC ''CCOPT' gzread" -
          192  +                gzread.c zutil.h zlib.h zconf.h
          193  +$   CALL MAKE gzwrite.OBJ "CC ''CCOPT' gzwrite" -
          194  +                gzwrite.c zutil.h zlib.h zconf.h
          195  +$   CALL MAKE infback.OBJ "CC ''CCOPT' infback" -
          196  +                infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h
          197  +$   CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" -
          198  +                inffast.c zutil.h zlib.h zconf.h inffast.h
          199  +$   CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" -
          200  +                inflate.c zutil.h zlib.h zconf.h infblock.h
          201  +$   CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" -
          202  +                inftrees.c zutil.h zlib.h zconf.h inftrees.h
          203  +$   CALL MAKE trees.OBJ "CC ''CCOPT' trees" -
          204  +                trees.c deflate.h zutil.h zlib.h zconf.h
          205  +$   CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" -
          206  +                uncompr.c zlib.h zconf.h
          207  +$   CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" -
          208  +                zutil.c zutil.h zlib.h zconf.h
          209  +$   write sys$output "Building Zlib ..."
          210  +$   CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ
          211  +$   write sys$output "Building example..."
          212  +$   CALL MAKE example.OBJ "CC ''CCOPT' [.test]example" -
          213  +                [.test]example.c zlib.h zconf.h
          214  +$   call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb
          215  +$   write sys$output "Building minigzip..."
          216  +$   CALL MAKE minigzip.OBJ "CC ''CCOPT' [.test]minigzip" -
          217  +              [.test]minigzip.c zlib.h zconf.h
          218  +$   call make minigzip.exe -
          219  +              "LINK minigzip,libz.olb/lib" -
          220  +              minigzip.obj libz.olb
          221  +$ else
          222  +$   gosub crea_mms
          223  +$   write sys$output "Make ''name' ''version' with ''Make' "
          224  +$   'make'
          225  +$ endif
          226  +$!
          227  +$! Create shareable image
          228  +$!
          229  +$ gosub crea_olist
          230  +$ write sys$output "Creating libzshr.exe"
          231  +$ call map_2_shopt 'mapfile' 'optfile'
          232  +$ LINK_'lopts'/SHARE=libzshr.exe modules.opt/opt,'optfile'/opt
          233  +$ write sys$output "Zlib build completed"
          234  +$ delete/nolog tmp.opt;*
          235  +$ exit
          236  +$AMISS_ERR:
          237  +$ write sys$output "No source for config.hin found."
          238  +$ write sys$output "Tried any of ''aconf_in_file'"
          239  +$ goto err_exit
          240  +$CC_ERR:
          241  +$ write sys$output "C compiler required to build ''name'"
          242  +$ goto err_exit
          243  +$ERR_EXIT:
          244  +$ set message/facil/ident/sever/text
          245  +$ close/nolog optf
          246  +$ close/nolog topt
          247  +$ close/nolog aconf_in
          248  +$ close/nolog aconf
          249  +$ close/nolog out
          250  +$ close/nolog min
          251  +$ close/nolog mod
          252  +$ close/nolog h_in
          253  +$ write sys$output "Exiting..."
          254  +$ exit 2
          255  +$!
          256  +$!
          257  +$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
          258  +$ V = 'F$Verify(0)
          259  +$! P1 = What we are trying to make
          260  +$! P2 = Command to make it
          261  +$! P3 - P8  What it depends on
          262  +$
          263  +$ If F$Search(P1) .Eqs. "" Then Goto Makeit
          264  +$ Time = F$CvTime(F$File(P1,"RDT"))
          265  +$arg=3
          266  +$Loop:
          267  +$       Argument = P'arg
          268  +$       If Argument .Eqs. "" Then Goto Exit
          269  +$       El=0
          270  +$Loop2:
          271  +$       File = F$Element(El," ",Argument)
          272  +$       If File .Eqs. " " Then Goto Endl
          273  +$       AFile = ""
          274  +$Loop3:
          275  +$       OFile = AFile
          276  +$       AFile = F$Search(File)
          277  +$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
          278  +$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
          279  +$       Goto Loop3
          280  +$NextEL:
          281  +$       El = El + 1
          282  +$       Goto Loop2
          283  +$EndL:
          284  +$ arg=arg+1
          285  +$ If arg .Le. 8 Then Goto Loop
          286  +$ Goto Exit
          287  +$
          288  +$Makeit:
          289  +$ VV=F$VERIFY(0)
          290  +$ write sys$output P2
          291  +$ 'P2
          292  +$ VV='F$Verify(VV)
          293  +$Exit:
          294  +$ If V Then Set Verify
          295  +$ENDSUBROUTINE
          296  +$!------------------------------------------------------------------------------
          297  +$!
          298  +$! Check command line options and set symbols accordingly
          299  +$!
          300  +$!------------------------------------------------------------------------------
          301  +$! Version history
          302  +$! 0.01 20041206 First version to receive a number
          303  +$! 0.02 20060126 Add new "HELP" target
          304  +$ CHECK_OPTS:
          305  +$ i = 1
          306  +$ OPT_LOOP:
          307  +$ if i .lt. 9
          308  +$ then
          309  +$   cparm = f$edit(p'i',"upcase")
          310  +$!
          311  +$! Check if parameter actually contains something
          312  +$!
          313  +$   if f$edit(cparm,"trim") .nes. ""
          314  +$   then
          315  +$     if cparm .eqs. "DEBUG"
          316  +$     then
          317  +$       ccopt = ccopt + "/noopt/deb"
          318  +$       lopts = lopts + "/deb"
          319  +$     endif
          320  +$     if f$locate("CCOPT=",cparm) .lt. f$length(cparm)
          321  +$     then
          322  +$       start = f$locate("=",cparm) + 1
          323  +$       len   = f$length(cparm) - start
          324  +$       ccopt = ccopt + f$extract(start,len,cparm)
          325  +$       if f$locate("AS_IS",f$edit(ccopt,"UPCASE")) .lt. f$length(ccopt) -
          326  +          then s_case = true
          327  +$     endif
          328  +$     if cparm .eqs. "LINK" then linkonly = true
          329  +$     if f$locate("LOPTS=",cparm) .lt. f$length(cparm)
          330  +$     then
          331  +$       start = f$locate("=",cparm) + 1
          332  +$       len   = f$length(cparm) - start
          333  +$       lopts = lopts + f$extract(start,len,cparm)
          334  +$     endif
          335  +$     if f$locate("CC=",cparm) .lt. f$length(cparm)
          336  +$     then
          337  +$       start  = f$locate("=",cparm) + 1
          338  +$       len    = f$length(cparm) - start
          339  +$       cc_com = f$extract(start,len,cparm)
          340  +        if (cc_com .nes. "DECC") .and. -
          341  +           (cc_com .nes. "VAXC") .and. -
          342  +           (cc_com .nes. "GNUC")
          343  +$       then
          344  +$         write sys$output "Unsupported compiler choice ''cc_com' ignored"
          345  +$         write sys$output "Use DECC, VAXC, or GNUC instead"
          346  +$       else
          347  +$         if cc_com .eqs. "DECC" then its_decc = true
          348  +$         if cc_com .eqs. "VAXC" then its_vaxc = true
          349  +$         if cc_com .eqs. "GNUC" then its_gnuc = true
          350  +$       endif
          351  +$     endif
          352  +$     if f$locate("MAKE=",cparm) .lt. f$length(cparm)
          353  +$     then
          354  +$       start  = f$locate("=",cparm) + 1
          355  +$       len    = f$length(cparm) - start
          356  +$       mmks = f$extract(start,len,cparm)
          357  +$       if (mmks .eqs. "MMK") .or. (mmks .eqs. "MMS")
          358  +$       then
          359  +$         make = mmks
          360  +$       else
          361  +$         write sys$output "Unsupported make choice ''mmks' ignored"
          362  +$         write sys$output "Use MMK or MMS instead"
          363  +$       endif
          364  +$     endif
          365  +$     if cparm .eqs. "HELP" then gosub bhelp
          366  +$   endif
          367  +$   i = i + 1
          368  +$   goto opt_loop
          369  +$ endif
          370  +$ return
          371  +$!------------------------------------------------------------------------------
          372  +$!
          373  +$! Look for the compiler used
          374  +$!
          375  +$! Version history
          376  +$! 0.01 20040223 First version to receive a number
          377  +$! 0.02 20040229 Save/set value of decc$no_rooted_search_lists
          378  +$! 0.03 20060202 Extend handling of GNU C
          379  +$! 0.04 20090402 Compaq -> hp
          380  +$CHECK_COMPILER:
          381  +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc))
          382  +$ then
          383  +$   its_decc = (f$search("SYS$SYSTEM:DECC$COMPILER.EXE") .nes. "")
          384  +$   its_vaxc = .not. its_decc .and. (F$Search("SYS$System:VAXC.Exe") .nes. "")
          385  +$   its_gnuc = .not. (its_decc .or. its_vaxc) .and. (f$trnlnm("gnu_cc") .nes. "")
          386  +$ endif
          387  +$!
          388  +$! Exit if no compiler available
          389  +$!
          390  +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc))
          391  +$ then goto CC_ERR
          392  +$ else
          393  +$   if its_decc
          394  +$   then
          395  +$     write sys$output "CC compiler check ... hp C"
          396  +$     if f$trnlnm("decc$no_rooted_search_lists") .nes. ""
          397  +$     then
          398  +$       dnrsl = f$trnlnm("decc$no_rooted_search_lists")
          399  +$     endif
          400  +$     define/nolog decc$no_rooted_search_lists 1
          401  +$   else
          402  +$     if its_vaxc then write sys$output "CC compiler check ... VAX C"
          403  +$     if its_gnuc
          404  +$     then
          405  +$         write sys$output "CC compiler check ... GNU C"
          406  +$         if f$trnlnm(topt) then write topt "gnu_cc:[000000]gcclib.olb/lib"
          407  +$         if f$trnlnm(optf) then write optf "gnu_cc:[000000]gcclib.olb/lib"
          408  +$         cc = "gcc"
          409  +$     endif
          410  +$     if f$trnlnm(topt) then write topt "sys$share:vaxcrtl.exe/share"
          411  +$     if f$trnlnm(optf) then write optf "sys$share:vaxcrtl.exe/share"
          412  +$   endif
          413  +$ endif
          414  +$ return
          415  +$!------------------------------------------------------------------------------
          416  +$!
          417  +$! If MMS/MMK are available dump out the descrip.mms if required
          418  +$!
          419  +$CREA_MMS:
          420  +$ write sys$output "Creating descrip.mms..."
          421  +$ create descrip.mms
          422  +$ open/append out descrip.mms
          423  +$ copy sys$input: out
          424  +$ deck
          425  +# descrip.mms: MMS description file for building zlib on VMS
          426  +# written by Martin P.J. Zinser
          427  +# <zinser@zinser.no-ip.info or martin.zinser@eurexchange.com>
          428  +
          429  +OBJS = adler32.obj, compress.obj, crc32.obj, gzclose.obj, gzlib.obj\
          430  +       gzread.obj, gzwrite.obj, uncompr.obj, infback.obj\
          431  +       deflate.obj, trees.obj, zutil.obj, inflate.obj, \
          432  +       inftrees.obj, inffast.obj
          433  +
          434  +$ eod
          435  +$ write out "CFLAGS=", ccopt
          436  +$ write out "LOPTS=", lopts
          437  +$ write out "all : example.exe minigzip.exe libz.olb"
          438  +$ copy sys$input: out
          439  +$ deck
          440  +        @ write sys$output " Example applications available"
          441  +
          442  +libz.olb : libz.olb($(OBJS))
          443  +	@ write sys$output " libz available"
          444  +
          445  +example.exe : example.obj libz.olb
          446  +              link $(LOPTS) example,libz.olb/lib
          447  +
          448  +minigzip.exe : minigzip.obj libz.olb
          449  +              link $(LOPTS) minigzip,libz.olb/lib
          450  +
          451  +clean :
          452  +	delete *.obj;*,libz.olb;*,*.opt;*,*.exe;*
          453  +
          454  +
          455  +# Other dependencies.
          456  +adler32.obj  : adler32.c zutil.h zlib.h zconf.h
          457  +compress.obj : compress.c zlib.h zconf.h
          458  +crc32.obj    : crc32.c zutil.h zlib.h zconf.h
          459  +deflate.obj  : deflate.c deflate.h zutil.h zlib.h zconf.h
          460  +example.obj  : [.test]example.c zlib.h zconf.h
          461  +gzclose.obj  : gzclose.c zutil.h zlib.h zconf.h
          462  +gzlib.obj    : gzlib.c zutil.h zlib.h zconf.h
          463  +gzread.obj   : gzread.c zutil.h zlib.h zconf.h
          464  +gzwrite.obj  : gzwrite.c zutil.h zlib.h zconf.h
          465  +inffast.obj  : inffast.c zutil.h zlib.h zconf.h inftrees.h inffast.h
          466  +inflate.obj  : inflate.c zutil.h zlib.h zconf.h
          467  +inftrees.obj : inftrees.c zutil.h zlib.h zconf.h inftrees.h
          468  +minigzip.obj : [.test]minigzip.c zlib.h zconf.h
          469  +trees.obj    : trees.c deflate.h zutil.h zlib.h zconf.h
          470  +uncompr.obj  : uncompr.c zlib.h zconf.h
          471  +zutil.obj    : zutil.c zutil.h zlib.h zconf.h
          472  +infback.obj  : infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h
          473  +$ eod
          474  +$ close out
          475  +$ return
          476  +$!------------------------------------------------------------------------------
          477  +$!
          478  +$! Read list of core library sources from makefile.in and create options
          479  +$! needed to build shareable image
          480  +$!
          481  +$CREA_OLIST:
          482  +$ open/read min makefile.in
          483  +$ open/write mod modules.opt
          484  +$ src_check_list = "OBJZ =#OBJG ="
          485  +$MRLOOP:
          486  +$ read/end=mrdone min rec
          487  +$ i = 0
          488  +$SRC_CHECK_LOOP:
          489  +$ src_check = f$element(i, "#", src_check_list)
          490  +$ i = i+1
          491  +$ if src_check .eqs. "#" then goto mrloop
          492  +$ if (f$extract(0,6,rec) .nes. src_check) then goto src_check_loop
          493  +$ rec = rec - src_check
          494  +$ gosub extra_filnam
          495  +$ if (f$element(1,"\",rec) .eqs. "\") then goto mrloop
          496  +$MRSLOOP:
          497  +$ read/end=mrdone min rec
          498  +$ gosub extra_filnam
          499  +$ if (f$element(1,"\",rec) .nes. "\") then goto mrsloop
          500  +$MRDONE:
          501  +$ close min
          502  +$ close mod
          503  +$ return
          504  +$!------------------------------------------------------------------------------
          505  +$!
          506  +$! Take record extracted in crea_olist and split it into single filenames
          507  +$!
          508  +$EXTRA_FILNAM:
          509  +$ myrec = f$edit(rec - "\", "trim,compress")
          510  +$ i = 0
          511  +$FELOOP:
          512  +$ srcfil = f$element(i," ", myrec)
          513  +$ if (srcfil .nes. " ")
          514  +$ then
          515  +$   write mod f$parse(srcfil,,,"NAME"), ".obj"
          516  +$   i = i + 1
          517  +$   goto feloop
          518  +$ endif
          519  +$ return
          520  +$!------------------------------------------------------------------------------
          521  +$!
          522  +$! Find current Zlib version number
          523  +$!
          524  +$FIND_VERSION:
          525  +$ open/read h_in 'v_file'
          526  +$hloop:
          527  +$ read/end=hdone h_in rec
          528  +$ rec = f$edit(rec,"TRIM")
          529  +$ if (f$extract(0,1,rec) .nes. "#") then goto hloop
          530  +$ rec = f$edit(rec - "#", "TRIM")
          531  +$ if f$element(0," ",rec) .nes. "define" then goto hloop
          532  +$ if f$element(1," ",rec) .eqs. v_string
          533  +$ then
          534  +$   version = 'f$element(2," ",rec)'
          535  +$   goto hdone
          536  +$ endif
          537  +$ goto hloop
          538  +$hdone:
          539  +$ close h_in
          540  +$ return
          541  +$!------------------------------------------------------------------------------
          542  +$!
          543  +$CHECK_CONFIG:
          544  +$!
          545  +$ in_ldef = f$locate(cdef,libdefs)
          546  +$ if (in_ldef .lt. f$length(libdefs))
          547  +$ then
          548  +$   write aconf "#define ''cdef' 1"
          549  +$   libdefs = f$extract(0,in_ldef,libdefs) + -
          550  +              f$extract(in_ldef + f$length(cdef) + 1, -
          551  +                        f$length(libdefs) - in_ldef - f$length(cdef) - 1, -
          552  +                        libdefs)
          553  +$ else
          554  +$   if (f$type('cdef') .eqs. "INTEGER")
          555  +$   then
          556  +$     write aconf "#define ''cdef' ", 'cdef'
          557  +$   else
          558  +$     if (f$type('cdef') .eqs. "STRING")
          559  +$     then
          560  +$       write aconf "#define ''cdef' ", """", '''cdef'', """"
          561  +$     else
          562  +$       gosub check_cc_def
          563  +$     endif
          564  +$   endif
          565  +$ endif
          566  +$ return
          567  +$!------------------------------------------------------------------------------
          568  +$!
          569  +$! Check if this is a define relating to the properties of the C/C++
          570  +$! compiler
          571  +$!
          572  +$ CHECK_CC_DEF:
          573  +$ if (cdef .eqs. "_LARGEFILE64_SOURCE")
          574  +$ then
          575  +$   copy sys$input: 'tc'
          576  +$   deck
          577  +#include "tconfig"
          578  +#define _LARGEFILE
          579  +#include <stdio.h>
          580  +
          581  +int main(){
          582  +FILE *fp;
          583  +  fp = fopen("temp.txt","r");
          584  +  fseeko(fp,1,SEEK_SET);
          585  +  fclose(fp);
          586  +}
          587  +
          588  +$   eod
          589  +$   test_inv = false
          590  +$   comm_h = false
          591  +$   gosub cc_prop_check
          592  +$   return
          593  +$ endif
          594  +$ write aconf "/* ", line, " */"
          595  +$ return
          596  +$!------------------------------------------------------------------------------
          597  +$!
          598  +$! Check for properties of C/C++ compiler
          599  +$!
          600  +$! Version history
          601  +$! 0.01 20031020 First version to receive a number
          602  +$! 0.02 20031022 Added logic for defines with value
          603  +$! 0.03 20040309 Make sure local config file gets not deleted
          604  +$! 0.04 20041230 Also write include for configure run
          605  +$! 0.05 20050103 Add processing of "comment defines"
          606  +$CC_PROP_CHECK:
          607  +$ cc_prop = true
          608  +$ is_need = false
          609  +$ is_need = (f$extract(0,4,cdef) .eqs. "NEED") .or. (test_inv .eq. true)
          610  +$ if f$search(th) .eqs. "" then create 'th'
          611  +$ set message/nofac/noident/nosever/notext
          612  +$ on error then continue
          613  +$ cc 'tmpnam'
          614  +$ if .not. ($status)  then cc_prop = false
          615  +$ on error then continue
          616  +$! The headers might lie about the capabilities of the RTL
          617  +$ link 'tmpnam',tmp.opt/opt
          618  +$ if .not. ($status)  then cc_prop = false
          619  +$ set message/fac/ident/sever/text
          620  +$ on error then goto err_exit
          621  +$ delete/nolog 'tmpnam'.*;*/exclude='th'
          622  +$ if (cc_prop .and. .not. is_need) .or. -
          623  +     (.not. cc_prop .and. is_need)
          624  +$ then
          625  +$   write sys$output "Checking for ''cdef'... yes"
          626  +$   if f$type('cdef_val'_yes) .nes. ""
          627  +$   then
          628  +$     if f$type('cdef_val'_yes) .eqs. "INTEGER" -
          629  +         then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_yes)
          630  +$     if f$type('cdef_val'_yes) .eqs. "STRING" -
          631  +         then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_yes)
          632  +$   else
          633  +$     call write_config f$fao("#define !AS 1",cdef)
          634  +$   endif
          635  +$   if (cdef .eqs. "HAVE_FSEEKO") .or. (cdef .eqs. "_LARGE_FILES") .or. -
          636  +       (cdef .eqs. "_LARGEFILE64_SOURCE") then -
          637  +      call write_config f$string("#define _LARGEFILE 1")
          638  +$ else
          639  +$   write sys$output "Checking for ''cdef'... no"
          640  +$   if (comm_h)
          641  +$   then
          642  +      call write_config f$fao("/* !AS */",line)
          643  +$   else
          644  +$     if f$type('cdef_val'_no) .nes. ""
          645  +$     then
          646  +$       if f$type('cdef_val'_no) .eqs. "INTEGER" -
          647  +           then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_no)
          648  +$       if f$type('cdef_val'_no) .eqs. "STRING" -
          649  +           then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_no)
          650  +$     else
          651  +$       call write_config f$fao("#undef !AS",cdef)
          652  +$     endif
          653  +$   endif
          654  +$ endif
          655  +$ return
          656  +$!------------------------------------------------------------------------------
          657  +$!
          658  +$! Check for properties of C/C++ compiler with multiple result values
          659  +$!
          660  +$! Version history
          661  +$! 0.01 20040127 First version
          662  +$! 0.02 20050103 Reconcile changes from cc_prop up to version 0.05
          663  +$CC_MPROP_CHECK:
          664  +$ cc_prop = true
          665  +$ i    = 1
          666  +$ idel = 1
          667  +$ MT_LOOP:
          668  +$ if f$type(result_'i') .eqs. "STRING"
          669  +$ then
          670  +$   set message/nofac/noident/nosever/notext
          671  +$   on error then continue
          672  +$   cc 'tmpnam'_'i'
          673  +$   if .not. ($status)  then cc_prop = false
          674  +$   on error then continue
          675  +$! The headers might lie about the capabilities of the RTL
          676  +$   link 'tmpnam'_'i',tmp.opt/opt
          677  +$   if .not. ($status)  then cc_prop = false
          678  +$   set message/fac/ident/sever/text
          679  +$   on error then goto err_exit
          680  +$   delete/nolog 'tmpnam'_'i'.*;*
          681  +$   if (cc_prop)
          682  +$   then
          683  +$     write sys$output "Checking for ''cdef'... ", mdef_'i'
          684  +$     if f$type(mdef_'i') .eqs. "INTEGER" -
          685  +         then call write_config f$fao("#define !AS !UL",cdef,mdef_'i')
          686  +$     if f$type('cdef_val'_yes) .eqs. "STRING" -
          687  +         then call write_config f$fao("#define !AS !AS",cdef,mdef_'i')
          688  +$     goto msym_clean
          689  +$   else
          690  +$     i = i + 1
          691  +$     goto mt_loop
          692  +$   endif
          693  +$ endif
          694  +$ write sys$output "Checking for ''cdef'... no"
          695  +$ call write_config f$fao("#undef !AS",cdef)
          696  +$ MSYM_CLEAN:
          697  +$ if (idel .le. msym_max)
          698  +$ then
          699  +$   delete/sym mdef_'idel'
          700  +$   idel = idel + 1
          701  +$   goto msym_clean
          702  +$ endif
          703  +$ return
          704  +$!------------------------------------------------------------------------------
          705  +$!
          706  +$! Write configuration to both permanent and temporary config file
          707  +$!
          708  +$! Version history
          709  +$! 0.01 20031029 First version to receive a number
          710  +$!
          711  +$WRITE_CONFIG: SUBROUTINE
          712  +$  write aconf 'p1'
          713  +$  open/append confh 'th'
          714  +$  write confh 'p1'
          715  +$  close confh
          716  +$ENDSUBROUTINE
          717  +$!------------------------------------------------------------------------------
          718  +$!
          719  +$! Analyze the project map file and create the symbol vector for a shareable
          720  +$! image from it
          721  +$!
          722  +$! Version history
          723  +$! 0.01 20120128 First version
          724  +$! 0.02 20120226 Add pre-load logic
          725  +$!
          726  +$ MAP_2_SHOPT: Subroutine
          727  +$!
          728  +$ SAY := "WRITE_ SYS$OUTPUT"
          729  +$!
          730  +$ IF F$SEARCH("''P1'") .EQS. ""
          731  +$ THEN
          732  +$    SAY "MAP_2_SHOPT-E-NOSUCHFILE:  Error, inputfile ''p1' not available"
          733  +$    goto exit_m2s
          734  +$ ENDIF
          735  +$ IF "''P2'" .EQS. ""
          736  +$ THEN
          737  +$    SAY "MAP_2_SHOPT:  Error, no output file provided"
          738  +$    goto exit_m2s
          739  +$ ENDIF
          740  +$!
          741  +$ module1 = "deflate#deflateEnd#deflateInit_#deflateParams#deflateSetDictionary"
          742  +$ module2 = "gzclose#gzerror#gzgetc#gzgets#gzopen#gzprintf#gzputc#gzputs#gzread"
          743  +$ module3 = "gzseek#gztell#inflate#inflateEnd#inflateInit_#inflateSetDictionary"
          744  +$ module4 = "inflateSync#uncompress#zlibVersion#compress"
          745  +$ open/read map 'p1
          746  +$ if axp .or. ia64
          747  +$ then
          748  +$     open/write aopt a.opt
          749  +$     open/write bopt b.opt
          750  +$     write aopt " CASE_SENSITIVE=YES"
          751  +$     write bopt "SYMBOL_VECTOR= (-"
          752  +$     mod_sym_num = 1
          753  +$ MOD_SYM_LOOP:
          754  +$     if f$type(module'mod_sym_num') .nes. ""
          755  +$     then
          756  +$         mod_in = 0
          757  +$ MOD_SYM_IN:
          758  +$         shared_proc = f$element(mod_in, "#", module'mod_sym_num')
          759  +$         if shared_proc .nes. "#"
          760  +$         then
          761  +$             write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",-
          762  +        		       f$edit(shared_proc,"upcase"),shared_proc)
          763  +$             write bopt f$fao("!AS=PROCEDURE,-",shared_proc)
          764  +$             mod_in = mod_in + 1
          765  +$             goto mod_sym_in
          766  +$         endif
          767  +$         mod_sym_num = mod_sym_num + 1
          768  +$         goto mod_sym_loop
          769  +$     endif
          770  +$MAP_LOOP:
          771  +$     read/end=map_end map line
          772  +$     if (f$locate("{",line).lt. f$length(line)) .or. -
          773  +         (f$locate("global:", line) .lt. f$length(line))
          774  +$     then
          775  +$         proc = true
          776  +$         goto map_loop
          777  +$     endif
          778  +$     if f$locate("}",line).lt. f$length(line) then proc = false
          779  +$     if f$locate("local:", line) .lt. f$length(line) then proc = false
          780  +$     if proc
          781  +$     then
          782  +$         shared_proc = f$edit(line,"collapse")
          783  +$         chop_semi = f$locate(";", shared_proc)
          784  +$         if chop_semi .lt. f$length(shared_proc) then -
          785  +              shared_proc = f$extract(0, chop_semi, shared_proc)
          786  +$         write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",-
          787  +        			 f$edit(shared_proc,"upcase"),shared_proc)
          788  +$         write bopt f$fao("!AS=PROCEDURE,-",shared_proc)
          789  +$     endif
          790  +$     goto map_loop
          791  +$MAP_END:
          792  +$     close/nolog aopt
          793  +$     close/nolog bopt
          794  +$     open/append libopt 'p2'
          795  +$     open/read aopt a.opt
          796  +$     open/read bopt b.opt
          797  +$ALOOP:
          798  +$     read/end=aloop_end aopt line
          799  +$     write libopt line
          800  +$     goto aloop
          801  +$ALOOP_END:
          802  +$     close/nolog aopt
          803  +$     sv = ""
          804  +$BLOOP:
          805  +$     read/end=bloop_end bopt svn
          806  +$     if (svn.nes."")
          807  +$     then
          808  +$        if (sv.nes."") then write libopt sv
          809  +$        sv = svn
          810  +$     endif
          811  +$     goto bloop
          812  +$BLOOP_END:
          813  +$     write libopt f$extract(0,f$length(sv)-2,sv), "-"
          814  +$     write libopt ")"
          815  +$     close/nolog bopt
          816  +$     delete/nolog/noconf a.opt;*,b.opt;*
          817  +$ else
          818  +$     if vax
          819  +$     then
          820  +$     open/append libopt 'p2'
          821  +$     mod_sym_num = 1
          822  +$ VMOD_SYM_LOOP:
          823  +$     if f$type(module'mod_sym_num') .nes. ""
          824  +$     then
          825  +$         mod_in = 0
          826  +$ VMOD_SYM_IN:
          827  +$         shared_proc = f$element(mod_in, "#", module'mod_sym_num')
          828  +$         if shared_proc .nes. "#"
          829  +$         then
          830  +$     	      write libopt f$fao("UNIVERSAL=!AS",-
          831  +      	  			     f$edit(shared_proc,"upcase"))
          832  +$             mod_in = mod_in + 1
          833  +$             goto vmod_sym_in
          834  +$         endif
          835  +$         mod_sym_num = mod_sym_num + 1
          836  +$         goto vmod_sym_loop
          837  +$     endif
          838  +$VMAP_LOOP:
          839  +$     	  read/end=vmap_end map line
          840  +$     	  if (f$locate("{",line).lt. f$length(line)) .or. -
          841  +   	      (f$locate("global:", line) .lt. f$length(line))
          842  +$     	  then
          843  +$     	      proc = true
          844  +$     	      goto vmap_loop
          845  +$     	  endif
          846  +$     	  if f$locate("}",line).lt. f$length(line) then proc = false
          847  +$     	  if f$locate("local:", line) .lt. f$length(line) then proc = false
          848  +$     	  if proc
          849  +$     	  then
          850  +$     	      shared_proc = f$edit(line,"collapse")
          851  +$     	      chop_semi = f$locate(";", shared_proc)
          852  +$     	      if chop_semi .lt. f$length(shared_proc) then -
          853  +      	  	  shared_proc = f$extract(0, chop_semi, shared_proc)
          854  +$     	      write libopt f$fao("UNIVERSAL=!AS",-
          855  +      	  			     f$edit(shared_proc,"upcase"))
          856  +$     	  endif
          857  +$     	  goto vmap_loop
          858  +$VMAP_END:
          859  +$     else
          860  +$         write sys$output "Unknown Architecture (Not VAX, AXP, or IA64)"
          861  +$         write sys$output "No options file created"
          862  +$     endif
          863  +$ endif
          864  +$ EXIT_M2S:
          865  +$ close/nolog map
          866  +$ close/nolog libopt
          867  +$ endsubroutine

Added compat/zlib/msdos/Makefile.bor.

            1  +# Makefile for zlib
            2  +# Borland C++
            3  +# Last updated: 15-Mar-2003
            4  +
            5  +# To use, do "make -fmakefile.bor"
            6  +# To compile in small model, set below: MODEL=s
            7  +
            8  +# WARNING: the small model is supported but only for small values of
            9  +# MAX_WBITS and MAX_MEM_LEVEL. For example:
           10  +#    -DMAX_WBITS=11 -DDEF_WBITS=11 -DMAX_MEM_LEVEL=3
           11  +# If you wish to reduce the memory requirements (default 256K for big
           12  +# objects plus a few K), you can add to the LOC macro below:
           13  +#   -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14
           14  +# See zconf.h for details about the memory requirements.
           15  +
           16  +# ------------ Turbo C++, Borland C++ ------------
           17  +
           18  +#    Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7)
           19  +#    should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added
           20  +#    to the declaration of LOC here:
           21  +LOC = $(LOCAL_ZLIB)
           22  +
           23  +# type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc.
           24  +CPU_TYP = 0
           25  +
           26  +# memory model: one of s, m, c, l (small, medium, compact, large)
           27  +MODEL=l
           28  +
           29  +# replace bcc with tcc for Turbo C++ 1.0, with bcc32 for the 32 bit version
           30  +CC=bcc
           31  +LD=bcc
           32  +AR=tlib
           33  +
           34  +# compiler flags
           35  +# replace "-O2" by "-O -G -a -d" for Turbo C++ 1.0
           36  +CFLAGS=-O2 -Z -m$(MODEL) $(LOC)
           37  +
           38  +LDFLAGS=-m$(MODEL) -f-
           39  +
           40  +
           41  +# variables
           42  +ZLIB_LIB = zlib_$(MODEL).lib
           43  +
           44  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           45  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           46  +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj
           47  +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj
           48  +
           49  +
           50  +# targets
           51  +all: $(ZLIB_LIB) example.exe minigzip.exe
           52  +
           53  +.c.obj:
           54  +	$(CC) -c $(CFLAGS) $*.c
           55  +
           56  +adler32.obj: adler32.c zlib.h zconf.h
           57  +
           58  +compress.obj: compress.c zlib.h zconf.h
           59  +
           60  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           61  +
           62  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           63  +
           64  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           65  +
           66  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           67  +
           68  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           69  +
           70  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           71  +
           72  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           73  + inffast.h inffixed.h
           74  +
           75  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           76  + inffast.h
           77  +
           78  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           79  + inffast.h inffixed.h
           80  +
           81  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           82  +
           83  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           84  +
           85  +uncompr.obj: uncompr.c zlib.h zconf.h
           86  +
           87  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           88  +
           89  +example.obj: test/example.c zlib.h zconf.h
           90  +
           91  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           92  +
           93  +
           94  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           95  +$(ZLIB_LIB): $(OBJ1) $(OBJ2)
           96  +	-del $(ZLIB_LIB)
           97  +	$(AR) $(ZLIB_LIB) $(OBJP1)
           98  +	$(AR) $(ZLIB_LIB) $(OBJP2)
           99  +
          100  +example.exe: example.obj $(ZLIB_LIB)
          101  +	$(LD) $(LDFLAGS) example.obj $(ZLIB_LIB)
          102  +
          103  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
          104  +	$(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB)
          105  +
          106  +test: example.exe minigzip.exe
          107  +	example
          108  +	echo hello world | minigzip | minigzip -d
          109  +
          110  +clean:
          111  +	-del *.obj
          112  +	-del *.lib
          113  +	-del *.exe
          114  +	-del zlib_*.bak
          115  +	-del foo.gz

Added compat/zlib/msdos/Makefile.dj2.

            1  +# Makefile for zlib.  Modified for djgpp v2.0 by F. J. Donahoe, 3/15/96.
            2  +# Copyright (C) 1995-1998 Jean-loup Gailly.
            3  +# For conditions of distribution and use, see copyright notice in zlib.h
            4  +
            5  +# To compile, or to compile and test, type:
            6  +#
            7  +#   make -fmakefile.dj2;  make test -fmakefile.dj2
            8  +#
            9  +# To install libz.a, zconf.h and zlib.h in the djgpp directories, type:
           10  +#
           11  +#    make install -fmakefile.dj2
           12  +#
           13  +# after first defining LIBRARY_PATH and INCLUDE_PATH in djgpp.env as
           14  +# in the sample below if the pattern of the DJGPP distribution is to
           15  +# be followed.  Remember that, while <sp>'es around <=> are ignored in
           16  +# makefiles, they are *not* in batch files or in djgpp.env.
           17  +# - - - - -
           18  +# [make]
           19  +# INCLUDE_PATH=%\>;INCLUDE_PATH%%\DJDIR%\include
           20  +# LIBRARY_PATH=%\>;LIBRARY_PATH%%\DJDIR%\lib
           21  +# BUTT=-m486
           22  +# - - - - -
           23  +# Alternately, these variables may be defined below, overriding the values
           24  +# in djgpp.env, as
           25  +# INCLUDE_PATH=c:\usr\include
           26  +# LIBRARY_PATH=c:\usr\lib
           27  +
           28  +CC=gcc
           29  +
           30  +#CFLAGS=-MMD -O
           31  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
           32  +#CFLAGS=-MMD -g -DDEBUG
           33  +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
           34  +             -Wstrict-prototypes -Wmissing-prototypes
           35  +
           36  +# If cp.exe is available, replace "copy /Y" with "cp -fp" .
           37  +CP=copy /Y
           38  +# If gnu install.exe is available, replace $(CP) with ginstall.
           39  +INSTALL=$(CP)
           40  +# The default value of RM is "rm -f."  If "rm.exe" is found, comment out:
           41  +RM=del
           42  +LDLIBS=-L. -lz
           43  +LD=$(CC) -s -o
           44  +LDSHARED=$(CC)
           45  +
           46  +INCL=zlib.h zconf.h
           47  +LIBS=libz.a
           48  +
           49  +AR=ar rcs
           50  +
           51  +prefix=/usr/local
           52  +exec_prefix = $(prefix)
           53  +
           54  +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \
           55  +       uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o
           56  +
           57  +OBJA =
           58  +# to use the asm code: make OBJA=match.o
           59  +
           60  +TEST_OBJS = example.o minigzip.o
           61  +
           62  +all: example.exe minigzip.exe
           63  +
           64  +check: test
           65  +test: all
           66  +	./example
           67  +	echo hello world | .\minigzip | .\minigzip -d
           68  +
           69  +%.o : %.c
           70  +	$(CC) $(CFLAGS) -c $< -o $@
           71  +
           72  +libz.a: $(OBJS) $(OBJA)
           73  +	$(AR) $@ $(OBJS) $(OBJA)
           74  +
           75  +%.exe : %.o $(LIBS)
           76  +	$(LD) $@ $< $(LDLIBS)
           77  +
           78  +# INCLUDE_PATH and LIBRARY_PATH were set for [make] in djgpp.env .
           79  +
           80  +.PHONY : uninstall clean
           81  +
           82  +install: $(INCL) $(LIBS)
           83  +	-@if not exist $(INCLUDE_PATH)\nul mkdir $(INCLUDE_PATH)
           84  +	-@if not exist $(LIBRARY_PATH)\nul mkdir $(LIBRARY_PATH)
           85  +	$(INSTALL) zlib.h $(INCLUDE_PATH)
           86  +	$(INSTALL) zconf.h $(INCLUDE_PATH)
           87  +	$(INSTALL) libz.a $(LIBRARY_PATH)
           88  +
           89  +uninstall:
           90  +	$(RM) $(INCLUDE_PATH)\zlib.h
           91  +	$(RM) $(INCLUDE_PATH)\zconf.h
           92  +	$(RM) $(LIBRARY_PATH)\libz.a
           93  +
           94  +clean:
           95  +	$(RM) *.d
           96  +	$(RM) *.o
           97  +	$(RM) *.exe
           98  +	$(RM) libz.a
           99  +	$(RM) foo.gz
          100  +
          101  +DEPS := $(wildcard *.d)
          102  +ifneq ($(DEPS),)
          103  +include $(DEPS)
          104  +endif

Added compat/zlib/msdos/Makefile.emx.

            1  +# Makefile for zlib.  Modified for emx 0.9c by Chr. Spieler, 6/17/98.
            2  +# Copyright (C) 1995-1998 Jean-loup Gailly.
            3  +# For conditions of distribution and use, see copyright notice in zlib.h
            4  +
            5  +# To compile, or to compile and test, type:
            6  +#
            7  +#   make -fmakefile.emx;  make test -fmakefile.emx
            8  +#
            9  +
           10  +CC=gcc
           11  +
           12  +#CFLAGS=-MMD -O
           13  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
           14  +#CFLAGS=-MMD -g -DDEBUG
           15  +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
           16  +             -Wstrict-prototypes -Wmissing-prototypes
           17  +
           18  +# If cp.exe is available, replace "copy /Y" with "cp -fp" .
           19  +CP=copy /Y
           20  +# If gnu install.exe is available, replace $(CP) with ginstall.
           21  +INSTALL=$(CP)
           22  +# The default value of RM is "rm -f."  If "rm.exe" is found, comment out:
           23  +RM=del
           24  +LDLIBS=-L. -lzlib
           25  +LD=$(CC) -s -o
           26  +LDSHARED=$(CC)
           27  +
           28  +INCL=zlib.h zconf.h
           29  +LIBS=zlib.a
           30  +
           31  +AR=ar rcs
           32  +
           33  +prefix=/usr/local
           34  +exec_prefix = $(prefix)
           35  +
           36  +OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \
           37  +       uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o
           38  +
           39  +TEST_OBJS = example.o minigzip.o
           40  +
           41  +all: example.exe minigzip.exe
           42  +
           43  +test: all
           44  +	./example
           45  +	echo hello world | .\minigzip | .\minigzip -d
           46  +
           47  +%.o : %.c
           48  +	$(CC) $(CFLAGS) -c $< -o $@
           49  +
           50  +zlib.a: $(OBJS)
           51  +	$(AR) $@ $(OBJS)
           52  +
           53  +%.exe : %.o $(LIBS)
           54  +	$(LD) $@ $< $(LDLIBS)
           55  +
           56  +
           57  +.PHONY : clean
           58  +
           59  +clean:
           60  +	$(RM) *.d
           61  +	$(RM) *.o
           62  +	$(RM) *.exe
           63  +	$(RM) zlib.a
           64  +	$(RM) foo.gz
           65  +
           66  +DEPS := $(wildcard *.d)
           67  +ifneq ($(DEPS),)
           68  +include $(DEPS)
           69  +endif

Added compat/zlib/msdos/Makefile.msc.

            1  +# Makefile for zlib
            2  +# Microsoft C 5.1 or later
            3  +# Last updated: 19-Mar-2003
            4  +
            5  +# To use, do "make makefile.msc"
            6  +# To compile in small model, set below: MODEL=S
            7  +
            8  +# If you wish to reduce the memory requirements (default 256K for big
            9  +# objects plus a few K), you can add to the LOC macro below:
           10  +#   -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14
           11  +# See zconf.h for details about the memory requirements.
           12  +
           13  +# ------------- Microsoft C 5.1 and later -------------
           14  +
           15  +#    Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7)
           16  +#    should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added
           17  +#    to the declaration of LOC here:
           18  +LOC = $(LOCAL_ZLIB)
           19  +
           20  +# Type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc.
           21  +CPU_TYP = 0
           22  +
           23  +# Memory model: one of S, M, C, L (small, medium, compact, large)
           24  +MODEL=L
           25  +
           26  +CC=cl
           27  +CFLAGS=-nologo -A$(MODEL) -G$(CPU_TYP) -W3 -Oait -Gs $(LOC)
           28  +#-Ox generates bad code with MSC 5.1
           29  +LIB_CFLAGS=-Zl $(CFLAGS)
           30  +
           31  +LD=link
           32  +LDFLAGS=/noi/e/st:0x1500/noe/farcall/packcode
           33  +# "/farcall/packcode" are only useful for `large code' memory models
           34  +# but should be a "no-op" for small code models.
           35  +
           36  +
           37  +# variables
           38  +ZLIB_LIB = zlib_$(MODEL).lib
           39  +
           40  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           41  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           42  +
           43  +
           44  +# targets
           45  +all:  $(ZLIB_LIB) example.exe minigzip.exe
           46  +
           47  +.c.obj:
           48  +	$(CC) -c $(LIB_CFLAGS) $*.c
           49  +
           50  +adler32.obj: adler32.c zlib.h zconf.h
           51  +
           52  +compress.obj: compress.c zlib.h zconf.h
           53  +
           54  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           55  +
           56  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           57  +
           58  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           59  +
           60  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           61  +
           62  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           63  +
           64  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           65  +
           66  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           67  + inffast.h inffixed.h
           68  +
           69  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           70  + inffast.h
           71  +
           72  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           73  + inffast.h inffixed.h
           74  +
           75  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           76  +
           77  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           78  +
           79  +uncompr.obj: uncompr.c zlib.h zconf.h
           80  +
           81  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           82  +
           83  +example.obj: test/example.c zlib.h zconf.h
           84  +	$(CC) -c $(CFLAGS) $*.c
           85  +
           86  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           87  +	$(CC) -c $(CFLAGS) $*.c
           88  +
           89  +
           90  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           91  +$(ZLIB_LIB): $(OBJ1) $(OBJ2)
           92  +	if exist $(ZLIB_LIB) del $(ZLIB_LIB)
           93  +	lib $(ZLIB_LIB) $(OBJ1);
           94  +	lib $(ZLIB_LIB) $(OBJ2);
           95  +
           96  +example.exe: example.obj $(ZLIB_LIB)
           97  +	$(LD) $(LDFLAGS) example.obj,,,$(ZLIB_LIB);
           98  +
           99  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
          100  +	$(LD) $(LDFLAGS) minigzip.obj,,,$(ZLIB_LIB);
          101  +
          102  +test: example.exe minigzip.exe
          103  +	example
          104  +	echo hello world | minigzip | minigzip -d
          105  +
          106  +clean:
          107  +	-del *.obj
          108  +	-del *.lib
          109  +	-del *.exe
          110  +	-del *.map
          111  +	-del zlib_*.bak
          112  +	-del foo.gz

Added compat/zlib/msdos/Makefile.tc.

            1  +# Makefile for zlib
            2  +# Turbo C 2.01, Turbo C++ 1.01
            3  +# Last updated: 15-Mar-2003
            4  +
            5  +# To use, do "make -fmakefile.tc"
            6  +# To compile in small model, set below: MODEL=s
            7  +
            8  +# WARNING: the small model is supported but only for small values of
            9  +# MAX_WBITS and MAX_MEM_LEVEL. For example:
           10  +#    -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
           11  +# If you wish to reduce the memory requirements (default 256K for big
           12  +# objects plus a few K), you can add to CFLAGS below:
           13  +#   -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14
           14  +# See zconf.h for details about the memory requirements.
           15  +
           16  +# ------------ Turbo C 2.01, Turbo C++ 1.01 ------------
           17  +MODEL=l
           18  +CC=tcc
           19  +LD=tcc
           20  +AR=tlib
           21  +# CFLAGS=-O2 -G -Z -m$(MODEL) -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
           22  +CFLAGS=-O2 -G -Z -m$(MODEL)
           23  +LDFLAGS=-m$(MODEL) -f-
           24  +
           25  +
           26  +# variables
           27  +ZLIB_LIB = zlib_$(MODEL).lib
           28  +
           29  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           30  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           31  +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj
           32  +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj
           33  +
           34  +
           35  +# targets
           36  +all: $(ZLIB_LIB) example.exe minigzip.exe
           37  +
           38  +.c.obj:
           39  +	$(CC) -c $(CFLAGS) $*.c
           40  +
           41  +adler32.obj: adler32.c zlib.h zconf.h
           42  +
           43  +compress.obj: compress.c zlib.h zconf.h
           44  +
           45  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           46  +
           47  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           48  +
           49  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           50  +
           51  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           52  +
           53  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           54  +
           55  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           56  +
           57  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           58  + inffast.h inffixed.h
           59  +
           60  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           61  + inffast.h
           62  +
           63  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           64  + inffast.h inffixed.h
           65  +
           66  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           67  +
           68  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           69  +
           70  +uncompr.obj: uncompr.c zlib.h zconf.h
           71  +
           72  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           73  +
           74  +example.obj: test/example.c zlib.h zconf.h
           75  +
           76  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           77  +
           78  +
           79  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           80  +$(ZLIB_LIB): $(OBJ1) $(OBJ2)
           81  +	-del $(ZLIB_LIB)
           82  +	$(AR) $(ZLIB_LIB) $(OBJP1)
           83  +	$(AR) $(ZLIB_LIB) $(OBJP2)
           84  +
           85  +example.exe: example.obj $(ZLIB_LIB)
           86  +	$(LD) $(LDFLAGS) example.obj $(ZLIB_LIB)
           87  +
           88  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
           89  +	$(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB)
           90  +
           91  +test: example.exe minigzip.exe
           92  +	example
           93  +	echo hello world | minigzip | minigzip -d
           94  +
           95  +clean:
           96  +	-del *.obj
           97  +	-del *.lib
           98  +	-del *.exe
           99  +	-del zlib_*.bak
          100  +	-del foo.gz

Added compat/zlib/nintendods/Makefile.

            1  +#---------------------------------------------------------------------------------
            2  +.SUFFIXES:
            3  +#---------------------------------------------------------------------------------
            4  +
            5  +ifeq ($(strip $(DEVKITARM)),)
            6  +$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
            7  +endif
            8  +
            9  +include $(DEVKITARM)/ds_rules
           10  +
           11  +#---------------------------------------------------------------------------------
           12  +# TARGET is the name of the output
           13  +# BUILD is the directory where object files & intermediate files will be placed
           14  +# SOURCES is a list of directories containing source code
           15  +# DATA is a list of directories containing data files
           16  +# INCLUDES is a list of directories containing header files
           17  +#---------------------------------------------------------------------------------
           18  +TARGET		:=	$(shell basename $(CURDIR))
           19  +BUILD		:=	build
           20  +SOURCES		:=	../../
           21  +DATA		:=	data
           22  +INCLUDES	:=	include
           23  +
           24  +#---------------------------------------------------------------------------------
           25  +# options for code generation
           26  +#---------------------------------------------------------------------------------
           27  +ARCH	:=	-mthumb -mthumb-interwork
           28  +
           29  +CFLAGS	:=	-Wall -O2\
           30  +		-march=armv5te -mtune=arm946e-s \
           31  +		-fomit-frame-pointer -ffast-math \
           32  +		$(ARCH)
           33  +
           34  +CFLAGS	+=	$(INCLUDE) -DARM9
           35  +CXXFLAGS	:= $(CFLAGS) -fno-rtti -fno-exceptions
           36  +
           37  +ASFLAGS	:=	$(ARCH) -march=armv5te -mtune=arm946e-s
           38  +LDFLAGS	=	-specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
           39  +
           40  +#---------------------------------------------------------------------------------
           41  +# list of directories containing libraries, this must be the top level containing
           42  +# include and lib
           43  +#---------------------------------------------------------------------------------
           44  +LIBDIRS	:=	$(LIBNDS)
           45  +
           46  +#---------------------------------------------------------------------------------
           47  +# no real need to edit anything past this point unless you need to add additional
           48  +# rules for different file extensions
           49  +#---------------------------------------------------------------------------------
           50  +ifneq ($(BUILD),$(notdir $(CURDIR)))
           51  +#---------------------------------------------------------------------------------
           52  +
           53  +export OUTPUT	:=	$(CURDIR)/lib/libz.a
           54  +
           55  +export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
           56  +			$(foreach dir,$(DATA),$(CURDIR)/$(dir))
           57  +
           58  +export DEPSDIR	:=	$(CURDIR)/$(BUILD)
           59  +
           60  +CFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
           61  +CPPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
           62  +SFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
           63  +BINFILES	:=	$(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
           64  +
           65  +#---------------------------------------------------------------------------------
           66  +# use CXX for linking C++ projects, CC for standard C
           67  +#---------------------------------------------------------------------------------
           68  +ifeq ($(strip $(CPPFILES)),)
           69  +#---------------------------------------------------------------------------------
           70  +	export LD	:=	$(CC)
           71  +#---------------------------------------------------------------------------------
           72  +else
           73  +#---------------------------------------------------------------------------------
           74  +	export LD	:=	$(CXX)
           75  +#---------------------------------------------------------------------------------
           76  +endif
           77  +#---------------------------------------------------------------------------------
           78  +
           79  +export OFILES	:=	$(addsuffix .o,$(BINFILES)) \
           80  +			$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
           81  +
           82  +export INCLUDE	:=	$(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
           83  +			$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
           84  +			-I$(CURDIR)/$(BUILD)
           85  +
           86  +.PHONY: $(BUILD) clean all
           87  +
           88  +#---------------------------------------------------------------------------------
           89  +all: $(BUILD)
           90  +	@[ -d $@ ] || mkdir -p include
           91  +	@cp ../../*.h include
           92  +
           93  +lib:
           94  +	@[ -d $@ ] || mkdir -p $@
           95  +	
           96  +$(BUILD): lib
           97  +	@[ -d $@ ] || mkdir -p $@
           98  +	@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
           99  +
          100  +#---------------------------------------------------------------------------------
          101  +clean:
          102  +	@echo clean ...
          103  +	@rm -fr $(BUILD) lib
          104  +
          105  +#---------------------------------------------------------------------------------
          106  +else
          107  +
          108  +DEPENDS	:=	$(OFILES:.o=.d)
          109  +
          110  +#---------------------------------------------------------------------------------
          111  +# main targets
          112  +#---------------------------------------------------------------------------------
          113  +$(OUTPUT)	:	$(OFILES)
          114  +
          115  +#---------------------------------------------------------------------------------
          116  +%.bin.o	:	%.bin
          117  +#---------------------------------------------------------------------------------
          118  +	@echo $(notdir $<)
          119  +	@$(bin2o)
          120  +
          121  +
          122  +-include $(DEPENDS)
          123  +
          124  +#---------------------------------------------------------------------------------------
          125  +endif
          126  +#---------------------------------------------------------------------------------------

Added compat/zlib/nintendods/README.

            1  +This Makefile requires devkitARM (http://www.devkitpro.org/category/devkitarm/) and works inside "contrib/nds". It is based on a devkitARM template.
            2  +
            3  +Eduardo Costa <eduardo.m.costa@gmail.com>
            4  +January 3, 2009
            5  +

Added compat/zlib/old/Makefile.emx.

            1  +# Makefile for zlib.  Modified for emx/rsxnt by Chr. Spieler, 6/16/98.
            2  +# Copyright (C) 1995-1998 Jean-loup Gailly.
            3  +# For conditions of distribution and use, see copyright notice in zlib.h
            4  +
            5  +# To compile, or to compile and test, type:
            6  +#
            7  +#   make -fmakefile.emx;  make test -fmakefile.emx
            8  +#
            9  +
           10  +CC=gcc -Zwin32
           11  +
           12  +#CFLAGS=-MMD -O
           13  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
           14  +#CFLAGS=-MMD -g -DDEBUG
           15  +CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
           16  +             -Wstrict-prototypes -Wmissing-prototypes
           17  +
           18  +# If cp.exe is available, replace "copy /Y" with "cp -fp" .
           19  +CP=copy /Y
           20  +# If gnu install.exe is available, replace $(CP) with ginstall.
           21  +INSTALL=$(CP)
           22  +# The default value of RM is "rm -f."  If "rm.exe" is found, comment out:
           23  +RM=del
           24  +LDLIBS=-L. -lzlib
           25  +LD=$(CC) -s -o
           26  +LDSHARED=$(CC)
           27  +
           28  +INCL=zlib.h zconf.h
           29  +LIBS=zlib.a
           30  +
           31  +AR=ar rcs
           32  +
           33  +prefix=/usr/local
           34  +exec_prefix = $(prefix)
           35  +
           36  +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \
           37  +       gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o
           38  +
           39  +TEST_OBJS = example.o minigzip.o
           40  +
           41  +all: example.exe minigzip.exe
           42  +
           43  +test: all
           44  +	./example
           45  +	echo hello world | .\minigzip | .\minigzip -d
           46  +
           47  +%.o : %.c
           48  +	$(CC) $(CFLAGS) -c $< -o $@
           49  +
           50  +zlib.a: $(OBJS)
           51  +	$(AR) $@ $(OBJS)
           52  +
           53  +%.exe : %.o $(LIBS)
           54  +	$(LD) $@ $< $(LDLIBS)
           55  +
           56  +
           57  +.PHONY : clean
           58  +
           59  +clean:
           60  +	$(RM) *.d
           61  +	$(RM) *.o
           62  +	$(RM) *.exe
           63  +	$(RM) zlib.a
           64  +	$(RM) foo.gz
           65  +
           66  +DEPS := $(wildcard *.d)
           67  +ifneq ($(DEPS),)
           68  +include $(DEPS)
           69  +endif

Added compat/zlib/old/Makefile.riscos.

            1  +# Project:   zlib_1_03
            2  +# Patched for zlib 1.1.2 rw@shadow.org.uk 19980430
            3  +# test works out-of-the-box, installs `somewhere' on demand
            4  +
            5  +# Toolflags:
            6  +CCflags = -c -depend !Depend -IC: -g -throwback  -DRISCOS  -fah
            7  +C++flags = -c -depend !Depend -IC: -throwback
            8  +Linkflags = -aif -c++ -o $@
            9  +ObjAsmflags = -throwback -NoCache -depend !Depend
           10  +CMHGflags =
           11  +LibFileflags = -c -l -o $@
           12  +Squeezeflags = -o $@
           13  +
           14  +# change the line below to where _you_ want the library installed.
           15  +libdest = lib:zlib
           16  +
           17  +# Final targets:
           18  +@.lib:   @.o.adler32 @.o.compress @.o.crc32 @.o.deflate @.o.gzio \
           19  +        @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil @.o.trees \
           20  +        @.o.uncompr @.o.zutil
           21  +        LibFile $(LibFileflags) @.o.adler32 @.o.compress @.o.crc32 @.o.deflate \
           22  +        @.o.gzio @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil \
           23  +        @.o.trees @.o.uncompr @.o.zutil
           24  +test:   @.minigzip @.example @.lib
           25  +	@copy @.lib @.libc  A~C~DF~L~N~P~Q~RS~TV
           26  +	@echo running tests: hang on.
           27  +	@/@.minigzip -f -9 libc
           28  +	@/@.minigzip -d libc-gz
           29  +	@/@.minigzip -f -1 libc
           30  +	@/@.minigzip -d libc-gz
           31  +	@/@.minigzip -h -9 libc
           32  +	@/@.minigzip -d libc-gz
           33  +	@/@.minigzip -h -1 libc
           34  +	@/@.minigzip -d libc-gz
           35  +	@/@.minigzip -9 libc
           36  +	@/@.minigzip -d libc-gz
           37  +	@/@.minigzip -1 libc
           38  +	@/@.minigzip -d libc-gz
           39  +	@diff @.lib @.libc
           40  +	@echo that should have reported '@.lib and @.libc identical' if you have diff.
           41  +	@/@.example @.fred @.fred
           42  +	@echo that will have given lots of hello!'s.
           43  +
           44  +@.minigzip:   @.o.minigzip @.lib C:o.Stubs
           45  +        Link $(Linkflags) @.o.minigzip @.lib C:o.Stubs
           46  +@.example:   @.o.example @.lib C:o.Stubs
           47  +        Link $(Linkflags) @.o.example @.lib C:o.Stubs
           48  +
           49  +install: @.lib
           50  +	cdir $(libdest)
           51  +	cdir $(libdest).h
           52  +	@copy @.h.zlib $(libdest).h.zlib A~C~DF~L~N~P~Q~RS~TV
           53  +	@copy @.h.zconf $(libdest).h.zconf A~C~DF~L~N~P~Q~RS~TV
           54  +	@copy @.lib $(libdest).lib  A~C~DF~L~N~P~Q~RS~TV
           55  +	@echo okay, installed zlib in $(libdest)
           56  +
           57  +clean:; remove @.minigzip
           58  +	remove @.example
           59  +	remove @.libc
           60  +	-wipe @.o.* F~r~cV
           61  +	remove @.fred
           62  +
           63  +# User-editable dependencies:
           64  +.c.o:
           65  +        cc $(ccflags) -o $@ $<
           66  +
           67  +# Static dependencies:
           68  +
           69  +# Dynamic dependencies:
           70  +o.example:	c.example
           71  +o.example:	h.zlib
           72  +o.example:	h.zconf
           73  +o.minigzip:	c.minigzip
           74  +o.minigzip:	h.zlib
           75  +o.minigzip:	h.zconf
           76  +o.adler32:	c.adler32
           77  +o.adler32:	h.zlib
           78  +o.adler32:	h.zconf
           79  +o.compress:	c.compress
           80  +o.compress:	h.zlib
           81  +o.compress:	h.zconf
           82  +o.crc32:	c.crc32
           83  +o.crc32:	h.zlib
           84  +o.crc32:	h.zconf
           85  +o.deflate:	c.deflate
           86  +o.deflate:	h.deflate
           87  +o.deflate:	h.zutil
           88  +o.deflate:	h.zlib
           89  +o.deflate:	h.zconf
           90  +o.gzio:	c.gzio
           91  +o.gzio:	h.zutil
           92  +o.gzio:	h.zlib
           93  +o.gzio:	h.zconf
           94  +o.infblock:	c.infblock
           95  +o.infblock:	h.zutil
           96  +o.infblock:	h.zlib
           97  +o.infblock:	h.zconf
           98  +o.infblock:	h.infblock
           99  +o.infblock:	h.inftrees
          100  +o.infblock:	h.infcodes
          101  +o.infblock:	h.infutil
          102  +o.infcodes:	c.infcodes
          103  +o.infcodes:	h.zutil
          104  +o.infcodes:	h.zlib
          105  +o.infcodes:	h.zconf
          106  +o.infcodes:	h.inftrees
          107  +o.infcodes:	h.infblock
          108  +o.infcodes:	h.infcodes
          109  +o.infcodes:	h.infutil
          110  +o.infcodes:	h.inffast
          111  +o.inffast:	c.inffast
          112  +o.inffast:	h.zutil
          113  +o.inffast:	h.zlib
          114  +o.inffast:	h.zconf
          115  +o.inffast:	h.inftrees
          116  +o.inffast:	h.infblock
          117  +o.inffast:	h.infcodes
          118  +o.inffast:	h.infutil
          119  +o.inffast:	h.inffast
          120  +o.inflate:	c.inflate
          121  +o.inflate:	h.zutil
          122  +o.inflate:	h.zlib
          123  +o.inflate:	h.zconf
          124  +o.inflate:	h.infblock
          125  +o.inftrees:	c.inftrees
          126  +o.inftrees:	h.zutil
          127  +o.inftrees:	h.zlib
          128  +o.inftrees:	h.zconf
          129  +o.inftrees:	h.inftrees
          130  +o.inftrees:	h.inffixed
          131  +o.infutil:	c.infutil
          132  +o.infutil:	h.zutil
          133  +o.infutil:	h.zlib
          134  +o.infutil:	h.zconf
          135  +o.infutil:	h.infblock
          136  +o.infutil:	h.inftrees
          137  +o.infutil:	h.infcodes
          138  +o.infutil:	h.infutil
          139  +o.trees:	c.trees
          140  +o.trees:	h.deflate
          141  +o.trees:	h.zutil
          142  +o.trees:	h.zlib
          143  +o.trees:	h.zconf
          144  +o.trees:	h.trees
          145  +o.uncompr:	c.uncompr
          146  +o.uncompr:	h.zlib
          147  +o.uncompr:	h.zconf
          148  +o.zutil:	c.zutil
          149  +o.zutil:	h.zutil
          150  +o.zutil:	h.zlib
          151  +o.zutil:	h.zconf

Added compat/zlib/old/README.

            1  +This directory contains files that have not been updated for zlib 1.2.x
            2  +
            3  +(Volunteers are encouraged to help clean this up.  Thanks.)

Added compat/zlib/old/descrip.mms.

            1  +# descrip.mms: MMS description file for building zlib on VMS
            2  +# written by Martin P.J. Zinser <m.zinser@gsi.de>
            3  +
            4  +cc_defs =
            5  +c_deb =
            6  +
            7  +.ifdef __DECC__
            8  +pref = /prefix=all
            9  +.endif
           10  +
           11  +OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\
           12  +       deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\
           13  +       inftrees.obj, infcodes.obj, infutil.obj, inffast.obj
           14  +
           15  +CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF)
           16  +
           17  +all : example.exe minigzip.exe
           18  +        @ write sys$output " Example applications available"
           19  +libz.olb : libz.olb($(OBJS))
           20  +	@ write sys$output " libz available"
           21  +
           22  +example.exe : example.obj libz.olb
           23  +              link example,libz.olb/lib
           24  +
           25  +minigzip.exe : minigzip.obj libz.olb
           26  +              link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib
           27  +
           28  +clean :
           29  +	delete *.obj;*,libz.olb;*
           30  +
           31  +
           32  +# Other dependencies.
           33  +adler32.obj : zutil.h zlib.h zconf.h
           34  +compress.obj : zlib.h zconf.h
           35  +crc32.obj : zutil.h zlib.h zconf.h
           36  +deflate.obj : deflate.h zutil.h zlib.h zconf.h
           37  +example.obj : zlib.h zconf.h
           38  +gzio.obj : zutil.h zlib.h zconf.h
           39  +infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
           40  +infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h
           41  +inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h
           42  +inflate.obj : zutil.h zlib.h zconf.h infblock.h
           43  +inftrees.obj : zutil.h zlib.h zconf.h inftrees.h
           44  +infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h
           45  +minigzip.obj : zlib.h zconf.h
           46  +trees.obj : deflate.h zutil.h zlib.h zconf.h
           47  +uncompr.obj : zlib.h zconf.h
           48  +zutil.obj : zutil.h zlib.h zconf.h

Added compat/zlib/old/os2/Makefile.os2.

            1  +# Makefile for zlib under OS/2 using GCC (PGCC)
            2  +# For conditions of distribution and use, see copyright notice in zlib.h
            3  +
            4  +# To compile and test, type:
            5  +#   cp Makefile.os2 ..
            6  +#   cd ..
            7  +#   make -f Makefile.os2 test
            8  +
            9  +# This makefile will build a static library z.lib, a shared library
           10  +# z.dll and a import library zdll.lib. You can use either z.lib or
           11  +# zdll.lib by specifying either -lz or -lzdll on gcc's command line
           12  +
           13  +CC=gcc -Zomf -s
           14  +
           15  +CFLAGS=-O6 -Wall
           16  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
           17  +#CFLAGS=-g -DDEBUG
           18  +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
           19  +#           -Wstrict-prototypes -Wmissing-prototypes
           20  +
           21  +#################### BUG WARNING: #####################
           22  +## infcodes.c hits a bug in pgcc-1.0, so you have to use either
           23  +## -O# where # <= 4 or one of (-fno-ommit-frame-pointer or -fno-force-mem)
           24  +## This bug is reportedly fixed in pgcc >1.0, but this was not tested
           25  +CFLAGS+=-fno-force-mem
           26  +
           27  +LDFLAGS=-s -L. -lzdll -Zcrtdll
           28  +LDSHARED=$(CC) -s -Zomf -Zdll -Zcrtdll
           29  +
           30  +VER=1.1.0
           31  +ZLIB=z.lib
           32  +SHAREDLIB=z.dll
           33  +SHAREDLIBIMP=zdll.lib
           34  +LIBS=$(ZLIB) $(SHAREDLIB) $(SHAREDLIBIMP)
           35  +
           36  +AR=emxomfar cr
           37  +IMPLIB=emximp
           38  +RANLIB=echo
           39  +TAR=tar
           40  +SHELL=bash
           41  +
           42  +prefix=/usr/local
           43  +exec_prefix = $(prefix)
           44  +
           45  +OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
           46  +       zutil.o inflate.o infblock.o inftrees.o infcodes.o infutil.o inffast.o
           47  +
           48  +TEST_OBJS = example.o minigzip.o
           49  +
           50  +DISTFILES = README INDEX ChangeLog configure Make*[a-z0-9] *.[ch] descrip.mms \
           51  +  algorithm.txt zlib.3 msdos/Make*[a-z0-9] msdos/zlib.def msdos/zlib.rc \
           52  +  nt/Makefile.nt nt/zlib.dnt  contrib/README.contrib contrib/*.txt \
           53  +  contrib/asm386/*.asm contrib/asm386/*.c \
           54  +  contrib/asm386/*.bat contrib/asm386/zlibvc.d?? contrib/iostream/*.cpp \
           55  +  contrib/iostream/*.h  contrib/iostream2/*.h contrib/iostream2/*.cpp \
           56  +  contrib/untgz/Makefile contrib/untgz/*.c contrib/untgz/*.w32
           57  +
           58  +all: example.exe minigzip.exe
           59  +
           60  +test: all
           61  +	@LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
           62  +	echo hello world | ./minigzip | ./minigzip -d || \
           63  +	  echo '		*** minigzip test FAILED ***' ; \
           64  +	if ./example; then \
           65  +	  echo '		*** zlib test OK ***'; \
           66  +	else \
           67  +	  echo '		*** zlib test FAILED ***'; \
           68  +	fi
           69  +
           70  +$(ZLIB): $(OBJS)
           71  +	$(AR) $@ $(OBJS)
           72  +	-@ ($(RANLIB) $@ || true) >/dev/null 2>&1
           73  +
           74  +$(SHAREDLIB): $(OBJS) os2/z.def
           75  +	$(LDSHARED) -o $@ $^
           76  +
           77  +$(SHAREDLIBIMP): os2/z.def
           78  +	$(IMPLIB) -o $@ $^
           79  +
           80  +example.exe: example.o $(LIBS)
           81  +	$(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
           82  +
           83  +minigzip.exe: minigzip.o $(LIBS)
           84  +	$(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
           85  +
           86  +clean:
           87  +	rm -f *.o *~ example minigzip libz.a libz.so* foo.gz
           88  +
           89  +distclean:	clean
           90  +
           91  +zip:
           92  +	mv Makefile Makefile~; cp -p Makefile.in Makefile
           93  +	rm -f test.c ztest*.c
           94  +	v=`sed -n -e 's/\.//g' -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
           95  +	zip -ul9 zlib$$v $(DISTFILES)
           96  +	mv Makefile~ Makefile
           97  +
           98  +dist:
           99  +	mv Makefile Makefile~; cp -p Makefile.in Makefile
          100  +	rm -f test.c ztest*.c
          101  +	d=zlib-`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\
          102  +	rm -f $$d.tar.gz; \
          103  +	if test ! -d ../$$d; then rm -f ../$$d; ln -s `pwd` ../$$d; fi; \
          104  +	files=""; \
          105  +	for f in $(DISTFILES); do files="$$files $$d/$$f"; done; \
          106  +	cd ..; \
          107  +	GZIP=-9 $(TAR) chofz $$d/$$d.tar.gz $$files; \
          108  +	if test ! -d $$d; then rm -f $$d; fi
          109  +	mv Makefile~ Makefile
          110  +
          111  +tags:
          112  +	etags *.[ch]
          113  +
          114  +depend:
          115  +	makedepend -- $(CFLAGS) -- *.[ch]
          116  +
          117  +# DO NOT DELETE THIS LINE -- make depend depends on it.
          118  +
          119  +adler32.o: zlib.h zconf.h
          120  +compress.o: zlib.h zconf.h
          121  +crc32.o: zlib.h zconf.h
          122  +deflate.o: deflate.h zutil.h zlib.h zconf.h
          123  +example.o: zlib.h zconf.h
          124  +gzio.o: zutil.h zlib.h zconf.h
          125  +infblock.o: infblock.h inftrees.h infcodes.h infutil.h zutil.h zlib.h zconf.h
          126  +infcodes.o: zutil.h zlib.h zconf.h
          127  +infcodes.o: inftrees.h infblock.h infcodes.h infutil.h inffast.h
          128  +inffast.o: zutil.h zlib.h zconf.h inftrees.h
          129  +inffast.o: infblock.h infcodes.h infutil.h inffast.h
          130  +inflate.o: zutil.h zlib.h zconf.h infblock.h
          131  +inftrees.o: zutil.h zlib.h zconf.h inftrees.h
          132  +infutil.o: zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h
          133  +minigzip.o: zlib.h zconf.h
          134  +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
          135  +uncompr.o: zlib.h zconf.h
          136  +zutil.o: zutil.h zlib.h zconf.h

Added compat/zlib/old/os2/zlib.def.

            1  +;
            2  +; Slightly modified version of ../nt/zlib.dnt :-)
            3  +;
            4  +
            5  +LIBRARY		Z
            6  +DESCRIPTION	"Zlib compression library for OS/2"
            7  +CODE		PRELOAD MOVEABLE DISCARDABLE
            8  +DATA		PRELOAD MOVEABLE MULTIPLE
            9  +
           10  +EXPORTS
           11  +    adler32
           12  +    compress
           13  +    crc32
           14  +    deflate
           15  +    deflateCopy
           16  +    deflateEnd
           17  +    deflateInit2_
           18  +    deflateInit_
           19  +    deflateParams
           20  +    deflateReset
           21  +    deflateSetDictionary
           22  +    gzclose
           23  +    gzdopen
           24  +    gzerror
           25  +    gzflush
           26  +    gzopen
           27  +    gzread
           28  +    gzwrite
           29  +    inflate
           30  +    inflateEnd
           31  +    inflateInit2_
           32  +    inflateInit_
           33  +    inflateReset
           34  +    inflateSetDictionary
           35  +    inflateSync
           36  +    uncompress
           37  +    zlibVersion
           38  +    gzprintf
           39  +    gzputc
           40  +    gzgetc
           41  +    gzseek
           42  +    gzrewind
           43  +    gztell
           44  +    gzeof
           45  +    gzsetparams
           46  +    zError
           47  +    inflateSyncPoint
           48  +    get_crc_table
           49  +    compress2
           50  +    gzputs
           51  +    gzgets

Added compat/zlib/old/visual-basic.txt.

            1  +See below some functions declarations for Visual Basic.
            2  +
            3  +Frequently Asked Question:
            4  +
            5  +Q: Each time I use the compress function I get the -5 error (not enough
            6  +   room in the output buffer).
            7  +
            8  +A: Make sure that the length of the compressed buffer is passed by
            9  +   reference ("as any"), not by value ("as long"). Also check that
           10  +   before the call of compress this length is equal to the total size of
           11  +   the compressed buffer and not zero.
           12  +
           13  +
           14  +From: "Jon Caruana" <jon-net@usa.net>
           15  +Subject: Re: How to port zlib declares to vb?
           16  +Date: Mon, 28 Oct 1996 18:33:03 -0600
           17  +
           18  +Got the answer! (I haven't had time to check this but it's what I got, and
           19  +looks correct):
           20  +
           21  +He has the following routines working:
           22  +        compress
           23  +        uncompress
           24  +        gzopen
           25  +        gzwrite
           26  +        gzread
           27  +        gzclose
           28  +
           29  +Declares follow: (Quoted from Carlos Rios <c_rios@sonda.cl>, in Vb4 form)
           30  +
           31  +#If Win16 Then   'Use Win16 calls.
           32  +Declare Function compress Lib "ZLIB.DLL" (ByVal compr As
           33  +        String, comprLen As Any, ByVal buf As String, ByVal buflen
           34  +        As Long) As Integer
           35  +Declare Function uncompress Lib "ZLIB.DLL" (ByVal uncompr
           36  +        As String, uncomprLen As Any, ByVal compr As String, ByVal
           37  +        lcompr As Long) As Integer
           38  +Declare Function gzopen Lib "ZLIB.DLL" (ByVal filePath As
           39  +        String, ByVal mode As String) As Long
           40  +Declare Function gzread Lib "ZLIB.DLL" (ByVal file As
           41  +        Long, ByVal uncompr As String, ByVal uncomprLen As Integer)
           42  +        As Integer
           43  +Declare Function gzwrite Lib "ZLIB.DLL" (ByVal file As
           44  +        Long, ByVal uncompr As String, ByVal uncomprLen As Integer)
           45  +        As Integer
           46  +Declare Function gzclose Lib "ZLIB.DLL" (ByVal file As
           47  +        Long) As Integer
           48  +#Else
           49  +Declare Function compress Lib "ZLIB32.DLL"
           50  +        (ByVal compr As String, comprLen As Any, ByVal buf As
           51  +        String, ByVal buflen As Long) As Integer
           52  +Declare Function uncompress Lib "ZLIB32.DLL"
           53  +        (ByVal uncompr As String, uncomprLen As Any, ByVal compr As
           54  +        String, ByVal lcompr As Long) As Long
           55  +Declare Function gzopen Lib "ZLIB32.DLL"
           56  +        (ByVal file As String, ByVal mode As String) As Long
           57  +Declare Function gzread Lib "ZLIB32.DLL"
           58  +        (ByVal file As Long, ByVal uncompr As String, ByVal
           59  +        uncomprLen As Long) As Long
           60  +Declare Function gzwrite Lib "ZLIB32.DLL"
           61  +        (ByVal file As Long, ByVal uncompr As String, ByVal
           62  +        uncomprLen As Long) As Long
           63  +Declare Function gzclose Lib "ZLIB32.DLL"
           64  +        (ByVal file As Long) As Long
           65  +#End If
           66  +
           67  +-Jon Caruana
           68  +jon-net@usa.net
           69  +Microsoft Sitebuilder Network Level 1 Member - HTML Writer's Guild Member
           70  +
           71  +
           72  +Here is another example from Michael <michael_borgsys@hotmail.com> that he
           73  +says conforms to the VB guidelines, and that solves the problem of not
           74  +knowing the uncompressed size by storing it at the end of the file:
           75  +
           76  +'Calling the functions:
           77  +'bracket meaning: <parameter> [optional] {Range of possible values}
           78  +'Call subCompressFile(<path with filename to compress> [, <path with
           79  +filename to write to>, [level of compression {1..9}]])
           80  +'Call subUncompressFile(<path with filename to compress>)
           81  +
           82  +Option Explicit
           83  +Private lngpvtPcnSml As Long 'Stores value for 'lngPercentSmaller'
           84  +Private Const SUCCESS As Long = 0
           85  +Private Const strFilExt As String = ".cpr"
           86  +Private Declare Function lngfncCpr Lib "zlib.dll" Alias "compress2" (ByRef
           87  +dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long,
           88  +ByVal level As Integer) As Long
           89  +Private Declare Function lngfncUcp Lib "zlib.dll" Alias "uncompress" (ByRef
           90  +dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long)
           91  +As Long
           92  +
           93  +Public Sub subCompressFile(ByVal strargOriFilPth As String, Optional ByVal
           94  +strargCprFilPth As String, Optional ByVal intLvl As Integer = 9)
           95  +    Dim strCprPth As String
           96  +    Dim lngOriSiz As Long
           97  +    Dim lngCprSiz As Long
           98  +    Dim bytaryOri() As Byte
           99  +    Dim bytaryCpr() As Byte
          100  +    lngOriSiz = FileLen(strargOriFilPth)
          101  +    ReDim bytaryOri(lngOriSiz - 1)
          102  +    Open strargOriFilPth For Binary Access Read As #1
          103  +        Get #1, , bytaryOri()
          104  +    Close #1
          105  +    strCprPth = IIf(strargCprFilPth = "", strargOriFilPth, strargCprFilPth)
          106  +'Select file path and name
          107  +    strCprPth = strCprPth & IIf(Right(strCprPth, Len(strFilExt)) =
          108  +strFilExt, "", strFilExt) 'Add file extension if not exists
          109  +    lngCprSiz = (lngOriSiz * 1.01) + 12 'Compression needs temporary a bit
          110  +more space then original file size
          111  +    ReDim bytaryCpr(lngCprSiz - 1)
          112  +    If lngfncCpr(bytaryCpr(0), lngCprSiz, bytaryOri(0), lngOriSiz, intLvl) =
          113  +SUCCESS Then
          114  +        lngpvtPcnSml = (1# - (lngCprSiz / lngOriSiz)) * 100
          115  +        ReDim Preserve bytaryCpr(lngCprSiz - 1)
          116  +        Open strCprPth For Binary Access Write As #1
          117  +            Put #1, , bytaryCpr()
          118  +            Put #1, , lngOriSiz 'Add the the original size value to the end
          119  +(last 4 bytes)
          120  +        Close #1
          121  +    Else
          122  +        MsgBox "Compression error"
          123  +    End If
          124  +    Erase bytaryCpr
          125  +    Erase bytaryOri
          126  +End Sub
          127  +
          128  +Public Sub subUncompressFile(ByVal strargFilPth As String)
          129  +    Dim bytaryCpr() As Byte
          130  +    Dim bytaryOri() As Byte
          131  +    Dim lngOriSiz As Long
          132  +    Dim lngCprSiz As Long
          133  +    Dim strOriPth As String
          134  +    lngCprSiz = FileLen(strargFilPth)
          135  +    ReDim bytaryCpr(lngCprSiz - 1)
          136  +    Open strargFilPth For Binary Access Read As #1
          137  +        Get #1, , bytaryCpr()
          138  +    Close #1
          139  +    'Read the original file size value:
          140  +    lngOriSiz = bytaryCpr(lngCprSiz - 1) * (2 ^ 24) _
          141  +              + bytaryCpr(lngCprSiz - 2) * (2 ^ 16) _
          142  +              + bytaryCpr(lngCprSiz - 3) * (2 ^ 8) _
          143  +              + bytaryCpr(lngCprSiz - 4)
          144  +    ReDim Preserve bytaryCpr(lngCprSiz - 5) 'Cut of the original size value
          145  +    ReDim bytaryOri(lngOriSiz - 1)
          146  +    If lngfncUcp(bytaryOri(0), lngOriSiz, bytaryCpr(0), lngCprSiz) = SUCCESS
          147  +Then
          148  +        strOriPth = Left(strargFilPth, Len(strargFilPth) - Len(strFilExt))
          149  +        Open strOriPth For Binary Access Write As #1
          150  +            Put #1, , bytaryOri()
          151  +        Close #1
          152  +    Else
          153  +        MsgBox "Uncompression error"
          154  +    End If
          155  +    Erase bytaryCpr
          156  +    Erase bytaryOri
          157  +End Sub
          158  +Public Property Get lngPercentSmaller() As Long
          159  +    lngPercentSmaller = lngpvtPcnSml
          160  +End Property

Added compat/zlib/qnx/package.qpg.

            1  +<QPG:Generation>
            2  +   <QPG:Options>
            3  +      <QPG:User unattended="no" verbosity="2" listfiles="yes"/>
            4  +      <QPG:Defaults type="qnx_package"/>
            5  +      <QPG:Source></QPG:Source>
            6  +      <QPG:Release number="+"/>
            7  +      <QPG:Build></QPG:Build>
            8  +      <QPG:FileSorting strip="yes"/>
            9  +      <QPG:Package targets="combine"/>
           10  +      <QPG:Repository generate="yes"/>
           11  +      <QPG:FinalDir></QPG:FinalDir>
           12  +      <QPG:Cleanup></QPG:Cleanup>
           13  +   </QPG:Options>
           14  +
           15  +   <QPG:Responsible>
           16  +      <QPG:Company></QPG:Company>
           17  +      <QPG:Department></QPG:Department>
           18  +      <QPG:Group></QPG:Group>
           19  +      <QPG:Team></QPG:Team>
           20  +      <QPG:Employee></QPG:Employee>
           21  +      <QPG:EmailAddress></QPG:EmailAddress>
           22  +   </QPG:Responsible>
           23  +
           24  +   <QPG:Values>
           25  +      <QPG:Files>
           26  +         <QPG:Add file="../zconf.h" install="/opt/include/" user="root:sys" permission="644"/>
           27  +         <QPG:Add file="../zlib.h" install="/opt/include/" user="root:sys" permission="644"/>
           28  +         <QPG:Add file="../libz.so.1.2.7" install="/opt/lib/" user="root:bin" permission="644"/>
           29  +         <QPG:Add file="libz.so" install="/opt/lib/" component="dev" filetype="symlink" linkto="libz.so.1.2.7"/>
           30  +         <QPG:Add file="libz.so.1" install="/opt/lib/" filetype="symlink" linkto="libz.so.1.2.7"/>
           31  +         <QPG:Add file="../libz.so.1.2.7" install="/opt/lib/" component="slib"/>
           32  +      </QPG:Files>
           33  +
           34  +      <QPG:PackageFilter>
           35  +         <QPM:PackageManifest>
           36  +            <QPM:PackageDescription>
           37  +               <QPM:PackageType>Library</QPM:PackageType>
           38  +               <QPM:PackageReleaseNotes></QPM:PackageReleaseNotes>
           39  +               <QPM:PackageReleaseUrgency>Medium</QPM:PackageReleaseUrgency>
           40  +               <QPM:PackageRepository></QPM:PackageRepository>
           41  +               <QPM:FileVersion>2.0</QPM:FileVersion>
           42  +            </QPM:PackageDescription>
           43  +
           44  +            <QPM:ProductDescription>
           45  +               <QPM:ProductName>zlib</QPM:ProductName>
           46  +               <QPM:ProductIdentifier>zlib</QPM:ProductIdentifier>
           47  +               <QPM:ProductEmail>alain.bonnefoy@icbt.com</QPM:ProductEmail>
           48  +               <QPM:VendorName>Public</QPM:VendorName>
           49  +               <QPM:VendorInstallName>public</QPM:VendorInstallName>
           50  +               <QPM:VendorURL>www.gzip.org/zlib</QPM:VendorURL>
           51  +               <QPM:VendorEmbedURL></QPM:VendorEmbedURL>
           52  +               <QPM:VendorEmail></QPM:VendorEmail>
           53  +               <QPM:AuthorName>Jean-Loup Gailly,Mark Adler</QPM:AuthorName>
           54  +               <QPM:AuthorURL>www.gzip.org/zlib</QPM:AuthorURL>
           55  +               <QPM:AuthorEmbedURL></QPM:AuthorEmbedURL>
           56  +               <QPM:AuthorEmail>zlib@gzip.org</QPM:AuthorEmail>
           57  +               <QPM:ProductIconSmall></QPM:ProductIconSmall>
           58  +               <QPM:ProductIconLarge></QPM:ProductIconLarge>
           59  +               <QPM:ProductDescriptionShort>A massively spiffy yet delicately unobtrusive compression library.</QPM:ProductDescriptionShort>
           60  +               <QPM:ProductDescriptionLong>zlib is designed to be a free, general-purpose, legally unencumbered, lossless data compression library for use on virtually any computer hardware and operating system.</QPM:ProductDescriptionLong>
           61  +               <QPM:ProductDescriptionURL>http://www.gzip.org/zlib</QPM:ProductDescriptionURL>
           62  +               <QPM:ProductDescriptionEmbedURL></QPM:ProductDescriptionEmbedURL>
           63  +            </QPM:ProductDescription>
           64  +
           65  +            <QPM:ReleaseDescription>
           66  +               <QPM:ReleaseVersion>1.2.7</QPM:ReleaseVersion>
           67  +               <QPM:ReleaseUrgency>Medium</QPM:ReleaseUrgency>
           68  +               <QPM:ReleaseStability>Stable</QPM:ReleaseStability>
           69  +               <QPM:ReleaseNoteMinor></QPM:ReleaseNoteMinor>
           70  +               <QPM:ReleaseNoteMajor></QPM:ReleaseNoteMajor>
           71  +               <QPM:ExcludeCountries>
           72  +                  <QPM:Country></QPM:Country>
           73  +               </QPM:ExcludeCountries>
           74  +
           75  +               <QPM:ReleaseCopyright>No License</QPM:ReleaseCopyright>
           76  +            </QPM:ReleaseDescription>
           77  +
           78  +            <QPM:ContentDescription>
           79  +               <QPM:ContentTopic xmlmultiple="true">Software Development/Libraries and Extensions/C Libraries</QPM:ContentTopic>
           80  +               <QPM:ContentKeyword>zlib,compression</QPM:ContentKeyword>
           81  +               <QPM:TargetOS>qnx6</QPM:TargetOS>
           82  +               <QPM:HostOS>qnx6</QPM:HostOS>
           83  +               <QPM:DisplayEnvironment xmlmultiple="true">None</QPM:DisplayEnvironment>
           84  +               <QPM:TargetAudience xmlmultiple="true">Developer</QPM:TargetAudience>
           85  +            </QPM:ContentDescription>
           86  +         </QPM:PackageManifest>
           87  +      </QPG:PackageFilter>
           88  +
           89  +      <QPG:PackageFilter proc="none" target="none">
           90  +         <QPM:PackageManifest>
           91  +            <QPM:ProductInstallationDependencies>
           92  +               <QPM:ProductRequirements></QPM:ProductRequirements>
           93  +            </QPM:ProductInstallationDependencies>
           94  +
           95  +            <QPM:ProductInstallationProcedure>
           96  +               <QPM:Script xmlmultiple="true">
           97  +                  <QPM:ScriptName></QPM:ScriptName>
           98  +                  <QPM:ScriptType>Install</QPM:ScriptType>
           99  +                  <QPM:ScriptTiming>Post</QPM:ScriptTiming>
          100  +                  <QPM:ScriptBlocking>No</QPM:ScriptBlocking>
          101  +                  <QPM:ScriptResult>Ignore</QPM:ScriptResult>
          102  +                  <QPM:ShortDescription></QPM:ShortDescription>
          103  +                  <QPM:UseBinaries>No</QPM:UseBinaries>
          104  +                  <QPM:Priority>Optional</QPM:Priority>
          105  +               </QPM:Script>
          106  +            </QPM:ProductInstallationProcedure>
          107  +         </QPM:PackageManifest>
          108  +
          109  +         <QPM:Launch>
          110  +         </QPM:Launch>
          111  +      </QPG:PackageFilter>
          112  +
          113  +      <QPG:PackageFilter type="core" component="none">
          114  +         <QPM:PackageManifest>
          115  +            <QPM:ProductInstallationProcedure>
          116  +	       <QPM:OrderDependency xmlmultiple="true">
          117  +	          <QPM:Order>InstallOver</QPM:Order>
          118  +	          <QPM:Product>zlib</QPM:Product>
          119  +	       </QPM:OrderDependency>
          120  +            </QPM:ProductInstallationProcedure>
          121  +         </QPM:PackageManifest>
          122  +
          123  +         <QPM:Launch>
          124  +         </QPM:Launch>
          125  +      </QPG:PackageFilter>
          126  +
          127  +      <QPG:PackageFilter type="core" component="dev">
          128  +         <QPM:PackageManifest>
          129  +            <QPM:ProductInstallationProcedure>
          130  +	       <QPM:OrderDependency xmlmultiple="true">
          131  +	          <QPM:Order>InstallOver</QPM:Order>
          132  +	          <QPM:Product>zlib-dev</QPM:Product>
          133  +	       </QPM:OrderDependency>
          134  +            </QPM:ProductInstallationProcedure>
          135  +         </QPM:PackageManifest>
          136  +
          137  +         <QPM:Launch>
          138  +         </QPM:Launch>
          139  +      </QPG:PackageFilter>
          140  +   </QPG:Values>
          141  +</QPG:Generation>

Added compat/zlib/test/example.c.

            1  +/* example.c -- usage example of the zlib compression library
            2  + * Copyright (C) 1995-2006, 2011 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#include "zlib.h"
            9  +#include <stdio.h>
           10  +
           11  +#ifdef STDC
           12  +#  include <string.h>
           13  +#  include <stdlib.h>
           14  +#endif
           15  +
           16  +#if defined(VMS) || defined(RISCOS)
           17  +#  define TESTFILE "foo-gz"
           18  +#else
           19  +#  define TESTFILE "foo.gz"
           20  +#endif
           21  +
           22  +#define CHECK_ERR(err, msg) { \
           23  +    if (err != Z_OK) { \
           24  +        fprintf(stderr, "%s error: %d\n", msg, err); \
           25  +        exit(1); \
           26  +    } \
           27  +}
           28  +
           29  +const char hello[] = "hello, hello!";
           30  +/* "hello world" would be more standard, but the repeated "hello"
           31  + * stresses the compression code better, sorry...
           32  + */
           33  +
           34  +const char dictionary[] = "hello";
           35  +uLong dictId; /* Adler32 value of the dictionary */
           36  +
           37  +void test_deflate       OF((Byte *compr, uLong comprLen));
           38  +void test_inflate       OF((Byte *compr, uLong comprLen,
           39  +                            Byte *uncompr, uLong uncomprLen));
           40  +void test_large_deflate OF((Byte *compr, uLong comprLen,
           41  +                            Byte *uncompr, uLong uncomprLen));
           42  +void test_large_inflate OF((Byte *compr, uLong comprLen,
           43  +                            Byte *uncompr, uLong uncomprLen));
           44  +void test_flush         OF((Byte *compr, uLong *comprLen));
           45  +void test_sync          OF((Byte *compr, uLong comprLen,
           46  +                            Byte *uncompr, uLong uncomprLen));
           47  +void test_dict_deflate  OF((Byte *compr, uLong comprLen));
           48  +void test_dict_inflate  OF((Byte *compr, uLong comprLen,
           49  +                            Byte *uncompr, uLong uncomprLen));
           50  +int  main               OF((int argc, char *argv[]));
           51  +
           52  +
           53  +#ifdef Z_SOLO
           54  +
           55  +void *myalloc OF((void *, unsigned, unsigned));
           56  +void myfree OF((void *, void *));
           57  +
           58  +void *myalloc(q, n, m)
           59  +    void *q;
           60  +    unsigned n, m;
           61  +{
           62  +    q = Z_NULL;
           63  +    return calloc(n, m);
           64  +}
           65  +
           66  +void myfree(void *q, void *p)
           67  +{
           68  +    q = Z_NULL;
           69  +    free(p);
           70  +}
           71  +
           72  +static alloc_func zalloc = myalloc;
           73  +static free_func zfree = myfree;
           74  +
           75  +#else /* !Z_SOLO */
           76  +
           77  +static alloc_func zalloc = (alloc_func)0;
           78  +static free_func zfree = (free_func)0;
           79  +
           80  +void test_compress      OF((Byte *compr, uLong comprLen,
           81  +                            Byte *uncompr, uLong uncomprLen));
           82  +void test_gzio          OF((const char *fname,
           83  +                            Byte *uncompr, uLong uncomprLen));
           84  +
           85  +/* ===========================================================================
           86  + * Test compress() and uncompress()
           87  + */
           88  +void test_compress(compr, comprLen, uncompr, uncomprLen)
           89  +    Byte *compr, *uncompr;
           90  +    uLong comprLen, uncomprLen;
           91  +{
           92  +    int err;
           93  +    uLong len = (uLong)strlen(hello)+1;
           94  +
           95  +    err = compress(compr, &comprLen, (const Bytef*)hello, len);
           96  +    CHECK_ERR(err, "compress");
           97  +
           98  +    strcpy((char*)uncompr, "garbage");
           99  +
          100  +    err = uncompress(uncompr, &uncomprLen, compr, comprLen);
          101  +    CHECK_ERR(err, "uncompress");
          102  +
          103  +    if (strcmp((char*)uncompr, hello)) {
          104  +        fprintf(stderr, "bad uncompress\n");
          105  +        exit(1);
          106  +    } else {
          107  +        printf("uncompress(): %s\n", (char *)uncompr);
          108  +    }
          109  +}
          110  +
          111  +/* ===========================================================================
          112  + * Test read/write of .gz files
          113  + */
          114  +void test_gzio(fname, uncompr, uncomprLen)
          115  +    const char *fname; /* compressed file name */
          116  +    Byte *uncompr;
          117  +    uLong uncomprLen;
          118  +{
          119  +#ifdef NO_GZCOMPRESS
          120  +    fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
          121  +#else
          122  +    int err;
          123  +    int len = (int)strlen(hello)+1;
          124  +    gzFile file;
          125  +    z_off_t pos;
          126  +
          127  +    file = gzopen(fname, "wb");
          128  +    if (file == NULL) {
          129  +        fprintf(stderr, "gzopen error\n");
          130  +        exit(1);
          131  +    }
          132  +    gzputc(file, 'h');
          133  +    if (gzputs(file, "ello") != 4) {
          134  +        fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
          135  +        exit(1);
          136  +    }
          137  +    if (gzprintf(file, ", %s!", "hello") != 8) {
          138  +        fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
          139  +        exit(1);
          140  +    }
          141  +    gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
          142  +    gzclose(file);
          143  +
          144  +    file = gzopen(fname, "rb");
          145  +    if (file == NULL) {
          146  +        fprintf(stderr, "gzopen error\n");
          147  +        exit(1);
          148  +    }
          149  +    strcpy((char*)uncompr, "garbage");
          150  +
          151  +    if (gzread(file, uncompr, (unsigned)uncomprLen) != len) {
          152  +        fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
          153  +        exit(1);
          154  +    }
          155  +    if (strcmp((char*)uncompr, hello)) {
          156  +        fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
          157  +        exit(1);
          158  +    } else {
          159  +        printf("gzread(): %s\n", (char*)uncompr);
          160  +    }
          161  +
          162  +    pos = gzseek(file, -8L, SEEK_CUR);
          163  +    if (pos != 6 || gztell(file) != pos) {
          164  +        fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
          165  +                (long)pos, (long)gztell(file));
          166  +        exit(1);
          167  +    }
          168  +
          169  +    if (gzgetc(file) != ' ') {
          170  +        fprintf(stderr, "gzgetc error\n");
          171  +        exit(1);
          172  +    }
          173  +
          174  +    if (gzungetc(' ', file) != ' ') {
          175  +        fprintf(stderr, "gzungetc error\n");
          176  +        exit(1);
          177  +    }
          178  +
          179  +    gzgets(file, (char*)uncompr, (int)uncomprLen);
          180  +    if (strlen((char*)uncompr) != 7) { /* " hello!" */
          181  +        fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
          182  +        exit(1);
          183  +    }
          184  +    if (strcmp((char*)uncompr, hello + 6)) {
          185  +        fprintf(stderr, "bad gzgets after gzseek\n");
          186  +        exit(1);
          187  +    } else {
          188  +        printf("gzgets() after gzseek: %s\n", (char*)uncompr);
          189  +    }
          190  +
          191  +    gzclose(file);
          192  +#endif
          193  +}
          194  +
          195  +#endif /* Z_SOLO */
          196  +
          197  +/* ===========================================================================
          198  + * Test deflate() with small buffers
          199  + */
          200  +void test_deflate(compr, comprLen)
          201  +    Byte *compr;
          202  +    uLong comprLen;
          203  +{
          204  +    z_stream c_stream; /* compression stream */
          205  +    int err;
          206  +    uLong len = (uLong)strlen(hello)+1;
          207  +
          208  +    c_stream.zalloc = zalloc;
          209  +    c_stream.zfree = zfree;
          210  +    c_stream.opaque = (voidpf)0;
          211  +
          212  +    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
          213  +    CHECK_ERR(err, "deflateInit");
          214  +
          215  +    c_stream.next_in  = (Bytef*)hello;
          216  +    c_stream.next_out = compr;
          217  +
          218  +    while (c_stream.total_in != len && c_stream.total_out < comprLen) {
          219  +        c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
          220  +        err = deflate(&c_stream, Z_NO_FLUSH);
          221  +        CHECK_ERR(err, "deflate");
          222  +    }
          223  +    /* Finish the stream, still forcing small buffers: */
          224  +    for (;;) {
          225  +        c_stream.avail_out = 1;
          226  +        err = deflate(&c_stream, Z_FINISH);
          227  +        if (err == Z_STREAM_END) break;
          228  +        CHECK_ERR(err, "deflate");
          229  +    }
          230  +
          231  +    err = deflateEnd(&c_stream);
          232  +    CHECK_ERR(err, "deflateEnd");
          233  +}
          234  +
          235  +/* ===========================================================================
          236  + * Test inflate() with small buffers
          237  + */
          238  +void test_inflate(compr, comprLen, uncompr, uncomprLen)
          239  +    Byte *compr, *uncompr;
          240  +    uLong comprLen, uncomprLen;
          241  +{
          242  +    int err;
          243  +    z_stream d_stream; /* decompression stream */
          244  +
          245  +    strcpy((char*)uncompr, "garbage");
          246  +
          247  +    d_stream.zalloc = zalloc;
          248  +    d_stream.zfree = zfree;
          249  +    d_stream.opaque = (voidpf)0;
          250  +
          251  +    d_stream.next_in  = compr;
          252  +    d_stream.avail_in = 0;
          253  +    d_stream.next_out = uncompr;
          254  +
          255  +    err = inflateInit(&d_stream);
          256  +    CHECK_ERR(err, "inflateInit");
          257  +
          258  +    while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
          259  +        d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
          260  +        err = inflate(&d_stream, Z_NO_FLUSH);
          261  +        if (err == Z_STREAM_END) break;
          262  +        CHECK_ERR(err, "inflate");
          263  +    }
          264  +
          265  +    err = inflateEnd(&d_stream);
          266  +    CHECK_ERR(err, "inflateEnd");
          267  +
          268  +    if (strcmp((char*)uncompr, hello)) {
          269  +        fprintf(stderr, "bad inflate\n");
          270  +        exit(1);
          271  +    } else {
          272  +        printf("inflate(): %s\n", (char *)uncompr);
          273  +    }
          274  +}
          275  +
          276  +/* ===========================================================================
          277  + * Test deflate() with large buffers and dynamic change of compression level
          278  + */
          279  +void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
          280  +    Byte *compr, *uncompr;
          281  +    uLong comprLen, uncomprLen;
          282  +{
          283  +    z_stream c_stream; /* compression stream */
          284  +    int err;
          285  +
          286  +    c_stream.zalloc = zalloc;
          287  +    c_stream.zfree = zfree;
          288  +    c_stream.opaque = (voidpf)0;
          289  +
          290  +    err = deflateInit(&c_stream, Z_BEST_SPEED);
          291  +    CHECK_ERR(err, "deflateInit");
          292  +
          293  +    c_stream.next_out = compr;
          294  +    c_stream.avail_out = (uInt)comprLen;
          295  +
          296  +    /* At this point, uncompr is still mostly zeroes, so it should compress
          297  +     * very well:
          298  +     */
          299  +    c_stream.next_in = uncompr;
          300  +    c_stream.avail_in = (uInt)uncomprLen;
          301  +    err = deflate(&c_stream, Z_NO_FLUSH);
          302  +    CHECK_ERR(err, "deflate");
          303  +    if (c_stream.avail_in != 0) {
          304  +        fprintf(stderr, "deflate not greedy\n");
          305  +        exit(1);
          306  +    }
          307  +
          308  +    /* Feed in already compressed data and switch to no compression: */
          309  +    deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
          310  +    c_stream.next_in = compr;
          311  +    c_stream.avail_in = (uInt)comprLen/2;
          312  +    err = deflate(&c_stream, Z_NO_FLUSH);
          313  +    CHECK_ERR(err, "deflate");
          314  +
          315  +    /* Switch back to compressing mode: */
          316  +    deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
          317  +    c_stream.next_in = uncompr;
          318  +    c_stream.avail_in = (uInt)uncomprLen;
          319  +    err = deflate(&c_stream, Z_NO_FLUSH);
          320  +    CHECK_ERR(err, "deflate");
          321  +
          322  +    err = deflate(&c_stream, Z_FINISH);
          323  +    if (err != Z_STREAM_END) {
          324  +        fprintf(stderr, "deflate should report Z_STREAM_END\n");
          325  +        exit(1);
          326  +    }
          327  +    err = deflateEnd(&c_stream);
          328  +    CHECK_ERR(err, "deflateEnd");
          329  +}
          330  +
          331  +/* ===========================================================================
          332  + * Test inflate() with large buffers
          333  + */
          334  +void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
          335  +    Byte *compr, *uncompr;
          336  +    uLong comprLen, uncomprLen;
          337  +{
          338  +    int err;
          339  +    z_stream d_stream; /* decompression stream */
          340  +
          341  +    strcpy((char*)uncompr, "garbage");
          342  +
          343  +    d_stream.zalloc = zalloc;
          344  +    d_stream.zfree = zfree;
          345  +    d_stream.opaque = (voidpf)0;
          346  +
          347  +    d_stream.next_in  = compr;
          348  +    d_stream.avail_in = (uInt)comprLen;
          349  +
          350  +    err = inflateInit(&d_stream);
          351  +    CHECK_ERR(err, "inflateInit");
          352  +
          353  +    for (;;) {
          354  +        d_stream.next_out = uncompr;            /* discard the output */
          355  +        d_stream.avail_out = (uInt)uncomprLen;
          356  +        err = inflate(&d_stream, Z_NO_FLUSH);
          357  +        if (err == Z_STREAM_END) break;
          358  +        CHECK_ERR(err, "large inflate");
          359  +    }
          360  +
          361  +    err = inflateEnd(&d_stream);
          362  +    CHECK_ERR(err, "inflateEnd");
          363  +
          364  +    if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
          365  +        fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
          366  +        exit(1);
          367  +    } else {
          368  +        printf("large_inflate(): OK\n");
          369  +    }
          370  +}
          371  +
          372  +/* ===========================================================================
          373  + * Test deflate() with full flush
          374  + */
          375  +void test_flush(compr, comprLen)
          376  +    Byte *compr;
          377  +    uLong *comprLen;
          378  +{
          379  +    z_stream c_stream; /* compression stream */
          380  +    int err;
          381  +    uInt len = (uInt)strlen(hello)+1;
          382  +
          383  +    c_stream.zalloc = zalloc;
          384  +    c_stream.zfree = zfree;
          385  +    c_stream.opaque = (voidpf)0;
          386  +
          387  +    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
          388  +    CHECK_ERR(err, "deflateInit");
          389  +
          390  +    c_stream.next_in  = (Bytef*)hello;
          391  +    c_stream.next_out = compr;
          392  +    c_stream.avail_in = 3;
          393  +    c_stream.avail_out = (uInt)*comprLen;
          394  +    err = deflate(&c_stream, Z_FULL_FLUSH);
          395  +    CHECK_ERR(err, "deflate");
          396  +
          397  +    compr[3]++; /* force an error in first compressed block */
          398  +    c_stream.avail_in = len - 3;
          399  +
          400  +    err = deflate(&c_stream, Z_FINISH);
          401  +    if (err != Z_STREAM_END) {
          402  +        CHECK_ERR(err, "deflate");
          403  +    }
          404  +    err = deflateEnd(&c_stream);
          405  +    CHECK_ERR(err, "deflateEnd");
          406  +
          407  +    *comprLen = c_stream.total_out;
          408  +}
          409  +
          410  +/* ===========================================================================
          411  + * Test inflateSync()
          412  + */
          413  +void test_sync(compr, comprLen, uncompr, uncomprLen)
          414  +    Byte *compr, *uncompr;
          415  +    uLong comprLen, uncomprLen;
          416  +{
          417  +    int err;
          418  +    z_stream d_stream; /* decompression stream */
          419  +
          420  +    strcpy((char*)uncompr, "garbage");
          421  +
          422  +    d_stream.zalloc = zalloc;
          423  +    d_stream.zfree = zfree;
          424  +    d_stream.opaque = (voidpf)0;
          425  +
          426  +    d_stream.next_in  = compr;
          427  +    d_stream.avail_in = 2; /* just read the zlib header */
          428  +
          429  +    err = inflateInit(&d_stream);
          430  +    CHECK_ERR(err, "inflateInit");
          431  +
          432  +    d_stream.next_out = uncompr;
          433  +    d_stream.avail_out = (uInt)uncomprLen;
          434  +
          435  +    inflate(&d_stream, Z_NO_FLUSH);
          436  +    CHECK_ERR(err, "inflate");
          437  +
          438  +    d_stream.avail_in = (uInt)comprLen-2;   /* read all compressed data */
          439  +    err = inflateSync(&d_stream);           /* but skip the damaged part */
          440  +    CHECK_ERR(err, "inflateSync");
          441  +
          442  +    err = inflate(&d_stream, Z_FINISH);
          443  +    if (err != Z_DATA_ERROR) {
          444  +        fprintf(stderr, "inflate should report DATA_ERROR\n");
          445  +        /* Because of incorrect adler32 */
          446  +        exit(1);
          447  +    }
          448  +    err = inflateEnd(&d_stream);
          449  +    CHECK_ERR(err, "inflateEnd");
          450  +
          451  +    printf("after inflateSync(): hel%s\n", (char *)uncompr);
          452  +}
          453  +
          454  +/* ===========================================================================
          455  + * Test deflate() with preset dictionary
          456  + */
          457  +void test_dict_deflate(compr, comprLen)
          458  +    Byte *compr;
          459  +    uLong comprLen;
          460  +{
          461  +    z_stream c_stream; /* compression stream */
          462  +    int err;
          463  +
          464  +    c_stream.zalloc = zalloc;
          465  +    c_stream.zfree = zfree;
          466  +    c_stream.opaque = (voidpf)0;
          467  +
          468  +    err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
          469  +    CHECK_ERR(err, "deflateInit");
          470  +
          471  +    err = deflateSetDictionary(&c_stream,
          472  +                (const Bytef*)dictionary, (int)sizeof(dictionary));
          473  +    CHECK_ERR(err, "deflateSetDictionary");
          474  +
          475  +    dictId = c_stream.adler;
          476  +    c_stream.next_out = compr;
          477  +    c_stream.avail_out = (uInt)comprLen;
          478  +
          479  +    c_stream.next_in = (Bytef*)hello;
          480  +    c_stream.avail_in = (uInt)strlen(hello)+1;
          481  +
          482  +    err = deflate(&c_stream, Z_FINISH);
          483  +    if (err != Z_STREAM_END) {
          484  +        fprintf(stderr, "deflate should report Z_STREAM_END\n");
          485  +        exit(1);
          486  +    }
          487  +    err = deflateEnd(&c_stream);
          488  +    CHECK_ERR(err, "deflateEnd");
          489  +}
          490  +
          491  +/* ===========================================================================
          492  + * Test inflate() with a preset dictionary
          493  + */
          494  +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
          495  +    Byte *compr, *uncompr;
          496  +    uLong comprLen, uncomprLen;
          497  +{
          498  +    int err;
          499  +    z_stream d_stream; /* decompression stream */
          500  +
          501  +    strcpy((char*)uncompr, "garbage");
          502  +
          503  +    d_stream.zalloc = zalloc;
          504  +    d_stream.zfree = zfree;
          505  +    d_stream.opaque = (voidpf)0;
          506  +
          507  +    d_stream.next_in  = compr;
          508  +    d_stream.avail_in = (uInt)comprLen;
          509  +
          510  +    err = inflateInit(&d_stream);
          511  +    CHECK_ERR(err, "inflateInit");
          512  +
          513  +    d_stream.next_out = uncompr;
          514  +    d_stream.avail_out = (uInt)uncomprLen;
          515  +
          516  +    for (;;) {
          517  +        err = inflate(&d_stream, Z_NO_FLUSH);
          518  +        if (err == Z_STREAM_END) break;
          519  +        if (err == Z_NEED_DICT) {
          520  +            if (d_stream.adler != dictId) {
          521  +                fprintf(stderr, "unexpected dictionary");
          522  +                exit(1);
          523  +            }
          524  +            err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
          525  +                                       (int)sizeof(dictionary));
          526  +        }
          527  +        CHECK_ERR(err, "inflate with dict");
          528  +    }
          529  +
          530  +    err = inflateEnd(&d_stream);
          531  +    CHECK_ERR(err, "inflateEnd");
          532  +
          533  +    if (strcmp((char*)uncompr, hello)) {
          534  +        fprintf(stderr, "bad inflate with dict\n");
          535  +        exit(1);
          536  +    } else {
          537  +        printf("inflate with dictionary: %s\n", (char *)uncompr);
          538  +    }
          539  +}
          540  +
          541  +/* ===========================================================================
          542  + * Usage:  example [output.gz  [input.gz]]
          543  + */
          544  +
          545  +int main(argc, argv)
          546  +    int argc;
          547  +    char *argv[];
          548  +{
          549  +    Byte *compr, *uncompr;
          550  +    uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
          551  +    uLong uncomprLen = comprLen;
          552  +    static const char* myVersion = ZLIB_VERSION;
          553  +
          554  +    if (zlibVersion()[0] != myVersion[0]) {
          555  +        fprintf(stderr, "incompatible zlib version\n");
          556  +        exit(1);
          557  +
          558  +    } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
          559  +        fprintf(stderr, "warning: different zlib version\n");
          560  +    }
          561  +
          562  +    printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n",
          563  +            ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags());
          564  +
          565  +    compr    = (Byte*)calloc((uInt)comprLen, 1);
          566  +    uncompr  = (Byte*)calloc((uInt)uncomprLen, 1);
          567  +    /* compr and uncompr are cleared to avoid reading uninitialized
          568  +     * data and to ensure that uncompr compresses well.
          569  +     */
          570  +    if (compr == Z_NULL || uncompr == Z_NULL) {
          571  +        printf("out of memory\n");
          572  +        exit(1);
          573  +    }
          574  +
          575  +#ifdef Z_SOLO
          576  +    argc = strlen(argv[0]);
          577  +#else
          578  +    test_compress(compr, comprLen, uncompr, uncomprLen);
          579  +
          580  +    test_gzio((argc > 1 ? argv[1] : TESTFILE),
          581  +              uncompr, uncomprLen);
          582  +#endif
          583  +
          584  +    test_deflate(compr, comprLen);
          585  +    test_inflate(compr, comprLen, uncompr, uncomprLen);
          586  +
          587  +    test_large_deflate(compr, comprLen, uncompr, uncomprLen);
          588  +    test_large_inflate(compr, comprLen, uncompr, uncomprLen);
          589  +
          590  +    test_flush(compr, &comprLen);
          591  +    test_sync(compr, comprLen, uncompr, uncomprLen);
          592  +    comprLen = uncomprLen;
          593  +
          594  +    test_dict_deflate(compr, comprLen);
          595  +    test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
          596  +
          597  +    free(compr);
          598  +    free(uncompr);
          599  +
          600  +    return 0;
          601  +}

Added compat/zlib/test/infcover.c.

            1  +/* infcover.c -- test zlib's inflate routines with full code coverage
            2  + * Copyright (C) 2011 Mark Adler
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* to use, do: ./configure --cover && make cover */
            7  +
            8  +#include <stdio.h>
            9  +#include <stdlib.h>
           10  +#include <string.h>
           11  +#include <assert.h>
           12  +#include "zlib.h"
           13  +
           14  +/* get definition of internal structure so we can mess with it (see pull()),
           15  +   and so we can call inflate_trees() (see cover5()) */
           16  +#define ZLIB_INTERNAL
           17  +#include "inftrees.h"
           18  +#include "inflate.h"
           19  +
           20  +#define local static
           21  +
           22  +/* -- memory tracking routines -- */
           23  +
           24  +/*
           25  +   These memory tracking routines are provided to zlib and track all of zlib's
           26  +   allocations and deallocations, check for LIFO operations, keep a current
           27  +   and high water mark of total bytes requested, optionally set a limit on the
           28  +   total memory that can be allocated, and when done check for memory leaks.
           29  +
           30  +   They are used as follows:
           31  +
           32  +   z_stream strm;
           33  +   mem_setup(&strm)         initializes the memory tracking and sets the
           34  +                            zalloc, zfree, and opaque members of strm to use
           35  +                            memory tracking for all zlib operations on strm
           36  +   mem_limit(&strm, limit)  sets a limit on the total bytes requested -- a
           37  +                            request that exceeds this limit will result in an
           38  +                            allocation failure (returns NULL) -- setting the
           39  +                            limit to zero means no limit, which is the default
           40  +                            after mem_setup()
           41  +   mem_used(&strm, "msg")   prints to stderr "msg" and the total bytes used
           42  +   mem_high(&strm, "msg")   prints to stderr "msg" and the high water mark
           43  +   mem_done(&strm, "msg")   ends memory tracking, releases all allocations
           44  +                            for the tracking as well as leaked zlib blocks, if
           45  +                            any.  If there was anything unusual, such as leaked
           46  +                            blocks, non-FIFO frees, or frees of addresses not
           47  +                            allocated, then "msg" and information about the
           48  +                            problem is printed to stderr.  If everything is
           49  +                            normal, nothing is printed. mem_done resets the
           50  +                            strm members to Z_NULL to use the default memory
           51  +                            allocation routines on the next zlib initialization
           52  +                            using strm.
           53  + */
           54  +
           55  +/* these items are strung together in a linked list, one for each allocation */
           56  +struct mem_item {
           57  +    void *ptr;                  /* pointer to allocated memory */
           58  +    size_t size;                /* requested size of allocation */
           59  +    struct mem_item *next;      /* pointer to next item in list, or NULL */
           60  +};
           61  +
           62  +/* this structure is at the root of the linked list, and tracks statistics */
           63  +struct mem_zone {
           64  +    struct mem_item *first;     /* pointer to first item in list, or NULL */
           65  +    size_t total, highwater;    /* total allocations, and largest total */
           66  +    size_t limit;               /* memory allocation limit, or 0 if no limit */
           67  +    int notlifo, rogue;         /* counts of non-LIFO frees and rogue frees */
           68  +};
           69  +
           70  +/* memory allocation routine to pass to zlib */
           71  +local void *mem_alloc(void *mem, unsigned count, unsigned size)
           72  +{
           73  +    void *ptr;
           74  +    struct mem_item *item;
           75  +    struct mem_zone *zone = mem;
           76  +    size_t len = count * (size_t)size;
           77  +
           78  +    /* induced allocation failure */
           79  +    if (zone == NULL || (zone->limit && zone->total + len > zone->limit))
           80  +        return NULL;
           81  +
           82  +    /* perform allocation using the standard library, fill memory with a
           83  +       non-zero value to make sure that the code isn't depending on zeros */
           84  +    ptr = malloc(len);
           85  +    if (ptr == NULL)
           86  +        return NULL;
           87  +    memset(ptr, 0xa5, len);
           88  +
           89  +    /* create a new item for the list */
           90  +    item = malloc(sizeof(struct mem_item));
           91  +    if (item == NULL) {
           92  +        free(ptr);
           93  +        return NULL;
           94  +    }
           95  +    item->ptr = ptr;
           96  +    item->size = len;
           97  +
           98  +    /* insert item at the beginning of the list */
           99  +    item->next = zone->first;
          100  +    zone->first = item;
          101  +
          102  +    /* update the statistics */
          103  +    zone->total += item->size;
          104  +    if (zone->total > zone->highwater)
          105  +        zone->highwater = zone->total;
          106  +
          107  +    /* return the allocated memory */
          108  +    return ptr;
          109  +}
          110  +
          111  +/* memory free routine to pass to zlib */
          112  +local void mem_free(void *mem, void *ptr)
          113  +{
          114  +    struct mem_item *item, *next;
          115  +    struct mem_zone *zone = mem;
          116  +
          117  +    /* if no zone, just do a free */
          118  +    if (zone == NULL) {
          119  +        free(ptr);
          120  +        return;
          121  +    }
          122  +
          123  +    /* point next to the item that matches ptr, or NULL if not found -- remove
          124  +       the item from the linked list if found */
          125  +    next = zone->first;
          126  +    if (next) {
          127  +        if (next->ptr == ptr)
          128  +            zone->first = next->next;   /* first one is it, remove from list */
          129  +        else {
          130  +            do {                        /* search the linked list */
          131  +                item = next;
          132  +                next = item->next;
          133  +            } while (next != NULL && next->ptr != ptr);
          134  +            if (next) {                 /* if found, remove from linked list */
          135  +                item->next = next->next;
          136  +                zone->notlifo++;        /* not a LIFO free */
          137  +            }
          138  +
          139  +        }
          140  +    }
          141  +
          142  +    /* if found, update the statistics and free the item */
          143  +    if (next) {
          144  +        zone->total -= next->size;
          145  +        free(next);
          146  +    }
          147  +
          148  +    /* if not found, update the rogue count */
          149  +    else
          150  +        zone->rogue++;
          151  +
          152  +    /* in any case, do the requested free with the standard library function */
          153  +    free(ptr);
          154  +}
          155  +
          156  +/* set up a controlled memory allocation space for monitoring, set the stream
          157  +   parameters to the controlled routines, with opaque pointing to the space */
          158  +local void mem_setup(z_stream *strm)
          159  +{
          160  +    struct mem_zone *zone;
          161  +
          162  +    zone = malloc(sizeof(struct mem_zone));
          163  +    assert(zone != NULL);
          164  +    zone->first = NULL;
          165  +    zone->total = 0;
          166  +    zone->highwater = 0;
          167  +    zone->limit = 0;
          168  +    zone->notlifo = 0;
          169  +    zone->rogue = 0;
          170  +    strm->opaque = zone;
          171  +    strm->zalloc = mem_alloc;
          172  +    strm->zfree = mem_free;
          173  +}
          174  +
          175  +/* set a limit on the total memory allocation, or 0 to remove the limit */
          176  +local void mem_limit(z_stream *strm, size_t limit)
          177  +{
          178  +    struct mem_zone *zone = strm->opaque;
          179  +
          180  +    zone->limit = limit;
          181  +}
          182  +
          183  +/* show the current total requested allocations in bytes */
          184  +local void mem_used(z_stream *strm, char *prefix)
          185  +{
          186  +    struct mem_zone *zone = strm->opaque;
          187  +
          188  +    fprintf(stderr, "%s: %lu allocated\n", prefix, zone->total);
          189  +}
          190  +
          191  +/* show the high water allocation in bytes */
          192  +local void mem_high(z_stream *strm, char *prefix)
          193  +{
          194  +    struct mem_zone *zone = strm->opaque;
          195  +
          196  +    fprintf(stderr, "%s: %lu high water mark\n", prefix, zone->highwater);
          197  +}
          198  +
          199  +/* release the memory allocation zone -- if there are any surprises, notify */
          200  +local void mem_done(z_stream *strm, char *prefix)
          201  +{
          202  +    int count = 0;
          203  +    struct mem_item *item, *next;
          204  +    struct mem_zone *zone = strm->opaque;
          205  +
          206  +    /* show high water mark */
          207  +    mem_high(strm, prefix);
          208  +
          209  +    /* free leftover allocations and item structures, if any */
          210  +    item = zone->first;
          211  +    while (item != NULL) {
          212  +        free(item->ptr);
          213  +        next = item->next;
          214  +        free(item);
          215  +        item = next;
          216  +        count++;
          217  +    }
          218  +
          219  +    /* issue alerts about anything unexpected */
          220  +    if (count || zone->total)
          221  +        fprintf(stderr, "** %s: %lu bytes in %d blocks not freed\n",
          222  +                prefix, zone->total, count);
          223  +    if (zone->notlifo)
          224  +        fprintf(stderr, "** %s: %d frees not LIFO\n", prefix, zone->notlifo);
          225  +    if (zone->rogue)
          226  +        fprintf(stderr, "** %s: %d frees not recognized\n",
          227  +                prefix, zone->rogue);
          228  +
          229  +    /* free the zone and delete from the stream */
          230  +    free(zone);
          231  +    strm->opaque = Z_NULL;
          232  +    strm->zalloc = Z_NULL;
          233  +    strm->zfree = Z_NULL;
          234  +}
          235  +
          236  +/* -- inflate test routines -- */
          237  +
          238  +/* Decode a hexadecimal string, set *len to length, in[] to the bytes.  This
          239  +   decodes liberally, in that hex digits can be adjacent, in which case two in
          240  +   a row writes a byte.  Or they can delimited by any non-hex character, where
          241  +   the delimiters are ignored except when a single hex digit is followed by a
          242  +   delimiter in which case that single digit writes a byte.  The returned
          243  +   data is allocated and must eventually be freed.  NULL is returned if out of
          244  +   memory.  If the length is not needed, then len can be NULL. */
          245  +local unsigned char *h2b(const char *hex, unsigned *len)
          246  +{
          247  +    unsigned char *in;
          248  +    unsigned next, val;
          249  +
          250  +    in = malloc((strlen(hex) + 1) >> 1);
          251  +    if (in == NULL)
          252  +        return NULL;
          253  +    next = 0;
          254  +    val = 1;
          255  +    do {
          256  +        if (*hex >= '0' && *hex <= '9')
          257  +            val = (val << 4) + *hex - '0';
          258  +        else if (*hex >= 'A' && *hex <= 'F')
          259  +            val = (val << 4) + *hex - 'A' + 10;
          260  +        else if (*hex >= 'a' && *hex <= 'f')
          261  +            val = (val << 4) + *hex - 'a' + 10;
          262  +        else if (val != 1 && val < 32)  /* one digit followed by delimiter */
          263  +            val += 240;                 /* make it look like two digits */
          264  +        if (val > 255) {                /* have two digits */
          265  +            in[next++] = val & 0xff;    /* save the decoded byte */
          266  +            val = 1;                    /* start over */
          267  +        }
          268  +    } while (*hex++);       /* go through the loop with the terminating null */
          269  +    if (len != NULL)
          270  +        *len = next;
          271  +    in = reallocf(in, next);
          272  +    return in;
          273  +}
          274  +
          275  +/* generic inflate() run, where hex is the hexadecimal input data, what is the
          276  +   text to include in an error message, step is how much input data to feed
          277  +   inflate() on each call, or zero to feed it all, win is the window bits
          278  +   parameter to inflateInit2(), len is the size of the output buffer, and err
          279  +   is the error code expected from the first inflate() call (the second
          280  +   inflate() call is expected to return Z_STREAM_END).  If win is 47, then
          281  +   header information is collected with inflateGetHeader().  If a zlib stream
          282  +   is looking for a dictionary, then an empty dictionary is provided.
          283  +   inflate() is run until all of the input data is consumed. */
          284  +local void inf(char *hex, char *what, unsigned step, int win, unsigned len,
          285  +               int err)
          286  +{
          287  +    int ret;
          288  +    unsigned have;
          289  +    unsigned char *in, *out;
          290  +    z_stream strm, copy;
          291  +    gz_header head;
          292  +
          293  +    mem_setup(&strm);
          294  +    strm.avail_in = 0;
          295  +    strm.next_in = Z_NULL;
          296  +    ret = inflateInit2(&strm, win);
          297  +    if (ret != Z_OK) {
          298  +        mem_done(&strm, what);
          299  +        return;
          300  +    }
          301  +    out = malloc(len);                          assert(out != NULL);
          302  +    if (win == 47) {
          303  +        head.extra = out;
          304  +        head.extra_max = len;
          305  +        head.name = out;
          306  +        head.name_max = len;
          307  +        head.comment = out;
          308  +        head.comm_max = len;
          309  +        ret = inflateGetHeader(&strm, &head);   assert(ret == Z_OK);
          310  +    }
          311  +    in = h2b(hex, &have);                       assert(in != NULL);
          312  +    if (step == 0 || step > have)
          313  +        step = have;
          314  +    strm.avail_in = step;
          315  +    have -= step;
          316  +    strm.next_in = in;
          317  +    do {
          318  +        strm.avail_out = len;
          319  +        strm.next_out = out;
          320  +        ret = inflate(&strm, Z_NO_FLUSH);       assert(err == 9 || ret == err);
          321  +        if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT)
          322  +            break;
          323  +        if (ret == Z_NEED_DICT) {
          324  +            ret = inflateSetDictionary(&strm, in, 1);
          325  +                                                assert(ret == Z_DATA_ERROR);
          326  +            mem_limit(&strm, 1);
          327  +            ret = inflateSetDictionary(&strm, out, 0);
          328  +                                                assert(ret == Z_MEM_ERROR);
          329  +            mem_limit(&strm, 0);
          330  +            ((struct inflate_state *)strm.state)->mode = DICT;
          331  +            ret = inflateSetDictionary(&strm, out, 0);
          332  +                                                assert(ret == Z_OK);
          333  +            ret = inflate(&strm, Z_NO_FLUSH);   assert(ret == Z_BUF_ERROR);
          334  +        }
          335  +        ret = inflateCopy(&copy, &strm);        assert(ret == Z_OK);
          336  +        ret = inflateEnd(&copy);                assert(ret == Z_OK);
          337  +        err = 9;                        /* don't care next time around */
          338  +        have += strm.avail_in;
          339  +        strm.avail_in = step > have ? have : step;
          340  +        have -= strm.avail_in;
          341  +    } while (strm.avail_in);
          342  +    free(in);
          343  +    free(out);
          344  +    ret = inflateReset2(&strm, -8);             assert(ret == Z_OK);
          345  +    ret = inflateEnd(&strm);                    assert(ret == Z_OK);
          346  +    mem_done(&strm, what);
          347  +}
          348  +
          349  +/* cover all of the lines in inflate.c up to inflate() */
          350  +local void cover_support(void)
          351  +{
          352  +    int ret;
          353  +    z_stream strm;
          354  +
          355  +    mem_setup(&strm);
          356  +    strm.avail_in = 0;
          357  +    strm.next_in = Z_NULL;
          358  +    ret = inflateInit(&strm);                   assert(ret == Z_OK);
          359  +    mem_used(&strm, "inflate init");
          360  +    ret = inflatePrime(&strm, 5, 31);           assert(ret == Z_OK);
          361  +    ret = inflatePrime(&strm, -1, 0);           assert(ret == Z_OK);
          362  +    ret = inflateSetDictionary(&strm, Z_NULL, 0);
          363  +                                                assert(ret == Z_STREAM_ERROR);
          364  +    ret = inflateEnd(&strm);                    assert(ret == Z_OK);
          365  +    mem_done(&strm, "prime");
          366  +
          367  +    inf("63 0", "force window allocation", 0, -15, 1, Z_OK);
          368  +    inf("63 18 5", "force window replacement", 0, -8, 259, Z_OK);
          369  +    inf("63 18 68 30 d0 0 0", "force split window update", 4, -8, 259, Z_OK);
          370  +    inf("3 0", "use fixed blocks", 0, -15, 1, Z_STREAM_END);
          371  +    inf("", "bad window size", 0, 1, 0, Z_STREAM_ERROR);
          372  +
          373  +    mem_setup(&strm);
          374  +    strm.avail_in = 0;
          375  +    strm.next_in = Z_NULL;
          376  +    ret = inflateInit_(&strm, ZLIB_VERSION - 1, (int)sizeof(z_stream));
          377  +                                                assert(ret == Z_VERSION_ERROR);
          378  +    mem_done(&strm, "wrong version");
          379  +
          380  +    strm.avail_in = 0;
          381  +    strm.next_in = Z_NULL;
          382  +    ret = inflateInit(&strm);                   assert(ret == Z_OK);
          383  +    ret = inflateEnd(&strm);                    assert(ret == Z_OK);
          384  +    fputs("inflate built-in memory routines\n", stderr);
          385  +}
          386  +
          387  +/* cover all inflate() header and trailer cases and code after inflate() */
          388  +local void cover_wrap(void)
          389  +{
          390  +    int ret;
          391  +    z_stream strm, copy;
          392  +    unsigned char dict[257];
          393  +
          394  +    ret = inflate(Z_NULL, 0);                   assert(ret == Z_STREAM_ERROR);
          395  +    ret = inflateEnd(Z_NULL);                   assert(ret == Z_STREAM_ERROR);
          396  +    ret = inflateCopy(Z_NULL, Z_NULL);          assert(ret == Z_STREAM_ERROR);
          397  +    fputs("inflate bad parameters\n", stderr);
          398  +
          399  +    inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR);
          400  +    inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR);
          401  +    inf("77 85", "bad zlib method", 0, 15, 0, Z_DATA_ERROR);
          402  +    inf("8 99", "set window size from header", 0, 0, 0, Z_OK);
          403  +    inf("78 9c", "bad zlib window size", 0, 8, 0, Z_DATA_ERROR);
          404  +    inf("78 9c 63 0 0 0 1 0 1", "check adler32", 0, 15, 1, Z_STREAM_END);
          405  +    inf("1f 8b 8 1e 0 0 0 0 0 0 1 0 0 0 0 0 0", "bad header crc", 0, 47, 1,
          406  +        Z_DATA_ERROR);
          407  +    inf("1f 8b 8 2 0 0 0 0 0 0 1d 26 3 0 0 0 0 0 0 0 0 0", "check gzip length",
          408  +        0, 47, 0, Z_STREAM_END);
          409  +    inf("78 90", "bad zlib header check", 0, 47, 0, Z_DATA_ERROR);
          410  +    inf("8 b8 0 0 0 1", "need dictionary", 0, 8, 0, Z_NEED_DICT);
          411  +    inf("78 9c 63 0", "compute adler32", 0, 15, 1, Z_OK);
          412  +
          413  +    mem_setup(&strm);
          414  +    strm.avail_in = 0;
          415  +    strm.next_in = Z_NULL;
          416  +    ret = inflateInit2(&strm, -8);
          417  +    strm.avail_in = 2;
          418  +    strm.next_in = (void *)"\x63";
          419  +    strm.avail_out = 1;
          420  +    strm.next_out = (void *)&ret;
          421  +    mem_limit(&strm, 1);
          422  +    ret = inflate(&strm, Z_NO_FLUSH);           assert(ret == Z_MEM_ERROR);
          423  +    ret = inflate(&strm, Z_NO_FLUSH);           assert(ret == Z_MEM_ERROR);
          424  +    mem_limit(&strm, 0);
          425  +    memset(dict, 0, 257);
          426  +    ret = inflateSetDictionary(&strm, dict, 257);
          427  +                                                assert(ret == Z_OK);
          428  +    mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256);
          429  +    ret = inflatePrime(&strm, 16, 0);           assert(ret == Z_OK);
          430  +    strm.avail_in = 2;
          431  +    strm.next_in = (void *)"\x80";
          432  +    ret = inflateSync(&strm);                   assert(ret == Z_DATA_ERROR);
          433  +    ret = inflate(&strm, Z_NO_FLUSH);           assert(ret == Z_STREAM_ERROR);
          434  +    strm.avail_in = 4;
          435  +    strm.next_in = (void *)"\0\0\xff\xff";
          436  +    ret = inflateSync(&strm);                   assert(ret == Z_OK);
          437  +    (void)inflateSyncPoint(&strm);
          438  +    ret = inflateCopy(&copy, &strm);            assert(ret == Z_MEM_ERROR);
          439  +    mem_limit(&strm, 0);
          440  +    ret = inflateUndermine(&strm, 1);           assert(ret == Z_DATA_ERROR);
          441  +    (void)inflateMark(&strm);
          442  +    ret = inflateEnd(&strm);                    assert(ret == Z_OK);
          443  +    mem_done(&strm, "miscellaneous, force memory errors");
          444  +}
          445  +
          446  +/* input and output functions for inflateBack() */
          447  +local unsigned pull(void *desc, unsigned char **buf)
          448  +{
          449  +    static unsigned int next = 0;
          450  +    static unsigned char dat[] = {0x63, 0, 2, 0};
          451  +    struct inflate_state *state;
          452  +
          453  +    if (desc == Z_NULL) {
          454  +        next = 0;
          455  +        return 0;   /* no input (already provided at next_in) */
          456  +    }
          457  +    state = (void *)((z_stream *)desc)->state;
          458  +    if (state != Z_NULL)
          459  +        state->mode = SYNC;     /* force an otherwise impossible situation */
          460  +    return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0;
          461  +}
          462  +
          463  +local int push(void *desc, unsigned char *buf, unsigned len)
          464  +{
          465  +    buf += len;
          466  +    return desc != Z_NULL;      /* force error if desc not null */
          467  +}
          468  +
          469  +/* cover inflateBack() up to common deflate data cases and after those */
          470  +local void cover_back(void)
          471  +{
          472  +    int ret;
          473  +    z_stream strm;
          474  +    unsigned char win[32768];
          475  +
          476  +    ret = inflateBackInit_(Z_NULL, 0, win, 0, 0);
          477  +                                                assert(ret == Z_VERSION_ERROR);
          478  +    ret = inflateBackInit(Z_NULL, 0, win);      assert(ret == Z_STREAM_ERROR);
          479  +    ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL);
          480  +                                                assert(ret == Z_STREAM_ERROR);
          481  +    ret = inflateBackEnd(Z_NULL);               assert(ret == Z_STREAM_ERROR);
          482  +    fputs("inflateBack bad parameters\n", stderr);
          483  +
          484  +    mem_setup(&strm);
          485  +    ret = inflateBackInit(&strm, 15, win);      assert(ret == Z_OK);
          486  +    strm.avail_in = 2;
          487  +    strm.next_in = (void *)"\x03";
          488  +    ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL);
          489  +                                                assert(ret == Z_STREAM_END);
          490  +        /* force output error */
          491  +    strm.avail_in = 3;
          492  +    strm.next_in = (void *)"\x63\x00";
          493  +    ret = inflateBack(&strm, pull, Z_NULL, push, &strm);
          494  +                                                assert(ret == Z_BUF_ERROR);
          495  +        /* force mode error by mucking with state */
          496  +    ret = inflateBack(&strm, pull, &strm, push, Z_NULL);
          497  +                                                assert(ret == Z_STREAM_ERROR);
          498  +    ret = inflateBackEnd(&strm);                assert(ret == Z_OK);
          499  +    mem_done(&strm, "inflateBack bad state");
          500  +
          501  +    ret = inflateBackInit(&strm, 15, win);      assert(ret == Z_OK);
          502  +    ret = inflateBackEnd(&strm);                assert(ret == Z_OK);
          503  +    fputs("inflateBack built-in memory routines\n", stderr);
          504  +}
          505  +
          506  +/* do a raw inflate of data in hexadecimal with both inflate and inflateBack */
          507  +local int try(char *hex, char *id, int err)
          508  +{
          509  +    int ret;
          510  +    unsigned len, size;
          511  +    unsigned char *in, *out, *win;
          512  +    char *prefix;
          513  +    z_stream strm;
          514  +
          515  +    /* convert to hex */
          516  +    in = h2b(hex, &len);
          517  +    assert(in != NULL);
          518  +
          519  +    /* allocate work areas */
          520  +    size = len << 3;
          521  +    out = malloc(size);
          522  +    assert(out != NULL);
          523  +    win = malloc(32768);
          524  +    assert(win != NULL);
          525  +    prefix = malloc(strlen(id) + 6);
          526  +    assert(prefix != NULL);
          527  +
          528  +    /* first with inflate */
          529  +    strcpy(prefix, id);
          530  +    strcat(prefix, "-late");
          531  +    mem_setup(&strm);
          532  +    strm.avail_in = 0;
          533  +    strm.next_in = Z_NULL;
          534  +    ret = inflateInit2(&strm, err < 0 ? 47 : -15);
          535  +    assert(ret == Z_OK);
          536  +    strm.avail_in = len;
          537  +    strm.next_in = in;
          538  +    do {
          539  +        strm.avail_out = size;
          540  +        strm.next_out = out;
          541  +        ret = inflate(&strm, Z_TREES);
          542  +        assert(ret != Z_STREAM_ERROR && ret != Z_MEM_ERROR);
          543  +        if (ret == Z_DATA_ERROR || ret == Z_NEED_DICT)
          544  +            break;
          545  +    } while (strm.avail_in || strm.avail_out == 0);
          546  +    if (err) {
          547  +        assert(ret == Z_DATA_ERROR);
          548  +        assert(strcmp(id, strm.msg) == 0);
          549  +    }
          550  +    inflateEnd(&strm);
          551  +    mem_done(&strm, prefix);
          552  +
          553  +    /* then with inflateBack */
          554  +    if (err >= 0) {
          555  +        strcpy(prefix, id);
          556  +        strcat(prefix, "-back");
          557  +        mem_setup(&strm);
          558  +        ret = inflateBackInit(&strm, 15, win);
          559  +        assert(ret == Z_OK);
          560  +        strm.avail_in = len;
          561  +        strm.next_in = in;
          562  +        ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL);
          563  +        assert(ret != Z_STREAM_ERROR);
          564  +        if (err) {
          565  +            assert(ret == Z_DATA_ERROR);
          566  +            assert(strcmp(id, strm.msg) == 0);
          567  +        }
          568  +        inflateBackEnd(&strm);
          569  +        mem_done(&strm, prefix);
          570  +    }
          571  +
          572  +    /* clean up */
          573  +    free(prefix);
          574  +    free(win);
          575  +    free(out);
          576  +    free(in);
          577  +    return ret;
          578  +}
          579  +
          580  +/* cover deflate data cases in both inflate() and inflateBack() */
          581  +local void cover_inflate(void)
          582  +{
          583  +    try("0 0 0 0 0", "invalid stored block lengths", 1);
          584  +    try("3 0", "fixed", 0);
          585  +    try("6", "invalid block type", 1);
          586  +    try("1 1 0 fe ff 0", "stored", 0);
          587  +    try("fc 0 0", "too many length or distance symbols", 1);
          588  +    try("4 0 fe ff", "invalid code lengths set", 1);
          589  +    try("4 0 24 49 0", "invalid bit length repeat", 1);
          590  +    try("4 0 24 e9 ff ff", "invalid bit length repeat", 1);
          591  +    try("4 0 24 e9 ff 6d", "invalid code -- missing end-of-block", 1);
          592  +    try("4 80 49 92 24 49 92 24 71 ff ff 93 11 0",
          593  +        "invalid literal/lengths set", 1);
          594  +    try("4 80 49 92 24 49 92 24 f b4 ff ff c3 84", "invalid distances set", 1);
          595  +    try("4 c0 81 8 0 0 0 0 20 7f eb b 0 0", "invalid literal/length code", 1);
          596  +    try("2 7e ff ff", "invalid distance code", 1);
          597  +    try("c c0 81 0 0 0 0 0 90 ff 6b 4 0", "invalid distance too far back", 1);
          598  +
          599  +    /* also trailer mismatch just in inflate() */
          600  +    try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1", "incorrect data check", -1);
          601  +    try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1",
          602  +        "incorrect length check", -1);
          603  +    try("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c", "pull 17", 0);
          604  +    try("5 e0 81 91 24 cb b2 2c 49 e2 f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f",
          605  +        "long code", 0);
          606  +    try("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f", "length extra", 0);
          607  +    try("ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c",
          608  +        "long distance and extra", 0);
          609  +    try("ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
          610  +        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6", "window end", 0);
          611  +    inf("2 8 20 80 0 3 0", "inflate_fast TYPE return", 0, -15, 258,
          612  +        Z_STREAM_END);
          613  +    inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK);
          614  +}
          615  +
          616  +/* cover remaining lines in inftrees.c */
          617  +local void cover_trees(void)
          618  +{
          619  +    int ret;
          620  +    unsigned bits;
          621  +    unsigned short lens[16], work[16];
          622  +    code *next, table[ENOUGH_DISTS];
          623  +
          624  +    /* we need to call inflate_table() directly in order to manifest not-
          625  +       enough errors, since zlib insures that enough is always enough */
          626  +    for (bits = 0; bits < 15; bits++)
          627  +        lens[bits] = (unsigned short)(bits + 1);
          628  +    lens[15] = 15;
          629  +    next = table;
          630  +    bits = 15;
          631  +    ret = inflate_table(DISTS, lens, 16, &next, &bits, work);
          632  +                                                assert(ret == 1);
          633  +    next = table;
          634  +    bits = 1;
          635  +    ret = inflate_table(DISTS, lens, 16, &next, &bits, work);
          636  +                                                assert(ret == 1);
          637  +    fputs("inflate_table not enough errors\n", stderr);
          638  +}
          639  +
          640  +/* cover remaining inffast.c decoding and window copying */
          641  +local void cover_fast(void)
          642  +{
          643  +    inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68"
          644  +        " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR);
          645  +    inf("25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49"
          646  +        " 50 fe ff ff 3f 0 0", "fast distance extra bits", 0, -8, 258,
          647  +        Z_DATA_ERROR);
          648  +    inf("3 7e 0 0 0 0 0", "fast invalid distance code", 0, -8, 258,
          649  +        Z_DATA_ERROR);
          650  +    inf("1b 7 0 0 0 0 0", "fast invalid literal/length code", 0, -8, 258,
          651  +        Z_DATA_ERROR);
          652  +    inf("d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0",
          653  +        "fast 2nd level codes and too far back", 0, -8, 258, Z_DATA_ERROR);
          654  +    inf("63 18 5 8c 10 8 0 0 0 0", "very common case", 0, -8, 259, Z_OK);
          655  +    inf("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0",
          656  +        "contiguous and wrap around window", 6, -8, 259, Z_OK);
          657  +    inf("63 0 3 0 0 0 0 0", "copy direct from output", 0, -8, 259,
          658  +        Z_STREAM_END);
          659  +}
          660  +
          661  +int main(void)
          662  +{
          663  +    fprintf(stderr, "%s\n", zlibVersion());
          664  +    cover_support();
          665  +    cover_wrap();
          666  +    cover_back();
          667  +    cover_inflate();
          668  +    cover_trees();
          669  +    cover_fast();
          670  +    return 0;
          671  +}

Added compat/zlib/test/minigzip.c.

            1  +/* minigzip.c -- simulate gzip using the zlib compression library
            2  + * Copyright (C) 1995-2006, 2010, 2011 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/*
            7  + * minigzip is a minimal implementation of the gzip utility. This is
            8  + * only an example of using zlib and isn't meant to replace the
            9  + * full-featured gzip. No attempt is made to deal with file systems
           10  + * limiting names to 14 or 8+3 characters, etc... Error checking is
           11  + * very limited. So use minigzip only for testing; use gzip for the
           12  + * real thing. On MSDOS, use only on file names without extension
           13  + * or in pipe mode.
           14  + */
           15  +
           16  +/* @(#) $Id$ */
           17  +
           18  +#include "zlib.h"
           19  +#include <stdio.h>
           20  +
           21  +#ifdef STDC
           22  +#  include <string.h>
           23  +#  include <stdlib.h>
           24  +#endif
           25  +
           26  +#ifdef USE_MMAP
           27  +#  include <sys/types.h>
           28  +#  include <sys/mman.h>
           29  +#  include <sys/stat.h>
           30  +#endif
           31  +
           32  +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
           33  +#  include <fcntl.h>
           34  +#  include <io.h>
           35  +#  ifdef UNDER_CE
           36  +#    include <stdlib.h>
           37  +#  endif
           38  +#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
           39  +#else
           40  +#  define SET_BINARY_MODE(file)
           41  +#endif
           42  +
           43  +#ifdef VMS
           44  +#  define unlink delete
           45  +#  define GZ_SUFFIX "-gz"
           46  +#endif
           47  +#ifdef RISCOS
           48  +#  define unlink remove
           49  +#  define GZ_SUFFIX "-gz"
           50  +#  define fileno(file) file->__file
           51  +#endif
           52  +#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
           53  +#  include <unix.h> /* for fileno */
           54  +#endif
           55  +
           56  +#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
           57  +#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
           58  +  extern int unlink OF((const char *));
           59  +#endif
           60  +#endif
           61  +
           62  +#if defined(UNDER_CE)
           63  +#  include <windows.h>
           64  +#  define perror(s) pwinerror(s)
           65  +
           66  +/* Map the Windows error number in ERROR to a locale-dependent error
           67  +   message string and return a pointer to it.  Typically, the values
           68  +   for ERROR come from GetLastError.
           69  +
           70  +   The string pointed to shall not be modified by the application,
           71  +   but may be overwritten by a subsequent call to strwinerror
           72  +
           73  +   The strwinerror function does not change the current setting
           74  +   of GetLastError.  */
           75  +
           76  +static char *strwinerror (error)
           77  +     DWORD error;
           78  +{
           79  +    static char buf[1024];
           80  +
           81  +    wchar_t *msgbuf;
           82  +    DWORD lasterr = GetLastError();
           83  +    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
           84  +        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
           85  +        NULL,
           86  +        error,
           87  +        0, /* Default language */
           88  +        (LPVOID)&msgbuf,
           89  +        0,
           90  +        NULL);
           91  +    if (chars != 0) {
           92  +        /* If there is an \r\n appended, zap it.  */
           93  +        if (chars >= 2
           94  +            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
           95  +            chars -= 2;
           96  +            msgbuf[chars] = 0;
           97  +        }
           98  +
           99  +        if (chars > sizeof (buf) - 1) {
          100  +            chars = sizeof (buf) - 1;
          101  +            msgbuf[chars] = 0;
          102  +        }
          103  +
          104  +        wcstombs(buf, msgbuf, chars + 1);
          105  +        LocalFree(msgbuf);
          106  +    }
          107  +    else {
          108  +        sprintf(buf, "unknown win32 error (%ld)", error);
          109  +    }
          110  +
          111  +    SetLastError(lasterr);
          112  +    return buf;
          113  +}
          114  +
          115  +static void pwinerror (s)
          116  +    const char *s;
          117  +{
          118  +    if (s && *s)
          119  +        fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
          120  +    else
          121  +        fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
          122  +}
          123  +
          124  +#endif /* UNDER_CE */
          125  +
          126  +#ifndef GZ_SUFFIX
          127  +#  define GZ_SUFFIX ".gz"
          128  +#endif
          129  +#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
          130  +
          131  +#define BUFLEN      16384
          132  +#define MAX_NAME_LEN 1024
          133  +
          134  +#ifdef MAXSEG_64K
          135  +#  define local static
          136  +   /* Needed for systems with limitation on stack size. */
          137  +#else
          138  +#  define local
          139  +#endif
          140  +
          141  +#ifdef Z_SOLO
          142  +/* for Z_SOLO, create simplified gz* functions using deflate and inflate */
          143  +
          144  +#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
          145  +#  include <unistd.h>       /* for unlink() */
          146  +#endif
          147  +
          148  +void *myalloc OF((void *, unsigned, unsigned));
          149  +void myfree OF((void *, void *));
          150  +
          151  +void *myalloc(q, n, m)
          152  +    void *q;
          153  +    unsigned n, m;
          154  +{
          155  +    q = Z_NULL;
          156  +    return calloc(n, m);
          157  +}
          158  +
          159  +void myfree(q, p)
          160  +    void *q, *p;
          161  +{
          162  +    q = Z_NULL;
          163  +    free(p);
          164  +}
          165  +
          166  +typedef struct gzFile_s {
          167  +    FILE *file;
          168  +    int write;
          169  +    int err;
          170  +    char *msg;
          171  +    z_stream strm;
          172  +} *gzFile;
          173  +
          174  +gzFile gzopen OF((const char *, const char *));
          175  +gzFile gzdopen OF((int, const char *));
          176  +gzFile gz_open OF((const char *, int, const char *));
          177  +
          178  +gzFile gzopen(path, mode)
          179  +const char *path;
          180  +const char *mode;
          181  +{
          182  +    return gz_open(path, -1, mode);
          183  +}
          184  +
          185  +gzFile gzdopen(fd, mode)
          186  +int fd;
          187  +const char *mode;
          188  +{
          189  +    return gz_open(NULL, fd, mode);
          190  +}
          191  +
          192  +gzFile gz_open(path, fd, mode)
          193  +    const char *path;
          194  +    int fd;
          195  +    const char *mode;
          196  +{
          197  +    gzFile gz;
          198  +    int ret;
          199  +
          200  +    gz = malloc(sizeof(struct gzFile_s));
          201  +    if (gz == NULL)
          202  +        return NULL;
          203  +    gz->write = strchr(mode, 'w') != NULL;
          204  +    gz->strm.zalloc = myalloc;
          205  +    gz->strm.zfree = myfree;
          206  +    gz->strm.opaque = Z_NULL;
          207  +    if (gz->write)
          208  +        ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
          209  +    else {
          210  +        gz->strm.next_in = 0;
          211  +        gz->strm.avail_in = Z_NULL;
          212  +        ret = inflateInit2(&(gz->strm), 15 + 16);
          213  +    }
          214  +    if (ret != Z_OK) {
          215  +        free(gz);
          216  +        return NULL;
          217  +    }
          218  +    gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
          219  +                              fopen(path, gz->write ? "wb" : "rb");
          220  +    if (gz->file == NULL) {
          221  +        gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
          222  +        free(gz);
          223  +        return NULL;
          224  +    }
          225  +    gz->err = 0;
          226  +    gz->msg = "";
          227  +    return gz;
          228  +}
          229  +
          230  +int gzwrite OF((gzFile, const void *, unsigned));
          231  +
          232  +int gzwrite(gz, buf, len)
          233  +    gzFile gz;
          234  +    const void *buf;
          235  +    unsigned len;
          236  +{
          237  +    z_stream *strm;
          238  +    unsigned char out[BUFLEN];
          239  +
          240  +    if (gz == NULL || !gz->write)
          241  +        return 0;
          242  +    strm = &(gz->strm);
          243  +    strm->next_in = (void *)buf;
          244  +    strm->avail_in = len;
          245  +    do {
          246  +        strm->next_out = out;
          247  +        strm->avail_out = BUFLEN;
          248  +        (void)deflate(strm, Z_NO_FLUSH);
          249  +        fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
          250  +    } while (strm->avail_out == 0);
          251  +    return len;
          252  +}
          253  +
          254  +int gzread OF((gzFile, void *, unsigned));
          255  +
          256  +int gzread(gz, buf, len)
          257  +    gzFile gz;
          258  +    void *buf;
          259  +    unsigned len;
          260  +{
          261  +    int ret;
          262  +    unsigned got;
          263  +    unsigned char in[1];
          264  +    z_stream *strm;
          265  +
          266  +    if (gz == NULL || gz->write)
          267  +        return 0;
          268  +    if (gz->err)
          269  +        return 0;
          270  +    strm = &(gz->strm);
          271  +    strm->next_out = (void *)buf;
          272  +    strm->avail_out = len;
          273  +    do {
          274  +        got = fread(in, 1, 1, gz->file);
          275  +        if (got == 0)
          276  +            break;
          277  +        strm->next_in = in;
          278  +        strm->avail_in = 1;
          279  +        ret = inflate(strm, Z_NO_FLUSH);
          280  +        if (ret == Z_DATA_ERROR) {
          281  +            gz->err = Z_DATA_ERROR;
          282  +            gz->msg = strm->msg;
          283  +            return 0;
          284  +        }
          285  +        if (ret == Z_STREAM_END)
          286  +            inflateReset(strm);
          287  +    } while (strm->avail_out);
          288  +    return len - strm->avail_out;
          289  +}
          290  +
          291  +int gzclose OF((gzFile));
          292  +
          293  +int gzclose(gz)
          294  +    gzFile gz;
          295  +{
          296  +    z_stream *strm;
          297  +    unsigned char out[BUFLEN];
          298  +
          299  +    if (gz == NULL)
          300  +        return Z_STREAM_ERROR;
          301  +    strm = &(gz->strm);
          302  +    if (gz->write) {
          303  +        strm->next_in = Z_NULL;
          304  +        strm->avail_in = 0;
          305  +        do {
          306  +            strm->next_out = out;
          307  +            strm->avail_out = BUFLEN;
          308  +            (void)deflate(strm, Z_FINISH);
          309  +            fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
          310  +        } while (strm->avail_out == 0);
          311  +        deflateEnd(strm);
          312  +    }
          313  +    else
          314  +        inflateEnd(strm);
          315  +    fclose(gz->file);
          316  +    free(gz);
          317  +    return Z_OK;
          318  +}
          319  +
          320  +const char *gzerror OF((gzFile, int *));
          321  +
          322  +const char *gzerror(gz, err)
          323  +    gzFile gz;
          324  +    int *err;
          325  +{
          326  +    *err = gz->err;
          327  +    return gz->msg;
          328  +}
          329  +
          330  +#endif
          331  +
          332  +char *prog;
          333  +
          334  +void error            OF((const char *msg));
          335  +void gz_compress      OF((FILE   *in, gzFile out));
          336  +#ifdef USE_MMAP
          337  +int  gz_compress_mmap OF((FILE   *in, gzFile out));
          338  +#endif
          339  +void gz_uncompress    OF((gzFile in, FILE   *out));
          340  +void file_compress    OF((char  *file, char *mode));
          341  +void file_uncompress  OF((char  *file));
          342  +int  main             OF((int argc, char *argv[]));
          343  +
          344  +/* ===========================================================================
          345  + * Display error message and exit
          346  + */
          347  +void error(msg)
          348  +    const char *msg;
          349  +{
          350  +    fprintf(stderr, "%s: %s\n", prog, msg);
          351  +    exit(1);
          352  +}
          353  +
          354  +/* ===========================================================================
          355  + * Compress input to output then close both files.
          356  + */
          357  +
          358  +void gz_compress(in, out)
          359  +    FILE   *in;
          360  +    gzFile out;
          361  +{
          362  +    local char buf[BUFLEN];
          363  +    int len;
          364  +    int err;
          365  +
          366  +#ifdef USE_MMAP
          367  +    /* Try first compressing with mmap. If mmap fails (minigzip used in a
          368  +     * pipe), use the normal fread loop.
          369  +     */
          370  +    if (gz_compress_mmap(in, out) == Z_OK) return;
          371  +#endif
          372  +    for (;;) {
          373  +        len = (int)fread(buf, 1, sizeof(buf), in);
          374  +        if (ferror(in)) {
          375  +            perror("fread");
          376  +            exit(1);
          377  +        }
          378  +        if (len == 0) break;
          379  +
          380  +        if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
          381  +    }
          382  +    fclose(in);
          383  +    if (gzclose(out) != Z_OK) error("failed gzclose");
          384  +}
          385  +
          386  +#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
          387  +
          388  +/* Try compressing the input file at once using mmap. Return Z_OK if
          389  + * if success, Z_ERRNO otherwise.
          390  + */
          391  +int gz_compress_mmap(in, out)
          392  +    FILE   *in;
          393  +    gzFile out;
          394  +{
          395  +    int len;
          396  +    int err;
          397  +    int ifd = fileno(in);
          398  +    caddr_t buf;    /* mmap'ed buffer for the entire input file */
          399  +    off_t buf_len;  /* length of the input file */
          400  +    struct stat sb;
          401  +
          402  +    /* Determine the size of the file, needed for mmap: */
          403  +    if (fstat(ifd, &sb) < 0) return Z_ERRNO;
          404  +    buf_len = sb.st_size;
          405  +    if (buf_len <= 0) return Z_ERRNO;
          406  +
          407  +    /* Now do the actual mmap: */
          408  +    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
          409  +    if (buf == (caddr_t)(-1)) return Z_ERRNO;
          410  +
          411  +    /* Compress the whole file at once: */
          412  +    len = gzwrite(out, (char *)buf, (unsigned)buf_len);
          413  +
          414  +    if (len != (int)buf_len) error(gzerror(out, &err));
          415  +
          416  +    munmap(buf, buf_len);
          417  +    fclose(in);
          418  +    if (gzclose(out) != Z_OK) error("failed gzclose");
          419  +    return Z_OK;
          420  +}
          421  +#endif /* USE_MMAP */
          422  +
          423  +/* ===========================================================================
          424  + * Uncompress input to output then close both files.
          425  + */
          426  +void gz_uncompress(in, out)
          427  +    gzFile in;
          428  +    FILE   *out;
          429  +{
          430  +    local char buf[BUFLEN];
          431  +    int len;
          432  +    int err;
          433  +
          434  +    for (;;) {
          435  +        len = gzread(in, buf, sizeof(buf));
          436  +        if (len < 0) error (gzerror(in, &err));
          437  +        if (len == 0) break;
          438  +
          439  +        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
          440  +            error("failed fwrite");
          441  +        }
          442  +    }
          443  +    if (fclose(out)) error("failed fclose");
          444  +
          445  +    if (gzclose(in) != Z_OK) error("failed gzclose");
          446  +}
          447  +
          448  +
          449  +/* ===========================================================================
          450  + * Compress the given file: create a corresponding .gz file and remove the
          451  + * original.
          452  + */
          453  +void file_compress(file, mode)
          454  +    char  *file;
          455  +    char  *mode;
          456  +{
          457  +    local char outfile[MAX_NAME_LEN];
          458  +    FILE  *in;
          459  +    gzFile out;
          460  +
          461  +    if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
          462  +        fprintf(stderr, "%s: filename too long\n", prog);
          463  +        exit(1);
          464  +    }
          465  +
          466  +    strcpy(outfile, file);
          467  +    strcat(outfile, GZ_SUFFIX);
          468  +
          469  +    in = fopen(file, "rb");
          470  +    if (in == NULL) {
          471  +        perror(file);
          472  +        exit(1);
          473  +    }
          474  +    out = gzopen(outfile, mode);
          475  +    if (out == NULL) {
          476  +        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
          477  +        exit(1);
          478  +    }
          479  +    gz_compress(in, out);
          480  +
          481  +    unlink(file);
          482  +}
          483  +
          484  +
          485  +/* ===========================================================================
          486  + * Uncompress the given file and remove the original.
          487  + */
          488  +void file_uncompress(file)
          489  +    char  *file;
          490  +{
          491  +    local char buf[MAX_NAME_LEN];
          492  +    char *infile, *outfile;
          493  +    FILE  *out;
          494  +    gzFile in;
          495  +    size_t len = strlen(file);
          496  +
          497  +    if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
          498  +        fprintf(stderr, "%s: filename too long\n", prog);
          499  +        exit(1);
          500  +    }
          501  +
          502  +    strcpy(buf, file);
          503  +
          504  +    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
          505  +        infile = file;
          506  +        outfile = buf;
          507  +        outfile[len-3] = '\0';
          508  +    } else {
          509  +        outfile = file;
          510  +        infile = buf;
          511  +        strcat(infile, GZ_SUFFIX);
          512  +    }
          513  +    in = gzopen(infile, "rb");
          514  +    if (in == NULL) {
          515  +        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
          516  +        exit(1);
          517  +    }
          518  +    out = fopen(outfile, "wb");
          519  +    if (out == NULL) {
          520  +        perror(file);
          521  +        exit(1);
          522  +    }
          523  +
          524  +    gz_uncompress(in, out);
          525  +
          526  +    unlink(infile);
          527  +}
          528  +
          529  +
          530  +/* ===========================================================================
          531  + * Usage:  minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
          532  + *   -c : write to standard output
          533  + *   -d : decompress
          534  + *   -f : compress with Z_FILTERED
          535  + *   -h : compress with Z_HUFFMAN_ONLY
          536  + *   -r : compress with Z_RLE
          537  + *   -1 to -9 : compression level
          538  + */
          539  +
          540  +int main(argc, argv)
          541  +    int argc;
          542  +    char *argv[];
          543  +{
          544  +    int copyout = 0;
          545  +    int uncompr = 0;
          546  +    gzFile file;
          547  +    char *bname, outmode[20];
          548  +
          549  +    strcpy(outmode, "wb6 ");
          550  +
          551  +    prog = argv[0];
          552  +    bname = strrchr(argv[0], '/');
          553  +    if (bname)
          554  +      bname++;
          555  +    else
          556  +      bname = argv[0];
          557  +    argc--, argv++;
          558  +
          559  +    if (!strcmp(bname, "gunzip"))
          560  +      uncompr = 1;
          561  +    else if (!strcmp(bname, "zcat"))
          562  +      copyout = uncompr = 1;
          563  +
          564  +    while (argc > 0) {
          565  +      if (strcmp(*argv, "-c") == 0)
          566  +        copyout = 1;
          567  +      else if (strcmp(*argv, "-d") == 0)
          568  +        uncompr = 1;
          569  +      else if (strcmp(*argv, "-f") == 0)
          570  +        outmode[3] = 'f';
          571  +      else if (strcmp(*argv, "-h") == 0)
          572  +        outmode[3] = 'h';
          573  +      else if (strcmp(*argv, "-r") == 0)
          574  +        outmode[3] = 'R';
          575  +      else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
          576  +               (*argv)[2] == 0)
          577  +        outmode[2] = (*argv)[1];
          578  +      else
          579  +        break;
          580  +      argc--, argv++;
          581  +    }
          582  +    if (outmode[3] == ' ')
          583  +        outmode[3] = 0;
          584  +    if (argc == 0) {
          585  +        SET_BINARY_MODE(stdin);
          586  +        SET_BINARY_MODE(stdout);
          587  +        if (uncompr) {
          588  +            file = gzdopen(fileno(stdin), "rb");
          589  +            if (file == NULL) error("can't gzdopen stdin");
          590  +            gz_uncompress(file, stdout);
          591  +        } else {
          592  +            file = gzdopen(fileno(stdout), outmode);
          593  +            if (file == NULL) error("can't gzdopen stdout");
          594  +            gz_compress(stdin, file);
          595  +        }
          596  +    } else {
          597  +        if (copyout) {
          598  +            SET_BINARY_MODE(stdout);
          599  +        }
          600  +        do {
          601  +            if (uncompr) {
          602  +                if (copyout) {
          603  +                    file = gzopen(*argv, "rb");
          604  +                    if (file == NULL)
          605  +                        fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
          606  +                    else
          607  +                        gz_uncompress(file, stdout);
          608  +                } else {
          609  +                    file_uncompress(*argv);
          610  +                }
          611  +            } else {
          612  +                if (copyout) {
          613  +                    FILE * in = fopen(*argv, "rb");
          614  +
          615  +                    if (in == NULL) {
          616  +                        perror(*argv);
          617  +                    } else {
          618  +                        file = gzdopen(fileno(stdout), outmode);
          619  +                        if (file == NULL) error("can't gzdopen stdout");
          620  +
          621  +                        gz_compress(in, file);
          622  +                    }
          623  +
          624  +                } else {
          625  +                    file_compress(*argv, outmode);
          626  +                }
          627  +            }
          628  +        } while (argv++, --argc);
          629  +    }
          630  +    return 0;
          631  +}

Added compat/zlib/treebuild.xml.

            1  +<?xml version="1.0" ?>
            2  +<package name="zlib" version="1.2.7">
            3  +    <library name="zlib" dlversion="1.2.7" dlname="z">
            4  +	<property name="description"> zip compression library </property>
            5  +	<property name="include-target-dir" value="$(@PACKAGE/install-includedir)" />
            6  +
            7  +	<!-- fixme: not implemented yet -->
            8  +	<property name="compiler/c/inline" value="yes" />
            9  +
           10  +	<include-file name="zlib.h" scope="public" mode="644" />
           11  +	<include-file name="zconf.h" scope="public" mode="644" />
           12  +
           13  +	<source name="adler32.c">
           14  +	    <depend name="zlib.h" />
           15  +	    <depend name="zconf.h" />
           16  +	</source>
           17  +	<source name="compress.c">
           18  +	    <depend name="zlib.h" />
           19  +	    <depend name="zconf.h" />
           20  +	</source>
           21  +	<source name="crc32.c">
           22  +	    <depend name="zlib.h" />
           23  +	    <depend name="zconf.h" />
           24  +	    <depend name="crc32.h" />
           25  +	</source>
           26  +	<source name="gzclose.c">
           27  +	    <depend name="zlib.h" />
           28  +	    <depend name="zconf.h" />
           29  +	    <depend name="gzguts.h" />
           30  +	</source>
           31  +	<source name="gzlib.c">
           32  +	    <depend name="zlib.h" />
           33  +	    <depend name="zconf.h" />
           34  +	    <depend name="gzguts.h" />
           35  +	</source>
           36  +	<source name="gzread.c">
           37  +	    <depend name="zlib.h" />
           38  +	    <depend name="zconf.h" />
           39  +	    <depend name="gzguts.h" />
           40  +	</source>
           41  +	<source name="gzwrite.c">
           42  +	    <depend name="zlib.h" />
           43  +	    <depend name="zconf.h" />
           44  +	    <depend name="gzguts.h" />
           45  +	</source>
           46  +	<source name="uncompr.c">
           47  +	    <depend name="zlib.h" />
           48  +	    <depend name="zconf.h" />
           49  +	</source>
           50  +	<source name="deflate.c">
           51  +	    <depend name="zlib.h" />
           52  +	    <depend name="zconf.h" />
           53  +	    <depend name="zutil.h" />
           54  +	    <depend name="deflate.h" />
           55  +	</source>
           56  +	<source name="trees.c">
           57  +	    <depend name="zlib.h" />
           58  +	    <depend name="zconf.h" />
           59  +	    <depend name="zutil.h" />
           60  +	    <depend name="deflate.h" />
           61  +	    <depend name="trees.h" />
           62  +	</source>
           63  +	<source name="zutil.c">
           64  +	    <depend name="zlib.h" />
           65  +	    <depend name="zconf.h" />
           66  +	    <depend name="zutil.h" />
           67  +	</source>
           68  +	<source name="inflate.c">
           69  +	    <depend name="zlib.h" />
           70  +	    <depend name="zconf.h" />
           71  +	    <depend name="zutil.h" />
           72  +	    <depend name="inftrees.h" />
           73  +	    <depend name="inflate.h" />
           74  +	    <depend name="inffast.h" />
           75  +	</source>
           76  +	<source name="infback.c">
           77  +	    <depend name="zlib.h" />
           78  +	    <depend name="zconf.h" />
           79  +	    <depend name="zutil.h" />
           80  +	    <depend name="inftrees.h" />
           81  +	    <depend name="inflate.h" />
           82  +	    <depend name="inffast.h" />
           83  +	</source>
           84  +	<source name="inftrees.c">
           85  +	    <depend name="zlib.h" />
           86  +	    <depend name="zconf.h" />
           87  +	    <depend name="zutil.h" />
           88  +	    <depend name="inftrees.h" />
           89  +	</source>
           90  +	<source name="inffast.c">
           91  +	    <depend name="zlib.h" />
           92  +	    <depend name="zconf.h" />
           93  +	    <depend name="zutil.h" />
           94  +	    <depend name="inftrees.h" />
           95  +	    <depend name="inflate.h" />
           96  +	    <depend name="inffast.h" />
           97  +	</source>
           98  +    </library>
           99  +</package>
          100  +
          101  +<!--
          102  +CFLAGS=-O
          103  +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
          104  +#CFLAGS=-g -DDEBUG
          105  +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
          106  +#           -Wstrict-prototypes -Wmissing-prototypes
          107  +
          108  +# OBJA =
          109  +# to use the asm code: make OBJA=match.o
          110  +#
          111  +match.o: match.S
          112  +	$(CPP) match.S > _match.s
          113  +	$(CC) -c _match.s
          114  +	mv _match.o match.o
          115  +	rm -f _match.s
          116  +-->

Added compat/zlib/trees.c.

            1  +/* trees.c -- output deflated data using Huffman coding
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly
            3  + * detect_data_type() function provided freely by Cosmin Truta, 2006
            4  + * For conditions of distribution and use, see copyright notice in zlib.h
            5  + */
            6  +
            7  +/*
            8  + *  ALGORITHM
            9  + *
           10  + *      The "deflation" process uses several Huffman trees. The more
           11  + *      common source values are represented by shorter bit sequences.
           12  + *
           13  + *      Each code tree is stored in a compressed form which is itself
           14  + * a Huffman encoding of the lengths of all the code strings (in
           15  + * ascending order by source values).  The actual code strings are
           16  + * reconstructed from the lengths in the inflate process, as described
           17  + * in the deflate specification.
           18  + *
           19  + *  REFERENCES
           20  + *
           21  + *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
           22  + *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
           23  + *
           24  + *      Storer, James A.
           25  + *          Data Compression:  Methods and Theory, pp. 49-50.
           26  + *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
           27  + *
           28  + *      Sedgewick, R.
           29  + *          Algorithms, p290.
           30  + *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
           31  + */
           32  +
           33  +/* @(#) $Id$ */
           34  +
           35  +/* #define GEN_TREES_H */
           36  +
           37  +#include "deflate.h"
           38  +
           39  +#ifdef DEBUG
           40  +#  include <ctype.h>
           41  +#endif
           42  +
           43  +/* ===========================================================================
           44  + * Constants
           45  + */
           46  +
           47  +#define MAX_BL_BITS 7
           48  +/* Bit length codes must not exceed MAX_BL_BITS bits */
           49  +
           50  +#define END_BLOCK 256
           51  +/* end of block literal code */
           52  +
           53  +#define REP_3_6      16
           54  +/* repeat previous bit length 3-6 times (2 bits of repeat count) */
           55  +
           56  +#define REPZ_3_10    17
           57  +/* repeat a zero length 3-10 times  (3 bits of repeat count) */
           58  +
           59  +#define REPZ_11_138  18
           60  +/* repeat a zero length 11-138 times  (7 bits of repeat count) */
           61  +
           62  +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
           63  +   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
           64  +
           65  +local const int extra_dbits[D_CODES] /* extra bits for each distance code */
           66  +   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
           67  +
           68  +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
           69  +   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
           70  +
           71  +local const uch bl_order[BL_CODES]
           72  +   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
           73  +/* The lengths of the bit length codes are sent in order of decreasing
           74  + * probability, to avoid transmitting the lengths for unused bit length codes.
           75  + */
           76  +
           77  +/* ===========================================================================
           78  + * Local data. These are initialized only once.
           79  + */
           80  +
           81  +#define DIST_CODE_LEN  512 /* see definition of array dist_code below */
           82  +
           83  +#if defined(GEN_TREES_H) || !defined(STDC)
           84  +/* non ANSI compilers may not accept trees.h */
           85  +
           86  +local ct_data static_ltree[L_CODES+2];
           87  +/* The static literal tree. Since the bit lengths are imposed, there is no
           88  + * need for the L_CODES extra codes used during heap construction. However
           89  + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
           90  + * below).
           91  + */
           92  +
           93  +local ct_data static_dtree[D_CODES];
           94  +/* The static distance tree. (Actually a trivial tree since all codes use
           95  + * 5 bits.)
           96  + */
           97  +
           98  +uch _dist_code[DIST_CODE_LEN];
           99  +/* Distance codes. The first 256 values correspond to the distances
          100  + * 3 .. 258, the last 256 values correspond to the top 8 bits of
          101  + * the 15 bit distances.
          102  + */
          103  +
          104  +uch _length_code[MAX_MATCH-MIN_MATCH+1];
          105  +/* length code for each normalized match length (0 == MIN_MATCH) */
          106  +
          107  +local int base_length[LENGTH_CODES];
          108  +/* First normalized length for each code (0 = MIN_MATCH) */
          109  +
          110  +local int base_dist[D_CODES];
          111  +/* First normalized distance for each code (0 = distance of 1) */
          112  +
          113  +#else
          114  +#  include "trees.h"
          115  +#endif /* GEN_TREES_H */
          116  +
          117  +struct static_tree_desc_s {
          118  +    const ct_data *static_tree;  /* static tree or NULL */
          119  +    const intf *extra_bits;      /* extra bits for each code or NULL */
          120  +    int     extra_base;          /* base index for extra_bits */
          121  +    int     elems;               /* max number of elements in the tree */
          122  +    int     max_length;          /* max bit length for the codes */
          123  +};
          124  +
          125  +local static_tree_desc  static_l_desc =
          126  +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
          127  +
          128  +local static_tree_desc  static_d_desc =
          129  +{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
          130  +
          131  +local static_tree_desc  static_bl_desc =
          132  +{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};
          133  +
          134  +/* ===========================================================================
          135  + * Local (static) routines in this file.
          136  + */
          137  +
          138  +local void tr_static_init OF((void));
          139  +local void init_block     OF((deflate_state *s));
          140  +local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
          141  +local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
          142  +local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
          143  +local void build_tree     OF((deflate_state *s, tree_desc *desc));
          144  +local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
          145  +local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
          146  +local int  build_bl_tree  OF((deflate_state *s));
          147  +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
          148  +                              int blcodes));
          149  +local void compress_block OF((deflate_state *s, ct_data *ltree,
          150  +                              ct_data *dtree));
          151  +local int  detect_data_type OF((deflate_state *s));
          152  +local unsigned bi_reverse OF((unsigned value, int length));
          153  +local void bi_windup      OF((deflate_state *s));
          154  +local void bi_flush       OF((deflate_state *s));
          155  +local void copy_block     OF((deflate_state *s, charf *buf, unsigned len,
          156  +                              int header));
          157  +
          158  +#ifdef GEN_TREES_H
          159  +local void gen_trees_header OF((void));
          160  +#endif
          161  +
          162  +#ifndef DEBUG
          163  +#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
          164  +   /* Send a code of the given tree. c and tree must not have side effects */
          165  +
          166  +#else /* DEBUG */
          167  +#  define send_code(s, c, tree) \
          168  +     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
          169  +       send_bits(s, tree[c].Code, tree[c].Len); }
          170  +#endif
          171  +
          172  +/* ===========================================================================
          173  + * Output a short LSB first on the stream.
          174  + * IN assertion: there is enough room in pendingBuf.
          175  + */
          176  +#define put_short(s, w) { \
          177  +    put_byte(s, (uch)((w) & 0xff)); \
          178  +    put_byte(s, (uch)((ush)(w) >> 8)); \
          179  +}
          180  +
          181  +/* ===========================================================================
          182  + * Send a value on a given number of bits.
          183  + * IN assertion: length <= 16 and value fits in length bits.
          184  + */
          185  +#ifdef DEBUG
          186  +local void send_bits      OF((deflate_state *s, int value, int length));
          187  +
          188  +local void send_bits(s, value, length)
          189  +    deflate_state *s;
          190  +    int value;  /* value to send */
          191  +    int length; /* number of bits */
          192  +{
          193  +    Tracevv((stderr," l %2d v %4x ", length, value));
          194  +    Assert(length > 0 && length <= 15, "invalid length");
          195  +    s->bits_sent += (ulg)length;
          196  +
          197  +    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
          198  +     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
          199  +     * unused bits in value.
          200  +     */
          201  +    if (s->bi_valid > (int)Buf_size - length) {
          202  +        s->bi_buf |= (ush)value << s->bi_valid;
          203  +        put_short(s, s->bi_buf);
          204  +        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
          205  +        s->bi_valid += length - Buf_size;
          206  +    } else {
          207  +        s->bi_buf |= (ush)value << s->bi_valid;
          208  +        s->bi_valid += length;
          209  +    }
          210  +}
          211  +#else /* !DEBUG */
          212  +
          213  +#define send_bits(s, value, length) \
          214  +{ int len = length;\
          215  +  if (s->bi_valid > (int)Buf_size - len) {\
          216  +    int val = value;\
          217  +    s->bi_buf |= (ush)val << s->bi_valid;\
          218  +    put_short(s, s->bi_buf);\
          219  +    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
          220  +    s->bi_valid += len - Buf_size;\
          221  +  } else {\
          222  +    s->bi_buf |= (ush)(value) << s->bi_valid;\
          223  +    s->bi_valid += len;\
          224  +  }\
          225  +}
          226  +#endif /* DEBUG */
          227  +
          228  +
          229  +/* the arguments must not have side effects */
          230  +
          231  +/* ===========================================================================
          232  + * Initialize the various 'constant' tables.
          233  + */
          234  +local void tr_static_init()
          235  +{
          236  +#if defined(GEN_TREES_H) || !defined(STDC)
          237  +    static int static_init_done = 0;
          238  +    int n;        /* iterates over tree elements */
          239  +    int bits;     /* bit counter */
          240  +    int length;   /* length value */
          241  +    int code;     /* code value */
          242  +    int dist;     /* distance index */
          243  +    ush bl_count[MAX_BITS+1];
          244  +    /* number of codes at each bit length for an optimal tree */
          245  +
          246  +    if (static_init_done) return;
          247  +
          248  +    /* For some embedded targets, global variables are not initialized: */
          249  +#ifdef NO_INIT_GLOBAL_POINTERS
          250  +    static_l_desc.static_tree = static_ltree;
          251  +    static_l_desc.extra_bits = extra_lbits;
          252  +    static_d_desc.static_tree = static_dtree;
          253  +    static_d_desc.extra_bits = extra_dbits;
          254  +    static_bl_desc.extra_bits = extra_blbits;
          255  +#endif
          256  +
          257  +    /* Initialize the mapping length (0..255) -> length code (0..28) */
          258  +    length = 0;
          259  +    for (code = 0; code < LENGTH_CODES-1; code++) {
          260  +        base_length[code] = length;
          261  +        for (n = 0; n < (1<<extra_lbits[code]); n++) {
          262  +            _length_code[length++] = (uch)code;
          263  +        }
          264  +    }
          265  +    Assert (length == 256, "tr_static_init: length != 256");
          266  +    /* Note that the length 255 (match length 258) can be represented
          267  +     * in two different ways: code 284 + 5 bits or code 285, so we
          268  +     * overwrite length_code[255] to use the best encoding:
          269  +     */
          270  +    _length_code[length-1] = (uch)code;
          271  +
          272  +    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
          273  +    dist = 0;
          274  +    for (code = 0 ; code < 16; code++) {
          275  +        base_dist[code] = dist;
          276  +        for (n = 0; n < (1<<extra_dbits[code]); n++) {
          277  +            _dist_code[dist++] = (uch)code;
          278  +        }
          279  +    }
          280  +    Assert (dist == 256, "tr_static_init: dist != 256");
          281  +    dist >>= 7; /* from now on, all distances are divided by 128 */
          282  +    for ( ; code < D_CODES; code++) {
          283  +        base_dist[code] = dist << 7;
          284  +        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
          285  +            _dist_code[256 + dist++] = (uch)code;
          286  +        }
          287  +    }
          288  +    Assert (dist == 256, "tr_static_init: 256+dist != 512");
          289  +
          290  +    /* Construct the codes of the static literal tree */
          291  +    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
          292  +    n = 0;
          293  +    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
          294  +    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
          295  +    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
          296  +    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
          297  +    /* Codes 286 and 287 do not exist, but we must include them in the
          298  +     * tree construction to get a canonical Huffman tree (longest code
          299  +     * all ones)
          300  +     */
          301  +    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
          302  +
          303  +    /* The static distance tree is trivial: */
          304  +    for (n = 0; n < D_CODES; n++) {
          305  +        static_dtree[n].Len = 5;
          306  +        static_dtree[n].Code = bi_reverse((unsigned)n, 5);
          307  +    }
          308  +    static_init_done = 1;
          309  +
          310  +#  ifdef GEN_TREES_H
          311  +    gen_trees_header();
          312  +#  endif
          313  +#endif /* defined(GEN_TREES_H) || !defined(STDC) */
          314  +}
          315  +
          316  +/* ===========================================================================
          317  + * Genererate the file trees.h describing the static trees.
          318  + */
          319  +#ifdef GEN_TREES_H
          320  +#  ifndef DEBUG
          321  +#    include <stdio.h>
          322  +#  endif
          323  +
          324  +#  define SEPARATOR(i, last, width) \
          325  +      ((i) == (last)? "\n};\n\n" :    \
          326  +       ((i) % (width) == (width)-1 ? ",\n" : ", "))
          327  +
          328  +void gen_trees_header()
          329  +{
          330  +    FILE *header = fopen("trees.h", "w");
          331  +    int i;
          332  +
          333  +    Assert (header != NULL, "Can't open trees.h");
          334  +    fprintf(header,
          335  +            "/* header created automatically with -DGEN_TREES_H */\n\n");
          336  +
          337  +    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
          338  +    for (i = 0; i < L_CODES+2; i++) {
          339  +        fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
          340  +                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
          341  +    }
          342  +
          343  +    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
          344  +    for (i = 0; i < D_CODES; i++) {
          345  +        fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
          346  +                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
          347  +    }
          348  +
          349  +    fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n");
          350  +    for (i = 0; i < DIST_CODE_LEN; i++) {
          351  +        fprintf(header, "%2u%s", _dist_code[i],
          352  +                SEPARATOR(i, DIST_CODE_LEN-1, 20));
          353  +    }
          354  +
          355  +    fprintf(header,
          356  +        "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
          357  +    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
          358  +        fprintf(header, "%2u%s", _length_code[i],
          359  +                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
          360  +    }
          361  +
          362  +    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
          363  +    for (i = 0; i < LENGTH_CODES; i++) {
          364  +        fprintf(header, "%1u%s", base_length[i],
          365  +                SEPARATOR(i, LENGTH_CODES-1, 20));
          366  +    }
          367  +
          368  +    fprintf(header, "local const int base_dist[D_CODES] = {\n");
          369  +    for (i = 0; i < D_CODES; i++) {
          370  +        fprintf(header, "%5u%s", base_dist[i],
          371  +                SEPARATOR(i, D_CODES-1, 10));
          372  +    }
          373  +
          374  +    fclose(header);
          375  +}
          376  +#endif /* GEN_TREES_H */
          377  +
          378  +/* ===========================================================================
          379  + * Initialize the tree data structures for a new zlib stream.
          380  + */
          381  +void ZLIB_INTERNAL _tr_init(s)
          382  +    deflate_state *s;
          383  +{
          384  +    tr_static_init();
          385  +
          386  +    s->l_desc.dyn_tree = s->dyn_ltree;
          387  +    s->l_desc.stat_desc = &static_l_desc;
          388  +
          389  +    s->d_desc.dyn_tree = s->dyn_dtree;
          390  +    s->d_desc.stat_desc = &static_d_desc;
          391  +
          392  +    s->bl_desc.dyn_tree = s->bl_tree;
          393  +    s->bl_desc.stat_desc = &static_bl_desc;
          394  +
          395  +    s->bi_buf = 0;
          396  +    s->bi_valid = 0;
          397  +#ifdef DEBUG
          398  +    s->compressed_len = 0L;
          399  +    s->bits_sent = 0L;
          400  +#endif
          401  +
          402  +    /* Initialize the first block of the first file: */
          403  +    init_block(s);
          404  +}
          405  +
          406  +/* ===========================================================================
          407  + * Initialize a new block.
          408  + */
          409  +local void init_block(s)
          410  +    deflate_state *s;
          411  +{
          412  +    int n; /* iterates over tree elements */
          413  +
          414  +    /* Initialize the trees. */
          415  +    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
          416  +    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
          417  +    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
          418  +
          419  +    s->dyn_ltree[END_BLOCK].Freq = 1;
          420  +    s->opt_len = s->static_len = 0L;
          421  +    s->last_lit = s->matches = 0;
          422  +}
          423  +
          424  +#define SMALLEST 1
          425  +/* Index within the heap array of least frequent node in the Huffman tree */
          426  +
          427  +
          428  +/* ===========================================================================
          429  + * Remove the smallest element from the heap and recreate the heap with
          430  + * one less element. Updates heap and heap_len.
          431  + */
          432  +#define pqremove(s, tree, top) \
          433  +{\
          434  +    top = s->heap[SMALLEST]; \
          435  +    s->heap[SMALLEST] = s->heap[s->heap_len--]; \
          436  +    pqdownheap(s, tree, SMALLEST); \
          437  +}
          438  +
          439  +/* ===========================================================================
          440  + * Compares to subtrees, using the tree depth as tie breaker when
          441  + * the subtrees have equal frequency. This minimizes the worst case length.
          442  + */
          443  +#define smaller(tree, n, m, depth) \
          444  +   (tree[n].Freq < tree[m].Freq || \
          445  +   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
          446  +
          447  +/* ===========================================================================
          448  + * Restore the heap property by moving down the tree starting at node k,
          449  + * exchanging a node with the smallest of its two sons if necessary, stopping
          450  + * when the heap property is re-established (each father smaller than its
          451  + * two sons).
          452  + */
          453  +local void pqdownheap(s, tree, k)
          454  +    deflate_state *s;
          455  +    ct_data *tree;  /* the tree to restore */
          456  +    int k;               /* node to move down */
          457  +{
          458  +    int v = s->heap[k];
          459  +    int j = k << 1;  /* left son of k */
          460  +    while (j <= s->heap_len) {
          461  +        /* Set j to the smallest of the two sons: */
          462  +        if (j < s->heap_len &&
          463  +            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
          464  +            j++;
          465  +        }
          466  +        /* Exit if v is smaller than both sons */
          467  +        if (smaller(tree, v, s->heap[j], s->depth)) break;
          468  +
          469  +        /* Exchange v with the smallest son */
          470  +        s->heap[k] = s->heap[j];  k = j;
          471  +
          472  +        /* And continue down the tree, setting j to the left son of k */
          473  +        j <<= 1;
          474  +    }
          475  +    s->heap[k] = v;
          476  +}
          477  +
          478  +/* ===========================================================================
          479  + * Compute the optimal bit lengths for a tree and update the total bit length
          480  + * for the current block.
          481  + * IN assertion: the fields freq and dad are set, heap[heap_max] and
          482  + *    above are the tree nodes sorted by increasing frequency.
          483  + * OUT assertions: the field len is set to the optimal bit length, the
          484  + *     array bl_count contains the frequencies for each bit length.
          485  + *     The length opt_len is updated; static_len is also updated if stree is
          486  + *     not null.
          487  + */
          488  +local void gen_bitlen(s, desc)
          489  +    deflate_state *s;
          490  +    tree_desc *desc;    /* the tree descriptor */
          491  +{
          492  +    ct_data *tree        = desc->dyn_tree;
          493  +    int max_code         = desc->max_code;
          494  +    const ct_data *stree = desc->stat_desc->static_tree;
          495  +    const intf *extra    = desc->stat_desc->extra_bits;
          496  +    int base             = desc->stat_desc->extra_base;
          497  +    int max_length       = desc->stat_desc->max_length;
          498  +    int h;              /* heap index */
          499  +    int n, m;           /* iterate over the tree elements */
          500  +    int bits;           /* bit length */
          501  +    int xbits;          /* extra bits */
          502  +    ush f;              /* frequency */
          503  +    int overflow = 0;   /* number of elements with bit length too large */
          504  +
          505  +    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
          506  +
          507  +    /* In a first pass, compute the optimal bit lengths (which may
          508  +     * overflow in the case of the bit length tree).
          509  +     */
          510  +    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
          511  +
          512  +    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
          513  +        n = s->heap[h];
          514  +        bits = tree[tree[n].Dad].Len + 1;
          515  +        if (bits > max_length) bits = max_length, overflow++;
          516  +        tree[n].Len = (ush)bits;
          517  +        /* We overwrite tree[n].Dad which is no longer needed */
          518  +
          519  +        if (n > max_code) continue; /* not a leaf node */
          520  +
          521  +        s->bl_count[bits]++;
          522  +        xbits = 0;
          523  +        if (n >= base) xbits = extra[n-base];
          524  +        f = tree[n].Freq;
          525  +        s->opt_len += (ulg)f * (bits + xbits);
          526  +        if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
          527  +    }
          528  +    if (overflow == 0) return;
          529  +
          530  +    Trace((stderr,"\nbit length overflow\n"));
          531  +    /* This happens for example on obj2 and pic of the Calgary corpus */
          532  +
          533  +    /* Find the first bit length which could increase: */
          534  +    do {
          535  +        bits = max_length-1;
          536  +        while (s->bl_count[bits] == 0) bits--;
          537  +        s->bl_count[bits]--;      /* move one leaf down the tree */
          538  +        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
          539  +        s->bl_count[max_length]--;
          540  +        /* The brother of the overflow item also moves one step up,
          541  +         * but this does not affect bl_count[max_length]
          542  +         */
          543  +        overflow -= 2;
          544  +    } while (overflow > 0);
          545  +
          546  +    /* Now recompute all bit lengths, scanning in increasing frequency.
          547  +     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
          548  +     * lengths instead of fixing only the wrong ones. This idea is taken
          549  +     * from 'ar' written by Haruhiko Okumura.)
          550  +     */
          551  +    for (bits = max_length; bits != 0; bits--) {
          552  +        n = s->bl_count[bits];
          553  +        while (n != 0) {
          554  +            m = s->heap[--h];
          555  +            if (m > max_code) continue;
          556  +            if ((unsigned) tree[m].Len != (unsigned) bits) {
          557  +                Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
          558  +                s->opt_len += ((long)bits - (long)tree[m].Len)
          559  +                              *(long)tree[m].Freq;
          560  +                tree[m].Len = (ush)bits;
          561  +            }
          562  +            n--;
          563  +        }
          564  +    }
          565  +}
          566  +
          567  +/* ===========================================================================
          568  + * Generate the codes for a given tree and bit counts (which need not be
          569  + * optimal).
          570  + * IN assertion: the array bl_count contains the bit length statistics for
          571  + * the given tree and the field len is set for all tree elements.
          572  + * OUT assertion: the field code is set for all tree elements of non
          573  + *     zero code length.
          574  + */
          575  +local void gen_codes (tree, max_code, bl_count)
          576  +    ct_data *tree;             /* the tree to decorate */
          577  +    int max_code;              /* largest code with non zero frequency */
          578  +    ushf *bl_count;            /* number of codes at each bit length */
          579  +{
          580  +    ush next_code[MAX_BITS+1]; /* next code value for each bit length */
          581  +    ush code = 0;              /* running code value */
          582  +    int bits;                  /* bit index */
          583  +    int n;                     /* code index */
          584  +
          585  +    /* The distribution counts are first used to generate the code values
          586  +     * without bit reversal.
          587  +     */
          588  +    for (bits = 1; bits <= MAX_BITS; bits++) {
          589  +        next_code[bits] = code = (code + bl_count[bits-1]) << 1;
          590  +    }
          591  +    /* Check that the bit counts in bl_count are consistent. The last code
          592  +     * must be all ones.
          593  +     */
          594  +    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
          595  +            "inconsistent bit counts");
          596  +    Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
          597  +
          598  +    for (n = 0;  n <= max_code; n++) {
          599  +        int len = tree[n].Len;
          600  +        if (len == 0) continue;
          601  +        /* Now reverse the bits */
          602  +        tree[n].Code = bi_reverse(next_code[len]++, len);
          603  +
          604  +        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
          605  +             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
          606  +    }
          607  +}
          608  +
          609  +/* ===========================================================================
          610  + * Construct one Huffman tree and assigns the code bit strings and lengths.
          611  + * Update the total bit length for the current block.
          612  + * IN assertion: the field freq is set for all tree elements.
          613  + * OUT assertions: the fields len and code are set to the optimal bit length
          614  + *     and corresponding code. The length opt_len is updated; static_len is
          615  + *     also updated if stree is not null. The field max_code is set.
          616  + */
          617  +local void build_tree(s, desc)
          618  +    deflate_state *s;
          619  +    tree_desc *desc; /* the tree descriptor */
          620  +{
          621  +    ct_data *tree         = desc->dyn_tree;
          622  +    const ct_data *stree  = desc->stat_desc->static_tree;
          623  +    int elems             = desc->stat_desc->elems;
          624  +    int n, m;          /* iterate over heap elements */
          625  +    int max_code = -1; /* largest code with non zero frequency */
          626  +    int node;          /* new node being created */
          627  +
          628  +    /* Construct the initial heap, with least frequent element in
          629  +     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
          630  +     * heap[0] is not used.
          631  +     */
          632  +    s->heap_len = 0, s->heap_max = HEAP_SIZE;
          633  +
          634  +    for (n = 0; n < elems; n++) {
          635  +        if (tree[n].Freq != 0) {
          636  +            s->heap[++(s->heap_len)] = max_code = n;
          637  +            s->depth[n] = 0;
          638  +        } else {
          639  +            tree[n].Len = 0;
          640  +        }
          641  +    }
          642  +
          643  +    /* The pkzip format requires that at least one distance code exists,
          644  +     * and that at least one bit should be sent even if there is only one
          645  +     * possible code. So to avoid special checks later on we force at least
          646  +     * two codes of non zero frequency.
          647  +     */
          648  +    while (s->heap_len < 2) {
          649  +        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
          650  +        tree[node].Freq = 1;
          651  +        s->depth[node] = 0;
          652  +        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
          653  +        /* node is 0 or 1 so it does not have extra bits */
          654  +    }
          655  +    desc->max_code = max_code;
          656  +
          657  +    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
          658  +     * establish sub-heaps of increasing lengths:
          659  +     */
          660  +    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
          661  +
          662  +    /* Construct the Huffman tree by repeatedly combining the least two
          663  +     * frequent nodes.
          664  +     */
          665  +    node = elems;              /* next internal node of the tree */
          666  +    do {
          667  +        pqremove(s, tree, n);  /* n = node of least frequency */
          668  +        m = s->heap[SMALLEST]; /* m = node of next least frequency */
          669  +
          670  +        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
          671  +        s->heap[--(s->heap_max)] = m;
          672  +
          673  +        /* Create a new node father of n and m */
          674  +        tree[node].Freq = tree[n].Freq + tree[m].Freq;
          675  +        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
          676  +                                s->depth[n] : s->depth[m]) + 1);
          677  +        tree[n].Dad = tree[m].Dad = (ush)node;
          678  +#ifdef DUMP_BL_TREE
          679  +        if (tree == s->bl_tree) {
          680  +            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
          681  +                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
          682  +        }
          683  +#endif
          684  +        /* and insert the new node in the heap */
          685  +        s->heap[SMALLEST] = node++;
          686  +        pqdownheap(s, tree, SMALLEST);
          687  +
          688  +    } while (s->heap_len >= 2);
          689  +
          690  +    s->heap[--(s->heap_max)] = s->heap[SMALLEST];
          691  +
          692  +    /* At this point, the fields freq and dad are set. We can now
          693  +     * generate the bit lengths.
          694  +     */
          695  +    gen_bitlen(s, (tree_desc *)desc);
          696  +
          697  +    /* The field len is now set, we can generate the bit codes */
          698  +    gen_codes ((ct_data *)tree, max_code, s->bl_count);
          699  +}
          700  +
          701  +/* ===========================================================================
          702  + * Scan a literal or distance tree to determine the frequencies of the codes
          703  + * in the bit length tree.
          704  + */
          705  +local void scan_tree (s, tree, max_code)
          706  +    deflate_state *s;
          707  +    ct_data *tree;   /* the tree to be scanned */
          708  +    int max_code;    /* and its largest code of non zero frequency */
          709  +{
          710  +    int n;                     /* iterates over all tree elements */
          711  +    int prevlen = -1;          /* last emitted length */
          712  +    int curlen;                /* length of current code */
          713  +    int nextlen = tree[0].Len; /* length of next code */
          714  +    int count = 0;             /* repeat count of the current code */
          715  +    int max_count = 7;         /* max repeat count */
          716  +    int min_count = 4;         /* min repeat count */
          717  +
          718  +    if (nextlen == 0) max_count = 138, min_count = 3;
          719  +    tree[max_code+1].Len = (ush)0xffff; /* guard */
          720  +
          721  +    for (n = 0; n <= max_code; n++) {
          722  +        curlen = nextlen; nextlen = tree[n+1].Len;
          723  +        if (++count < max_count && curlen == nextlen) {
          724  +            continue;
          725  +        } else if (count < min_count) {
          726  +            s->bl_tree[curlen].Freq += count;
          727  +        } else if (curlen != 0) {
          728  +            if (curlen != prevlen) s->bl_tree[curlen].Freq++;
          729  +            s->bl_tree[REP_3_6].Freq++;
          730  +        } else if (count <= 10) {
          731  +            s->bl_tree[REPZ_3_10].Freq++;
          732  +        } else {
          733  +            s->bl_tree[REPZ_11_138].Freq++;
          734  +        }
          735  +        count = 0; prevlen = curlen;
          736  +        if (nextlen == 0) {
          737  +            max_count = 138, min_count = 3;
          738  +        } else if (curlen == nextlen) {
          739  +            max_count = 6, min_count = 3;
          740  +        } else {
          741  +            max_count = 7, min_count = 4;
          742  +        }
          743  +    }
          744  +}
          745  +
          746  +/* ===========================================================================
          747  + * Send a literal or distance tree in compressed form, using the codes in
          748  + * bl_tree.
          749  + */
          750  +local void send_tree (s, tree, max_code)
          751  +    deflate_state *s;
          752  +    ct_data *tree; /* the tree to be scanned */
          753  +    int max_code;       /* and its largest code of non zero frequency */
          754  +{
          755  +    int n;                     /* iterates over all tree elements */
          756  +    int prevlen = -1;          /* last emitted length */
          757  +    int curlen;                /* length of current code */
          758  +    int nextlen = tree[0].Len; /* length of next code */
          759  +    int count = 0;             /* repeat count of the current code */
          760  +    int max_count = 7;         /* max repeat count */
          761  +    int min_count = 4;         /* min repeat count */
          762  +
          763  +    /* tree[max_code+1].Len = -1; */  /* guard already set */
          764  +    if (nextlen == 0) max_count = 138, min_count = 3;
          765  +
          766  +    for (n = 0; n <= max_code; n++) {
          767  +        curlen = nextlen; nextlen = tree[n+1].Len;
          768  +        if (++count < max_count && curlen == nextlen) {
          769  +            continue;
          770  +        } else if (count < min_count) {
          771  +            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
          772  +
          773  +        } else if (curlen != 0) {
          774  +            if (curlen != prevlen) {
          775  +                send_code(s, curlen, s->bl_tree); count--;
          776  +            }
          777  +            Assert(count >= 3 && count <= 6, " 3_6?");
          778  +            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
          779  +
          780  +        } else if (count <= 10) {
          781  +            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
          782  +
          783  +        } else {
          784  +            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
          785  +        }
          786  +        count = 0; prevlen = curlen;
          787  +        if (nextlen == 0) {
          788  +            max_count = 138, min_count = 3;
          789  +        } else if (curlen == nextlen) {
          790  +            max_count = 6, min_count = 3;
          791  +        } else {
          792  +            max_count = 7, min_count = 4;
          793  +        }
          794  +    }
          795  +}
          796  +
          797  +/* ===========================================================================
          798  + * Construct the Huffman tree for the bit lengths and return the index in
          799  + * bl_order of the last bit length code to send.
          800  + */
          801  +local int build_bl_tree(s)
          802  +    deflate_state *s;
          803  +{
          804  +    int max_blindex;  /* index of last bit length code of non zero freq */
          805  +
          806  +    /* Determine the bit length frequencies for literal and distance trees */
          807  +    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
          808  +    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
          809  +
          810  +    /* Build the bit length tree: */
          811  +    build_tree(s, (tree_desc *)(&(s->bl_desc)));
          812  +    /* opt_len now includes the length of the tree representations, except
          813  +     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
          814  +     */
          815  +
          816  +    /* Determine the number of bit length codes to send. The pkzip format
          817  +     * requires that at least 4 bit length codes be sent. (appnote.txt says
          818  +     * 3 but the actual value used is 4.)
          819  +     */
          820  +    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
          821  +        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
          822  +    }
          823  +    /* Update opt_len to include the bit length tree and counts */
          824  +    s->opt_len += 3*(max_blindex+1) + 5+5+4;
          825  +    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
          826  +            s->opt_len, s->static_len));
          827  +
          828  +    return max_blindex;
          829  +}
          830  +
          831  +/* ===========================================================================
          832  + * Send the header for a block using dynamic Huffman trees: the counts, the
          833  + * lengths of the bit length codes, the literal tree and the distance tree.
          834  + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
          835  + */
          836  +local void send_all_trees(s, lcodes, dcodes, blcodes)
          837  +    deflate_state *s;
          838  +    int lcodes, dcodes, blcodes; /* number of codes for each tree */
          839  +{
          840  +    int rank;                    /* index in bl_order */
          841  +
          842  +    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
          843  +    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
          844  +            "too many codes");
          845  +    Tracev((stderr, "\nbl counts: "));
          846  +    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
          847  +    send_bits(s, dcodes-1,   5);
          848  +    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
          849  +    for (rank = 0; rank < blcodes; rank++) {
          850  +        Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
          851  +        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
          852  +    }
          853  +    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
          854  +
          855  +    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
          856  +    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
          857  +
          858  +    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
          859  +    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
          860  +}
          861  +
          862  +/* ===========================================================================
          863  + * Send a stored block
          864  + */
          865  +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
          866  +    deflate_state *s;
          867  +    charf *buf;       /* input block */
          868  +    ulg stored_len;   /* length of input block */
          869  +    int last;         /* one if this is the last block for a file */
          870  +{
          871  +    send_bits(s, (STORED_BLOCK<<1)+last, 3);    /* send block type */
          872  +#ifdef DEBUG
          873  +    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
          874  +    s->compressed_len += (stored_len + 4) << 3;
          875  +#endif
          876  +    copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
          877  +}
          878  +
          879  +/* ===========================================================================
          880  + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
          881  + */
          882  +void ZLIB_INTERNAL _tr_flush_bits(s)
          883  +    deflate_state *s;
          884  +{
          885  +    bi_flush(s);
          886  +}
          887  +
          888  +/* ===========================================================================
          889  + * Send one empty static block to give enough lookahead for inflate.
          890  + * This takes 10 bits, of which 7 may remain in the bit buffer.
          891  + */
          892  +void ZLIB_INTERNAL _tr_align(s)
          893  +    deflate_state *s;
          894  +{
          895  +    send_bits(s, STATIC_TREES<<1, 3);
          896  +    send_code(s, END_BLOCK, static_ltree);
          897  +#ifdef DEBUG
          898  +    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
          899  +#endif
          900  +    bi_flush(s);
          901  +}
          902  +
          903  +/* ===========================================================================
          904  + * Determine the best encoding for the current block: dynamic trees, static
          905  + * trees or store, and output the encoded block to the zip file.
          906  + */
          907  +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
          908  +    deflate_state *s;
          909  +    charf *buf;       /* input block, or NULL if too old */
          910  +    ulg stored_len;   /* length of input block */
          911  +    int last;         /* one if this is the last block for a file */
          912  +{
          913  +    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
          914  +    int max_blindex = 0;  /* index of last bit length code of non zero freq */
          915  +
          916  +    /* Build the Huffman trees unless a stored block is forced */
          917  +    if (s->level > 0) {
          918  +
          919  +        /* Check if the file is binary or text */
          920  +        if (s->strm->data_type == Z_UNKNOWN)
          921  +            s->strm->data_type = detect_data_type(s);
          922  +
          923  +        /* Construct the literal and distance trees */
          924  +        build_tree(s, (tree_desc *)(&(s->l_desc)));
          925  +        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
          926  +                s->static_len));
          927  +
          928  +        build_tree(s, (tree_desc *)(&(s->d_desc)));
          929  +        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
          930  +                s->static_len));
          931  +        /* At this point, opt_len and static_len are the total bit lengths of
          932  +         * the compressed block data, excluding the tree representations.
          933  +         */
          934  +
          935  +        /* Build the bit length tree for the above two trees, and get the index
          936  +         * in bl_order of the last bit length code to send.
          937  +         */
          938  +        max_blindex = build_bl_tree(s);
          939  +
          940  +        /* Determine the best encoding. Compute the block lengths in bytes. */
          941  +        opt_lenb = (s->opt_len+3+7)>>3;
          942  +        static_lenb = (s->static_len+3+7)>>3;
          943  +
          944  +        Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
          945  +                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
          946  +                s->last_lit));
          947  +
          948  +        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
          949  +
          950  +    } else {
          951  +        Assert(buf != (char*)0, "lost buf");
          952  +        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
          953  +    }
          954  +
          955  +#ifdef FORCE_STORED
          956  +    if (buf != (char*)0) { /* force stored block */
          957  +#else
          958  +    if (stored_len+4 <= opt_lenb && buf != (char*)0) {
          959  +                       /* 4: two words for the lengths */
          960  +#endif
          961  +        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
          962  +         * Otherwise we can't have processed more than WSIZE input bytes since
          963  +         * the last block flush, because compression would have been
          964  +         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
          965  +         * transform a block into a stored block.
          966  +         */
          967  +        _tr_stored_block(s, buf, stored_len, last);
          968  +
          969  +#ifdef FORCE_STATIC
          970  +    } else if (static_lenb >= 0) { /* force static trees */
          971  +#else
          972  +    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
          973  +#endif
          974  +        send_bits(s, (STATIC_TREES<<1)+last, 3);
          975  +        compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
          976  +#ifdef DEBUG
          977  +        s->compressed_len += 3 + s->static_len;
          978  +#endif
          979  +    } else {
          980  +        send_bits(s, (DYN_TREES<<1)+last, 3);
          981  +        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
          982  +                       max_blindex+1);
          983  +        compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
          984  +#ifdef DEBUG
          985  +        s->compressed_len += 3 + s->opt_len;
          986  +#endif
          987  +    }
          988  +    Assert (s->compressed_len == s->bits_sent, "bad compressed size");
          989  +    /* The above check is made mod 2^32, for files larger than 512 MB
          990  +     * and uLong implemented on 32 bits.
          991  +     */
          992  +    init_block(s);
          993  +
          994  +    if (last) {
          995  +        bi_windup(s);
          996  +#ifdef DEBUG
          997  +        s->compressed_len += 7;  /* align on byte boundary */
          998  +#endif
          999  +    }
         1000  +    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
         1001  +           s->compressed_len-7*last));
         1002  +}
         1003  +
         1004  +/* ===========================================================================
         1005  + * Save the match info and tally the frequency counts. Return true if
         1006  + * the current block must be flushed.
         1007  + */
         1008  +int ZLIB_INTERNAL _tr_tally (s, dist, lc)
         1009  +    deflate_state *s;
         1010  +    unsigned dist;  /* distance of matched string */
         1011  +    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
         1012  +{
         1013  +    s->d_buf[s->last_lit] = (ush)dist;
         1014  +    s->l_buf[s->last_lit++] = (uch)lc;
         1015  +    if (dist == 0) {
         1016  +        /* lc is the unmatched char */
         1017  +        s->dyn_ltree[lc].Freq++;
         1018  +    } else {
         1019  +        s->matches++;
         1020  +        /* Here, lc is the match length - MIN_MATCH */
         1021  +        dist--;             /* dist = match distance - 1 */
         1022  +        Assert((ush)dist < (ush)MAX_DIST(s) &&
         1023  +               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
         1024  +               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");
         1025  +
         1026  +        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
         1027  +        s->dyn_dtree[d_code(dist)].Freq++;
         1028  +    }
         1029  +
         1030  +#ifdef TRUNCATE_BLOCK
         1031  +    /* Try to guess if it is profitable to stop the current block here */
         1032  +    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
         1033  +        /* Compute an upper bound for the compressed length */
         1034  +        ulg out_length = (ulg)s->last_lit*8L;
         1035  +        ulg in_length = (ulg)((long)s->strstart - s->block_start);
         1036  +        int dcode;
         1037  +        for (dcode = 0; dcode < D_CODES; dcode++) {
         1038  +            out_length += (ulg)s->dyn_dtree[dcode].Freq *
         1039  +                (5L+extra_dbits[dcode]);
         1040  +        }
         1041  +        out_length >>= 3;
         1042  +        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
         1043  +               s->last_lit, in_length, out_length,
         1044  +               100L - out_length*100L/in_length));
         1045  +        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
         1046  +    }
         1047  +#endif
         1048  +    return (s->last_lit == s->lit_bufsize-1);
         1049  +    /* We avoid equality with lit_bufsize because of wraparound at 64K
         1050  +     * on 16 bit machines and because stored blocks are restricted to
         1051  +     * 64K-1 bytes.
         1052  +     */
         1053  +}
         1054  +
         1055  +/* ===========================================================================
         1056  + * Send the block data compressed using the given Huffman trees
         1057  + */
         1058  +local void compress_block(s, ltree, dtree)
         1059  +    deflate_state *s;
         1060  +    ct_data *ltree; /* literal tree */
         1061  +    ct_data *dtree; /* distance tree */
         1062  +{
         1063  +    unsigned dist;      /* distance of matched string */
         1064  +    int lc;             /* match length or unmatched char (if dist == 0) */
         1065  +    unsigned lx = 0;    /* running index in l_buf */
         1066  +    unsigned code;      /* the code to send */
         1067  +    int extra;          /* number of extra bits to send */
         1068  +
         1069  +    if (s->last_lit != 0) do {
         1070  +        dist = s->d_buf[lx];
         1071  +        lc = s->l_buf[lx++];
         1072  +        if (dist == 0) {
         1073  +            send_code(s, lc, ltree); /* send a literal byte */
         1074  +            Tracecv(isgraph(lc), (stderr," '%c' ", lc));
         1075  +        } else {
         1076  +            /* Here, lc is the match length - MIN_MATCH */
         1077  +            code = _length_code[lc];
         1078  +            send_code(s, code+LITERALS+1, ltree); /* send the length code */
         1079  +            extra = extra_lbits[code];
         1080  +            if (extra != 0) {
         1081  +                lc -= base_length[code];
         1082  +                send_bits(s, lc, extra);       /* send the extra length bits */
         1083  +            }
         1084  +            dist--; /* dist is now the match distance - 1 */
         1085  +            code = d_code(dist);
         1086  +            Assert (code < D_CODES, "bad d_code");
         1087  +
         1088  +            send_code(s, code, dtree);       /* send the distance code */
         1089  +            extra = extra_dbits[code];
         1090  +            if (extra != 0) {
         1091  +                dist -= base_dist[code];
         1092  +                send_bits(s, dist, extra);   /* send the extra distance bits */
         1093  +            }
         1094  +        } /* literal or match pair ? */
         1095  +
         1096  +        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
         1097  +        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
         1098  +               "pendingBuf overflow");
         1099  +
         1100  +    } while (lx < s->last_lit);
         1101  +
         1102  +    send_code(s, END_BLOCK, ltree);
         1103  +}
         1104  +
         1105  +/* ===========================================================================
         1106  + * Check if the data type is TEXT or BINARY, using the following algorithm:
         1107  + * - TEXT if the two conditions below are satisfied:
         1108  + *    a) There are no non-portable control characters belonging to the
         1109  + *       "black list" (0..6, 14..25, 28..31).
         1110  + *    b) There is at least one printable character belonging to the
         1111  + *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
         1112  + * - BINARY otherwise.
         1113  + * - The following partially-portable control characters form a
         1114  + *   "gray list" that is ignored in this detection algorithm:
         1115  + *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
         1116  + * IN assertion: the fields Freq of dyn_ltree are set.
         1117  + */
         1118  +local int detect_data_type(s)
         1119  +    deflate_state *s;
         1120  +{
         1121  +    /* black_mask is the bit mask of black-listed bytes
         1122  +     * set bits 0..6, 14..25, and 28..31
         1123  +     * 0xf3ffc07f = binary 11110011111111111100000001111111
         1124  +     */
         1125  +    unsigned long black_mask = 0xf3ffc07fUL;
         1126  +    int n;
         1127  +
         1128  +    /* Check for non-textual ("black-listed") bytes. */
         1129  +    for (n = 0; n <= 31; n++, black_mask >>= 1)
         1130  +        if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
         1131  +            return Z_BINARY;
         1132  +
         1133  +    /* Check for textual ("white-listed") bytes. */
         1134  +    if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
         1135  +            || s->dyn_ltree[13].Freq != 0)
         1136  +        return Z_TEXT;
         1137  +    for (n = 32; n < LITERALS; n++)
         1138  +        if (s->dyn_ltree[n].Freq != 0)
         1139  +            return Z_TEXT;
         1140  +
         1141  +    /* There are no "black-listed" or "white-listed" bytes:
         1142  +     * this stream either is empty or has tolerated ("gray-listed") bytes only.
         1143  +     */
         1144  +    return Z_BINARY;
         1145  +}
         1146  +
         1147  +/* ===========================================================================
         1148  + * Reverse the first len bits of a code, using straightforward code (a faster
         1149  + * method would use a table)
         1150  + * IN assertion: 1 <= len <= 15
         1151  + */
         1152  +local unsigned bi_reverse(code, len)
         1153  +    unsigned code; /* the value to invert */
         1154  +    int len;       /* its bit length */
         1155  +{
         1156  +    register unsigned res = 0;
         1157  +    do {
         1158  +        res |= code & 1;
         1159  +        code >>= 1, res <<= 1;
         1160  +    } while (--len > 0);
         1161  +    return res >> 1;
         1162  +}
         1163  +
         1164  +/* ===========================================================================
         1165  + * Flush the bit buffer, keeping at most 7 bits in it.
         1166  + */
         1167  +local void bi_flush(s)
         1168  +    deflate_state *s;
         1169  +{
         1170  +    if (s->bi_valid == 16) {
         1171  +        put_short(s, s->bi_buf);
         1172  +        s->bi_buf = 0;
         1173  +        s->bi_valid = 0;
         1174  +    } else if (s->bi_valid >= 8) {
         1175  +        put_byte(s, (Byte)s->bi_buf);
         1176  +        s->bi_buf >>= 8;
         1177  +        s->bi_valid -= 8;
         1178  +    }
         1179  +}
         1180  +
         1181  +/* ===========================================================================
         1182  + * Flush the bit buffer and align the output on a byte boundary
         1183  + */
         1184  +local void bi_windup(s)
         1185  +    deflate_state *s;
         1186  +{
         1187  +    if (s->bi_valid > 8) {
         1188  +        put_short(s, s->bi_buf);
         1189  +    } else if (s->bi_valid > 0) {
         1190  +        put_byte(s, (Byte)s->bi_buf);
         1191  +    }
         1192  +    s->bi_buf = 0;
         1193  +    s->bi_valid = 0;
         1194  +#ifdef DEBUG
         1195  +    s->bits_sent = (s->bits_sent+7) & ~7;
         1196  +#endif
         1197  +}
         1198  +
         1199  +/* ===========================================================================
         1200  + * Copy a stored block, storing first the length and its
         1201  + * one's complement if requested.
         1202  + */
         1203  +local void copy_block(s, buf, len, header)
         1204  +    deflate_state *s;
         1205  +    charf    *buf;    /* the input data */
         1206  +    unsigned len;     /* its length */
         1207  +    int      header;  /* true if block header must be written */
         1208  +{
         1209  +    bi_windup(s);        /* align on byte boundary */
         1210  +
         1211  +    if (header) {
         1212  +        put_short(s, (ush)len);
         1213  +        put_short(s, (ush)~len);
         1214  +#ifdef DEBUG
         1215  +        s->bits_sent += 2*16;
         1216  +#endif
         1217  +    }
         1218  +#ifdef DEBUG
         1219  +    s->bits_sent += (ulg)len<<3;
         1220  +#endif
         1221  +    while (len--) {
         1222  +        put_byte(s, *buf++);
         1223  +    }
         1224  +}

Added compat/zlib/trees.h.

            1  +/* header created automatically with -DGEN_TREES_H */
            2  +
            3  +local const ct_data static_ltree[L_CODES+2] = {
            4  +{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
            5  +{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
            6  +{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
            7  +{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
            8  +{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
            9  +{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
           10  +{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
           11  +{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
           12  +{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
           13  +{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
           14  +{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
           15  +{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
           16  +{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
           17  +{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
           18  +{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
           19  +{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
           20  +{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
           21  +{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
           22  +{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
           23  +{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
           24  +{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
           25  +{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
           26  +{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
           27  +{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
           28  +{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
           29  +{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
           30  +{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
           31  +{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
           32  +{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
           33  +{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
           34  +{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
           35  +{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
           36  +{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
           37  +{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
           38  +{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
           39  +{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
           40  +{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
           41  +{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
           42  +{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
           43  +{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
           44  +{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
           45  +{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
           46  +{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
           47  +{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
           48  +{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
           49  +{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
           50  +{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
           51  +{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
           52  +{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
           53  +{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
           54  +{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
           55  +{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
           56  +{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
           57  +{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
           58  +{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
           59  +{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
           60  +{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
           61  +{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
           62  +};
           63  +
           64  +local const ct_data static_dtree[D_CODES] = {
           65  +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
           66  +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
           67  +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
           68  +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
           69  +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
           70  +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
           71  +};
           72  +
           73  +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
           74  + 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
           75  + 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
           76  +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
           77  +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
           78  +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
           79  +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
           80  +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
           81  +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
           82  +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
           83  +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
           84  +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
           85  +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
           86  +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
           87  +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
           88  +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
           89  +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
           90  +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
           91  +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
           92  +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
           93  +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
           94  +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
           95  +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
           96  +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
           97  +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
           98  +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
           99  +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
          100  +};
          101  +
          102  +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
          103  + 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
          104  +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
          105  +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
          106  +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
          107  +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
          108  +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
          109  +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
          110  +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
          111  +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
          112  +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
          113  +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
          114  +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
          115  +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
          116  +};
          117  +
          118  +local const int base_length[LENGTH_CODES] = {
          119  +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
          120  +64, 80, 96, 112, 128, 160, 192, 224, 0
          121  +};
          122  +
          123  +local const int base_dist[D_CODES] = {
          124  +    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
          125  +   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
          126  + 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
          127  +};
          128  +

Added compat/zlib/uncompr.c.

            1  +/* uncompr.c -- decompress a memory buffer
            2  + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#define ZLIB_INTERNAL
            9  +#include "zlib.h"
           10  +
           11  +/* ===========================================================================
           12  +     Decompresses the source buffer into the destination buffer.  sourceLen is
           13  +   the byte length of the source buffer. Upon entry, destLen is the total
           14  +   size of the destination buffer, which must be large enough to hold the
           15  +   entire uncompressed data. (The size of the uncompressed data must have
           16  +   been saved previously by the compressor and transmitted to the decompressor
           17  +   by some mechanism outside the scope of this compression library.)
           18  +   Upon exit, destLen is the actual size of the compressed buffer.
           19  +
           20  +     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
           21  +   enough memory, Z_BUF_ERROR if there was not enough room in the output
           22  +   buffer, or Z_DATA_ERROR if the input data was corrupted.
           23  +*/
           24  +int ZEXPORT uncompress (dest, destLen, source, sourceLen)
           25  +    Bytef *dest;
           26  +    uLongf *destLen;
           27  +    const Bytef *source;
           28  +    uLong sourceLen;
           29  +{
           30  +    z_stream stream;
           31  +    int err;
           32  +
           33  +    stream.next_in = (Bytef*)source;
           34  +    stream.avail_in = (uInt)sourceLen;
           35  +    /* Check for source > 64K on 16-bit machine: */
           36  +    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
           37  +
           38  +    stream.next_out = dest;
           39  +    stream.avail_out = (uInt)*destLen;
           40  +    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
           41  +
           42  +    stream.zalloc = (alloc_func)0;
           43  +    stream.zfree = (free_func)0;
           44  +
           45  +    err = inflateInit(&stream);
           46  +    if (err != Z_OK) return err;
           47  +
           48  +    err = inflate(&stream, Z_FINISH);
           49  +    if (err != Z_STREAM_END) {
           50  +        inflateEnd(&stream);
           51  +        if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
           52  +            return Z_DATA_ERROR;
           53  +        return err;
           54  +    }
           55  +    *destLen = stream.total_out;
           56  +
           57  +    err = inflateEnd(&stream);
           58  +    return err;
           59  +}

Added compat/zlib/watcom/watcom_f.mak.

            1  +# Makefile for zlib
            2  +# OpenWatcom flat model
            3  +# Last updated: 28-Dec-2005
            4  +
            5  +# To use, do "wmake -f watcom_f.mak"
            6  +
            7  +C_SOURCE =  adler32.c  compress.c crc32.c   deflate.c    &
            8  +	    gzclose.c  gzlib.c    gzread.c  gzwrite.c    &
            9  +            infback.c  inffast.c  inflate.c inftrees.c   &
           10  +            trees.c    uncompr.c  zutil.c
           11  +
           12  +OBJS =      adler32.obj  compress.obj crc32.obj   deflate.obj    &
           13  +	    gzclose.obj  gzlib.obj    gzread.obj  gzwrite.obj    &
           14  +            infback.obj  inffast.obj  inflate.obj inftrees.obj   &
           15  +            trees.obj    uncompr.obj  zutil.obj
           16  +
           17  +CC       = wcc386
           18  +LINKER   = wcl386
           19  +CFLAGS   = -zq -mf -3r -fp3 -s -bt=dos -oilrtfm -fr=nul -wx
           20  +ZLIB_LIB = zlib_f.lib
           21  +
           22  +.C.OBJ:
           23  +        $(CC) $(CFLAGS) $[@
           24  +
           25  +all: $(ZLIB_LIB) example.exe minigzip.exe
           26  +
           27  +$(ZLIB_LIB): $(OBJS)
           28  +	wlib -b -c $(ZLIB_LIB) -+adler32.obj  -+compress.obj -+crc32.obj
           29  +	wlib -b -c $(ZLIB_LIB) -+gzclose.obj  -+gzlib.obj    -+gzread.obj   -+gzwrite.obj
           30  +        wlib -b -c $(ZLIB_LIB) -+deflate.obj  -+infback.obj
           31  +        wlib -b -c $(ZLIB_LIB) -+inffast.obj  -+inflate.obj  -+inftrees.obj
           32  +        wlib -b -c $(ZLIB_LIB) -+trees.obj    -+uncompr.obj  -+zutil.obj
           33  +
           34  +example.exe: $(ZLIB_LIB) example.obj
           35  +	$(LINKER) -ldos32a -fe=example.exe example.obj $(ZLIB_LIB)
           36  +
           37  +minigzip.exe: $(ZLIB_LIB) minigzip.obj
           38  +	$(LINKER) -ldos32a -fe=minigzip.exe minigzip.obj $(ZLIB_LIB)
           39  +
           40  +clean: .SYMBOLIC
           41  +          del *.obj
           42  +          del $(ZLIB_LIB)
           43  +          @echo Cleaning done

Added compat/zlib/watcom/watcom_l.mak.

            1  +# Makefile for zlib
            2  +# OpenWatcom large model
            3  +# Last updated: 28-Dec-2005
            4  +
            5  +# To use, do "wmake -f watcom_l.mak"
            6  +
            7  +C_SOURCE =  adler32.c  compress.c crc32.c   deflate.c    &
            8  +	    gzclose.c  gzlib.c    gzread.c  gzwrite.c    &
            9  +            infback.c  inffast.c  inflate.c inftrees.c   &
           10  +            trees.c    uncompr.c  zutil.c
           11  +
           12  +OBJS =      adler32.obj  compress.obj crc32.obj   deflate.obj    &
           13  +	    gzclose.obj  gzlib.obj    gzread.obj  gzwrite.obj    &
           14  +            infback.obj  inffast.obj  inflate.obj inftrees.obj   &
           15  +            trees.obj    uncompr.obj  zutil.obj
           16  +
           17  +CC       = wcc
           18  +LINKER   = wcl
           19  +CFLAGS   = -zq -ml -s -bt=dos -oilrtfm -fr=nul -wx
           20  +ZLIB_LIB = zlib_l.lib
           21  +
           22  +.C.OBJ:
           23  +        $(CC) $(CFLAGS) $[@
           24  +
           25  +all: $(ZLIB_LIB) example.exe minigzip.exe
           26  +
           27  +$(ZLIB_LIB): $(OBJS)
           28  +	wlib -b -c $(ZLIB_LIB) -+adler32.obj  -+compress.obj -+crc32.obj
           29  +	wlib -b -c $(ZLIB_LIB) -+gzclose.obj  -+gzlib.obj    -+gzread.obj   -+gzwrite.obj
           30  +        wlib -b -c $(ZLIB_LIB) -+deflate.obj  -+infback.obj
           31  +        wlib -b -c $(ZLIB_LIB) -+inffast.obj  -+inflate.obj  -+inftrees.obj
           32  +        wlib -b -c $(ZLIB_LIB) -+trees.obj    -+uncompr.obj  -+zutil.obj
           33  +
           34  +example.exe: $(ZLIB_LIB) example.obj
           35  +	$(LINKER) -fe=example.exe example.obj $(ZLIB_LIB)
           36  +
           37  +minigzip.exe: $(ZLIB_LIB) minigzip.obj
           38  +	$(LINKER) -fe=minigzip.exe minigzip.obj $(ZLIB_LIB)
           39  +
           40  +clean: .SYMBOLIC
           41  +          del *.obj
           42  +          del $(ZLIB_LIB)
           43  +          @echo Cleaning done

Added compat/zlib/win32/DLL_FAQ.txt.

            1  +
            2  +            Frequently Asked Questions about ZLIB1.DLL
            3  +
            4  +
            5  +This document describes the design, the rationale, and the usage
            6  +of the official DLL build of zlib, named ZLIB1.DLL.  If you have
            7  +general questions about zlib, you should see the file "FAQ" found
            8  +in the zlib distribution, or at the following location:
            9  +  http://www.gzip.org/zlib/zlib_faq.html
           10  +
           11  +
           12  + 1. What is ZLIB1.DLL, and how can I get it?
           13  +
           14  +  - ZLIB1.DLL is the official build of zlib as a DLL.
           15  +    (Please remark the character '1' in the name.)
           16  +
           17  +    Pointers to a precompiled ZLIB1.DLL can be found in the zlib
           18  +    web site at:
           19  +      http://www.zlib.net/
           20  +
           21  +    Applications that link to ZLIB1.DLL can rely on the following
           22  +    specification:
           23  +
           24  +    * The exported symbols are exclusively defined in the source
           25  +      files "zlib.h" and "zlib.def", found in an official zlib
           26  +      source distribution.
           27  +    * The symbols are exported by name, not by ordinal.
           28  +    * The exported names are undecorated.
           29  +    * The calling convention of functions is "C" (CDECL).
           30  +    * The ZLIB1.DLL binary is linked to MSVCRT.DLL.
           31  +
           32  +    The archive in which ZLIB1.DLL is bundled contains compiled
           33  +    test programs that must run with a valid build of ZLIB1.DLL.
           34  +    It is recommended to download the prebuilt DLL from the zlib
           35  +    web site, instead of building it yourself, to avoid potential
           36  +    incompatibilities that could be introduced by your compiler
           37  +    and build settings.  If you do build the DLL yourself, please
           38  +    make sure that it complies with all the above requirements,
           39  +    and it runs with the precompiled test programs, bundled with
           40  +    the original ZLIB1.DLL distribution.
           41  +
           42  +    If, for any reason, you need to build an incompatible DLL,
           43  +    please use a different file name.
           44  +
           45  +
           46  + 2. Why did you change the name of the DLL to ZLIB1.DLL?
           47  +    What happened to the old ZLIB.DLL?
           48  +
           49  +  - The old ZLIB.DLL, built from zlib-1.1.4 or earlier, required
           50  +    compilation settings that were incompatible to those used by
           51  +    a static build.  The DLL settings were supposed to be enabled
           52  +    by defining the macro ZLIB_DLL, before including "zlib.h".
           53  +    Incorrect handling of this macro was silently accepted at
           54  +    build time, resulting in two major problems:
           55  +
           56  +    * ZLIB_DLL was missing from the old makefile.  When building
           57  +      the DLL, not all people added it to the build options.  In
           58  +      consequence, incompatible incarnations of ZLIB.DLL started
           59  +      to circulate around the net.
           60  +
           61  +    * When switching from using the static library to using the
           62  +      DLL, applications had to define the ZLIB_DLL macro and
           63  +      to recompile all the sources that contained calls to zlib
           64  +      functions.  Failure to do so resulted in creating binaries
           65  +      that were unable to run with the official ZLIB.DLL build.
           66  +
           67  +    The only possible solution that we could foresee was to make
           68  +    a binary-incompatible change in the DLL interface, in order to
           69  +    remove the dependency on the ZLIB_DLL macro, and to release
           70  +    the new DLL under a different name.
           71  +
           72  +    We chose the name ZLIB1.DLL, where '1' indicates the major
           73  +    zlib version number.  We hope that we will not have to break
           74  +    the binary compatibility again, at least not as long as the
           75  +    zlib-1.x series will last.
           76  +
           77  +    There is still a ZLIB_DLL macro, that can trigger a more
           78  +    efficient build and use of the DLL, but compatibility no
           79  +    longer dependents on it.
           80  +
           81  +
           82  + 3. Can I build ZLIB.DLL from the new zlib sources, and replace
           83  +    an old ZLIB.DLL, that was built from zlib-1.1.4 or earlier?
           84  +
           85  +  - In principle, you can do it by assigning calling convention
           86  +    keywords to the macros ZEXPORT and ZEXPORTVA.  In practice,
           87  +    it depends on what you mean by "an old ZLIB.DLL", because the
           88  +    old DLL exists in several mutually-incompatible versions.
           89  +    You have to find out first what kind of calling convention is
           90  +    being used in your particular ZLIB.DLL build, and to use the
           91  +    same one in the new build.  If you don't know what this is all
           92  +    about, you might be better off if you would just leave the old
           93  +    DLL intact.
           94  +
           95  +
           96  + 4. Can I compile my application using the new zlib interface, and
           97  +    link it to an old ZLIB.DLL, that was built from zlib-1.1.4 or
           98  +    earlier?
           99  +
          100  +  - The official answer is "no"; the real answer depends again on
          101  +    what kind of ZLIB.DLL you have.  Even if you are lucky, this
          102  +    course of action is unreliable.
          103  +
          104  +    If you rebuild your application and you intend to use a newer
          105  +    version of zlib (post- 1.1.4), it is strongly recommended to
          106  +    link it to the new ZLIB1.DLL.
          107  +
          108  +
          109  + 5. Why are the zlib symbols exported by name, and not by ordinal?
          110  +
          111  +  - Although exporting symbols by ordinal is a little faster, it
          112  +    is risky.  Any single glitch in the maintenance or use of the
          113  +    DEF file that contains the ordinals can result in incompatible
          114  +    builds and frustrating crashes.  Simply put, the benefits of
          115  +    exporting symbols by ordinal do not justify the risks.
          116  +
          117  +    Technically, it should be possible to maintain ordinals in
          118  +    the DEF file, and still export the symbols by name.  Ordinals
          119  +    exist in every DLL, and even if the dynamic linking performed
          120  +    at the DLL startup is searching for names, ordinals serve as
          121  +    hints, for a faster name lookup.  However, if the DEF file
          122  +    contains ordinals, the Microsoft linker automatically builds
          123  +    an implib that will cause the executables linked to it to use
          124  +    those ordinals, and not the names.  It is interesting to
          125  +    notice that the GNU linker for Win32 does not suffer from this
          126  +    problem.
          127  +
          128  +    It is possible to avoid the DEF file if the exported symbols
          129  +    are accompanied by a "__declspec(dllexport)" attribute in the
          130  +    source files.  You can do this in zlib by predefining the
          131  +    ZLIB_DLL macro.
          132  +
          133  +
          134  + 6. I see that the ZLIB1.DLL functions use the "C" (CDECL) calling
          135  +    convention.  Why not use the STDCALL convention?
          136  +    STDCALL is the standard convention in Win32, and I need it in
          137  +    my Visual Basic project!
          138  +
          139  +    (For readability, we use CDECL to refer to the convention
          140  +     triggered by the "__cdecl" keyword, STDCALL to refer to
          141  +     the convention triggered by "__stdcall", and FASTCALL to
          142  +     refer to the convention triggered by "__fastcall".)
          143  +
          144  +  - Most of the native Windows API functions (without varargs) use
          145  +    indeed the WINAPI convention (which translates to STDCALL in
          146  +    Win32), but the standard C functions use CDECL.  If a user
          147  +    application is intrinsically tied to the Windows API (e.g.
          148  +    it calls native Windows API functions such as CreateFile()),
          149  +    sometimes it makes sense to decorate its own functions with
          150  +    WINAPI.  But if ANSI C or POSIX portability is a goal (e.g.
          151  +    it calls standard C functions such as fopen()), it is not a
          152  +    sound decision to request the inclusion of <windows.h>, or to
          153  +    use non-ANSI constructs, for the sole purpose to make the user
          154  +    functions STDCALL-able.
          155  +
          156  +    The functionality offered by zlib is not in the category of
          157  +    "Windows functionality", but is more like "C functionality".
          158  +
          159  +    Technically, STDCALL is not bad; in fact, it is slightly
          160  +    faster than CDECL, and it works with variable-argument
          161  +    functions, just like CDECL.  It is unfortunate that, in spite
          162  +    of using STDCALL in the Windows API, it is not the default
          163  +    convention used by the C compilers that run under Windows.
          164  +    The roots of the problem reside deep inside the unsafety of
          165  +    the K&R-style function prototypes, where the argument types
          166  +    are not specified; but that is another story for another day.
          167  +
          168  +    The remaining fact is that CDECL is the default convention.
          169  +    Even if an explicit convention is hard-coded into the function
          170  +    prototypes inside C headers, problems may appear.  The
          171  +    necessity to expose the convention in users' callbacks is one
          172  +    of these problems.
          173  +
          174  +    The calling convention issues are also important when using
          175  +    zlib in other programming languages.  Some of them, like Ada
          176  +    (GNAT) and Fortran (GNU G77), have C bindings implemented
          177  +    initially on Unix, and relying on the C calling convention.
          178  +    On the other hand, the pre- .NET versions of Microsoft Visual
          179  +    Basic require STDCALL, while Borland Delphi prefers, although
          180  +    it does not require, FASTCALL.
          181  +
          182  +    In fairness to all possible uses of zlib outside the C
          183  +    programming language, we choose the default "C" convention.
          184  +    Anyone interested in different bindings or conventions is
          185  +    encouraged to maintain specialized projects.  The "contrib/"
          186  +    directory from the zlib distribution already holds a couple
          187  +    of foreign bindings, such as Ada, C++, and Delphi.
          188  +
          189  +
          190  + 7. I need a DLL for my Visual Basic project.  What can I do?
          191  +
          192  +  - Define the ZLIB_WINAPI macro before including "zlib.h", when
          193  +    building both the DLL and the user application (except that
          194  +    you don't need to define anything when using the DLL in Visual
          195  +    Basic).  The ZLIB_WINAPI macro will switch on the WINAPI
          196  +    (STDCALL) convention.  The name of this DLL must be different
          197  +    than the official ZLIB1.DLL.
          198  +
          199  +    Gilles Vollant has contributed a build named ZLIBWAPI.DLL,
          200  +    with the ZLIB_WINAPI macro turned on, and with the minizip
          201  +    functionality built in.  For more information, please read
          202  +    the notes inside "contrib/vstudio/readme.txt", found in the
          203  +    zlib distribution.
          204  +
          205  +
          206  + 8. I need to use zlib in my Microsoft .NET project.  What can I
          207  +    do?
          208  +
          209  +  - Henrik Ravn has contributed a .NET wrapper around zlib.  Look
          210  +    into contrib/dotzlib/, inside the zlib distribution.
          211  +
          212  +
          213  + 9. If my application uses ZLIB1.DLL, should I link it to
          214  +    MSVCRT.DLL?  Why?
          215  +
          216  +  - It is not required, but it is recommended to link your
          217  +    application to MSVCRT.DLL, if it uses ZLIB1.DLL.
          218  +
          219  +    The executables (.EXE, .DLL, etc.) that are involved in the
          220  +    same process and are using the C run-time library (i.e. they
          221  +    are calling standard C functions), must link to the same
          222  +    library.  There are several libraries in the Win32 system:
          223  +    CRTDLL.DLL, MSVCRT.DLL, the static C libraries, etc.
          224  +    Since ZLIB1.DLL is linked to MSVCRT.DLL, the executables that
          225  +    depend on it should also be linked to MSVCRT.DLL.
          226  +
          227  +
          228  +10. Why are you saying that ZLIB1.DLL and my application should
          229  +    be linked to the same C run-time (CRT) library?  I linked my
          230  +    application and my DLLs to different C libraries (e.g. my
          231  +    application to a static library, and my DLLs to MSVCRT.DLL),
          232  +    and everything works fine.
          233  +
          234  +  - If a user library invokes only pure Win32 API (accessible via
          235  +    <windows.h> and the related headers), its DLL build will work
          236  +    in any context.  But if this library invokes standard C API,
          237  +    things get more complicated.
          238  +
          239  +    There is a single Win32 library in a Win32 system.  Every
          240  +    function in this library resides in a single DLL module, that
          241  +    is safe to call from anywhere.  On the other hand, there are
          242  +    multiple versions of the C library, and each of them has its
          243  +    own separate internal state.  Standalone executables and user
          244  +    DLLs that call standard C functions must link to a C run-time
          245  +    (CRT) library, be it static or shared (DLL).  Intermixing
          246  +    occurs when an executable (not necessarily standalone) and a
          247  +    DLL are linked to different CRTs, and both are running in the
          248  +    same process.
          249  +
          250  +    Intermixing multiple CRTs is possible, as long as their
          251  +    internal states are kept intact.  The Microsoft Knowledge Base
          252  +    articles KB94248 "HOWTO: Use the C Run-Time" and KB140584
          253  +    "HOWTO: Link with the Correct C Run-Time (CRT) Library"
          254  +    mention the potential problems raised by intermixing.
          255  +
          256  +    If intermixing works for you, it's because your application
          257  +    and DLLs are avoiding the corruption of each of the CRTs'
          258  +    internal states, maybe by careful design, or maybe by fortune.
          259  +
          260  +    Also note that linking ZLIB1.DLL to non-Microsoft CRTs, such
          261  +    as those provided by Borland, raises similar problems.
          262  +
          263  +
          264  +11. Why are you linking ZLIB1.DLL to MSVCRT.DLL?
          265  +
          266  +  - MSVCRT.DLL exists on every Windows 95 with a new service pack
          267  +    installed, or with Microsoft Internet Explorer 4 or later, and
          268  +    on all other Windows 4.x or later (Windows 98, Windows NT 4,
          269  +    or later).  It is freely distributable; if not present in the
          270  +    system, it can be downloaded from Microsoft or from other
          271  +    software provider for free.
          272  +
          273  +    The fact that MSVCRT.DLL does not exist on a virgin Windows 95
          274  +    is not so problematic.  Windows 95 is scarcely found nowadays,
          275  +    Microsoft ended its support a long time ago, and many recent
          276  +    applications from various vendors, including Microsoft, do not
          277  +    even run on it.  Furthermore, no serious user should run
          278  +    Windows 95 without a proper update installed.
          279  +
          280  +
          281  +12. Why are you not linking ZLIB1.DLL to
          282  +    <<my favorite C run-time library>> ?
          283  +
          284  +  - We considered and abandoned the following alternatives:
          285  +
          286  +    * Linking ZLIB1.DLL to a static C library (LIBC.LIB, or
          287  +      LIBCMT.LIB) is not a good option.  People are using the DLL
          288  +      mainly to save disk space.  If you are linking your program
          289  +      to a static C library, you may as well consider linking zlib
          290  +      in statically, too.
          291  +
          292  +    * Linking ZLIB1.DLL to CRTDLL.DLL looks appealing, because
          293  +      CRTDLL.DLL is present on every Win32 installation.
          294  +      Unfortunately, it has a series of problems: it does not
          295  +      work properly with Microsoft's C++ libraries, it does not
          296  +      provide support for 64-bit file offsets, (and so on...),
          297  +      and Microsoft discontinued its support a long time ago.
          298  +
          299  +    * Linking ZLIB1.DLL to MSVCR70.DLL or MSVCR71.DLL, supplied
          300  +      with the Microsoft .NET platform, and Visual C++ 7.0/7.1,
          301  +      raises problems related to the status of ZLIB1.DLL as a
          302  +      system component.  According to the Microsoft Knowledge Base
          303  +      article KB326922 "INFO: Redistribution of the Shared C
          304  +      Runtime Component in Visual C++ .NET", MSVCR70.DLL and
          305  +      MSVCR71.DLL are not supposed to function as system DLLs,
          306  +      because they may clash with MSVCRT.DLL.  Instead, the
          307  +      application's installer is supposed to put these DLLs
          308  +      (if needed) in the application's private directory.
          309  +      If ZLIB1.DLL depends on a non-system runtime, it cannot
          310  +      function as a redistributable system component.
          311  +
          312  +    * Linking ZLIB1.DLL to non-Microsoft runtimes, such as
          313  +      Borland's, or Cygwin's, raises problems related to the
          314  +      reliable presence of these runtimes on Win32 systems.
          315  +      It's easier to let the DLL build of zlib up to the people
          316  +      who distribute these runtimes, and who may proceed as
          317  +      explained in the answer to Question 14.
          318  +
          319  +
          320  +13. If ZLIB1.DLL cannot be linked to MSVCR70.DLL or MSVCR71.DLL,
          321  +    how can I build/use ZLIB1.DLL in Microsoft Visual C++ 7.0
          322  +    (Visual Studio .NET) or newer?
          323  +
          324  +  - Due to the problems explained in the Microsoft Knowledge Base
          325  +    article KB326922 (see the previous answer), the C runtime that
          326  +    comes with the VC7 environment is no longer considered a
          327  +    system component.  That is, it should not be assumed that this
          328  +    runtime exists, or may be installed in a system directory.
          329  +    Since ZLIB1.DLL is supposed to be a system component, it may
          330  +    not depend on a non-system component.
          331  +
          332  +    In order to link ZLIB1.DLL and your application to MSVCRT.DLL
          333  +    in VC7, you need the library of Visual C++ 6.0 or older.  If
          334  +    you don't have this library at hand, it's probably best not to
          335  +    use ZLIB1.DLL.
          336  +
          337  +    We are hoping that, in the future, Microsoft will provide a
          338  +    way to build applications linked to a proper system runtime,
          339  +    from the Visual C++ environment.  Until then, you have a
          340  +    couple of alternatives, such as linking zlib in statically.
          341  +    If your application requires dynamic linking, you may proceed
          342  +    as explained in the answer to Question 14.
          343  +
          344  +
          345  +14. I need to link my own DLL build to a CRT different than
          346  +    MSVCRT.DLL.  What can I do?
          347  +
          348  +  - Feel free to rebuild the DLL from the zlib sources, and link
          349  +    it the way you want.  You should, however, clearly state that
          350  +    your build is unofficial.  You should give it a different file
          351  +    name, and/or install it in a private directory that can be
          352  +    accessed by your application only, and is not visible to the
          353  +    others (i.e. it's neither in the PATH, nor in the SYSTEM or
          354  +    SYSTEM32 directories).  Otherwise, your build may clash with
          355  +    applications that link to the official build.
          356  +
          357  +    For example, in Cygwin, zlib is linked to the Cygwin runtime
          358  +    CYGWIN1.DLL, and it is distributed under the name CYGZ.DLL.
          359  +
          360  +
          361  +15. May I include additional pieces of code that I find useful,
          362  +    link them in ZLIB1.DLL, and export them?
          363  +
          364  +  - No.  A legitimate build of ZLIB1.DLL must not include code
          365  +    that does not originate from the official zlib source code.
          366  +    But you can make your own private DLL build, under a different
          367  +    file name, as suggested in the previous answer.
          368  +
          369  +    For example, zlib is a part of the VCL library, distributed
          370  +    with Borland Delphi and C++ Builder.  The DLL build of VCL
          371  +    is a redistributable file, named VCLxx.DLL.
          372  +
          373  +
          374  +16. May I remove some functionality out of ZLIB1.DLL, by enabling
          375  +    macros like NO_GZCOMPRESS or NO_GZIP at compile time?
          376  +
          377  +  - No.  A legitimate build of ZLIB1.DLL must provide the complete
          378  +    zlib functionality, as implemented in the official zlib source
          379  +    code.  But you can make your own private DLL build, under a
          380  +    different file name, as suggested in the previous answer.
          381  +
          382  +
          383  +17. I made my own ZLIB1.DLL build.  Can I test it for compliance?
          384  +
          385  +  - We prefer that you download the official DLL from the zlib
          386  +    web site.  If you need something peculiar from this DLL, you
          387  +    can send your suggestion to the zlib mailing list.
          388  +
          389  +    However, in case you do rebuild the DLL yourself, you can run
          390  +    it with the test programs found in the DLL distribution.
          391  +    Running these test programs is not a guarantee of compliance,
          392  +    but a failure can imply a detected problem.
          393  +
          394  +**
          395  +
          396  +This document is written and maintained by
          397  +Cosmin Truta <cosmint@cs.ubbcluj.ro>

Added compat/zlib/win32/Makefile.bor.

            1  +# Makefile for zlib
            2  +# Borland C++ for Win32
            3  +#
            4  +# Usage:
            5  +#  make -f win32/Makefile.bor
            6  +#  make -f win32/Makefile.bor LOCAL_ZLIB=-DASMV OBJA=match.obj OBJPA=+match.obj
            7  +
            8  +# ------------ Borland C++ ------------
            9  +
           10  +# Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7)
           11  +# should be added to the environment via "set LOCAL_ZLIB=-DFOO" or
           12  +# added to the declaration of LOC here:
           13  +LOC = $(LOCAL_ZLIB)
           14  +
           15  +CC = bcc32
           16  +AS = bcc32
           17  +LD = bcc32
           18  +AR = tlib
           19  +CFLAGS  = -a -d -k- -O2 $(LOC)
           20  +ASFLAGS = $(LOC)
           21  +LDFLAGS = $(LOC)
           22  +
           23  +
           24  +# variables
           25  +ZLIB_LIB = zlib.lib
           26  +
           27  +OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj
           28  +OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj
           29  +#OBJA =
           30  +OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj
           31  +OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj
           32  +#OBJPA=
           33  +
           34  +
           35  +# targets
           36  +all: $(ZLIB_LIB) example.exe minigzip.exe
           37  +
           38  +.c.obj:
           39  +	$(CC) -c $(CFLAGS) $<
           40  +
           41  +.asm.obj:
           42  +	$(AS) -c $(ASFLAGS) $<
           43  +
           44  +adler32.obj: adler32.c zlib.h zconf.h
           45  +
           46  +compress.obj: compress.c zlib.h zconf.h
           47  +
           48  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           49  +
           50  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           51  +
           52  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           53  +
           54  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           55  +
           56  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
           57  +
           58  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
           59  +
           60  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           61  + inffast.h inffixed.h
           62  +
           63  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           64  + inffast.h
           65  +
           66  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
           67  + inffast.h inffixed.h
           68  +
           69  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
           70  +
           71  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
           72  +
           73  +uncompr.obj: uncompr.c zlib.h zconf.h
           74  +
           75  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
           76  +
           77  +example.obj: test/example.c zlib.h zconf.h
           78  +
           79  +minigzip.obj: test/minigzip.c zlib.h zconf.h
           80  +
           81  +
           82  +# For the sake of the old Borland make,
           83  +# the command line is cut to fit in the MS-DOS 128 byte limit:
           84  +$(ZLIB_LIB): $(OBJ1) $(OBJ2) $(OBJA)
           85  +	-del $(ZLIB_LIB)
           86  +	$(AR) $(ZLIB_LIB) $(OBJP1)
           87  +	$(AR) $(ZLIB_LIB) $(OBJP2)
           88  +	$(AR) $(ZLIB_LIB) $(OBJPA)
           89  +
           90  +
           91  +# testing
           92  +test: example.exe minigzip.exe
           93  +	example
           94  +	echo hello world | minigzip | minigzip -d
           95  +
           96  +example.exe: example.obj $(ZLIB_LIB)
           97  +	$(LD) $(LDFLAGS) example.obj $(ZLIB_LIB)
           98  +
           99  +minigzip.exe: minigzip.obj $(ZLIB_LIB)
          100  +	$(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB)
          101  +
          102  +
          103  +# cleanup
          104  +clean:
          105  +	-del $(ZLIB_LIB)
          106  +	-del *.obj
          107  +	-del *.exe
          108  +	-del *.tds
          109  +	-del zlib.bak
          110  +	-del foo.gz

Added compat/zlib/win32/Makefile.gcc.

            1  +# Makefile for zlib, derived from Makefile.dj2.
            2  +# Modified for mingw32 by C. Spieler, 6/16/98.
            3  +# Updated for zlib 1.2.x by Christian Spieler and Cosmin Truta, Mar-2003.
            4  +# Last updated: Mar 2012.
            5  +# Tested under Cygwin and MinGW.
            6  +
            7  +# Copyright (C) 1995-2003 Jean-loup Gailly.
            8  +# For conditions of distribution and use, see copyright notice in zlib.h
            9  +
           10  +# To compile, or to compile and test, type from the top level zlib directory:
           11  +#
           12  +#   make -fwin32/Makefile.gcc;  make test testdll -fwin32/Makefile.gcc
           13  +#
           14  +# To use the asm code, type:
           15  +#   cp contrib/asm?86/match.S ./match.S
           16  +#   make LOC=-DASMV OBJA=match.o -fwin32/Makefile.gcc
           17  +#
           18  +# To install libz.a, zconf.h and zlib.h in the system directories, type:
           19  +#
           20  +#   make install -fwin32/Makefile.gcc
           21  +#
           22  +# BINARY_PATH, INCLUDE_PATH and LIBRARY_PATH must be set.
           23  +#
           24  +# To install the shared lib, append SHARED_MODE=1 to the make command :
           25  +#
           26  +#   make install -fwin32/Makefile.gcc SHARED_MODE=1
           27  +
           28  +# Note:
           29  +# If the platform is *not* MinGW (e.g. it is Cygwin or UWIN),
           30  +# the DLL name should be changed from "zlib1.dll".
           31  +
           32  +STATICLIB = libz.a
           33  +SHAREDLIB = zlib1.dll
           34  +IMPLIB    = libz.dll.a
           35  +
           36  +#
           37  +# Set to 1 if shared object needs to be installed
           38  +#
           39  +SHARED_MODE=0
           40  +
           41  +#LOC = -DASMV
           42  +#LOC = -DDEBUG -g
           43  +
           44  +PREFIX =
           45  +CC = $(PREFIX)gcc
           46  +CFLAGS = $(LOC) -O3 -Wall
           47  +
           48  +AS = $(CC)
           49  +ASFLAGS = $(LOC) -Wall
           50  +
           51  +LD = $(CC)
           52  +LDFLAGS = $(LOC)
           53  +
           54  +AR = $(PREFIX)ar
           55  +ARFLAGS = rcs
           56  +
           57  +RC = $(PREFIX)windres
           58  +RCFLAGS = --define GCC_WINDRES
           59  +
           60  +STRIP = $(PREFIX)strip
           61  +
           62  +CP = cp -fp
           63  +# If GNU install is available, replace $(CP) with install.
           64  +INSTALL = $(CP)
           65  +RM = rm -f
           66  +
           67  +prefix ?= /usr/local
           68  +exec_prefix = $(prefix)
           69  +
           70  +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \
           71  +       gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o
           72  +OBJA =
           73  +
           74  +all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) example.exe minigzip.exe example_d.exe minigzip_d.exe
           75  +
           76  +test: example.exe minigzip.exe
           77  +	./example
           78  +	echo hello world | ./minigzip | ./minigzip -d
           79  +
           80  +testdll: example_d.exe minigzip_d.exe
           81  +	./example_d
           82  +	echo hello world | ./minigzip_d | ./minigzip_d -d
           83  +
           84  +.c.o:
           85  +	$(CC) $(CFLAGS) -c -o $@ $<
           86  +
           87  +.S.o:
           88  +	$(AS) $(ASFLAGS) -c -o $@ $<
           89  +
           90  +$(STATICLIB): $(OBJS) $(OBJA)
           91  +	$(AR) $(ARFLAGS) $@ $(OBJS) $(OBJA)
           92  +
           93  +$(IMPLIB): $(SHAREDLIB)
           94  +
           95  +$(SHAREDLIB): win32/zlib.def $(OBJS) $(OBJA) zlibrc.o
           96  +	$(CC) -shared -Wl,--out-implib,$(IMPLIB) $(LDFLAGS) \
           97  +	-o $@ win32/zlib.def $(OBJS) $(OBJA) zlibrc.o
           98  +	$(STRIP) $@
           99  +
          100  +example.exe: example.o $(STATICLIB)
          101  +	$(LD) $(LDFLAGS) -o $@ example.o $(STATICLIB)
          102  +	$(STRIP) $@
          103  +
          104  +minigzip.exe: minigzip.o $(STATICLIB)
          105  +	$(LD) $(LDFLAGS) -o $@ minigzip.o $(STATICLIB)
          106  +	$(STRIP) $@
          107  +
          108  +example_d.exe: example.o $(IMPLIB)
          109  +	$(LD) $(LDFLAGS) -o $@ example.o $(IMPLIB)
          110  +	$(STRIP) $@
          111  +
          112  +minigzip_d.exe: minigzip.o $(IMPLIB)
          113  +	$(LD) $(LDFLAGS) -o $@ minigzip.o $(IMPLIB)
          114  +	$(STRIP) $@
          115  +
          116  +example.o: test/example.c zlib.h zconf.h
          117  +	$(CC) $(CFLAGS) -I. -c -o $@ test/example.c
          118  +
          119  +minigzip.o: test/minigzip.c zlib.h zconf.h
          120  +	$(CC) $(CFLAGS) -I. -c -o $@ test/minigzip.c
          121  +
          122  +zlibrc.o: win32/zlib1.rc
          123  +	$(RC) $(RCFLAGS) -o $@ win32/zlib1.rc
          124  +
          125  +.PHONY: install uninstall clean
          126  +
          127  +install: zlib.h zconf.h $(STATICLIB) $(IMPLIB)
          128  +	@if test -z "$(DESTDIR)$(INCLUDE_PATH)" -o -z "$(DESTDIR)$(LIBRARY_PATH)" -o -z "$(DESTDIR)$(BINARY_PATH)"; then \
          129  +		echo INCLUDE_PATH, LIBRARY_PATH, and BINARY_PATH must be specified; \
          130  +		exit 1; \
          131  +	fi
          132  +	-@mkdir -p '$(DESTDIR)$(INCLUDE_PATH)'
          133  +	-@mkdir -p '$(DESTDIR)$(LIBRARY_PATH)' '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig
          134  +	-if [ "$(SHARED_MODE)" = "1" ]; then \
          135  +		mkdir -p '$(DESTDIR)$(BINARY_PATH)'; \
          136  +		$(INSTALL) $(SHAREDLIB) '$(DESTDIR)$(BINARY_PATH)'; \
          137  +		$(INSTALL) $(IMPLIB) '$(DESTDIR)$(LIBRARY_PATH)'; \
          138  +	fi
          139  +	-$(INSTALL) zlib.h '$(DESTDIR)$(INCLUDE_PATH)'
          140  +	-$(INSTALL) zconf.h '$(DESTDIR)$(INCLUDE_PATH)'
          141  +	-$(INSTALL) $(STATICLIB) '$(DESTDIR)$(LIBRARY_PATH)'
          142  +	sed \
          143  +		-e 's|@prefix@|${prefix}|g' \
          144  +		-e 's|@exec_prefix@|${exec_prefix}|g' \
          145  +		-e 's|@libdir@|$(LIBRARY_PATH)|g' \
          146  +		-e 's|@sharedlibdir@|$(LIBRARY_PATH)|g' \
          147  +		-e 's|@includedir@|$(INCLUDE_PATH)|g' \
          148  +		-e 's|@VERSION@|'`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' zlib.h`'|g' \
          149  +		zlib.pc.in > '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig/zlib.pc
          150  +
          151  +uninstall:
          152  +	-if [ "$(SHARED_MODE)" = "1" ]; then \
          153  +		$(RM) '$(DESTDIR)$(BINARY_PATH)'/$(SHAREDLIB); \
          154  +		$(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(IMPLIB); \
          155  +	fi
          156  +	-$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zlib.h
          157  +	-$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zconf.h
          158  +	-$(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(STATICLIB)
          159  +
          160  +clean:
          161  +	-$(RM) $(STATICLIB)
          162  +	-$(RM) $(SHAREDLIB)
          163  +	-$(RM) $(IMPLIB)
          164  +	-$(RM) *.o
          165  +	-$(RM) *.exe
          166  +	-$(RM) foo.gz
          167  +
          168  +adler32.o: zlib.h zconf.h
          169  +compress.o: zlib.h zconf.h
          170  +crc32.o: crc32.h zlib.h zconf.h
          171  +deflate.o: deflate.h zutil.h zlib.h zconf.h
          172  +gzclose.o: zlib.h zconf.h gzguts.h
          173  +gzlib.o: zlib.h zconf.h gzguts.h
          174  +gzread.o: zlib.h zconf.h gzguts.h
          175  +gzwrite.o: zlib.h zconf.h gzguts.h
          176  +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
          177  +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
          178  +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
          179  +inftrees.o: zutil.h zlib.h zconf.h inftrees.h
          180  +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
          181  +uncompr.o: zlib.h zconf.h
          182  +zutil.o: zutil.h zlib.h zconf.h

Added compat/zlib/win32/Makefile.msc.

            1  +# Makefile for zlib using Microsoft (Visual) C
            2  +# zlib is copyright (C) 1995-2006 Jean-loup Gailly and Mark Adler
            3  +#
            4  +# Usage:
            5  +#   nmake -f win32/Makefile.msc                          (standard build)
            6  +#   nmake -f win32/Makefile.msc LOC=-DFOO                (nonstandard build)
            7  +#   nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" \
            8  +#         OBJA="inffas32.obj match686.obj"               (use ASM code, x86)
            9  +#   nmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." \
           10  +#         OBJA="inffasx64.obj gvmat64.obj inffas8664.obj"  (use ASM code, x64)
           11  +
           12  +# optional build flags
           13  +LOC =
           14  +
           15  +# variables
           16  +STATICLIB = zlib.lib
           17  +SHAREDLIB = zlib1.dll
           18  +IMPLIB    = zdll.lib
           19  +
           20  +CC = cl
           21  +AS = ml
           22  +LD = link
           23  +AR = lib
           24  +RC = rc
           25  +CFLAGS  = -nologo -MD -W3 -O2 -Oy- -Zi -Fd"zlib" $(LOC)
           26  +WFLAGS  = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
           27  +ASFLAGS = -coff -Zi $(LOC)
           28  +LDFLAGS = -nologo -debug -incremental:no -opt:ref
           29  +ARFLAGS = -nologo
           30  +RCFLAGS = /dWIN32 /r
           31  +
           32  +OBJS = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj \
           33  +       gzwrite.obj infback.obj inflate.obj inftrees.obj inffast.obj trees.obj uncompr.obj zutil.obj
           34  +OBJA =
           35  +
           36  +
           37  +# targets
           38  +all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \
           39  +     example.exe minigzip.exe example_d.exe minigzip_d.exe
           40  +
           41  +$(STATICLIB): $(OBJS) $(OBJA)
           42  +	$(AR) $(ARFLAGS) -out:$@ $(OBJS) $(OBJA)
           43  +
           44  +$(IMPLIB): $(SHAREDLIB)
           45  +
           46  +$(SHAREDLIB): win32/zlib.def $(OBJS) $(OBJA) zlib1.res
           47  +	$(LD) $(LDFLAGS) -def:win32/zlib.def -dll -implib:$(IMPLIB) \
           48  +	  -out:$@ -base:0x5A4C0000 $(OBJS) $(OBJA) zlib1.res
           49  +	if exist $@.manifest \
           50  +	  mt -nologo -manifest $@.manifest -outputresource:$@;2
           51  +
           52  +example.exe: example.obj $(STATICLIB)
           53  +	$(LD) $(LDFLAGS) example.obj $(STATICLIB)
           54  +	if exist $@.manifest \
           55  +	  mt -nologo -manifest $@.manifest -outputresource:$@;1
           56  +
           57  +minigzip.exe: minigzip.obj $(STATICLIB)
           58  +	$(LD) $(LDFLAGS) minigzip.obj $(STATICLIB)
           59  +	if exist $@.manifest \
           60  +	  mt -nologo -manifest $@.manifest -outputresource:$@;1
           61  +
           62  +example_d.exe: example.obj $(IMPLIB)
           63  +	$(LD) $(LDFLAGS) -out:$@ example.obj $(IMPLIB)
           64  +	if exist $@.manifest \
           65  +	  mt -nologo -manifest $@.manifest -outputresource:$@;1
           66  +
           67  +minigzip_d.exe: minigzip.obj $(IMPLIB)
           68  +	$(LD) $(LDFLAGS) -out:$@ minigzip.obj $(IMPLIB)
           69  +	if exist $@.manifest \
           70  +	  mt -nologo -manifest $@.manifest -outputresource:$@;1
           71  +
           72  +.c.obj:
           73  +	$(CC) -c $(WFLAGS) $(CFLAGS) $<
           74  +
           75  +{test}.c.obj:
           76  +	$(CC) -c -I. $(WFLAGS) $(CFLAGS) $<
           77  +
           78  +{contrib/masmx64}.c.obj:
           79  +	$(CC) -c $(WFLAGS) $(CFLAGS) $<
           80  +
           81  +{contrib/masmx64}.asm.obj:
           82  +	$(AS) -c $(ASFLAGS) $<
           83  +
           84  +{contrib/masmx86}.asm.obj:
           85  +	$(AS) -c $(ASFLAGS) $<
           86  +
           87  +adler32.obj: adler32.c zlib.h zconf.h
           88  +
           89  +compress.obj: compress.c zlib.h zconf.h
           90  +
           91  +crc32.obj: crc32.c zlib.h zconf.h crc32.h
           92  +
           93  +deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h
           94  +
           95  +gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h
           96  +
           97  +gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h
           98  +
           99  +gzread.obj: gzread.c zlib.h zconf.h gzguts.h
          100  +
          101  +gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h
          102  +
          103  +infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
          104  +             inffast.h inffixed.h
          105  +
          106  +inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
          107  +             inffast.h
          108  +
          109  +inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \
          110  +             inffast.h inffixed.h
          111  +
          112  +inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h
          113  +
          114  +trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h
          115  +
          116  +uncompr.obj: uncompr.c zlib.h zconf.h
          117  +
          118  +zutil.obj: zutil.c zutil.h zlib.h zconf.h
          119  +
          120  +gvmat64.obj: contrib\masmx64\gvmat64.asm
          121  +
          122  +inffasx64.obj: contrib\masmx64\inffasx64.asm
          123  +
          124  +inffas8664.obj: contrib\masmx64\inffas8664.c zutil.h zlib.h zconf.h \
          125  +		inftrees.h inflate.h inffast.h
          126  +
          127  +inffas32.obj: contrib\masmx86\inffas32.asm
          128  +
          129  +match686.obj: contrib\masmx86\match686.asm
          130  +
          131  +example.obj: test/example.c zlib.h zconf.h
          132  +
          133  +minigzip.obj: test/minigzip.c zlib.h zconf.h
          134  +
          135  +zlib1.res: win32/zlib1.rc
          136  +	$(RC) $(RCFLAGS) /fo$@ win32/zlib1.rc
          137  +
          138  +
          139  +# testing
          140  +test: example.exe minigzip.exe
          141  +	example
          142  +	echo hello world | minigzip | minigzip -d
          143  +
          144  +testdll: example_d.exe minigzip_d.exe
          145  +	example_d
          146  +	echo hello world | minigzip_d | minigzip_d -d
          147  +
          148  +
          149  +# cleanup
          150  +clean:
          151  +	-del $(STATICLIB)
          152  +	-del $(SHAREDLIB)
          153  +	-del $(IMPLIB)
          154  +	-del *.obj
          155  +	-del *.res
          156  +	-del *.exp
          157  +	-del *.exe
          158  +	-del *.pdb
          159  +	-del *.manifest
          160  +	-del foo.gz

Added compat/zlib/win32/README-WIN32.txt.

            1  +ZLIB DATA COMPRESSION LIBRARY
            2  +
            3  +zlib 1.2.7 is a general purpose data compression library.  All the code is
            4  +thread safe.  The data format used by the zlib library is described by RFCs
            5  +(Request for Comments) 1950 to 1952 in the files
            6  +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format)
            7  +and rfc1952.txt (gzip format).
            8  +
            9  +All functions of the compression library are documented in the file zlib.h
           10  +(volunteer to write man pages welcome, contact zlib@gzip.org).  Two compiled
           11  +examples are distributed in this package, example and minigzip.  The example_d
           12  +and minigzip_d flavors validate that the zlib1.dll file is working correctly.
           13  +
           14  +Questions about zlib should be sent to <zlib@gzip.org>.  The zlib home page
           15  +is http://zlib.net/ .  Before reporting a problem, please check this site to
           16  +verify that you have the latest version of zlib; otherwise get the latest
           17  +version and check whether the problem still exists or not.
           18  +
           19  +PLEASE read DLL_FAQ.txt, and the the zlib FAQ http://zlib.net/zlib_faq.html
           20  +before asking for help.
           21  +
           22  +
           23  +Manifest:
           24  +
           25  +The package zlib-1.2.7-win32-x86.zip will contain the following files:
           26  +
           27  +  README-WIN32.txt This document
           28  +  ChangeLog        Changes since previous zlib packages
           29  +  DLL_FAQ.txt      Frequently asked questions about zlib1.dll
           30  +  zlib.3.pdf       Documentation of this library in Adobe Acrobat format
           31  +
           32  +  example.exe      A statically-bound example (using zlib.lib, not the dll)
           33  +  example.pdb      Symbolic information for debugging example.exe
           34  +
           35  +  example_d.exe    A zlib1.dll bound example (using zdll.lib)
           36  +  example_d.pdb    Symbolic information for debugging example_d.exe
           37  +
           38  +  minigzip.exe     A statically-bound test program (using zlib.lib, not the dll)
           39  +  minigzip.pdb     Symbolic information for debugging minigzip.exe
           40  +
           41  +  minigzip_d.exe   A zlib1.dll bound test program (using zdll.lib)
           42  +  minigzip_d.pdb   Symbolic information for debugging minigzip_d.exe
           43  +
           44  +  zlib.h           Install these files into the compilers' INCLUDE path to
           45  +  zconf.h          compile programs which use zlib.lib or zdll.lib
           46  +
           47  +  zdll.lib         Install these files into the compilers' LIB path if linking
           48  +  zdll.exp         a compiled program to the zlib1.dll binary
           49  +
           50  +  zlib.lib         Install these files into the compilers' LIB path to link zlib
           51  +  zlib.pdb         into compiled programs, without zlib1.dll runtime dependency
           52  +                   (zlib.pdb provides debugging info to the compile time linker)
           53  +
           54  +  zlib1.dll        Install this binary shared library into the system PATH, or
           55  +                   the program's runtime directory (where the .exe resides)
           56  +  zlib1.pdb        Install in the same directory as zlib1.dll, in order to debug
           57  +                   an application crash using WinDbg or similar tools.
           58  +
           59  +All .pdb files above are entirely optional, but are very useful to a developer
           60  +attempting to diagnose program misbehavior or a crash.  Many additional
           61  +important files for developers can be found in the zlib127.zip source package
           62  +available from http://zlib.net/ - review that package's README file for details.
           63  +
           64  +
           65  +Acknowledgments:
           66  +
           67  +The deflate format used by zlib was defined by Phil Katz.  The deflate and
           68  +zlib specifications were written by L.  Peter Deutsch.  Thanks to all the
           69  +people who reported problems and suggested various improvements in zlib; they
           70  +are too numerous to cite here.
           71  +
           72  +
           73  +Copyright notice:
           74  +
           75  +  (C) 1995-2012 Jean-loup Gailly and Mark Adler
           76  +
           77  +  This software is provided 'as-is', without any express or implied
           78  +  warranty.  In no event will the authors be held liable for any damages
           79  +  arising from the use of this software.
           80  +
           81  +  Permission is granted to anyone to use this software for any purpose,
           82  +  including commercial applications, and to alter it and redistribute it
           83  +  freely, subject to the following restrictions:
           84  +
           85  +  1. The origin of this software must not be misrepresented; you must not
           86  +     claim that you wrote the original software. If you use this software
           87  +     in a product, an acknowledgment in the product documentation would be
           88  +     appreciated but is not required.
           89  +  2. Altered source versions must be plainly marked as such, and must not be
           90  +     misrepresented as being the original software.
           91  +  3. This notice may not be removed or altered from any source distribution.
           92  +
           93  +  Jean-loup Gailly        Mark Adler
           94  +  jloup@gzip.org          madler@alumni.caltech.edu
           95  +
           96  +If you use the zlib library in a product, we would appreciate *not* receiving
           97  +lengthy legal documents to sign.  The sources are provided for free but without
           98  +warranty of any kind.  The library has been entirely written by Jean-loup
           99  +Gailly and Mark Adler; it does not include third-party code.
          100  +
          101  +If you redistribute modified sources, we would appreciate that you include in
          102  +the file ChangeLog history information documenting your changes.  Please read
          103  +the FAQ for more information on the distribution of modified source versions.

Added compat/zlib/win32/VisualC.txt.

            1  +
            2  +To build zlib using the Microsoft Visual C++ environment,
            3  +use the appropriate project from the projects/ directory.

Added compat/zlib/win32/zlib.def.

            1  +; zlib data compression library
            2  +EXPORTS
            3  +; basic functions
            4  +    zlibVersion
            5  +    deflate
            6  +    deflateEnd
            7  +    inflate
            8  +    inflateEnd
            9  +; advanced functions
           10  +    deflateSetDictionary
           11  +    deflateCopy
           12  +    deflateReset
           13  +    deflateParams
           14  +    deflateTune
           15  +    deflateBound
           16  +    deflatePending
           17  +    deflatePrime
           18  +    deflateSetHeader
           19  +    inflateSetDictionary
           20  +    inflateSync
           21  +    inflateCopy
           22  +    inflateReset
           23  +    inflateReset2
           24  +    inflatePrime
           25  +    inflateMark
           26  +    inflateGetHeader
           27  +    inflateBack
           28  +    inflateBackEnd
           29  +    zlibCompileFlags
           30  +; utility functions
           31  +    compress
           32  +    compress2
           33  +    compressBound
           34  +    uncompress
           35  +    gzopen
           36  +    gzdopen
           37  +    gzbuffer
           38  +    gzsetparams
           39  +    gzread
           40  +    gzwrite
           41  +    gzprintf
           42  +    gzputs
           43  +    gzgets
           44  +    gzputc
           45  +    gzgetc
           46  +    gzungetc
           47  +    gzflush
           48  +    gzseek
           49  +    gzrewind
           50  +    gztell
           51  +    gzoffset
           52  +    gzeof
           53  +    gzdirect
           54  +    gzclose
           55  +    gzclose_r
           56  +    gzclose_w
           57  +    gzerror
           58  +    gzclearerr
           59  +; large file functions
           60  +    gzopen64
           61  +    gzseek64
           62  +    gztell64
           63  +    gzoffset64
           64  +    adler32_combine64
           65  +    crc32_combine64
           66  +; checksum functions
           67  +    adler32
           68  +    crc32
           69  +    adler32_combine
           70  +    crc32_combine
           71  +; various hacks, don't look :)
           72  +    deflateInit_
           73  +    deflateInit2_
           74  +    inflateInit_
           75  +    inflateInit2_
           76  +    inflateBackInit_
           77  +    gzgetc_
           78  +    zError
           79  +    inflateSyncPoint
           80  +    get_crc_table
           81  +    inflateUndermine
           82  +    inflateResetKeep
           83  +    deflateResetKeep
           84  +    gzopen_w

Added compat/zlib/win32/zlib1.rc.

            1  +#include <winver.h>
            2  +#include "../zlib.h"
            3  +
            4  +#ifdef GCC_WINDRES
            5  +VS_VERSION_INFO		VERSIONINFO
            6  +#else
            7  +VS_VERSION_INFO		VERSIONINFO	MOVEABLE IMPURE LOADONCALL DISCARDABLE
            8  +#endif
            9  +  FILEVERSION		ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0
           10  +  PRODUCTVERSION	ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0
           11  +  FILEFLAGSMASK		VS_FFI_FILEFLAGSMASK
           12  +#ifdef _DEBUG
           13  +  FILEFLAGS		1
           14  +#else
           15  +  FILEFLAGS		0
           16  +#endif
           17  +  FILEOS		VOS__WINDOWS32
           18  +  FILETYPE		VFT_DLL
           19  +  FILESUBTYPE		0	// not used
           20  +BEGIN
           21  +  BLOCK "StringFileInfo"
           22  +  BEGIN
           23  +    BLOCK "040904E4"
           24  +    //language ID = U.S. English, char set = Windows, Multilingual
           25  +    BEGIN
           26  +      VALUE "FileDescription",	"zlib data compression library\0"
           27  +      VALUE "FileVersion",	ZLIB_VERSION "\0"
           28  +      VALUE "InternalName",	"zlib1.dll\0"
           29  +      VALUE "LegalCopyright",	"(C) 1995-2006 Jean-loup Gailly & Mark Adler\0"
           30  +      VALUE "OriginalFilename",	"zlib1.dll\0"
           31  +      VALUE "ProductName",	"zlib\0"
           32  +      VALUE "ProductVersion",	ZLIB_VERSION "\0"
           33  +      VALUE "Comments",		"For more information visit http://www.zlib.net/\0"
           34  +    END
           35  +  END
           36  +  BLOCK "VarFileInfo"
           37  +  BEGIN
           38  +    VALUE "Translation", 0x0409, 1252
           39  +  END
           40  +END

Added compat/zlib/zconf.h.

            1  +/* zconf.h -- configuration of the zlib compression library
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#ifndef ZCONF_H
            9  +#define ZCONF_H
           10  +
           11  +/*
           12  + * If you *really* need a unique prefix for all types and library functions,
           13  + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
           14  + * Even better than compiling with -DZ_PREFIX would be to use configure to set
           15  + * this permanently in zconf.h using "./configure --zprefix".
           16  + */
           17  +#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */
           18  +#  define Z_PREFIX_SET
           19  +
           20  +/* all linked symbols */
           21  +#  define _dist_code            z__dist_code
           22  +#  define _length_code          z__length_code
           23  +#  define _tr_align             z__tr_align
           24  +#  define _tr_flush_block       z__tr_flush_block
           25  +#  define _tr_init              z__tr_init
           26  +#  define _tr_stored_block      z__tr_stored_block
           27  +#  define _tr_tally             z__tr_tally
           28  +#  define adler32               z_adler32
           29  +#  define adler32_combine       z_adler32_combine
           30  +#  define adler32_combine64     z_adler32_combine64
           31  +#  ifndef Z_SOLO
           32  +#    define compress              z_compress
           33  +#    define compress2             z_compress2
           34  +#    define compressBound         z_compressBound
           35  +#  endif
           36  +#  define crc32                 z_crc32
           37  +#  define crc32_combine         z_crc32_combine
           38  +#  define crc32_combine64       z_crc32_combine64
           39  +#  define deflate               z_deflate
           40  +#  define deflateBound          z_deflateBound
           41  +#  define deflateCopy           z_deflateCopy
           42  +#  define deflateEnd            z_deflateEnd
           43  +#  define deflateInit2_         z_deflateInit2_
           44  +#  define deflateInit_          z_deflateInit_
           45  +#  define deflateParams         z_deflateParams
           46  +#  define deflatePending        z_deflatePending
           47  +#  define deflatePrime          z_deflatePrime
           48  +#  define deflateReset          z_deflateReset
           49  +#  define deflateResetKeep      z_deflateResetKeep
           50  +#  define deflateSetDictionary  z_deflateSetDictionary
           51  +#  define deflateSetHeader      z_deflateSetHeader
           52  +#  define deflateTune           z_deflateTune
           53  +#  define deflate_copyright     z_deflate_copyright
           54  +#  define get_crc_table         z_get_crc_table
           55  +#  ifndef Z_SOLO
           56  +#    define gz_error              z_gz_error
           57  +#    define gz_intmax             z_gz_intmax
           58  +#    define gz_strwinerror        z_gz_strwinerror
           59  +#    define gzbuffer              z_gzbuffer
           60  +#    define gzclearerr            z_gzclearerr
           61  +#    define gzclose               z_gzclose
           62  +#    define gzclose_r             z_gzclose_r
           63  +#    define gzclose_w             z_gzclose_w
           64  +#    define gzdirect              z_gzdirect
           65  +#    define gzdopen               z_gzdopen
           66  +#    define gzeof                 z_gzeof
           67  +#    define gzerror               z_gzerror
           68  +#    define gzflush               z_gzflush
           69  +#    define gzgetc                z_gzgetc
           70  +#    define gzgetc_               z_gzgetc_
           71  +#    define gzgets                z_gzgets
           72  +#    define gzoffset              z_gzoffset
           73  +#    define gzoffset64            z_gzoffset64
           74  +#    define gzopen                z_gzopen
           75  +#    define gzopen64              z_gzopen64
           76  +#    ifdef _WIN32
           77  +#      define gzopen_w              z_gzopen_w
           78  +#    endif
           79  +#    define gzprintf              z_gzprintf
           80  +#    define gzputc                z_gzputc
           81  +#    define gzputs                z_gzputs
           82  +#    define gzread                z_gzread
           83  +#    define gzrewind              z_gzrewind
           84  +#    define gzseek                z_gzseek
           85  +#    define gzseek64              z_gzseek64
           86  +#    define gzsetparams           z_gzsetparams
           87  +#    define gztell                z_gztell
           88  +#    define gztell64              z_gztell64
           89  +#    define gzungetc              z_gzungetc
           90  +#    define gzwrite               z_gzwrite
           91  +#  endif
           92  +#  define inflate               z_inflate
           93  +#  define inflateBack           z_inflateBack
           94  +#  define inflateBackEnd        z_inflateBackEnd
           95  +#  define inflateBackInit_      z_inflateBackInit_
           96  +#  define inflateCopy           z_inflateCopy
           97  +#  define inflateEnd            z_inflateEnd
           98  +#  define inflateGetHeader      z_inflateGetHeader
           99  +#  define inflateInit2_         z_inflateInit2_
          100  +#  define inflateInit_          z_inflateInit_
          101  +#  define inflateMark           z_inflateMark
          102  +#  define inflatePrime          z_inflatePrime
          103  +#  define inflateReset          z_inflateReset
          104  +#  define inflateReset2         z_inflateReset2
          105  +#  define inflateSetDictionary  z_inflateSetDictionary
          106  +#  define inflateSync           z_inflateSync
          107  +#  define inflateSyncPoint      z_inflateSyncPoint
          108  +#  define inflateUndermine      z_inflateUndermine
          109  +#  define inflateResetKeep      z_inflateResetKeep
          110  +#  define inflate_copyright     z_inflate_copyright
          111  +#  define inflate_fast          z_inflate_fast
          112  +#  define inflate_table         z_inflate_table
          113  +#  ifndef Z_SOLO
          114  +#    define uncompress            z_uncompress
          115  +#  endif
          116  +#  define zError                z_zError
          117  +#  ifndef Z_SOLO
          118  +#    define zcalloc               z_zcalloc
          119  +#    define zcfree                z_zcfree
          120  +#  endif
          121  +#  define zlibCompileFlags      z_zlibCompileFlags
          122  +#  define zlibVersion           z_zlibVersion
          123  +
          124  +/* all zlib typedefs in zlib.h and zconf.h */
          125  +#  define Byte                  z_Byte
          126  +#  define Bytef                 z_Bytef
          127  +#  define alloc_func            z_alloc_func
          128  +#  define charf                 z_charf
          129  +#  define free_func             z_free_func
          130  +#  ifndef Z_SOLO
          131  +#    define gzFile                z_gzFile
          132  +#  endif
          133  +#  define gz_header             z_gz_header
          134  +#  define gz_headerp            z_gz_headerp
          135  +#  define in_func               z_in_func
          136  +#  define intf                  z_intf
          137  +#  define out_func              z_out_func
          138  +#  define uInt                  z_uInt
          139  +#  define uIntf                 z_uIntf
          140  +#  define uLong                 z_uLong
          141  +#  define uLongf                z_uLongf
          142  +#  define voidp                 z_voidp
          143  +#  define voidpc                z_voidpc
          144  +#  define voidpf                z_voidpf
          145  +
          146  +/* all zlib structs in zlib.h and zconf.h */
          147  +#  define gz_header_s           z_gz_header_s
          148  +#  define internal_state        z_internal_state
          149  +
          150  +#endif
          151  +
          152  +#if defined(__MSDOS__) && !defined(MSDOS)
          153  +#  define MSDOS
          154  +#endif
          155  +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
          156  +#  define OS2
          157  +#endif
          158  +#if defined(_WINDOWS) && !defined(WINDOWS)
          159  +#  define WINDOWS
          160  +#endif
          161  +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
          162  +#  ifndef WIN32
          163  +#    define WIN32
          164  +#  endif
          165  +#endif
          166  +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
          167  +#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
          168  +#    ifndef SYS16BIT
          169  +#      define SYS16BIT
          170  +#    endif
          171  +#  endif
          172  +#endif
          173  +
          174  +/*
          175  + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
          176  + * than 64k bytes at a time (needed on systems with 16-bit int).
          177  + */
          178  +#ifdef SYS16BIT
          179  +#  define MAXSEG_64K
          180  +#endif
          181  +#ifdef MSDOS
          182  +#  define UNALIGNED_OK
          183  +#endif
          184  +
          185  +#ifdef __STDC_VERSION__
          186  +#  ifndef STDC
          187  +#    define STDC
          188  +#  endif
          189  +#  if __STDC_VERSION__ >= 199901L
          190  +#    ifndef STDC99
          191  +#      define STDC99
          192  +#    endif
          193  +#  endif
          194  +#endif
          195  +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
          196  +#  define STDC
          197  +#endif
          198  +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
          199  +#  define STDC
          200  +#endif
          201  +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
          202  +#  define STDC
          203  +#endif
          204  +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
          205  +#  define STDC
          206  +#endif
          207  +
          208  +#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
          209  +#  define STDC
          210  +#endif
          211  +
          212  +#ifndef STDC
          213  +#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
          214  +#    define const       /* note: need a more gentle solution here */
          215  +#  endif
          216  +#endif
          217  +
          218  +#if defined(ZLIB_CONST) && !defined(z_const)
          219  +#  define z_const const
          220  +#else
          221  +#  define z_const
          222  +#endif
          223  +
          224  +/* Some Mac compilers merge all .h files incorrectly: */
          225  +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
          226  +#  define NO_DUMMY_DECL
          227  +#endif
          228  +
          229  +/* Maximum value for memLevel in deflateInit2 */
          230  +#ifndef MAX_MEM_LEVEL
          231  +#  ifdef MAXSEG_64K
          232  +#    define MAX_MEM_LEVEL 8
          233  +#  else
          234  +#    define MAX_MEM_LEVEL 9
          235  +#  endif
          236  +#endif
          237  +
          238  +/* Maximum value for windowBits in deflateInit2 and inflateInit2.
          239  + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
          240  + * created by gzip. (Files created by minigzip can still be extracted by
          241  + * gzip.)
          242  + */
          243  +#ifndef MAX_WBITS
          244  +#  define MAX_WBITS   15 /* 32K LZ77 window */
          245  +#endif
          246  +
          247  +/* The memory requirements for deflate are (in bytes):
          248  +            (1 << (windowBits+2)) +  (1 << (memLevel+9))
          249  + that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
          250  + plus a few kilobytes for small objects. For example, if you want to reduce
          251  + the default memory requirements from 256K to 128K, compile with
          252  +     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
          253  + Of course this will generally degrade compression (there's no free lunch).
          254  +
          255  +   The memory requirements for inflate are (in bytes) 1 << windowBits
          256  + that is, 32K for windowBits=15 (default value) plus a few kilobytes
          257  + for small objects.
          258  +*/
          259  +
          260  +                        /* Type declarations */
          261  +
          262  +#ifndef OF /* function prototypes */
          263  +#  ifdef STDC
          264  +#    define OF(args)  args
          265  +#  else
          266  +#    define OF(args)  ()
          267  +#  endif
          268  +#endif
          269  +
          270  +#ifndef Z_ARG /* function prototypes for stdarg */
          271  +#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
          272  +#    define Z_ARG(args)  args
          273  +#  else
          274  +#    define Z_ARG(args)  ()
          275  +#  endif
          276  +#endif
          277  +
          278  +/* The following definitions for FAR are needed only for MSDOS mixed
          279  + * model programming (small or medium model with some far allocations).
          280  + * This was tested only with MSC; for other MSDOS compilers you may have
          281  + * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
          282  + * just define FAR to be empty.
          283  + */
          284  +#ifdef SYS16BIT
          285  +#  if defined(M_I86SM) || defined(M_I86MM)
          286  +     /* MSC small or medium model */
          287  +#    define SMALL_MEDIUM
          288  +#    ifdef _MSC_VER
          289  +#      define FAR _far
          290  +#    else
          291  +#      define FAR far
          292  +#    endif
          293  +#  endif
          294  +#  if (defined(__SMALL__) || defined(__MEDIUM__))
          295  +     /* Turbo C small or medium model */
          296  +#    define SMALL_MEDIUM
          297  +#    ifdef __BORLANDC__
          298  +#      define FAR _far
          299  +#    else
          300  +#      define FAR far
          301  +#    endif
          302  +#  endif
          303  +#endif
          304  +
          305  +#if defined(WINDOWS) || defined(WIN32)
          306  +   /* If building or using zlib as a DLL, define ZLIB_DLL.
          307  +    * This is not mandatory, but it offers a little performance increase.
          308  +    */
          309  +#  ifdef ZLIB_DLL
          310  +#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
          311  +#      ifdef ZLIB_INTERNAL
          312  +#        define ZEXTERN extern __declspec(dllexport)
          313  +#      else
          314  +#        define ZEXTERN extern __declspec(dllimport)
          315  +#      endif
          316  +#    endif
          317  +#  endif  /* ZLIB_DLL */
          318  +   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
          319  +    * define ZLIB_WINAPI.
          320  +    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
          321  +    */
          322  +#  ifdef ZLIB_WINAPI
          323  +#    ifdef FAR
          324  +#      undef FAR
          325  +#    endif
          326  +#    include <windows.h>
          327  +     /* No need for _export, use ZLIB.DEF instead. */
          328  +     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
          329  +#    define ZEXPORT WINAPI
          330  +#    ifdef WIN32
          331  +#      define ZEXPORTVA WINAPIV
          332  +#    else
          333  +#      define ZEXPORTVA FAR CDECL
          334  +#    endif
          335  +#  endif
          336  +#endif
          337  +
          338  +#if defined (__BEOS__)
          339  +#  ifdef ZLIB_DLL
          340  +#    ifdef ZLIB_INTERNAL
          341  +#      define ZEXPORT   __declspec(dllexport)
          342  +#      define ZEXPORTVA __declspec(dllexport)
          343  +#    else
          344  +#      define ZEXPORT   __declspec(dllimport)
          345  +#      define ZEXPORTVA __declspec(dllimport)
          346  +#    endif
          347  +#  endif
          348  +#endif
          349  +
          350  +#ifndef ZEXTERN
          351  +#  define ZEXTERN extern
          352  +#endif
          353  +#ifndef ZEXPORT
          354  +#  define ZEXPORT
          355  +#endif
          356  +#ifndef ZEXPORTVA
          357  +#  define ZEXPORTVA
          358  +#endif
          359  +
          360  +#ifndef FAR
          361  +#  define FAR
          362  +#endif
          363  +
          364  +#if !defined(__MACTYPES__)
          365  +typedef unsigned char  Byte;  /* 8 bits */
          366  +#endif
          367  +typedef unsigned int   uInt;  /* 16 bits or more */
          368  +typedef unsigned long  uLong; /* 32 bits or more */
          369  +
          370  +#ifdef SMALL_MEDIUM
          371  +   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
          372  +#  define Bytef Byte FAR
          373  +#else
          374  +   typedef Byte  FAR Bytef;
          375  +#endif
          376  +typedef char  FAR charf;
          377  +typedef int   FAR intf;
          378  +typedef uInt  FAR uIntf;
          379  +typedef uLong FAR uLongf;
          380  +
          381  +#ifdef STDC
          382  +   typedef void const *voidpc;
          383  +   typedef void FAR   *voidpf;
          384  +   typedef void       *voidp;
          385  +#else
          386  +   typedef Byte const *voidpc;
          387  +   typedef Byte FAR   *voidpf;
          388  +   typedef Byte       *voidp;
          389  +#endif
          390  +
          391  +/* ./configure may #define Z_U4 here */
          392  +
          393  +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
          394  +#  include <limits.h>
          395  +#  if (UINT_MAX == 0xffffffffUL)
          396  +#    define Z_U4 unsigned
          397  +#  else
          398  +#    if (ULONG_MAX == 0xffffffffUL)
          399  +#      define Z_U4 unsigned long
          400  +#    else
          401  +#      if (USHRT_MAX == 0xffffffffUL)
          402  +#        define Z_U4 unsigned short
          403  +#      endif
          404  +#    endif
          405  +#  endif
          406  +#endif
          407  +
          408  +#ifdef Z_U4
          409  +   typedef Z_U4 z_crc_t;
          410  +#else
          411  +   typedef unsigned long z_crc_t;
          412  +#endif
          413  +
          414  +#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
          415  +#  define Z_HAVE_UNISTD_H
          416  +#endif
          417  +
          418  +#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
          419  +#  define Z_HAVE_STDARG_H
          420  +#endif
          421  +
          422  +#ifdef STDC
          423  +#  ifndef Z_SOLO
          424  +#    include <sys/types.h>      /* for off_t */
          425  +#  endif
          426  +#endif
          427  +
          428  +#ifdef _WIN32
          429  +#  include <stddef.h>           /* for wchar_t */
          430  +#endif
          431  +
          432  +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
          433  + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
          434  + * though the former does not conform to the LFS document), but considering
          435  + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
          436  + * equivalently requesting no 64-bit operations
          437  + */
          438  +#if defined(LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
          439  +#  undef _LARGEFILE64_SOURCE
          440  +#endif
          441  +
          442  +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
          443  +#  define Z_HAVE_UNISTD_H
          444  +#endif
          445  +#ifndef Z_SOLO
          446  +#  if defined(Z_HAVE_UNISTD_H) || defined(LARGEFILE64_SOURCE)
          447  +#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
          448  +#    ifdef VMS
          449  +#      include <unixio.h>       /* for off_t */
          450  +#    endif
          451  +#    ifndef z_off_t
          452  +#      define z_off_t off_t
          453  +#    endif
          454  +#  endif
          455  +#endif
          456  +
          457  +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
          458  +#  define Z_LFS64
          459  +#endif
          460  +
          461  +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
          462  +#  define Z_LARGE64
          463  +#endif
          464  +
          465  +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
          466  +#  define Z_WANT64
          467  +#endif
          468  +
          469  +#if !defined(SEEK_SET) && !defined(Z_SOLO)
          470  +#  define SEEK_SET        0       /* Seek from beginning of file.  */
          471  +#  define SEEK_CUR        1       /* Seek from current position.  */
          472  +#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
          473  +#endif
          474  +
          475  +#ifndef z_off_t
          476  +#  define z_off_t long
          477  +#endif
          478  +
          479  +#if !defined(_WIN32) && defined(Z_LARGE64)
          480  +#  define z_off64_t off64_t
          481  +#else
          482  +#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
          483  +#    define z_off64_t __int64
          484  +#  else
          485  +#    define z_off64_t z_off_t
          486  +#  endif
          487  +#endif
          488  +
          489  +/* MVS linker does not support external names larger than 8 bytes */
          490  +#if defined(__MVS__)
          491  +  #pragma map(deflateInit_,"DEIN")
          492  +  #pragma map(deflateInit2_,"DEIN2")
          493  +  #pragma map(deflateEnd,"DEEND")
          494  +  #pragma map(deflateBound,"DEBND")
          495  +  #pragma map(inflateInit_,"ININ")
          496  +  #pragma map(inflateInit2_,"ININ2")
          497  +  #pragma map(inflateEnd,"INEND")
          498  +  #pragma map(inflateSync,"INSY")
          499  +  #pragma map(inflateSetDictionary,"INSEDI")
          500  +  #pragma map(compressBound,"CMBND")
          501  +  #pragma map(inflate_table,"INTABL")
          502  +  #pragma map(inflate_fast,"INFA")
          503  +  #pragma map(inflate_copyright,"INCOPY")
          504  +#endif
          505  +
          506  +#endif /* ZCONF_H */

Added compat/zlib/zconf.h.cmakein.

            1  +/* zconf.h -- configuration of the zlib compression library
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#ifndef ZCONF_H
            9  +#define ZCONF_H
           10  +#cmakedefine Z_PREFIX
           11  +#cmakedefine Z_HAVE_UNISTD_H
           12  +
           13  +/*
           14  + * If you *really* need a unique prefix for all types and library functions,
           15  + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
           16  + * Even better than compiling with -DZ_PREFIX would be to use configure to set
           17  + * this permanently in zconf.h using "./configure --zprefix".
           18  + */
           19  +#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */
           20  +#  define Z_PREFIX_SET
           21  +
           22  +/* all linked symbols */
           23  +#  define _dist_code            z__dist_code
           24  +#  define _length_code          z__length_code
           25  +#  define _tr_align             z__tr_align
           26  +#  define _tr_flush_block       z__tr_flush_block
           27  +#  define _tr_init              z__tr_init
           28  +#  define _tr_stored_block      z__tr_stored_block
           29  +#  define _tr_tally             z__tr_tally
           30  +#  define adler32               z_adler32
           31  +#  define adler32_combine       z_adler32_combine
           32  +#  define adler32_combine64     z_adler32_combine64
           33  +#  ifndef Z_SOLO
           34  +#    define compress              z_compress
           35  +#    define compress2             z_compress2
           36  +#    define compressBound         z_compressBound
           37  +#  endif
           38  +#  define crc32                 z_crc32
           39  +#  define crc32_combine         z_crc32_combine
           40  +#  define crc32_combine64       z_crc32_combine64
           41  +#  define deflate               z_deflate
           42  +#  define deflateBound          z_deflateBound
           43  +#  define deflateCopy           z_deflateCopy
           44  +#  define deflateEnd            z_deflateEnd
           45  +#  define deflateInit2_         z_deflateInit2_
           46  +#  define deflateInit_          z_deflateInit_
           47  +#  define deflateParams         z_deflateParams
           48  +#  define deflatePending        z_deflatePending
           49  +#  define deflatePrime          z_deflatePrime
           50  +#  define deflateReset          z_deflateReset
           51  +#  define deflateResetKeep      z_deflateResetKeep
           52  +#  define deflateSetDictionary  z_deflateSetDictionary
           53  +#  define deflateSetHeader      z_deflateSetHeader
           54  +#  define deflateTune           z_deflateTune
           55  +#  define deflate_copyright     z_deflate_copyright
           56  +#  define get_crc_table         z_get_crc_table
           57  +#  ifndef Z_SOLO
           58  +#    define gz_error              z_gz_error
           59  +#    define gz_intmax             z_gz_intmax
           60  +#    define gz_strwinerror        z_gz_strwinerror
           61  +#    define gzbuffer              z_gzbuffer
           62  +#    define gzclearerr            z_gzclearerr
           63  +#    define gzclose               z_gzclose
           64  +#    define gzclose_r             z_gzclose_r
           65  +#    define gzclose_w             z_gzclose_w
           66  +#    define gzdirect              z_gzdirect
           67  +#    define gzdopen               z_gzdopen
           68  +#    define gzeof                 z_gzeof
           69  +#    define gzerror               z_gzerror
           70  +#    define gzflush               z_gzflush
           71  +#    define gzgetc                z_gzgetc
           72  +#    define gzgetc_               z_gzgetc_
           73  +#    define gzgets                z_gzgets
           74  +#    define gzoffset              z_gzoffset
           75  +#    define gzoffset64            z_gzoffset64
           76  +#    define gzopen                z_gzopen
           77  +#    define gzopen64              z_gzopen64
           78  +#    ifdef _WIN32
           79  +#      define gzopen_w              z_gzopen_w
           80  +#    endif
           81  +#    define gzprintf              z_gzprintf
           82  +#    define gzputc                z_gzputc
           83  +#    define gzputs                z_gzputs
           84  +#    define gzread                z_gzread
           85  +#    define gzrewind              z_gzrewind
           86  +#    define gzseek                z_gzseek
           87  +#    define gzseek64              z_gzseek64
           88  +#    define gzsetparams           z_gzsetparams
           89  +#    define gztell                z_gztell
           90  +#    define gztell64              z_gztell64
           91  +#    define gzungetc              z_gzungetc
           92  +#    define gzwrite               z_gzwrite
           93  +#  endif
           94  +#  define inflate               z_inflate
           95  +#  define inflateBack           z_inflateBack
           96  +#  define inflateBackEnd        z_inflateBackEnd
           97  +#  define inflateBackInit_      z_inflateBackInit_
           98  +#  define inflateCopy           z_inflateCopy
           99  +#  define inflateEnd            z_inflateEnd
          100  +#  define inflateGetHeader      z_inflateGetHeader
          101  +#  define inflateInit2_         z_inflateInit2_
          102  +#  define inflateInit_          z_inflateInit_
          103  +#  define inflateMark           z_inflateMark
          104  +#  define inflatePrime          z_inflatePrime
          105  +#  define inflateReset          z_inflateReset
          106  +#  define inflateReset2         z_inflateReset2
          107  +#  define inflateSetDictionary  z_inflateSetDictionary
          108  +#  define inflateSync           z_inflateSync
          109  +#  define inflateSyncPoint      z_inflateSyncPoint
          110  +#  define inflateUndermine      z_inflateUndermine
          111  +#  define inflateResetKeep      z_inflateResetKeep
          112  +#  define inflate_copyright     z_inflate_copyright
          113  +#  define inflate_fast          z_inflate_fast
          114  +#  define inflate_table         z_inflate_table
          115  +#  ifndef Z_SOLO
          116  +#    define uncompress            z_uncompress
          117  +#  endif
          118  +#  define zError                z_zError
          119  +#  ifndef Z_SOLO
          120  +#    define zcalloc               z_zcalloc
          121  +#    define zcfree                z_zcfree
          122  +#  endif
          123  +#  define zlibCompileFlags      z_zlibCompileFlags
          124  +#  define zlibVersion           z_zlibVersion
          125  +
          126  +/* all zlib typedefs in zlib.h and zconf.h */
          127  +#  define Byte                  z_Byte
          128  +#  define Bytef                 z_Bytef
          129  +#  define alloc_func            z_alloc_func
          130  +#  define charf                 z_charf
          131  +#  define free_func             z_free_func
          132  +#  ifndef Z_SOLO
          133  +#    define gzFile                z_gzFile
          134  +#  endif
          135  +#  define gz_header             z_gz_header
          136  +#  define gz_headerp            z_gz_headerp
          137  +#  define in_func               z_in_func
          138  +#  define intf                  z_intf
          139  +#  define out_func              z_out_func
          140  +#  define uInt                  z_uInt
          141  +#  define uIntf                 z_uIntf
          142  +#  define uLong                 z_uLong
          143  +#  define uLongf                z_uLongf
          144  +#  define voidp                 z_voidp
          145  +#  define voidpc                z_voidpc
          146  +#  define voidpf                z_voidpf
          147  +
          148  +/* all zlib structs in zlib.h and zconf.h */
          149  +#  define gz_header_s           z_gz_header_s
          150  +#  define internal_state        z_internal_state
          151  +
          152  +#endif
          153  +
          154  +#if defined(__MSDOS__) && !defined(MSDOS)
          155  +#  define MSDOS
          156  +#endif
          157  +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
          158  +#  define OS2
          159  +#endif
          160  +#if defined(_WINDOWS) && !defined(WINDOWS)
          161  +#  define WINDOWS
          162  +#endif
          163  +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
          164  +#  ifndef WIN32
          165  +#    define WIN32
          166  +#  endif
          167  +#endif
          168  +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
          169  +#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
          170  +#    ifndef SYS16BIT
          171  +#      define SYS16BIT
          172  +#    endif
          173  +#  endif
          174  +#endif
          175  +
          176  +/*
          177  + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
          178  + * than 64k bytes at a time (needed on systems with 16-bit int).
          179  + */
          180  +#ifdef SYS16BIT
          181  +#  define MAXSEG_64K
          182  +#endif
          183  +#ifdef MSDOS
          184  +#  define UNALIGNED_OK
          185  +#endif
          186  +
          187  +#ifdef __STDC_VERSION__
          188  +#  ifndef STDC
          189  +#    define STDC
          190  +#  endif
          191  +#  if __STDC_VERSION__ >= 199901L
          192  +#    ifndef STDC99
          193  +#      define STDC99
          194  +#    endif
          195  +#  endif
          196  +#endif
          197  +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
          198  +#  define STDC
          199  +#endif
          200  +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
          201  +#  define STDC
          202  +#endif
          203  +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
          204  +#  define STDC
          205  +#endif
          206  +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
          207  +#  define STDC
          208  +#endif
          209  +
          210  +#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
          211  +#  define STDC
          212  +#endif
          213  +
          214  +#ifndef STDC
          215  +#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
          216  +#    define const       /* note: need a more gentle solution here */
          217  +#  endif
          218  +#endif
          219  +
          220  +#if defined(ZLIB_CONST) && !defined(z_const)
          221  +#  define z_const const
          222  +#else
          223  +#  define z_const
          224  +#endif
          225  +
          226  +/* Some Mac compilers merge all .h files incorrectly: */
          227  +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
          228  +#  define NO_DUMMY_DECL
          229  +#endif
          230  +
          231  +/* Maximum value for memLevel in deflateInit2 */
          232  +#ifndef MAX_MEM_LEVEL
          233  +#  ifdef MAXSEG_64K
          234  +#    define MAX_MEM_LEVEL 8
          235  +#  else
          236  +#    define MAX_MEM_LEVEL 9
          237  +#  endif
          238  +#endif
          239  +
          240  +/* Maximum value for windowBits in deflateInit2 and inflateInit2.
          241  + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
          242  + * created by gzip. (Files created by minigzip can still be extracted by
          243  + * gzip.)
          244  + */
          245  +#ifndef MAX_WBITS
          246  +#  define MAX_WBITS   15 /* 32K LZ77 window */
          247  +#endif
          248  +
          249  +/* The memory requirements for deflate are (in bytes):
          250  +            (1 << (windowBits+2)) +  (1 << (memLevel+9))
          251  + that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
          252  + plus a few kilobytes for small objects. For example, if you want to reduce
          253  + the default memory requirements from 256K to 128K, compile with
          254  +     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
          255  + Of course this will generally degrade compression (there's no free lunch).
          256  +
          257  +   The memory requirements for inflate are (in bytes) 1 << windowBits
          258  + that is, 32K for windowBits=15 (default value) plus a few kilobytes
          259  + for small objects.
          260  +*/
          261  +
          262  +                        /* Type declarations */
          263  +
          264  +#ifndef OF /* function prototypes */
          265  +#  ifdef STDC
          266  +#    define OF(args)  args
          267  +#  else
          268  +#    define OF(args)  ()
          269  +#  endif
          270  +#endif
          271  +
          272  +#ifndef Z_ARG /* function prototypes for stdarg */
          273  +#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
          274  +#    define Z_ARG(args)  args
          275  +#  else
          276  +#    define Z_ARG(args)  ()
          277  +#  endif
          278  +#endif
          279  +
          280  +/* The following definitions for FAR are needed only for MSDOS mixed
          281  + * model programming (small or medium model with some far allocations).
          282  + * This was tested only with MSC; for other MSDOS compilers you may have
          283  + * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
          284  + * just define FAR to be empty.
          285  + */
          286  +#ifdef SYS16BIT
          287  +#  if defined(M_I86SM) || defined(M_I86MM)
          288  +     /* MSC small or medium model */
          289  +#    define SMALL_MEDIUM
          290  +#    ifdef _MSC_VER
          291  +#      define FAR _far
          292  +#    else
          293  +#      define FAR far
          294  +#    endif
          295  +#  endif
          296  +#  if (defined(__SMALL__) || defined(__MEDIUM__))
          297  +     /* Turbo C small or medium model */
          298  +#    define SMALL_MEDIUM
          299  +#    ifdef __BORLANDC__
          300  +#      define FAR _far
          301  +#    else
          302  +#      define FAR far
          303  +#    endif
          304  +#  endif
          305  +#endif
          306  +
          307  +#if defined(WINDOWS) || defined(WIN32)
          308  +   /* If building or using zlib as a DLL, define ZLIB_DLL.
          309  +    * This is not mandatory, but it offers a little performance increase.
          310  +    */
          311  +#  ifdef ZLIB_DLL
          312  +#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
          313  +#      ifdef ZLIB_INTERNAL
          314  +#        define ZEXTERN extern __declspec(dllexport)
          315  +#      else
          316  +#        define ZEXTERN extern __declspec(dllimport)
          317  +#      endif
          318  +#    endif
          319  +#  endif  /* ZLIB_DLL */
          320  +   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
          321  +    * define ZLIB_WINAPI.
          322  +    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
          323  +    */
          324  +#  ifdef ZLIB_WINAPI
          325  +#    ifdef FAR
          326  +#      undef FAR
          327  +#    endif
          328  +#    include <windows.h>
          329  +     /* No need for _export, use ZLIB.DEF instead. */
          330  +     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
          331  +#    define ZEXPORT WINAPI
          332  +#    ifdef WIN32
          333  +#      define ZEXPORTVA WINAPIV
          334  +#    else
          335  +#      define ZEXPORTVA FAR CDECL
          336  +#    endif
          337  +#  endif
          338  +#endif
          339  +
          340  +#if defined (__BEOS__)
          341  +#  ifdef ZLIB_DLL
          342  +#    ifdef ZLIB_INTERNAL
          343  +#      define ZEXPORT   __declspec(dllexport)
          344  +#      define ZEXPORTVA __declspec(dllexport)
          345  +#    else
          346  +#      define ZEXPORT   __declspec(dllimport)
          347  +#      define ZEXPORTVA __declspec(dllimport)
          348  +#    endif
          349  +#  endif
          350  +#endif
          351  +
          352  +#ifndef ZEXTERN
          353  +#  define ZEXTERN extern
          354  +#endif
          355  +#ifndef ZEXPORT
          356  +#  define ZEXPORT
          357  +#endif
          358  +#ifndef ZEXPORTVA
          359  +#  define ZEXPORTVA
          360  +#endif
          361  +
          362  +#ifndef FAR
          363  +#  define FAR
          364  +#endif
          365  +
          366  +#if !defined(__MACTYPES__)
          367  +typedef unsigned char  Byte;  /* 8 bits */
          368  +#endif
          369  +typedef unsigned int   uInt;  /* 16 bits or more */
          370  +typedef unsigned long  uLong; /* 32 bits or more */
          371  +
          372  +#ifdef SMALL_MEDIUM
          373  +   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
          374  +#  define Bytef Byte FAR
          375  +#else
          376  +   typedef Byte  FAR Bytef;
          377  +#endif
          378  +typedef char  FAR charf;
          379  +typedef int   FAR intf;
          380  +typedef uInt  FAR uIntf;
          381  +typedef uLong FAR uLongf;
          382  +
          383  +#ifdef STDC
          384  +   typedef void const *voidpc;
          385  +   typedef void FAR   *voidpf;
          386  +   typedef void       *voidp;
          387  +#else
          388  +   typedef Byte const *voidpc;
          389  +   typedef Byte FAR   *voidpf;
          390  +   typedef Byte       *voidp;
          391  +#endif
          392  +
          393  +/* ./configure may #define Z_U4 here */
          394  +
          395  +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
          396  +#  include <limits.h>
          397  +#  if (UINT_MAX == 0xffffffffUL)
          398  +#    define Z_U4 unsigned
          399  +#  else
          400  +#    if (ULONG_MAX == 0xffffffffUL)
          401  +#      define Z_U4 unsigned long
          402  +#    else
          403  +#      if (USHRT_MAX == 0xffffffffUL)
          404  +#        define Z_U4 unsigned short
          405  +#      endif
          406  +#    endif
          407  +#  endif
          408  +#endif
          409  +
          410  +#ifdef Z_U4
          411  +   typedef Z_U4 z_crc_t;
          412  +#else
          413  +   typedef unsigned long z_crc_t;
          414  +#endif
          415  +
          416  +#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
          417  +#  define Z_HAVE_UNISTD_H
          418  +#endif
          419  +
          420  +#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
          421  +#  define Z_HAVE_STDARG_H
          422  +#endif
          423  +
          424  +#ifdef STDC
          425  +#  ifndef Z_SOLO
          426  +#    include <sys/types.h>      /* for off_t */
          427  +#  endif
          428  +#endif
          429  +
          430  +#ifdef _WIN32
          431  +#  include <stddef.h>           /* for wchar_t */
          432  +#endif
          433  +
          434  +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
          435  + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
          436  + * though the former does not conform to the LFS document), but considering
          437  + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
          438  + * equivalently requesting no 64-bit operations
          439  + */
          440  +#if defined(LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
          441  +#  undef _LARGEFILE64_SOURCE
          442  +#endif
          443  +
          444  +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
          445  +#  define Z_HAVE_UNISTD_H
          446  +#endif
          447  +#ifndef Z_SOLO
          448  +#  if defined(Z_HAVE_UNISTD_H) || defined(LARGEFILE64_SOURCE)
          449  +#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
          450  +#    ifdef VMS
          451  +#      include <unixio.h>       /* for off_t */
          452  +#    endif
          453  +#    ifndef z_off_t
          454  +#      define z_off_t off_t
          455  +#    endif
          456  +#  endif
          457  +#endif
          458  +
          459  +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
          460  +#  define Z_LFS64
          461  +#endif
          462  +
          463  +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
          464  +#  define Z_LARGE64
          465  +#endif
          466  +
          467  +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
          468  +#  define Z_WANT64
          469  +#endif
          470  +
          471  +#if !defined(SEEK_SET) && !defined(Z_SOLO)
          472  +#  define SEEK_SET        0       /* Seek from beginning of file.  */
          473  +#  define SEEK_CUR        1       /* Seek from current position.  */
          474  +#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
          475  +#endif
          476  +
          477  +#ifndef z_off_t
          478  +#  define z_off_t long
          479  +#endif
          480  +
          481  +#if !defined(_WIN32) && defined(Z_LARGE64)
          482  +#  define z_off64_t off64_t
          483  +#else
          484  +#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
          485  +#    define z_off64_t __int64
          486  +#  else
          487  +#    define z_off64_t z_off_t
          488  +#  endif
          489  +#endif
          490  +
          491  +/* MVS linker does not support external names larger than 8 bytes */
          492  +#if defined(__MVS__)
          493  +  #pragma map(deflateInit_,"DEIN")
          494  +  #pragma map(deflateInit2_,"DEIN2")
          495  +  #pragma map(deflateEnd,"DEEND")
          496  +  #pragma map(deflateBound,"DEBND")
          497  +  #pragma map(inflateInit_,"ININ")
          498  +  #pragma map(inflateInit2_,"ININ2")
          499  +  #pragma map(inflateEnd,"INEND")
          500  +  #pragma map(inflateSync,"INSY")
          501  +  #pragma map(inflateSetDictionary,"INSEDI")
          502  +  #pragma map(compressBound,"CMBND")
          503  +  #pragma map(inflate_table,"INTABL")
          504  +  #pragma map(inflate_fast,"INFA")
          505  +  #pragma map(inflate_copyright,"INCOPY")
          506  +#endif
          507  +
          508  +#endif /* ZCONF_H */

Added compat/zlib/zconf.h.in.

            1  +/* zconf.h -- configuration of the zlib compression library
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#ifndef ZCONF_H
            9  +#define ZCONF_H
           10  +
           11  +/*
           12  + * If you *really* need a unique prefix for all types and library functions,
           13  + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
           14  + * Even better than compiling with -DZ_PREFIX would be to use configure to set
           15  + * this permanently in zconf.h using "./configure --zprefix".
           16  + */
           17  +#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */
           18  +#  define Z_PREFIX_SET
           19  +
           20  +/* all linked symbols */
           21  +#  define _dist_code            z__dist_code
           22  +#  define _length_code          z__length_code
           23  +#  define _tr_align             z__tr_align
           24  +#  define _tr_flush_block       z__tr_flush_block
           25  +#  define _tr_init              z__tr_init
           26  +#  define _tr_stored_block      z__tr_stored_block
           27  +#  define _tr_tally             z__tr_tally
           28  +#  define adler32               z_adler32
           29  +#  define adler32_combine       z_adler32_combine
           30  +#  define adler32_combine64     z_adler32_combine64
           31  +#  ifndef Z_SOLO
           32  +#    define compress              z_compress
           33  +#    define compress2             z_compress2
           34  +#    define compressBound         z_compressBound
           35  +#  endif
           36  +#  define crc32                 z_crc32
           37  +#  define crc32_combine         z_crc32_combine
           38  +#  define crc32_combine64       z_crc32_combine64
           39  +#  define deflate               z_deflate
           40  +#  define deflateBound          z_deflateBound
           41  +#  define deflateCopy           z_deflateCopy
           42  +#  define deflateEnd            z_deflateEnd
           43  +#  define deflateInit2_         z_deflateInit2_
           44  +#  define deflateInit_          z_deflateInit_
           45  +#  define deflateParams         z_deflateParams
           46  +#  define deflatePending        z_deflatePending
           47  +#  define deflatePrime          z_deflatePrime
           48  +#  define deflateReset          z_deflateReset
           49  +#  define deflateResetKeep      z_deflateResetKeep
           50  +#  define deflateSetDictionary  z_deflateSetDictionary
           51  +#  define deflateSetHeader      z_deflateSetHeader
           52  +#  define deflateTune           z_deflateTune
           53  +#  define deflate_copyright     z_deflate_copyright
           54  +#  define get_crc_table         z_get_crc_table
           55  +#  ifndef Z_SOLO
           56  +#    define gz_error              z_gz_error
           57  +#    define gz_intmax             z_gz_intmax
           58  +#    define gz_strwinerror        z_gz_strwinerror
           59  +#    define gzbuffer              z_gzbuffer
           60  +#    define gzclearerr            z_gzclearerr
           61  +#    define gzclose               z_gzclose
           62  +#    define gzclose_r             z_gzclose_r
           63  +#    define gzclose_w             z_gzclose_w
           64  +#    define gzdirect              z_gzdirect
           65  +#    define gzdopen               z_gzdopen
           66  +#    define gzeof                 z_gzeof
           67  +#    define gzerror               z_gzerror
           68  +#    define gzflush               z_gzflush
           69  +#    define gzgetc                z_gzgetc
           70  +#    define gzgetc_               z_gzgetc_
           71  +#    define gzgets                z_gzgets
           72  +#    define gzoffset              z_gzoffset
           73  +#    define gzoffset64            z_gzoffset64
           74  +#    define gzopen                z_gzopen
           75  +#    define gzopen64              z_gzopen64
           76  +#    ifdef _WIN32
           77  +#      define gzopen_w              z_gzopen_w
           78  +#    endif
           79  +#    define gzprintf              z_gzprintf
           80  +#    define gzputc                z_gzputc
           81  +#    define gzputs                z_gzputs
           82  +#    define gzread                z_gzread
           83  +#    define gzrewind              z_gzrewind
           84  +#    define gzseek                z_gzseek
           85  +#    define gzseek64              z_gzseek64
           86  +#    define gzsetparams           z_gzsetparams
           87  +#    define gztell                z_gztell
           88  +#    define gztell64              z_gztell64
           89  +#    define gzungetc              z_gzungetc
           90  +#    define gzwrite               z_gzwrite
           91  +#  endif
           92  +#  define inflate               z_inflate
           93  +#  define inflateBack           z_inflateBack
           94  +#  define inflateBackEnd        z_inflateBackEnd
           95  +#  define inflateBackInit_      z_inflateBackInit_
           96  +#  define inflateCopy           z_inflateCopy
           97  +#  define inflateEnd            z_inflateEnd
           98  +#  define inflateGetHeader      z_inflateGetHeader
           99  +#  define inflateInit2_         z_inflateInit2_
          100  +#  define inflateInit_          z_inflateInit_
          101  +#  define inflateMark           z_inflateMark
          102  +#  define inflatePrime          z_inflatePrime
          103  +#  define inflateReset          z_inflateReset
          104  +#  define inflateReset2         z_inflateReset2
          105  +#  define inflateSetDictionary  z_inflateSetDictionary
          106  +#  define inflateSync           z_inflateSync
          107  +#  define inflateSyncPoint      z_inflateSyncPoint
          108  +#  define inflateUndermine      z_inflateUndermine
          109  +#  define inflateResetKeep      z_inflateResetKeep
          110  +#  define inflate_copyright     z_inflate_copyright
          111  +#  define inflate_fast          z_inflate_fast
          112  +#  define inflate_table         z_inflate_table
          113  +#  ifndef Z_SOLO
          114  +#    define uncompress            z_uncompress
          115  +#  endif
          116  +#  define zError                z_zError
          117  +#  ifndef Z_SOLO
          118  +#    define zcalloc               z_zcalloc
          119  +#    define zcfree                z_zcfree
          120  +#  endif
          121  +#  define zlibCompileFlags      z_zlibCompileFlags
          122  +#  define zlibVersion           z_zlibVersion
          123  +
          124  +/* all zlib typedefs in zlib.h and zconf.h */
          125  +#  define Byte                  z_Byte
          126  +#  define Bytef                 z_Bytef
          127  +#  define alloc_func            z_alloc_func
          128  +#  define charf                 z_charf
          129  +#  define free_func             z_free_func
          130  +#  ifndef Z_SOLO
          131  +#    define gzFile                z_gzFile
          132  +#  endif
          133  +#  define gz_header             z_gz_header
          134  +#  define gz_headerp            z_gz_headerp
          135  +#  define in_func               z_in_func
          136  +#  define intf                  z_intf
          137  +#  define out_func              z_out_func
          138  +#  define uInt                  z_uInt
          139  +#  define uIntf                 z_uIntf
          140  +#  define uLong                 z_uLong
          141  +#  define uLongf                z_uLongf
          142  +#  define voidp                 z_voidp
          143  +#  define voidpc                z_voidpc
          144  +#  define voidpf                z_voidpf
          145  +
          146  +/* all zlib structs in zlib.h and zconf.h */
          147  +#  define gz_header_s           z_gz_header_s
          148  +#  define internal_state        z_internal_state
          149  +
          150  +#endif
          151  +
          152  +#if defined(__MSDOS__) && !defined(MSDOS)
          153  +#  define MSDOS
          154  +#endif
          155  +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
          156  +#  define OS2
          157  +#endif
          158  +#if defined(_WINDOWS) && !defined(WINDOWS)
          159  +#  define WINDOWS
          160  +#endif
          161  +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
          162  +#  ifndef WIN32
          163  +#    define WIN32
          164  +#  endif
          165  +#endif
          166  +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
          167  +#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
          168  +#    ifndef SYS16BIT
          169  +#      define SYS16BIT
          170  +#    endif
          171  +#  endif
          172  +#endif
          173  +
          174  +/*
          175  + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
          176  + * than 64k bytes at a time (needed on systems with 16-bit int).
          177  + */
          178  +#ifdef SYS16BIT
          179  +#  define MAXSEG_64K
          180  +#endif
          181  +#ifdef MSDOS
          182  +#  define UNALIGNED_OK
          183  +#endif
          184  +
          185  +#ifdef __STDC_VERSION__
          186  +#  ifndef STDC
          187  +#    define STDC
          188  +#  endif
          189  +#  if __STDC_VERSION__ >= 199901L
          190  +#    ifndef STDC99
          191  +#      define STDC99
          192  +#    endif
          193  +#  endif
          194  +#endif
          195  +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
          196  +#  define STDC
          197  +#endif
          198  +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
          199  +#  define STDC
          200  +#endif
          201  +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
          202  +#  define STDC
          203  +#endif
          204  +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
          205  +#  define STDC
          206  +#endif
          207  +
          208  +#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
          209  +#  define STDC
          210  +#endif
          211  +
          212  +#ifndef STDC
          213  +#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
          214  +#    define const       /* note: need a more gentle solution here */
          215  +#  endif
          216  +#endif
          217  +
          218  +#if defined(ZLIB_CONST) && !defined(z_const)
          219  +#  define z_const const
          220  +#else
          221  +#  define z_const
          222  +#endif
          223  +
          224  +/* Some Mac compilers merge all .h files incorrectly: */
          225  +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
          226  +#  define NO_DUMMY_DECL
          227  +#endif
          228  +
          229  +/* Maximum value for memLevel in deflateInit2 */
          230  +#ifndef MAX_MEM_LEVEL
          231  +#  ifdef MAXSEG_64K
          232  +#    define MAX_MEM_LEVEL 8
          233  +#  else
          234  +#    define MAX_MEM_LEVEL 9
          235  +#  endif
          236  +#endif
          237  +
          238  +/* Maximum value for windowBits in deflateInit2 and inflateInit2.
          239  + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
          240  + * created by gzip. (Files created by minigzip can still be extracted by
          241  + * gzip.)
          242  + */
          243  +#ifndef MAX_WBITS
          244  +#  define MAX_WBITS   15 /* 32K LZ77 window */
          245  +#endif
          246  +
          247  +/* The memory requirements for deflate are (in bytes):
          248  +            (1 << (windowBits+2)) +  (1 << (memLevel+9))
          249  + that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
          250  + plus a few kilobytes for small objects. For example, if you want to reduce
          251  + the default memory requirements from 256K to 128K, compile with
          252  +     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
          253  + Of course this will generally degrade compression (there's no free lunch).
          254  +
          255  +   The memory requirements for inflate are (in bytes) 1 << windowBits
          256  + that is, 32K for windowBits=15 (default value) plus a few kilobytes
          257  + for small objects.
          258  +*/
          259  +
          260  +                        /* Type declarations */
          261  +
          262  +#ifndef OF /* function prototypes */
          263  +#  ifdef STDC
          264  +#    define OF(args)  args
          265  +#  else
          266  +#    define OF(args)  ()
          267  +#  endif
          268  +#endif
          269  +
          270  +#ifndef Z_ARG /* function prototypes for stdarg */
          271  +#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
          272  +#    define Z_ARG(args)  args
          273  +#  else
          274  +#    define Z_ARG(args)  ()
          275  +#  endif
          276  +#endif
          277  +
          278  +/* The following definitions for FAR are needed only for MSDOS mixed
          279  + * model programming (small or medium model with some far allocations).
          280  + * This was tested only with MSC; for other MSDOS compilers you may have
          281  + * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
          282  + * just define FAR to be empty.
          283  + */
          284  +#ifdef SYS16BIT
          285  +#  if defined(M_I86SM) || defined(M_I86MM)
          286  +     /* MSC small or medium model */
          287  +#    define SMALL_MEDIUM
          288  +#    ifdef _MSC_VER
          289  +#      define FAR _far
          290  +#    else
          291  +#      define FAR far
          292  +#    endif
          293  +#  endif
          294  +#  if (defined(__SMALL__) || defined(__MEDIUM__))
          295  +     /* Turbo C small or medium model */
          296  +#    define SMALL_MEDIUM
          297  +#    ifdef __BORLANDC__
          298  +#      define FAR _far
          299  +#    else
          300  +#      define FAR far
          301  +#    endif
          302  +#  endif
          303  +#endif
          304  +
          305  +#if defined(WINDOWS) || defined(WIN32)
          306  +   /* If building or using zlib as a DLL, define ZLIB_DLL.
          307  +    * This is not mandatory, but it offers a little performance increase.
          308  +    */
          309  +#  ifdef ZLIB_DLL
          310  +#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
          311  +#      ifdef ZLIB_INTERNAL
          312  +#        define ZEXTERN extern __declspec(dllexport)
          313  +#      else
          314  +#        define ZEXTERN extern __declspec(dllimport)
          315  +#      endif
          316  +#    endif
          317  +#  endif  /* ZLIB_DLL */
          318  +   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
          319  +    * define ZLIB_WINAPI.
          320  +    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
          321  +    */
          322  +#  ifdef ZLIB_WINAPI
          323  +#    ifdef FAR
          324  +#      undef FAR
          325  +#    endif
          326  +#    include <windows.h>
          327  +     /* No need for _export, use ZLIB.DEF instead. */
          328  +     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
          329  +#    define ZEXPORT WINAPI
          330  +#    ifdef WIN32
          331  +#      define ZEXPORTVA WINAPIV
          332  +#    else
          333  +#      define ZEXPORTVA FAR CDECL
          334  +#    endif
          335  +#  endif
          336  +#endif
          337  +
          338  +#if defined (__BEOS__)
          339  +#  ifdef ZLIB_DLL
          340  +#    ifdef ZLIB_INTERNAL
          341  +#      define ZEXPORT   __declspec(dllexport)
          342  +#      define ZEXPORTVA __declspec(dllexport)
          343  +#    else
          344  +#      define ZEXPORT   __declspec(dllimport)
          345  +#      define ZEXPORTVA __declspec(dllimport)
          346  +#    endif
          347  +#  endif
          348  +#endif
          349  +
          350  +#ifndef ZEXTERN
          351  +#  define ZEXTERN extern
          352  +#endif
          353  +#ifndef ZEXPORT
          354  +#  define ZEXPORT
          355  +#endif
          356  +#ifndef ZEXPORTVA
          357  +#  define ZEXPORTVA
          358  +#endif
          359  +
          360  +#ifndef FAR
          361  +#  define FAR
          362  +#endif
          363  +
          364  +#if !defined(__MACTYPES__)
          365  +typedef unsigned char  Byte;  /* 8 bits */
          366  +#endif
          367  +typedef unsigned int   uInt;  /* 16 bits or more */
          368  +typedef unsigned long  uLong; /* 32 bits or more */
          369  +
          370  +#ifdef SMALL_MEDIUM
          371  +   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
          372  +#  define Bytef Byte FAR
          373  +#else
          374  +   typedef Byte  FAR Bytef;
          375  +#endif
          376  +typedef char  FAR charf;
          377  +typedef int   FAR intf;
          378  +typedef uInt  FAR uIntf;
          379  +typedef uLong FAR uLongf;
          380  +
          381  +#ifdef STDC
          382  +   typedef void const *voidpc;
          383  +   typedef void FAR   *voidpf;
          384  +   typedef void       *voidp;
          385  +#else
          386  +   typedef Byte const *voidpc;
          387  +   typedef Byte FAR   *voidpf;
          388  +   typedef Byte       *voidp;
          389  +#endif
          390  +
          391  +/* ./configure may #define Z_U4 here */
          392  +
          393  +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
          394  +#  include <limits.h>
          395  +#  if (UINT_MAX == 0xffffffffUL)
          396  +#    define Z_U4 unsigned
          397  +#  else
          398  +#    if (ULONG_MAX == 0xffffffffUL)
          399  +#      define Z_U4 unsigned long
          400  +#    else
          401  +#      if (USHRT_MAX == 0xffffffffUL)
          402  +#        define Z_U4 unsigned short
          403  +#      endif
          404  +#    endif
          405  +#  endif
          406  +#endif
          407  +
          408  +#ifdef Z_U4
          409  +   typedef Z_U4 z_crc_t;
          410  +#else
          411  +   typedef unsigned long z_crc_t;
          412  +#endif
          413  +
          414  +#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
          415  +#  define Z_HAVE_UNISTD_H
          416  +#endif
          417  +
          418  +#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
          419  +#  define Z_HAVE_STDARG_H
          420  +#endif
          421  +
          422  +#ifdef STDC
          423  +#  ifndef Z_SOLO
          424  +#    include <sys/types.h>      /* for off_t */
          425  +#  endif
          426  +#endif
          427  +
          428  +#ifdef _WIN32
          429  +#  include <stddef.h>           /* for wchar_t */
          430  +#endif
          431  +
          432  +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
          433  + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
          434  + * though the former does not conform to the LFS document), but considering
          435  + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
          436  + * equivalently requesting no 64-bit operations
          437  + */
          438  +#if defined(LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
          439  +#  undef _LARGEFILE64_SOURCE
          440  +#endif
          441  +
          442  +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
          443  +#  define Z_HAVE_UNISTD_H
          444  +#endif
          445  +#ifndef Z_SOLO
          446  +#  if defined(Z_HAVE_UNISTD_H) || defined(LARGEFILE64_SOURCE)
          447  +#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
          448  +#    ifdef VMS
          449  +#      include <unixio.h>       /* for off_t */
          450  +#    endif
          451  +#    ifndef z_off_t
          452  +#      define z_off_t off_t
          453  +#    endif
          454  +#  endif
          455  +#endif
          456  +
          457  +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
          458  +#  define Z_LFS64
          459  +#endif
          460  +
          461  +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
          462  +#  define Z_LARGE64
          463  +#endif
          464  +
          465  +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
          466  +#  define Z_WANT64
          467  +#endif
          468  +
          469  +#if !defined(SEEK_SET) && !defined(Z_SOLO)
          470  +#  define SEEK_SET        0       /* Seek from beginning of file.  */
          471  +#  define SEEK_CUR        1       /* Seek from current position.  */
          472  +#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
          473  +#endif
          474  +
          475  +#ifndef z_off_t
          476  +#  define z_off_t long
          477  +#endif
          478  +
          479  +#if !defined(_WIN32) && defined(Z_LARGE64)
          480  +#  define z_off64_t off64_t
          481  +#else
          482  +#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
          483  +#    define z_off64_t __int64
          484  +#  else
          485  +#    define z_off64_t z_off_t
          486  +#  endif
          487  +#endif
          488  +
          489  +/* MVS linker does not support external names larger than 8 bytes */
          490  +#if defined(__MVS__)
          491  +  #pragma map(deflateInit_,"DEIN")
          492  +  #pragma map(deflateInit2_,"DEIN2")
          493  +  #pragma map(deflateEnd,"DEEND")
          494  +  #pragma map(deflateBound,"DEBND")
          495  +  #pragma map(inflateInit_,"ININ")
          496  +  #pragma map(inflateInit2_,"ININ2")
          497  +  #pragma map(inflateEnd,"INEND")
          498  +  #pragma map(inflateSync,"INSY")
          499  +  #pragma map(inflateSetDictionary,"INSEDI")
          500  +  #pragma map(compressBound,"CMBND")
          501  +  #pragma map(inflate_table,"INTABL")
          502  +  #pragma map(inflate_fast,"INFA")
          503  +  #pragma map(inflate_copyright,"INCOPY")
          504  +#endif
          505  +
          506  +#endif /* ZCONF_H */

Added compat/zlib/zlib.3.

            1  +.TH ZLIB 3 "2 May 2012"
            2  +.SH NAME
            3  +zlib \- compression/decompression library
            4  +.SH SYNOPSIS
            5  +[see
            6  +.I zlib.h
            7  +for full description]
            8  +.SH DESCRIPTION
            9  +The
           10  +.I zlib
           11  +library is a general purpose data compression library.
           12  +The code is thread safe, assuming that the standard library functions
           13  +used are thread safe, such as memory allocation routines.
           14  +It provides in-memory compression and decompression functions,
           15  +including integrity checks of the uncompressed data.
           16  +This version of the library supports only one compression method (deflation)
           17  +but other algorithms may be added later
           18  +with the same stream interface.
           19  +.LP
           20  +Compression can be done in a single step if the buffers are large enough
           21  +or can be done by repeated calls of the compression function.
           22  +In the latter case,
           23  +the application must provide more input and/or consume the output
           24  +(providing more output space) before each call.
           25  +.LP
           26  +The library also supports reading and writing files in
           27  +.IR gzip (1)
           28  +(.gz) format
           29  +with an interface similar to that of stdio.
           30  +.LP
           31  +The library does not install any signal handler.
           32  +The decoder checks the consistency of the compressed data,
           33  +so the library should never crash even in the case of corrupted input.
           34  +.LP
           35  +All functions of the compression library are documented in the file
           36  +.IR zlib.h .
           37  +The distribution source includes examples of use of the library
           38  +in the files
           39  +.I test/example.c
           40  +and
           41  +.IR test/minigzip.c,
           42  +as well as other examples in the
           43  +.IR examples/
           44  +directory.
           45  +.LP
           46  +Changes to this version are documented in the file
           47  +.I ChangeLog
           48  +that accompanies the source.
           49  +.LP
           50  +.I zlib
           51  +is available in Java using the java.util.zip package:
           52  +.IP
           53  +http://java.sun.com/developer/technicalArticles/Programming/compression/
           54  +.LP
           55  +A Perl interface to
           56  +.IR zlib ,
           57  +written by Paul Marquess (pmqs@cpan.org),
           58  +is available at CPAN (Comprehensive Perl Archive Network) sites,
           59  +including:
           60  +.IP
           61  +http://search.cpan.org/~pmqs/IO-Compress-Zlib/
           62  +.LP
           63  +A Python interface to
           64  +.IR zlib ,
           65  +written by A.M. Kuchling (amk@magnet.com),
           66  +is available in Python 1.5 and later versions:
           67  +.IP
           68  +http://docs.python.org/library/zlib.html
           69  +.LP
           70  +.I zlib
           71  +is built into
           72  +.IR tcl:
           73  +.IP
           74  +http://wiki.tcl.tk/4610
           75  +.LP
           76  +An experimental package to read and write files in .zip format,
           77  +written on top of
           78  +.I zlib
           79  +by Gilles Vollant (info@winimage.com),
           80  +is available at:
           81  +.IP
           82  +http://www.winimage.com/zLibDll/minizip.html
           83  +and also in the
           84  +.I contrib/minizip
           85  +directory of the main
           86  +.I zlib
           87  +source distribution.
           88  +.SH "SEE ALSO"
           89  +The
           90  +.I zlib
           91  +web site can be found at:
           92  +.IP
           93  +http://zlib.net/
           94  +.LP
           95  +The data format used by the zlib library is described by RFC
           96  +(Request for Comments) 1950 to 1952 in the files:
           97  +.IP
           98  +http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format)
           99  +.br
          100  +http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format)
          101  +.br
          102  +http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format)
          103  +.LP
          104  +Mark Nelson wrote an article about
          105  +.I zlib
          106  +for the Jan. 1997 issue of  Dr. Dobb's Journal;
          107  +a copy of the article is available at:
          108  +.IP
          109  +http://marknelson.us/1997/01/01/zlib-engine/
          110  +.SH "REPORTING PROBLEMS"
          111  +Before reporting a problem,
          112  +please check the
          113  +.I zlib
          114  +web site to verify that you have the latest version of
          115  +.IR zlib ;
          116  +otherwise,
          117  +obtain the latest version and see if the problem still exists.
          118  +Please read the
          119  +.I zlib
          120  +FAQ at:
          121  +.IP
          122  +http://zlib.net/zlib_faq.html
          123  +.LP
          124  +before asking for help.
          125  +Send questions and/or comments to zlib@gzip.org,
          126  +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com).
          127  +.SH AUTHORS
          128  +Version 1.2.7
          129  +Copyright (C) 1995-2012 Jean-loup Gailly (jloup@gzip.org)
          130  +and Mark Adler (madler@alumni.caltech.edu).
          131  +.LP
          132  +This software is provided "as-is,"
          133  +without any express or implied warranty.
          134  +In no event will the authors be held liable for any damages
          135  +arising from the use of this software.
          136  +See the distribution directory with respect to requirements
          137  +governing redistribution.
          138  +The deflate format used by
          139  +.I zlib
          140  +was defined by Phil Katz.
          141  +The deflate and
          142  +.I zlib
          143  +specifications were written by L. Peter Deutsch.
          144  +Thanks to all the people who reported problems and suggested various
          145  +improvements in
          146  +.IR zlib ;
          147  +who are too numerous to cite here.
          148  +.LP
          149  +UNIX manual page by R. P. C. Rodgers,
          150  +U.S. National Library of Medicine (rodgers@nlm.nih.gov).
          151  +.\" end of man page

Added compat/zlib/zlib.3.pdf.

cannot compute difference between binary files

Added compat/zlib/zlib.h.

            1  +/* zlib.h -- interface of the 'zlib' general purpose compression library
            2  +  version 1.2.7, May 2nd, 2012
            3  +
            4  +  Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler
            5  +
            6  +  This software is provided 'as-is', without any express or implied
            7  +  warranty.  In no event will the authors be held liable for any damages
            8  +  arising from the use of this software.
            9  +
           10  +  Permission is granted to anyone to use this software for any purpose,
           11  +  including commercial applications, and to alter it and redistribute it
           12  +  freely, subject to the following restrictions:
           13  +
           14  +  1. The origin of this software must not be misrepresented; you must not
           15  +     claim that you wrote the original software. If you use this software
           16  +     in a product, an acknowledgment in the product documentation would be
           17  +     appreciated but is not required.
           18  +  2. Altered source versions must be plainly marked as such, and must not be
           19  +     misrepresented as being the original software.
           20  +  3. This notice may not be removed or altered from any source distribution.
           21  +
           22  +  Jean-loup Gailly        Mark Adler
           23  +  jloup@gzip.org          madler@alumni.caltech.edu
           24  +
           25  +
           26  +  The data format used by the zlib library is described by RFCs (Request for
           27  +  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
           28  +  (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
           29  +*/
           30  +
           31  +#ifndef ZLIB_H
           32  +#define ZLIB_H
           33  +
           34  +#include "zconf.h"
           35  +
           36  +#ifdef __cplusplus
           37  +extern "C" {
           38  +#endif
           39  +
           40  +#define ZLIB_VERSION "1.2.7"
           41  +#define ZLIB_VERNUM 0x1270
           42  +#define ZLIB_VER_MAJOR 1
           43  +#define ZLIB_VER_MINOR 2
           44  +#define ZLIB_VER_REVISION 7
           45  +#define ZLIB_VER_SUBREVISION 0
           46  +
           47  +/*
           48  +    The 'zlib' compression library provides in-memory compression and
           49  +  decompression functions, including integrity checks of the uncompressed data.
           50  +  This version of the library supports only one compression method (deflation)
           51  +  but other algorithms will be added later and will have the same stream
           52  +  interface.
           53  +
           54  +    Compression can be done in a single step if the buffers are large enough,
           55  +  or can be done by repeated calls of the compression function.  In the latter
           56  +  case, the application must provide more input and/or consume the output
           57  +  (providing more output space) before each call.
           58  +
           59  +    The compressed data format used by default by the in-memory functions is
           60  +  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
           61  +  around a deflate stream, which is itself documented in RFC 1951.
           62  +
           63  +    The library also supports reading and writing files in gzip (.gz) format
           64  +  with an interface similar to that of stdio using the functions that start
           65  +  with "gz".  The gzip format is different from the zlib format.  gzip is a
           66  +  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
           67  +
           68  +    This library can optionally read and write gzip streams in memory as well.
           69  +
           70  +    The zlib format was designed to be compact and fast for use in memory
           71  +  and on communications channels.  The gzip format was designed for single-
           72  +  file compression on file systems, has a larger header than zlib to maintain
           73  +  directory information, and uses a different, slower check method than zlib.
           74  +
           75  +    The library does not install any signal handler.  The decoder checks
           76  +  the consistency of the compressed data, so the library should never crash
           77  +  even in case of corrupted input.
           78  +*/
           79  +
           80  +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
           81  +typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
           82  +
           83  +struct internal_state;
           84  +
           85  +typedef struct z_stream_s {
           86  +    z_const Bytef *next_in;     /* next input byte */
           87  +    uInt     avail_in;  /* number of bytes available at next_in */
           88  +    uLong    total_in;  /* total number of input bytes read so far */
           89  +
           90  +    Bytef    *next_out; /* next output byte should be put there */
           91  +    uInt     avail_out; /* remaining free space at next_out */
           92  +    uLong    total_out; /* total number of bytes output so far */
           93  +
           94  +    z_const char *msg;  /* last error message, NULL if no error */
           95  +    struct internal_state FAR *state; /* not visible by applications */
           96  +
           97  +    alloc_func zalloc;  /* used to allocate the internal state */
           98  +    free_func  zfree;   /* used to free the internal state */
           99  +    voidpf     opaque;  /* private data object passed to zalloc and zfree */
          100  +
          101  +    int     data_type;  /* best guess about the data type: binary or text */
          102  +    uLong   adler;      /* adler32 value of the uncompressed data */
          103  +    uLong   reserved;   /* reserved for future use */
          104  +} z_stream;
          105  +
          106  +typedef z_stream FAR *z_streamp;
          107  +
          108  +/*
          109  +     gzip header information passed to and from zlib routines.  See RFC 1952
          110  +  for more details on the meanings of these fields.
          111  +*/
          112  +typedef struct gz_header_s {
          113  +    int     text;       /* true if compressed data believed to be text */
          114  +    uLong   time;       /* modification time */
          115  +    int     xflags;     /* extra flags (not used when writing a gzip file) */
          116  +    int     os;         /* operating system */
          117  +    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
          118  +    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
          119  +    uInt    extra_max;  /* space at extra (only when reading header) */
          120  +    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
          121  +    uInt    name_max;   /* space at name (only when reading header) */
          122  +    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
          123  +    uInt    comm_max;   /* space at comment (only when reading header) */
          124  +    int     hcrc;       /* true if there was or will be a header crc */
          125  +    int     done;       /* true when done reading gzip header (not used
          126  +                           when writing a gzip file) */
          127  +} gz_header;
          128  +
          129  +typedef gz_header FAR *gz_headerp;
          130  +
          131  +/*
          132  +     The application must update next_in and avail_in when avail_in has dropped
          133  +   to zero.  It must update next_out and avail_out when avail_out has dropped
          134  +   to zero.  The application must initialize zalloc, zfree and opaque before
          135  +   calling the init function.  All other fields are set by the compression
          136  +   library and must not be updated by the application.
          137  +
          138  +     The opaque value provided by the application will be passed as the first
          139  +   parameter for calls of zalloc and zfree.  This can be useful for custom
          140  +   memory management.  The compression library attaches no meaning to the
          141  +   opaque value.
          142  +
          143  +     zalloc must return Z_NULL if there is not enough memory for the object.
          144  +   If zlib is used in a multi-threaded application, zalloc and zfree must be
          145  +   thread safe.
          146  +
          147  +     On 16-bit systems, the functions zalloc and zfree must be able to allocate
          148  +   exactly 65536 bytes, but will not be required to allocate more than this if
          149  +   the symbol MAXSEG_64K is defined (see zconf.h).  WARNING: On MSDOS, pointers
          150  +   returned by zalloc for objects of exactly 65536 bytes *must* have their
          151  +   offset normalized to zero.  The default allocation function provided by this
          152  +   library ensures this (see zutil.c).  To reduce memory requirements and avoid
          153  +   any allocation of 64K objects, at the expense of compression ratio, compile
          154  +   the library with -DMAX_WBITS=14 (see zconf.h).
          155  +
          156  +     The fields total_in and total_out can be used for statistics or progress
          157  +   reports.  After compression, total_in holds the total size of the
          158  +   uncompressed data and may be saved for use in the decompressor (particularly
          159  +   if the decompressor wants to decompress everything in a single step).
          160  +*/
          161  +
          162  +                        /* constants */
          163  +
          164  +#define Z_NO_FLUSH      0
          165  +#define Z_PARTIAL_FLUSH 1
          166  +#define Z_SYNC_FLUSH    2
          167  +#define Z_FULL_FLUSH    3
          168  +#define Z_FINISH        4
          169  +#define Z_BLOCK         5
          170  +#define Z_TREES         6
          171  +/* Allowed flush values; see deflate() and inflate() below for details */
          172  +
          173  +#define Z_OK            0
          174  +#define Z_STREAM_END    1
          175  +#define Z_NEED_DICT     2
          176  +#define Z_ERRNO        (-1)
          177  +#define Z_STREAM_ERROR (-2)
          178  +#define Z_DATA_ERROR   (-3)
          179  +#define Z_MEM_ERROR    (-4)
          180  +#define Z_BUF_ERROR    (-5)
          181  +#define Z_VERSION_ERROR (-6)
          182  +/* Return codes for the compression/decompression functions. Negative values
          183  + * are errors, positive values are used for special but normal events.
          184  + */
          185  +
          186  +#define Z_NO_COMPRESSION         0
          187  +#define Z_BEST_SPEED             1
          188  +#define Z_BEST_COMPRESSION       9
          189  +#define Z_DEFAULT_COMPRESSION  (-1)
          190  +/* compression levels */
          191  +
          192  +#define Z_FILTERED            1
          193  +#define Z_HUFFMAN_ONLY        2
          194  +#define Z_RLE                 3
          195  +#define Z_FIXED               4
          196  +#define Z_DEFAULT_STRATEGY    0
          197  +/* compression strategy; see deflateInit2() below for details */
          198  +
          199  +#define Z_BINARY   0
          200  +#define Z_TEXT     1
          201  +#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
          202  +#define Z_UNKNOWN  2
          203  +/* Possible values of the data_type field (though see inflate()) */
          204  +
          205  +#define Z_DEFLATED   8
          206  +/* The deflate compression method (the only one supported in this version) */
          207  +
          208  +#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
          209  +
          210  +#define zlib_version zlibVersion()
          211  +/* for compatibility with versions < 1.0.2 */
          212  +
          213  +
          214  +                        /* basic functions */
          215  +
          216  +ZEXTERN const char * ZEXPORT zlibVersion OF((void));
          217  +/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
          218  +   If the first character differs, the library code actually used is not
          219  +   compatible with the zlib.h header file used by the application.  This check
          220  +   is automatically made by deflateInit and inflateInit.
          221  + */
          222  +
          223  +/*
          224  +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
          225  +
          226  +     Initializes the internal stream state for compression.  The fields
          227  +   zalloc, zfree and opaque must be initialized before by the caller.  If
          228  +   zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
          229  +   allocation functions.
          230  +
          231  +     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
          232  +   1 gives best speed, 9 gives best compression, 0 gives no compression at all
          233  +   (the input data is simply copied a block at a time).  Z_DEFAULT_COMPRESSION
          234  +   requests a default compromise between speed and compression (currently
          235  +   equivalent to level 6).
          236  +
          237  +     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
          238  +   memory, Z_STREAM_ERROR if level is not a valid compression level, or
          239  +   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
          240  +   with the version assumed by the caller (ZLIB_VERSION).  msg is set to null
          241  +   if there is no error message.  deflateInit does not perform any compression:
          242  +   this will be done by deflate().
          243  +*/
          244  +
          245  +
          246  +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
          247  +/*
          248  +    deflate compresses as much data as possible, and stops when the input
          249  +  buffer becomes empty or the output buffer becomes full.  It may introduce
          250  +  some output latency (reading input without producing any output) except when
          251  +  forced to flush.
          252  +
          253  +    The detailed semantics are as follows.  deflate performs one or both of the
          254  +  following actions:
          255  +
          256  +  - Compress more input starting at next_in and update next_in and avail_in
          257  +    accordingly.  If not all input can be processed (because there is not
          258  +    enough room in the output buffer), next_in and avail_in are updated and
          259  +    processing will resume at this point for the next call of deflate().
          260  +
          261  +  - Provide more output starting at next_out and update next_out and avail_out
          262  +    accordingly.  This action is forced if the parameter flush is non zero.
          263  +    Forcing flush frequently degrades the compression ratio, so this parameter
          264  +    should be set only when necessary (in interactive applications).  Some
          265  +    output may be provided even if flush is not set.
          266  +
          267  +    Before the call of deflate(), the application should ensure that at least
          268  +  one of the actions is possible, by providing more input and/or consuming more
          269  +  output, and updating avail_in or avail_out accordingly; avail_out should
          270  +  never be zero before the call.  The application can consume the compressed
          271  +  output when it wants, for example when the output buffer is full (avail_out
          272  +  == 0), or after each call of deflate().  If deflate returns Z_OK and with
          273  +  zero avail_out, it must be called again after making room in the output
          274  +  buffer because there might be more output pending.
          275  +
          276  +    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
          277  +  decide how much data to accumulate before producing output, in order to
          278  +  maximize compression.
          279  +
          280  +    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
          281  +  flushed to the output buffer and the output is aligned on a byte boundary, so
          282  +  that the decompressor can get all input data available so far.  (In
          283  +  particular avail_in is zero after the call if enough output space has been
          284  +  provided before the call.) Flushing may degrade compression for some
          285  +  compression algorithms and so it should be used only when necessary.  This
          286  +  completes the current deflate block and follows it with an empty stored block
          287  +  that is three bits plus filler bits to the next byte, followed by four bytes
          288  +  (00 00 ff ff).
          289  +
          290  +    If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
          291  +  output buffer, but the output is not aligned to a byte boundary.  All of the
          292  +  input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
          293  +  This completes the current deflate block and follows it with an empty fixed
          294  +  codes block that is 10 bits long.  This assures that enough bytes are output
          295  +  in order for the decompressor to finish the block before the empty fixed code
          296  +  block.
          297  +
          298  +    If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
          299  +  for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
          300  +  seven bits of the current block are held to be written as the next byte after
          301  +  the next deflate block is completed.  In this case, the decompressor may not
          302  +  be provided enough bits at this point in order to complete decompression of
          303  +  the data provided so far to the compressor.  It may need to wait for the next
          304  +  block to be emitted.  This is for advanced applications that need to control
          305  +  the emission of deflate blocks.
          306  +
          307  +    If flush is set to Z_FULL_FLUSH, all output is flushed as with
          308  +  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
          309  +  restart from this point if previous compressed data has been damaged or if
          310  +  random access is desired.  Using Z_FULL_FLUSH too often can seriously degrade
          311  +  compression.
          312  +
          313  +    If deflate returns with avail_out == 0, this function must be called again
          314  +  with the same value of the flush parameter and more output space (updated
          315  +  avail_out), until the flush is complete (deflate returns with non-zero
          316  +  avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
          317  +  avail_out is greater than six to avoid repeated flush markers due to
          318  +  avail_out == 0 on return.
          319  +
          320  +    If the parameter flush is set to Z_FINISH, pending input is processed,
          321  +  pending output is flushed and deflate returns with Z_STREAM_END if there was
          322  +  enough output space; if deflate returns with Z_OK, this function must be
          323  +  called again with Z_FINISH and more output space (updated avail_out) but no
          324  +  more input data, until it returns with Z_STREAM_END or an error.  After
          325  +  deflate has returned Z_STREAM_END, the only possible operations on the stream
          326  +  are deflateReset or deflateEnd.
          327  +
          328  +    Z_FINISH can be used immediately after deflateInit if all the compression
          329  +  is to be done in a single step.  In this case, avail_out must be at least the
          330  +  value returned by deflateBound (see below).  Then deflate is guaranteed to
          331  +  return Z_STREAM_END.  If not enough output space is provided, deflate will
          332  +  not return Z_STREAM_END, and it must be called again as described above.
          333  +
          334  +    deflate() sets strm->adler to the adler32 checksum of all input read
          335  +  so far (that is, total_in bytes).
          336  +
          337  +    deflate() may update strm->data_type if it can make a good guess about
          338  +  the input data type (Z_BINARY or Z_TEXT).  In doubt, the data is considered
          339  +  binary.  This field is only for information purposes and does not affect the
          340  +  compression algorithm in any manner.
          341  +
          342  +    deflate() returns Z_OK if some progress has been made (more input
          343  +  processed or more output produced), Z_STREAM_END if all input has been
          344  +  consumed and all output has been produced (only when flush is set to
          345  +  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
          346  +  if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible
          347  +  (for example avail_in or avail_out was zero).  Note that Z_BUF_ERROR is not
          348  +  fatal, and deflate() can be called again with more input and more output
          349  +  space to continue compressing.
          350  +*/
          351  +
          352  +
          353  +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
          354  +/*
          355  +     All dynamically allocated data structures for this stream are freed.
          356  +   This function discards any unprocessed input and does not flush any pending
          357  +   output.
          358  +
          359  +     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
          360  +   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
          361  +   prematurely (some input or output was discarded).  In the error case, msg
          362  +   may be set but then points to a static string (which must not be
          363  +   deallocated).
          364  +*/
          365  +
          366  +
          367  +/*
          368  +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
          369  +
          370  +     Initializes the internal stream state for decompression.  The fields
          371  +   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
          372  +   the caller.  If next_in is not Z_NULL and avail_in is large enough (the
          373  +   exact value depends on the compression method), inflateInit determines the
          374  +   compression method from the zlib header and allocates all data structures
          375  +   accordingly; otherwise the allocation will be deferred to the first call of
          376  +   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
          377  +   use default allocation functions.
          378  +
          379  +     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
          380  +   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
          381  +   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
          382  +   invalid, such as a null pointer to the structure.  msg is set to null if
          383  +   there is no error message.  inflateInit does not perform any decompression
          384  +   apart from possibly reading the zlib header if present: actual decompression
          385  +   will be done by inflate().  (So next_in and avail_in may be modified, but
          386  +   next_out and avail_out are unused and unchanged.) The current implementation
          387  +   of inflateInit() does not process any header information -- that is deferred
          388  +   until inflate() is called.
          389  +*/
          390  +
          391  +
          392  +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
          393  +/*
          394  +    inflate decompresses as much data as possible, and stops when the input
          395  +  buffer becomes empty or the output buffer becomes full.  It may introduce
          396  +  some output latency (reading input without producing any output) except when
          397  +  forced to flush.
          398  +
          399  +  The detailed semantics are as follows.  inflate performs one or both of the
          400  +  following actions:
          401  +
          402  +  - Decompress more input starting at next_in and update next_in and avail_in
          403  +    accordingly.  If not all input can be processed (because there is not
          404  +    enough room in the output buffer), next_in is updated and processing will
          405  +    resume at this point for the next call of inflate().
          406  +
          407  +  - Provide more output starting at next_out and update next_out and avail_out
          408  +    accordingly.  inflate() provides as much output as possible, until there is
          409  +    no more input data or no more space in the output buffer (see below about
          410  +    the flush parameter).
          411  +
          412  +    Before the call of inflate(), the application should ensure that at least
          413  +  one of the actions is possible, by providing more input and/or consuming more
          414  +  output, and updating the next_* and avail_* values accordingly.  The
          415  +  application can consume the uncompressed output when it wants, for example
          416  +  when the output buffer is full (avail_out == 0), or after each call of
          417  +  inflate().  If inflate returns Z_OK and with zero avail_out, it must be
          418  +  called again after making room in the output buffer because there might be
          419  +  more output pending.
          420  +
          421  +    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
          422  +  Z_BLOCK, or Z_TREES.  Z_SYNC_FLUSH requests that inflate() flush as much
          423  +  output as possible to the output buffer.  Z_BLOCK requests that inflate()
          424  +  stop if and when it gets to the next deflate block boundary.  When decoding
          425  +  the zlib or gzip format, this will cause inflate() to return immediately
          426  +  after the header and before the first block.  When doing a raw inflate,
          427  +  inflate() will go ahead and process the first block, and will return when it
          428  +  gets to the end of that block, or when it runs out of data.
          429  +
          430  +    The Z_BLOCK option assists in appending to or combining deflate streams.
          431  +  Also to assist in this, on return inflate() will set strm->data_type to the
          432  +  number of unused bits in the last byte taken from strm->next_in, plus 64 if
          433  +  inflate() is currently decoding the last block in the deflate stream, plus
          434  +  128 if inflate() returned immediately after decoding an end-of-block code or
          435  +  decoding the complete header up to just before the first byte of the deflate
          436  +  stream.  The end-of-block will not be indicated until all of the uncompressed
          437  +  data from that block has been written to strm->next_out.  The number of
          438  +  unused bits may in general be greater than seven, except when bit 7 of
          439  +  data_type is set, in which case the number of unused bits will be less than
          440  +  eight.  data_type is set as noted here every time inflate() returns for all
          441  +  flush options, and so can be used to determine the amount of currently
          442  +  consumed input in bits.
          443  +
          444  +    The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
          445  +  end of each deflate block header is reached, before any actual data in that
          446  +  block is decoded.  This allows the caller to determine the length of the
          447  +  deflate block header for later use in random access within a deflate block.
          448  +  256 is added to the value of strm->data_type when inflate() returns
          449  +  immediately after reaching the end of the deflate block header.
          450  +
          451  +    inflate() should normally be called until it returns Z_STREAM_END or an
          452  +  error.  However if all decompression is to be performed in a single step (a
          453  +  single call of inflate), the parameter flush should be set to Z_FINISH.  In
          454  +  this case all pending input is processed and all pending output is flushed;
          455  +  avail_out must be large enough to hold all of the uncompressed data for the
          456  +  operation to complete.  (The size of the uncompressed data may have been
          457  +  saved by the compressor for this purpose.) The use of Z_FINISH is not
          458  +  required to perform an inflation in one step.  However it may be used to
          459  +  inform inflate that a faster approach can be used for the single inflate()
          460  +  call.  Z_FINISH also informs inflate to not maintain a sliding window if the
          461  +  stream completes, which reduces inflate's memory footprint.  If the stream
          462  +  does not complete, either because not all of the stream is provided or not
          463  +  enough output space is provided, then a sliding window will be allocated and
          464  +  inflate() can be called again to continue the operation as if Z_NO_FLUSH had
          465  +  been used.
          466  +
          467  +     In this implementation, inflate() always flushes as much output as
          468  +  possible to the output buffer, and always uses the faster approach on the
          469  +  first call.  So the effects of the flush parameter in this implementation are
          470  +  on the return value of inflate() as noted below, when inflate() returns early
          471  +  when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
          472  +  memory for a sliding window when Z_FINISH is used.
          473  +
          474  +     If a preset dictionary is needed after this call (see inflateSetDictionary
          475  +  below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
          476  +  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
          477  +  strm->adler to the Adler-32 checksum of all output produced so far (that is,
          478  +  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
          479  +  below.  At the end of the stream, inflate() checks that its computed adler32
          480  +  checksum is equal to that saved by the compressor and returns Z_STREAM_END
          481  +  only if the checksum is correct.
          482  +
          483  +    inflate() can decompress and check either zlib-wrapped or gzip-wrapped
          484  +  deflate data.  The header type is detected automatically, if requested when
          485  +  initializing with inflateInit2().  Any information contained in the gzip
          486  +  header is not retained, so applications that need that information should
          487  +  instead use raw inflate, see inflateInit2() below, or inflateBack() and
          488  +  perform their own processing of the gzip header and trailer.  When processing
          489  +  gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
          490  +  producted so far.  The CRC-32 is checked against the gzip trailer.
          491  +
          492  +    inflate() returns Z_OK if some progress has been made (more input processed
          493  +  or more output produced), Z_STREAM_END if the end of the compressed data has
          494  +  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
          495  +  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
          496  +  corrupted (input stream not conforming to the zlib format or incorrect check
          497  +  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
          498  +  next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory,
          499  +  Z_BUF_ERROR if no progress is possible or if there was not enough room in the
          500  +  output buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and
          501  +  inflate() can be called again with more input and more output space to
          502  +  continue decompressing.  If Z_DATA_ERROR is returned, the application may
          503  +  then call inflateSync() to look for a good compression block if a partial
          504  +  recovery of the data is desired.
          505  +*/
          506  +
          507  +
          508  +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
          509  +/*
          510  +     All dynamically allocated data structures for this stream are freed.
          511  +   This function discards any unprocessed input and does not flush any pending
          512  +   output.
          513  +
          514  +     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
          515  +   was inconsistent.  In the error case, msg may be set but then points to a
          516  +   static string (which must not be deallocated).
          517  +*/
          518  +
          519  +
          520  +                        /* Advanced functions */
          521  +
          522  +/*
          523  +    The following functions are needed only in some special applications.
          524  +*/
          525  +
          526  +/*
          527  +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
          528  +                                     int  level,
          529  +                                     int  method,
          530  +                                     int  windowBits,
          531  +                                     int  memLevel,
          532  +                                     int  strategy));
          533  +
          534  +     This is another version of deflateInit with more compression options.  The
          535  +   fields next_in, zalloc, zfree and opaque must be initialized before by the
          536  +   caller.
          537  +
          538  +     The method parameter is the compression method.  It must be Z_DEFLATED in
          539  +   this version of the library.
          540  +
          541  +     The windowBits parameter is the base two logarithm of the window size
          542  +   (the size of the history buffer).  It should be in the range 8..15 for this
          543  +   version of the library.  Larger values of this parameter result in better
          544  +   compression at the expense of memory usage.  The default value is 15 if
          545  +   deflateInit is used instead.
          546  +
          547  +     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits
          548  +   determines the window size.  deflate() will then generate raw deflate data
          549  +   with no zlib header or trailer, and will not compute an adler32 check value.
          550  +
          551  +     windowBits can also be greater than 15 for optional gzip encoding.  Add
          552  +   16 to windowBits to write a simple gzip header and trailer around the
          553  +   compressed data instead of a zlib wrapper.  The gzip header will have no
          554  +   file name, no extra data, no comment, no modification time (set to zero), no
          555  +   header crc, and the operating system will be set to 255 (unknown).  If a
          556  +   gzip stream is being written, strm->adler is a crc32 instead of an adler32.
          557  +
          558  +     The memLevel parameter specifies how much memory should be allocated
          559  +   for the internal compression state.  memLevel=1 uses minimum memory but is
          560  +   slow and reduces compression ratio; memLevel=9 uses maximum memory for
          561  +   optimal speed.  The default value is 8.  See zconf.h for total memory usage
          562  +   as a function of windowBits and memLevel.
          563  +
          564  +     The strategy parameter is used to tune the compression algorithm.  Use the
          565  +   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
          566  +   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
          567  +   string match), or Z_RLE to limit match distances to one (run-length
          568  +   encoding).  Filtered data consists mostly of small values with a somewhat
          569  +   random distribution.  In this case, the compression algorithm is tuned to
          570  +   compress them better.  The effect of Z_FILTERED is to force more Huffman
          571  +   coding and less string matching; it is somewhat intermediate between
          572  +   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as
          573  +   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The
          574  +   strategy parameter only affects the compression ratio but not the
          575  +   correctness of the compressed output even if it is not set appropriately.
          576  +   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
          577  +   decoder for special applications.
          578  +
          579  +     deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
          580  +   memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
          581  +   method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
          582  +   incompatible with the version assumed by the caller (ZLIB_VERSION).  msg is
          583  +   set to null if there is no error message.  deflateInit2 does not perform any
          584  +   compression: this will be done by deflate().
          585  +*/
          586  +
          587  +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
          588  +                                             const Bytef *dictionary,
          589  +                                             uInt  dictLength));
          590  +/*
          591  +     Initializes the compression dictionary from the given byte sequence
          592  +   without producing any compressed output.  When using the zlib format, this
          593  +   function must be called immediately after deflateInit, deflateInit2 or
          594  +   deflateReset, and before any call of deflate.  When doing raw deflate, this
          595  +   function must be called either before any call of deflate, or immediately
          596  +   after the completion of a deflate block, i.e. after all input has been
          597  +   consumed and all output has been delivered when using any of the flush
          598  +   options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH.  The
          599  +   compressor and decompressor must use exactly the same dictionary (see
          600  +   inflateSetDictionary).
          601  +
          602  +     The dictionary should consist of strings (byte sequences) that are likely
          603  +   to be encountered later in the data to be compressed, with the most commonly
          604  +   used strings preferably put towards the end of the dictionary.  Using a
          605  +   dictionary is most useful when the data to be compressed is short and can be
          606  +   predicted with good accuracy; the data can then be compressed better than
          607  +   with the default empty dictionary.
          608  +
          609  +     Depending on the size of the compression data structures selected by
          610  +   deflateInit or deflateInit2, a part of the dictionary may in effect be
          611  +   discarded, for example if the dictionary is larger than the window size
          612  +   provided in deflateInit or deflateInit2.  Thus the strings most likely to be
          613  +   useful should be put at the end of the dictionary, not at the front.  In
          614  +   addition, the current implementation of deflate will use at most the window
          615  +   size minus 262 bytes of the provided dictionary.
          616  +
          617  +     Upon return of this function, strm->adler is set to the adler32 value
          618  +   of the dictionary; the decompressor may later use this value to determine
          619  +   which dictionary has been used by the compressor.  (The adler32 value
          620  +   applies to the whole dictionary even if only a subset of the dictionary is
          621  +   actually used by the compressor.) If a raw deflate was requested, then the
          622  +   adler32 value is not computed and strm->adler is not set.
          623  +
          624  +     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
          625  +   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
          626  +   inconsistent (for example if deflate has already been called for this stream
          627  +   or if not at a block boundary for raw deflate).  deflateSetDictionary does
          628  +   not perform any compression: this will be done by deflate().
          629  +*/
          630  +
          631  +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
          632  +                                    z_streamp source));
          633  +/*
          634  +     Sets the destination stream as a complete copy of the source stream.
          635  +
          636  +     This function can be useful when several compression strategies will be
          637  +   tried, for example when there are several ways of pre-processing the input
          638  +   data with a filter.  The streams that will be discarded should then be freed
          639  +   by calling deflateEnd.  Note that deflateCopy duplicates the internal
          640  +   compression state which can be quite large, so this strategy is slow and can
          641  +   consume lots of memory.
          642  +
          643  +     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
          644  +   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
          645  +   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
          646  +   destination.
          647  +*/
          648  +
          649  +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
          650  +/*
          651  +     This function is equivalent to deflateEnd followed by deflateInit,
          652  +   but does not free and reallocate all the internal compression state.  The
          653  +   stream will keep the same compression level and any other attributes that
          654  +   may have been set by deflateInit2.
          655  +
          656  +     deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
          657  +   stream state was inconsistent (such as zalloc or state being Z_NULL).
          658  +*/
          659  +
          660  +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
          661  +                                      int level,
          662  +                                      int strategy));
          663  +/*
          664  +     Dynamically update the compression level and compression strategy.  The
          665  +   interpretation of level and strategy is as in deflateInit2.  This can be
          666  +   used to switch between compression and straight copy of the input data, or
          667  +   to switch to a different kind of input data requiring a different strategy.
          668  +   If the compression level is changed, the input available so far is
          669  +   compressed with the old level (and may be flushed); the new level will take
          670  +   effect only at the next call of deflate().
          671  +
          672  +     Before the call of deflateParams, the stream state must be set as for
          673  +   a call of deflate(), since the currently available input may have to be
          674  +   compressed and flushed.  In particular, strm->avail_out must be non-zero.
          675  +
          676  +     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
          677  +   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if
          678  +   strm->avail_out was zero.
          679  +*/
          680  +
          681  +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
          682  +                                    int good_length,
          683  +                                    int max_lazy,
          684  +                                    int nice_length,
          685  +                                    int max_chain));
          686  +/*
          687  +     Fine tune deflate's internal compression parameters.  This should only be
          688  +   used by someone who understands the algorithm used by zlib's deflate for
          689  +   searching for the best matching string, and even then only by the most
          690  +   fanatic optimizer trying to squeeze out the last compressed bit for their
          691  +   specific input data.  Read the deflate.c source code for the meaning of the
          692  +   max_lazy, good_length, nice_length, and max_chain parameters.
          693  +
          694  +     deflateTune() can be called after deflateInit() or deflateInit2(), and
          695  +   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
          696  + */
          697  +
          698  +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
          699  +                                       uLong sourceLen));
          700  +/*
          701  +     deflateBound() returns an upper bound on the compressed size after
          702  +   deflation of sourceLen bytes.  It must be called after deflateInit() or
          703  +   deflateInit2(), and after deflateSetHeader(), if used.  This would be used
          704  +   to allocate an output buffer for deflation in a single pass, and so would be
          705  +   called before deflate().  If that first deflate() call is provided the
          706  +   sourceLen input bytes, an output buffer allocated to the size returned by
          707  +   deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
          708  +   to return Z_STREAM_END.  Note that it is possible for the compressed size to
          709  +   be larger than the value returned by deflateBound() if flush options other
          710  +   than Z_FINISH or Z_NO_FLUSH are used.
          711  +*/
          712  +
          713  +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
          714  +                                       unsigned *pending,
          715  +                                       int *bits));
          716  +/*
          717  +     deflatePending() returns the number of bytes and bits of output that have
          718  +   been generated, but not yet provided in the available output.  The bytes not
          719  +   provided would be due to the available output space having being consumed.
          720  +   The number of bits of output not provided are between 0 and 7, where they
          721  +   await more bits to join them in order to fill out a full byte.  If pending
          722  +   or bits are Z_NULL, then those values are not set.
          723  +
          724  +     deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
          725  +   stream state was inconsistent.
          726  + */
          727  +
          728  +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
          729  +                                     int bits,
          730  +                                     int value));
          731  +/*
          732  +     deflatePrime() inserts bits in the deflate output stream.  The intent
          733  +   is that this function is used to start off the deflate output with the bits
          734  +   leftover from a previous deflate stream when appending to it.  As such, this
          735  +   function can only be used for raw deflate, and must be used before the first
          736  +   deflate() call after a deflateInit2() or deflateReset().  bits must be less
          737  +   than or equal to 16, and that many of the least significant bits of value
          738  +   will be inserted in the output.
          739  +
          740  +     deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
          741  +   room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
          742  +   source stream state was inconsistent.
          743  +*/
          744  +
          745  +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
          746  +                                         gz_headerp head));
          747  +/*
          748  +     deflateSetHeader() provides gzip header information for when a gzip
          749  +   stream is requested by deflateInit2().  deflateSetHeader() may be called
          750  +   after deflateInit2() or deflateReset() and before the first call of
          751  +   deflate().  The text, time, os, extra field, name, and comment information
          752  +   in the provided gz_header structure are written to the gzip header (xflag is
          753  +   ignored -- the extra flags are set according to the compression level).  The
          754  +   caller must assure that, if not Z_NULL, name and comment are terminated with
          755  +   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
          756  +   available there.  If hcrc is true, a gzip header crc is included.  Note that
          757  +   the current versions of the command-line version of gzip (up through version
          758  +   1.3.x) do not support header crc's, and will report that it is a "multi-part
          759  +   gzip file" and give up.
          760  +
          761  +     If deflateSetHeader is not used, the default gzip header has text false,
          762  +   the time set to zero, and os set to 255, with no extra, name, or comment
          763  +   fields.  The gzip header is returned to the default state by deflateReset().
          764  +
          765  +     deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
          766  +   stream state was inconsistent.
          767  +*/
          768  +
          769  +/*
          770  +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
          771  +                                     int  windowBits));
          772  +
          773  +     This is another version of inflateInit with an extra parameter.  The
          774  +   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
          775  +   before by the caller.
          776  +
          777  +     The windowBits parameter is the base two logarithm of the maximum window
          778  +   size (the size of the history buffer).  It should be in the range 8..15 for
          779  +   this version of the library.  The default value is 15 if inflateInit is used
          780  +   instead.  windowBits must be greater than or equal to the windowBits value
          781  +   provided to deflateInit2() while compressing, or it must be equal to 15 if
          782  +   deflateInit2() was not used.  If a compressed stream with a larger window
          783  +   size is given as input, inflate() will return with the error code
          784  +   Z_DATA_ERROR instead of trying to allocate a larger window.
          785  +
          786  +     windowBits can also be zero to request that inflate use the window size in
          787  +   the zlib header of the compressed stream.
          788  +
          789  +     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits
          790  +   determines the window size.  inflate() will then process raw deflate data,
          791  +   not looking for a zlib or gzip header, not generating a check value, and not
          792  +   looking for any check values for comparison at the end of the stream.  This
          793  +   is for use with other formats that use the deflate compressed data format
          794  +   such as zip.  Those formats provide their own check values.  If a custom
          795  +   format is developed using the raw deflate format for compressed data, it is
          796  +   recommended that a check value such as an adler32 or a crc32 be applied to
          797  +   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
          798  +   most applications, the zlib format should be used as is.  Note that comments
          799  +   above on the use in deflateInit2() applies to the magnitude of windowBits.
          800  +
          801  +     windowBits can also be greater than 15 for optional gzip decoding.  Add
          802  +   32 to windowBits to enable zlib and gzip decoding with automatic header
          803  +   detection, or add 16 to decode only the gzip format (the zlib format will
          804  +   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
          805  +   crc32 instead of an adler32.
          806  +
          807  +     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
          808  +   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
          809  +   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
          810  +   invalid, such as a null pointer to the structure.  msg is set to null if
          811  +   there is no error message.  inflateInit2 does not perform any decompression
          812  +   apart from possibly reading the zlib header if present: actual decompression
          813  +   will be done by inflate().  (So next_in and avail_in may be modified, but
          814  +   next_out and avail_out are unused and unchanged.) The current implementation
          815  +   of inflateInit2() does not process any header information -- that is
          816  +   deferred until inflate() is called.
          817  +*/
          818  +
          819  +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
          820  +                                             const Bytef *dictionary,
          821  +                                             uInt  dictLength));
          822  +/*
          823  +     Initializes the decompression dictionary from the given uncompressed byte
          824  +   sequence.  This function must be called immediately after a call of inflate,
          825  +   if that call returned Z_NEED_DICT.  The dictionary chosen by the compressor
          826  +   can be determined from the adler32 value returned by that call of inflate.
          827  +   The compressor and decompressor must use exactly the same dictionary (see
          828  +   deflateSetDictionary).  For raw inflate, this function can be called at any
          829  +   time to set the dictionary.  If the provided dictionary is smaller than the
          830  +   window and there is already data in the window, then the provided dictionary
          831  +   will amend what's there.  The application must insure that the dictionary
          832  +   that was used for compression is provided.
          833  +
          834  +     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
          835  +   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
          836  +   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
          837  +   expected one (incorrect adler32 value).  inflateSetDictionary does not
          838  +   perform any decompression: this will be done by subsequent calls of
          839  +   inflate().
          840  +*/
          841  +
          842  +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
          843  +/*
          844  +     Skips invalid compressed data until a possible full flush point (see above
          845  +   for the description of deflate with Z_FULL_FLUSH) can be found, or until all
          846  +   available input is skipped.  No output is provided.
          847  +
          848  +     inflateSync searches for a 00 00 FF FF pattern in the compressed data.
          849  +   All full flush points have this pattern, but not all occurences of this
          850  +   pattern are full flush points.
          851  +
          852  +     inflateSync returns Z_OK if a possible full flush point has been found,
          853  +   Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
          854  +   has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
          855  +   In the success case, the application may save the current current value of
          856  +   total_in which indicates where valid compressed data was found.  In the
          857  +   error case, the application may repeatedly call inflateSync, providing more
          858  +   input each time, until success or end of the input data.
          859  +*/
          860  +
          861  +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
          862  +                                    z_streamp source));
          863  +/*
          864  +     Sets the destination stream as a complete copy of the source stream.
          865  +
          866  +     This function can be useful when randomly accessing a large stream.  The
          867  +   first pass through the stream can periodically record the inflate state,
          868  +   allowing restarting inflate at those points when randomly accessing the
          869  +   stream.
          870  +
          871  +     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
          872  +   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
          873  +   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
          874  +   destination.
          875  +*/
          876  +
          877  +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
          878  +/*
          879  +     This function is equivalent to inflateEnd followed by inflateInit,
          880  +   but does not free and reallocate all the internal decompression state.  The
          881  +   stream will keep attributes that may have been set by inflateInit2.
          882  +
          883  +     inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
          884  +   stream state was inconsistent (such as zalloc or state being Z_NULL).
          885  +*/
          886  +
          887  +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
          888  +                                      int windowBits));
          889  +/*
          890  +     This function is the same as inflateReset, but it also permits changing
          891  +   the wrap and window size requests.  The windowBits parameter is interpreted
          892  +   the same as it is for inflateInit2.
          893  +
          894  +     inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
          895  +   stream state was inconsistent (such as zalloc or state being Z_NULL), or if
          896  +   the windowBits parameter is invalid.
          897  +*/
          898  +
          899  +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
          900  +                                     int bits,
          901  +                                     int value));
          902  +/*
          903  +     This function inserts bits in the inflate input stream.  The intent is
          904  +   that this function is used to start inflating at a bit position in the
          905  +   middle of a byte.  The provided bits will be used before any bytes are used
          906  +   from next_in.  This function should only be used with raw inflate, and
          907  +   should be used before the first inflate() call after inflateInit2() or
          908  +   inflateReset().  bits must be less than or equal to 16, and that many of the
          909  +   least significant bits of value will be inserted in the input.
          910  +
          911  +     If bits is negative, then the input stream bit buffer is emptied.  Then
          912  +   inflatePrime() can be called again to put bits in the buffer.  This is used
          913  +   to clear out bits leftover after feeding inflate a block description prior
          914  +   to feeding inflate codes.
          915  +
          916  +     inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
          917  +   stream state was inconsistent.
          918  +*/
          919  +
          920  +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
          921  +/*
          922  +     This function returns two values, one in the lower 16 bits of the return
          923  +   value, and the other in the remaining upper bits, obtained by shifting the
          924  +   return value down 16 bits.  If the upper value is -1 and the lower value is
          925  +   zero, then inflate() is currently decoding information outside of a block.
          926  +   If the upper value is -1 and the lower value is non-zero, then inflate is in
          927  +   the middle of a stored block, with the lower value equaling the number of
          928  +   bytes from the input remaining to copy.  If the upper value is not -1, then
          929  +   it is the number of bits back from the current bit position in the input of
          930  +   the code (literal or length/distance pair) currently being processed.  In
          931  +   that case the lower value is the number of bytes already emitted for that
          932  +   code.
          933  +
          934  +     A code is being processed if inflate is waiting for more input to complete
          935  +   decoding of the code, or if it has completed decoding but is waiting for
          936  +   more output space to write the literal or match data.
          937  +
          938  +     inflateMark() is used to mark locations in the input data for random
          939  +   access, which may be at bit positions, and to note those cases where the
          940  +   output of a code may span boundaries of random access blocks.  The current
          941  +   location in the input stream can be determined from avail_in and data_type
          942  +   as noted in the description for the Z_BLOCK flush parameter for inflate.
          943  +
          944  +     inflateMark returns the value noted above or -1 << 16 if the provided
          945  +   source stream state was inconsistent.
          946  +*/
          947  +
          948  +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
          949  +                                         gz_headerp head));
          950  +/*
          951  +     inflateGetHeader() requests that gzip header information be stored in the
          952  +   provided gz_header structure.  inflateGetHeader() may be called after
          953  +   inflateInit2() or inflateReset(), and before the first call of inflate().
          954  +   As inflate() processes the gzip stream, head->done is zero until the header
          955  +   is completed, at which time head->done is set to one.  If a zlib stream is
          956  +   being decoded, then head->done is set to -1 to indicate that there will be
          957  +   no gzip header information forthcoming.  Note that Z_BLOCK or Z_TREES can be
          958  +   used to force inflate() to return immediately after header processing is
          959  +   complete and before any actual data is decompressed.
          960  +
          961  +     The text, time, xflags, and os fields are filled in with the gzip header
          962  +   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
          963  +   was valid if done is set to one.) If extra is not Z_NULL, then extra_max
          964  +   contains the maximum number of bytes to write to extra.  Once done is true,
          965  +   extra_len contains the actual extra field length, and extra contains the
          966  +   extra field, or that field truncated if extra_max is less than extra_len.
          967  +   If name is not Z_NULL, then up to name_max characters are written there,
          968  +   terminated with a zero unless the length is greater than name_max.  If
          969  +   comment is not Z_NULL, then up to comm_max characters are written there,
          970  +   terminated with a zero unless the length is greater than comm_max.  When any
          971  +   of extra, name, or comment are not Z_NULL and the respective field is not
          972  +   present in the header, then that field is set to Z_NULL to signal its
          973  +   absence.  This allows the use of deflateSetHeader() with the returned
          974  +   structure to duplicate the header.  However if those fields are set to
          975  +   allocated memory, then the application will need to save those pointers
          976  +   elsewhere so that they can be eventually freed.
          977  +
          978  +     If inflateGetHeader is not used, then the header information is simply
          979  +   discarded.  The header is always checked for validity, including the header
          980  +   CRC if present.  inflateReset() will reset the process to discard the header
          981  +   information.  The application would need to call inflateGetHeader() again to
          982  +   retrieve the header from the next gzip stream.
          983  +
          984  +     inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
          985  +   stream state was inconsistent.
          986  +*/
          987  +
          988  +/*
          989  +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
          990  +                                        unsigned char FAR *window));
          991  +
          992  +     Initialize the internal stream state for decompression using inflateBack()
          993  +   calls.  The fields zalloc, zfree and opaque in strm must be initialized
          994  +   before the call.  If zalloc and zfree are Z_NULL, then the default library-
          995  +   derived memory allocation routines are used.  windowBits is the base two
          996  +   logarithm of the window size, in the range 8..15.  window is a caller
          997  +   supplied buffer of that size.  Except for special applications where it is
          998  +   assured that deflate was used with small window sizes, windowBits must be 15
          999  +   and a 32K byte window must be supplied to be able to decompress general
         1000  +   deflate streams.
         1001  +
         1002  +     See inflateBack() for the usage of these routines.
         1003  +
         1004  +     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
         1005  +   the parameters are invalid, Z_MEM_ERROR if the internal state could not be
         1006  +   allocated, or Z_VERSION_ERROR if the version of the library does not match
         1007  +   the version of the header file.
         1008  +*/
         1009  +
         1010  +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
         1011  +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
         1012  +
         1013  +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
         1014  +                                    in_func in, void FAR *in_desc,
         1015  +                                    out_func out, void FAR *out_desc));
         1016  +/*
         1017  +     inflateBack() does a raw inflate with a single call using a call-back
         1018  +   interface for input and output.  This is more efficient than inflate() for
         1019  +   file i/o applications in that it avoids copying between the output and the
         1020  +   sliding window by simply making the window itself the output buffer.  This
         1021  +   function trusts the application to not change the output buffer passed by
         1022  +   the output function, at least until inflateBack() returns.
         1023  +
         1024  +     inflateBackInit() must be called first to allocate the internal state
         1025  +   and to initialize the state with the user-provided window buffer.
         1026  +   inflateBack() may then be used multiple times to inflate a complete, raw
         1027  +   deflate stream with each call.  inflateBackEnd() is then called to free the
         1028  +   allocated state.
         1029  +
         1030  +     A raw deflate stream is one with no zlib or gzip header or trailer.
         1031  +   This routine would normally be used in a utility that reads zip or gzip
         1032  +   files and writes out uncompressed files.  The utility would decode the
         1033  +   header and process the trailer on its own, hence this routine expects only
         1034  +   the raw deflate stream to decompress.  This is different from the normal
         1035  +   behavior of inflate(), which expects either a zlib or gzip header and
         1036  +   trailer around the deflate stream.
         1037  +
         1038  +     inflateBack() uses two subroutines supplied by the caller that are then
         1039  +   called by inflateBack() for input and output.  inflateBack() calls those
         1040  +   routines until it reads a complete deflate stream and writes out all of the
         1041  +   uncompressed data, or until it encounters an error.  The function's
         1042  +   parameters and return types are defined above in the in_func and out_func
         1043  +   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
         1044  +   number of bytes of provided input, and a pointer to that input in buf.  If
         1045  +   there is no input available, in() must return zero--buf is ignored in that
         1046  +   case--and inflateBack() will return a buffer error.  inflateBack() will call
         1047  +   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out()
         1048  +   should return zero on success, or non-zero on failure.  If out() returns
         1049  +   non-zero, inflateBack() will return with an error.  Neither in() nor out()
         1050  +   are permitted to change the contents of the window provided to
         1051  +   inflateBackInit(), which is also the buffer that out() uses to write from.
         1052  +   The length written by out() will be at most the window size.  Any non-zero
         1053  +   amount of input may be provided by in().
         1054  +
         1055  +     For convenience, inflateBack() can be provided input on the first call by
         1056  +   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
         1057  +   in() will be called.  Therefore strm->next_in must be initialized before
         1058  +   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
         1059  +   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
         1060  +   must also be initialized, and then if strm->avail_in is not zero, input will
         1061  +   initially be taken from strm->next_in[0 ..  strm->avail_in - 1].
         1062  +
         1063  +     The in_desc and out_desc parameters of inflateBack() is passed as the
         1064  +   first parameter of in() and out() respectively when they are called.  These
         1065  +   descriptors can be optionally used to pass any information that the caller-
         1066  +   supplied in() and out() functions need to do their job.
         1067  +
         1068  +     On return, inflateBack() will set strm->next_in and strm->avail_in to
         1069  +   pass back any unused input that was provided by the last in() call.  The
         1070  +   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
         1071  +   if in() or out() returned an error, Z_DATA_ERROR if there was a format error
         1072  +   in the deflate stream (in which case strm->msg is set to indicate the nature
         1073  +   of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
         1074  +   In the case of Z_BUF_ERROR, an input or output error can be distinguished
         1075  +   using strm->next_in which will be Z_NULL only if in() returned an error.  If
         1076  +   strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
         1077  +   non-zero.  (in() will always be called before out(), so strm->next_in is
         1078  +   assured to be defined if out() returns non-zero.) Note that inflateBack()
         1079  +   cannot return Z_OK.
         1080  +*/
         1081  +
         1082  +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
         1083  +/*
         1084  +     All memory allocated by inflateBackInit() is freed.
         1085  +
         1086  +     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
         1087  +   state was inconsistent.
         1088  +*/
         1089  +
         1090  +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
         1091  +/* Return flags indicating compile-time options.
         1092  +
         1093  +    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
         1094  +     1.0: size of uInt
         1095  +     3.2: size of uLong
         1096  +     5.4: size of voidpf (pointer)
         1097  +     7.6: size of z_off_t
         1098  +
         1099  +    Compiler, assembler, and debug options:
         1100  +     8: DEBUG
         1101  +     9: ASMV or ASMINF -- use ASM code
         1102  +     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
         1103  +     11: 0 (reserved)
         1104  +
         1105  +    One-time table building (smaller code, but not thread-safe if true):
         1106  +     12: BUILDFIXED -- build static block decoding tables when needed
         1107  +     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
         1108  +     14,15: 0 (reserved)
         1109  +
         1110  +    Library content (indicates missing functionality):
         1111  +     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
         1112  +                          deflate code when not needed)
         1113  +     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
         1114  +                    and decode gzip streams (to avoid linking crc code)
         1115  +     18-19: 0 (reserved)
         1116  +
         1117  +    Operation variations (changes in library functionality):
         1118  +     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
         1119  +     21: FASTEST -- deflate algorithm with only one, lowest compression level
         1120  +     22,23: 0 (reserved)
         1121  +
         1122  +    The sprintf variant used by gzprintf (zero is best):
         1123  +     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
         1124  +     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
         1125  +     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
         1126  +
         1127  +    Remainder:
         1128  +     27-31: 0 (reserved)
         1129  + */
         1130  +
         1131  +#ifndef Z_SOLO
         1132  +
         1133  +                        /* utility functions */
         1134  +
         1135  +/*
         1136  +     The following utility functions are implemented on top of the basic
         1137  +   stream-oriented functions.  To simplify the interface, some default options
         1138  +   are assumed (compression level and memory usage, standard memory allocation
         1139  +   functions).  The source code of these utility functions can be modified if
         1140  +   you need special options.
         1141  +*/
         1142  +
         1143  +ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
         1144  +                                 const Bytef *source, uLong sourceLen));
         1145  +/*
         1146  +     Compresses the source buffer into the destination buffer.  sourceLen is
         1147  +   the byte length of the source buffer.  Upon entry, destLen is the total size
         1148  +   of the destination buffer, which must be at least the value returned by
         1149  +   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
         1150  +   compressed buffer.
         1151  +
         1152  +     compress returns Z_OK if success, Z_MEM_ERROR if there was not
         1153  +   enough memory, Z_BUF_ERROR if there was not enough room in the output
         1154  +   buffer.
         1155  +*/
         1156  +
         1157  +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
         1158  +                                  const Bytef *source, uLong sourceLen,
         1159  +                                  int level));
         1160  +/*
         1161  +     Compresses the source buffer into the destination buffer.  The level
         1162  +   parameter has the same meaning as in deflateInit.  sourceLen is the byte
         1163  +   length of the source buffer.  Upon entry, destLen is the total size of the
         1164  +   destination buffer, which must be at least the value returned by
         1165  +   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
         1166  +   compressed buffer.
         1167  +
         1168  +     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
         1169  +   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
         1170  +   Z_STREAM_ERROR if the level parameter is invalid.
         1171  +*/
         1172  +
         1173  +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
         1174  +/*
         1175  +     compressBound() returns an upper bound on the compressed size after
         1176  +   compress() or compress2() on sourceLen bytes.  It would be used before a
         1177  +   compress() or compress2() call to allocate the destination buffer.
         1178  +*/
         1179  +
         1180  +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
         1181  +                                   const Bytef *source, uLong sourceLen));
         1182  +/*
         1183  +     Decompresses the source buffer into the destination buffer.  sourceLen is
         1184  +   the byte length of the source buffer.  Upon entry, destLen is the total size
         1185  +   of the destination buffer, which must be large enough to hold the entire
         1186  +   uncompressed data.  (The size of the uncompressed data must have been saved
         1187  +   previously by the compressor and transmitted to the decompressor by some
         1188  +   mechanism outside the scope of this compression library.) Upon exit, destLen
         1189  +   is the actual size of the uncompressed buffer.
         1190  +
         1191  +     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
         1192  +   enough memory, Z_BUF_ERROR if there was not enough room in the output
         1193  +   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
         1194  +   the case where there is not enough room, uncompress() will fill the output
         1195  +   buffer with the uncompressed data up to that point.
         1196  +*/
         1197  +
         1198  +                        /* gzip file access functions */
         1199  +
         1200  +/*
         1201  +     This library supports reading and writing files in gzip (.gz) format with
         1202  +   an interface similar to that of stdio, using the functions that start with
         1203  +   "gz".  The gzip format is different from the zlib format.  gzip is a gzip
         1204  +   wrapper, documented in RFC 1952, wrapped around a deflate stream.
         1205  +*/
         1206  +
         1207  +typedef struct gzFile_s *gzFile;    /* semi-opaque gzip file descriptor */
         1208  +
         1209  +/*
         1210  +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
         1211  +
         1212  +     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as
         1213  +   in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
         1214  +   a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
         1215  +   compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
         1216  +   for fixed code compression as in "wb9F".  (See the description of
         1217  +   deflateInit2 for more information about the strategy parameter.)  'T' will
         1218  +   request transparent writing or appending with no compression and not using
         1219  +   the gzip format.
         1220  +
         1221  +     "a" can be used instead of "w" to request that the gzip stream that will
         1222  +   be written be appended to the file.  "+" will result in an error, since
         1223  +   reading and writing to the same gzip file is not supported.  The addition of
         1224  +   "x" when writing will create the file exclusively, which fails if the file
         1225  +   already exists.  On systems that support it, the addition of "e" when
         1226  +   reading or writing will set the flag to close the file on an execve() call.
         1227  +
         1228  +     These functions, as well as gzip, will read and decode a sequence of gzip
         1229  +   streams in a file.  The append function of gzopen() can be used to create
         1230  +   such a file.  (Also see gzflush() for another way to do this.)  When
         1231  +   appending, gzopen does not test whether the file begins with a gzip stream,
         1232  +   nor does it look for the end of the gzip streams to begin appending.  gzopen
         1233  +   will simply append a gzip stream to the existing file.
         1234  +
         1235  +     gzopen can be used to read a file which is not in gzip format; in this
         1236  +   case gzread will directly read from the file without decompression.  When
         1237  +   reading, this will be detected automatically by looking for the magic two-
         1238  +   byte gzip header.
         1239  +
         1240  +     gzopen returns NULL if the file could not be opened, if there was
         1241  +   insufficient memory to allocate the gzFile state, or if an invalid mode was
         1242  +   specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
         1243  +   errno can be checked to determine if the reason gzopen failed was that the
         1244  +   file could not be opened.
         1245  +*/
         1246  +
         1247  +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
         1248  +/*
         1249  +     gzdopen associates a gzFile with the file descriptor fd.  File descriptors
         1250  +   are obtained from calls like open, dup, creat, pipe or fileno (if the file
         1251  +   has been previously opened with fopen).  The mode parameter is as in gzopen.
         1252  +
         1253  +     The next call of gzclose on the returned gzFile will also close the file
         1254  +   descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
         1255  +   fd.  If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
         1256  +   mode);.  The duplicated descriptor should be saved to avoid a leak, since
         1257  +   gzdopen does not close fd if it fails.  If you are using fileno() to get the
         1258  +   file descriptor from a FILE *, then you will have to use dup() to avoid
         1259  +   double-close()ing the file descriptor.  Both gzclose() and fclose() will
         1260  +   close the associated file descriptor, so they need to have different file
         1261  +   descriptors.
         1262  +
         1263  +     gzdopen returns NULL if there was insufficient memory to allocate the
         1264  +   gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
         1265  +   provided, or '+' was provided), or if fd is -1.  The file descriptor is not
         1266  +   used until the next gz* read, write, seek, or close operation, so gzdopen
         1267  +   will not detect if fd is invalid (unless fd is -1).
         1268  +*/
         1269  +
         1270  +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
         1271  +/*
         1272  +     Set the internal buffer size used by this library's functions.  The
         1273  +   default buffer size is 8192 bytes.  This function must be called after
         1274  +   gzopen() or gzdopen(), and before any other calls that read or write the
         1275  +   file.  The buffer memory allocation is always deferred to the first read or
         1276  +   write.  Two buffers are allocated, either both of the specified size when
         1277  +   writing, or one of the specified size and the other twice that size when
         1278  +   reading.  A larger buffer size of, for example, 64K or 128K bytes will
         1279  +   noticeably increase the speed of decompression (reading).
         1280  +
         1281  +     The new buffer size also affects the maximum length for gzprintf().
         1282  +
         1283  +     gzbuffer() returns 0 on success, or -1 on failure, such as being called
         1284  +   too late.
         1285  +*/
         1286  +
         1287  +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
         1288  +/*
         1289  +     Dynamically update the compression level or strategy.  See the description
         1290  +   of deflateInit2 for the meaning of these parameters.
         1291  +
         1292  +     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
         1293  +   opened for writing.
         1294  +*/
         1295  +
         1296  +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
         1297  +/*
         1298  +     Reads the given number of uncompressed bytes from the compressed file.  If
         1299  +   the input file is not in gzip format, gzread copies the given number of
         1300  +   bytes into the buffer directly from the file.
         1301  +
         1302  +     After reaching the end of a gzip stream in the input, gzread will continue
         1303  +   to read, looking for another gzip stream.  Any number of gzip streams may be
         1304  +   concatenated in the input file, and will all be decompressed by gzread().
         1305  +   If something other than a gzip stream is encountered after a gzip stream,
         1306  +   that remaining trailing garbage is ignored (and no error is returned).
         1307  +
         1308  +     gzread can be used to read a gzip file that is being concurrently written.
         1309  +   Upon reaching the end of the input, gzread will return with the available
         1310  +   data.  If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
         1311  +   gzclearerr can be used to clear the end of file indicator in order to permit
         1312  +   gzread to be tried again.  Z_OK indicates that a gzip stream was completed
         1313  +   on the last gzread.  Z_BUF_ERROR indicates that the input file ended in the
         1314  +   middle of a gzip stream.  Note that gzread does not return -1 in the event
         1315  +   of an incomplete gzip stream.  This error is deferred until gzclose(), which
         1316  +   will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
         1317  +   stream.  Alternatively, gzerror can be used before gzclose to detect this
         1318  +   case.
         1319  +
         1320  +     gzread returns the number of uncompressed bytes actually read, less than
         1321  +   len for end of file, or -1 for error.
         1322  +*/
         1323  +
         1324  +ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
         1325  +                                voidpc buf, unsigned len));
         1326  +/*
         1327  +     Writes the given number of uncompressed bytes into the compressed file.
         1328  +   gzwrite returns the number of uncompressed bytes written or 0 in case of
         1329  +   error.
         1330  +*/
         1331  +
         1332  +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
         1333  +/*
         1334  +     Converts, formats, and writes the arguments to the compressed file under
         1335  +   control of the format string, as in fprintf.  gzprintf returns the number of
         1336  +   uncompressed bytes actually written, or 0 in case of error.  The number of
         1337  +   uncompressed bytes written is limited to 8191, or one less than the buffer
         1338  +   size given to gzbuffer().  The caller should assure that this limit is not
         1339  +   exceeded.  If it is exceeded, then gzprintf() will return an error (0) with
         1340  +   nothing written.  In this case, there may also be a buffer overflow with
         1341  +   unpredictable consequences, which is possible only if zlib was compiled with
         1342  +   the insecure functions sprintf() or vsprintf() because the secure snprintf()
         1343  +   or vsnprintf() functions were not available.  This can be determined using
         1344  +   zlibCompileFlags().
         1345  +*/
         1346  +
         1347  +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
         1348  +/*
         1349  +     Writes the given null-terminated string to the compressed file, excluding
         1350  +   the terminating null character.
         1351  +
         1352  +     gzputs returns the number of characters written, or -1 in case of error.
         1353  +*/
         1354  +
         1355  +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
         1356  +/*
         1357  +     Reads bytes from the compressed file until len-1 characters are read, or a
         1358  +   newline character is read and transferred to buf, or an end-of-file
         1359  +   condition is encountered.  If any characters are read or if len == 1, the
         1360  +   string is terminated with a null character.  If no characters are read due
         1361  +   to an end-of-file or len < 1, then the buffer is left untouched.
         1362  +
         1363  +     gzgets returns buf which is a null-terminated string, or it returns NULL
         1364  +   for end-of-file or in case of error.  If there was an error, the contents at
         1365  +   buf are indeterminate.
         1366  +*/
         1367  +
         1368  +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
         1369  +/*
         1370  +     Writes c, converted to an unsigned char, into the compressed file.  gzputc
         1371  +   returns the value that was written, or -1 in case of error.
         1372  +*/
         1373  +
         1374  +ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
         1375  +/*
         1376  +     Reads one byte from the compressed file.  gzgetc returns this byte or -1
         1377  +   in case of end of file or error.  This is implemented as a macro for speed.
         1378  +   As such, it does not do all of the checking the other functions do.  I.e.
         1379  +   it does not check to see if file is NULL, nor whether the structure file
         1380  +   points to has been clobbered or not.
         1381  +*/
         1382  +
         1383  +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
         1384  +/*
         1385  +     Push one character back onto the stream to be read as the first character
         1386  +   on the next read.  At least one character of push-back is allowed.
         1387  +   gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
         1388  +   fail if c is -1, and may fail if a character has been pushed but not read
         1389  +   yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
         1390  +   output buffer size of pushed characters is allowed.  (See gzbuffer above.)
         1391  +   The pushed character will be discarded if the stream is repositioned with
         1392  +   gzseek() or gzrewind().
         1393  +*/
         1394  +
         1395  +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
         1396  +/*
         1397  +     Flushes all pending output into the compressed file.  The parameter flush
         1398  +   is as in the deflate() function.  The return value is the zlib error number
         1399  +   (see function gzerror below).  gzflush is only permitted when writing.
         1400  +
         1401  +     If the flush parameter is Z_FINISH, the remaining data is written and the
         1402  +   gzip stream is completed in the output.  If gzwrite() is called again, a new
         1403  +   gzip stream will be started in the output.  gzread() is able to read such
         1404  +   concatented gzip streams.
         1405  +
         1406  +     gzflush should be called only when strictly necessary because it will
         1407  +   degrade compression if called too often.
         1408  +*/
         1409  +
         1410  +/*
         1411  +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
         1412  +                                   z_off_t offset, int whence));
         1413  +
         1414  +     Sets the starting position for the next gzread or gzwrite on the given
         1415  +   compressed file.  The offset represents a number of bytes in the
         1416  +   uncompressed data stream.  The whence parameter is defined as in lseek(2);
         1417  +   the value SEEK_END is not supported.
         1418  +
         1419  +     If the file is opened for reading, this function is emulated but can be
         1420  +   extremely slow.  If the file is opened for writing, only forward seeks are
         1421  +   supported; gzseek then compresses a sequence of zeroes up to the new
         1422  +   starting position.
         1423  +
         1424  +     gzseek returns the resulting offset location as measured in bytes from
         1425  +   the beginning of the uncompressed stream, or -1 in case of error, in
         1426  +   particular if the file is opened for writing and the new starting position
         1427  +   would be before the current position.
         1428  +*/
         1429  +
         1430  +ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
         1431  +/*
         1432  +     Rewinds the given file. This function is supported only for reading.
         1433  +
         1434  +     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
         1435  +*/
         1436  +
         1437  +/*
         1438  +ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
         1439  +
         1440  +     Returns the starting position for the next gzread or gzwrite on the given
         1441  +   compressed file.  This position represents a number of bytes in the
         1442  +   uncompressed data stream, and is zero when starting, even if appending or
         1443  +   reading a gzip stream from the middle of a file using gzdopen().
         1444  +
         1445  +     gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
         1446  +*/
         1447  +
         1448  +/*
         1449  +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
         1450  +
         1451  +     Returns the current offset in the file being read or written.  This offset
         1452  +   includes the count of bytes that precede the gzip stream, for example when
         1453  +   appending or when using gzdopen() for reading.  When reading, the offset
         1454  +   does not include as yet unused buffered input.  This information can be used
         1455  +   for a progress indicator.  On error, gzoffset() returns -1.
         1456  +*/
         1457  +
         1458  +ZEXTERN int ZEXPORT gzeof OF((gzFile file));
         1459  +/*
         1460  +     Returns true (1) if the end-of-file indicator has been set while reading,
         1461  +   false (0) otherwise.  Note that the end-of-file indicator is set only if the
         1462  +   read tried to go past the end of the input, but came up short.  Therefore,
         1463  +   just like feof(), gzeof() may return false even if there is no more data to
         1464  +   read, in the event that the last read request was for the exact number of
         1465  +   bytes remaining in the input file.  This will happen if the input file size
         1466  +   is an exact multiple of the buffer size.
         1467  +
         1468  +     If gzeof() returns true, then the read functions will return no more data,
         1469  +   unless the end-of-file indicator is reset by gzclearerr() and the input file
         1470  +   has grown since the previous end of file was detected.
         1471  +*/
         1472  +
         1473  +ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
         1474  +/*
         1475  +     Returns true (1) if file is being copied directly while reading, or false
         1476  +   (0) if file is a gzip stream being decompressed.
         1477  +
         1478  +     If the input file is empty, gzdirect() will return true, since the input
         1479  +   does not contain a gzip stream.
         1480  +
         1481  +     If gzdirect() is used immediately after gzopen() or gzdopen() it will
         1482  +   cause buffers to be allocated to allow reading the file to determine if it
         1483  +   is a gzip file.  Therefore if gzbuffer() is used, it should be called before
         1484  +   gzdirect().
         1485  +
         1486  +     When writing, gzdirect() returns true (1) if transparent writing was
         1487  +   requested ("wT" for the gzopen() mode), or false (0) otherwise.  (Note:
         1488  +   gzdirect() is not needed when writing.  Transparent writing must be
         1489  +   explicitly requested, so the application already knows the answer.  When
         1490  +   linking statically, using gzdirect() will include all of the zlib code for
         1491  +   gzip file reading and decompression, which may not be desired.)
         1492  +*/
         1493  +
         1494  +ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
         1495  +/*
         1496  +     Flushes all pending output if necessary, closes the compressed file and
         1497  +   deallocates the (de)compression state.  Note that once file is closed, you
         1498  +   cannot call gzerror with file, since its structures have been deallocated.
         1499  +   gzclose must not be called more than once on the same file, just as free
         1500  +   must not be called more than once on the same allocation.
         1501  +
         1502  +     gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
         1503  +   file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
         1504  +   last read ended in the middle of a gzip stream, or Z_OK on success.
         1505  +*/
         1506  +
         1507  +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
         1508  +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
         1509  +/*
         1510  +     Same as gzclose(), but gzclose_r() is only for use when reading, and
         1511  +   gzclose_w() is only for use when writing or appending.  The advantage to
         1512  +   using these instead of gzclose() is that they avoid linking in zlib
         1513  +   compression or decompression code that is not used when only reading or only
         1514  +   writing respectively.  If gzclose() is used, then both compression and
         1515  +   decompression code will be included the application when linking to a static
         1516  +   zlib library.
         1517  +*/
         1518  +
         1519  +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
         1520  +/*
         1521  +     Returns the error message for the last error which occurred on the given
         1522  +   compressed file.  errnum is set to zlib error number.  If an error occurred
         1523  +   in the file system and not in the compression library, errnum is set to
         1524  +   Z_ERRNO and the application may consult errno to get the exact error code.
         1525  +
         1526  +     The application must not modify the returned string.  Future calls to
         1527  +   this function may invalidate the previously returned string.  If file is
         1528  +   closed, then the string previously returned by gzerror will no longer be
         1529  +   available.
         1530  +
         1531  +     gzerror() should be used to distinguish errors from end-of-file for those
         1532  +   functions above that do not distinguish those cases in their return values.
         1533  +*/
         1534  +
         1535  +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
         1536  +/*
         1537  +     Clears the error and end-of-file flags for file.  This is analogous to the
         1538  +   clearerr() function in stdio.  This is useful for continuing to read a gzip
         1539  +   file that is being written concurrently.
         1540  +*/
         1541  +
         1542  +#endif /* !Z_SOLO */
         1543  +
         1544  +                        /* checksum functions */
         1545  +
         1546  +/*
         1547  +     These functions are not related to compression but are exported
         1548  +   anyway because they might be useful in applications using the compression
         1549  +   library.
         1550  +*/
         1551  +
         1552  +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
         1553  +/*
         1554  +     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
         1555  +   return the updated checksum.  If buf is Z_NULL, this function returns the
         1556  +   required initial value for the checksum.
         1557  +
         1558  +     An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
         1559  +   much faster.
         1560  +
         1561  +   Usage example:
         1562  +
         1563  +     uLong adler = adler32(0L, Z_NULL, 0);
         1564  +
         1565  +     while (read_buffer(buffer, length) != EOF) {
         1566  +       adler = adler32(adler, buffer, length);
         1567  +     }
         1568  +     if (adler != original_adler) error();
         1569  +*/
         1570  +
         1571  +/*
         1572  +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
         1573  +                                          z_off_t len2));
         1574  +
         1575  +     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
         1576  +   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
         1577  +   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
         1578  +   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.  Note
         1579  +   that the z_off_t type (like off_t) is a signed integer.  If len2 is
         1580  +   negative, the result has no meaning or utility.
         1581  +*/
         1582  +
         1583  +ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
         1584  +/*
         1585  +     Update a running CRC-32 with the bytes buf[0..len-1] and return the
         1586  +   updated CRC-32.  If buf is Z_NULL, this function returns the required
         1587  +   initial value for the crc.  Pre- and post-conditioning (one's complement) is
         1588  +   performed within this function so it shouldn't be done by the application.
         1589  +
         1590  +   Usage example:
         1591  +
         1592  +     uLong crc = crc32(0L, Z_NULL, 0);
         1593  +
         1594  +     while (read_buffer(buffer, length) != EOF) {
         1595  +       crc = crc32(crc, buffer, length);
         1596  +     }
         1597  +     if (crc != original_crc) error();
         1598  +*/
         1599  +
         1600  +/*
         1601  +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
         1602  +
         1603  +     Combine two CRC-32 check values into one.  For two sequences of bytes,
         1604  +   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
         1605  +   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
         1606  +   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
         1607  +   len2.
         1608  +*/
         1609  +
         1610  +
         1611  +                        /* various hacks, don't look :) */
         1612  +
         1613  +/* deflateInit and inflateInit are macros to allow checking the zlib version
         1614  + * and the compiler's view of z_stream:
         1615  + */
         1616  +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
         1617  +                                     const char *version, int stream_size));
         1618  +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
         1619  +                                     const char *version, int stream_size));
         1620  +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
         1621  +                                      int windowBits, int memLevel,
         1622  +                                      int strategy, const char *version,
         1623  +                                      int stream_size));
         1624  +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
         1625  +                                      const char *version, int stream_size));
         1626  +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
         1627  +                                         unsigned char FAR *window,
         1628  +                                         const char *version,
         1629  +                                         int stream_size));
         1630  +#define deflateInit(strm, level) \
         1631  +        deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
         1632  +#define inflateInit(strm) \
         1633  +        inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
         1634  +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
         1635  +        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
         1636  +                      (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
         1637  +#define inflateInit2(strm, windowBits) \
         1638  +        inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
         1639  +                      (int)sizeof(z_stream))
         1640  +#define inflateBackInit(strm, windowBits, window) \
         1641  +        inflateBackInit_((strm), (windowBits), (window), \
         1642  +                      ZLIB_VERSION, (int)sizeof(z_stream))
         1643  +
         1644  +#ifndef Z_SOLO
         1645  +
         1646  +/* gzgetc() macro and its supporting function and exposed data structure.  Note
         1647  + * that the real internal state is much larger than the exposed structure.
         1648  + * This abbreviated structure exposes just enough for the gzgetc() macro.  The
         1649  + * user should not mess with these exposed elements, since their names or
         1650  + * behavior could change in the future, perhaps even capriciously.  They can
         1651  + * only be used by the gzgetc() macro.  You have been warned.
         1652  + */
         1653  +struct gzFile_s {
         1654  +    unsigned have;
         1655  +    unsigned char *next;
         1656  +    z_off64_t pos;
         1657  +};
         1658  +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file));  /* backward compatibility */
         1659  +#ifdef Z_PREFIX_SET
         1660  +#  undef z_gzgetc
         1661  +#  define z_gzgetc(g) \
         1662  +          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
         1663  +#else
         1664  +#  define gzgetc(g) \
         1665  +          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
         1666  +#endif
         1667  +
         1668  +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
         1669  + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
         1670  + * both are true, the application gets the *64 functions, and the regular
         1671  + * functions are changed to 64 bits) -- in case these are set on systems
         1672  + * without large file support, _LFS64_LARGEFILE must also be true
         1673  + */
         1674  +#ifdef Z_LARGE64
         1675  +   ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
         1676  +   ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
         1677  +   ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
         1678  +   ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
         1679  +   ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
         1680  +   ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
         1681  +#endif
         1682  +
         1683  +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
         1684  +#  ifdef Z_PREFIX_SET
         1685  +#    define z_gzopen z_gzopen64
         1686  +#    define z_gzseek z_gzseek64
         1687  +#    define z_gztell z_gztell64
         1688  +#    define z_gzoffset z_gzoffset64
         1689  +#    define z_adler32_combine z_adler32_combine64
         1690  +#    define z_crc32_combine z_crc32_combine64
         1691  +#  else
         1692  +#    define gzopen gzopen64
         1693  +#    define gzseek gzseek64
         1694  +#    define gztell gztell64
         1695  +#    define gzoffset gzoffset64
         1696  +#    define adler32_combine adler32_combine64
         1697  +#    define crc32_combine crc32_combine64
         1698  +#  endif
         1699  +#  ifndef Z_LARGE64
         1700  +     ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
         1701  +     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
         1702  +     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
         1703  +     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
         1704  +     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
         1705  +     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
         1706  +#  endif
         1707  +#else
         1708  +   ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
         1709  +   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
         1710  +   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
         1711  +   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
         1712  +   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
         1713  +   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
         1714  +#endif
         1715  +
         1716  +#else /* Z_SOLO */
         1717  +
         1718  +   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
         1719  +   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
         1720  +
         1721  +#endif /* !Z_SOLO */
         1722  +
         1723  +/* hack for buggy compilers */
         1724  +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
         1725  +    struct internal_state {int dummy;};
         1726  +#endif
         1727  +
         1728  +/* undocumented functions */
         1729  +ZEXTERN const char   * ZEXPORT zError           OF((int));
         1730  +ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp));
         1731  +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table    OF((void));
         1732  +ZEXTERN int            ZEXPORT inflateUndermine OF((z_streamp, int));
         1733  +ZEXTERN int            ZEXPORT inflateResetKeep OF((z_streamp));
         1734  +ZEXTERN int            ZEXPORT deflateResetKeep OF((z_streamp));
         1735  +#if defined(_WIN32) && !defined(Z_SOLO)
         1736  +ZEXTERN gzFile         ZEXPORT gzopen_w OF((const wchar_t *path,
         1737  +                                            const char *mode));
         1738  +#endif
         1739  +
         1740  +#ifdef __cplusplus
         1741  +}
         1742  +#endif
         1743  +
         1744  +#endif /* ZLIB_H */

Added compat/zlib/zlib.map.

            1  +ZLIB_1.2.0 {
            2  +  global:
            3  +    compressBound;
            4  +    deflateBound;
            5  +    inflateBack;
            6  +    inflateBackEnd;
            7  +    inflateBackInit_;
            8  +    inflateCopy;
            9  +  local:
           10  +    deflate_copyright;
           11  +    inflate_copyright;
           12  +    inflate_fast;
           13  +    inflate_table;
           14  +    zcalloc;
           15  +    zcfree;
           16  +    z_errmsg;
           17  +    gz_error;
           18  +    gz_intmax;
           19  +    _*;
           20  +};
           21  +
           22  +ZLIB_1.2.0.2 {
           23  +    gzclearerr;
           24  +    gzungetc;
           25  +    zlibCompileFlags;
           26  +} ZLIB_1.2.0;
           27  +
           28  +ZLIB_1.2.0.8 {
           29  +    deflatePrime;
           30  +} ZLIB_1.2.0.2;
           31  +
           32  +ZLIB_1.2.2 {
           33  +    adler32_combine;
           34  +    crc32_combine;
           35  +    deflateSetHeader;
           36  +    inflateGetHeader;
           37  +} ZLIB_1.2.0.8;
           38  +
           39  +ZLIB_1.2.2.3 {
           40  +    deflateTune;
           41  +    gzdirect;
           42  +} ZLIB_1.2.2;
           43  +
           44  +ZLIB_1.2.2.4 {
           45  +    inflatePrime;
           46  +} ZLIB_1.2.2.3;
           47  +
           48  +ZLIB_1.2.3.3 {
           49  +    adler32_combine64;
           50  +    crc32_combine64;
           51  +    gzopen64;
           52  +    gzseek64;
           53  +    gztell64;
           54  +    inflateUndermine;
           55  +} ZLIB_1.2.2.4;
           56  +
           57  +ZLIB_1.2.3.4 {
           58  +    inflateReset2;
           59  +    inflateMark;
           60  +} ZLIB_1.2.3.3;
           61  +
           62  +ZLIB_1.2.3.5 {
           63  +    gzbuffer;
           64  +    gzoffset;
           65  +    gzoffset64;
           66  +    gzclose_r;
           67  +    gzclose_w;
           68  +} ZLIB_1.2.3.4;
           69  +
           70  +ZLIB_1.2.5.1 {
           71  +    deflatePending;
           72  +} ZLIB_1.2.3.5;
           73  +
           74  +ZLIB_1.2.5.2 {
           75  +    deflateResetKeep;
           76  +    gzgetc_;
           77  +    inflateResetKeep;
           78  +} ZLIB_1.2.5.1;

Added compat/zlib/zlib.pc.cmakein.

            1  +prefix=@CMAKE_INSTALL_PREFIX@
            2  +exec_prefix=@CMAKE_INSTALL_PREFIX@
            3  +libdir=@INSTALL_LIB_DIR@
            4  +sharedlibdir=@INSTALL_LIB_DIR@
            5  +includedir=@INSTALL_INC_DIR@
            6  +
            7  +Name: zlib
            8  +Description: zlib compression library
            9  +Version: @VERSION@
           10  +
           11  +Requires:
           12  +Libs: -L${libdir} -L${sharedlibdir} -lz
           13  +Cflags: -I${includedir}

Added compat/zlib/zlib.pc.in.

            1  +prefix=@prefix@
            2  +exec_prefix=@exec_prefix@
            3  +libdir=@libdir@
            4  +sharedlibdir=@sharedlibdir@
            5  +includedir=@includedir@
            6  +
            7  +Name: zlib
            8  +Description: zlib compression library
            9  +Version: @VERSION@
           10  +
           11  +Requires:
           12  +Libs: -L${libdir} -L${sharedlibdir} -lz
           13  +Cflags: -I${includedir}

Added compat/zlib/zlib2ansi.

            1  +#!/usr/bin/perl
            2  +
            3  +# Transform K&R C function definitions into ANSI equivalent.
            4  +#
            5  +# Author: Paul Marquess
            6  +# Version: 1.0
            7  +# Date: 3 October 2006
            8  +
            9  +# TODO
           10  +#
           11  +# Asumes no function pointer parameters. unless they are typedefed.
           12  +# Assumes no literal strings that look like function definitions
           13  +# Assumes functions start at the beginning of a line
           14  +
           15  +use strict;
           16  +use warnings;
           17  +
           18  +local $/;
           19  +$_ = <>;
           20  +
           21  +my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments
           22  +
           23  +my $d1    = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ;
           24  +my $decl  = qr{ $sp (?: \w+ $sp )+ $d1 }xo ;
           25  +my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ;
           26  +
           27  +
           28  +while (s/^
           29  +            (                  # Start $1
           30  +                (              #   Start $2
           31  +                    .*?        #     Minimal eat content
           32  +                    ( ^ \w [\w\s\*]+ )    #     $3 -- function name
           33  +                    \s*        #     optional whitespace
           34  +                )              # $2 - Matched up to before parameter list
           35  +
           36  +                \( \s*         # Literal "(" + optional whitespace
           37  +                ( [^\)]+ )     # $4 - one or more anythings except ")"
           38  +                \s* \)         # optional whitespace surrounding a Literal ")"
           39  +
           40  +                ( (?: $dList )+ ) # $5
           41  +
           42  +                $sp ^ {        # literal "{" at start of line
           43  +            )                  # Remember to $1
           44  +        //xsom
           45  +      )
           46  +{
           47  +    my $all = $1 ;
           48  +    my $prefix = $2;
           49  +    my $param_list = $4 ;
           50  +    my $params = $5;
           51  +
           52  +    StripComments($params);
           53  +    StripComments($param_list);
           54  +    $param_list =~ s/^\s+//;
           55  +    $param_list =~ s/\s+$//;
           56  +
           57  +    my $i = 0 ;
           58  +    my %pList = map { $_ => $i++ }
           59  +                split /\s*,\s*/, $param_list;
           60  +    my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ;
           61  +
           62  +    my @params = split /\s*;\s*/, $params;
           63  +    my @outParams = ();
           64  +    foreach my $p (@params)
           65  +    {
           66  +        if ($p =~ /,/)
           67  +        {
           68  +            my @bits = split /\s*,\s*/, $p;
           69  +            my $first = shift @bits;
           70  +            $first =~ s/^\s*//;
           71  +            push @outParams, $first;
           72  +            $first =~ /^(\w+\s*)/;
           73  +            my $type = $1 ;
           74  +            push @outParams, map { $type . $_ } @bits;
           75  +        }
           76  +        else
           77  +        {
           78  +            $p =~ s/^\s+//;
           79  +            push @outParams, $p;
           80  +        }
           81  +    }
           82  +
           83  +
           84  +    my %tmp = map { /$pMatch/;  $_ => $pList{$1}  }
           85  +              @outParams ;
           86  +
           87  +    @outParams = map  { "    $_" }
           88  +                 sort { $tmp{$a} <=> $tmp{$b} }
           89  +                 @outParams ;
           90  +
           91  +    print $prefix ;
           92  +    print "(\n" . join(",\n", @outParams) . ")\n";
           93  +    print "{" ;
           94  +
           95  +}
           96  +
           97  +# Output any trailing code.
           98  +print ;
           99  +exit 0;
          100  +
          101  +
          102  +sub StripComments
          103  +{
          104  +
          105  +  no warnings;
          106  +
          107  +  # Strip C & C++ coments
          108  +  # From the perlfaq
          109  +  $_[0] =~
          110  +
          111  +    s{
          112  +       /\*         ##  Start of /* ... */ comment
          113  +       [^*]*\*+    ##  Non-* followed by 1-or-more *'s
          114  +       (
          115  +         [^/*][^*]*\*+
          116  +       )*          ##  0-or-more things which don't start with /
          117  +                   ##    but do end with '*'
          118  +       /           ##  End of /* ... */ comment
          119  +
          120  +     |         ##     OR  C++ Comment
          121  +       //          ## Start of C++ comment //
          122  +       [^\n]*      ## followed by 0-or-more non end of line characters
          123  +
          124  +     |         ##     OR  various things which aren't comments:
          125  +
          126  +       (
          127  +         "           ##  Start of " ... " string
          128  +         (
          129  +           \\.           ##  Escaped char
          130  +         |               ##    OR
          131  +           [^"\\]        ##  Non "\
          132  +         )*
          133  +         "           ##  End of " ... " string
          134  +
          135  +       |         ##     OR
          136  +
          137  +         '           ##  Start of ' ... ' string
          138  +         (
          139  +           \\.           ##  Escaped char
          140  +         |               ##    OR
          141  +           [^'\\]        ##  Non '\
          142  +         )*
          143  +         '           ##  End of ' ... ' string
          144  +
          145  +       |         ##     OR
          146  +
          147  +         .           ##  Anything other char
          148  +         [^/"'\\]*   ##  Chars which doesn't start a comment, string or escape
          149  +       )
          150  +     }{$2}gxs;
          151  +
          152  +}

Added compat/zlib/zutil.c.

            1  +/* zutil.c -- target dependent utility functions for the compression library
            2  + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* @(#) $Id$ */
            7  +
            8  +#include "zutil.h"
            9  +#ifndef Z_SOLO
           10  +#  include "gzguts.h"
           11  +#endif
           12  +
           13  +#ifndef NO_DUMMY_DECL
           14  +struct internal_state      {int dummy;}; /* for buggy compilers */
           15  +#endif
           16  +
           17  +const char * const z_errmsg[10] = {
           18  +"need dictionary",     /* Z_NEED_DICT       2  */
           19  +"stream end",          /* Z_STREAM_END      1  */
           20  +"",                    /* Z_OK              0  */
           21  +"file error",          /* Z_ERRNO         (-1) */
           22  +"stream error",        /* Z_STREAM_ERROR  (-2) */
           23  +"data error",          /* Z_DATA_ERROR    (-3) */
           24  +"insufficient memory", /* Z_MEM_ERROR     (-4) */
           25  +"buffer error",        /* Z_BUF_ERROR     (-5) */
           26  +"incompatible version",/* Z_VERSION_ERROR (-6) */
           27  +""};
           28  +
           29  +
           30  +const char * ZEXPORT zlibVersion()
           31  +{
           32  +    return ZLIB_VERSION;
           33  +}
           34  +
           35  +uLong ZEXPORT zlibCompileFlags()
           36  +{
           37  +    uLong flags;
           38  +
           39  +    flags = 0;
           40  +    switch ((int)(sizeof(uInt))) {
           41  +    case 2:     break;
           42  +    case 4:     flags += 1;     break;
           43  +    case 8:     flags += 2;     break;
           44  +    default:    flags += 3;
           45  +    }
           46  +    switch ((int)(sizeof(uLong))) {
           47  +    case 2:     break;
           48  +    case 4:     flags += 1 << 2;        break;
           49  +    case 8:     flags += 2 << 2;        break;
           50  +    default:    flags += 3 << 2;
           51  +    }
           52  +    switch ((int)(sizeof(voidpf))) {
           53  +    case 2:     break;
           54  +    case 4:     flags += 1 << 4;        break;
           55  +    case 8:     flags += 2 << 4;        break;
           56  +    default:    flags += 3 << 4;
           57  +    }
           58  +    switch ((int)(sizeof(z_off_t))) {
           59  +    case 2:     break;
           60  +    case 4:     flags += 1 << 6;        break;
           61  +    case 8:     flags += 2 << 6;        break;
           62  +    default:    flags += 3 << 6;
           63  +    }
           64  +#ifdef DEBUG
           65  +    flags += 1 << 8;
           66  +#endif
           67  +#if defined(ASMV) || defined(ASMINF)
           68  +    flags += 1 << 9;
           69  +#endif
           70  +#ifdef ZLIB_WINAPI
           71  +    flags += 1 << 10;
           72  +#endif
           73  +#ifdef BUILDFIXED
           74  +    flags += 1 << 12;
           75  +#endif
           76  +#ifdef DYNAMIC_CRC_TABLE
           77  +    flags += 1 << 13;
           78  +#endif
           79  +#ifdef NO_GZCOMPRESS
           80  +    flags += 1L << 16;
           81  +#endif
           82  +#ifdef NO_GZIP
           83  +    flags += 1L << 17;
           84  +#endif
           85  +#ifdef PKZIP_BUG_WORKAROUND
           86  +    flags += 1L << 20;
           87  +#endif
           88  +#ifdef FASTEST
           89  +    flags += 1L << 21;
           90  +#endif
           91  +#if defined(STDC) || defined(Z_HAVE_STDARG_H)
           92  +#  ifdef NO_vsnprintf
           93  +    flags += 1L << 25;
           94  +#    ifdef HAS_vsprintf_void
           95  +    flags += 1L << 26;
           96  +#    endif
           97  +#  else
           98  +#    ifdef HAS_vsnprintf_void
           99  +    flags += 1L << 26;
          100  +#    endif
          101  +#  endif
          102  +#else
          103  +    flags += 1L << 24;
          104  +#  ifdef NO_snprintf
          105  +    flags += 1L << 25;
          106  +#    ifdef HAS_sprintf_void
          107  +    flags += 1L << 26;
          108  +#    endif
          109  +#  else
          110  +#    ifdef HAS_snprintf_void
          111  +    flags += 1L << 26;
          112  +#    endif
          113  +#  endif
          114  +#endif
          115  +    return flags;
          116  +}
          117  +
          118  +#ifdef DEBUG
          119  +
          120  +#  ifndef verbose
          121  +#    define verbose 0
          122  +#  endif
          123  +int ZLIB_INTERNAL z_verbose = verbose;
          124  +
          125  +void ZLIB_INTERNAL z_error (m)
          126  +    char *m;
          127  +{
          128  +    fprintf(stderr, "%s\n", m);
          129  +    exit(1);
          130  +}
          131  +#endif
          132  +
          133  +/* exported to allow conversion of error code to string for compress() and
          134  + * uncompress()
          135  + */
          136  +const char * ZEXPORT zError(err)
          137  +    int err;
          138  +{
          139  +    return ERR_MSG(err);
          140  +}
          141  +
          142  +#if defined(_WIN32_WCE)
          143  +    /* The Microsoft C Run-Time Library for Windows CE doesn't have
          144  +     * errno.  We define it as a global variable to simplify porting.
          145  +     * Its value is always 0 and should not be used.
          146  +     */
          147  +    int errno = 0;
          148  +#endif
          149  +
          150  +#ifndef HAVE_MEMCPY
          151  +
          152  +void ZLIB_INTERNAL zmemcpy(dest, source, len)
          153  +    Bytef* dest;
          154  +    const Bytef* source;
          155  +    uInt  len;
          156  +{
          157  +    if (len == 0) return;
          158  +    do {
          159  +        *dest++ = *source++; /* ??? to be unrolled */
          160  +    } while (--len != 0);
          161  +}
          162  +
          163  +int ZLIB_INTERNAL zmemcmp(s1, s2, len)
          164  +    const Bytef* s1;
          165  +    const Bytef* s2;
          166  +    uInt  len;
          167  +{
          168  +    uInt j;
          169  +
          170  +    for (j = 0; j < len; j++) {
          171  +        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
          172  +    }
          173  +    return 0;
          174  +}
          175  +
          176  +void ZLIB_INTERNAL zmemzero(dest, len)
          177  +    Bytef* dest;
          178  +    uInt  len;
          179  +{
          180  +    if (len == 0) return;
          181  +    do {
          182  +        *dest++ = 0;  /* ??? to be unrolled */
          183  +    } while (--len != 0);
          184  +}
          185  +#endif
          186  +
          187  +#ifndef Z_SOLO
          188  +
          189  +#ifdef SYS16BIT
          190  +
          191  +#ifdef __TURBOC__
          192  +/* Turbo C in 16-bit mode */
          193  +
          194  +#  define MY_ZCALLOC
          195  +
          196  +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
          197  + * and farmalloc(64K) returns a pointer with an offset of 8, so we
          198  + * must fix the pointer. Warning: the pointer must be put back to its
          199  + * original form in order to free it, use zcfree().
          200  + */
          201  +
          202  +#define MAX_PTR 10
          203  +/* 10*64K = 640K */
          204  +
          205  +local int next_ptr = 0;
          206  +
          207  +typedef struct ptr_table_s {
          208  +    voidpf org_ptr;
          209  +    voidpf new_ptr;
          210  +} ptr_table;
          211  +
          212  +local ptr_table table[MAX_PTR];
          213  +/* This table is used to remember the original form of pointers
          214  + * to large buffers (64K). Such pointers are normalized with a zero offset.
          215  + * Since MSDOS is not a preemptive multitasking OS, this table is not
          216  + * protected from concurrent access. This hack doesn't work anyway on
          217  + * a protected system like OS/2. Use Microsoft C instead.
          218  + */
          219  +
          220  +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
          221  +{
          222  +    voidpf buf = opaque; /* just to make some compilers happy */
          223  +    ulg bsize = (ulg)items*size;
          224  +
          225  +    /* If we allocate less than 65520 bytes, we assume that farmalloc
          226  +     * will return a usable pointer which doesn't have to be normalized.
          227  +     */
          228  +    if (bsize < 65520L) {
          229  +        buf = farmalloc(bsize);
          230  +        if (*(ush*)&buf != 0) return buf;
          231  +    } else {
          232  +        buf = farmalloc(bsize + 16L);
          233  +    }
          234  +    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
          235  +    table[next_ptr].org_ptr = buf;
          236  +
          237  +    /* Normalize the pointer to seg:0 */
          238  +    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
          239  +    *(ush*)&buf = 0;
          240  +    table[next_ptr++].new_ptr = buf;
          241  +    return buf;
          242  +}
          243  +
          244  +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
          245  +{
          246  +    int n;
          247  +    if (*(ush*)&ptr != 0) { /* object < 64K */
          248  +        farfree(ptr);
          249  +        return;
          250  +    }
          251  +    /* Find the original pointer */
          252  +    for (n = 0; n < next_ptr; n++) {
          253  +        if (ptr != table[n].new_ptr) continue;
          254  +
          255  +        farfree(table[n].org_ptr);
          256  +        while (++n < next_ptr) {
          257  +            table[n-1] = table[n];
          258  +        }
          259  +        next_ptr--;
          260  +        return;
          261  +    }
          262  +    ptr = opaque; /* just to make some compilers happy */
          263  +    Assert(0, "zcfree: ptr not found");
          264  +}
          265  +
          266  +#endif /* __TURBOC__ */
          267  +
          268  +
          269  +#ifdef M_I86
          270  +/* Microsoft C in 16-bit mode */
          271  +
          272  +#  define MY_ZCALLOC
          273  +
          274  +#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
          275  +#  define _halloc  halloc
          276  +#  define _hfree   hfree
          277  +#endif
          278  +
          279  +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
          280  +{
          281  +    if (opaque) opaque = 0; /* to make compiler happy */
          282  +    return _halloc((long)items, size);
          283  +}
          284  +
          285  +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
          286  +{
          287  +    if (opaque) opaque = 0; /* to make compiler happy */
          288  +    _hfree(ptr);
          289  +}
          290  +
          291  +#endif /* M_I86 */
          292  +
          293  +#endif /* SYS16BIT */
          294  +
          295  +
          296  +#ifndef MY_ZCALLOC /* Any system without a special alloc function */
          297  +
          298  +#ifndef STDC
          299  +extern voidp  malloc OF((uInt size));
          300  +extern voidp  calloc OF((uInt items, uInt size));
          301  +extern void   free   OF((voidpf ptr));
          302  +#endif
          303  +
          304  +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
          305  +    voidpf opaque;
          306  +    unsigned items;
          307  +    unsigned size;
          308  +{
          309  +    if (opaque) items += size - size; /* make compiler happy */
          310  +    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
          311  +                              (voidpf)calloc(items, size);
          312  +}
          313  +
          314  +void ZLIB_INTERNAL zcfree (opaque, ptr)
          315  +    voidpf opaque;
          316  +    voidpf ptr;
          317  +{
          318  +    free(ptr);
          319  +    if (opaque) return; /* make compiler happy */
          320  +}
          321  +
          322  +#endif /* MY_ZCALLOC */
          323  +
          324  +#endif /* !Z_SOLO */

Added compat/zlib/zutil.h.

            1  +/* zutil.h -- internal interface and configuration of the compression library
            2  + * Copyright (C) 1995-2012 Jean-loup Gailly.
            3  + * For conditions of distribution and use, see copyright notice in zlib.h
            4  + */
            5  +
            6  +/* WARNING: this file should *not* be used by applications. It is
            7  +   part of the implementation of the compression library and is
            8  +   subject to change. Applications should only use zlib.h.
            9  + */
           10  +
           11  +/* @(#) $Id$ */
           12  +
           13  +#ifndef ZUTIL_H
           14  +#define ZUTIL_H
           15  +
           16  +#ifdef HAVE_HIDDEN
           17  +#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
           18  +#else
           19  +#  define ZLIB_INTERNAL
           20  +#endif
           21  +
           22  +#include "zlib.h"
           23  +
           24  +#if defined(STDC) && !defined(Z_SOLO)
           25  +#  if !(defined(_WIN32_WCE) && defined(_MSC_VER))
           26  +#    include <stddef.h>
           27  +#  endif
           28  +#  include <string.h>
           29  +#  include <stdlib.h>
           30  +#endif
           31  +
           32  +#ifdef Z_SOLO
           33  +   typedef long ptrdiff_t;  /* guess -- will be caught if guess is wrong */
           34  +#endif
           35  +
           36  +#ifndef local
           37  +#  define local static
           38  +#endif
           39  +/* compile with -Dlocal if your debugger can't find static symbols */
           40  +
           41  +typedef unsigned char  uch;
           42  +typedef uch FAR uchf;
           43  +typedef unsigned short ush;
           44  +typedef ush FAR ushf;
           45  +typedef unsigned long  ulg;
           46  +
           47  +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
           48  +/* (size given to avoid silly warnings with Visual C++) */
           49  +
           50  +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
           51  +
           52  +#define ERR_RETURN(strm,err) \
           53  +  return (strm->msg = (char*)ERR_MSG(err), (err))
           54  +/* To be used only when the state is known to be valid */
           55  +
           56  +        /* common constants */
           57  +
           58  +#ifndef DEF_WBITS
           59  +#  define DEF_WBITS MAX_WBITS
           60  +#endif
           61  +/* default windowBits for decompression. MAX_WBITS is for compression only */
           62  +
           63  +#if MAX_MEM_LEVEL >= 8
           64  +#  define DEF_MEM_LEVEL 8
           65  +#else
           66  +#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
           67  +#endif
           68  +/* default memLevel */
           69  +
           70  +#define STORED_BLOCK 0
           71  +#define STATIC_TREES 1
           72  +#define DYN_TREES    2
           73  +/* The three kinds of block type */
           74  +
           75  +#define MIN_MATCH  3
           76  +#define MAX_MATCH  258
           77  +/* The minimum and maximum match lengths */
           78  +
           79  +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
           80  +
           81  +        /* target dependencies */
           82  +
           83  +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
           84  +#  define OS_CODE  0x00
           85  +#  ifndef Z_SOLO
           86  +#    if defined(__TURBOC__) || defined(__BORLANDC__)
           87  +#      if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
           88  +         /* Allow compilation with ANSI keywords only enabled */
           89  +         void _Cdecl farfree( void *block );
           90  +         void *_Cdecl farmalloc( unsigned long nbytes );
           91  +#      else
           92  +#        include <alloc.h>
           93  +#      endif
           94  +#    else /* MSC or DJGPP */
           95  +#      include <malloc.h>
           96  +#    endif
           97  +#  endif
           98  +#endif
           99  +
          100  +#ifdef AMIGA
          101  +#  define OS_CODE  0x01
          102  +#endif
          103  +
          104  +#if defined(VAXC) || defined(VMS)
          105  +#  define OS_CODE  0x02
          106  +#  define F_OPEN(name, mode) \
          107  +     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
          108  +#endif
          109  +
          110  +#if defined(ATARI) || defined(atarist)
          111  +#  define OS_CODE  0x05
          112  +#endif
          113  +
          114  +#ifdef OS2
          115  +#  define OS_CODE  0x06
          116  +#  if defined(M_I86) && !defined(Z_SOLO)
          117  +#    include <malloc.h>
          118  +#  endif
          119  +#endif
          120  +
          121  +#if defined(MACOS) || defined(TARGET_OS_MAC)
          122  +#  define OS_CODE  0x07
          123  +#  ifndef Z_SOLO
          124  +#    if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
          125  +#      include <unix.h> /* for fdopen */
          126  +#    else
          127  +#      ifndef fdopen
          128  +#        define fdopen(fd,mode) NULL /* No fdopen() */
          129  +#      endif
          130  +#    endif
          131  +#  endif
          132  +#endif
          133  +
          134  +#ifdef TOPS20
          135  +#  define OS_CODE  0x0a
          136  +#endif
          137  +
          138  +#ifdef WIN32
          139  +#  ifndef __CYGWIN__  /* Cygwin is Unix, not Win32 */
          140  +#    define OS_CODE  0x0b
          141  +#  endif
          142  +#endif
          143  +
          144  +#ifdef __50SERIES /* Prime/PRIMOS */
          145  +#  define OS_CODE  0x0f
          146  +#endif
          147  +
          148  +#if defined(_BEOS_) || defined(RISCOS)
          149  +#  define fdopen(fd,mode) NULL /* No fdopen() */
          150  +#endif
          151  +
          152  +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
          153  +#  if defined(_WIN32_WCE)
          154  +#    define fdopen(fd,mode) NULL /* No fdopen() */
          155  +#    ifndef _PTRDIFF_T_DEFINED
          156  +       typedef int ptrdiff_t;
          157  +#      define _PTRDIFF_T_DEFINED
          158  +#    endif
          159  +#  else
          160  +#    define fdopen(fd,type)  _fdopen(fd,type)
          161  +#  endif
          162  +#endif
          163  +
          164  +#if defined(__BORLANDC__) && !defined(MSDOS)
          165  +  #pragma warn -8004
          166  +  #pragma warn -8008
          167  +  #pragma warn -8066
          168  +#endif
          169  +
          170  +/* provide prototypes for these when building zlib without LFS */
          171  +#if !defined(_WIN32) && (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
          172  +    ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
          173  +    ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
          174  +#endif
          175  +
          176  +        /* common defaults */
          177  +
          178  +#ifndef OS_CODE
          179  +#  define OS_CODE  0x03  /* assume Unix */
          180  +#endif
          181  +
          182  +#ifndef F_OPEN
          183  +#  define F_OPEN(name, mode) fopen((name), (mode))
          184  +#endif
          185  +
          186  +         /* functions */
          187  +
          188  +#if defined(pyr) || defined(Z_SOLO)
          189  +#  define NO_MEMCPY
          190  +#endif
          191  +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
          192  + /* Use our own functions for small and medium model with MSC <= 5.0.
          193  +  * You may have to use the same strategy for Borland C (untested).
          194  +  * The __SC__ check is for Symantec.
          195  +  */
          196  +#  define NO_MEMCPY
          197  +#endif
          198  +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
          199  +#  define HAVE_MEMCPY
          200  +#endif
          201  +#ifdef HAVE_MEMCPY
          202  +#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
          203  +#    define zmemcpy _fmemcpy
          204  +#    define zmemcmp _fmemcmp
          205  +#    define zmemzero(dest, len) _fmemset(dest, 0, len)
          206  +#  else
          207  +#    define zmemcpy memcpy
          208  +#    define zmemcmp memcmp
          209  +#    define zmemzero(dest, len) memset(dest, 0, len)
          210  +#  endif
          211  +#else
          212  +   void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
          213  +   int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
          214  +   void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
          215  +#endif
          216  +
          217  +/* Diagnostic functions */
          218  +#ifdef DEBUG
          219  +#  include <stdio.h>
          220  +   extern int ZLIB_INTERNAL z_verbose;
          221  +   extern void ZLIB_INTERNAL z_error OF((char *m));
          222  +#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
          223  +#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
          224  +#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
          225  +#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
          226  +#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
          227  +#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
          228  +#else
          229  +#  define Assert(cond,msg)
          230  +#  define Trace(x)
          231  +#  define Tracev(x)
          232  +#  define Tracevv(x)
          233  +#  define Tracec(c,x)
          234  +#  define Tracecv(c,x)
          235  +#endif
          236  +
          237  +#ifndef Z_SOLO
          238  +   voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
          239  +                                    unsigned size));
          240  +   void ZLIB_INTERNAL zcfree  OF((voidpf opaque, voidpf ptr));
          241  +#endif
          242  +
          243  +#define ZALLOC(strm, items, size) \
          244  +           (*((strm)->zalloc))((strm)->opaque, (items), (size))
          245  +#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
          246  +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
          247  +
          248  +/* Reverse the bytes in a 32-bit value */
          249  +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
          250  +                    (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
          251  +
          252  +#endif /* ZUTIL_H */

Added configure.

            1  +#!/bin/sh
            2  +dir="`dirname "$0"`/autosetup"
            3  +WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"

Deleted cvs2fossil.txt.

     1         -
     2         -Known problems and areas to work on
     3         -===================================
     4         -
     5         -*	Not yet able to handle the specification of multiple projects
     6         -	for one CVS repository. I.e. I can, for example, import all of
     7         -	tcllib, or a single subproject of tcllib, like tklib, but not
     8         -	multiple sub-projects in one go.
     9         -
    10         -*	Consider to rework the breaker- and sort-passes so that they
    11         -        do not need all changesets as objects in memory.
    12         -
    13         -	Current memory consumption after all changesets are loaded:
    14         -
    15         -	bwidget		 6971627    6.6
    16         -	cvs-memchan	 4634049    4.4
    17         -	cvs-sqlite	45674501   43.6
    18         -	cvs-trf		 8781289    8.4
    19         -	faqs		 2835116    2.7
    20         -	libtommath	 4405066    4.2
    21         -	mclistbox	 3350190    3.2 
    22         -	newclock	 5020460    4.8
    23         -	oocore		 4064574    3.9
    24         -	sampleextension	 4729932    4.5
    25         -	tclapps		 8482135    8.1
    26         -	tclbench	 4116887    3.9
    27         -	tcl_bignum	 2545192    2.4
    28         -	tclconfig	 4105042    3.9
    29         -	tcllib		31707688   30.2
    30         -	tcltutorial	 3512048    3.3
    31         -	tcl	       109926382  104.8
    32         -	thread		 8953139    8.5
    33         -	tklib		13935220   13.3
    34         -	tk		66149870   63.1
    35         -	widget		 2625609    2.5
    36         -
    37         -*	Look at the dependencies on external packages and consider
    38         -	which of them can be moved into the importer, either as a
    39         -	simple utility command, or wholesale.
    40         -
    41         -	struct::list
    42         -		assign, map, reverse, filter
    43         -
    44         -		Very few and self-contained commands.
    45         -
    46         -	struct::set
    47         -		size, empty, contains, add, include, exclude,
    48         -		intersect, subsetof
    49         -
    50         -		Most of the core commands.
    51         -
    52         -	fileutil
    53         -		cat, appendToFile, writeFile,
    54         -		tempfile, stripPath, test
    55         -
    56         -	fileutil::traverse
    57         -		In toto
    58         -
    59         -	struct::graph
    60         -		In toto
    61         -
    62         -	snit
    63         -		In toto
    64         -
    65         -	sqlite3
    66         -		In toto

Changes to debian/makedeb.sh.

     1      1   #!/bin/bash
     2      2   # A quick hack to generate a Debian package of fossil. i took most of this
     3      3   # from Martin Krafft's "The Debian System" book.
     4      4   
     5      5   DEB_REV=${1-1} # .deb package build/revision number.
     6         -PACKAGE_DEBNAME=fossil-scm
            6  +PACKAGE_DEBNAME=fossil
     7      7   THISDIR=${PWD}
     8      8   
     9      9   if uname -a | grep -i nexenta &>/dev/null; then
    10     10   # Assume NexentaOS/GnuSolaris:
    11         -    DEB_PLATFORM=nexenta
    12     11       DEB_ARCH_NAME=solaris-i386
    13     12       DEB_ARCH_PKGDEPENDS="sunwcsl" # for -lsocket
    14     13   else
    15         -    DEB_PLATFORM=${DEB_PLATFORM-ubuntu-gutsy}
    16         -    DEB_ARCH_NAME=i386
           14  +    DEB_ARCH_NAME=$(dpkg --print-architecture)
    17     15   fi
    18     16   
    19     17   SRCDIR=$(cd ..; pwd)
    20     18   test -e ${SRCDIR}/fossil || {
    21     19       echo "This script must be run from a BUILT copy of the source tree."
    22     20       exit 1
    23     21   }
................................................................................
    39     37   
    40     38   
    41     39   rm -fr DEBIAN
    42     40   mkdir DEBIAN
    43     41   
    44     42   PACKAGE_VERSION=$(date +%Y.%m.%d)
    45     43   PACKAGE_DEB_VERSION=${PACKAGE_VERSION}-${DEB_REV}
    46         -DEBFILE=${THISDIR}/${PACKAGE_DEBNAME}-${PACKAGE_DEB_VERSION}-dev-${DEB_ARCH_NAME}-${DEB_PLATFORM}.deb
           44  +DEBFILE=${THISDIR}/${PACKAGE_DEBNAME}-${PACKAGE_DEB_VERSION}-dev-${DEB_ARCH_NAME}.deb
    47     45   PACKAGE_TIME=$(/bin/date)
    48     46   
    49     47   rm -f ${DEBFILE}
    50     48   echo "Creating .deb package [${DEBFILE}]..."
    51     49   
    52     50   echo "Generating md5 sums..."
    53     51   find ${DEBLOCALPREFIX} -type f -exec md5sum {} \; > DEBIAN/md5sums
    54     52   
    55     53   true && {
    56     54       echo "Generating Debian-specific files..."
    57     55       COPYRIGHT=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/copyright
    58     56       cat <<EOF > ${COPYRIGHT}
    59         -This package was created by stephan beal <stephan@s11n.net>
           57  +This package was created by fossil-scm <fossil-dev@lists.fossil-scm.org>
    60     58   on ${PACKAGE_TIME}.
    61     59   
    62     60   The original sources for fossil can be downloaded for free from:
    63     61   
    64     62   http://www.fossil-scm.org/
    65     63   
    66     64   fossil is released under the terms of the GNU General Public License.
................................................................................
    72     70       CHANGELOG=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/changelog.gz
    73     71       cat <<EOF | gzip -c > ${CHANGELOG}
    74     72   ${PACKAGE_DEBNAME} ${PACKAGE_DEB_VERSION}; urgency=low
    75     73   
    76     74   This release has no changes over the core source distribution. It has
    77     75   simply been Debianized.
    78     76   
    79         -Packaged by stephan beal <stephan@s11n.net> on
           77  +Packaged by fossil-dev <fossil-dev@lists.fossil-scm.org> on
    80     78   ${PACKAGE_TIME}.
    81     79   
    82     80   EOF
    83     81   
    84     82   }
    85     83   
    86     84   
    87     85   true && {
    88     86       CONTROL=DEBIAN/control
    89     87       echo "Generating ${CONTROL}..."
    90     88       cat <<EOF > ${CONTROL}
    91     89   Package: ${PACKAGE_DEBNAME}
    92         -Section: devel
           90  +Section: vcs
    93     91   Priority: optional
    94         -Maintainer: stephan beal <stephan@s11n.net>
           92  +Maintainer: fossil-dev <fossil-dev@lists.fossil-scm.org>
    95     93   Architecture: ${DEB_ARCH_NAME}
    96         -Depends: libc6-dev ${DEB_ARCH_PKGDEPENDS+, }${DEB_ARCH_PKGDEPENDS}
           94  +Depends: libc6 ${DEB_ARCH_PKGDEPENDS+, }${DEB_ARCH_PKGDEPENDS}
    97     95   Version: ${PACKAGE_DEB_VERSION}
    98     96   Description: Fossil is a unique SCM (Software Configuration Management) system.
    99     97    This package contains the Fossil binary for *buntu/Debian systems.
   100     98    Fossil is a unique SCM program which supports distributed source control
   101     99    management using local repositories, access over HTTP CGI, or using the
   102    100    built-in HTTP server. It has a built-in wiki, file browsing, etc.
   103    101    Fossil home page: http://fossil-scm.org

Changes to fossil.nsi.

     6      6   ; It will install notepad.exe into a directory that the user selects,
     7      7   ;
     8      8   
     9      9   ; The name of the installer
    10     10   Name "Fossil"
    11     11   
    12     12   ; The file to write
    13         -OutFile "fossil-setup-7c0bd3ee08.exe"
           13  +OutFile "fossil-setup.exe"
    14     14   
    15     15   ; The default installation directory
    16     16   InstallDir $PROGRAMFILES\Fossil
    17     17   ; Registry key to check for directory (so if you install again, it will 
    18     18   ; overwrite the old one automatically)
    19     19   InstallDirRegKey HKLM SOFTWARE\Fossil "Install_Dir"
    20     20   
................................................................................
    24     24   DirText "Choose a directory to install in to:"
    25     25   
    26     26   ; The stuff to install
    27     27   Section "Fossil (required)"
    28     28     ; Set output path to the installation directory.
    29     29     SetOutPath $INSTDIR
    30     30     ; Put file there
    31         -  File ".\build\fossil.exe"
           31  +  File ".\fossil.exe"
    32     32     ; Write the installation path into the registry
    33     33     WriteRegStr HKLM SOFTWARE\Fossil "Install_Dir" "$INSTDIR"
    34     34     ; Write the uninstall keys for Windows
    35     35     WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Fossil" "DisplayName" "Fossil (remove only)"
    36     36     WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Fossil" "UninstallString" '"$INSTDIR\uninstall.exe"'
    37     37     WriteUninstaller "uninstall.exe"
    38     38   SectionEnd

Deleted kktodo.wiki.

     1         -<h3>kkinnell</h3>
     2         -
     3         -.plan -- <i>Fossil, the DG</i> Bwahahahaha! The cover art could be an <i>homo erectus</i> skull lying on some COBOL code...
     4         -
     5         -  1.  Command line interface reference docs
     6         -     <ul>
     7         -         <li> <font color="#bb4466">Finish initial pages.</font></li>
     8         -	 <li> Start on tech-spec (serious, not "chatty") reference pages.</li>
     9         -	 <li> Edit, edit, edit.</li>
    10         -     </ul>
    11         -
    12         -  2.  Support docs
    13         -     <ul>
    14         -	 <li>Basic explanation of Distributed SCM.</i>
    15         -	 <li>Tutorial
    16         -	 <ul>
    17         -	     <li>Silly source.  Start with existing dir struct.</li>
    18         -	     <li>Repository.  Creatiing and populating.</li>
    19         -	     <li>Where?  Local, Intranet, Internet.</li>
    20         -	     <li>Who? Project size defined by size of code base versus
    21         -	     number of developers.  Size matters.</li>
    22         -	     <li>How?
    23         -	     <ul>
    24         -	         <li>Open, close, commit, checkout, update, merge.</li>
    25         -	     </ul>
    26         -	     </li>
    27         -	     <li>Hmmm.  Experimenting.</li>
    28         -	     <ul>
    29         -	         <li>The road less travelled, or where'd that
    30         -		 fork come from?</li>
    31         -	     </ul>
    32         -	     <li>Oops!  Going back in time.</li>
    33         -	     <ul>
    34         -	         <li>Versions</li>
    35         -		 <ul>
    36         -		     <li>What <i>is</i> a version?</li>
    37         -		     <li>Is it a "version" or a "tag"?</li>
    38         -		     <li>DSCM redux: Revisionist versioning.</li>
    39         -		 </ul>
    40         -	     </ul>
    41         -	 </ul>
    42         -	 </li>
    43         -         <li>Basic explanation of <i>merge</i>.
    44         -	 <ol>
    45         -	     <li>Leaves, branches and baselines: We want a shrubbery!</li>
    46         -	     <li><i>update</i> merges vs. <i>merge</i> merges.
    47         -	     All merges are equal, but some are more equal than others.</li>
    48         -	 </ol>
    49         -	 </li>
    50         -     </ul>
    51         -
    52         -  3.  Configuration
    53         -
    54         -  42.  General
    55         -     <ul>
    56         -	 <li>Co-ordinate style and tone with drh, other devs. (documentation
    57         -         standard?  yuck.)</li>
    58         -     </ul>
    59         -
    60         -  *  Tips & tricks.
    61         -
    62         -  *  <b>Fossil</b> and <b>Sqlite</b>
    63         -     <ul>
    64         -         <li>Get a word in for Mrinal Kant's
    65         -	 <a href="http://code.google.com/p/sqlite-manager/">SQLite Manager</a>
    66         -	 XUL app.  Great tool.</li>
    67         -     </ul>
    68         -
    69         -  *  Th (code groveling &#91;and code groveling {and code groveling ... } ... &#93; ... )

Deleted rse-notes.txt.

     1         -From: "Ralf S. Engelschall"
     2         -Date: October 18, 2008 1:40:53 PM EDT
     3         -To: drh@hwaci.com
     4         -Subject: Some Fossil Feedback
     5         -
     6         -I've today looked at your Fossil project (latest version from your
     7         -repository). That's a _really_ interesting and very promising DVCS.
     8         -While I looked around and tested it, I stumbled over some issues I want
     9         -to share with you:
    10         -
    11         -o No tarball
    12         -
    13         -  You currently still do not provide any tarballs of the Fossil sources.
    14         -  Sure, you are still hacking wild on the code and one can on-the-fly
    15         -  fetch a ZIP archive, but that's a manual process. For packagers (like
    16         -  me in the OpenPKG project) this is very nasty and just means that
    17         -  Fossil will usually still not be packaged. As a result it will be not
    18         -  spreaded as much as possible and this way you still do not get as much
    19         -  as possible feedback. Hence, I recommend that you let daily snapshot
    20         -  tarballs (or ZIP files) be rolled. This would be great.
    21         -
    22         -o UUID
    23         -
    24         -  Under http://www.fossil-scm.org/index.html/doc/tip/www/concepts.wiki
    25         -  you describe the concepts and you clearly name the artifact ID a
    26         -  "UUID" and even say that this is a "Universally Unique Identifier".
    27         -  Unfortunately, a UUID is a 128-bit entity standardized by the ISO
    28         -  (under ISO/IEC 11578:1996) and IETF (under RFC-4122) and hence it is
    29         -  *VERY MUCH* confusing and unclean that your 160-bit SHA1-based ID is
    30         -  called "UUID" in Fossil.
    31         -
    32         -  I *STRONGLY* recommend you to either use real UUIDs (128-bit
    33         -  entities, where a UUID of version 5 even is SHA1-based!) or you name
    34         -  your 160-bit SHA1 entities differently (perhaps AID for "Artifact
    35         -  Identifier"?).
    36         -
    37         -o "fossil cgi <script>"
    38         -
    39         -  Currently we have only "fossil cgi <script>" where <script> contains
    40         -  "repository: <file>". This is a neat idea and should be kept. But
    41         -  this way one cannot easily host multiple repositories over this
    42         -  CGI interface without creating such a script for every individual
    43         -  repository.
    44         -
    45         -  Perhaps a "fossil cgi --repository <file>" would help here, as this
    46         -  way one can use a generic CGI script which first figures out the
    47         -  name <name> of the individual project and then runs "fossil cgi
    48         -  --repository /path/to/repositories/<name>.db". But perhaps I'm just
    49         -  overlooking something and this is already possible...
    50         -
    51         -o "fossil db <operation>"
    52         -
    53         -  In Monotone DVCS we have a "mtn db" command for the low-level SQLite
    54         -  database manipulations. For instance a "mtn --db=<monotone-repository>
    55         -  db dump" is more or less equal to a "sqlite3 <monotone-repository>
    56         -  .dump". A "mtn --db=<monotone-repository> db exec '<SQL>'" is equal
    57         -  "echo '<SQL>' | sqlite3 <monotone-repository>", etc. The point is
    58         -  that the DVCS user usually has no SQLite CLI at hand but as the DVCS
    59         -  already contains more or less the complete SQLite it is very useful to
    60         -  also include the SQLite CLI functionality and wire it into the DVCS
    61         -  CLI via a "db" command.
    62         -
    63         -o "fossil version"
    64         -
    65         -  Mostly all VCS I know if either support a command "version" or a
    66         -  command-line option "--version" or most of the time even both. Please
    67         -  output the "This is fossil version [9e80dc66cf] 2008-10-18 13:03:36"
    68         -  there (at least additionally) instead of at the end of "fossil help".
    69         -
    70         -o "--port" vs. "-port"
    71         -
    72         -  In the "fossil help server" there is "--port" while under
    73         -  http://www.fossil-scm.org/index.html/doc/tip/www/quickstart.wiki there
    74         -  is "-port". I personally like GNU-long-style options "--xxxx" more
    75         -  than the CC-style options "-xxx". But anyway, independent which one is
    76         -  correct (well, perhaps both even work) only one should be documented
    77         -  to avoid confusion.
    78         -
    79         -o User creation on the CLI
    80         -
    81         -  There is "fossil user new ?USERNAME?" which interactively prompts
    82         -  for the capabilities and the password -- even from the TTY and not
    83         -  from STDIN. One needs "fossil user new ?USERNAME? ?CAPABILITIES?
    84         -  ?PASSWORD?" to be able to create a repository in batch without having
    85         -  to hack the user into the "user" table via the SQLite CLI. Similar:
    86         -  the "fossil user password USERNAME" should be actually "fossil user
    87         -  password USERNAME ?PASSWORD?", please.
    88         -
    89         -o "-R <repository"
    90         -
    91         -  There is the "-R" option for specifiying the repository. But it is
    92         -  a sub-command option and not a global option. And on "fossil ui" or
    93         -  "fossil server" it even is an argument and not an option. This is
    94         -  confusing. Every time one has to figure out how to set the repository
    95         -  on the CLI. Monotone simply uses a *global* option "--db" and that's
    96         -  it. So, I recommend that Fossil also uses a single global option
    97         -  "--repository"/"-R" instead of a command-specific option. Sure, not
    98         -  all commands might require a repository, but IMHO it is better to
    99         -  ignore the global option there than having to figure out every time
   100         -  how the repository has to be specified.
   101         -
   102         -o Setup pages
   103         -
   104         -  When hitting "Apply changes" on any setup page, one keeps staying on
   105         -  this page. Sure, that's what an "apply" button usually is about. But
   106         -  I at least would have liked to see a button (either instead of the
   107         -  "apply changes" or at least in addition) which applies the changes and
   108         -  goes back to the setup main page (from where one usually come).
   109         -
   110         -o _FOSSIL_
   111         -
   112         -    Very nice idea to use another SQLite database for the _FOSSIL_
   113         -    control file. BUT: Why "_FOSSIL_"? CVS's "CVS" directory was ugly for
   114         -    decades. Today we have ".svn", ".git", ".hg" and "_MTN"/".mtn" to get
   115         -    rid of those ugly control files or directories of the DVCS! Sure,
   116         -    dot-files are disliked by Windows people. But that's no problem, one
   117         -    can accept "_XXX" in addition to ".XXX" easily, of course.
   118         -
   119         -    So, I really would like to see the file "_FOSSIL_" to be renamed
   120         -    to ".fossil" under Unix and "_fossil" under Windows or (if the
   121         -    upper-case is important to you) ".FOSSIL" and "_FOSSIL". But to see
   122         -    an ugly "_FOSSIL_" at the top of every source tree is really rather
   123         -    boring for Unix people!
   124         -
   125         -o "fossil open", "fossil checkout", "fossil update".
   126         -
   127         -  I'm personally confused that "fossil open" is what "checkout" does in
   128         -  other DVCS and that "checkout" is just a variant of "update". Hmmm...
   129         -  for me it looks at least one should be eleminated to reduce confusion.
   130         -  The difference between checkout and update could become an option of
   131         -  a single option. And the remaining to perhaps better should be either
   132         -  "open" + "update" or "checkout" + "update". Well, perhaps I'm still
   133         -  not understanding Fossil enough. But I can only tell that I got rather
   134         -  confused.
   135         -
   136         -o "fossil commit"
   137         -
   138         -  There is "fossil commit" but no short-hand "fossil ci". But there
   139         -  is "fossil status" and even just "fossil st" which clearly shows
   140         -  that Fossil allows abbreviations or at least prefix-matching on the
   141         -  commands. Sure, "ci" is not a prefix of "commit" but mostly all VCS
   142         -  support the good old RCS/CVS "ci" and similar aliases. So I recommend
   143         -  that Fossil does not only alias matching, but also provides aliases:
   144         -  "ci" for "commit", "co" for "checkout", "log" for "timeline", etc.
   145         -  Sorry, having to type the long "fossil commit" every time is too long
   146         -  for us Unix hackers ;-)
   147         -
   148         -  Additionally, Fossil seems to use GnuPG when installed and --nosign is
   149         -  not specified. Hmm... two questions arise here for me: 1. should the
   150         -  use of a cryptographically strong signature really be just _optional_
   151         -  (Monotone for instance RSA signs every commit) and 2. is GnuPG here
   152         -  really the right tool (Monotone does a plain RSA signing and is even
   153         -  able to leverage a running SSH agent for the operation -- something
   154         -  which is very cool both under Unix with "ssh-agent" and under Windows
   155         -  with "pagent"). OTOH, GnuPG 2.x supports a gpg-agent, so this might be
   156         -  no longer a big issue. But Fossil should document this a little bit
   157         -  futher: how to create the necessary GnuPG key, how to setup gpg-agent,
   158         -  etc.
   159         -
   160         -o "fossil diff"
   161         -
   162         -  There is "Usage: fossil diff|gdiff ?-i? ?-r REVISION? FILE...". Two
   163         -  questions arise: Why do I have to specify a "FILE"? I would expect
   164         -  that a simple "fossil diff" recursively shows a "diff" of all changed
   165         -  files in the source tree. Additionally, how does one do a diff between
   166         -  two particular revisions (one seems to be not able to specifiy "-r"
   167         -  twice).
   168         -
   169         -o "manifest" / "manifest.uuid"
   170         -
   171         -  Above I was already bothered by the _FOSSIL_ file, but now that I
   172         -  played with Fossil I see that "manifest" and "manifest.uuid" files
   173         -  showed up in my source tree. Why is this not implicitly in the
   174         -  database and instead externally represented in the source tree.
   175         -  Hmmm... then I recommend that you use a .fossil *DIRECTORY*, place
   176         -  the control file into .fossil/control (or whatever) and the manifest
   177         -  stuff into .fossil/manifest and .fossil/manifest.uuid. But please do
   178         -  not clutter the top-level of the source three with control data of the
   179         -  DVCS.
   180         -
   181         -o "fossil mv"
   182         -
   183         -  There is "fossil add" and "fossil rm", but no "fossil mv". Hopefully
   184         -  this doesn't mean a file move is an "add" and a "remove" bundle, as
   185         -  this way Fossil would have trouble to know that the file was renamed
   186         -  and hence the full history tracking of a file seems to be broken.
   187         -
   188         -o "fossil changes" vs. "fossil status"
   189         -
   190         -  Finally, I got confused by the "fossil changes" and "fossil status"
   191         -  commands. "fossil status" seems to be a super-set of "fossil changes".
   192         -  Looks a little bit less orthogonal than necessary. I would remove
   193         -  "fossil changes" or at least do not show the file details on "fossil
   194         -  status".
   195         -
   196         -Yours,
   197         -                                                                            Ralf S. Engelschall
   198         -

Added src/Makefile.

            1  +all clean:
            2  +	$(MAKE) -C .. $(MAKECMDGOALS)
            3  +

Changes to src/add.c.

    20     20   */
    21     21   #include "config.h"
    22     22   #include "add.h"
    23     23   #include <assert.h>
    24     24   #include <dirent.h>
    25     25   
    26     26   /*
    27         -** Set to true if files whose names begin with "." should be
    28         -** included when processing a recursive "add" command.
    29         -*/
    30         -static int includeDotFiles = 0;
    31         -
    32         -/*
    33         -** Add a single file
    34         -*/
    35         -static void add_one_file(const char *zName, int vid, Blob *pOmit){
    36         -  Blob pathname;
    37         -  const char *zPath;
    38         -
    39         -  file_tree_name(zName, &pathname, 1);
    40         -  zPath = blob_str(&pathname);
    41         -  if( strcmp(zPath, "manifest")==0
    42         -   || strcmp(zPath, "_FOSSIL_")==0
    43         -   || strcmp(zPath, "_FOSSIL_-journal")==0
    44         -   || strcmp(zPath, ".fos")==0
    45         -   || strcmp(zPath, ".fos-journal")==0
    46         -   || strcmp(zPath, "manifest.uuid")==0
    47         -   || blob_compare(&pathname, pOmit)==0
    48         -  ){
    49         -    fossil_warning("cannot add %s", zPath);
    50         -  }else{
    51         -    if( !file_is_simple_pathname(zPath) ){
    52         -      fossil_fatal("filename contains illegal characters: %s", zPath);
           27  +** This routine returns the names of files in a working checkout that
           28  +** are created by Fossil itself, and hence should not be added, deleted,
           29  +** or merge, and should be omitted from "clean" and "extra" lists.
           30  +**
           31  +** Return the N-th name.  The first name has N==0.  When all names have
           32  +** been used, return 0.
           33  +*/
           34  +const char *fossil_reserved_name(int N, int omitRepo){
           35  +  /* Possible names of the local per-checkout database file and
           36  +  ** its associated journals
           37  +  */
           38  +  static const char *const azName[] = {
           39  +     "_FOSSIL_",
           40  +     "_FOSSIL_-journal",
           41  +     "_FOSSIL_-wal",
           42  +     "_FOSSIL_-shm",
           43  +     ".fslckout",
           44  +     ".fslckout-journal",
           45  +     ".fslckout-wal",
           46  +     ".fslckout-shm",
           47  +
           48  +     /* The use of ".fos" as the name of the checkout database is 
           49  +     ** deprecated.  Use ".fslckout" instead.  At some point, the following
           50  +     ** entries should be removed.  2012-02-04 */
           51  +     ".fos",
           52  +     ".fos-journal",
           53  +     ".fos-wal",
           54  +     ".fos-shm",
           55  +  };
           56  +
           57  +  /* Names of auxiliary files generated by SQLite when the "manifest"
           58  +  ** property is enabled
           59  +  */
           60  +  static const char *const azManifest[] = {
           61  +     "manifest",
           62  +     "manifest.uuid",
           63  +  };
           64  +
           65  +  /*
           66  +  ** Names of repository files, if they exist in the checkout.
           67  +  */
           68  +  static const char *azRepo[4] = { 0, 0, 0, 0 };
           69  +
           70  +  /* Cached setting "manifest" */
           71  +  static int cachedManifest = -1;
           72  +
           73  +  if( cachedManifest == -1 ){
           74  +    Blob repo;
           75  +    cachedManifest = db_get_boolean("manifest",0);
           76  +    blob_zero(&repo);
           77  +    if( file_tree_name(g.zRepositoryName, &repo, 0) ){
           78  +      const char *zRepo = blob_str(&repo);
           79  +      azRepo[0] = zRepo;
           80  +      azRepo[1] = mprintf("%s-journal", zRepo);
           81  +      azRepo[2] = mprintf("%s-wal", zRepo);
           82  +      azRepo[3] = mprintf("%s-shm", zRepo);
           83  +    }
           84  +  }
           85  +
           86  +  if( N<0 ) return 0;
           87  +  if( N<count(azName) ) return azName[N];
           88  +  N -= count(azName);
           89  +  if( cachedManifest ){
           90  +    if( N<count(azManifest) ) return azManifest[N];
           91  +    N -= count(azManifest);
           92  +  }
           93  +  if( !omitRepo && N<count(azRepo) ) return azRepo[N];
           94  +  return 0;
           95  +}
           96  +
           97  +/*
           98  +** Return a list of all reserved filenames as an SQL list.
           99  +*/
          100  +const char *fossil_all_reserved_names(int omitRepo){
          101  +  static char *zAll = 0;
          102  +  if( zAll==0 ){
          103  +    Blob x;
          104  +    int i;
          105  +    const char *z;
          106  +    blob_zero(&x);
          107  +    for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){
          108  +      if( i>0 ) blob_append(&x, ",", 1);
          109  +      blob_appendf(&x, "'%q'", z);
    53    110       }
    54         -#ifdef __MINGW32__
    55         -    if( db_exists("SELECT 1 FROM vfile"
    56         -                  " WHERE pathname=%Q COLLATE nocase", zPath) ){
    57         -      db_multi_exec("UPDATE vfile SET deleted=0"
    58         -                    " WHERE pathname=%Q COLLATE nocase", zPath);
    59         -    }
    60         -#else
    61         -    if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
    62         -      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
    63         -    }
    64         -#endif
    65         -    else{
    66         -      db_multi_exec(
    67         -        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
    68         -        "VALUES(%d,0,0,0,%Q)", vid, zPath);
    69         -    }
    70         -    printf("ADDED  %s\n", zPath);
          111  +    zAll = blob_str(&x);
    71    112     }
    72         -  blob_reset(&pathname);
          113  +  return zAll;
          114  +}
          115  +
          116  +/*
          117  +** COMMAND: test-reserved-names
          118  +**
          119  +** Usage: %fossil test-reserved-names [-omitrepo]
          120  +**
          121  +** Show all reserved filenames for the current check-out.
          122  +*/
          123  +void test_reserved_names(void){
          124  +  int i;
          125  +  const char *z;
          126  +  int omitRepo = find_option("omitrepo",0,0)!=0;
          127  +  db_must_be_within_tree();
          128  +  for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){
          129  +    fossil_print("%3d: %s\n", i, z);
          130  +  }
          131  +  fossil_print("ALL: (%s)\n", fossil_all_reserved_names(omitRepo));
    73    132   }
    74    133   
    75    134   /*
    76         -** All content of the zDir directory to the SFILE table.
          135  +** Add a single file named zName to the VFILE table with vid.
          136  +**
          137  +** Omit any file whose name is pOmit.
    77    138   */
    78         -void add_directory_content(const char *zDir){
    79         -  DIR *d;
    80         -  int origSize;
    81         -  struct dirent *pEntry;
    82         -  Blob path;
    83         -
    84         -  blob_zero(&path);
    85         -  blob_append(&path, zDir, -1);
    86         -  origSize = blob_size(&path);
    87         -  d = opendir(zDir);
    88         -  if( d ){
    89         -    while( (pEntry=readdir(d))!=0 ){
    90         -      char *zPath;
    91         -      if( pEntry->d_name[0]=='.' ){
    92         -        if( !includeDotFiles ) continue;
    93         -        if( pEntry->d_name[1]==0 ) continue;
    94         -        if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
    95         -      }
    96         -      blob_appendf(&path, "/%s", pEntry->d_name);
    97         -      zPath = blob_str(&path);
    98         -      if( file_isdir(zPath)==1 ){
    99         -        add_directory_content(zPath);
   100         -      }else if( file_isfile(zPath) ){
   101         -        db_multi_exec("INSERT INTO sfile VALUES(%Q)", zPath);
   102         -      }
   103         -      blob_resize(&path, origSize);
   104         -    }
   105         -  }
   106         -  closedir(d);
   107         -  blob_reset(&path);
          139  +static int add_one_file(
          140  +  const char *zPath,   /* Tree-name of file to add. */
          141  +  int vid,             /* Add to this VFILE */
          142  +  int caseSensitive    /* True if filenames are case sensitive */
          143  +){
          144  +  const char *zCollate = caseSensitive ? "binary" : "nocase";
          145  +  if( !file_is_simple_pathname(zPath, 1) ){
          146  +    fossil_warning("filename contains illegal characters: %s", zPath);
          147  +    return 0;
          148  +  }
          149  +  if( db_exists("SELECT 1 FROM vfile"
          150  +                " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
          151  +    db_multi_exec("UPDATE vfile SET deleted=0"
          152  +                  " WHERE pathname=%Q COLLATE %s", zPath, zCollate);
          153  +  }else{
          154  +    char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
          155  +    db_multi_exec(
          156  +      "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
          157  +      "VALUES(%d,0,0,0,%Q,%d,%d)",
          158  +      vid, zPath, file_wd_isexe(zFullname), file_wd_islink(zFullname));
          159  +    fossil_free(zFullname);
          160  +  }
          161  +  if( db_changes() ){
          162  +    fossil_print("ADDED  %s\n", zPath);
          163  +    return 1;
          164  +  }else{
          165  +    fossil_print("SKIP   %s\n", zPath);
          166  +    return 0;
          167  +  }
   108    168   }
   109    169   
   110    170   /*
   111         -** Add all content of a directory.
          171  +** Add all files in the sfile temp table.
          172  +**
          173  +** Automatically exclude the repository file.
   112    174   */
   113         -void add_directory(const char *zDir, int vid, Blob *pOmit){
   114         -  Stmt q;
   115         -  add_directory_content(zDir);
   116         -  db_prepare(&q, "SELECT x FROM sfile ORDER BY x");
   117         -  while( db_step(&q)==SQLITE_ROW ){
   118         -    const char *zName = db_column_text(&q, 0);
   119         -    add_one_file(zName, vid, pOmit);
          175  +static int add_files_in_sfile(int vid, int caseSensitive){
          176  +  const char *zRepo;        /* Name of the repository database file */
          177  +  int nAdd = 0;             /* Number of files added */
          178  +  int i;                    /* Loop counter */
          179  +  const char *zReserved;    /* Name of a reserved file */
          180  +  Blob repoName;            /* Treename of the repository */
          181  +  Stmt loop;                /* SQL to loop over all files to add */
          182  +  int (*xCmp)(const char*,const char*);
          183  + 
          184  +  if( !file_tree_name(g.zRepositoryName, &repoName, 0) ){
          185  +    blob_zero(&repoName);
          186  +    zRepo = "";
          187  +  }else{
          188  +    zRepo = blob_str(&repoName);
   120    189     }
   121         -  db_finalize(&q);
   122         -  db_multi_exec("DELETE FROM sfile");
          190  +  if( caseSensitive ){
          191  +    xCmp = fossil_strcmp;
          192  +  }else{
          193  +    xCmp = fossil_stricmp;
          194  +    db_multi_exec(
          195  +      "CREATE INDEX IF NOT EXISTS vfile_nocase"
          196  +      "    ON vfile(pathname COLLATE nocase)"
          197  +    );
          198  +  }
          199  +  db_prepare(&loop, "SELECT x FROM sfile ORDER BY x");
          200  +  while( db_step(&loop)==SQLITE_ROW ){
          201  +    const char *zToAdd = db_column_text(&loop, 0);
          202  +    if( fossil_strcmp(zToAdd, zRepo)==0 ) continue;
          203  +    for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){
          204  +      if( xCmp(zToAdd, zReserved)==0 ) break;
          205  +    }
          206  +    if( zReserved ) continue;
          207  +    nAdd += add_one_file(zToAdd, vid, caseSensitive);
          208  +  }
          209  +  db_finalize(&loop);
          210  +  blob_reset(&repoName);
          211  +  return nAdd;
   123    212   }
   124    213   
   125    214   /*
   126    215   ** COMMAND: add
   127    216   **
   128         -** Usage: %fossil add FILE...
          217  +** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...?
   129    218   **
   130         -** Make arrangements to add one or more files to the current checkout
   131         -** at the next commit.
          219  +** Make arrangements to add one or more files or directories to the
          220  +** current checkout at the next commit.
   132    221   **
   133         -** When adding files recursively, filenames that begin with "." are
   134         -** excluded by default.  To include such files, add the "--dotfiles"
   135         -** option to the command-line.
          222  +** When adding files or directories recursively, filenames that begin
          223  +** with "." are excluded by default.  To include such files, add
          224  +** the "--dotfiles" option to the command-line.
          225  +**
          226  +** The --ignore option is a comma-separate list of glob patterns for files
          227  +** to be excluded.  Example:  '*.o,*.obj,*.exe'  If the --ignore option
          228  +** does not appear on the command line then the "ignore-glob" setting is
          229  +** used.
          230  +**
          231  +** The --case-sensitive option determines whether or not filenames should
          232  +** be treated case sensitive or not. If the option is not given, the default
          233  +** depends on the global setting, or the operating system default, if not set.
          234  +**
          235  +** Options:
          236  +**
          237  +**    --case-sensitive <BOOL> override case-sensitive setting
          238  +**    --dotfiles              include files beginning with a dot (".")   
          239  +**    --ignore <CSG>          ignore files matching patterns from the 
          240  +**                            comma separated list of glob patterns.
          241  +** 
          242  +** See also: addremove, rm
   136    243   */
   137    244   void add_cmd(void){
   138         -  int i;
   139         -  int vid;
   140         -  Blob repo;
          245  +  int i;                     /* Loop counter */
          246  +  int vid;                   /* Currently checked out version */
          247  +  int nRoot;                 /* Full path characters in g.zLocalRoot */
          248  +  const char *zIgnoreFlag;   /* The --ignore option or ignore-glob setting */
          249  +  Glob *pIgnore;             /* Ignore everything matching this glob pattern */
          250  +  int caseSensitive;         /* True if filenames are case sensitive */
          251  +  unsigned scanFlags = 0;    /* Flags passed to vfile_scan() */
   141    252   
   142         -  includeDotFiles = find_option("dotfiles",0,0)!=0;
          253  +  zIgnoreFlag = find_option("ignore",0,1);
          254  +  if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
          255  +  capture_case_sensitive_option();
   143    256     db_must_be_within_tree();
          257  +  caseSensitive = filenames_are_case_sensitive();
          258  +  if( zIgnoreFlag==0 ){
          259  +    zIgnoreFlag = db_get("ignore-glob", 0);
          260  +  }
   144    261     vid = db_lget_int("checkout",0);
   145    262     if( vid==0 ){
   146    263       fossil_panic("no checkout to add to");
   147    264     }
   148    265     db_begin_transaction();
   149         -  if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
   150         -    blob_zero(&repo);
   151         -  }
   152    266     db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
   153         -#ifdef __MINGW32__
          267  +#if defined(_WIN32)
   154    268     db_multi_exec(
   155    269        "CREATE INDEX IF NOT EXISTS vfile_pathname "
   156    270        "  ON vfile(pathname COLLATE nocase)"
   157    271     );
   158    272   #endif
          273  +  pIgnore = glob_create(zIgnoreFlag);
          274  +  nRoot = strlen(g.zLocalRoot);
          275  +  
          276  +  /* Load the names of all files that are to be added into sfile temp table */
   159    277     for(i=2; i<g.argc; i++){
   160    278       char *zName;
   161    279       int isDir;
          280  +    Blob fullName;
   162    281   
   163         -    zName = mprintf("%/", g.argv[i]);
   164         -    isDir = file_isdir(zName);
          282  +    file_canonical_name(g.argv[i], &fullName, 0);
          283  +    zName = blob_str(&fullName);
          284  +    isDir = file_wd_isdir(zName);
   165    285       if( isDir==1 ){
   166         -      add_directory(zName, vid, &repo);
          286  +      vfile_scan(&fullName, nRoot-1, scanFlags, pIgnore);
   167    287       }else if( isDir==0 ){
   168         -      fossil_fatal("not found: %s", zName);
   169         -    }else if( access(zName, R_OK) ){
          288  +      fossil_warning("not found: %s", zName);
          289  +    }else if( file_access(zName, R_OK) ){
   170    290         fossil_fatal("cannot open %s", zName);
   171    291       }else{
   172         -      add_one_file(zName, vid, &repo);
          292  +      char *zTreeName = &zName[nRoot];
          293  +      db_multi_exec(
          294  +         "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)",
          295  +         zTreeName
          296  +      );
   173    297       }
   174         -    free(zName);
          298  +    blob_reset(&fullName);
   175    299     }
          300  +  glob_free(pIgnore);
          301  +
          302  +  add_files_in_sfile(vid, caseSensitive);
   176    303     db_end_transaction(0);
   177    304   }
   178    305   
   179         -/*
   180         -** Remove all contents of zDir
   181         -*/
   182         -void del_directory_content(const char *zDir){
   183         -  DIR *d;
   184         -  int origSize;
   185         -  struct dirent *pEntry;
   186         -  Blob path;
   187         -
   188         -  blob_zero(&path);
   189         -  blob_append(&path, zDir, -1);
   190         -  origSize = blob_size(&path);
   191         -  d = opendir(zDir);
   192         -  if( d ){
   193         -    while( (pEntry=readdir(d))!=0 ){
   194         -      char *zPath;
   195         -      if( pEntry->d_name[0]=='.'){
   196         -        if( !includeDotFiles ) continue;
   197         -        if( pEntry->d_name[1]==0 ) continue;
   198         -        if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
   199         -      }
   200         -      blob_appendf(&path, "/%s", pEntry->d_name);
   201         -      zPath = blob_str(&path);
   202         -      if( file_isdir(zPath)==1 ){
   203         -        del_directory_content(zPath);
   204         -      }else if( file_isfile(zPath) ){
   205         -        char *zFilePath;
   206         -        Blob pathname;
   207         -        file_tree_name(zPath, &pathname, 1);
   208         -        zFilePath = blob_str(&pathname);
   209         -        if( !db_exists(
   210         -            "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zFilePath)
   211         -        ){
   212         -          printf("SKIPPED  %s\n", zPath);
   213         -        }else{
   214         -          db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath);
   215         -          printf("DELETED  %s\n", zPath);
   216         -        }
   217         -        blob_reset(&pathname);
   218         -      }
   219         -      blob_resize(&path, origSize);
   220         -    }
   221         -  }
   222         -  closedir(d);
   223         -  blob_reset(&path);
   224         -}
   225         -
   226    306   /*
   227    307   ** COMMAND: rm
   228         -** COMMAND: del
          308  +** COMMAND: delete*
   229    309   **
   230         -** Usage: %fossil rm FILE...
   231         -**    or: %fossil del FILE...
          310  +** Usage: %fossil rm FILE1 ?FILE2 ...?
          311  +**    or: %fossil delete FILE1 ?FILE2 ...?
   232    312   **
   233         -** Remove one or more files from the tree.
          313  +** Remove one or more files or directories from the repository.
   234    314   **
   235         -** This command does not remove the files from disk.  It just marks the
          315  +** This command does NOT remove the files from disk.  It just marks the
   236    316   ** files as no longer being part of the project.  In other words, future
   237    317   ** changes to the named files will not be versioned.
          318  +**
          319  +** See also: addremove, add
   238    320   */
   239         -void del_cmd(void){
          321  +void delete_cmd(void){
   240    322     int i;
   241    323     int vid;
          324  +  Stmt loop;
   242    325   
   243    326     db_must_be_within_tree();
   244    327     vid = db_lget_int("checkout", 0);
   245    328     if( vid==0 ){
   246    329       fossil_panic("no checkout to remove from");
   247    330     }
   248    331     db_begin_transaction();
          332  +  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
   249    333     for(i=2; i<g.argc; i++){
   250         -    char *zName;
          334  +    Blob treeName;
          335  +    char *zTreeName;
   251    336   
   252         -    zName = mprintf("%/", g.argv[i]);
   253         -    if( file_isdir(zName) == 1 ){
   254         -      del_directory_content(zName);
   255         -    } else {
   256         -      char *zPath;
   257         -      Blob pathname;
   258         -      file_tree_name(zName, &pathname, 1);
   259         -      zPath = blob_str(&pathname);
   260         -      if( !db_exists(
   261         -               "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){
   262         -        fossil_fatal("not in the repository: %s", zName);
   263         -      }else{
   264         -        db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath);
   265         -        printf("DELETED  %s\n", zPath);
   266         -      }
   267         -      blob_reset(&pathname);
   268         -    }
   269         -    free(zName);
          337  +    file_tree_name(g.argv[i], &treeName, 1);
          338  +    zTreeName = blob_str(&treeName);
          339  +    db_multi_exec(
          340  +       "INSERT OR IGNORE INTO sfile"
          341  +       " SELECT pathname FROM vfile"
          342  +       "  WHERE (pathname=%Q"
          343  +       "     OR (pathname>'%q/' AND pathname<'%q0'))"
          344  +       "    AND NOT deleted",
          345  +       zTreeName, zTreeName, zTreeName
          346  +    );
          347  +    blob_reset(&treeName);
          348  +  }
          349  +  
          350  +  db_prepare(&loop, "SELECT x FROM sfile");
          351  +  while( db_step(&loop)==SQLITE_ROW ){
          352  +    fossil_print("DELETED %s\n", db_column_text(&loop, 0));
   270    353     }
   271         -  db_multi_exec("DELETE FROM vfile WHERE deleted AND rid=0");
          354  +  db_finalize(&loop);
          355  +  db_multi_exec(
          356  +    "UPDATE vfile SET deleted=1 WHERE pathname IN sfile;"
          357  +    "DELETE FROM vfile WHERE rid=0 AND deleted;"
          358  +  );
   272    359     db_end_transaction(0);
   273    360   }
          361  +
          362  +/*
          363  +** Capture the command-line --case-sensitive option.
          364  +*/
          365  +static const char *zCaseSensitive = 0;
          366  +void capture_case_sensitive_option(void){
          367  +  if( zCaseSensitive==0 ){
          368  +    zCaseSensitive = find_option("case-sensitive",0,1);
          369  +  }
          370  +}
          371  +
          372  +/*
          373  +** This routine determines if files should be case-sensitive or not.
          374  +** In other words, this routine determines if two filenames that
          375  +** differ only in case should be considered the same name or not.
          376  +**
          377  +** The case-sensitive setting determines the default value.  If
          378  +** the case-sensitive setting is undefined, then case sensitivity
          379  +** defaults on for Mac and Windows and off for all other unix.
          380  +**
          381  +** The --case-sensitive BOOLEAN command-line option overrides any
          382  +** setting.
          383  +*/
          384  +int filenames_are_case_sensitive(void){
          385  +  static int caseSensitive;
          386  +  static int once = 1;
          387  +
          388  +  if( once ){
          389  +    once = 0;
          390  +    if( zCaseSensitive ){
          391  +      caseSensitive = is_truth(zCaseSensitive);
          392  +    }else{
          393  +#if !defined(_WIN32) && !defined(__DARWIN__) && !defined(__APPLE__)
          394  +      caseSensitive = 1;  /* Unix */
          395  +#else
          396  +      caseSensitive = 0;  /* Windows and Mac */
          397  +#endif
          398  +      caseSensitive = db_get_boolean("case-sensitive",caseSensitive);
          399  +    }
          400  +  }
          401  +  return caseSensitive;
          402  +}
          403  +
          404  +/*
          405  +** Return one of two things:
          406  +**
          407  +**   ""                 (empty string) if filenames are case sensitive
          408  +**
          409  +**   "COLLATE nocase"   if filenames are not case sensitive.
          410  +*/
          411  +const char *filename_collation(void){
          412  +  return filenames_are_case_sensitive() ? "" : "COLLATE nocase";
          413  +}
          414  +
          415  +/*
          416  +** Do a strncmp() operation which is either case-sensitive or not
          417  +** depending on the setting of filenames_are_case_sensitive().
          418  +*/
          419  +int filenames_strncmp(const char *zA, const char *zB, int nByte){
          420  +  if( filenames_are_case_sensitive() ){
          421  +    return fossil_strncmp(zA,zB,nByte);
          422  +  }else{
          423  +    return fossil_strnicmp(zA,zB,nByte);
          424  +  }
          425  +}
          426  +
          427  +/*
          428  +** COMMAND: addremove
          429  +**
          430  +** Usage: %fossil addremove ?OPTIONS?
          431  +**
          432  +** Do all necessary "add" and "rm" commands to synchronize the repository
          433  +** with the content of the working checkout:
          434  +**
          435  +**  *  All files in the checkout but not in the repository (that is,
          436  +**     all files displayed using the "extra" command) are added as
          437  +**     if by the "add" command.
          438  +**
          439  +**  *  All files in the repository but missing from the checkout (that is,
          440  +**     all files that show as MISSING with the "status" command) are
          441  +**     removed as if by the "rm" command.
          442  +**
          443  +** The command does not "commit".  You must run the "commit" separately
          444  +** as a separate step.
          445  +**
          446  +** Files and directories whose names begin with "." are ignored unless
          447  +** the --dotfiles option is used.
          448  +**
          449  +** The --ignore option overrides the "ignore-glob" setting, as does the
          450  +** --case-sensitive option with the "case-sensitive" setting. See the
          451  +** documentation on the "settings" command for further information.
          452  +**
          453  +** The --test option shows what would happen without actually doing anything.
          454  +**
          455  +** This command can be used to track third party software.
          456  +** 
          457  +** Options: 
          458  +**   --case-sensitive <BOOL> override case-sensitive setting
          459  +**   --dotfiles              include files beginning with a dot (".")   
          460  +**   --ignore <CSG>          ignore files matching patterns from the 
          461  +**                           comma separated list of glob patterns.
          462  +**   --test                  If given, display instead of run actions
          463  +**
          464  +** See also: add, rm
          465  +*/
          466  +void addremove_cmd(void){
          467  +  Blob path;
          468  +  const char *zIgnoreFlag = find_option("ignore",0,1);
          469  +  unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
          470  +  int isTest = find_option("test",0,0)!=0;
          471  +  int caseSensitive;
          472  +  int n;
          473  +  Stmt q;
          474  +  int vid;
          475  +  int nAdd = 0;
          476  +  int nDelete = 0;
          477  +  Glob *pIgnore;
          478  +
          479  +  capture_case_sensitive_option();
          480  +  db_must_be_within_tree();
          481  +  caseSensitive = filenames_are_case_sensitive();
          482  +  if( zIgnoreFlag==0 ){
          483  +    zIgnoreFlag = db_get("ignore-glob", 0);
          484  +  }
          485  +  vid = db_lget_int("checkout",0);
          486  +  if( vid==0 ){
          487  +    fossil_panic("no checkout to add to");
          488  +  }
          489  +  db_begin_transaction();
          490  +
          491  +  /* step 1:  
          492  +  ** Populate the temp table "sfile" with the names of all unmanaged
          493  +  ** files currently in the check-out, except for files that match the
          494  +  ** --ignore or ignore-glob patterns and dot-files.  Then add all of
          495  +  ** the files in the sfile temp table to the set of managed files.
          496  +  */
          497  +  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
          498  +  n = strlen(g.zLocalRoot);
          499  +  blob_init(&path, g.zLocalRoot, n-1);
          500  +  /* now we read the complete file structure into a temp table */
          501  +  pIgnore = glob_create(zIgnoreFlag);
          502  +  vfile_scan(&path, blob_size(&path), scanFlags, pIgnore);
          503  +  glob_free(pIgnore);
          504  +  nAdd = add_files_in_sfile(vid, caseSensitive);
          505  +
          506  +  /* step 2: search for missing files */
          507  +  db_prepare(&q,
          508  +      "SELECT pathname, %Q || pathname, deleted FROM vfile"
          509  +      " WHERE NOT deleted"
          510  +      " ORDER BY 1",
          511  +      g.zLocalRoot
          512  +  );
          513  +  while( db_step(&q)==SQLITE_ROW ){
          514  +    const char * zFile;
          515  +    const char * zPath;
          516  +
          517  +    zFile = db_column_text(&q, 0);
          518  +    zPath = db_column_text(&q, 1);
          519  +    if( !file_wd_isfile_or_link(zPath) ){
          520  +      if( !isTest ){
          521  +        db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
          522  +      }
          523  +      fossil_print("DELETED  %s\n", zFile);
          524  +      nDelete++;
          525  +    }
          526  +  }
          527  +  db_finalize(&q);
          528  +  /* show command summary */
          529  +  fossil_print("added %d files, deleted %d files\n", nAdd, nDelete);
          530  +
          531  +  db_end_transaction(isTest);
          532  +}
          533  +
   274    534   
   275    535   /*
   276    536   ** Rename a single file.
   277    537   **
   278    538   ** The original name of the file is zOrig.  The new filename is zNew.
   279    539   */
   280    540   static void mv_one_file(int vid, const char *zOrig, const char *zNew){
   281         -  printf("RENAME %s %s\n", zOrig, zNew);
          541  +  int x = db_int(-1, "SELECT deleted FROM vfile WHERE pathname=%Q", zNew);
          542  +  if( x>=0 ){
          543  +    if( x==0 ){
          544  +      fossil_fatal("cannot rename '%s' to '%s' since another file named '%s'"
          545  +                   " is currently under management", zOrig, zNew, zNew); 
          546  +    }else{
          547  +      fossil_fatal("cannot rename '%s' to '%s' since the delete of '%s' has "
          548  +                   "not yet been committed", zOrig, zNew, zNew);
          549  +    }
          550  +  }
          551  +  fossil_print("RENAME %s %s\n", zOrig, zNew);
   282    552     db_multi_exec(
   283         -    "UPDATE vfile SET pathname='%s' WHERE pathname='%s' AND vid=%d",
          553  +    "UPDATE vfile SET pathname='%q' WHERE pathname='%q' AND vid=%d",
   284    554       zNew, zOrig, vid
   285    555     );
   286    556   }
   287    557   
   288    558   /*
   289    559   ** COMMAND: mv
   290         -** COMMAND: rename
          560  +** COMMAND: rename*
   291    561   **
   292    562   ** Usage: %fossil mv|rename OLDNAME NEWNAME
   293    563   **    or: %fossil mv|rename OLDNAME... DIR
   294    564   **
   295         -** Move or rename one or more files within the tree
          565  +** Move or rename one or more files or directories within the repository tree.
          566  +** You can either rename a file or directory or move it to another subdirectory.
   296    567   **
   297         -** This command does not rename the files on disk.  This command merely
          568  +** This command does NOT rename or move the files on disk.  This command merely
   298    569   ** records the fact that filenames have changed so that appropriate notations
   299    570   ** can be made at the next commit/checkin.
          571  +**
          572  +** See also: changes, status
   300    573   */
   301    574   void mv_cmd(void){
   302    575     int i;
   303    576     int vid;
   304    577     char *zDest;
   305    578     Blob dest;
   306    579     Stmt q;
................................................................................
   318    591     file_tree_name(zDest, &dest, 1);
   319    592     db_multi_exec(
   320    593       "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
   321    594     );
   322    595     db_multi_exec(
   323    596       "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
   324    597     );
   325         -  if( file_isdir(zDest)!=1 ){
          598  +  if( file_wd_isdir(zDest)!=1 ){
   326    599       Blob orig;
   327    600       if( g.argc!=4 ){
   328    601         usage("OLDNAME NEWNAME");
   329    602       }
   330    603       file_tree_name(g.argv[2], &orig, 1);
   331    604       db_multi_exec(
   332    605         "INSERT INTO mv VALUES(%B,%B)", &orig, &dest
................................................................................
   343    616         int nOrig;
   344    617         file_tree_name(g.argv[i], &orig, 1);
   345    618         zOrig = blob_str(&orig);
   346    619         nOrig = blob_size(&orig);
   347    620         db_prepare(&q,
   348    621            "SELECT pathname FROM vfile"
   349    622            " WHERE vid=%d"
   350         -         "   AND (pathname='%s' OR pathname GLOB '%s/*')"
          623  +         "   AND (pathname='%q' OR (pathname>'%q/' AND pathname<'%q0'))"
   351    624            " ORDER BY 1",
   352         -         vid, zOrig, zOrig
          625  +         vid, zOrig, zOrig, zOrig
   353    626         );
   354    627         while( db_step(&q)==SQLITE_ROW ){
   355    628           const char *zPath = db_column_text(&q, 0);
   356    629           int nPath = db_column_bytes(&q, 0);
   357    630           const char *zTail;
   358    631           if( nPath==nOrig ){
   359    632             zTail = file_tail(zPath);
   360    633           }else{
   361    634             zTail = &zPath[nOrig+1];
   362    635           }
   363    636           db_multi_exec(
   364         -          "INSERT INTO mv VALUES('%s','%s%s')",
          637  +          "INSERT INTO mv VALUES('%q','%q%q')",
   365    638             zPath, blob_str(&dest), zTail
   366    639           );
   367    640         }
   368    641         db_finalize(&q);
   369    642       }
   370    643     }
   371    644     db_prepare(&q, "SELECT f, t FROM mv ORDER BY f");

Changes to src/allrepo.c.

    32     32   ** string is returned even if no quoting is needed.
    33     33   */
    34     34   static char *quoteFilename(const char *zFilename){
    35     35     int i, c;
    36     36     int needQuote = 0;
    37     37     for(i=0; (c = zFilename[i])!=0; i++){
    38     38       if( c=='"' ) return 0;
    39         -    if( isspace(c) ) needQuote = 1;
           39  +    if( fossil_isspace(c) ) needQuote = 1;
    40     40       if( c=='\\' && zFilename[i+1]==0 ) return 0;
    41     41       if( c=='$' ) return 0;
    42     42     }
    43     43     if( needQuote ){
    44     44       return mprintf("\"%s\"", zFilename);
    45     45     }else{
    46     46       return mprintf("%s", zFilename);
    47     47     }
    48     48   }
           49  +
           50  +/*
           51  +** Build a string that contains all of the command-line options
           52  +** specified as arguments.  If the option name begins with "+" then
           53  +** it takes an argument.  Without the "+" it does not.
           54  +*/
           55  +static void collect_argument(Blob *pExtra, const char *zArg){
           56  +  if( find_option(zArg, 0, 0)!=0 ){
           57  +    blob_appendf(pExtra, " --%s", zArg);
           58  +  }
           59  +}
           60  +static void collect_argument_value(Blob *pExtra, const char *zArg){
           61  +  const char *zValue = find_option(zArg, 0, 1);
           62  +  if( zValue ){
           63  +    blob_appendf(pExtra, " --%s %s", zArg, zValue);
           64  +  }
           65  +}
    49     66   
    50     67   
    51     68   /*
    52     69   ** COMMAND: all
    53     70   **
    54     71   ** Usage: %fossil all (list|ls|pull|push|rebuild|sync)
    55     72   **
    56     73   ** The ~/.fossil file records the location of all repositories for a
    57     74   ** user.  This command performs certain operations on all repositories
    58         -** that can be useful before or after a period of disconnection operation.
           75  +** that can be useful before or after a period of disconnected operation.
           76  +**
           77  +** On Win32 systems, the file is named "_fossil" and is located in
           78  +** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%.
           79  +**
    59     80   ** Available operations are:
    60     81   **
    61         -**    list | ls  Display the location of all repositories
           82  +**    ignore     Arguments are repositories that should be ignored
           83  +**               by subsequent list, pull, push, rebuild, and sync.
           84  +**
           85  +**    list | ls  Display the location of all repositories.
           86  +**               The --ckout option causes all local checkouts to be
           87  +**               list instead.
           88  +**
           89  +**    changes    Shows all local checkouts that have uncommitted changes
    62     90   **
    63     91   **    pull       Run a "pull" operation on all repositories
    64     92   **
    65     93   **    push       Run a "push" on all repositories
    66     94   **
    67     95   **    rebuild    Rebuild on all repositories
    68     96   **
    69     97   **    sync       Run a "sync" on all repositories
    70     98   **
    71         -** Respositories are automatically added to the set of known repositories
    72         -** when one of the following commands against the repository: clone, info,
    73         -** pull, push, or sync
           99  +** Repositories are automatically added to the set of known repositories
          100  +** when one of the following commands are run against the repository: clone,
          101  +** info, pull, push, or sync.  Even previously ignored repositories are
          102  +** added back to the list of repositories by these commands.
    74    103   */
    75    104   void all_cmd(void){
    76    105     int n;
    77    106     Stmt q;
    78    107     const char *zCmd;
    79    108     char *zSyscmd;
    80    109     char *zFossil;
    81    110     char *zQFilename;
    82         -  int nMissing;
          111  +  Blob extra;
          112  +  int useCheckouts = 0;
          113  +  int quiet = 0;
          114  +  int testRun = 0;
          115  +  int stopOnError = find_option("dontstop",0,0)==0;
          116  +  int rc;
          117  +  Bag outOfDate;
    83    118     
          119  +  /* The undocumented --test option causes no changes to occur to any
          120  +  ** repository, but instead show what would have happened.  Intended for
          121  +  ** test and debugging use.
          122  +  */
          123  +  testRun = find_option("test",0,0)!=0;
          124  +
    84    125     if( g.argc<3 ){
    85         -    usage("list|ls|pull|push|rebuild|sync");
          126  +    usage("changes|list|ls|pull|push|rebuild|sync");
    86    127     }
    87    128     n = strlen(g.argv[2]);
    88    129     db_open_config(1);
          130  +  blob_zero(&extra);
    89    131     zCmd = g.argv[2];
    90    132     if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){
    91    133       zCmd = "list";
          134  +    useCheckouts = find_option("ckout","c",0)!=0;
    92    135     }else if( strncmp(zCmd, "push", n)==0 ){
    93    136       zCmd = "push -autourl -R";
          137  +    collect_argument(&extra, "verbose");
    94    138     }else if( strncmp(zCmd, "pull", n)==0 ){
    95    139       zCmd = "pull -autourl -R";
          140  +    collect_argument(&extra, "verbose");
    96    141     }else if( strncmp(zCmd, "rebuild", n)==0 ){
    97    142       zCmd = "rebuild";
          143  +    collect_argument(&extra, "cluster");
          144  +    collect_argument(&extra, "compress");
          145  +    collect_argument(&extra, "noverify");
          146  +    collect_argument_value(&extra, "pagesize");
          147  +    collect_argument(&extra, "vacuum");
          148  +    collect_argument(&extra, "deanalyze");
          149  +    collect_argument(&extra, "analyze");
          150  +    collect_argument(&extra, "wal");
          151  +    collect_argument(&extra, "stat");
    98    152     }else if( strncmp(zCmd, "sync", n)==0 ){
    99    153       zCmd = "sync -autourl -R";
          154  +    collect_argument(&extra, "verbose");
          155  +  }else if( strncmp(zCmd, "test-integrity", n)==0 ){
          156  +    zCmd = "test-integrity";
          157  +  }else if( strncmp(zCmd, "test-orphans", n)==0 ){
          158  +    zCmd = "test-orphans -R";
          159  +  }else if( strncmp(zCmd, "test-missing", n)==0 ){
          160  +    zCmd = "test-missing -q -R";
          161  +    collect_argument(&extra, "notshunned");
          162  +  }else if( strncmp(zCmd, "changes", n)==0 ){
          163  +    zCmd = "changes --quiet --header --chdir";
          164  +    useCheckouts = 1;
          165  +    stopOnError = 0;
          166  +    quiet = 1;
          167  +  }else if( strncmp(zCmd, "ignore", n)==0 ){
          168  +    int j;
          169  +    verify_all_options();
          170  +    db_begin_transaction();
          171  +    for(j=3; j<g.argc; j++){
          172  +      char *zSql = mprintf("DELETE FROM global_config"
          173  +                           " WHERE name GLOB 'repo:%q'", g.argv[j]);
          174  +      if( testRun ){
          175  +        fossil_print("%s\n", zSql);
          176  +      }else{
          177  +        db_multi_exec("%s", zSql);
          178  +      }
          179  +      fossil_free(zSql);
          180  +    }
          181  +    db_end_transaction(0);
          182  +    return;
   100    183     }else{
   101    184       fossil_fatal("\"all\" subcommand should be one of: "
   102         -                 "list ls push pull rebuild sync");
          185  +                 "changes ignore list ls push pull rebuild sync");
          186  +  }
          187  +  verify_all_options();
          188  +  zFossil = quoteFilename(g.nameOfExe);
          189  +  if( useCheckouts ){
          190  +    db_prepare(&q,
          191  +       "SELECT substr(name, 7) COLLATE nocase, max(rowid)"
          192  +       "  FROM global_config"
          193  +       " WHERE substr(name, 1, 6)=='ckout:'"
          194  +       " GROUP BY 1 ORDER BY 1"
          195  +    );
          196  +  }else{
          197  +    db_prepare(&q,
          198  +       "SELECT substr(name, 6) COLLATE nocase, max(rowid)"
          199  +       "  FROM global_config"
          200  +       " WHERE substr(name, 1, 5)=='repo:'"
          201  +       " GROUP BY 1 ORDER BY 1"
          202  +    );
   103    203     }
   104         -  zFossil = quoteFilename(g.argv[0]);
   105         -  nMissing = 0;
   106         -  db_prepare(&q,
   107         -     "SELECT DISTINCT substr(name, 6) COLLATE nocase"
   108         -     "  FROM global_config"
   109         -     " WHERE substr(name, 1, 5)=='repo:' ORDER BY 1"
   110         -  );
          204  +  bag_init(&outOfDate);
   111    205     while( db_step(&q)==SQLITE_ROW ){
   112    206       const char *zFilename = db_column_text(&q, 0);
   113         -    if( access(zFilename, 0) ){
   114         -      nMissing++;
          207  +    int rowid = db_column_int(&q, 1);
          208  +    if( file_access(zFilename, 0) || !file_is_canonical(zFilename) ){
          209  +      bag_insert(&outOfDate, rowid);
          210  +      continue;
          211  +    }
          212  +    if( useCheckouts && file_isdir(zFilename)!=1 ){
          213  +      bag_insert(&outOfDate, rowid);
   115    214         continue;
   116    215       }
   117         -    if( !file_is_canonical(zFilename) ) nMissing++;
   118    216       if( zCmd[0]=='l' ){
   119         -      printf("%s\n", zFilename);
          217  +      fossil_print("%s\n", zFilename);
   120    218         continue;
   121    219       }
   122    220       zQFilename = quoteFilename(zFilename);
   123         -    zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename);
   124         -    printf("%s\n", zSyscmd);
   125         -    fflush(stdout);
   126         -    portable_system(zSyscmd);
          221  +    zSyscmd = mprintf("%s %s %s%s",
          222  +                      zFossil, zCmd, zQFilename, blob_str(&extra));
          223  +    if( !quiet || testRun ){
          224  +      fossil_print("%s\n", zSyscmd);
          225  +      fflush(stdout);
          226  +    }
          227  +    rc = testRun ? 0 : fossil_system(zSyscmd);
   127    228       free(zSyscmd);
   128    229       free(zQFilename);
          230  +    if( stopOnError && rc ){
          231  +      break;
          232  +    }
   129    233     }
          234  +  db_finalize(&q);
   130    235     
   131    236     /* If any repositories whose names appear in the ~/.fossil file could not
   132    237     ** be found, remove those names from the ~/.fossil file.
   133    238     */
   134         -  if( nMissing ){
   135         -    db_begin_transaction();
   136         -    db_reset(&q);
   137         -    while( db_step(&q)==SQLITE_ROW ){
   138         -      const char *zFilename = db_column_text(&q, 0);
   139         -      if( access(zFilename, 0) ){
   140         -        char *zRepo = mprintf("repo:%s", zFilename);
   141         -        db_unset(zRepo, 1);
   142         -        free(zRepo);
   143         -      }else if( !file_is_canonical(zFilename) ){
   144         -        Blob cname;
   145         -        char *zRepo = mprintf("repo:%s", zFilename);
   146         -        db_unset(zRepo, 1);
   147         -        free(zRepo);
   148         -        file_canonical_name(zFilename, &cname);
   149         -        zRepo = mprintf("repo:%s", blob_str(&cname));
   150         -        db_set(zRepo, "1", 1);
   151         -        free(zRepo);
   152         -      }
          239  +  if( bag_count(&outOfDate)>0 ){
          240  +    Blob sql;
          241  +    char *zSep = "(";
          242  +    int rowid;
          243  +    blob_zero(&sql);
          244  +    blob_appendf(&sql, "DELETE FROM global_config WHERE rowid IN ");
          245  +    for(rowid=bag_first(&outOfDate); rowid>0; rowid=bag_next(&outOfDate,rowid)){
          246  +      blob_appendf(&sql, "%s%d", zSep, rowid);
          247  +      zSep = ",";
          248  +    }
          249  +    blob_appendf(&sql, ")");
          250  +    if( testRun ){
          251  +      fossil_print("%s\n", blob_str(&sql));
          252  +    }else{
          253  +      db_multi_exec(blob_str(&sql));
   153    254       }
   154         -    db_reset(&q);
   155         -    db_end_transaction(0);
          255  +    blob_reset(&sql);
   156    256     }
   157         -  db_finalize(&q);
   158    257   }

Changes to src/attach.c.

    39     39     Blob sql;
    40     40     Stmt q;
    41     41   
    42     42     if( zPage && zTkt ) zTkt = 0;
    43     43     login_check_credentials();
    44     44     blob_zero(&sql);
    45     45     blob_append(&sql,
    46         -     "SELECT datetime(mtime,'localtime'), src, target, filename, comment, user"
           46  +     "SELECT datetime(mtime,'localtime'), src, target, filename,"
           47  +     "       comment, user,"
           48  +     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
    47     49        "  FROM attachment",
    48     50        -1
    49     51     );
    50     52     if( zPage ){
    51         -    if( g.okRdWiki==0 ) login_needed();
           53  +    if( g.perm.RdWiki==0 ) login_needed();
    52     54       style_header("Attachments To %h", zPage);
    53     55       blob_appendf(&sql, " WHERE target=%Q", zPage);
    54     56     }else if( zTkt ){
    55         -    if( g.okRdTkt==0 ) login_needed();
           57  +    if( g.perm.RdTkt==0 ) login_needed();
    56     58       style_header("Attachments To Ticket %.10s", zTkt);
    57     59       blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
    58     60     }else{
    59         -    if( g.okRdTkt==0 && g.okRdWiki==0 ) login_needed();
           61  +    if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
    60     62       style_header("All Attachments");
    61     63     }
    62     64     blob_appendf(&sql, " ORDER BY mtime DESC");
    63     65     db_prepare(&q, "%s", blob_str(&sql));
           66  +  @ <ol>
    64     67     while( db_step(&q)==SQLITE_ROW ){
    65     68       const char *zDate = db_column_text(&q, 0);
    66     69       const char *zSrc = db_column_text(&q, 1);
    67     70       const char *zTarget = db_column_text(&q, 2);
    68     71       const char *zFilename = db_column_text(&q, 3);
    69     72       const char *zComment = db_column_text(&q, 4);
    70     73       const char *zUser = db_column_text(&q, 5);
           74  +    const char *zUuid = db_column_text(&q, 6);
           75  +    int attachid = db_column_int(&q, 7);
           76  +    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
    71     77       int i;
    72     78       char *zUrlTail;
    73     79       for(i=0; zFilename[i]; i++){
    74     80         if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ 
    75     81           zFilename = &zFilename[i+1];
    76     82           i = -1;
    77     83         }
    78     84       }
    79     85       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
    80     86         zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
    81     87       }else{
    82     88         zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    83     89       }
    84         -    @
    85         -    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    86         -    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
    87         -    if( zComment ) while( isspace(zComment[0]) ) zComment++;
           90  +    @ <li><p>
           91  +    @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a>
           92  +    if( moderation_pending(attachid) ){
           93  +      @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
           94  +    }
           95  +    @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
           96  +    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
           97  +    if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
    88     98       if( zComment && zComment[0] ){
    89         -      @ %w(zComment)<br>
           99  +      @ %w(zComment)<br />
    90    100       }
    91    101       if( zPage==0 && zTkt==0 ){
    92    102         if( zSrc==0 || zSrc[0]==0 ){
    93    103           zSrc = "Deleted from";
    94    104         }else {
    95    105           zSrc = "Added to";
    96    106         }
    97    107         if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
    98         -        char zShort[20];
    99         -        memcpy(zShort, zTarget, 10);
   100         -        zShort[10] = 0;
   101    108           @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
   102         -        @ %s(zShort)</a>
          109  +        @ %S(zTarget)</a>
   103    110         }else{
   104    111           @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
   105    112           @ %h(zTarget)</a>
   106    113         }
   107    114       }else{
   108    115         if( zSrc==0 || zSrc[0]==0 ){
   109    116           @ Deleted
   110    117         }else {
   111    118           @ Added
   112    119         }
   113    120       }
   114         -    @ by %h(zUser) on
          121  +    @ by %h(zDispUser) on
   115    122       hyperlink_to_date(zDate, ".");
   116    123       free(zUrlTail);
   117    124     }
   118    125     db_finalize(&q);
          126  +  @ </ol>
   119    127     style_footer();
   120    128     return;
   121    129   }
   122    130   
   123    131   /*
   124    132   ** WEBPAGE: attachdownload
   125    133   ** WEBPAGE: attachimage
................................................................................
   140    148     int attachid = atoi(PD("attachid","0"));
   141    149     char *zUUID;
   142    150   
   143    151     if( zPage && zTkt ) zTkt = 0;
   144    152     if( zFile==0 ) fossil_redirect_home();
   145    153     login_check_credentials();
   146    154     if( zPage ){
   147         -    if( g.okRdWiki==0 ) login_needed();
          155  +    if( g.perm.RdWiki==0 ) login_needed();
   148    156       zTarget = zPage;
   149    157     }else if( zTkt ){
   150         -    if( g.okRdTkt==0 ) login_needed();
          158  +    if( g.perm.RdTkt==0 ) login_needed();
   151    159       zTarget = zTkt;
   152    160     }else{
   153    161       fossil_redirect_home();
   154    162     }
   155    163     if( attachid>0 ){
   156    164       zUUID = db_text(0,
   157    165          "SELECT coalesce(src,'x') FROM attachment"
................................................................................
   173    181       return;
   174    182     }else if( zUUID[0]=='x' ){
   175    183       style_header("Missing");
   176    184       @ Attachment has been deleted
   177    185       style_footer();
   178    186       return;
   179    187     }
   180         -  g.okRead = 1;
          188  +  g.perm.Read = 1;
   181    189     cgi_replace_parameter("name",zUUID);
   182         -  if( strcmp(g.zPath,"attachview")==0 ){
          190  +  if( fossil_strcmp(g.zPath,"attachview")==0 ){
   183    191       artifact_page();
   184    192     }else{
   185    193       cgi_replace_parameter("m", mimetype_from_name(zFile));
   186    194       rawartifact_page();
   187    195     }
   188    196   }
          197  +
          198  +/*
          199  +** Save an attachment control artifact into the repository
          200  +*/
          201  +static void attach_put(
          202  +  Blob *pAttach,     /* Text of the Attachment record */
          203  +  int attachRid,     /* RID for the file that is being attached */
          204  +  int needMod        /* True if the attachment is subject to moderation */
          205  +){
          206  +  int rid;
          207  +  if( needMod ){
          208  +    rid = content_put_ex(pAttach, 0, 0, 0, 1);
          209  +    moderation_table_create();
          210  +    db_multi_exec(
          211  +      "INSERT INTO modreq(objid,attachRid) VALUES(%d,%d);",
          212  +      rid, attachRid
          213  +    );
          214  +  }else{
          215  +    rid = content_put(pAttach);
          216  +    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
          217  +    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
          218  +  }
          219  +  manifest_crosslink(rid, pAttach);
          220  +}
   189    221   
   190    222   
   191    223   /*
   192    224   ** WEBPAGE: attachadd
   193    225   **
   194    226   **    tkt=TICKETUUID
   195    227   **    page=WIKIPAGE
................................................................................
   202    234     const char *zTkt = P("tkt");
   203    235     const char *zFrom = P("from");
   204    236     const char *aContent = P("f");
   205    237     const char *zName = PD("f:filename","unknown");
   206    238     const char *zTarget;
   207    239     const char *zTargetType;
   208    240     int szContent = atoi(PD("f:bytes","0"));
          241  +  int goodCaptcha = 1;
   209    242   
   210    243     if( P("cancel") ) cgi_redirect(zFrom);
   211    244     if( zPage && zTkt ) fossil_redirect_home();
   212    245     if( zPage==0 && zTkt==0 ) fossil_redirect_home();
   213    246     login_check_credentials();
   214    247     if( zPage ){
   215         -    if( g.okApndWiki==0 || g.okAttach==0 ) login_needed();
          248  +    if( g.perm.ApndWiki==0 || g.perm.Attach==0 ) login_needed();
   216    249       if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
   217    250         fossil_redirect_home();
   218    251       }
   219    252       zTarget = zPage;
   220    253       zTargetType = mprintf("Wiki Page <a href=\"%s/wiki?name=%h\">%h</a>",
   221    254                              g.zTop, zPage, zPage);
   222    255     }else{
   223         -    if( g.okApndTkt==0 || g.okAttach==0 ) login_needed();
          256  +    if( g.perm.ApndTkt==0 || g.perm.Attach==0 ) login_needed();
   224    257       if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
   225         -      fossil_redirect_home();
          258  +      zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag" 
          259  +                        " WHERE tagname GLOB 'tkt-%q*'", zTkt);
          260  +      if( zTkt==0 ) fossil_redirect_home();
   226    261       }
   227    262       zTarget = zTkt;
   228         -    zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>",
          263  +    zTargetType = mprintf("Ticket <a href=\"%s/tktview/%S\">%S</a>",
   229    264                             g.zTop, zTkt, zTkt);
   230    265     }
   231    266     if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
   232    267     if( P("cancel") ){
   233    268       cgi_redirect(zFrom);
   234    269     }
   235         -  if( P("ok") && szContent>0 ){
          270  +  if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
   236    271       Blob content;
   237    272       Blob manifest;
   238    273       Blob cksum;
   239    274       char *zUUID;
   240    275       const char *zComment;
   241    276       char *zDate;
   242    277       int rid;
   243    278       int i, n;
          279  +    int addCompress = 0;
          280  +    Manifest *pManifest;
          281  +    int needModerator;
   244    282   
   245    283       db_begin_transaction();
   246    284       blob_init(&content, aContent, szContent);
   247         -    rid = content_put(&content, 0, 0);
          285  +    pManifest = manifest_parse(&content, 0, 0);
          286  +    manifest_destroy(pManifest);
          287  +    blob_init(&content, aContent, szContent);
          288  +    if( pManifest ){
          289  +      blob_compress(&content, &content);
          290  +      addCompress = 1;
          291  +    }
          292  +    needModerator =
          293  +         (zTkt!=0 && g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1) ||
          294  +         (zPage!=0 && g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1);
          295  +    rid = content_put_ex(&content, 0, 0, 0, needModerator);
   248    296       zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   249    297       blob_zero(&manifest);
   250    298       for(i=n=0; zName[i]; i++){
   251    299         if( zName[i]=='/' || zName[i]=='\\' ) n = i;
   252    300       }
   253    301       zName += n;
   254    302       if( zName[0]==0 ) zName = "unknown";
   255         -    blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID);
          303  +    blob_appendf(&manifest, "A %F%s %F %s\n",
          304  +                 zName, addCompress ? ".gz" : "", zTarget, zUUID);
   256    305       zComment = PD("comment", "");
   257         -    while( isspace(zComment[0]) ) zComment++;
          306  +    while( fossil_isspace(zComment[0]) ) zComment++;
   258    307       n = strlen(zComment);
   259         -    while( n>0 && isspace(zComment[n-1]) ){ n--; }
          308  +    while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
   260    309       if( n>0 ){
   261    310         blob_appendf(&manifest, "C %F\n", zComment);
   262    311       }
   263         -    zDate = db_text(0, "SELECT datetime('now')");
   264         -    zDate[10] = 'T';
          312  +    zDate = date_in_standard_format("now");
   265    313       blob_appendf(&manifest, "D %s\n", zDate);
   266    314       blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
   267    315       md5sum_blob(&manifest, &cksum);
   268    316       blob_appendf(&manifest, "Z %b\n", &cksum);
   269         -    rid = content_put(&manifest, 0, 0);
   270         -    manifest_crosslink(rid, &manifest);
          317  +    attach_put(&manifest, rid, needModerator);
          318  +    assert( blob_is_reset(&manifest) );
   271    319       db_end_transaction(0);
   272    320       cgi_redirect(zFrom);
   273    321     }
   274    322     style_header("Add Attachment");
          323  +  if( !goodCaptcha ){
          324  +    @ <p class="generalError">Error: Incorrect security code.</p>
          325  +  }
   275    326     @ <h2>Add Attachment To %s(zTargetType)</h2>
   276         -  @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
   277         -  @  enctype="multipart/form-data">
          327  +  form_begin("enctype='multipart/form-data'", "%R/attachadd");
          328  +  @ <div>
   278    329     @ File to Attach:
   279         -  @ <input type="file" name="f" size="60"><br>
   280         -  @ Description:<br>
   281         -  @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br>
          330  +  @ <input type="file" name="f" size="60" /><br />
          331  +  @ Description:<br />
          332  +  @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
   282    333     if( zTkt ){
   283         -    @ <input type="hidden" name="tkt" value="%h(zTkt)">
          334  +    @ <input type="hidden" name="tkt" value="%h(zTkt)" />
   284    335     }else{
   285         -    @ <input type="hidden" name="page" value="%h(zPage)">
          336  +    @ <input type="hidden" name="page" value="%h(zPage)" />
   286    337     }
   287         -  @ <input type="hidden" name="from" value="%h(zFrom)">
   288         -  @ <input type="submit" name="ok" value="Add Attachment">
   289         -  @ <input type="submit" name="cancel" value="Cancel">
          338  +  @ <input type="hidden" name="from" value="%h(zFrom)" />
          339  +  @ <input type="submit" name="ok" value="Add Attachment" />
          340  +  @ <input type="submit" name="cancel" value="Cancel" />
          341  +  @ </div>
          342  +  captcha_generate();
   290    343     @ </form>
   291    344     style_footer();
   292    345   }
   293    346   
   294         -
   295    347   /*
   296         -** WEBPAGE: attachdelete
          348  +** WEBPAGE: ainfo
          349  +** URL: /ainfo?name=ARTIFACTID
   297    350   **
   298         -**    tkt=TICKETUUID
   299         -**    page=WIKIPAGE
   300         -**    file=FILENAME
   301         -**
   302         -** "Delete" an attachment.  Because objects in Fossil are immutable
   303         -** the attachment isn't really deleted.  Instead, we change the content
   304         -** of the attachment to NULL, which the system understands as being
   305         -** deleted.  Historical values of the attachment are preserved.
          351  +** Show the details of an attachment artifact.
   306    352   */
   307         -void attachdel_page(void){
   308         -  const char *zPage = P("page");
   309         -  const char *zTkt = P("tkt");
   310         -  const char *zFile = P("file");
   311         -  const char *zFrom = P("from");
   312         -  const char *zTarget;
          353  +void ainfo_page(void){
          354  +  int rid;                       /* RID for the control artifact */
          355  +  int ridSrc;                    /* RID for the attached file */
          356  +  char *zDate;                   /* Date attached */
          357  +  const char *zUuid;             /* UUID of the control artifact */
          358  +  Manifest *pAttach;             /* Parse of the control artifact */
          359  +  const char *zTarget;           /* Wiki or ticket attached to */
          360  +  const char *zSrc;              /* UUID of the attached file */
          361  +  const char *zName;             /* Name of the attached file */
          362  +  const char *zDesc;             /* Description of the attached file */
          363  +  const char *zWikiName = 0;     /* Wiki page name when attached to Wiki */
          364  +  const char *zTktUuid = 0;      /* Ticket ID when attached to a ticket */
          365  +  int modPending;                /* True if awaiting moderation */
          366  +  const char *zModAction;        /* Moderation action or NULL */
          367  +  int isModerator;               /* TRUE if user is the moderator */
          368  +  const char *zMime;             /* MIME Type */
          369  +  Blob attach;                   /* Content of the attachment */
   313    370   
   314         -  if( zPage && zTkt ) fossil_redirect_home();
   315         -  if( zPage==0 && zTkt==0 ) fossil_redirect_home();
   316         -  if( zFile==0 ) fossil_redirect_home();
   317    371     login_check_credentials();
   318         -  if( zPage ){
   319         -    if( g.okWrWiki==0 || g.okAttach==0 ) login_needed();
   320         -    zTarget = zPage;
   321         -  }else{
   322         -    if( g.okWrTkt==0 || g.okAttach==0 ) login_needed();
   323         -    zTarget = zTkt;
   324         -  }
   325         -  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
   326         -  if( P("cancel") ){
   327         -    cgi_redirect(zFrom);
   328         -  }
   329         -  if( P("confirm") ){
          372  +  if( !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(); return; }
          373  +  rid = name_to_rid_www("name");
          374  +  if( rid==0 ){ fossil_redirect_home(); }
          375  +  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
          376  +#if 0
          377  +  /* Shunning here needs to get both the attachment control artifact and
          378  +  ** the object that is attached. */
          379  +  if( g.perm.Admin ){
          380  +    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
          381  +      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
          382  +            g.zTop, zUuid);
          383  +    }else{
          384  +      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
          385  +            g.zTop, zUuid);
          386  +    }
          387  +  }
          388  +#endif
          389  +  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT);
          390  +  if( pAttach==0 ) fossil_redirect_home();
          391  +  zTarget = pAttach->zAttachTarget;
          392  +  zSrc = pAttach->zAttachSrc;
          393  +  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%s'", zSrc);
          394  +  zName = pAttach->zAttachName;
          395  +  zDesc = pAttach->zComment;
          396  +  if( validate16(zTarget, strlen(zTarget))
          397  +   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%s'", zTarget)
          398  +  ){
          399  +    zTktUuid = zTarget;
          400  +    if( !g.perm.RdTkt ){ login_needed(); return; }
          401  +    if( g.perm.WrTkt ){
          402  +      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
          403  +    }
          404  +  }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){
          405  +    zWikiName = zTarget;
          406  +    if( !g.perm.RdWiki ){ login_needed(); return; }
          407  +    if( g.perm.WrWiki ){
          408  +      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
          409  +    }
          410  +  }
          411  +  zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
          412  +
          413  +  if( P("confirm")
          414  +   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
          415  +  ){
   330    416       int i, n, rid;
   331    417       char *zDate;
   332    418       Blob manifest;
   333    419       Blob cksum;
          420  +    const char *zFile = zName;
   334    421   
   335    422       db_begin_transaction();
   336    423       blob_zero(&manifest);
   337    424       for(i=n=0; zFile[i]; i++){
   338    425         if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
   339    426       }
   340    427       zFile += n;
   341    428       if( zFile[0]==0 ) zFile = "unknown";
   342    429       blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
   343         -    zDate = db_text(0, "SELECT datetime('now')");
   344         -    zDate[10] = 'T';
          430  +    zDate = date_in_standard_format("now");
   345    431       blob_appendf(&manifest, "D %s\n", zDate);
   346    432       blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
   347    433       md5sum_blob(&manifest, &cksum);
   348    434       blob_appendf(&manifest, "Z %b\n", &cksum);
   349         -    rid = content_put(&manifest, 0, 0);
          435  +    rid = content_put(&manifest);
   350    436       manifest_crosslink(rid, &manifest);
   351    437       db_end_transaction(0);
   352         -    cgi_redirect(zFrom);
   353         -  }    
   354         -  style_header("Delete Attachment");
   355         -  @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
   356         -  @ <p>Confirm that you want to delete the attachment named
   357         -  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
   358         -  if( zTkt ){
   359         -    @ <input type="hidden" name="tkt" value="%h(zTkt)">
          438  +    @ <p>The attachment below has been deleted.</p>
          439  +  }
          440  +
          441  +  if( P("del")
          442  +   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
          443  +  ){
          444  +    form_begin(0, "%R/ainfo/%s", zUuid);
          445  +    @ <p>Confirm you want to delete the attachment shown below.
          446  +    @ <input type="submit" name="confirm" value="Confirm">
          447  +    @ </form>
          448  +  }
          449  +
          450  +  isModerator = (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki);
          451  +  if( isModerator && (zModAction = P("modaction"))!=0 ){
          452  +    if( strcmp(zModAction,"delete")==0 ){
          453  +      moderation_disapprove(rid);
          454  +      if( zTktUuid ){
          455  +        cgi_redirectf("%R/tktview/%s", zTktUuid);
          456  +      }else{
          457  +        cgi_redirectf("%R/wiki?name=%t", zWikiName);
          458  +      }
          459  +      return;
          460  +    }
          461  +    if( strcmp(zModAction,"approve")==0 ){
          462  +      moderation_approve(rid);
          463  +    }
          464  +  }
          465  +  style_header("Attachment Details");
          466  +  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
          467  +
          468  +  @ <div class="section">Overview</div>
          469  +  @ <p><table class="label-value">
          470  +  @ <tr><th>Artifact&nbsp;ID:</th>
          471  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
          472  +  if( g.perm.Setup ){
          473  +    @ (%d(rid))
          474  +  }
          475  +  modPending = moderation_pending(rid);
          476  +  if( modPending ){
          477  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          478  +  }
          479  +  if( zTktUuid ){
          480  +    @ <tr><th>Ticket:</th>
          481  +    @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
          482  +  }
          483  +  if( zWikiName ){
          484  +    @ <tr><th>Wiki&nbsp;Page:</th>
          485  +    @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
          486  +  }
          487  +  @ <tr><th>Date:</th><td>
          488  +  hyperlink_to_date(zDate, "</td></tr>");
          489  +  free(zDate);
          490  +  @ <tr><th>User:</th><td>
          491  +  hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>");
          492  +  @ <tr><th>Artifact&nbsp;Attached:</th>
          493  +  @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a>
          494  +  if( g.perm.Setup ){
          495  +    @ (%d(ridSrc))
          496  +  }
          497  +  @ <tr><th>Filename:</th><td>%h(zName)</td></tr>
          498  +  zMime = mimetype_from_name(zName);
          499  +  if( g.perm.Setup ){
          500  +    @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr>
          501  +  }
          502  +  @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr>
          503  +  @ </table>
          504  +  
          505  +  if( isModerator && modPending ){
          506  +    @ <div class="section">Moderation</div>
          507  +    @ <blockquote>
          508  +    form_begin(0, "%R/ainfo/%s", zUuid);
          509  +    @ <label><input type="radio" name="modaction" value="delete">
          510  +    @ Delete this change</label><br />
          511  +    @ <label><input type="radio" name="modaction" value="approve">
          512  +    @ Approve this change</label><br />
          513  +    @ <input type="submit" value="Submit">
          514  +    @ </form>
          515  +    @ </blockquote>
          516  +  }
          517  +
          518  +  @ <div class="section">Content Appended</div>
          519  +  @ <blockquote>
          520  +  blob_zero(&attach);
          521  +  if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
          522  +    const char *z;
          523  +    const char *zLn = P("ln");
          524  +    content_get(ridSrc, &attach);
          525  +    blob_to_utf8_no_bom(&attach, 0);
          526  +    z = blob_str(&attach);
          527  +    if( zLn ){
          528  +      output_text_with_line_numbers(z, zLn);
          529  +    }else{
          530  +      @ <pre>
          531  +      @ %h(z)
          532  +      @ </pre>
          533  +    }
          534  +  }else if( strncmp(zMime, "image/", 6)==0 ){
          535  +    @ <img src="%R/raw/%S(zSrc)?m=%s(zMime)"></img>
          536  +    style_submenu_element("Image", "Image", "%R/raw/%S?m=%s", zSrc, zMime);
   360    537     }else{
   361         -    @ <input type="hidden" name="page" value="%h(zPage)">
          538  +    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
          539  +    @ <i>(file is %d(sz) bytes of binary data)</i>
   362    540     }
   363         -  @ <input type="hidden" name="file" value="%h(zFile)">
   364         -  @ <input type="hidden" name="from" value="%h(zFrom)">
   365         -  @ <input type="submit" name="confirm" value="Delete">
   366         -  @ <input type="submit" name="cancel" value="Cancel">
   367         -  @ </form>
          541  +  @ </blockquote>
          542  +  manifest_destroy(pAttach);
          543  +  blob_reset(&attach);
   368    544     style_footer();
          545  +}
   369    546   
          547  +/*
          548  +** Output HTML to show a list of attachments.
          549  +*/
          550  +void attachment_list(
          551  +  const char *zTarget,   /* Object that things are attached to */
          552  +  const char *zHeader    /* Header to display with attachments */
          553  +){
          554  +  int cnt = 0;
          555  +  Stmt q;
          556  +  db_prepare(&q,
          557  +     "SELECT datetime(mtime,'localtime'), filename, user,"
          558  +     "       (SELECT uuid FROM blob WHERE rid=attachid), src"
          559  +     "  FROM attachment"
          560  +     " WHERE isLatest AND src!='' AND target=%Q"
          561  +     " ORDER BY mtime DESC", 
          562  +     zTarget
          563  +  );
          564  +  while( db_step(&q)==SQLITE_ROW ){
          565  +    const char *zDate = db_column_text(&q, 0);
          566  +    const char *zFile = db_column_text(&q, 1);
          567  +    const char *zUser = db_column_text(&q, 2);
          568  +    const char *zUuid = db_column_text(&q, 3);
          569  +    const char *zSrc = db_column_text(&q, 4);
          570  +    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
          571  +    if( cnt==0 ){
          572  +      @ %s(zHeader)
          573  +    }
          574  +    cnt++;
          575  +    @ <li>
          576  +    @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
          577  +    @ added by %h(zDispUser) on
          578  +    hyperlink_to_date(zDate, ".");
          579  +    @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
          580  +    @ </li>
          581  +  }
          582  +  if( cnt ){
          583  +    @ </ul>
          584  +  }
          585  +  db_finalize(&q);
          586  +  
   370    587   }

Changes to src/bag.c.

    84     84     int i;
    85     85     Bag old;
    86     86     int nDel = 0;   /* Number of deleted entries */
    87     87     int nLive = 0;  /* Number of live entries */
    88     88   
    89     89     old = *p;
    90     90     assert( newSize>old.cnt );
    91         -  p->a = malloc( sizeof(p->a[0])*newSize );
           91  +  p->a = fossil_malloc( sizeof(p->a[0])*newSize );
    92     92     p->sz = newSize;
    93     93     memset(p->a, 0, sizeof(p->a[0])*newSize );
    94     94     for(i=0; i<old.sz; i++){
    95     95       int e = old.a[i];
    96     96       if( e>0 ){
    97     97         unsigned h = bag_hash(e)%newSize;
    98     98         while( p->a[h] ){

Added src/bisect.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@sqlite.org
           14  +**
           15  +*******************************************************************************
           16  +**
           17  +** This file contains code used to implement the "bisect" command.
           18  +**
           19  +** This file also contains logic used to compute the closure of filename
           20  +** changes that have occurred across multiple check-ins.
           21  +*/
           22  +#include "config.h"
           23  +#include "bisect.h"
           24  +#include <assert.h>
           25  +
           26  +/*
           27  +** Local variables for this module
           28  +*/
           29  +static struct {
           30  +  int bad;                /* The bad version */
           31  +  int good;               /* The good version */
           32  +} bisect;
           33  +
           34  +/*
           35  +** Find the shortest path between bad and good.
           36  +*/
           37  +void bisect_path(void){
           38  +  PathNode *p;
           39  +  bisect.bad = db_lget_int("bisect-bad", 0);
           40  +  if( bisect.bad==0 ){
           41  +    fossil_fatal("no \"bad\" version has been identified");
           42  +  }
           43  +  bisect.good = db_lget_int("bisect-good", 0);
           44  +  if( bisect.good==0 ){
           45  +    fossil_fatal("no \"good\" version has been identified");
           46  +  }
           47  +  p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0);
           48  +  if( p==0 ){
           49  +    char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad);
           50  +    char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good);
           51  +    fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
           52  +                 zGood, zBad);
           53  +  }
           54  +}
           55  +
           56  +/*
           57  +** The set of all bisect options.
           58  +*/
           59  +static const struct {
           60  +  const char *zName;
           61  +  const char *zDefault;
           62  +  const char *zDesc;
           63  +} aBisectOption[] = {
           64  +  { "auto-next",    "on",    "Automatically run \"bisect next\" after each "
           65  +                             "\"bisect good\" or \"bisect bad\"" },
           66  +  { "direct-only",  "on",    "Follow only primary parent-child links, not "
           67  +                             "merges\n" },
           68  +};
           69  +
           70  +/*
           71  +** Return the value of a boolean bisect option.
           72  +*/
           73  +int bisect_option(const char *zName){
           74  +  unsigned int i;
           75  +  int r = -1;
           76  +  for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
           77  +    if( fossil_strcmp(zName, aBisectOption[i].zName)==0 ){
           78  +      char *zLabel = mprintf("bisect-%s", zName);
           79  +      char *z = db_lget(zLabel, (char*)aBisectOption[i].zDefault);
           80  +      if( is_truth(z) ) r = 1;
           81  +      if( is_false(z) ) r = 0;
           82  +      if( r<0 ) r = is_truth(aBisectOption[i].zDefault);
           83  +      free(zLabel);
           84  +      break;
           85  +    }
           86  +  }
           87  +  assert( r>=0 );
           88  +  return r;
           89  +}
           90  +
           91  +/*
           92  +** List a bisect path.
           93  +*/
           94  +static void bisect_list(int abbreviated){
           95  +  PathNode *p;
           96  +  int vid = db_lget_int("checkout", 0);
           97  +  int n;
           98  +  Stmt s;
           99  +  int nStep;
          100  +  int nHidden = 0;
          101  +  bisect_path();
          102  +  db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) "
          103  +                 "  FROM blob, event"
          104  +                 " WHERE blob.rid=:rid AND event.objid=:rid"
          105  +                 "   AND event.type='ci'");
          106  +  nStep = path_length();
          107  +  if( abbreviated ){
          108  +    for(p=path_last(); p; p=p->pFrom) p->isHidden = 1;
          109  +    for(p=path_last(), n=0; p; p=p->pFrom, n++){
          110  +      if( p->rid==bisect.good
          111  +       || p->rid==bisect.bad
          112  +       || p->rid==vid
          113  +       || (nStep>1 && n==nStep/2)
          114  +      ){
          115  +        p->isHidden = 0;
          116  +        if( p->pFrom ) p->pFrom->isHidden = 0;
          117  +      }
          118  +    }
          119  +    for(p=path_last(); p; p=p->pFrom){
          120  +      if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0;
          121  +    }
          122  +  }
          123  +  for(p=path_last(), n=0; p; p=p->pFrom, n++){
          124  +    if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){
          125  +      nHidden++;
          126  +      continue;
          127  +    }else if( nHidden ){
          128  +      fossil_print("  ... %d other check-ins omitted\n", nHidden);
          129  +      nHidden = 0;
          130  +    }
          131  +    db_bind_int(&s, ":rid", p->rid);
          132  +    if( db_step(&s)==SQLITE_ROW ){
          133  +      const char *zUuid = db_column_text(&s, 0);
          134  +      const char *zDate = db_column_text(&s, 1);
          135  +      fossil_print("%s %S", zDate, zUuid);
          136  +      if( p->rid==bisect.good ) fossil_print(" GOOD");
          137  +      if( p->rid==bisect.bad ) fossil_print(" BAD");
          138  +      if( p->rid==vid ) fossil_print(" CURRENT");
          139  +      if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
          140  +      fossil_print("\n");
          141  +    }
          142  +    db_reset(&s);
          143  +  }
          144  +  db_finalize(&s);
          145  +}
          146  +
          147  +/*
          148  +** COMMAND: bisect
          149  +**
          150  +** Usage: %fossil bisect SUBCOMMAND ...
          151  +**
          152  +** Run various subcommands useful for searching for bugs.
          153  +**
          154  +**   fossil bisect bad ?VERSION?
          155  +**
          156  +**     Identify version VERSION as non-working.  If VERSION is omitted,
          157  +**     the current checkout is marked as non-working.
          158  +**
          159  +**   fossil bisect good ?VERSION?
          160  +**
          161  +**     Identify version VERSION as working.  If VERSION is omitted,
          162  +**     the current checkout is marked as working.
          163  +**
          164  +**   fossil bisect next
          165  +**
          166  +**     Update to the next version that is halfway between the working and
          167  +**     non-working versions.
          168  +**
          169  +**   fossil bisect options ?NAME? ?VALUE?
          170  +**
          171  +**     List all bisect options, or the value of a single option, or set the
          172  +**     value of a bisect option.
          173  +**
          174  +**   fossil bisect reset
          175  +**
          176  +**     Reinitialize a bisect session.  This cancels prior bisect history
          177  +**     and allows a bisect session to start over from the beginning.
          178  +**
          179  +**   fossil bisect vlist|ls ?--all?
          180  +**
          181  +**     List the versions in between "bad" and "good".
          182  +*/
          183  +void bisect_cmd(void){
          184  +  int n;
          185  +  const char *zCmd;
          186  +  db_must_be_within_tree();
          187  +  if( g.argc<3 ){
          188  +    usage("bisect SUBCOMMAND ARGS...");
          189  +  }
          190  +  zCmd = g.argv[2];
          191  +  n = strlen(zCmd);
          192  +  if( n==0 ) zCmd = "-";
          193  +  if( memcmp(zCmd, "bad", n)==0 ){
          194  +    int ridBad;
          195  +    if( g.argc==3 ){
          196  +      ridBad = db_lget_int("checkout",0);
          197  +    }else{
          198  +      ridBad = name_to_typed_rid(g.argv[3], "ci");
          199  +    }
          200  +    db_lset_int("bisect-bad", ridBad);
          201  +    if( ridBad>0
          202  +     && bisect_option("auto-next")
          203  +     && db_lget_int("bisect-good",0)>0
          204  +    ){
          205  +      zCmd = "next";
          206  +      n = 4;
          207  +    }else{
          208  +      return;
          209  +    }
          210  +  }else if( memcmp(zCmd, "good", n)==0 ){
          211  +    int ridGood;
          212  +    if( g.argc==3 ){
          213  +      ridGood = db_lget_int("checkout",0);
          214  +    }else{
          215  +      ridGood = name_to_typed_rid(g.argv[3], "ci");
          216  +    }
          217  +    db_lset_int("bisect-good", ridGood);
          218  +    if( ridGood>0
          219  +     && bisect_option("auto-next")
          220  +     && db_lget_int("bisect-bad",0)>0
          221  +    ){
          222  +      zCmd = "next";
          223  +      n = 4;
          224  +    }else{
          225  +      return;
          226  +    }
          227  +  }
          228  +  if( memcmp(zCmd, "next", n)==0 ){
          229  +    PathNode *pMid;
          230  +    bisect_path();
          231  +    pMid = path_midpoint();
          232  +    if( pMid==0 ){
          233  +      fossil_fatal("bisect is done - there are no more intermediate versions");
          234  +    }
          235  +    g.argv[1] = "update";
          236  +    g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
          237  +    g.argc = 3;
          238  +    g.fNoSync = 1;
          239  +    update_cmd();
          240  +    bisect_list(1);
          241  +  }else if( memcmp(zCmd, "options", n)==0 ){
          242  +    if( g.argc==3 ){
          243  +      unsigned int i;
          244  +      for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
          245  +        char *z = mprintf("bisect-%s", aBisectOption[i].zName);
          246  +        fossil_print("  %-15s  %-6s  ", aBisectOption[i].zName,
          247  +               db_lget(z, (char*)aBisectOption[i].zDefault));
          248  +        fossil_free(z);
          249  +        comment_print(aBisectOption[i].zDesc, 27, 79);
          250  +      }
          251  +    }else if( g.argc==4 || g.argc==5 ){
          252  +      unsigned int i;
          253  +      n = strlen(g.argv[3]);
          254  +      for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
          255  +        if( memcmp(g.argv[3], aBisectOption[i].zName, n)==0 ){
          256  +          char *z = mprintf("bisect-%s", aBisectOption[i].zName);
          257  +          if( g.argc==5 ){
          258  +            db_lset(z, g.argv[4]);
          259  +          }
          260  +          fossil_print("%s\n", db_lget(z, (char*)aBisectOption[i].zDefault));
          261  +          fossil_free(z);
          262  +          break;
          263  +        }
          264  +      }
          265  +      if( i>=sizeof(aBisectOption)/sizeof(aBisectOption[0]) ){
          266  +        fossil_fatal("no such bisect option: %s", g.argv[3]);
          267  +      }
          268  +    }else{
          269  +      usage("bisect option ?NAME? ?VALUE?");
          270  +    }
          271  +  }else if( memcmp(zCmd, "reset", n)==0 ){
          272  +    db_multi_exec(
          273  +      "DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
          274  +    );
          275  +  }else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){
          276  +    int fAll = find_option("all", 0, 0)!=0;
          277  +    bisect_list(!fAll);
          278  +  }else{
          279  +    usage("bad|good|next|reset|vlist ...");
          280  +  }
          281  +}

Changes to src/blob.c.

    58     58   ** Make sure a blob is initialized
    59     59   */
    60     60   #define blob_is_init(x) \
    61     61     assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)
    62     62   
    63     63   /*
    64     64   ** Make sure a blob does not contain malloced memory.
           65  +**
           66  +** This might fail if we are unlucky and x is uninitialized.  For that
           67  +** reason it should only be used locally for debugging.  Leave it turned
           68  +** off for production.
    65     69   */
    66     70   #if 0  /* Enable for debugging only */
    67         -#define blob_is_reset(x) \
    68         -  assert((x)->xRealloc!=blobReallocMalloc || (x)->nAlloc==0)
           71  +#define assert_blob_is_reset(x) assert(blob_is_reset(x))
    69     72   #else
    70         -#define blob_is_reset(x)
           73  +#define assert_blob_is_reset(x)
    71     74   #endif
           75  +
           76  +
    72     77   
    73     78   /*
    74     79   ** We find that the built-in isspace() function does not work for
    75     80   ** some international character sets.  So here is a substitute.
    76     81   */
    77         -static int blob_isspace(char c){
           82  +int fossil_isspace(char c){
    78     83     return c==' ' || (c<='\r' && c>='\t');
    79     84   }
           85  +
           86  +/*
           87  +** Other replacements for ctype.h functions.
           88  +*/
           89  +int fossil_islower(char c){ return c>='a' && c<='z'; }
           90  +int fossil_isupper(char c){ return c>='A' && c<='Z'; }
           91  +int fossil_isdigit(char c){ return c>='0' && c<='9'; }
           92  +int fossil_tolower(char c){
           93  +  return fossil_isupper(c) ? c - 'A' + 'a' : c;
           94  +}
           95  +int fossil_toupper(char c){
           96  +  return fossil_islower(c) ? c - 'a' + 'A' : c;
           97  +}
           98  +int fossil_isalpha(char c){
           99  +  return (c>='a' && c<='z') || (c>='A' && c<='Z');
          100  +}
          101  +int fossil_isalnum(char c){
          102  +  return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
          103  +}
          104  +
    80    105   
    81    106   /*
    82    107   ** COMMAND: test-isspace
    83    108   */
    84    109   void isspace_cmd(void){
    85    110     int i;
    86    111     for(i=0; i<=255; i++){
    87    112       if( i==' ' || i=='\n' || i=='\t' || i=='\v'
    88    113           || i=='\f' || i=='\r' ){
    89         -      assert( blob_isspace((char)i) );
          114  +      assert( fossil_isspace((char)i) );
    90    115       }else{
    91         -      assert( !blob_isspace((char)i) );
          116  +      assert( !fossil_isspace((char)i) );
    92    117       }
    93    118     }
    94         -  printf("All 256 characters OK\n");
          119  +  fossil_print("All 256 characters OK\n");
    95    120   }
    96    121   
    97    122   /*
    98    123   ** This routine is called if a blob operation fails because we
    99    124   ** have run out of memory.
   100    125   */
   101    126   static void blob_panic(void){
   102    127     static const char zErrMsg[] = "out of memory\n";
   103         -  write(2, zErrMsg, sizeof(zErrMsg)-1);
   104         -  exit(1);
          128  +  fputs(zErrMsg, stderr);
          129  +  fossil_exit(1);
   105    130   }
   106    131   
   107    132   /*
   108    133   ** A reallocation function that assumes that aData came from malloc().
   109    134   ** This function attempts to resize the buffer of the blob to hold
   110         -** newSize bytes.  
          135  +** newSize bytes.
   111    136   **
   112    137   ** No attempt is made to recover from an out-of-memory error.
   113    138   ** If an OOM error occurs, an error message is printed on stderr
   114    139   ** and the program exits.
   115    140   */
   116    141   void blobReallocMalloc(Blob *pBlob, unsigned int newSize){
   117    142     if( newSize==0 ){
   118    143       free(pBlob->aData);
   119    144       pBlob->aData = 0;
   120    145       pBlob->nAlloc = 0;
   121    146       pBlob->nUsed = 0;
   122    147       pBlob->iCursor = 0;
   123    148     }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){
   124         -    char *pNew = realloc(pBlob->aData, newSize);
   125         -    if( pNew==0 ) blob_panic();
          149  +    char *pNew = fossil_realloc(pBlob->aData, newSize);
   126    150       pBlob->aData = pNew;
   127    151       pBlob->nAlloc = newSize;
   128    152       if( pBlob->nUsed>pBlob->nAlloc ){
   129    153         pBlob->nUsed = pBlob->nAlloc;
   130    154       }
   131    155     }
   132    156   }
................................................................................
   143    167   ** A reallocation function for when the initial string is in unmanaged
   144    168   ** space.  Copy the string to memory obtained from malloc().
   145    169   */
   146    170   static void blobReallocStatic(Blob *pBlob, unsigned int newSize){
   147    171     if( newSize==0 ){
   148    172       *pBlob = empty_blob;
   149    173     }else{
   150         -    char *pNew = malloc( newSize );
   151         -    if( pNew==0 ) blob_panic();
          174  +    char *pNew = fossil_malloc( newSize );
   152    175       if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
   153    176       memcpy(pNew, pBlob->aData, pBlob->nUsed);
   154    177       pBlob->aData = pNew;
   155    178       pBlob->xRealloc = blobReallocMalloc;
   156    179       pBlob->nAlloc = newSize;
   157    180     }
   158    181   }
................................................................................
   161    184   ** Reset a blob to be an empty container.
   162    185   */
   163    186   void blob_reset(Blob *pBlob){
   164    187     blob_is_init(pBlob);
   165    188     pBlob->xRealloc(pBlob, 0);
   166    189   }
   167    190   
          191  +
          192  +/*
          193  +** Return true if the blob has been zeroed - in other words if it contains
          194  +** no malloced memory.  This only works reliably if the blob has been
          195  +** initialized - it can return a false negative on an uninitialized blob.
          196  +*/
          197  +int blob_is_reset(Blob *pBlob){
          198  +  if( pBlob==0 ) return 1;
          199  +  if( pBlob->nUsed ) return 0;
          200  +  if( pBlob->xRealloc==blobReallocMalloc && pBlob->nAlloc ) return 0;
          201  +  return 1;
          202  +}
          203  +
   168    204   /*
   169    205   ** Initialize a blob to a string or byte-array constant of a specified length.
   170    206   ** Any prior data in the blob is discarded.
   171    207   */
   172    208   void blob_init(Blob *pBlob, const char *zData, int size){
   173         -  blob_is_reset(pBlob);
          209  +  assert_blob_is_reset(pBlob);
   174    210     if( zData==0 ){
   175    211       *pBlob = empty_blob;
   176    212     }else{
   177    213       if( size<=0 ) size = strlen(zData);
   178    214       pBlob->nUsed = pBlob->nAlloc = size;
   179    215       pBlob->aData = (char*)zData;
   180    216       pBlob->iCursor = 0;
................................................................................
   191    227   }
   192    228   
   193    229   /*
   194    230   ** Initialize a blob to an empty string.
   195    231   */
   196    232   void blob_zero(Blob *pBlob){
   197    233     static const char zEmpty[] = "";
   198         -  blob_is_reset(pBlob);
          234  +  assert_blob_is_reset(pBlob);
   199    235     pBlob->nUsed = 0;
   200    236     pBlob->nAlloc = 1;
   201    237     pBlob->aData = (char*)zEmpty;
   202    238     pBlob->iCursor = 0;
   203    239     pBlob->xRealloc = blobReallocStatic;
   204    240   }
   205    241   
................................................................................
   262    298     blob_is_init(p);
   263    299     if( p->nUsed==0 ) return "";
   264    300     p->aData[p->nUsed] = 0;
   265    301     return p->aData;
   266    302   }
   267    303   
   268    304   /*
   269         -** Compare two blobs.
          305  +** Compare two blobs.  Return negative, zero, or positive if the first
          306  +** blob is less then, equal to, or greater than the second.
   270    307   */
   271    308   int blob_compare(Blob *pA, Blob *pB){
   272    309     int szA, szB, sz, rc;
   273    310     blob_is_init(pA);
   274    311     blob_is_init(pB);
   275    312     szA = blob_size(pA);
   276    313     szB = blob_size(pB);
   277    314     sz = szA<szB ? szA : szB;
   278    315     rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz);
   279    316     if( rc==0 ){
   280    317       rc = szA - szB;
   281    318     }
          319  +  return rc;
          320  +}
          321  +
          322  +/*
          323  +** Compare two blobs in constant time and return zero if they are equal.
          324  +** Constant time comparison only applies for blobs of the same length.
          325  +** If lengths are different, immediately returns 1.
          326  +*/
          327  +int blob_constant_time_cmp(Blob *pA, Blob *pB){
          328  +  int szA, szB, i;
          329  +  unsigned char *buf1, *buf2;
          330  +  unsigned char rc = 0;
          331  +
          332  +  blob_is_init(pA);
          333  +  blob_is_init(pB);
          334  +  szA = blob_size(pA);
          335  +  szB = blob_size(pB);
          336  +  if( szA!=szB || szA==0 ) return 1;
          337  +
          338  +  buf1 = (unsigned char*)blob_buffer(pA);
          339  +  buf2 = (unsigned char*)blob_buffer(pB);
          340  +
          341  +  for( i=0; i<szA; i++ ){
          342  +    rc = rc | (buf1[i] ^ buf2[i]);
          343  +  }
          344  +
   282    345     return rc;
   283    346   }
   284    347   
   285    348   /*
   286    349   ** Compare a blob to a string.  Return TRUE if they are equal.
   287    350   */
   288    351   int blob_eq_str(Blob *pBlob, const char *z, int n){
................................................................................
   303    366   #if INTERFACE
   304    367   # define blob_eq(B,S) \
   305    368        ((B)->nUsed==sizeof(S)-1 && memcmp((B)->aData,S,sizeof(S)-1)==0)
   306    369   #endif
   307    370   
   308    371   
   309    372   /*
   310         -** Attempt to resize a blob so that its internal buffer is 
          373  +** Attempt to resize a blob so that its internal buffer is
   311    374   ** nByte in size.  The blob is truncated if necessary.
   312    375   */
   313    376   void blob_resize(Blob *pBlob, unsigned int newSize){
   314    377     pBlob->xRealloc(pBlob, newSize+1);
   315    378     pBlob->nUsed = newSize;
   316    379     pBlob->aData[newSize] = 0;
   317    380   }
................................................................................
   339    402   ** Extract N bytes from blob pFrom and use it to initialize blob pTo.
   340    403   ** Return the actual number of bytes extracted.
   341    404   **
   342    405   ** After this call completes, pTo will be an ephemeral blob.
   343    406   */
   344    407   int blob_extract(Blob *pFrom, int N, Blob *pTo){
   345    408     blob_is_init(pFrom);
   346         -  blob_is_reset(pTo);
          409  +  assert_blob_is_reset(pTo);
   347    410     if( pFrom->iCursor + N > pFrom->nUsed ){
   348    411       N = pFrom->nUsed - pFrom->iCursor;
   349    412       if( N<=0 ){
   350    413         blob_zero(pTo);
   351    414         return 0;
   352    415       }
   353    416     }
................................................................................
   374    437     if( whence==BLOB_SEEK_SET ){
   375    438       p->iCursor = offset;
   376    439     }else if( whence==BLOB_SEEK_CUR ){
   377    440       p->iCursor += offset;
   378    441     }else if( whence==BLOB_SEEK_END ){
   379    442       p->iCursor = p->nUsed + offset - 1;
   380    443     }
   381         -  if( p->iCursor<0 ){
   382         -    p->iCursor = 0;
   383         -  }
   384    444     if( p->iCursor>p->nUsed ){
   385    445       p->iCursor = p->nUsed;
   386    446     }
   387    447     return p->iCursor;
   388    448   }
   389    449   
   390    450   /*
................................................................................
   391    451   ** Return the current offset into the blob
   392    452   */
   393    453   int blob_tell(Blob *p){
   394    454     return p->iCursor;
   395    455   }
   396    456   
   397    457   /*
   398         -** Extract a single line of text from pFrom beginning at the current 
          458  +** Extract a single line of text from pFrom beginning at the current
   399    459   ** cursor location and use that line of text to initialize pTo.
   400    460   ** pTo will include the terminating \n.  Return the number of bytes
   401    461   ** in the line including the \n at the end.  0 is returned at
   402    462   ** end-of-file.
   403    463   **
   404    464   ** The cursor of pFrom is left pointing at the first byte past the
   405    465   ** \n that terminated the line.
................................................................................
   427    487   **
   428    488   ** All this does is reduce the length counter.  This routine does
   429    489   ** not insert a new zero terminator.
   430    490   */
   431    491   int blob_trim(Blob *p){
   432    492     char *z = p->aData;
   433    493     int n = p->nUsed;
   434         -  while( n>0 && blob_isspace(z[n-1]) ){ n--; }
          494  +  while( n>0 && fossil_isspace(z[n-1]) ){ n--; }
   435    495     p->nUsed = n;
   436    496     return n;
   437    497   }
   438    498   
   439    499   /*
   440    500   ** Extract a single token from pFrom and use it to initialize pTo.
   441    501   ** Return the number of bytes in the token.  If no token is found,
................................................................................
   450    510   ** pTo will be an ephermeral blob.  If pFrom changes, it might alter
   451    511   ** pTo as well.
   452    512   */
   453    513   int blob_token(Blob *pFrom, Blob *pTo){
   454    514     char *aData = pFrom->aData;
   455    515     int n = pFrom->nUsed;
   456    516     int i = pFrom->iCursor;
   457         -  while( i<n && blob_isspace(aData[i]) ){ i++; }
          517  +  while( i<n && fossil_isspace(aData[i]) ){ i++; }
          518  +  pFrom->iCursor = i;
          519  +  while( i<n && !fossil_isspace(aData[i]) ){ i++; }
          520  +  blob_extract(pFrom, i-pFrom->iCursor, pTo);
          521  +  while( i<n && fossil_isspace(aData[i]) ){ i++; }
          522  +  pFrom->iCursor = i;
          523  +  return pTo->nUsed;
          524  +}
          525  +
          526  +/*
          527  +** Extract a single SQL token from pFrom and use it to initialize pTo.
          528  +** Return the number of bytes in the token.  If no token is found,
          529  +** return 0.
          530  +**
          531  +** An SQL token consists of one or more non-space characters.  If the
          532  +** first character is ' then the token is terminated by a matching '
          533  +** (ignoring double '') or by the end of the string
          534  +**
          535  +** The cursor of pFrom is left pointing at the first character past
          536  +** the end of the token.
          537  +**
          538  +** pTo will be an ephermeral blob.  If pFrom changes, it might alter
          539  +** pTo as well.
          540  +*/
          541  +int blob_sqltoken(Blob *pFrom, Blob *pTo){
          542  +  char *aData = pFrom->aData;
          543  +  int n = pFrom->nUsed;
          544  +  int i = pFrom->iCursor;
          545  +  while( i<n && fossil_isspace(aData[i]) ){ i++; }
   458    546     pFrom->iCursor = i;
   459         -  while( i<n && !blob_isspace(aData[i]) ){ i++; }
          547  +  if( aData[i]=='\'' ){
          548  +    i++;
          549  +    while( i<n ){
          550  +      if( aData[i]=='\'' ){
          551  +        if( aData[++i]!='\'' ) break;
          552  +      }
          553  +      i++;
          554  +    }
          555  +  }else{
          556  +    while( i<n && !fossil_isspace(aData[i]) ){ i++; }
          557  +  }
   460    558     blob_extract(pFrom, i-pFrom->iCursor, pTo);
   461         -  while( i<n && blob_isspace(aData[i]) ){ i++; }
          559  +  while( i<n && fossil_isspace(aData[i]) ){ i++; }
   462    560     pFrom->iCursor = i;
   463    561     return pTo->nUsed;
   464    562   }
   465    563   
   466    564   /*
   467    565   ** Extract everything from the current cursor to the end of the blob
   468    566   ** into a new blob.  The new blob is an ephemerial reference to the
................................................................................
   521    619   ** the integer value in *pValue.
   522    620   */
   523    621   int blob_is_int(Blob *pBlob, int *pValue){
   524    622     const char *z = blob_buffer(pBlob);
   525    623     int i, n, c, v;
   526    624     n = blob_size(pBlob);
   527    625     v = 0;
   528         -  for(i=0; i<n && (c = z[i])!=0 && isdigit(c); i++){
          626  +  for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
   529    627       v = v*10 + c - '0';
   530    628     }
   531    629     if( i==n ){
   532    630       *pValue = v;
   533    631       return 1;
   534    632     }else{
   535    633       return 0;
................................................................................
   559    657     return i;
   560    658   }
   561    659   
   562    660   /*
   563    661   ** Do printf-style string rendering and append the results to a blob.
   564    662   */
   565    663   void blob_appendf(Blob *pBlob, const char *zFormat, ...){
   566         -  va_list ap;
   567         -  va_start(ap, zFormat);
   568         -  vxprintf(pBlob, zFormat, ap);
   569         -  va_end(ap);
          664  +  if( pBlob ){
          665  +    va_list ap;
          666  +    va_start(ap, zFormat);
          667  +    vxprintf(pBlob, zFormat, ap);
          668  +    va_end(ap);
          669  +  }
   570    670   }
   571    671   void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
   572         -  vxprintf(pBlob, zFormat, ap);
          672  +  if( pBlob ) vxprintf(pBlob, zFormat, ap);
   573    673   }
   574    674   
   575    675   /*
   576         -** Initalize a blob to the data on an input channel.  Return 
          676  +** Initialize a blob to the data on an input channel.  Return
   577    677   ** the number of bytes read into the blob.  Any prior content
   578    678   ** of the blob is discarded, not freed.
   579    679   */
   580    680   int blob_read_from_channel(Blob *pBlob, FILE *in, int nToRead){
   581    681     size_t n;
   582    682     blob_zero(pBlob);
   583    683     if( nToRead<0 ){
................................................................................
   598    698   
   599    699   /*
   600    700   ** Initialize a blob to be the content of a file.  If the filename
   601    701   ** is blank or "-" then read from standard input.
   602    702   **
   603    703   ** Any prior content of the blob is discarded, not freed.
   604    704   **
   605         -** Return the number of bytes read.  Return -1 for an error.
          705  +** Return the number of bytes read. Calls fossil_panic() error (i.e.
          706  +** it exit()s and does not return).
   606    707   */
   607    708   int blob_read_from_file(Blob *pBlob, const char *zFilename){
   608    709     int size, got;
   609    710     FILE *in;
   610    711     if( zFilename==0 || zFilename[0]==0
   611    712           || (zFilename[0]=='-' && zFilename[1]==0) ){
   612    713       return blob_read_from_channel(pBlob, stdin, -1);
   613    714     }
   614         -  size = file_size(zFilename);
          715  +  size = file_wd_size(zFilename);
   615    716     blob_zero(pBlob);
   616    717     if( size<0 ){
   617         -    fossil_panic("no such file: %s", zFilename);
          718  +    fossil_fatal("no such file: %s", zFilename);
   618    719     }
   619    720     if( size==0 ){
   620    721       return 0;
   621    722     }
   622    723     blob_resize(pBlob, size);
   623         -  in = fopen(zFilename, "rb");
          724  +  in = fossil_fopen(zFilename, "rb");
   624    725     if( in==0 ){
   625    726       fossil_panic("cannot open %s for reading", zFilename);
   626    727     }
   627    728     got = fread(blob_buffer(pBlob), 1, size, in);
   628    729     fclose(in);
   629    730     if( got<size ){
   630    731       blob_resize(pBlob, got);
   631    732     }
   632    733     return got;
   633    734   }
          735  +
          736  +/*
          737  +** Reads symlink destination path and puts int into blob.
          738  +** Any prior content of the blob is discarded, not freed.
          739  +**
          740  +** Returns length of destination path.
          741  +**
          742  +** On windows, zeros blob and returns 0.
          743  +*/
          744  +int blob_read_link(Blob *pBlob, const char *zFilename){
          745  +#if !defined(_WIN32)
          746  +  char zBuf[1024];
          747  +  ssize_t len = readlink(zFilename, zBuf, 1023);
          748  +  if( len < 0 ){
          749  +    fossil_panic("cannot read symbolic link %s", zFilename);
          750  +  }
          751  +  zBuf[len] = 0;   /* null-terminate */
          752  +  blob_zero(pBlob);
          753  +  blob_appendf(pBlob, "%s", zBuf);
          754  +  return len;
          755  +#else
          756  +  blob_zero(pBlob);
          757  +  return 0;
          758  +#endif
          759  +}
          760  +
   634    761   
   635    762   /*
   636    763   ** Write the content of a blob into a file.
   637    764   **
   638    765   ** If the filename is blank or "-" then write to standard output.
   639    766   **
   640    767   ** Return the number of bytes written.
   641    768   */
   642    769   int blob_write_to_file(Blob *pBlob, const char *zFilename){
   643    770     FILE *out;
   644         -  int needToClose;
   645    771     int wrote;
   646    772   
   647    773     if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
   648         -    out = stdout;
   649         -    needToClose = 0;
          774  +    int n = blob_size(pBlob);
          775  +#if defined(_WIN32)
          776  +    if( fossil_utf8_to_console(blob_buffer(pBlob), n, 0) >= 0 ){
          777  +      return n;
          778  +    }
          779  +#endif
          780  +    fwrite(blob_buffer(pBlob), 1, n, stdout);
          781  +    return n;
   650    782     }else{
   651    783       int i, nName;
   652    784       char *zName, zBuf[1000];
   653    785   
   654    786       nName = strlen(zFilename);
   655    787       if( nName>=sizeof(zBuf) ){
   656    788         zName = mprintf("%s", zFilename);
   657    789       }else{
   658    790         zName = zBuf;
   659         -      strcpy(zName, zFilename);
          791  +      memcpy(zName, zFilename, nName+1);
   660    792       }
   661         -    nName = file_simplify_name(zName, nName);
          793  +    nName = file_simplify_name(zName, nName, 0);
   662    794       for(i=1; i<nName; i++){
   663    795         if( zName[i]=='/' ){
   664    796           zName[i] = 0;
   665         -#ifdef __MINGW32__
          797  +#if defined(_WIN32)
   666    798           /*
   667    799           ** On Windows, local path looks like: C:/develop/project/file.txt
   668    800           ** The if stops us from trying to create a directory of a drive letter
   669    801           ** C: in this example.
   670    802           */
   671    803           if( !(i==2 && zName[1]==':') ){
   672    804   #endif
   673         -          if( file_mkdir(zName, 1) ){
          805  +          if( file_mkdir(zName, 1) && file_isdir(zName)!=1 ){
   674    806               fossil_fatal_recursive("unable to create directory %s", zName);
   675    807               return 0;
   676    808             }
   677         -#ifdef __MINGW32__
          809  +#if defined(_WIN32)
   678    810           }
   679    811   #endif
   680    812           zName[i] = '/';
   681    813         }
   682    814       }
   683         -    out = fopen(zName, "wb");
          815  +    out = fossil_fopen(zName, "wb");
   684    816       if( out==0 ){
   685    817         fossil_fatal_recursive("unable to open file \"%s\" for writing", zName);
   686    818         return 0;
   687    819       }
   688         -    needToClose = 1;
   689    820       if( zName!=zBuf ) free(zName);
   690    821     }
   691    822     blob_is_init(pBlob);
   692    823     wrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
   693         -  if( needToClose ) fclose(out);
   694         -  if( wrote!=blob_size(pBlob) ){
          824  +  fclose(out);
          825  +  if( wrote!=blob_size(pBlob) && out!=stdout ){
   695    826       fossil_fatal_recursive("short write: %d of %d bytes to %s", wrote,
   696    827          blob_size(pBlob), zFilename);
   697    828     }
   698    829     return wrote;
   699    830   }
   700    831   
   701    832   /*
   702    833   ** Compress a blob pIn.  Store the result in pOut.  It is ok for pIn and
   703         -** pOut to be the same blob. 
   704         -** 
          834  +** pOut to be the same blob.
          835  +**
   705    836   ** pOut must either be the same as pIn or else uninitialized.
   706    837   */
   707    838   void blob_compress(Blob *pIn, Blob *pOut){
   708    839     unsigned int nIn = blob_size(pIn);
   709    840     unsigned int nOut = 13 + nIn + (nIn+999)/1000;
   710    841     unsigned long int nOut2;
   711    842     unsigned char *outBuf;
................................................................................
   717    848     outBuf[1] = nIn>>16 & 0xff;
   718    849     outBuf[2] = nIn>>8 & 0xff;
   719    850     outBuf[3] = nIn & 0xff;
   720    851     nOut2 = (long int)nOut;
   721    852     compress(&outBuf[4], &nOut2,
   722    853              (unsigned char*)blob_buffer(pIn), blob_size(pIn));
   723    854     if( pOut==pIn ) blob_reset(pOut);
   724         -  blob_is_reset(pOut);
          855  +  assert_blob_is_reset(pOut);
   725    856     *pOut = temp;
   726    857     blob_resize(pOut, nOut2+4);
   727    858   }
   728    859   
   729    860   /*
   730    861   ** COMMAND: test-compress
   731    862   */
................................................................................
   734    865     if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
   735    866     blob_read_from_file(&f, g.argv[2]);
   736    867     blob_compress(&f, &f);
   737    868     blob_write_to_file(&f, g.argv[3]);
   738    869   }
   739    870   
   740    871   /*
   741         -** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result 
   742         -** in pOut. 
   743         -** 
          872  +** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result
          873  +** in pOut.
          874  +**
   744    875   ** pOut must be either uninitialized or must be the same as either pIn1 or
   745    876   ** pIn2.
   746    877   */
   747    878   void blob_compress2(Blob *pIn1, Blob *pIn2, Blob *pOut){
   748    879     unsigned int nIn = blob_size(pIn1) + blob_size(pIn2);
   749    880     unsigned int nOut = 13 + nIn + (nIn+999)/1000;
   750    881     unsigned char *outBuf;
................................................................................
   770    901     stream.next_in = (unsigned char*)blob_buffer(pIn2);
   771    902     deflate(&stream, 0);
   772    903     deflate(&stream, Z_FINISH);
   773    904     blob_resize(&temp, stream.total_out + 4);
   774    905     deflateEnd(&stream);
   775    906     if( pOut==pIn1 ) blob_reset(pOut);
   776    907     if( pOut==pIn2 ) blob_reset(pOut);
   777         -  blob_is_reset(pOut);
          908  +  assert_blob_is_reset(pOut);
   778    909     *pOut = temp;
   779    910   }
   780    911   
   781    912   /*
   782    913   ** COMMAND: test-compress-2
   783    914   */
   784    915   void compress2_cmd(void){
................................................................................
   807    938       return 0;
   808    939     }
   809    940     inBuf = (unsigned char*)blob_buffer(pIn);
   810    941     nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3];
   811    942     blob_zero(&temp);
   812    943     blob_resize(&temp, nOut+1);
   813    944     nOut2 = (long int)nOut;
   814         -  rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2, 
   815         -                  &inBuf[4], blob_size(pIn));
          945  +  rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2,
          946  +                  &inBuf[4], nIn - 4);
   816    947     if( rc!=Z_OK ){
   817    948       blob_reset(&temp);
   818    949       return 1;
   819    950     }
   820    951     blob_resize(&temp, nOut2);
   821    952     if( pOut==pIn ) blob_reset(pOut);
   822         -  blob_is_reset(pOut);
          953  +  assert_blob_is_reset(pOut);
   823    954     *pOut = temp;
   824    955     return 0;
   825    956   }
   826    957   
   827    958   /*
   828    959   ** COMMAND: test-uncompress
   829    960   */
................................................................................
   851    982       if( blob_compare(&b1, &b3) ){
   852    983         fossil_panic("compress/uncompress cycle failed for %s", g.argv[i]);
   853    984       }
   854    985       blob_reset(&b1);
   855    986       blob_reset(&b2);
   856    987       blob_reset(&b3);
   857    988     }
   858         -  printf("ok\n");
          989  +  fossil_print("ok\n");
   859    990   }
   860    991   
   861         -#ifdef __MINGW32__
          992  +#if defined(_WIN32)
   862    993   /*
   863    994   ** Convert every \n character in the given blob into \r\n.
   864    995   */
   865    996   void blob_add_cr(Blob *p){
   866    997     char *z = p->aData;
   867    998     int j   = p->nUsed;
   868    999     int i, n;
................................................................................
   894   1025     z = p->aData;
   895   1026     for(i=j=0; z[i]; i++){
   896   1027       if( z[i]!='\r' ) z[j++] = z[i];
   897   1028     }
   898   1029     z[j] = 0;
   899   1030     p->nUsed = j;
   900   1031   }
         1032  +
         1033  +/*
         1034  +** Shell-escape the given string.  Append the result to a blob.
         1035  +*/
         1036  +void shell_escape(Blob *pBlob, const char *zIn){
         1037  +  int n = blob_size(pBlob);
         1038  +  int k = strlen(zIn);
         1039  +  int i, c;
         1040  +  char *z;
         1041  +  for(i=0; (c = zIn[i])!=0; i++){
         1042  +    if( fossil_isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
         1043  +      blob_appendf(pBlob, "\"%s\"", zIn);
         1044  +      z = blob_buffer(pBlob);
         1045  +      for(i=n+1; i<=n+k; i++){
         1046  +        if( z[i]=='"' ) z[i] = '_';
         1047  +      }
         1048  +      return;
         1049  +    }
         1050  +  }
         1051  +  blob_append(pBlob, zIn, -1);
         1052  +}
         1053  +
         1054  +/*
         1055  +** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
         1056  +** bytes from pIn, starting at position pIn->iCursor, and copies them
         1057  +** to pDest (which must be valid memory at least nLen bytes long).
         1058  +**
         1059  +** Returns the number of bytes read/copied, which may be less than
         1060  +** nLen (if end-of-blob is encountered).
         1061  +**
         1062  +** Updates pIn's cursor.
         1063  +**
         1064  +** Returns 0 if pIn contains no data.
         1065  +*/
         1066  +unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){
         1067  +  if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){
         1068  +    return 0;
         1069  +  } else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){
         1070  +    nLen = (unsigned int) (pIn->nUsed - pIn->iCursor);
         1071  +  }
         1072  +  assert( pIn->nUsed > pIn->iCursor );
         1073  +  assert( (pIn->iCursor+nLen)  <= pIn->nUsed );
         1074  +  if( nLen ){
         1075  +    memcpy( pDest, pIn->aData, nLen );
         1076  +    pIn->iCursor += nLen;
         1077  +  }
         1078  +  return nLen;
         1079  +}
         1080  +
         1081  +/*
         1082  +** Swaps the contents of the given blobs. Results
         1083  +** are unspecified if either value is NULL or both
         1084  +** point to the same blob.
         1085  +*/
         1086  +void blob_swap( Blob *pLeft, Blob *pRight ){
         1087  +  Blob swap = *pLeft;
         1088  +  *pLeft = *pRight;
         1089  +  *pRight = swap;
         1090  +}
         1091  +
         1092  +/*
         1093  +** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there
         1094  +** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is
         1095  +** done.  If useMbcs is false and there is no BOM, the input string is assumed
         1096  +** to be UTF-8 already, so no conversion is done.
         1097  +*/
         1098  +void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
         1099  +  char *zUtf8;
         1100  +  int bomSize = 0;
         1101  +  int bomReverse = 0;
         1102  +  if( starts_with_utf8_bom(pBlob, &bomSize) ){
         1103  +    struct Blob temp;
         1104  +    zUtf8 = blob_str(pBlob) + bomSize;
         1105  +    blob_zero(&temp);
         1106  +    blob_append(&temp, zUtf8, -1);
         1107  +    blob_swap(pBlob, &temp);
         1108  +    blob_reset(&temp);
         1109  +#ifdef _WIN32
         1110  +  }else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
         1111  +    zUtf8 = blob_buffer(pBlob);
         1112  +    if( bomReverse ){
         1113  +      /* Found BOM, but with reversed bytes */
         1114  +      unsigned int i = blob_size(pBlob);
         1115  +      while( i>0 ){
         1116  +        /* swap bytes of unicode representation */
         1117  +        char zTemp = zUtf8[--i];
         1118  +        zUtf8[i] = zUtf8[i-1];
         1119  +        zUtf8[--i] = zTemp;
         1120  +      }
         1121  +    }
         1122  +    /* Make sure the blob contains two terminating 0-bytes */
         1123  +    blob_append(pBlob, "", 1);
         1124  +    zUtf8 = blob_str(pBlob) + bomSize;
         1125  +    zUtf8 = fossil_unicode_to_utf8(zUtf8);
         1126  +    blob_zero(pBlob);
         1127  +    blob_append(pBlob, zUtf8, -1);
         1128  +    fossil_unicode_free(zUtf8);
         1129  +  }else if( useMbcs ){
         1130  +    zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
         1131  +    blob_reset(pBlob);
         1132  +    blob_append(pBlob, zUtf8, -1);
         1133  +    fossil_mbcs_free(zUtf8);
         1134  +#endif /* _WIN32 */
         1135  +  }
         1136  +}

Changes to src/branch.c.

    18     18   ** This file contains code used to create new branches within a repository.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "branch.h"
    22     22   #include <assert.h>
    23     23   
    24     24   /*
    25         -**  fossil branch new    BRANCH-NAME ?ORIGIN-CHECK-IN? ?-bgcolor COLOR?
    26         -**  argv0  argv1  argv2  argv3       argv4
           25  +**  fossil branch new    NAME BASIS ?OPTIONS?
           26  +**  argv0  argv1  argv2  argv3 argv4
    27     27   */
    28     28   void branch_new(void){
    29     29     int rootid;            /* RID of the root check-in - what we branch off of */
    30     30     int brid;              /* RID of the branch check-in */
    31     31     int noSign;            /* True if the branch is unsigned */
    32     32     int i;                 /* Loop counter */
    33     33     char *zUuid;           /* Artifact ID of origin */
    34     34     Stmt q;                /* Generic query */
    35     35     const char *zBranch;   /* Name of the new branch */
    36     36     char *zDate;           /* Date that branch was created */
    37     37     char *zComment;        /* Check-in comment for the new branch */
    38     38     const char *zColor;    /* Color of the new branch */
    39     39     Blob branch;           /* manifest for the new branch */
    40         -  Blob parent;           /* root check-in manifest */
    41         -  Manifest mParent;      /* Parsed parent manifest */
           40  +  Manifest *pParent;     /* Parsed parent manifest */
    42     41     Blob mcksum;           /* Self-checksum on the manifest */
    43         - 
           42  +  const char *zDateOvrd; /* Override date string */
           43  +  const char *zUserOvrd; /* Override user name */
           44  +  int isPrivate = 0;     /* True if the branch should be private */
           45  +
    44     46     noSign = find_option("nosign","",0)!=0;
    45     47     zColor = find_option("bgcolor","c",1);
           48  +  isPrivate = find_option("private",0,0)!=0;
           49  +  zDateOvrd = find_option("date-override",0,1);
           50  +  zUserOvrd = find_option("user-override",0,1);
    46     51     verify_all_options();
    47     52     if( g.argc<5 ){
    48         -    usage("new BRANCH-NAME CHECK-IN ?-bgcolor COLOR?");
           53  +    usage("new BRANCH-NAME BASIS ?OPTIONS?");
    49     54     }
    50         -  db_find_and_open_repository(1);  
           55  +  db_find_and_open_repository(0, 0);
    51     56     noSign = db_get_int("omitsign", 0)|noSign;
    52         -  
           57  +
    53     58     /* fossil branch new name */
    54     59     zBranch = g.argv[3];
    55     60     if( zBranch==0 || zBranch[0]==0 ){
    56     61       fossil_panic("branch name cannot be empty");
    57     62     }
    58     63     if( db_exists(
    59     64           "SELECT 1 FROM tagxref"
    60     65           " WHERE tagtype>0"
    61         -        "   AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')",
           66  +        "   AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%q')",
    62     67           zBranch)!=0 ){
    63     68       fossil_fatal("branch \"%s\" already exists", zBranch);
    64     69     }
    65     70   
    66     71     user_select();
    67     72     db_begin_transaction();
    68         -  rootid = name_to_rid(g.argv[4]);
           73  +  rootid = name_to_typed_rid(g.argv[4], "ci");
    69     74     if( rootid==0 ){
    70     75       fossil_fatal("unable to locate check-in off of which to branch");
    71     76     }
           77  +
           78  +  pParent = manifest_get(rootid, CFTYPE_MANIFEST);
           79  +  if( pParent==0 ){
           80  +    fossil_fatal("%s is not a valid check-in", g.argv[4]);
           81  +  }
    72     82   
    73     83     /* Create a manifest for the new branch */
    74     84     blob_zero(&branch);
           85  +  if( pParent->zBaseline ){
           86  +    blob_appendf(&branch, "B %s\n", pParent->zBaseline);
           87  +  }
    75     88     zComment = mprintf("Create new branch named \"%h\"", zBranch);
    76     89     blob_appendf(&branch, "C %F\n", zComment);
    77         -  zDate = db_text(0, "SELECT datetime('now')");
    78         -  zDate[10] = 'T';
           90  +  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
    79     91     blob_appendf(&branch, "D %s\n", zDate);
    80     92   
    81     93     /* Copy all of the content from the parent into the branch */
    82         -  content_get(rootid, &parent);
    83         -  manifest_parse(&mParent, &parent);
    84         -  if( mParent.type!=CFTYPE_MANIFEST ){
    85         -    fossil_fatal("%s is not a valid check-in", g.argv[4]);
    86         -  }
    87         -  for(i=0; i<mParent.nFile; ++i){
    88         -    if( mParent.aFile[i].zPerm[0] ){
    89         -      blob_appendf(&branch, "F %F %s %s\n",
    90         -                   mParent.aFile[i].zName,
    91         -                   mParent.aFile[i].zUuid,
    92         -                   mParent.aFile[i].zPerm);
    93         -    }else{
    94         -      blob_appendf(&branch, "F %F %s\n",
    95         -                   mParent.aFile[i].zName,
    96         -                   mParent.aFile[i].zUuid);
           94  +  for(i=0; i<pParent->nFile; ++i){
           95  +    blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
           96  +    if( pParent->aFile[i].zUuid ){
           97  +      blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
           98  +      if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){
           99  +        blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
          100  +      }
    97    101       }
          102  +    blob_append(&branch, "\n", 1);
    98    103     }
    99    104     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
   100    105     blob_appendf(&branch, "P %s\n", zUuid);
   101         -  blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
   102         -  manifest_clear(&mParent);
          106  +  if( pParent->zRepoCksum ){
          107  +    blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
          108  +  }
          109  +  manifest_destroy(pParent);
   103    110   
   104    111     /* Add the symbolic branch name and the "branch" tag to identify
   105    112     ** this as a new branch */
          113  +  if( content_is_private(rootid) ) isPrivate = 1;
          114  +  if( isPrivate && zColor==0 ) zColor = "#fec084";
   106    115     if( zColor!=0 ){
   107    116       blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
   108    117     }
   109    118     blob_appendf(&branch, "T *branch * %F\n", zBranch);
   110    119     blob_appendf(&branch, "T *sym-%F *\n", zBranch);
          120  +  if( isPrivate ){
          121  +    blob_appendf(&branch, "T +private *\n");
          122  +    noSign = 1;
          123  +  }
   111    124   
   112    125     /* Cancel all other symbolic tags */
   113    126     db_prepare(&q,
   114    127         "SELECT tagname FROM tagxref, tag"
   115    128         " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
   116    129         "   AND tagtype>0 AND tagname GLOB 'sym-*'"
   117    130         " ORDER BY tagname",
   118    131         rootid);
   119    132     while( db_step(&q)==SQLITE_ROW ){
   120    133       const char *zTag = db_column_text(&q, 0);
   121    134       blob_appendf(&branch, "T -%F *\n", zTag);
   122    135     }
   123    136     db_finalize(&q);
   124         -  
   125         -  blob_appendf(&branch, "U %F\n", g.zLogin);
          137  +
          138  +  blob_appendf(&branch, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
   126    139     md5sum_blob(&branch, &mcksum);
   127    140     blob_appendf(&branch, "Z %b\n", &mcksum);
   128    141     if( !noSign && clearsign(&branch, &branch) ){
   129    142       Blob ans;
          143  +    char cReply;
   130    144       blob_zero(&ans);
   131    145       prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
   132         -    if( blob_str(&ans)[0]!='y' ){
          146  +    cReply = blob_str(&ans)[0];
          147  +    if( cReply!='y' && cReply!='Y'){
   133    148         db_end_transaction(1);
   134         -      exit(1);
          149  +      fossil_exit(1);
   135    150       }
   136    151     }
   137    152   
   138         -  brid = content_put(&branch, 0, 0);
          153  +  brid = content_put_ex(&branch, 0, 0, 0, isPrivate);
   139    154     if( brid==0 ){
   140    155       fossil_panic("trouble committing manifest: %s", g.zErrMsg);
   141    156     }
   142    157     db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
   143    158     if( manifest_crosslink(brid, &branch)==0 ){
   144    159       fossil_panic("unable to install new manifest");
   145    160     }
          161  +  assert( blob_is_reset(&branch) );
   146    162     content_deltify(rootid, brid, 0);
   147    163     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
   148         -  printf("New branch: %s\n", zUuid);
          164  +  fossil_print("New branch: %s\n", zUuid);
   149    165     if( g.argc==3 ){
   150         -    printf(
          166  +    fossil_print(
   151    167         "\n"
   152    168         "Note: the local check-out has not been updated to the new\n"
   153    169         "      branch.  To begin working on the new branch, do this:\n"
   154    170         "\n"
   155    171         "      %s update %s\n",
   156    172         g.argv[0], zBranch
   157    173       );
   158    174     }
   159    175   
   160    176   
   161    177     /* Commit */
   162    178     db_end_transaction(0);
   163         -  
          179  +
   164    180     /* Do an autosync push, if requested */
   165         -  autosync(AUTOSYNC_PUSH);
          181  +  if( !isPrivate ) autosync(SYNC_PUSH);
          182  +}
          183  +
          184  +/*
          185  +** Prepare a query that will list branches.
          186  +**
          187  +** If (which<0) then the query pulls only closed branches. If
          188  +** (which>0) then the query pulls all (closed and opened)
          189  +** branches. Else the query pulls currently-opened branches.
          190  +*/
          191  +void branch_prepare_list_query(Stmt *pQuery, int which ){
          192  +  if( which < 0 ){
          193  +    db_prepare(pQuery,
          194  +      "SELECT value FROM tagxref"
          195  +      " WHERE tagid=%d AND value NOT NULL "
          196  +      "EXCEPT "
          197  +      "SELECT value FROM tagxref"
          198  +      " WHERE tagid=%d"
          199  +      "   AND rid IN leaf"
          200  +      "   AND NOT %z"
          201  +      " ORDER BY value COLLATE nocase /*sort*/",
          202  +      TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
          203  +    );
          204  +  }else if( which>0 ){
          205  +    db_prepare(pQuery,
          206  +      "SELECT DISTINCT value FROM tagxref"
          207  +      " WHERE tagid=%d AND value NOT NULL"
          208  +      "   AND rid IN leaf"
          209  +      " ORDER BY value COLLATE nocase /*sort*/",
          210  +      TAG_BRANCH
          211  +    );
          212  +  }else{
          213  +    db_prepare(pQuery,
          214  +      "SELECT DISTINCT value FROM tagxref"
          215  +      " WHERE tagid=%d AND value NOT NULL"
          216  +      "   AND rid IN leaf"
          217  +      "   AND NOT %z"
          218  +      " ORDER BY value COLLATE nocase /*sort*/",
          219  +      TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
          220  +    );
          221  +  }
   166    222   }
          223  +
   167    224   
   168    225   /*
   169    226   ** COMMAND: branch
   170    227   **
   171         -** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE?
          228  +** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
   172    229   **
   173    230   ** Run various subcommands to manage branches of the open repository or
   174    231   ** of the repository identified by the -R or --repository option.
   175    232   **
   176         -**    %fossil branch new BRANCH-NAME BASIS ?-bgcolor COLOR? 
          233  +**    %fossil branch new BRANCH-NAME BASIS ?OPTIONS?
   177    234   **
   178    235   **        Create a new branch BRANCH-NAME off of check-in BASIS.
   179         -**        You can optionally give the branch a default color.
          236  +**        Supported options for this subcommand include:
          237  +**        --private             branch is private (i.e., remains local)
          238  +**        --bgcolor COLOR       use COLOR instead of automatic background
          239  +**        --nosign              do not sign contents on this branch
          240  +**        --date-override DATE  DATE to use instead of 'now'
          241  +**        --user-override USER  USER to use instead of the current default
          242  +**
          243  +**    %fossil branch list ?--all | --closed?
          244  +**    %fossil branch ls ?--all | --closed?
   180    245   **
   181         -**    %fossil branch list
          246  +**        List all branches.  Use --all or --closed to list all branches
          247  +**        or closed branches.  The default is to show only open branches.
   182    248   **
   183         -**        List all branches
   184         -**
          249  +** Options:
          250  +**    -R|--repository FILE       Run commands on repository FILE
   185    251   */
   186    252   void branch_cmd(void){
   187    253     int n;
   188         -  db_find_and_open_repository(1);
   189         -  if( g.argc<3 ){
   190         -    usage("new|list ...");
          254  +  const char *zCmd = "list";
          255  +  db_find_and_open_repository(0, 0);
          256  +  if( g.argc<2 ){
          257  +    usage("new|list|ls ...");
   191    258     }
   192         -  n = strlen(g.argv[2]);
   193         -  if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
          259  +  if( g.argc>=3 ) zCmd = g.argv[2];
          260  +  n = strlen(zCmd);
          261  +  if( strncmp(zCmd,"new",n)==0 ){
   194    262       branch_new();
   195         -  }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){
          263  +  }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){
   196    264       Stmt q;
   197         -    db_prepare(&q,
   198         -      "%s"
   199         -      "   AND blob.rid IN (SELECT rid FROM tagxref"
   200         -      "                     WHERE tagid=%d AND tagtype==2 AND srcid!=0)"
   201         -      " ORDER BY event.mtime DESC",
   202         -      timeline_query_for_tty(), TAG_BRANCH
   203         -    );
   204         -    print_timeline(&q, 2000);
          265  +    int vid;
          266  +    char *zCurrent = 0;
          267  +    int showAll = find_option("all",0,0)!=0;
          268  +    int showClosed = find_option("closed",0,0)!=0;
          269  +
          270  +    if( g.localOpen ){
          271  +      vid = db_lget_int("checkout", 0);
          272  +      zCurrent = db_text(0, "SELECT value FROM tagxref"
          273  +                            " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
          274  +    }
          275  +    branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
          276  +    while( db_step(&q)==SQLITE_ROW ){
          277  +      const char *zBr = db_column_text(&q, 0);
          278  +      int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
          279  +      fossil_print("%s%s\n", (isCur ? "* " : "  "), zBr);
          280  +    }
   205    281       db_finalize(&q);
   206    282     }else{
   207    283       fossil_panic("branch subcommand should be one of: "
   208         -                 "new list");
          284  +                 "new list ls");
   209    285     }
   210    286   }
   211    287   
   212    288   /*
   213    289   ** WEBPAGE: brlist
   214    290   **
   215    291   ** Show a timeline of all branches
   216    292   */
   217    293   void brlist_page(void){
   218    294     Stmt q;
   219    295     int cnt;
          296  +  int showClosed = P("closed")!=0;
          297  +  int showAll = P("all")!=0;
          298  +  int colorTest = P("colortest")!=0;
   220    299   
   221    300     login_check_credentials();
   222         -  if( !g.okRead ){ login_needed(); return; }
          301  +  if( !g.perm.Read ){ login_needed(); return; }
          302  +  if( colorTest ){
          303  +    showClosed = 0;
          304  +    showAll = 1;
          305  +  }
   223    306   
   224         -  style_header("Branches");
          307  +  style_header(showClosed ? "Closed Branches" :
          308  +                  showAll ? "All Branches" : "Open Branches");
   225    309     style_submenu_element("Timeline", "Timeline", "brtimeline");
          310  +  if( showClosed ){
          311  +    style_submenu_element("All", "All", "brlist?all");
          312  +    style_submenu_element("Open","Open","brlist");
          313  +  }else if( showAll ){
          314  +    style_submenu_element("Closed", "Closed", "brlist?closed");
          315  +    style_submenu_element("Open","Open","brlist");
          316  +  }else{
          317  +    style_submenu_element("All", "All", "brlist?all");
          318  +    style_submenu_element("Closed","Closed","brlist?closed");
          319  +  }
          320  +  if( !colorTest ){
          321  +    style_submenu_element("Color-Test", "Color-Test", "brlist?colortest");
          322  +  }else{
          323  +    style_submenu_element("All", "All", "brlist?all");
          324  +  }
   226    325     login_anonymous_available();
   227         -  compute_leaves(0, 1);
   228    326     style_sidebox_begin("Nomenclature:", "33%");
   229    327     @ <ol>
   230         -  @ <li> An <b>open branch</b> is a branch that has one or
   231         -  @ more <a href="leaves">open leaves.</a>
          328  +  @ <li> An <div class="sideboxDescribed">%z(href("brlist"))
          329  +  @ open branch</a></div> is a branch that has one or
          330  +  @ more %z(href("leaves"))open leaves.</a>
   232    331     @ The presence of open leaves presumably means
   233    332     @ that the branch is still being extended with new check-ins.</li>
   234         -  @ <li> A <b>closed branch</b> is a branch with only
   235         -  @ <a href="leaves?closed">closed leaves</a>.
          333  +  @ <li> A <div class="sideboxDescribed">%z(href("brlist?closed"))
          334  +  @ closed branch</a></div> is a branch with only
          335  +  @ <div class="sideboxDescribed">%z(href("leaves?closed"))
          336  +  @ closed leaves</a></div>.
   236    337     @ Closed branches are fixed and do not change (unless they are first
   237    338     @ reopened)</li>
   238    339     @ </ol>
   239    340     style_sidebox_end();
   240    341   
   241         -  db_prepare(&q,
   242         -    "SELECT DISTINCT value FROM tagxref"
   243         -    " WHERE tagid=%d AND value NOT NULL"
   244         -    "   AND rid IN leaves"
   245         -    " ORDER BY value /*sort*/",
   246         -    TAG_BRANCH
   247         -  );
          342  +  branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
   248    343     cnt = 0;
   249    344     while( db_step(&q)==SQLITE_ROW ){
   250    345       const char *zBr = db_column_text(&q, 0);
   251    346       if( cnt==0 ){
   252         -      @ <h2>Open Branches:</h2>
          347  +      if( colorTest ){
          348  +        @ <h2>Default background colors for all branches:</h2>
          349  +      }else if( showAll ){
          350  +        @ <h2>All Branches:</h2>
          351  +      }else if( showClosed ){
          352  +        @ <h2>Closed Branches:</h2>
          353  +      }else{
          354  +        @ <h2>Open Branches:</h2>
          355  +      }
   253    356         @ <ul>
   254    357         cnt++;
   255    358       }
   256         -    if( g.okHistory ){
   257         -      @ <li><a href="%s(g.zBaseURL)/timeline?t=%T(zBr)">%h(zBr)</a></li>
          359  +    if( colorTest ){
          360  +      const char *zColor = hash_color(zBr);
          361  +      @ <li><span style="background-color: %s(zColor)">
          362  +      @ %h(zBr) &rarr; %s(zColor)</span></li>
   258    363       }else{
   259         -      @ <li><b>%h(zBr)</b></li>
   260         -    }
   261         -  }
   262         -  db_finalize(&q);
   263         -  if( cnt ){
   264         -    @ </ul>
   265         -  }
   266         -  cnt = 0;
   267         -  db_prepare(&q,
   268         -    "SELECT value FROM tagxref"
   269         -    " WHERE tagid=%d AND value NOT NULL"
   270         -    " EXCEPT "
   271         -    "SELECT value FROM tagxref"
   272         -    " WHERE tagid=%d AND value NOT NULL"
   273         -    "   AND rid IN leaves"
   274         -    " ORDER BY value /*sort*/",
   275         -    TAG_BRANCH, TAG_BRANCH
   276         -  );
   277         -  while( db_step(&q)==SQLITE_ROW ){
   278         -    const char *zBr = db_column_text(&q, 0);
   279         -    if( cnt==0 ){
   280         -      @ <h2>Closed Branches:</h2>
   281         -      @ <ul>
   282         -      cnt++;     
   283         -    }
   284         -    if( g.okHistory ){
   285         -      @ <li><a href="%s(g.zBaseURL)/timeline?t=%T(zBr)">%h(zBr)</a></li>
   286         -    }else{
   287         -      @ <li><b>%h(zBr)</b></li>
          364  +      @ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li>
   288    365       }
   289    366     }
   290    367     if( cnt ){
   291    368       @ </ul>
   292    369     }
   293    370     db_finalize(&q);
   294         -  @ </ul>
   295         -  @ <br clear="both">
   296         -  @ <script>
          371  +  @ <script  type="text/JavaScript">
   297    372     @ function xin(id){
   298    373     @ }
   299    374     @ function xout(id){
   300    375     @ }
   301    376     @ </script>
   302    377     style_footer();
   303    378   }
................................................................................
   305    380   /*
   306    381   ** This routine is called while for each check-in that is rendered by
   307    382   ** the timeline of a "brlist" page.  Add some additional hyperlinks
   308    383   ** to the end of the line.
   309    384   */
   310    385   static void brtimeline_extra(int rid){
   311    386     Stmt q;
   312         -  if( !g.okHistory ) return;
   313         -  db_prepare(&q, 
          387  +  if( !g.perm.Hyperlink ) return;
          388  +  db_prepare(&q,
   314    389       "SELECT substr(tagname,5) FROM tagxref, tag"
   315    390       " WHERE tagxref.rid=%d"
   316    391       "   AND tagxref.tagid=tag.tagid"
   317    392       "   AND tagxref.tagtype>0"
   318    393       "   AND tag.tagname GLOB 'sym-*'",
   319    394       rid
   320    395     );
   321    396     while( db_step(&q)==SQLITE_ROW ){
   322    397       const char *zTagName = db_column_text(&q, 0);
   323         -    @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[timeline]</a>
          398  +    @ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
   324    399     }
   325    400     db_finalize(&q);
   326    401   }
   327    402   
   328    403   /*
   329    404   ** WEBPAGE: brtimeline
   330    405   **
   331    406   ** Show a timeline of all branches
   332    407   */
   333    408   void brtimeline_page(void){
   334    409     Stmt q;
   335    410   
   336    411     login_check_credentials();
   337         -  if( !g.okRead ){ login_needed(); return; }
          412  +  if( !g.perm.Read ){ login_needed(); return; }
   338    413   
   339    414     style_header("Branches");
   340    415     style_submenu_element("List", "List", "brlist");
   341    416     login_anonymous_available();
   342    417     @ <h2>The initial check-in for each branch:</h2>
   343    418     db_prepare(&q,
   344    419       "%s AND blob.rid IN (SELECT rid FROM tagxref"
   345    420       "                     WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
   346    421       " ORDER BY event.mtime DESC",
   347    422       timeline_query_for_www(), TAG_BRANCH
   348    423     );
   349         -  www_print_timeline(&q, 0, brtimeline_extra);
          424  +  www_print_timeline(&q, 0, 0, 0, brtimeline_extra);
   350    425     db_finalize(&q);
   351         -  @ <br clear="both">
   352         -  @ <script>
          426  +  @ <script  type="text/JavaScript">
   353    427     @ function xin(id){
   354    428     @ }
   355    429     @ function xout(id){
   356    430     @ }
   357    431     @ </script>
   358    432     style_footer();
   359    433   }

Changes to src/browse.c.

    18     18   ** This file contains code to implement the file browser web interface.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "browse.h"
    22     22   #include <assert.h>
    23     23   
    24     24   /*
    25         -** This is the implemention of the "pathelement(X,N)" SQL function.
           25  +** This is the implementation of the "pathelement(X,N)" SQL function.
    26     26   **
    27     27   ** If X is a unix-like pathname (with "/" separators) and N is an
    28     28   ** integer, then skip the initial N characters of X and return the
    29     29   ** name of the path component that begins on the N+1th character
    30     30   ** (numbered from 0).  If the path component is a directory (if
    31     31   ** it is followed by other path components) then prepend "/".
    32     32   **
    33     33   ** Examples:
    34     34   **
    35     35   **      pathelement('abc/pqr/xyz', 4)  ->  '/pqr'
    36     36   **      pathelement('abc/pqr', 4)      ->  'pqr'
    37     37   **      pathelement('abc/pqr/xyz', 0)  ->  '/abc'
    38     38   */
    39         -static void pathelementFunc(
           39  +void pathelementFunc(
    40     40     sqlite3_context *context,
    41     41     int argc,
    42     42     sqlite3_value **argv
    43     43   ){
    44     44     const unsigned char *z;
    45     45     int len, n, i;
    46     46     char *zOut;
................................................................................
    69     69   ** to the "dir" page for the directory.
    70     70   **
    71     71   ** There is no hyperlink on the file element of the path.
    72     72   **
    73     73   ** The computed string is appended to the pOut blob.  pOut should
    74     74   ** have already been initialized.
    75     75   */
    76         -void hyperlinked_path(const char *zPath, Blob *pOut){
           76  +void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
    77     77     int i, j;
    78     78     char *zSep = "";
    79     79   
    80     80     for(i=0; zPath[i]; i=j){
    81     81       for(j=i; zPath[j] && zPath[j]!='/'; j++){}
    82         -    if( zPath[j] && g.okHistory ){
    83         -      blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", 
    84         -                   zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
           82  +    if( zPath[j] && g.perm.Hyperlink ){
           83  +      if( zCI ){
           84  +        char *zLink = href("%R/dir?ci=%S&name=%#T", zCI, j, zPath);
           85  +        blob_appendf(pOut, "%s%z%#h</a>", 
           86  +                     zSep, zLink, j-i, &zPath[i]);
           87  +      }else{
           88  +        char *zLink = href("%R/dir?name=%#T", j, zPath);
           89  +        blob_appendf(pOut, "%s%z%#h</a>", 
           90  +                     zSep, zLink, j-i, &zPath[i]);
           91  +      }
    85     92       }else{
    86     93         blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
    87     94       }
    88     95       zSep = "/";
    89     96       while( zPath[j]=='/' ){ j++; }
    90     97     }
    91     98   }
................................................................................
    96    103   **
    97    104   ** Query parameters:
    98    105   **
    99    106   **    name=PATH        Directory to display.  Required.
   100    107   **    ci=LABEL         Show only files in this check-in.  Optional.
   101    108   */
   102    109   void page_dir(void){
   103         -  const char *zD = P("name");
          110  +  char *zD = fossil_strdup(P("name"));
          111  +  int nD = zD ? strlen(zD)+1 : 0;
   104    112     int mxLen;
   105    113     int nCol, nRow;
   106    114     int cnt, i;
   107    115     char *zPrefix;
   108    116     Stmt q;
   109    117     const char *zCI = P("ci");
   110    118     int rid = 0;
   111         -  Blob content;
          119  +  char *zUuid = 0;
   112    120     Blob dirname;
   113         -  Manifest m;
          121  +  Manifest *pM = 0;
   114    122     const char *zSubdirLink;
   115    123   
   116    124     login_check_credentials();
   117         -  if( !g.okHistory ){ login_needed(); return; }
          125  +  if( !g.perm.Read ){ login_needed(); return; }
          126  +  while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
   118    127     style_header("File List");
   119    128     sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
   120    129                             pathelementFunc, 0, 0);
   121    130   
   122    131     /* If the name= parameter is an empty string, make it a NULL pointer */
   123    132     if( zD && strlen(zD)==0 ){ zD = 0; }
   124    133   
   125         -  /* If a specific check-in is requested, fetch and parse it. */
   126         -  if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){
   127         -    if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
          134  +  /* If a specific check-in is requested, fetch and parse it.  If the
          135  +  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
          136  +  ** files from all check-ins to be displayed.
          137  +  */
          138  +  if( zCI ){
          139  +    pM = manifest_get_by_name(zCI, &rid);
          140  +    if( pM ){
          141  +      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          142  +    }else{
   128    143         zCI = 0;
   129    144       }
   130    145     }
          146  +
   131    147   
   132    148     /* Compute the title of the page */  
   133    149     blob_zero(&dirname);
   134    150     if( zD ){
   135    151       blob_append(&dirname, "in directory ", -1);
   136         -    hyperlinked_path(zD, &dirname);
   137         -    zPrefix = mprintf("%h/", zD);
          152  +    hyperlinked_path(zD, &dirname, zCI);
          153  +    zPrefix = mprintf("%s/", zD);
   138    154     }else{
   139    155       blob_append(&dirname, "in the top-level directory", -1);
   140    156       zPrefix = "";
   141    157     }
   142    158     if( zCI ){
   143         -    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   144    159       char zShort[20];
   145    160       memcpy(zShort, zUuid, 10);
   146    161       zShort[10] = 0;
   147         -    @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
          162  +    @ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
   148    163       @ %s(blob_str(&dirname))</h2>
   149         -    zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix);
          164  +    zSubdirLink = mprintf("%R/dir?ci=%S&name=%T", zUuid, zPrefix);
   150    165       if( zD ){
   151         -      style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
   152         -      style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
          166  +      style_submenu_element("Top", "Top", "%R/dir?ci=%S", zUuid);
          167  +      style_submenu_element("All", "All", "%R/dir?name=%t", zD);
   153    168       }else{
   154         -      style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
          169  +      style_submenu_element("All", "All", "%R/dir");
          170  +      style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
          171  +                            zUuid);
   155    172       }
   156    173     }else{
          174  +    int hasTrunk;
   157    175       @ <h2>The union of all files from all check-ins
   158    176       @ %s(blob_str(&dirname))</h2>
   159         -    zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
          177  +    hasTrunk = db_exists(
          178  +                  "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
          179  +                  TAG_BRANCH);
          180  +    zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
   160    181       if( zD ){
   161         -      style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
   162         -      style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
   163         -                            g.zBaseURL, zD);
   164         -      style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
   165         -                             g.zBaseURL,zD);
          182  +      style_submenu_element("Top", "Top", "%R/dir");
          183  +      style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
          184  +      if( hasTrunk ){
          185  +        style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
          186  +                               zD);
          187  +      }
   166    188       }else{
   167         -      style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
   168         -      style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
          189  +      style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
          190  +      if( hasTrunk ){
          191  +        style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
          192  +      }
   169    193       }
   170    194     }
   171    195   
   172    196     /* Compute the temporary table "localfiles" containing the names
   173    197     ** of all files and subdirectories in the zD[] directory.  
   174    198     **
   175    199     ** Subdirectory names begin with "/".  This causes them to sort
   176    200     ** first and it also gives us an easy way to distinguish files
   177    201     ** from directories in the loop that follows.
   178    202     */
   179    203     db_multi_exec(
   180         -     "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
   181         -     "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
          204  +     "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL %s, u);",
          205  +     filename_collation()
   182    206     );
   183    207     if( zCI ){
   184    208       Stmt ins;
   185         -    int i;
   186         -    db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
   187         -    for(i=0; i<m.nFile; i++){
   188         -      db_bind_text(&ins, ":x", m.aFile[i].zName);
   189         -      db_bind_text(&ins, ":u", m.aFile[i].zUuid);
          209  +    ManifestFile *pFile;
          210  +    ManifestFile *pPrev = 0;
          211  +    int nPrev = 0;
          212  +    int c;
          213  +
          214  +    db_prepare(&ins,
          215  +       "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
          216  +    );
          217  +    manifest_file_rewind(pM);
          218  +    while( (pFile = manifest_file_next(pM,0))!=0 ){
          219  +      if( nD>0 
          220  +       && (filenames_strncmp(pFile->zName, zD, nD-1)!=0
          221  +           || pFile->zName[nD-1]!='/')
          222  +      ){
          223  +        continue;
          224  +      }
          225  +      if( pPrev
          226  +       && filenames_strncmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0
          227  +       && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/')
          228  +      ){
          229  +        continue;
          230  +      }
          231  +      db_bind_text(&ins, ":x", &pFile->zName[nD]);
          232  +      db_bind_text(&ins, ":u", pFile->zUuid);
   190    233         db_step(&ins);
   191    234         db_reset(&ins);
          235  +      pPrev = pFile;
          236  +      for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
          237  +      if( c=='/' ) nPrev++;
   192    238       }
   193    239       db_finalize(&ins);
   194         -  }else{
   195         -    db_multi_exec(
   196         -      "INSERT INTO allfiles SELECT name, NULL FROM filename"
   197         -    );
   198         -  }
   199         -  if( zD ){
   200         -    db_multi_exec(
   201         -       "INSERT OR IGNORE INTO localfiles "
   202         -       "  SELECT pathelement(x,%d), u FROM allfiles"
   203         -       "   WHERE x GLOB '%q/*'",
   204         -       strlen(zD)+1, zD
   205         -    );
          240  +  }else if( zD ){
          241  +    if( filenames_are_case_sensitive() ){
          242  +      db_multi_exec(
          243  +        "INSERT OR IGNORE INTO localfiles"
          244  +        " SELECT pathelement(name,%d), NULL FROM filename"
          245  +        "  WHERE name GLOB '%q/*'",
          246  +        nD, zD
          247  +      );
          248  +    }else{
          249  +      db_multi_exec(
          250  +        "INSERT OR IGNORE INTO localfiles"
          251  +        " SELECT pathelement(name,%d), NULL FROM filename"
          252  +        "  WHERE name LIKE '%q/%%'",
          253  +        nD, zD
          254  +      );
          255  +    }
   206    256     }else{
   207    257       db_multi_exec(
   208         -       "INSERT OR IGNORE INTO localfiles "
   209         -       "  SELECT pathelement(x,0), u FROM allfiles"
          258  +      "INSERT OR IGNORE INTO localfiles"
          259  +      " SELECT pathelement(name,0), NULL FROM filename"
   210    260       );
   211    261     }
   212    262   
   213    263     /* Generate a multi-column table listing the contents of zD[]
   214    264     ** directory.
   215    265     */
   216    266     mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
   217    267     cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
   218         -  nCol = 4;
          268  +  if( mxLen<12 ) mxLen = 12;
          269  +  nCol = 100/mxLen;
          270  +  if( nCol<1 ) nCol = 1;
          271  +  if( nCol>5 ) nCol = 5;
   219    272     nRow = (cnt+nCol-1)/nCol;
   220    273     db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
   221         -  @ <table border="0" width="100%%"><tr><td valign="top" width="25%%">
          274  +  @ <table class="browser"><tr><td class="browser"><ul class="browser">
   222    275     i = 0;
   223    276     while( db_step(&q)==SQLITE_ROW ){
   224    277       const char *zFN;
   225    278       if( i==nRow ){
   226         -      @ </td><td valign="top" width="25%%">
          279  +      @ </ul></td><td class="browser"><ul class="browser">
   227    280         i = 0;
   228    281       }
   229    282       i++;
   230    283       zFN = db_column_text(&q, 0);
   231    284       if( zFN[0]=='/' ){
   232    285         zFN++;
   233         -      @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
          286  +      @ <li>%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
   234    287       }else if( zCI ){
   235    288         const char *zUuid = db_column_text(&q, 1);
   236         -      @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a>
          289  +      @ <li>%z(href("%R/artifact/%s",zUuid))%h(zFN)</a></li>
   237    290       }else{
   238         -      @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)</a>
          291  +      @ <li>%z(href("%R/finfo?name=%T%T",zPrefix,zFN))%h(zFN)
          292  +      @     </a></li>
   239    293       }
   240    294     }
   241    295     db_finalize(&q);
   242         -  @ </td></tr></table>
          296  +  manifest_destroy(pM);
          297  +  @ </ul></td></tr></table>
   243    298     style_footer();
   244    299   }
          300  +
          301  +/*
          302  +** Look at all file containing in the version "vid".  Construct a
          303  +** temporary table named "fileage" that contains the file-id for each
          304  +** files, the pathname, the check-in where the file was added, and the
          305  +** mtime on that checkin.
          306  +*/
          307  +int compute_fileage(int vid){
          308  +  Manifest *pManifest;
          309  +  ManifestFile *pFile;
          310  +  int nFile = 0;
          311  +  double vmtime;
          312  +  Stmt ins;
          313  +  Stmt q1, q2, q3;
          314  +  Stmt upd;
          315  +  db_multi_exec(
          316  +    /*"DROP TABLE IF EXISTS temp.fileage;"*/
          317  +    "CREATE TEMP TABLE fileage("
          318  +    "  fid INTEGER,"
          319  +    "  mid INTEGER,"
          320  +    "  mtime DATETIME,"
          321  +    "  pathname TEXT"
          322  +    ");"
          323  +    "CREATE INDEX fileage_fid ON fileage(fid);"
          324  +  );
          325  +  pManifest = manifest_get(vid, CFTYPE_MANIFEST);
          326  +  if( pManifest==0 ) return 1;
          327  +  manifest_file_rewind(pManifest);
          328  +  db_prepare(&ins, 
          329  +     "INSERT INTO temp.fileage(fid, pathname)"
          330  +     "  SELECT rid, :path FROM blob WHERE uuid=:uuid"
          331  +  );
          332  +  while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
          333  +    db_bind_text(&ins, ":uuid", pFile->zUuid);
          334  +    db_bind_text(&ins, ":path", pFile->zName);
          335  +    db_step(&ins);
          336  +    db_reset(&ins);
          337  +    nFile++;
          338  +  }
          339  +  db_finalize(&ins);
          340  +  manifest_destroy(pManifest);
          341  +  db_prepare(&q1,"SELECT fid FROM mlink WHERE mid=:mid");
          342  +  db_prepare(&upd, "UPDATE fileage SET mid=:mid, mtime=:vmtime"
          343  +                      " WHERE fid=:fid AND mid IS NULL");
          344  +  db_prepare(&q2,"SELECT pid FROM plink WHERE cid=:vid AND isprim");
          345  +  db_prepare(&q3,"SELECT mtime FROM event WHERE objid=:vid");
          346  +  while( nFile>0 && vid>0 ){
          347  +    db_bind_int(&q3, ":vid", vid);
          348  +    if( db_step(&q3)==SQLITE_ROW ){
          349  +      vmtime = db_column_double(&q3, 0);
          350  +    }else{
          351  +      break;
          352  +    }
          353  +    db_reset(&q3);
          354  +    db_bind_int(&q1, ":mid", vid);
          355  +    db_bind_int(&upd, ":mid", vid);
          356  +    db_bind_double(&upd, ":vmtime", vmtime);
          357  +    while( db_step(&q1)==SQLITE_ROW ){
          358  +      db_bind_int(&upd, ":fid", db_column_int(&q1, 0));
          359  +      db_step(&upd);
          360  +      nFile -= db_changes();
          361  +      db_reset(&upd);
          362  +    }
          363  +    db_reset(&q1);
          364  +    db_bind_int(&q2, ":vid", vid);
          365  +    if( db_step(&q2)!=SQLITE_ROW ) break;
          366  +    vid = db_column_int(&q2, 0);
          367  +    db_reset(&q2);
          368  +  }
          369  +  db_finalize(&q1);
          370  +  db_finalize(&upd);
          371  +  db_finalize(&q2);
          372  +  db_finalize(&q3);
          373  +  return 0;
          374  +}
          375  +
          376  +/*
          377  +** WEBPAGE:  fileage
          378  +**
          379  +** Parameters:
          380  +**   name=VERSION
          381  +*/
          382  +void fileage_page(void){
          383  +  int rid;
          384  +  const char *zName;
          385  +  char *zBaseTime;
          386  +  Stmt q;
          387  +  double baseTime;
          388  +  int lastMid = -1;
          389  +
          390  +  login_check_credentials();
          391  +  if( !g.perm.Read ){ login_needed(); return; }
          392  +  zName = P("name");
          393  +  if( zName==0 ) zName = "tip";
          394  +  rid = symbolic_name_to_rid(zName, "ci");
          395  +  if( rid==0 ){
          396  +    fossil_fatal("not a valid check-in: %s", zName);
          397  +  }
          398  +  style_header("File Ages", zName);
          399  +  compute_fileage(rid);
          400  +  baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
          401  +  zBaseTime = db_text("","SELECT datetime(%.20g,'localtime')", baseTime);
          402  +  @ <h2>File Ages For Check-in
          403  +  @ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
          404  +  @
          405  +  @ <p>The times given are relative 
          406  +  @ %z(href("%R/timeline?c=%T",zBaseTime))%s(zBaseTime)</a>, which is the
          407  +  @ check-in time for 
          408  +  @ %z(href("%R/info?name=%T",zName))%h(zName)</a></p>
          409  +  @
          410  +  @ <table border=0 cellspacing=0 cellpadding=0>
          411  +  db_prepare(&q,
          412  +    "SELECT mtime, (SELECT uuid FROM blob WHERE rid=fid), mid, pathname"
          413  +    "  FROM fileage"
          414  +    " ORDER BY mtime DESC, mid, pathname"
          415  +  );
          416  +  while( db_step(&q)==SQLITE_ROW ){
          417  +    double age = baseTime - db_column_double(&q, 0);
          418  +    int mid = db_column_int(&q, 2);
          419  +    const char *zFUuid = db_column_text(&q, 1);
          420  +    char zAge[200];
          421  +    if( lastMid!=mid ){
          422  +      @ <tr><td colspan=3><hr></tr>
          423  +      lastMid = mid;
          424  +      if( age*86400.0<120 ){
          425  +        sqlite3_snprintf(sizeof(zAge), zAge, "%d seconds", (int)(age*86400.0));
          426  +      }else if( age*1440.0<90 ){
          427  +        sqlite3_snprintf(sizeof(zAge), zAge, "%.1f minutes", age*1440.0);
          428  +      }else if( age*24.0<36 ){
          429  +        sqlite3_snprintf(sizeof(zAge), zAge, "%.1f hours", age*24.0);
          430  +      }else if( age<365.0 ){
          431  +        sqlite3_snprintf(sizeof(zAge), zAge, "%.1f days", age);
          432  +      }else{
          433  +        sqlite3_snprintf(sizeof(zAge), zAge, "%.2f years", age/365.0);
          434  +      }
          435  +    }else{
          436  +      zAge[0] = 0;
          437  +    }
          438  +    @ <tr>
          439  +    @ <td>%s(zAge)
          440  +    @ <td width="25">
          441  +    @ <td>%z(href("%R/artifact/%S?ln", zFUuid))%h(db_column_text(&q, 3))</a>
          442  +    @ </tr>
          443  +    @
          444  +  }
          445  +  @ <tr><td colspan=3><hr></tr>
          446  +  @ </table>
          447  +  db_finalize(&q);
          448  +  style_footer();  
          449  +}

Changes to src/captcha.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** This file contains code to a simple text-based CAPTCHA.  Though eaily
           18  +** This file contains code to a simple text-based CAPTCHA.  Though easily
    19     19   ** defeated by a sophisticated attacker, this CAPTCHA does at least make
    20     20   ** scripting attacks more difficult.
    21     21   */
    22     22   #include <assert.h>
    23     23   #include "config.h"
    24     24   #include "captcha.h"
    25     25   
................................................................................
    67     67   
    68     68   /*
    69     69   ** Render an 8-character hexadecimal string as ascii art.
    70     70   ** Space to hold the result is obtained from malloc() and should be freed
    71     71   ** by the caller.
    72     72   */
    73     73   char *captcha_render(const char *zPw){
    74         -  char *z = malloc( 500 );
           74  +  char *z = fossil_malloc( 500 );
    75     75     int i, j, k, m;
    76     76   
    77     77     k = 0;
    78     78     for(i=0; i<6; i++){
    79     79       for(j=0; j<8; j++){
    80     80         unsigned char v = hexValue(zPw[j]);
    81     81         v = (aFont1[v] >> ((5-i)*4)) & 0xf;
................................................................................
    96     96     z[k] = 0;
    97     97     return z;     
    98     98   }
    99     99   #endif /* CAPTCHA==1 */
   100    100   
   101    101   
   102    102   #if CAPTCHA==2
   103         -static const char *azFont2[] = {
          103  +static const char *const azFont2[] = {
   104    104    /* 0 */
   105    105    "  __  ",
   106    106    " /  \\ ",
   107    107    "| () |",
   108    108    " \\__/ ",
   109    109   
   110    110    /* 1 */
................................................................................
   200    200   
   201    201   /*
   202    202   ** Render an 8-digit hexadecimal string as ascii arg.
   203    203   ** Space to hold the result is obtained from malloc() and should be freed
   204    204   ** by the caller.
   205    205   */
   206    206   char *captcha_render(const char *zPw){
   207         -  char *z = malloc( 300 );
          207  +  char *z = fossil_malloc( 300 );
   208    208     int i, j, k, m;
   209    209     const char *zChar;
   210    210   
   211    211     k = 0;
   212    212     for(i=0; i<4; i++){
   213    213       for(j=0; j<8; j++){
   214    214         unsigned char v = hexValue(zPw[j]);
................................................................................
   221    221     }
   222    222     z[k] = 0;
   223    223     return z;     
   224    224   }
   225    225   #endif /* CAPTCHA==2 */
   226    226   
   227    227   #if CAPTCHA==3
   228         -static const char *azFont3[] = {
          228  +static const char *const azFont3[] = {
   229    229     /* 0 */
   230    230     "  ___  ",
   231    231     " / _ \\ ",
   232    232     "| | | |",
   233    233     "| | | |",
   234    234     "| |_| |",
   235    235     " \\___/ ",
................................................................................
   357    357   
   358    358   /*
   359    359   ** Render an 8-digit hexadecimal string as ascii arg.
   360    360   ** Space to hold the result is obtained from malloc() and should be freed
   361    361   ** by the caller.
   362    362   */
   363    363   char *captcha_render(const char *zPw){
   364         -  char *z = malloc( 600 );
          364  +  char *z = fossil_malloc( 600 );
   365    365     int i, j, k, m;
   366    366     const char *zChar;
   367    367   
   368    368     k = 0;
   369    369     for(i=0; i<6; i++){
   370    370       for(j=0; j<8; j++){
   371    371         unsigned char v = hexValue(zPw[j]);
................................................................................
   388    388     int i;
   389    389     unsigned int v;
   390    390     char *z;
   391    391   
   392    392     for(i=2; i<g.argc; i++){
   393    393       char zHex[30];
   394    394       v = (unsigned int)atoi(g.argv[i]);
   395         -    sprintf(zHex, "%x", v);
          395  +    sqlite3_snprintf(sizeof(zHex), zHex, "%x", v);
   396    396       z = captcha_render(zHex);
   397         -    printf("%s:\n%s", zHex, z);
          397  +    fossil_print("%s:\n%s", zHex, z);
   398    398       free(z);
   399    399     }
   400    400   }
   401    401   
   402    402   /*
   403    403   ** Compute a seed value for a captcha.  The seed is public and is sent
   404         -** has a hidden parameter with the page that contains the captcha.  Knowledge
          404  +** as a hidden parameter with the page that contains the captcha.  Knowledge
   405    405   ** of the seed is insufficient for determining the captcha without additional
   406    406   ** information held only on the server and never revealed.
   407    407   */
   408    408   unsigned int captcha_seed(void){
   409    409     unsigned int x;
   410    410     sqlite3_randomness(sizeof(x), &x);
   411    411     x &= 0x7fffffff;
   412    412     return x;
   413    413   }
   414    414   
   415    415   /*
   416    416   ** Translate a captcha seed value into the captcha password string.
          417  +** The returned string is static and overwritten on each call to
          418  +** this function.
   417    419   */
   418         -char *captcha_decode(unsigned int seed){
          420  +char const *captcha_decode(unsigned int seed){
   419    421     const char *zSecret;
   420    422     const char *z;
   421    423     Blob b;
   422    424     static char zRes[20];
   423    425   
   424    426     zSecret = db_get("captcha-secret", 0);
   425    427     if( zSecret==0 ){
................................................................................
   434    436     blob_appendf(&b, "%s-%x", zSecret, seed);
   435    437     sha1sum_blob(&b, &b);
   436    438     z = blob_buffer(&b);
   437    439     memcpy(zRes, z, 8);
   438    440     zRes[8] = 0;
   439    441     return zRes;
   440    442   }
          443  +
          444  +/*
          445  +** Return true if a CAPTCHA is required for editing wiki or tickets or for
          446  +** adding attachments.
          447  +**
          448  +** A CAPTCHA is required in those cases if the user is not logged in (if they
          449  +** are user "nobody") and if the "require-captcha" setting is true.  The
          450  +** "require-captcha" setting is controlled on the Admin/Access page.  It 
          451  +** defaults to true.
          452  +*/
          453  +int captcha_needed(void){
          454  +  if( g.zLogin!=0 ) return 0;
          455  +  return db_get_boolean("require-captcha", 1);
          456  +}
          457  +
          458  +/*
          459  +** If a captcha is required but the correct captcha code is not supplied
          460  +** in the query parameters, then return false (0).
          461  +**
          462  +** If no captcha is required or if the correct captcha is supplied, return
          463  +** true (non-zero).
          464  +**
          465  +** The query parameters examined are "captchaseed" for the seed value and
          466  +** "captcha" for text that the user types in response to the captcha prompt.
          467  +*/
          468  +int captcha_is_correct(void){
          469  +  const char *zSeed;
          470  +  const char *zEntered;
          471  +  const char *zDecode;
          472  +  char z[30];
          473  +  int i;
          474  +  if( !captcha_needed() ){
          475  +    return 1;  /* No captcha needed */
          476  +  }
          477  +  zSeed = P("captchaseed");
          478  +  if( zSeed==0 ) return 0;
          479  +  zEntered = P("captcha");
          480  +  if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
          481  +  zDecode = captcha_decode((unsigned int)atoi(zSeed));
          482  +  assert( strlen(zDecode)==8 );
          483  +  if( strlen(zEntered)!=8 ) return 0;
          484  +  for(i=0; i<8; i++){
          485  +    char c = zEntered[i];
          486  +    if( c>='A' && c<='F' ) c += 'a' - 'A';
          487  +    if( c=='O' ) c = '0';
          488  +    z[i] = c;
          489  +  }
          490  +  if( memcmp(zDecode,z,8)!=0 ) return 0;
          491  +  return 1;
          492  +}
          493  +
          494  +/*
          495  +** Generate a captcha display together with the necessary hidden parameter
          496  +** for the seed and the entry box into which the user will type the text of
          497  +** the captcha.  This is typically done at the very bottom of a form.
          498  +**
          499  +** This routine is a no-op if no captcha is required.
          500  +*/
          501  +void captcha_generate(void){
          502  +  unsigned int uSeed;
          503  +  const char *zDecoded;
          504  +  char *zCaptcha;
          505  +
          506  +  if( !captcha_needed() ) return;
          507  +  uSeed = captcha_seed();
          508  +  zDecoded = captcha_decode(uSeed);
          509  +  zCaptcha = captcha_render(zDecoded);
          510  +  @ <div class="captcha"><table class="captcha"><tr><td><pre>
          511  +  @ %h(zCaptcha)
          512  +  @ </pre>
          513  +  @ Enter security code shown above:
          514  +  @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
          515  +  @ <input type="text" name="captcha" size=8 />
          516  +  @ </td></tr></table></div>
          517  +}

Changes to src/cgi.c.

     1      1   /*
     2      2   ** Copyright (c) 2006 D. Richard Hipp
     3      3   **
     4      4   ** This program is free software; you can redistribute it and/or
     5      5   ** modify it under the terms of the Simplified BSD License (also
     6      6   ** known as the "2-Clause License" or "FreeBSD License".)
     7         -
            7  +**
     8      8   ** This program is distributed in the hope that it will be useful,
     9      9   ** but without any warranty; without even the implied warranty of
    10     10   ** merchantability or fitness for a particular purpose.
    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
................................................................................
    18     18   ** This file contains C functions and procedures that provide useful
    19     19   ** services to CGI programs.  There are procedures for parsing and
    20     20   ** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
    21     21   ** formatting function and its cousins, and routines to encode and
    22     22   ** decode strings in HTML or HTTP.
    23     23   */
    24     24   #include "config.h"
    25         -#ifdef __MINGW32__
    26         -# include <windows.h>           /* for Sleep once server works again */
    27         -# include <winsock2.h>          /* socket operations */
    28         -# define sleep Sleep            /* windows does not have sleep, but Sleep */
    29         -# include <ws2tcpip.h>          
           25  +#ifdef _WIN32
           26  +# include <winsock2.h>
           27  +# include <ws2tcpip.h>
    30     28   #else
    31     29   # include <sys/socket.h>
    32     30   # include <netinet/in.h>
    33     31   # include <arpa/inet.h>
    34     32   # include <sys/times.h>
    35     33   # include <sys/time.h>
    36     34   # include <sys/wait.h>
................................................................................
    49     47   /*
    50     48   ** Shortcuts for cgi_parameter.  P("x") returns the value of query parameter
    51     49   ** or cookie "x", or NULL if there is no such parameter or cookie.  PD("x","y")
    52     50   ** does the same except "y" is returned in place of NULL if there is not match.
    53     51   */
    54     52   #define P(x)        cgi_parameter((x),0)
    55     53   #define PD(x,y)     cgi_parameter((x),(y))
    56         -#define QP(x)       quotable_string(cgi_parameter((x),0))
    57         -#define QPD(x,y)    quotable_string(cgi_parameter((x),(y)))
           54  +#define PT(x)       cgi_parameter_trimmed((x),0)
           55  +#define PDT(x,y)    cgi_parameter_trimmed((x),(y))
    58     56   
    59     57   
    60     58   /*
    61     59   ** Destinations for output text.
    62     60   */
    63     61   #define CGI_HEADER   0
    64     62   #define CGI_BODY     1
................................................................................
    92     90         break;
    93     91       }
    94     92       default: {
    95     93         cgi_panic("bad destination");
    96     94       }
    97     95     }
    98     96   }
           97  +
           98  +/*
           99  +** Check to see if the header contains the zNeedle string.  Return true
          100  +** if it does and false if it does not.
          101  +*/
          102  +int cgi_header_contains(const char *zNeedle){
          103  +  return strstr(blob_str(&cgiContent[0]), zNeedle)!=0;
          104  +}
    99    105   
   100    106   /*
   101    107   ** Append reply content to what already exists.
   102    108   */
   103    109   void cgi_append_content(const char *zData, int nAmt){
   104    110     blob_append(pContent, zData, nAmt);
   105    111   }
................................................................................
   129    135       blob_reset(&cgiContent[1]);
   130    136     }
   131    137   }
   132    138   
   133    139   /*
   134    140   ** Return a pointer to the HTTP reply text.
   135    141   */
   136         -char *cgi_extract_content(int *pnAmt){
          142  +char *cgi_extract_content(void){
   137    143     cgi_combine_header_and_body();
   138    144     return blob_buffer(&cgiContent[0]);
   139    145   }
   140    146   
   141    147   /*
   142    148   ** Additional information used to form the HTTP reply
   143    149   */
................................................................................
   185    191   */
   186    192   void cgi_set_cookie(
   187    193     const char *zName,    /* Name of the cookie */
   188    194     const char *zValue,   /* Value of the cookie.  Automatically escaped */
   189    195     const char *zPath,    /* Path cookie applies to.  NULL means "/" */
   190    196     int lifetime          /* Expiration of the cookie in seconds from now */
   191    197   ){
          198  +  char *zSecure = "";
   192    199     if( zPath==0 ) zPath = g.zTop;
          200  +  if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
          201  +    zSecure = " secure;";
          202  +  }
   193    203     if( lifetime>0 ){
   194    204       lifetime += (int)time(0);
   195    205       blob_appendf(&extraHeader,
   196         -       "Set-Cookie: %s=%t; Path=%s; expires=%z; Version=1\r\n",
   197         -        zName, zValue, zPath, cgi_rfc822_datestamp(lifetime));
          206  +       "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
          207  +        zName, zValue, zPath, cgi_rfc822_datestamp(lifetime), zSecure);
   198    208     }else{
   199    209       blob_appendf(&extraHeader,
   200         -       "Set-Cookie: %s=%t; Path=%s; Version=1\r\n",
   201         -       zName, zValue, zPath);
          210  +       "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
          211  +       zName, zValue, zPath, zSecure);
   202    212     }
   203    213   }
   204    214   
   205    215   #if 0
   206    216   /*
   207    217   ** Add an ETag header line
   208    218   */
................................................................................
   215    225     MD5Init(&ctx);
   216    226     MD5Update(&ctx,zTxt,nLen);
   217    227     MD5Final(digest,&ctx);
   218    228     for(j=i=0; i<16; i++,j+=2){
   219    229       bprintf(&zETag[j],sizeof(zETag)-j,"%02x",(int)digest[i]);
   220    230     }
   221    231     blob_appendf(&extraHeader, "ETag: %s\r\n", zETag);
   222         -  return strdup(zETag);
          232  +  return fossil_strdup(zETag);
   223    233   }
   224    234   
   225    235   /*
   226    236   ** Do some cache control stuff. First, we generate an ETag and include it in
   227    237   ** the response headers. Second, we do whatever is necessary to determine if
   228    238   ** the request was asking about caching and whether we need to send back the
   229    239   ** response body. If we shouldn't send a body, return non-zero.
................................................................................
   235    245   */
   236    246   static int check_cache_control(void){
   237    247     /* FIXME: there's some gotchas wth cookies and some headers. */
   238    248     char *zETag = cgi_add_etag(blob_buffer(&cgiContent),blob_size(&cgiContent));
   239    249     char *zMatch = P("HTTP_IF_NONE_MATCH");
   240    250   
   241    251     if( zETag!=0 && zMatch!=0 ) {
   242         -    char *zBuf = strdup(zMatch);
          252  +    char *zBuf = fossil_strdup(zMatch);
   243    253       if( zBuf!=0 ){
   244    254         char *zTok = 0;
   245    255         char *zPos;
   246    256         for( zTok = strtok_r(zBuf, ",\"",&zPos);
   247         -           zTok && strcasecmp(zTok,zETag);
          257  +           zTok && fossil_stricmp(zTok,zETag);
   248    258              zTok =  strtok_r(0, ",\"",&zPos)){}
   249         -      free(zBuf);
          259  +      fossil_free(zBuf);
   250    260         if(zTok) return 1;
   251    261       }
   252    262     }
   253    263     
   254    264     return 0;
   255    265   }
   256    266   #endif
................................................................................
   283    293     }else{
   284    294       fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
   285    295     }
   286    296   
   287    297     if( blob_size(&extraHeader)>0 ){
   288    298       fprintf(g.httpOut, "%s", blob_buffer(&extraHeader));
   289    299     }
          300  +
          301  +  /* Add headers to turn on useful security options in browsers. */
          302  +  fprintf(g.httpOut, "X-Frame-Options: SAMEORIGIN\r\n");
          303  +  /* This stops fossil pages appearing in frames or iframes, preventing
          304  +  ** click-jacking attacks on supporting browsers.
          305  +  **
          306  +  ** Other good headers would be
          307  +  **   Strict-Transport-Security: max-age=62208000
          308  +  ** if we're using https. However, this would break sites which serve different
          309  +  ** content on http and https protocols. Also,
          310  +  **   X-Content-Security-Policy: allow 'self'
          311  +  ** would help mitigate some XSS and data injection attacks, but will break
          312  +  ** deliberate inclusion of external resources, such as JavaScript syntax
          313  +  ** highlighter scripts.
          314  +  **
          315  +  ** These headers are probably best added by the web server hosting fossil as
          316  +  ** a CGI script.
          317  +  */
   290    318   
   291    319     if( g.isConst ){
   292    320       /* constant means that the input URL will _never_ generate anything
   293    321       ** else. In the case of attachments, the contents won't change because
   294    322       ** an attempt to change them generates a new attachment number. In the
   295    323       ** case of most /getfile calls for specific versions, the only way the
   296    324       ** content changes is if someone breaks the SCM. And if that happens, a
................................................................................
   297    325       ** stale cache is the least of the problem. So we provide an Expires
   298    326       ** header set to a reasonable period (default: one week).
   299    327       */
   300    328       /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/
   301    329       time_t expires = time(0) + 604800;
   302    330       fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires));
   303    331     }else{
   304         -    fprintf(g.httpOut, "Cache-control: no-cache, no-store\r\n");
          332  +    fprintf(g.httpOut, "Cache-control: no-cache\r\n");
   305    333     }
   306    334   
   307    335     /* Content intended for logged in users should only be cached in
   308    336     ** the browser, not some shared location.
   309    337     */
   310    338     fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
   311         -  if( strcmp(zContentType,"application/x-fossil")==0 ){
          339  +  if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
   312    340       cgi_combine_header_and_body();
   313    341       blob_compress(&cgiContent[0], &cgiContent[0]);
   314    342     }
   315    343   
   316    344     if( iReplyStatus != 304 ) {
   317    345       total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
   318    346       fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
................................................................................
   325    353       for(i=0; i<2; i++){
   326    354         size = blob_size(&cgiContent[i]);
   327    355         if( size>0 ){
   328    356           fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut);
   329    357         }
   330    358       }
   331    359     }
          360  +  fflush(g.httpOut);
   332    361     CGIDEBUG(("DONE\n"));
   333    362   }
   334    363   
   335    364   /*
   336    365   ** Do a redirect request to the URL given in the argument.
   337    366   **
   338    367   ** The URL must be relative to the base of the fossil server.
   339    368   */
   340         -void cgi_redirect(const char *zURL){
          369  +NORETURN void cgi_redirect(const char *zURL){
   341    370     char *zLocation;
   342    371     CGIDEBUG(("redirect to %s\n", zURL));
   343         -  if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 || *zURL=='/' ){
          372  +  if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 ){
   344    373       zLocation = mprintf("Location: %s\r\n", zURL);
          374  +  }else if( *zURL=='/' ){
          375  +    int n1 = (int)strlen(g.zBaseURL);
          376  +    int n2 = (int)strlen(g.zTop);
          377  +    if( g.zBaseURL[n1-1]=='/' ) zURL++;
          378  +    zLocation = mprintf("Location: %.*s%s\r\n", n1-n2, g.zBaseURL, zURL);
   345    379     }else{
   346    380       zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL);
   347    381     }
   348    382     cgi_append_header(zLocation);
   349    383     cgi_reset_content();
   350         -  cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zURL);
          384  +  cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zLocation);
   351    385     cgi_set_status(302, "Moved Temporarily");
   352    386     free(zLocation);
   353    387     cgi_reply();
   354         -  exit(0);
          388  +  fossil_exit(0);
   355    389   }
   356         -void cgi_redirectf(const char *zFormat, ...){
          390  +NORETURN void cgi_redirectf(const char *zFormat, ...){
   357    391     va_list ap;
   358    392     va_start(ap, zFormat);
   359    393     cgi_redirect(vmprintf(zFormat, ap));
   360    394     va_end(ap);
   361    395   }
   362    396   
   363    397   /*
................................................................................
   381    415   **
   382    416   ** zName and zValue are not copied and must not change or be
   383    417   ** deallocated after this routine returns.
   384    418   */
   385    419   void cgi_set_parameter_nocopy(const char *zName, const char *zValue){
   386    420     if( nAllocQP<=nUsedQP ){
   387    421       nAllocQP = nAllocQP*2 + 10;
   388         -    aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
   389         -    if( aParamQP==0 ) exit(1);
          422  +    if( nAllocQP>1000 ){
          423  +      /* Prevent a DOS service attack against the framework */
          424  +      fossil_fatal("Too many query parameters");
          425  +    }
          426  +    aParamQP = fossil_realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
   390    427     }
   391    428     aParamQP[nUsedQP].zName = zName;
   392    429     aParamQP[nUsedQP].zValue = zValue;
   393    430     if( g.fHttpTrace ){
   394    431       fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue);
   395    432     }
   396    433     aParamQP[nUsedQP].seq = seqQP++;
................................................................................
   411    448   
   412    449   /*
   413    450   ** Replace a parameter with a new value.
   414    451   */
   415    452   void cgi_replace_parameter(const char *zName, const char *zValue){
   416    453     int i;
   417    454     for(i=0; i<nUsedQP; i++){
   418         -    if( strcmp(aParamQP[i].zName,zName)==0 ){
          455  +    if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
   419    456         aParamQP[i].zValue = zValue;
   420    457         return;
   421    458       }
   422    459     }
   423    460     cgi_set_parameter_nocopy(zName, zValue);
   424    461   }
   425    462   
................................................................................
   457    494   ** should not be deallocated or changed again after this routine
   458    495   ** returns or it will corrupt the parameter table.
   459    496   */
   460    497   static void add_param_list(char *z, int terminator){
   461    498     while( *z ){
   462    499       char *zName;
   463    500       char *zValue;
   464         -    while( isspace(*z) ){ z++; }
          501  +    while( fossil_isspace(*z) ){ z++; }
   465    502       zName = z;
   466    503       while( *z && *z!='=' && *z!=terminator ){ z++; }
   467    504       if( *z=='=' ){
   468    505         *z = 0;
   469    506         z++;
   470    507         zValue = z;
   471    508         while( *z && *z!=terminator ){ z++; }
................................................................................
   474    511           z++;
   475    512         }
   476    513         dehttpize(zValue);
   477    514       }else{
   478    515         if( *z ){ *z++ = 0; }
   479    516         zValue = "";
   480    517       }
   481         -    if( islower(zName[0]) ){
          518  +    if( fossil_islower(zName[0]) ){
   482    519         cgi_set_parameter_nocopy(zName, zValue);
   483    520       }
          521  +#ifdef FOSSIL_ENABLE_JSON
          522  +    json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
          523  +#endif /* FOSSIL_ENABLE_JSON */
   484    524     }
   485    525   }
   486    526   
   487    527   /*
   488    528   ** *pz is a string that consists of multiple lines of text.  This
   489    529   ** routine finds the end of the current line of text and converts
   490    530   ** the "\n" or "\r\n" that ends that line into a "\000".  It then
................................................................................
   569    609   ** '\000' characters are inserted in z[] at the end of each token.
   570    610   ** This routine returns the total number of tokens on the line, 6
   571    611   ** in the example above.
   572    612   */
   573    613   static int tokenize_line(char *z, int mxArg, char **azArg){
   574    614     int i = 0;
   575    615     while( *z ){
   576         -    while( isspace(*z) || *z==';' ){ z++; }
          616  +    while( fossil_isspace(*z) || *z==';' ){ z++; }
   577    617       if( *z=='"' && z[1] ){
   578    618         *z = 0;
   579    619         z++;
   580    620         if( i<mxArg-1 ){ azArg[i++] = z; }
   581    621         while( *z && *z!='"' ){ z++; }
   582    622         if( *z==0 ) break;
   583    623         *z = 0;
   584    624         z++;
   585    625       }else{
   586    626         if( i<mxArg-1 ){ azArg[i++] = z; }
   587         -      while( *z && !isspace(*z) && *z!=';' && *z!='"' ){ z++; }
          627  +      while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; }
   588    628         if( *z && *z!='"' ){
   589    629           *z = 0;
   590    630           z++;
   591    631         }
   592    632       }
   593    633     }
   594    634     azArg[i] = 0;
................................................................................
   615    655   
   616    656     zBoundry = get_line_from_string(&z, &len);
   617    657     if( zBoundry==0 ) return;
   618    658     while( (zLine = get_line_from_string(&z, &len))!=0 ){
   619    659       if( zLine[0]==0 ){
   620    660         int nContent = 0;
   621    661         zValue = get_bounded_content(&z, &len, zBoundry, &nContent);
   622         -      if( zName && zValue && islower(zName[0]) ){
          662  +      if( zName && zValue && fossil_islower(zName[0]) ){
   623    663           cgi_set_parameter_nocopy(zName, zValue);
   624    664           if( showBytes ){
   625    665             cgi_set_parameter_nocopy(mprintf("%s:bytes", zName),
   626    666                  mprintf("%d",nContent));
   627    667           }
   628    668         }
   629    669         zName = 0;
   630    670         showBytes = 0;
   631    671       }else{
   632    672         nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg);
   633    673         for(i=0; i<nArg; i++){
   634         -        int c = tolower(azArg[i][0]);
          674  +        int c = fossil_tolower(azArg[i][0]);
   635    675           int n = strlen(azArg[i]);
   636    676           if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){
   637    677             i++;
   638    678           }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){
   639    679             zName = azArg[++i];
   640    680           }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){
   641    681             char *z = azArg[++i];
   642         -          if( zName && z && islower(zName[0]) ){
          682  +          if( zName && z && fossil_islower(zName[0]) ){
   643    683               cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z);
   644    684             }
   645    685             showBytes = 1;
   646    686           }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){
   647    687             char *z = azArg[++i];
   648         -          if( zName && z && islower(zName[0]) ){
          688  +          if( zName && z && fossil_islower(zName[0]) ){
   649    689               cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z);
   650    690             }
   651    691           }
   652    692         }
   653    693       }
   654    694     }        
   655    695   }
          696  +
          697  +
          698  +#ifdef FOSSIL_ENABLE_JSON
          699  +/*
          700  +** Internal helper for cson_data_source_FILE_n().
          701  +*/
          702  +typedef struct CgiPostReadState_ {
          703  +    FILE * fh;
          704  +    unsigned int len;
          705  +    unsigned int pos;
          706  +} CgiPostReadState;
          707  +
          708  +/*
          709  +** cson_data_source_f() impl which reads only up to
          710  +** a specified amount of data from its input FILE.
          711  +** state MUST be a full populated (CgiPostReadState*).
          712  +*/
          713  +static int cson_data_source_FILE_n( void * state,
          714  +                                    void * dest,
          715  +                                    unsigned int * n ){
          716  +    if( ! state || !dest || !n ) return cson_rc.ArgError;
          717  +    else {
          718  +      CgiPostReadState * st = (CgiPostReadState *)state;
          719  +      if( st->pos >= st->len ){
          720  +        *n = 0;
          721  +        return 0;
          722  +      } else if( !*n || ((st->pos + *n) > st->len) ){
          723  +        return cson_rc.RangeError;
          724  +      }else{
          725  +        unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
          726  +        if( ! rsz ){
          727  +          *n = rsz;
          728  +          return feof(st->fh) ? 0 : cson_rc.IOError;
          729  +        }else{
          730  +          *n = rsz;
          731  +          st->pos += *n;
          732  +          return 0;
          733  +        }
          734  +      }
          735  +    }
          736  +}
          737  +
          738  +/*
          739  +** Reads a JSON object from the first contentLen bytes of zIn.  On
          740  +** g.json.post is updated to hold the content. On error a
          741  +** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
          742  +** called (in HTTP mode exit code 0 is used).
          743  +**
          744  +** If contentLen is 0 then the whole file is read.
          745  +*/
          746  +void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
          747  +  cson_value * jv = NULL;
          748  +  int rc;
          749  +  CgiPostReadState state;
          750  +  cson_parse_opt popt = cson_parse_opt_empty;
          751  +  cson_parse_info pinfo = cson_parse_info_empty;
          752  +  popt.maxDepth = 15;
          753  +  state.fh = zIn;
          754  +  state.len = contentLen;
          755  +  state.pos = 0;
          756  +  rc = cson_parse( &jv,
          757  +                   contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
          758  +                   contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo );
          759  +  if(rc){
          760  +    goto invalidRequest;
          761  +  }else{
          762  +    json_gc_add( "POST.JSON", jv );
          763  +    g.json.post.v = jv;
          764  +    g.json.post.o = cson_value_get_object( jv );
          765  +    if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
          766  +      goto invalidRequest;
          767  +    }
          768  +  }
          769  +  return;
          770  +  invalidRequest:
          771  +  cgi_set_content_type(json_guess_content_type());
          772  +  if(0 != pinfo.errorCode){ /* fancy error message */
          773  +      char * msg = mprintf("JSON parse error at line %u, column %u, "
          774  +                           "byte offset %u: %s",
          775  +                           pinfo.line, pinfo.col, pinfo.length,
          776  +                           cson_rc_string(pinfo.errorCode));
          777  +      json_err( FSL_JSON_E_INVALID_REQUEST, msg, 1 );
          778  +      free(msg);
          779  +  }else if(jv && !g.json.post.o){
          780  +      json_err( FSL_JSON_E_INVALID_REQUEST,
          781  +                "Request envelope must be a JSON Object (not array).", 1 );
          782  +  }else{ /* generic error message */
          783  +      json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
          784  +  }
          785  +  fossil_exit( g.isHTTP ? 0 : 1);
          786  +}
          787  +#endif /* FOSSIL_ENABLE_JSON */
          788  +
          789  +/*
          790  +** Log HTTP traffic to a file.  Begin the log on first use.  Close the log
          791  +** when the argument is NULL.
          792  +*/
          793  +void cgi_trace(const char *z){
          794  +  static FILE *pLog = 0;
          795  +  if( g.fHttpTrace==0 ) return;
          796  +  if( z==0 ){
          797  +    if( pLog ) fclose(pLog);
          798  +    pLog = 0;
          799  +    return;
          800  +  }
          801  +  if( pLog==0 ){
          802  +    char zFile[50];
          803  +    unsigned r;
          804  +    sqlite3_randomness(sizeof(r), &r);
          805  +    sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r);
          806  +    pLog = fossil_fopen(zFile, "wb");
          807  +    if( pLog ){
          808  +      fprintf(stderr, "# open log on %s\n", zFile);
          809  +    }else{
          810  +      fprintf(stderr, "# failed to open %s\n", zFile);
          811  +      return;
          812  +    }
          813  +  }
          814  +  fputs(z, pLog);
          815  +}
          816  +
   656    817   
   657    818   /*
   658    819   ** Initialize the query parameter database.  Information is pulled from
   659    820   ** the QUERY_STRING environment variable (if it exists), from standard
   660    821   ** input if there is POST data, and from HTTP_COOKIE.
   661    822   */
   662    823   void cgi_init(void){
   663    824     char *z;
   664    825     const char *zType;
   665    826     int len;
          827  +#ifdef FOSSIL_ENABLE_JSON
          828  +  json_main_bootstrap();
          829  +#endif
          830  +  g.isHTTP = 1;
   666    831     cgi_destination(CGI_BODY);
          832  +
          833  +  z = (char*)P("HTTP_COOKIE");
          834  +  if( z ){
          835  +    z = mprintf("%s",z);
          836  +    add_param_list(z, ';');
          837  +  }
          838  +  
   667    839     z = (char*)P("QUERY_STRING");
   668    840     if( z ){
   669    841       z = mprintf("%s",z);
   670    842       add_param_list(z, '&');
   671    843     }
   672    844   
   673    845     z = (char*)P("REMOTE_ADDR");
   674         -  if( z ) g.zIpAddr = mprintf("%s", z);
          846  +  if( z ){
          847  +    g.zIpAddr = mprintf("%s", z);
          848  +  }
   675    849   
   676    850     len = atoi(PD("CONTENT_LENGTH", "0"));
   677    851     g.zContentType = zType = P("CONTENT_TYPE");
   678    852     if( len>0 && zType ){
   679    853       blob_zero(&g.cgiIn);
   680         -    if( strcmp(zType,"application/x-www-form-urlencoded")==0 
          854  +    if( fossil_strcmp(zType,"application/x-www-form-urlencoded")==0 
   681    855            || strncmp(zType,"multipart/form-data",19)==0 ){
   682         -      z = malloc( len+1 );
   683         -      if( z==0 ) exit(1);
          856  +      z = fossil_malloc( len+1 );
   684    857         len = fread(z, 1, len, g.httpIn);
   685    858         z[len] = 0;
          859  +      cgi_trace(z);
   686    860         if( zType[0]=='a' ){
   687    861           add_param_list(z, '&');
   688    862         }else{
   689    863           process_multipart_form_data(z, len);
   690    864         }
   691         -    }else if( strcmp(zType, "application/x-fossil")==0 ){
          865  +    }else if( fossil_strcmp(zType, "application/x-fossil")==0 ){
   692    866         blob_read_from_channel(&g.cgiIn, g.httpIn, len);
   693    867         blob_uncompress(&g.cgiIn, &g.cgiIn);
   694         -    }else if( strcmp(zType, "application/x-fossil-debug")==0 ){
          868  +    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
          869  +      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
          870  +    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
   695    871         blob_read_from_channel(&g.cgiIn, g.httpIn, len);
   696    872       }
          873  +#ifdef FOSSIL_ENABLE_JSON
          874  +    else if( fossil_strcmp(zType, "application/json")
          875  +              || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
          876  +              || fossil_strcmp(zType,"application/javascript")){
          877  +      g.json.isJsonMode = 1;
          878  +      cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
          879  +      /* FIXMEs:
          880  +
          881  +      - See if fossil really needs g.cgiIn to be set for this purpose
          882  +      (i don't think it does). If it does then fill g.cgiIn and
          883  +      refactor to parse the JSON from there.
          884  +      
          885  +      - After parsing POST JSON, copy the "first layer" of keys/values
          886  +      to cgi_setenv(), honoring the upper-case distinction used
          887  +      in add_param_list(). However...
          888  +
          889  +      - If we do that then we might get a disconnect in precedence of
          890  +      GET/POST arguments. i prefer for GET entries to take precedence
          891  +      over like-named POST entries, but in order for that to happen we
          892  +      need to process QUERY_STRING _after_ reading the POST data.
          893  +      */
          894  +      cgi_set_content_type(json_guess_content_type());
          895  +    }
          896  +#endif /* FOSSIL_ENABLE_JSON */
   697    897     }
   698    898   
   699         -  z = (char*)P("HTTP_COOKIE");
   700         -  if( z ){
   701         -    z = mprintf("%s",z);
   702         -    add_param_list(z, ';');
   703         -  }
   704    899   }
   705    900   
   706    901   /*
   707    902   ** This is the comparison function used to sort the aParamQP[] array of
   708    903   ** query parameters and cookies.
   709    904   */
   710    905   static int qparam_compare(const void *a, const void *b){
   711    906     struct QParam *pA = (struct QParam*)a;
   712    907     struct QParam *pB = (struct QParam*)b;
   713    908     int c;
   714         -  c = strcmp(pA->zName, pB->zName);
          909  +  c = fossil_strcmp(pA->zName, pB->zName);
   715    910     if( c==0 ){
   716    911       c = pA->seq - pB->seq;
   717    912     }
   718    913     return c;
   719    914   }
   720    915   
   721    916   /*
................................................................................
   736    931       qsort(aParamQP, nUsedQP, sizeof(aParamQP[0]), qparam_compare);
   737    932       sortQP = 0;
   738    933       /* After sorting, remove duplicate parameters.  The secondary sort
   739    934       ** key is aParamQP[].seq and we keep the first entry.  That means
   740    935       ** with duplicate calls to cgi_set_parameter() the second and
   741    936       ** subsequent calls are effectively no-ops. */
   742    937       for(i=j=1; i<nUsedQP; i++){
   743         -      if( strcmp(aParamQP[i].zName,aParamQP[i-1].zName)==0 ){
          938  +      if( fossil_strcmp(aParamQP[i].zName,aParamQP[i-1].zName)==0 ){
   744    939           continue;
   745    940         }
   746    941         if( j<i ){
   747    942           memcpy(&aParamQP[j], &aParamQP[i], sizeof(aParamQP[j]));
   748    943         }
   749    944         j++;
   750    945       }
................................................................................
   752    947     }
   753    948   
   754    949     /* Do a binary search for a matching query parameter */
   755    950     lo = 0;
   756    951     hi = nUsedQP-1;
   757    952     while( lo<=hi ){
   758    953       mid = (lo+hi)/2;
   759         -    c = strcmp(aParamQP[mid].zName, zName);
          954  +    c = fossil_strcmp(aParamQP[mid].zName, zName);
   760    955       if( c==0 ){
   761    956         CGIDEBUG(("mem-match [%s] = [%s]\n", zName, aParamQP[mid].zValue));
   762    957         return aParamQP[mid].zValue;
   763    958       }else if( c>0 ){
   764    959         hi = mid-1;
   765    960       }else{
   766    961         lo = mid+1;
................................................................................
   767    962       }
   768    963     }
   769    964   
   770    965     /* If no match is found and the name begins with an upper-case
   771    966     ** letter, then check to see if there is an environment variable
   772    967     ** with the given name.
   773    968     */
   774         -  if( isupper(zName[0]) ){
   775         -    const char *zValue = getenv(zName);
          969  +  if( fossil_isupper(zName[0]) ){
          970  +    const char *zValue = fossil_getenv(zName);
   776    971       if( zValue ){
   777    972         cgi_set_parameter_nocopy(zName, zValue);
   778    973         CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
   779    974         return zValue;
   780    975       }
   781    976     }
   782    977     CGIDEBUG(("no-match [%s]\n", zName));
   783    978     return zDefault;
   784    979   }
          980  +
          981  +/*
          982  +** Return the value of a CGI parameter with leading and trailing
          983  +** spaces removed.
          984  +*/
          985  +char *cgi_parameter_trimmed(const char *zName, const char *zDefault){
          986  +  const char *zIn;
          987  +  char *zOut;
          988  +  int i;
          989  +  zIn = cgi_parameter(zName, 0);
          990  +  if( zIn==0 ) zIn = zDefault;
          991  +  while( fossil_isspace(zIn[0]) ) zIn++;
          992  +  zOut = fossil_strdup(zIn);
          993  +  for(i=0; zOut[i]; i++){}
          994  +  while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
          995  +  return zOut;
          996  +}
   785    997   
   786    998   /*
   787    999   ** Return the name of the i-th CGI parameter.  Return NULL if there
   788         -** are fewer than i registered CGI parmaeters.
         1000  +** are fewer than i registered CGI parameters.
   789   1001   */
   790   1002   const char *cgi_parameter_name(int i){
   791   1003     if( i>=0 && i<nUsedQP ){
   792   1004       return aParamQP[i].zName;
   793   1005     }else{
   794   1006       return 0;
   795   1007     }
................................................................................
   839   1051     va_end(ap);
   840   1052     return 1;
   841   1053   }
   842   1054   
   843   1055   /*
   844   1056   ** Print all query parameters on standard output.  Format the
   845   1057   ** parameters as HTML.  This is used for testing and debugging.
         1058  +**
         1059  +** Omit the values of the cookies unless showAll is true.
   846   1060   */
   847         -void cgi_print_all(void){
         1061  +void cgi_print_all(int showAll){
   848   1062     int i;
   849   1063     cgi_parameter("","");  /* Force the parameters into sorted order */
   850   1064     for(i=0; i<nUsedQP; i++){
   851         -    cgi_printf("%s = %s  <br />\n",
   852         -       htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
   853         -  }
   854         -}
   855         -
   856         -/*
   857         -** Write HTML text for an option menu to standard output.  zParam
   858         -** is the query parameter that the option menu sets.  zDflt is the
   859         -** initial value of the option menu.  Addition arguments are name/value
   860         -** pairs that define values on the menu.  The list is terminated with
   861         -** a single NULL argument.
   862         -*/
   863         -void cgi_optionmenu(int in, const char *zP, const char *zD, ...){
   864         -  va_list ap;
   865         -  char *zName, *zVal;
   866         -  int dfltSeen = 0;
   867         -  cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
   868         -  va_start(ap, zD);
   869         -  while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
   870         -    if( strcmp(zVal,zD)==0 ){ dfltSeen = 1; break; }
   871         -  }
   872         -  va_end(ap);
   873         -  if( !dfltSeen ){
   874         -    if( zD[0] ){
   875         -      cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
   876         -        in+2, "", zD, zD);
   877         -    }else{
   878         -      cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n", in+2, "");
         1065  +    const char *zName = aParamQP[i].zName;
         1066  +    if( !showAll ){
         1067  +      if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
         1068  +      if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
   879   1069       }
         1070  +    cgi_printf("%h = %h  <br />\n", zName, aParamQP[i].zValue);
   880   1071     }
   881         -  va_start(ap, zD);
   882         -  while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
   883         -    if( zName[0] ){
   884         -      cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
   885         -        in+2, "",
   886         -        zVal,
   887         -        strcmp(zVal, zD) ? "" : " selected",
   888         -        zName
   889         -      );
   890         -    }else{
   891         -      cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
   892         -        in+2, "",
   893         -        strcmp(zVal, zD) ? "" : " selected"
   894         -      );
   895         -    }
   896         -  }
   897         -  va_end(ap);
   898         -  cgi_printf("%*s</select>\n", in, "");
   899         -}
   900         -
   901         -/*
   902         -** This routine works a lot like cgi_optionmenu() except that the list of
   903         -** values is contained in an array.  Also, the values are just values, not
   904         -** name/value pairs as in cgi_optionmenu.
   905         -*/
   906         -void cgi_v_optionmenu(
   907         -  int in,              /* Indent by this amount */
   908         -  const char *zP,      /* The query parameter name */
   909         -  const char *zD,      /* Default value */
   910         -  const char **az      /* NULL-terminated list of allowed values */
   911         -){
   912         -  const char *zVal;
   913         -  int i;
   914         -  cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
   915         -  for(i=0; az[i]; i++){
   916         -    if( strcmp(az[i],zD)==0 ) break;
   917         -  }
   918         -  if( az[i]==0 ){
   919         -    if( zD[0]==0 ){
   920         -      cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
   921         -       in+2, "");
   922         -    }else{
   923         -      cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
   924         -       in+2, "", zD, zD);
   925         -    }
   926         -  }
   927         -  while( (zVal = *(az++))!=0  ){
   928         -    if( zVal[0] ){
   929         -      cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
   930         -        in+2, "",
   931         -        zVal,
   932         -        strcmp(zVal, zD) ? "" : " selected",
   933         -        zVal
   934         -      );
   935         -    }else{
   936         -      cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
   937         -        in+2, "",
   938         -        strcmp(zVal, zD) ? "" : " selected"
   939         -      );
   940         -    }
   941         -  }
   942         -  cgi_printf("%*s</select>\n", in, "");
   943         -}
   944         -
   945         -/*
   946         -** This routine works a lot like cgi_v_optionmenu() except that the list
   947         -** is a list of pairs.  The first element of each pair is the value used
   948         -** internally and the second element is the value displayed to the user.
   949         -*/
   950         -void cgi_v_optionmenu2(
   951         -  int in,              /* Indent by this amount */
   952         -  const char *zP,      /* The query parameter name */
   953         -  const char *zD,      /* Default value */
   954         -  const char **az      /* NULL-terminated list of allowed values */
   955         -){
   956         -  const char *zVal;
   957         -  int i;
   958         -  cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
   959         -  for(i=0; az[i]; i+=2){
   960         -    if( strcmp(az[i],zD)==0 ) break;
   961         -  }
   962         -  if( az[i]==0 ){
   963         -    if( zD[0]==0 ){
   964         -      cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
   965         -       in+2, "");
   966         -    }else{
   967         -      cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
   968         -       in+2, "", zD, zD);
   969         -    }
   970         -  }
   971         -  while( (zVal = *(az++))!=0  ){
   972         -    const char *zName = *(az++);
   973         -    if( zName[0] ){
   974         -      cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
   975         -        in+2, "",
   976         -        zVal,
   977         -        strcmp(zVal, zD) ? "" : " selected",
   978         -        zName
   979         -      );
   980         -    }else{
   981         -      cgi_printf("%*s<option value=\"%h\"%s>&nbsp;</option>\n",
   982         -        in+2, "",
   983         -        zVal,
   984         -        strcmp(zVal, zD) ? "" : " selected"
   985         -      );
   986         -    }
   987         -  }
   988         -  cgi_printf("%*s</select>\n", in, "");
   989   1072   }
   990   1073   
   991   1074   /*
   992   1075   ** This routine works like "printf" except that it has the
   993   1076   ** extra formatting capabilities such as %h and %t.
   994   1077   */
   995   1078   void cgi_printf(const char *zFormat, ...){
................................................................................
  1007   1090     vxprintf(pContent,zFormat,ap);
  1008   1091   }
  1009   1092   
  1010   1093   
  1011   1094   /*
  1012   1095   ** Send a reply indicating that the HTTP request was malformed
  1013   1096   */
  1014         -static void malformed_request(void){
         1097  +static NORETURN void malformed_request(void){
  1015   1098     cgi_set_status(501, "Not Implemented");
  1016   1099     cgi_printf(
  1017   1100       "<html><body>Unrecognized HTTP Request</body></html>\n"
  1018   1101     );
  1019   1102     cgi_reply();
  1020         -  exit(0);
         1103  +  fossil_exit(0);
  1021   1104   }
  1022   1105   
  1023   1106   /*
  1024   1107   ** Panic and die while processing a webpage.
  1025   1108   */
  1026         -void cgi_panic(const char *zFormat, ...){
         1109  +NORETURN void cgi_panic(const char *zFormat, ...){
  1027   1110     va_list ap;
  1028   1111     cgi_reset_content();
  1029         -  cgi_set_status(500, "Internal Server Error");
  1030         -  cgi_printf(
  1031         -    "<html><body><h1>Internal Server Error</h1>\n"
  1032         -    "<plaintext>"
  1033         -  );
  1034         -  va_start(ap, zFormat);
  1035         -  vxprintf(pContent,zFormat,ap);
  1036         -  va_end(ap);
  1037         -  cgi_reply();
  1038         -  exit(1);
         1112  +#ifdef FOSSIL_ENABLE_JSON
         1113  +  if( g.json.isJsonMode ){
         1114  +    char * zMsg;
         1115  +    va_start(ap, zFormat);
         1116  +    zMsg = vmprintf(zFormat,ap);
         1117  +    va_end(ap);
         1118  +    json_err( FSL_JSON_E_PANIC, zMsg, 1 );
         1119  +    free(zMsg);
         1120  +    fossil_exit( g.isHTTP ? 0 : 1 );
         1121  +  }else
         1122  +#endif /* FOSSIL_ENABLE_JSON */
         1123  +  {
         1124  +    cgi_set_status(500, "Internal Server Error");
         1125  +    cgi_printf(
         1126  +               "<html><body><h1>Internal Server Error</h1>\n"
         1127  +               "<plaintext>"
         1128  +               );
         1129  +    va_start(ap, zFormat);
         1130  +    vxprintf(pContent,zFormat,ap);
         1131  +    va_end(ap);
         1132  +    cgi_reply();
         1133  +    fossil_exit(1);
         1134  +  }
  1039   1135   }
  1040   1136   
  1041   1137   /*
  1042   1138   ** Remove the first space-delimited token from a string and return
  1043   1139   ** a pointer to it.  Add a NULL to the string to terminate the token.
  1044   1140   ** Make *zLeftOver point to the start of the next token.
  1045   1141   */
  1046   1142   static char *extract_token(char *zInput, char **zLeftOver){
  1047   1143     char *zResult = 0;
  1048   1144     if( zInput==0 ){
  1049   1145       if( zLeftOver ) *zLeftOver = 0;
  1050   1146       return 0;
  1051   1147     }
  1052         -  while( isspace(*zInput) ){ zInput++; }
         1148  +  while( fossil_isspace(*zInput) ){ zInput++; }
  1053   1149     zResult = zInput;
  1054         -  while( *zInput && !isspace(*zInput) ){ zInput++; }
         1150  +  while( *zInput && !fossil_isspace(*zInput) ){ zInput++; }
  1055   1151     if( *zInput ){
  1056   1152       *zInput = 0;
  1057   1153       zInput++;
  1058         -    while( isspace(*zInput) ){ zInput++; }
         1154  +    while( fossil_isspace(*zInput) ){ zInput++; }
  1059   1155     }
  1060   1156     if( zLeftOver ){ *zLeftOver = zInput; }
  1061   1157     return zResult;
  1062   1158   }
  1063   1159   
  1064   1160   /*
  1065   1161   ** This routine handles a single HTTP request which is coming in on
  1066         -** standard input and which replies on standard output.
         1162  +** g.httpIn and which replies on g.httpOut
  1067   1163   **
  1068         -** The HTTP request is read from standard input and is used to initialize
  1069         -** environment variables as per CGI.  The cgi_init() routine to complete
         1164  +** The HTTP request is read from g.httpIn and is used to initialize
         1165  +** entries in the cgi_parameter() hash, as if those entries were
         1166  +** environment variables.  A call to cgi_init() completes
  1070   1167   ** the setup.  Once all the setup is finished, this procedure returns
  1071   1168   ** and subsequent code handles the actual generation of the webpage.
  1072   1169   */
  1073   1170   void cgi_handle_http_request(const char *zIpAddr){
  1074   1171     char *z, *zToken;
  1075   1172     int i;
  1076   1173     struct sockaddr_in remoteName;
  1077         -  size_t size = sizeof(struct sockaddr_in);
         1174  +  socklen_t size = sizeof(struct sockaddr_in);
  1078   1175     char zLine[2000];     /* A single line of input. */
  1079         -
  1080   1176     g.fullHttpReply = 1;
  1081   1177     if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
  1082   1178       malformed_request();
  1083   1179     }
         1180  +  cgi_trace(zLine);
  1084   1181     zToken = extract_token(zLine, &z);
  1085   1182     if( zToken==0 ){
  1086   1183       malformed_request();
  1087   1184     }
  1088         -  if( strcmp(zToken,"GET")!=0 && strcmp(zToken,"POST")!=0
  1089         -      && strcmp(zToken,"HEAD")!=0 ){
         1185  +  if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
         1186  +      && fossil_strcmp(zToken,"HEAD")!=0 ){
  1090   1187       malformed_request();
  1091   1188     }
  1092   1189     cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
  1093   1190     cgi_setenv("REQUEST_METHOD",zToken);
  1094   1191     zToken = extract_token(z, &z);
  1095   1192     if( zToken==0 ){
  1096   1193       malformed_request();
................................................................................
  1098   1195     cgi_setenv("REQUEST_URI", zToken);
  1099   1196     for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  1100   1197     if( zToken[i] ) zToken[i++] = 0;
  1101   1198     cgi_setenv("PATH_INFO", zToken);
  1102   1199     cgi_setenv("QUERY_STRING", &zToken[i]);
  1103   1200     if( zIpAddr==0 &&
  1104   1201           getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, 
  1105         -                                (socklen_t*)&size)>=0
         1202  +                                &size)>=0
  1106   1203     ){
  1107   1204       zIpAddr = inet_ntoa(remoteName.sin_addr);
  1108   1205     }
  1109   1206     if( zIpAddr ){   
  1110   1207       cgi_setenv("REMOTE_ADDR", zIpAddr);
  1111   1208       g.zIpAddr = mprintf("%s", zIpAddr);
  1112   1209     }
................................................................................
  1113   1210    
  1114   1211     /* Get all the optional fields that follow the first line.
  1115   1212     */
  1116   1213     while( fgets(zLine,sizeof(zLine),g.httpIn) ){
  1117   1214       char *zFieldName;
  1118   1215       char *zVal;
  1119   1216   
         1217  +    cgi_trace(zLine);
  1120   1218       zFieldName = extract_token(zLine,&zVal);
  1121   1219       if( zFieldName==0 || *zFieldName==0 ) break;
  1122         -    while( isspace(*zVal) ){ zVal++; }
         1220  +    while( fossil_isspace(*zVal) ){ zVal++; }
  1123   1221       i = strlen(zVal);
  1124         -    while( i>0 && isspace(zVal[i-1]) ){ i--; }
         1222  +    while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
  1125   1223       zVal[i] = 0;
  1126         -    for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
  1127         -    if( strcmp(zFieldName,"user-agent:")==0 ){
  1128         -      cgi_setenv("HTTP_USER_AGENT", zVal);
  1129         -    }else if( strcmp(zFieldName,"content-length:")==0 ){
         1224  +    for(i=0; zFieldName[i]; i++){
         1225  +      zFieldName[i] = fossil_tolower(zFieldName[i]);
         1226  +    }
         1227  +    if( fossil_strcmp(zFieldName,"content-length:")==0 ){
  1130   1228         cgi_setenv("CONTENT_LENGTH", zVal);
  1131         -    }else if( strcmp(zFieldName,"referer:")==0 ){
  1132         -      cgi_setenv("HTTP_REFERER", zVal);
  1133         -    }else if( strcmp(zFieldName,"host:")==0 ){
         1229  +    }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
         1230  +      cgi_setenv("CONTENT_TYPE", zVal);
         1231  +    }else if( fossil_strcmp(zFieldName,"cookie:")==0 ){
         1232  +      cgi_setenv("HTTP_COOKIE", zVal);
         1233  +    }else if( fossil_strcmp(zFieldName,"https:")==0 ){
         1234  +      cgi_setenv("HTTPS", zVal);
         1235  +    }else if( fossil_strcmp(zFieldName,"host:")==0 ){
  1134   1236         cgi_setenv("HTTP_HOST", zVal);
  1135         -    }else if( strcmp(zFieldName,"content-type:")==0 ){
  1136         -      cgi_setenv("CONTENT_TYPE", zVal);
  1137         -    }else if( strcmp(zFieldName,"cookie:")==0 ){
  1138         -      cgi_setenv("HTTP_COOKIE", zVal);
  1139         -    }else if( strcmp(zFieldName,"if-none-match:")==0 ){
         1237  +    }else if( fossil_strcmp(zFieldName,"if-none-match:")==0 ){
  1140   1238         cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
  1141         -    }else if( strcmp(zFieldName,"if-modified-since:")==0 ){
         1239  +    }else if( fossil_strcmp(zFieldName,"if-modified-since:")==0 ){
  1142   1240         cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
         1241  +#if 0
         1242  +    }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
         1243  +      cgi_setenv("HTTP_REFERER", zVal);
         1244  +#endif
         1245  +    }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
         1246  +      cgi_setenv("HTTP_USER_AGENT", zVal);
  1143   1247       }
  1144   1248     }
  1145         -
  1146   1249     cgi_init();
         1250  +  cgi_trace(0);
  1147   1251   }
  1148   1252   
         1253  +#if INTERFACE
         1254  +/* 
         1255  +** Bitmap values for the flags parameter to cgi_http_server().
         1256  +*/
         1257  +#define HTTP_SERVER_LOCALHOST      0x0001     /* Bind to 127.0.0.1 only */
         1258  +
         1259  +#endif /* INTERFACE */
         1260  +
  1149   1261   /*
  1150   1262   ** Maximum number of child processes that we can have running
  1151   1263   ** at one time before we start slowing things down.
  1152   1264   */
  1153   1265   #define MAX_PARALLEL 2
  1154   1266   
  1155   1267   /*
................................................................................
  1158   1270   ** As new connections arrive, fork a child and let child return
  1159   1271   ** out of this procedure call.  The child will handle the request.
  1160   1272   ** The parent never returns from this procedure.
  1161   1273   **
  1162   1274   ** Return 0 to each child as it runs.  If unable to establish a
  1163   1275   ** listening socket, return non-zero.
  1164   1276   */
  1165         -int cgi_http_server(int mnPort, int mxPort, char *zBrowser){
  1166         -#ifdef __MINGW32__
         1277  +int cgi_http_server(int mnPort, int mxPort, char *zBrowser, int flags){
         1278  +#if defined(_WIN32)
  1167   1279     /* Use win32_http_server() instead */
  1168         -  exit(1);
         1280  +  fossil_exit(1);
  1169   1281   #else
  1170   1282     int listener = -1;           /* The server socket */
  1171   1283     int connection;              /* A socket for each individual connection */
  1172   1284     fd_set readfds;              /* Set of file descriptors for select() */
  1173         -  size_t lenaddr;              /* Length of the inaddr structure */
         1285  +  socklen_t lenaddr;           /* Length of the inaddr structure */
  1174   1286     int child;                   /* PID of the child process */
  1175   1287     int nchildren = 0;           /* Number of child processes */
  1176   1288     struct timeval delay;        /* How long to wait inside select() */
  1177   1289     struct sockaddr_in inaddr;   /* The socket address */
  1178   1290     int opt = 1;                 /* setsockopt flag */
  1179   1291     int iPort = mnPort;
  1180   1292   
  1181   1293     while( iPort<=mxPort ){
  1182   1294       memset(&inaddr, 0, sizeof(inaddr));
  1183   1295       inaddr.sin_family = AF_INET;
  1184         -    inaddr.sin_addr.s_addr = INADDR_ANY;
         1296  +    if( flags & HTTP_SERVER_LOCALHOST ){
         1297  +      inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
         1298  +    }else{
         1299  +      inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
         1300  +    }
  1185   1301       inaddr.sin_port = htons(iPort);
  1186   1302       listener = socket(AF_INET, SOCK_STREAM, 0);
  1187   1303       if( listener<0 ){
  1188   1304         iPort++;
  1189   1305         continue;
  1190   1306       }
  1191   1307   
................................................................................
  1206   1322         fossil_fatal("unable to open listening socket on any"
  1207   1323                      " port in the range %d..%d", mnPort, mxPort);
  1208   1324       }
  1209   1325     }
  1210   1326     if( iPort>mxPort ) return 1;
  1211   1327     listen(listener,10);
  1212   1328     if( iPort>mnPort ){
  1213         -    printf("Listening for HTTP requests on TCP port %d\n", iPort);
         1329  +    fossil_print("Listening for HTTP requests on TCP port %d\n", iPort);
  1214   1330       fflush(stdout);
  1215   1331     }
  1216   1332     if( zBrowser ){
  1217   1333       zBrowser = mprintf(zBrowser, iPort);
  1218         -    system(zBrowser);
         1334  +    if( system(zBrowser)<0 ){
         1335  +      fossil_warning("cannot start browser: %s\n", zBrowser);
         1336  +    }
  1219   1337     }
  1220   1338     while( 1 ){
  1221   1339       if( nchildren>MAX_PARALLEL ){
  1222   1340         /* Slow down if connections are arriving too fast */
  1223   1341         sleep( nchildren-MAX_PARALLEL );
  1224   1342       }
  1225   1343       delay.tv_sec = 60;
  1226   1344       delay.tv_usec = 0;
  1227   1345       FD_ZERO(&readfds);
         1346  +    assert( listener>=0 );
  1228   1347       FD_SET( listener, &readfds);
  1229         -    if( select( listener+1, &readfds, 0, 0, &delay) ){
         1348  +    select( listener+1, &readfds, 0, 0, &delay);
         1349  +    if( FD_ISSET(listener, &readfds) ){
  1230   1350         lenaddr = sizeof(inaddr);
  1231         -      connection = accept(listener, (struct sockaddr*)&inaddr,
  1232         -                                    (socklen_t*) &lenaddr);
         1351  +      connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
  1233   1352         if( connection>=0 ){
  1234   1353           child = fork();
  1235   1354           if( child!=0 ){
  1236   1355             if( child>0 ) nchildren++;
  1237   1356             close(connection);
  1238   1357           }else{
         1358  +          int nErr = 0, fd;
  1239   1359             close(0);
  1240         -          dup(connection);
         1360  +          fd = dup(connection);
         1361  +          if( fd!=0 ) nErr++;
  1241   1362             close(1);
  1242         -          dup(connection);
         1363  +          fd = dup(connection);
         1364  +          if( fd!=1 ) nErr++;
  1243   1365             if( !g.fHttpTrace && !g.fSqlTrace ){
  1244   1366               close(2);
  1245         -            dup(connection);
         1367  +            fd = dup(connection);
         1368  +            if( fd!=2 ) nErr++;
  1246   1369             }
  1247   1370             close(connection);
  1248         -          return 0;
         1371  +          return nErr;
  1249   1372           }
  1250   1373         }
  1251   1374       }
  1252   1375       /* Bury dead children */
  1253   1376       while( waitpid(0, 0, WNOHANG)>0 ){
  1254   1377         nchildren--;
  1255   1378       }
  1256   1379     }
  1257   1380     /* NOT REACHED */  
  1258         -  exit(1);
         1381  +  fossil_exit(1);
  1259   1382   #endif
         1383  +  /* NOT REACHED */
         1384  +  return 0;
  1260   1385   }
  1261   1386   
  1262   1387   
  1263   1388   /*
  1264   1389   ** Name of days and months.
  1265   1390   */
  1266         -static const char *azDays[] =
         1391  +static const char *const azDays[] =
  1267   1392       {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0};
  1268         -static const char *azMonths[] =
         1393  +static const char *const azMonths[] =
  1269   1394       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1270   1395        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0};
  1271   1396   
  1272   1397   
  1273   1398   /*
  1274   1399   ** Returns an RFC822-formatted time string suitable for HTTP headers.
  1275   1400   ** The timezone is always GMT.  The value returned is always a
................................................................................
  1306   1431     memset(&t, 0, sizeof(t));
  1307   1432     if( 7==sscanf(zDate, "%12[A-Za-z,] %d %12[A-Za-z] %d %d:%d:%d", zIgnore,
  1308   1433                          &t.tm_mday, zMonth, &t.tm_year, &t.tm_hour, &t.tm_min,
  1309   1434                          &t.tm_sec)){
  1310   1435   
  1311   1436       if( t.tm_year > 1900 ) t.tm_year -= 1900;
  1312   1437       for(t.tm_mon=0; azMonths[t.tm_mon]; t.tm_mon++){
  1313         -      if( !strncasecmp( azMonths[t.tm_mon], zMonth, 3 )){
         1438  +      if( !fossil_strnicmp( azMonths[t.tm_mon], zMonth, 3 )){
  1314   1439           return mkgmtime(&t);
  1315   1440         }
  1316   1441       }
  1317   1442     }
  1318   1443   
  1319   1444     return 0;
  1320   1445   }
................................................................................
  1354   1479   void cgi_modified_since(time_t objectTime){
  1355   1480     const char *zIf = P("HTTP_IF_MODIFIED_SINCE");
  1356   1481     if( zIf==0 ) return;
  1357   1482     if( objectTime > cgi_rfc822_parsedate(zIf) ) return;
  1358   1483     cgi_set_status(304,"Not Modified");
  1359   1484     cgi_reset_content();
  1360   1485     cgi_reply();
  1361         -  exit(0);
         1486  +  fossil_exit(0);
  1362   1487   }

Changes to src/checkin.c.

    30     30   **
    31     31   ** If missingIsFatal is true, then any files that are missing or which
    32     32   ** are not true files results in a fatal error.
    33     33   */
    34     34   static void status_report(
    35     35     Blob *report,          /* Append the status report here */
    36     36     const char *zPrefix,   /* Prefix on each line of the report */
    37         -  int missingIsFatal     /* MISSING and NOT_A_FILE are fatal errors */
           37  +  int missingIsFatal,    /* MISSING and NOT_A_FILE are fatal errors */
           38  +  int cwdRelative        /* Report relative to the current working dir */
    38     39   ){
    39     40     Stmt q;
    40     41     int nPrefix = strlen(zPrefix);
    41     42     int nErr = 0;
    42         -  db_prepare(&q, 
           43  +  Blob rewrittenPathname;
           44  +  db_prepare(&q,
    43     45       "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
    44     46       "  FROM vfile "
    45         -    " WHERE file_is_selected(id)"
           47  +    " WHERE is_selected(id)"
    46     48       "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1"
    47     49     );
           50  +  blob_zero(&rewrittenPathname);
    48     51     while( db_step(&q)==SQLITE_ROW ){
    49     52       const char *zPathname = db_column_text(&q,0);
           53  +    const char *zDisplayName = zPathname;
    50     54       int isDeleted = db_column_int(&q, 1);
    51     55       int isChnged = db_column_int(&q,2);
    52     56       int isNew = db_column_int(&q,3)==0;
    53     57       int isRenamed = db_column_int(&q,4);
    54         -    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
           58  +    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
           59  +    if( cwdRelative ){
           60  +      file_relative_name(zFullName, &rewrittenPathname, 0);
           61  +      zDisplayName = blob_str(&rewrittenPathname);
           62  +      if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){
           63  +        zDisplayName += 2;  /* no unnecessary ./ prefix */
           64  +      }
           65  +    }
    55     66       blob_append(report, zPrefix, nPrefix);
    56     67       if( isDeleted ){
    57         -      blob_appendf(report, "DELETED    %s\n", zPathname);
    58         -    }else if( !file_isfile(zFullName) ){
    59         -      if( access(zFullName, 0)==0 ){
    60         -        blob_appendf(report, "NOT_A_FILE %s\n", zPathname);
           68  +      blob_appendf(report, "DELETED    %s\n", zDisplayName);
           69  +    }else if( !file_wd_isfile_or_link(zFullName) ){
           70  +      if( file_access(zFullName, 0)==0 ){
           71  +        blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName);
    61     72           if( missingIsFatal ){
    62         -          fossil_warning("not a file: %s", zPathname);
           73  +          fossil_warning("not a file: %s", zDisplayName);
    63     74             nErr++;
    64     75           }
    65     76         }else{
    66         -        blob_appendf(report, "MISSING    %s\n", zPathname);
           77  +        blob_appendf(report, "MISSING    %s\n", zDisplayName);
    67     78           if( missingIsFatal ){
    68         -          fossil_warning("missing file: %s", zPathname);
           79  +          fossil_warning("missing file: %s", zDisplayName);
    69     80             nErr++;
    70     81           }
    71     82         }
    72     83       }else if( isNew ){
    73         -      blob_appendf(report, "ADDED      %s\n", zPathname);
           84  +      blob_appendf(report, "ADDED      %s\n", zDisplayName);
    74     85       }else if( isDeleted ){
    75         -      blob_appendf(report, "DELETED    %s\n", zPathname);
           86  +      blob_appendf(report, "DELETED    %s\n", zDisplayName);
    76     87       }else if( isChnged==2 ){
    77         -      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname);
           88  +      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName);
    78     89       }else if( isChnged==3 ){
    79         -      blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname);
           90  +      blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName);
    80     91       }else if( isChnged==1 ){
    81         -      blob_appendf(report, "EDITED     %s\n", zPathname);
           92  +      if( file_contains_merge_marker(zFullName) ){
           93  +        blob_appendf(report, "CONFLICT   %s\n", zDisplayName);
           94  +      }else{
           95  +        blob_appendf(report, "EDITED     %s\n", zDisplayName);
           96  +      }
    82     97       }else if( isRenamed ){
    83         -      blob_appendf(report, "RENAMED    %s\n", zPathname);
           98  +      blob_appendf(report, "RENAMED    %s\n", zDisplayName);
    84     99       }
    85    100       free(zFullName);
    86    101     }
          102  +  blob_reset(&rewrittenPathname);
    87    103     db_finalize(&q);
    88         -  db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid"
    89         -                 " WHERE id=0");
          104  +  db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
          105  +                 " WHERE id<=0");
    90    106     while( db_step(&q)==SQLITE_ROW ){
          107  +    const char *zLabel = "MERGED_WITH";
          108  +    switch( db_column_int(&q, 1) ){
          109  +      case -1:  zLabel = "CHERRYPICK ";  break;
          110  +      case -2:  zLabel = "BACKOUT    ";  break;
          111  +    }
    91    112       blob_append(report, zPrefix, nPrefix);
    92         -    blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0));
          113  +    blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0));
    93    114     }
    94    115     db_finalize(&q);
    95    116     if( nErr ){
    96    117       fossil_fatal("aborting due to prior errors");
    97    118     }
    98    119   }
          120  +
          121  +/*
          122  +** Use the "relative-paths" setting and the --abs-paths and
          123  +** --rel-paths command line options to determine whether the
          124  +** status report should be shown relative to the current
          125  +** working directory.
          126  +*/
          127  +static int determine_cwd_relative_option()
          128  +{
          129  +  int relativePaths = db_get_boolean("relative-paths", 1);
          130  +  int absPathOption = find_option("abs-paths", 0, 0)!=0;
          131  +  int relPathOption = find_option("rel-paths", 0, 0)!=0;
          132  +  if( absPathOption ){ relativePaths = 0; }
          133  +  if( relPathOption ){ relativePaths = 1; }
          134  +  return relativePaths;
          135  +}
    99    136   
   100    137   /*
   101    138   ** COMMAND: changes
   102    139   **
   103         -** Usage: %fossil changes
          140  +** Usage: %fossil changes ?OPTIONS?
   104    141   **
   105    142   ** Report on the edit status of all files in the current checkout.
   106         -** See also the "status" and "extra" commands.
          143  +**
          144  +** Pathnames are displayed according to the "relative-paths" setting,
          145  +** unless overridden by the --abs-paths or --rel-paths options.
          146  +**
          147  +** Options:
          148  +**    --abs-paths       Display absolute pathnames.
          149  +**    --rel-paths       Display pathnames relative to the current working
          150  +**                      directory.
          151  +**    --sha1sum         Verify file status using SHA1 hashing rather
          152  +**                      than relying on file mtimes.
          153  +**    --header          Identify the repository if there are changes
          154  +**    -v                Say "no changes" if there are none
          155  +**
          156  +** See also: extra, ls, status
   107    157   */
   108    158   void changes_cmd(void){
   109    159     Blob report;
   110    160     int vid;
          161  +  int useSha1sum = find_option("sha1sum", 0, 0)!=0;
          162  +  int showHdr = find_option("header",0,0)!=0;
          163  +  int verbose = find_option("verbose","v",0)!=0;
          164  +  int cwdRelative = 0;
   111    165     db_must_be_within_tree();
          166  +  cwdRelative = determine_cwd_relative_option();
   112    167     blob_zero(&report);
   113    168     vid = db_lget_int("checkout", 0);
   114         -  vfile_check_signature(vid, 0);
   115         -  status_report(&report, "", 0);
          169  +  vfile_check_signature(vid, useSha1sum ? CKSIG_SHA1 : 0);
          170  +  status_report(&report, "", 0, cwdRelative);
          171  +  if( verbose && blob_size(&report)==0 ){
          172  +    blob_append(&report, "  (none)\n", -1);
          173  +  }
          174  +  if( showHdr && blob_size(&report)>0 ){
          175  +    fossil_print("Changes for %s at %s:\n", db_get("project-name","???"),
          176  +                 g.zLocalRoot);
          177  +  }
   116    178     blob_write_to_file(&report, "-");
   117    179   }
   118    180   
   119    181   /*
   120    182   ** COMMAND: status
   121    183   **
   122         -** Usage: %fossil status
          184  +** Usage: %fossil status ?OPTIONS?
   123    185   **
   124    186   ** Report on the status of the current checkout.
          187  +**
          188  +** Pathnames are displayed according to the "relative-paths" setting,
          189  +** unless overridden by the --abs-paths or --rel-paths options.
          190  +**
          191  +** Options:
          192  +**
          193  +**    --abs-paths       Display absolute pathnames.
          194  +**    --rel-paths       Display pathnames relative to the current working
          195  +**                      directory.
          196  +**    --sha1sum         Verify file status using SHA1 hashing rather
          197  +**                      than relying on file mtimes.
          198  +**
          199  +** See also: changes, extra, ls
   125    200   */
   126    201   void status_cmd(void){
   127    202     int vid;
   128    203     db_must_be_within_tree();
   129    204          /* 012345678901234 */
   130         -  printf("repository:   %s\n", db_lget("repository",""));
   131         -  printf("local-root:   %s\n", g.zLocalRoot);
   132         -  printf("server-code:  %s\n", db_get("server-code", ""));
          205  +  fossil_print("repository:   %s\n", db_repository_filename());
          206  +  fossil_print("local-root:   %s\n", g.zLocalRoot);
   133    207     vid = db_lget_int("checkout", 0);
   134    208     if( vid ){
   135         -    show_common_info(vid, "checkout:", 0);
          209  +    show_common_info(vid, "checkout:", 1, 1);
   136    210     }
          211  +  db_record_repository_filename(0);
   137    212     changes_cmd();
   138    213   }
   139    214   
   140    215   /*
   141    216   ** COMMAND: ls
   142    217   **
   143         -** Usage: %fossil ls [-l]
          218  +** Usage: %fossil ls ?OPTIONS? ?VERSION?
   144    219   **
   145    220   ** Show the names of all files in the current checkout.  The -l provides
   146    221   ** extra information about each file.
          222  +**
          223  +** Options:
          224  +**   -l              Provide extra information about each file.
          225  +**   --age           Show when each file was committed
          226  +**
          227  +** See also: changes, extra, status
   147    228   */
   148    229   void ls_cmd(void){
   149    230     int vid;
   150    231     Stmt q;
   151    232     int isBrief;
          233  +  int showAge;
          234  +  char *zOrderBy = "pathname";
   152    235   
   153    236     isBrief = find_option("l","l", 0)==0;
          237  +  showAge = find_option("age",0,0)!=0;
   154    238     db_must_be_within_tree();
   155    239     vid = db_lget_int("checkout", 0);
          240  +  if( find_option("t","t",0)!=0 ){
          241  +    if( showAge ){
          242  +      zOrderBy = mprintf("checkin_mtime(%d,rid) DESC", vid);
          243  +    }else{
          244  +      zOrderBy = "mtime DESC";
          245  +    }
          246  +  }
          247  +  verify_all_options();
   156    248     vfile_check_signature(vid, 0);
   157         -  db_prepare(&q,
   158         -     "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
   159         -     "  FROM vfile"
   160         -     " ORDER BY 1"
   161         -  );
          249  +  if( showAge ){
          250  +    db_prepare(&q,
          251  +       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
          252  +       "       datetime(checkin_mtime(%d,rid),'unixepoch','localtime')"
          253  +       "  FROM vfile"
          254  +       " ORDER BY %s", vid, zOrderBy
          255  +    );
          256  +  }else{
          257  +    db_prepare(&q,
          258  +       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
          259  +       "  FROM vfile"
          260  +       " ORDER BY %s", zOrderBy
          261  +    );
          262  +  }
   162    263     while( db_step(&q)==SQLITE_ROW ){
   163    264       const char *zPathname = db_column_text(&q,0);
   164    265       int isDeleted = db_column_int(&q, 1);
   165    266       int isNew = db_column_int(&q,2)==0;
   166    267       int chnged = db_column_int(&q,3);
   167    268       int renamed = db_column_int(&q,4);
   168         -    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
   169         -    if( isBrief ){
   170         -      printf("%s\n", zPathname);
          269  +    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
          270  +    if( showAge ){
          271  +      fossil_print("%s  %s\n", db_column_text(&q, 5), zPathname);
          272  +    }else if( isBrief ){
          273  +      fossil_print("%s\n", zPathname);
   171    274       }else if( isNew ){
   172         -      printf("ADDED      %s\n", zPathname);
   173         -    }else if( !file_isfile(zFullName) ){
   174         -      if( access(zFullName, 0)==0 ){
   175         -        printf("NOT_A_FILE %s\n", zPathname);
          275  +      fossil_print("ADDED      %s\n", zPathname);
          276  +    }else if( isDeleted ){
          277  +      fossil_print("DELETED    %s\n", zPathname);
          278  +    }else if( !file_wd_isfile_or_link(zFullName) ){
          279  +      if( file_access(zFullName, 0)==0 ){
          280  +        fossil_print("NOT_A_FILE %s\n", zPathname);
   176    281         }else{
   177         -        printf("MISSING    %s\n", zPathname);
          282  +        fossil_print("MISSING    %s\n", zPathname);
   178    283         }
   179         -    }else if( isDeleted ){
   180         -      printf("DELETED    %s\n", zPathname);
   181    284       }else if( chnged ){
   182         -      printf("EDITED     %s\n", zPathname);
          285  +      fossil_print("EDITED     %s\n", zPathname);
   183    286       }else if( renamed ){
   184         -      printf("RENAMED    %s\n", zPathname);
          287  +      fossil_print("RENAMED    %s\n", zPathname);
   185    288       }else{
   186         -      printf("UNCHANGED  %s\n", zPathname);
          289  +      fossil_print("UNCHANGED  %s\n", zPathname);
   187    290       }
   188    291       free(zFullName);
   189    292     }
   190    293     db_finalize(&q);
   191    294   }
   192    295   
   193         -/*
   194         -** Construct and return a string which is an SQL expression that will
   195         -** be TRUE if value zVal matches any of the GLOB expressions in the list
   196         -** zGlobList.  For example:
   197         -**
   198         -**    zVal:       "x"
   199         -**    zGlobList:  "*.o,*.obj"
   200         -**
   201         -**    Result:     "(x GLOB '*.o' OR x GLOB '*.obj')"
   202         -**
   203         -** Each element of the GLOB list may optionally be enclosed in either '...'
   204         -** or "...".  This allows commas in the expression.  Whitespace at the
   205         -** beginning and end of each GLOB pattern is ignored, except when enclosed
   206         -** within '...' or "...".
   207         -**
   208         -** This routine makes no effort to free the memory space it uses.
   209         -*/
   210         -char *glob_expr(const char *zVal, const char *zGlobList){
   211         -  Blob expr;
   212         -  char *zSep = "(";
   213         -  int nTerm = 0;
   214         -  int i;
   215         -  int cTerm;
   216         -
   217         -  if( zGlobList==0 || zGlobList[0]==0 ) return "0";
   218         -  blob_zero(&expr);
   219         -  while( zGlobList[0] ){
   220         -    while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
   221         -    if( zGlobList[0]==0 ) break;
   222         -    if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
   223         -      cTerm = zGlobList[0];
   224         -      zGlobList++;
   225         -    }else{
   226         -      cTerm = ',';
   227         -    }
   228         -    for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
   229         -    if( cTerm==',' ){
   230         -      while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
   231         -    }
   232         -    blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
   233         -    zSep = " OR ";
   234         -    if( cTerm!=',' && zGlobList[i] ) i++;
   235         -    zGlobList += i;
   236         -    if( zGlobList[0] ) zGlobList++;
   237         -    nTerm++;
   238         -  }
   239         -  if( nTerm ){
   240         -    blob_appendf(&expr, ")");
   241         -    return blob_str(&expr);
   242         -  }else{
   243         -    return "0";
   244         -  }
   245         -}
   246         -
   247    296   /*
   248    297   ** COMMAND: extras
   249         -** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
          298  +** Usage: %fossil extras ?OPTIONS?
   250    299   **
   251    300   ** Print a list of all files in the source tree that are not part of
   252    301   ** the current checkout.  See also the "clean" command.
   253    302   **
   254    303   ** Files and subdirectories whose names begin with "." are normally
   255    304   ** ignored but can be included by adding the --dotfiles option.
          305  +**
          306  +** The GLOBPATTERN is a comma-separated list of GLOB expressions for
          307  +** files that are ignored.  The GLOBPATTERN specified by the "ignore-glob"
          308  +** is used if the --ignore option is omitted.
          309  +**
          310  +** Pathnames are displayed according to the "relative-paths" setting,
          311  +** unless overridden by the --abs-paths or --rel-paths options.
          312  +**
          313  +** Options:
          314  +**    --abs-paths      Display absolute pathnames.
          315  +**    --dotfiles       include files beginning with a dot (".")
          316  +**    --ignore <CSG>   ignore files matching patterns from the argument
          317  +**    --rel-paths      Display pathnames relative to the current working
          318  +**                     directory.
          319  +**
          320  +** See also: changes, clean, status
   256    321   */
   257    322   void extra_cmd(void){
   258    323     Blob path;
   259         -  Blob repo;
   260    324     Stmt q;
   261    325     int n;
   262    326     const char *zIgnoreFlag = find_option("ignore",0,1);
   263         -  int allFlag = find_option("dotfiles",0,0)!=0;
          327  +  unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
          328  +  int cwdRelative = 0;
          329  +  Glob *pIgnore;
          330  +  Blob rewrittenPathname;
          331  +  const char *zPathname, *zDisplayName;
   264    332   
          333  +  if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
   265    334     db_must_be_within_tree();
   266         -  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
          335  +  cwdRelative = determine_cwd_relative_option();
          336  +  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
          337  +                filename_collation());
   267    338     n = strlen(g.zLocalRoot);
   268    339     blob_init(&path, g.zLocalRoot, n-1);
   269    340     if( zIgnoreFlag==0 ){
   270    341       zIgnoreFlag = db_get("ignore-glob", 0);
   271    342     }
   272         -  vfile_scan(0, &path, blob_size(&path), allFlag);
   273         -  db_prepare(&q, 
          343  +  pIgnore = glob_create(zIgnoreFlag);
          344  +  vfile_scan(&path, blob_size(&path), scanFlags, pIgnore);
          345  +  glob_free(pIgnore);
          346  +  db_prepare(&q,
   274    347         "SELECT x FROM sfile"
   275         -      " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
   276         -                         "'_FOSSIL_-journal','.fos','.fos-journal')"
   277         -      "   AND NOT %s"
          348  +      " WHERE x NOT IN (%s)"
   278    349         " ORDER BY 1",
   279         -      glob_expr("x", zIgnoreFlag)
          350  +      fossil_all_reserved_names(0)
   280    351     );
   281         -  if( file_tree_name(g.zRepositoryName, &repo, 0) ){
   282         -    db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
   283         -  }
          352  +  db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
          353  +  blob_zero(&rewrittenPathname);
   284    354     while( db_step(&q)==SQLITE_ROW ){
   285         -    printf("%s\n", db_column_text(&q, 0));
          355  +    zDisplayName = zPathname = db_column_text(&q, 0);
          356  +    if( cwdRelative ) {
          357  +      char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
          358  +      file_relative_name(zFullName, &rewrittenPathname, 0);
          359  +      free(zFullName);
          360  +      zDisplayName = blob_str(&rewrittenPathname);
          361  +      if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){
          362  +        zDisplayName += 2;  /* no unnecessary ./ prefix */
          363  +      }
          364  +    }
          365  +    fossil_print("%s\n", zDisplayName);
   286    366     }
          367  +  blob_reset(&rewrittenPathname);
   287    368     db_finalize(&q);
   288    369   }
   289    370   
   290    371   /*
   291    372   ** COMMAND: clean
   292         -** Usage: %fossil clean ?--force? ?--dotfiles?
          373  +** Usage: %fossil clean ?OPTIONS?
   293    374   **
   294    375   ** Delete all "extra" files in the source tree.  "Extra" files are
   295         -** files that are not officially part of the checkout.  See also
   296         -** the "extra" command. This operation cannot be undone. 
          376  +** files that are not officially part of the checkout. This operation
          377  +** cannot be undone.
   297    378   **
   298    379   ** You will be prompted before removing each file. If you are
   299    380   ** sure you wish to remove all "extra" files you can specify the
   300    381   ** optional --force flag and no prompts will be issued.
   301    382   **
   302    383   ** Files and subdirectories whose names begin with "." are
   303    384   ** normally ignored.  They are included if the "--dotfiles" option
   304    385   ** is used.
          386  +**
          387  +** The GLOBPATTERN is a comma-separated list of GLOB expressions for
          388  +** files that are ignored.  The GLOBPATTERN specified by the "ignore-glob"
          389  +** is used if the --ignore option is omitted.
          390  +**
          391  +** Options:
          392  +**    --dotfiles       include files beginning with a dot (".")
          393  +**    --force          Remove files without prompting
          394  +**    --ignore <CSG>   ignore files matching patterns from the
          395  +**                     comma separated list of glob patterns.
          396  +**    --temp           Remove only Fossil-generated temporary files
          397  +**
          398  +** See also: addremove, extra, status
   305    399   */
   306    400   void clean_cmd(void){
   307    401     int allFlag;
   308         -  int dotfilesFlag;
          402  +  unsigned scanFlags = 0;
          403  +  const char *zIgnoreFlag;
   309    404     Blob path, repo;
   310    405     Stmt q;
   311    406     int n;
          407  +  Glob *pIgnore;
          408  +  int testFlag = 0;
          409  +
   312    410     allFlag = find_option("force","f",0)!=0;
   313         -  dotfilesFlag = find_option("dotfiles",0,0)!=0;
          411  +  if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
          412  +  if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
          413  +  zIgnoreFlag = find_option("ignore",0,1);
          414  +  testFlag = find_option("test",0,0)!=0;
   314    415     db_must_be_within_tree();
   315         -  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
          416  +  if( zIgnoreFlag==0 ){
          417  +    zIgnoreFlag = db_get("ignore-glob", 0);
          418  +  }
          419  +  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
          420  +                filename_collation());
   316    421     n = strlen(g.zLocalRoot);
   317    422     blob_init(&path, g.zLocalRoot, n-1);
   318         -  vfile_scan(0, &path, blob_size(&path), dotfilesFlag);
   319         -  db_prepare(&q, 
          423  +  pIgnore = glob_create(zIgnoreFlag);
          424  +  vfile_scan(&path, blob_size(&path), scanFlags, pIgnore);
          425  +  glob_free(pIgnore);
          426  +  db_prepare(&q,
   320    427         "SELECT %Q || x FROM sfile"
   321         -      " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
   322         -                       "'_FOSSIL_-journal','.fos','.fos-journal')"
   323         -      " ORDER BY 1", g.zLocalRoot);
          428  +      " WHERE x NOT IN (%s)"
          429  +      " ORDER BY 1",
          430  +      g.zLocalRoot, fossil_all_reserved_names(0)
          431  +  );
   324    432     if( file_tree_name(g.zRepositoryName, &repo, 0) ){
   325    433       db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
   326    434     }
          435  +  db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
   327    436     while( db_step(&q)==SQLITE_ROW ){
   328         -    if( allFlag ){
   329         -      unlink(db_column_text(&q, 0));
          437  +    if( testFlag ){
          438  +      fossil_print("%s\n", db_column_text(&q,0));
          439  +    }else if( allFlag ){
          440  +      file_delete(db_column_text(&q, 0));
   330    441       }else{
   331    442         Blob ans;
          443  +      char cReply;
   332    444         char *prompt = mprintf("remove unmanaged file \"%s\" (y/N)? ",
   333    445                                 db_column_text(&q, 0));
   334    446         blob_zero(&ans);
   335    447         prompt_user(prompt, &ans);
   336         -      if( blob_str(&ans)[0]=='y' ){
   337         -        unlink(db_column_text(&q, 0));
          448  +      cReply = blob_str(&ans)[0];
          449  +      if( cReply=='y' || cReply=='Y' ){
          450  +        file_delete(db_column_text(&q, 0));
   338    451         }
   339    452       }
   340    453     }
   341    454     db_finalize(&q);
   342    455   }
          456  +
          457  +/*
          458  +** Prompt the user for a check-in or stash comment (given in pPrompt),
          459  +** gather the response, then return the response in pComment.
          460  +**
          461  +** Lines of the prompt that begin with # are discarded.  Excess whitespace
          462  +** is removed from the reply.
          463  +**
          464  +** Appropriate encoding translations are made on windows.
          465  +*/
          466  +void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
          467  +  const char *zEditor;
          468  +  char *zCmd;
          469  +  char *zFile;
          470  +  Blob reply, line;
          471  +  char *zComment;
          472  +  int i;
          473  +
          474  +  zEditor = db_get("editor", 0);
          475  +  if( zEditor==0 ){
          476  +    zEditor = fossil_getenv("VISUAL");
          477  +  }
          478  +  if( zEditor==0 ){
          479  +    zEditor = fossil_getenv("EDITOR");
          480  +  }
          481  +#ifdef _WIN32
          482  +  if( zEditor==0 ){
          483  +    zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
          484  +  }
          485  +#endif
          486  +  if( zEditor==0 ){
          487  +    blob_append(pPrompt,
          488  +       "#\n"
          489  +       "# Since no default text editor is set using EDITOR or VISUAL\n"
          490  +       "# environment variables or the \"fossil set editor\" command,\n"
          491  +       "# and because no comment was specified using the \"-m\" or \"-M\"\n"
          492  +       "# command-line options, you will need to enter the comment below.\n"
          493  +       "# Type \".\" on a line by itself when you are done:\n", -1);
          494  +    zFile = mprintf("-");
          495  +  }else{
          496  +    zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
          497  +                    g.zLocalRoot);
          498  +  }
          499  +#if defined(_WIN32)
          500  +  blob_add_cr(pPrompt);
          501  +#endif
          502  +  blob_write_to_file(pPrompt, zFile);
          503  +  if( zEditor ){
          504  +    zCmd = mprintf("%s \"%s\"", zEditor, zFile);
          505  +    fossil_print("%s\n", zCmd);
          506  +    if( fossil_system(zCmd) ){
          507  +      fossil_fatal("editor aborted: \"%s\"", zCmd);
          508  +    }
          509  +
          510  +    blob_read_from_file(&reply, zFile);
          511  +  }else{
          512  +    char zIn[300];
          513  +    blob_zero(&reply);
          514  +    while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
          515  +      if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
          516  +        break;
          517  +      }
          518  +      blob_append(&reply, zIn, -1);
          519  +    }
          520  +  }
          521  +  blob_to_utf8_no_bom(&reply, 1);
          522  +  blob_remove_cr(&reply);
          523  +  file_delete(zFile);
          524  +  free(zFile);
          525  +  blob_zero(pComment);
          526  +  while( blob_line(&reply, &line) ){
          527  +    int i, n;
          528  +    char *z;
          529  +    n = blob_size(&line);
          530  +    z = blob_buffer(&line);
          531  +    for(i=0; i<n && fossil_isspace(z[i]);  i++){}
          532  +    if( i<n && z[i]=='#' ) continue;
          533  +    if( i<n || blob_size(pComment)>0 ){
          534  +      blob_appendf(pComment, "%b", &line);
          535  +    }
          536  +  }
          537  +  blob_reset(&reply);
          538  +  zComment = blob_str(pComment);
          539  +  i = strlen(zComment);
          540  +  while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
          541  +  blob_resize(pComment, i);
          542  +}
   343    543   
   344    544   /*
   345    545   ** Prepare a commit comment.  Let the user modify it using the
   346    546   ** editor specified in the global_config table or either
   347    547   ** the VISUAL or EDITOR environment variable.
   348    548   **
   349    549   ** Store the final commit comment in pComment.  pComment is assumed
................................................................................
   358    558   **
   359    559   ** parent_rid is the recordid of the parent check-in.
   360    560   */
   361    561   static void prepare_commit_comment(
   362    562     Blob *pComment,
   363    563     char *zInit,
   364    564     const char *zBranch,
   365         -  int parent_rid
          565  +  int parent_rid,
          566  +  const char *zUserOvrd
   366    567   ){
   367         -  const char *zEditor;
   368         -  char *zCmd;
   369         -  char *zFile;
   370         -  Blob text, line;
   371         -  char *zComment;
   372         -  int i;
   373         -  blob_init(&text, zInit, -1);
   374         -  blob_append(&text,
          568  +  Blob prompt;
          569  +#ifdef _WIN32
          570  +  int bomSize;
          571  +  const unsigned char *bom = get_utf8_bom(&bomSize);
          572  +  blob_init(&prompt, (const char *) bom, bomSize);
          573  +  if( zInit && zInit[0]) {
          574  +    blob_append(&prompt, zInit, -1);
          575  +  }
          576  +#else
          577  +  blob_init(&prompt, zInit, -1);
          578  +#endif
          579  +  blob_append(&prompt,
   375    580       "\n"
   376    581       "# Enter comments on this check-in.  Lines beginning with # are ignored.\n"
   377         -    "# The check-in comment follows wiki formatting rules.\n"
   378    582       "#\n", -1
   379    583     );
          584  +  blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
   380    585     if( zBranch && zBranch[0] ){
   381         -    blob_appendf(&text, "# tags: %s\n#\n", zBranch);
          586  +    blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
   382    587     }else{
   383    588       char *zTags = info_tags_of_checkin(parent_rid, 1);
   384         -    if( zTags )  blob_appendf(&text, "# tags: %z\n#\n", zTags);
          589  +    if( zTags )  blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
   385    590     }
          591  +  status_report(&prompt, "# ", 1, 0);
   386    592     if( g.markPrivate ){
   387         -    blob_append(&text,
          593  +    blob_append(&prompt,
   388    594         "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
   389    595         "# repositories.\n"
   390    596         "#\n", -1
   391    597       );
   392    598     }
   393         -  status_report(&text, "# ", 1);
   394         -  zEditor = db_get("editor", 0);
   395         -  if( zEditor==0 ){
   396         -    zEditor = getenv("VISUAL");
   397         -  }
   398         -  if( zEditor==0 ){
   399         -    zEditor = getenv("EDITOR");
   400         -  }
   401         -  if( zEditor==0 ){
   402         -#ifdef __MINGW32__
   403         -    zEditor = "notepad";
   404         -#else
   405         -    zEditor = "ed";
   406         -#endif
   407         -  }
   408         -  zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
   409         -                   g.zLocalRoot);
   410         -#ifdef __MINGW32__
   411         -  blob_add_cr(&text);
   412         -#endif
   413         -  blob_write_to_file(&text, zFile);
   414         -  zCmd = mprintf("%s \"%s\"", zEditor, zFile);
   415         -  printf("%s\n", zCmd);
   416         -  if( portable_system(zCmd) ){
   417         -    fossil_panic("editor aborted");
   418         -  }
   419         -  blob_reset(&text);
   420         -  blob_read_from_file(&text, zFile);
   421         -  blob_remove_cr(&text);
   422         -  unlink(zFile);
   423         -  free(zFile);
   424         -  blob_zero(pComment);
   425         -  while( blob_line(&text, &line) ){
   426         -    int i, n;
   427         -    char *z;
   428         -    n = blob_size(&line);
   429         -    z = blob_buffer(&line);
   430         -    for(i=0; i<n && isspace(z[i]);  i++){}
   431         -    if( i<n && z[i]=='#' ) continue;
   432         -    if( i<n || blob_size(pComment)>0 ){
   433         -      blob_appendf(pComment, "%b", &line);
   434         -    }
   435         -  }
   436         -  blob_reset(&text);
   437         -  zComment = blob_str(pComment);
   438         -  i = strlen(zComment);
   439         -  while( i>0 && isspace(zComment[i-1]) ){ i--; }
   440         -  blob_resize(pComment, i);
          599  +  prompt_for_user_comment(pComment, &prompt);
          600  +  blob_reset(&prompt);
   441    601   }
   442    602   
   443    603   /*
   444    604   ** Populate the Global.aCommitFile[] based on the command line arguments
   445    605   ** to a [commit] command. Global.aCommitFile is an array of integers
   446    606   ** sized at (N+1), where N is the number of arguments passed to [commit].
   447    607   ** The contents are the [id] values from the vfile table corresponding
................................................................................
   449    609   **
   450    610   ** The last element of aCommitFile[] is always 0 - indicating the end
   451    611   ** of the array.
   452    612   **
   453    613   ** If there were no arguments passed to [commit], aCommitFile is not
   454    614   ** allocated and remains NULL. Other parts of the code interpret this
   455    615   ** to mean "all files".
          616  +**
          617  +** Returns 1 if there was a warning, 0 otherwise.
   456    618   */
   457         -void select_commit_files(void){
          619  +int select_commit_files(void){
          620  +  int result = 0;
   458    621     if( g.argc>2 ){
   459         -    int ii;
          622  +    int ii, jj=0;
   460    623       Blob b;
   461    624       blob_zero(&b);
   462         -    g.aCommitFile = malloc(sizeof(int)*(g.argc-1));
          625  +    g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1));
   463    626   
   464    627       for(ii=2; ii<g.argc; ii++){
   465    628         int iId;
   466    629         file_tree_name(g.argv[ii], &b, 1);
   467    630         iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b));
   468    631         if( iId<0 ){
   469         -        fossil_fatal("fossil knows nothing about: %s", g.argv[ii]);
          632  +        fossil_warning("fossil knows nothing about: %s", g.argv[ii]);
          633  +        result = 1;
          634  +      } else {
          635  +        g.aCommitFile[jj++] = iId;
   470    636         }
   471         -      g.aCommitFile[ii-2] = iId;
   472    637         blob_reset(&b);
   473    638       }
   474         -    g.aCommitFile[ii-2] = 0;
          639  +    g.aCommitFile[jj] = 0;
   475    640     }
   476         -}
   477         -
   478         -/*
   479         -** Return true if the check-in with RID=rid is a leaf.
   480         -** A leaf has no children in the same branch. 
   481         -*/
   482         -int is_a_leaf(int rid){
   483         -  int rc;
   484         -  static const char zSql[] = 
   485         -    @ SELECT 1 FROM plink
   486         -    @  WHERE pid=%d
   487         -    @    AND coalesce((SELECT value FROM tagxref
   488         -    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
   489         -    @       =coalesce((SELECT value FROM tagxref
   490         -    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
   491         -  ;
   492         -  rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH);
   493         -  return rc==0;
          641  +  return result;
   494    642   }
   495    643   
   496    644   /*
   497    645   ** Make sure the current check-in with timestamp zDate is younger than its
   498    646   ** ancestor identified rid and zUuid.  Throw a fatal error if not.
   499    647   */
   500    648   static void checkin_verify_younger(
................................................................................
   507    655     b = db_exists(
   508    656       "SELECT 1 FROM event"
   509    657       " WHERE datetime(mtime)>=%Q"
   510    658       "   AND type='ci' AND objid=%d",
   511    659       zDate, rid
   512    660     );
   513    661     if( b ){
   514         -    fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)",
   515         -                 zUuid, zDate);
   516         -  }
          662  +    fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
          663  +                 " Use --allow-older to override.", zUuid, zDate);
          664  +  }
          665  +#endif
          666  +}
          667  +
          668  +/*
          669  +** zDate should be a valid date string.  Convert this string into the
          670  +** format YYYY-MM-DDTHH:MM:SS.  If the string is not a valid date,
          671  +** print a fatal error and quit.
          672  +*/
          673  +char *date_in_standard_format(const char *zInputDate){
          674  +  char *zDate;
          675  +  if( g.perm.Setup && fossil_strcmp(zInputDate,"now")==0 ){
          676  +    zInputDate = PD("date_override","now");
          677  +  }
          678  +  zDate = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
          679  +                  zInputDate);
          680  +  if( zDate[0]==0 ){
          681  +    fossil_fatal(
          682  +      "unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS.SSS\"",
          683  +      zInputDate
          684  +    );
          685  +  }
          686  +  return zDate;
          687  +}
          688  +
          689  +/*
          690  +** COMMAND: test-date-format
          691  +**
          692  +** Usage: %fossil test-date-format DATE-STRING...
          693  +**
          694  +** Convert the DATE-STRING into the standard format used in artifacts
          695  +** and display the result.
          696  +*/
          697  +void test_date_format(void){
          698  +  int i;
          699  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
          700  +  for(i=2; i<g.argc; i++){
          701  +    fossil_print("%s -> %s\n", g.argv[i], date_in_standard_format(g.argv[i]));
          702  +  }
          703  +}
          704  +
          705  +/*
          706  +** Create a manifest.
          707  +*/
          708  +static void create_manifest(
          709  +  Blob *pOut,                 /* Write the manifest here */
          710  +  const char *zBaselineUuid,  /* UUID of baseline, or zero */
          711  +  Manifest *pBaseline,        /* Make it a delta manifest if not zero */
          712  +  Blob *pComment,             /* Check-in comment text */
          713  +  int vid,                    /* blob-id of the parent manifest */
          714  +  int verifyDate,             /* Verify that child is younger */
          715  +  Blob *pCksum,               /* Repository checksum.  May be 0 */
          716  +  const char *zDateOvrd,      /* Date override.  If 0 then use 'now' */
          717  +  const char *zUserOvrd,      /* User override.  If 0 then use g.zLogin */
          718  +  const char *zBranch,        /* Branch name.  May be 0 */
          719  +  const char *zColor,         /* One-time background color.  May be 0 */
          720  +  const char *zBrClr,         /* Persistent branch color.  May be 0 */
          721  +  const char **azTag,         /* Tags to apply to this check-in */
          722  +  int *pnFBcard               /* Number of generated B- and F-cards */
          723  +){
          724  +  char *zDate;                /* Date of the check-in */
          725  +  char *zParentUuid;          /* UUID of parent check-in */
          726  +  Blob filename;              /* A single filename */
          727  +  int nBasename;              /* Size of base filename */
          728  +  Stmt q;                     /* Query of files changed */
          729  +  Stmt q2;                    /* Query of merge parents */
          730  +  Blob mcksum;                /* Manifest checksum */
          731  +  ManifestFile *pFile;        /* File from the baseline */
          732  +  int nFBcard = 0;            /* Number of B-cards and F-cards */
          733  +  int i;                      /* Loop counter */
          734  +
          735  +  assert( pBaseline==0 || pBaseline->zBaseline==0 );
          736  +  assert( pBaseline==0 || zBaselineUuid!=0 );
          737  +  blob_zero(pOut);
          738  +  zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
          739  +  if( pBaseline ){
          740  +    blob_appendf(pOut, "B %s\n", zBaselineUuid);
          741  +    manifest_file_rewind(pBaseline);
          742  +    pFile = manifest_file_next(pBaseline, 0);
          743  +    nFBcard++;
          744  +  }else{
          745  +    pFile = 0;
          746  +  }
          747  +  blob_appendf(pOut, "C %F\n", blob_str(pComment));
          748  +  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
          749  +  blob_appendf(pOut, "D %s\n", zDate);
          750  +  zDate[10] = ' ';
          751  +  db_prepare(&q,
          752  +    "SELECT pathname, uuid, origname, blob.rid, isexe, islink,"
          753  +    "       is_selected(vfile.id)"
          754  +    "  FROM vfile JOIN blob ON vfile.mrid=blob.rid"
          755  +    " WHERE (NOT deleted OR NOT is_selected(vfile.id))"
          756  +    "   AND vfile.vid=%d"
          757  +    " ORDER BY if_selected(vfile.id, pathname, origname)",
          758  +    vid);
          759  +  blob_zero(&filename);
          760  +  blob_appendf(&filename, "%s", g.zLocalRoot);
          761  +  nBasename = blob_size(&filename);
          762  +  while( db_step(&q)==SQLITE_ROW ){
          763  +    const char *zName = db_column_text(&q, 0);
          764  +    const char *zUuid = db_column_text(&q, 1);
          765  +    const char *zOrig = db_column_text(&q, 2);
          766  +    int frid = db_column_int(&q, 3);
          767  +    int isExe = db_column_int(&q, 4);
          768  +    int isLink = db_column_int(&q, 5);
          769  +    int isSelected = db_column_int(&q, 6);
          770  +    const char *zPerm;
          771  +    int cmp;
          772  +
          773  +    blob_resize(&filename, nBasename);
          774  +    blob_append(&filename, zName, -1);
          775  +
          776  +#if !defined(_WIN32)
          777  +    /* For unix, extract the "executable" and "symlink" permissions
          778  +    ** directly from the filesystem.  On windows, permissions are
          779  +    ** unchanged from the original.  However, only do this if the file
          780  +    ** itself is actually selected to be part of this check-in.
          781  +    */
          782  +    if( isSelected ){
          783  +      int mPerm;
          784  +
          785  +      mPerm = file_wd_perm(blob_str(&filename));
          786  +      isExe = ( mPerm==PERM_EXE );
          787  +      isLink = ( mPerm==PERM_LNK );
          788  +    }
          789  +#endif
          790  +    if( isExe ){
          791  +      zPerm = " x";
          792  +    }else if( isLink ){
          793  +      zPerm = " l"; /* note: symlinks don't have executable bit on unix */
          794  +    }else{
          795  +      zPerm = "";
          796  +    }
          797  +    if( !g.markPrivate ) content_make_public(frid);
          798  +    while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){
          799  +      blob_appendf(pOut, "F %F\n", pFile->zName);
          800  +      pFile = manifest_file_next(pBaseline, 0);
          801  +      nFBcard++;
          802  +    }
          803  +    cmp = 1;
          804  +    if( pFile==0
          805  +      || (cmp = fossil_strcmp(pFile->zName,zName))!=0
          806  +      || fossil_strcmp(pFile->zUuid, zUuid)!=0
          807  +    ){
          808  +      if( zOrig && !isSelected ){ zName = zOrig; zOrig = 0; }
          809  +      if( zOrig==0 || fossil_strcmp(zOrig,zName)==0 ){
          810  +        blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
          811  +      }else{
          812  +        if( zPerm[0]==0 ){ zPerm = " w"; }
          813  +        blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
          814  +      }
          815  +      nFBcard++;
          816  +    }
          817  +    if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
          818  +  }
          819  +  blob_reset(&filename);
          820  +  db_finalize(&q);
          821  +  while( pFile ){
          822  +    blob_appendf(pOut, "F %F\n", pFile->zName);
          823  +    pFile = manifest_file_next(pBaseline, 0);
          824  +    nFBcard++;
          825  +  }
          826  +  blob_appendf(pOut, "P %s", zParentUuid);
          827  +  if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
          828  +  free(zParentUuid);
          829  +  db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=0");
          830  +  while( db_step(&q2)==SQLITE_ROW ){
          831  +    char *zMergeUuid;
          832  +    int mid = db_column_int(&q2, 0);
          833  +    if( !g.markPrivate && content_is_private(mid) ) continue;
          834  +    zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
          835  +    if( zMergeUuid ){
          836  +      blob_appendf(pOut, " %s", zMergeUuid);
          837  +      if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
          838  +      free(zMergeUuid);
          839  +    }
          840  +  }
          841  +  db_finalize(&q2);
          842  +  free(zDate);
          843  +
          844  +  blob_appendf(pOut, "\n");
          845  +  if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
          846  +  if( zBranch && zBranch[0] ){
          847  +    /* Set tags for the new branch */
          848  +    if( zBrClr && zBrClr[0] ){
          849  +      zColor = 0;
          850  +      blob_appendf(pOut, "T *bgcolor * %F\n", zBrClr);
          851  +    }
          852  +    blob_appendf(pOut, "T *branch * %F\n", zBranch);
          853  +    blob_appendf(pOut, "T *sym-%F *\n", zBranch);
          854  +  }
          855  +  if( zColor && zColor[0] ){
          856  +    /* One-time background color */
          857  +    blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
          858  +  }
          859  +  if( azTag ){
          860  +    for(i=0; azTag[i]; i++){
          861  +      /* Add a symbolic tag to this check-in.  The tag names have already
          862  +      ** been sorted and converted using the %F format */
          863  +      blob_appendf(pOut, "T +sym-%s *\n", azTag[i]);
          864  +    }
          865  +  }
          866  +  if( zBranch && zBranch[0] ){
          867  +    /* For a new branch, cancel all prior propagating tags */
          868  +    Stmt q;
          869  +    db_prepare(&q,
          870  +        "SELECT tagname FROM tagxref, tag"
          871  +        " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
          872  +        "   AND tagtype==2 AND tagname GLOB 'sym-*'"
          873  +        "   AND tagname!='sym-'||%Q"
          874  +        " ORDER BY tagname",
          875  +        vid, zBranch);
          876  +    while( db_step(&q)==SQLITE_ROW ){
          877  +      const char *zBrTag = db_column_text(&q, 0);
          878  +      blob_appendf(pOut, "T -%F *\n", zBrTag);
          879  +    }
          880  +    db_finalize(&q);
          881  +  }
          882  +  blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
          883  +  md5sum_blob(pOut, &mcksum);
          884  +  blob_appendf(pOut, "Z %b\n", &mcksum);
          885  +  if( pnFBcard ) *pnFBcard = nFBcard;
          886  +}
          887  +
          888  +/*
          889  +** Issue a warning and give the user an opportunity to abandon out
          890  +** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending
          891  +** is seen in a text file.
          892  +**
          893  +** Return 1 if the user pressed 'c'. In that case, the file will have
          894  +** been converted to UTF-8 (if it was UTF-16) with NL line-endings,
          895  +** and the original file will have been renamed to "<filename>-original".
          896  +*/
          897  +static int commit_warning(
          898  +  Blob *p,              /* The content of the file being committed. */
          899  +  int crnlOk,           /* Non-zero if CR/NL warnings should be disabled. */
          900  +  int binOk,            /* Non-zero if binary warnings should be disabled. */
          901  +  int encodingOk,        /* Non-zero if encoding warnings should be disabled. */
          902  +  const char *zFilename /* The full name of the file being committed. */
          903  +){
          904  +  int eType;              /* return value of looks_like_utf8/utf16() */
          905  +  int fUnicode;           /* return value of starts_with_utf16_bom() */
          906  +  char *zMsg;             /* Warning message */
          907  +  Blob fname;             /* Relative pathname of the file */
          908  +  static int allOk = 0;   /* Set to true to disable this routine */
          909  +
          910  +  if( allOk ) return 0;
          911  +  fUnicode = starts_with_utf16_bom(p, 0, 0);
          912  +  eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
          913  +  if( eType==0 || eType==-1 || fUnicode ){
          914  +    const char *zWarning;
          915  +    const char *zDisable;
          916  +    const char *zConvert = "c=convert/";
          917  +    Blob ans;
          918  +    char cReply;
          919  +
          920  +    if( eType==-1 && fUnicode ){
          921  +      if ( crnlOk && encodingOk ){
          922  +        return 0; /* We don't want CR/NL and Unicode warnings for this file. */
          923  +      }
          924  +      zWarning = "CR/NL line endings and Unicode";
          925  +      zDisable = "\"crnl-glob\" and \"encoding-glob\" settings";
          926  +    }else if( eType==-1 ){
          927  +      if( crnlOk ){
          928  +        return 0; /* We don't want CR/NL warnings for this file. */
          929  +      }
          930  +      zWarning = "CR/NL line endings";
          931  +      zDisable = "\"crnl-glob\" setting";
          932  +    }else if( eType==0 ){
          933  +      if( binOk ){
          934  +        return 0; /* We don't want binary warnings for this file. */
          935  +      }
          936  +      zWarning = "binary data";
          937  +      zDisable = "\"binary-glob\" setting";
          938  +      zConvert = ""; /* We cannot convert binary files. */
          939  +    }else{
          940  +      if ( encodingOk ){
          941  +        return 0; /* We don't want encoding warnings for this file. */
          942  +      }
          943  +      zWarning = "Unicode";
          944  +      zDisable = "\"encoding-glob\" setting";
          945  +#ifndef _WIN32
          946  +      zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */
   517    947   #endif
          948  +    }
          949  +    file_relative_name(zFilename, &fname, 0);
          950  +    blob_zero(&ans);
          951  +    zMsg = mprintf(
          952  +         "%s contains %s. Use --no-warnings or the %s to disable this warning.\n"
          953  +    	 "Commit anyhow (a=all/%sy/N)? ",
          954  +         blob_str(&fname), zWarning, zDisable, zConvert);
          955  +    prompt_user(zMsg, &ans);
          956  +    fossil_free(zMsg);
          957  +    cReply = blob_str(&ans)[0];
          958  +    if( cReply=='a' || cReply=='A' ){
          959  +      allOk = 1;
          960  +    }else if( *zConvert && (cReply=='c' || cReply=='C') ){
          961  +      char *zOrig = file_newname(zFilename, "original", 1);
          962  +      FILE *f;
          963  +      blob_write_to_file(p, zOrig);
          964  +      fossil_free(zOrig);
          965  +      f = fossil_fopen(zFilename, "wb");
          966  +      if( fUnicode ) {
          967  +        int bomSize;
          968  +        const unsigned char *bom = get_utf8_bom(&bomSize);
          969  +        fwrite(bom, 1, bomSize, f);
          970  +        blob_to_utf8_no_bom(p, 0);
          971  +      }
          972  +      blob_remove_cr(p);
          973  +      fwrite(blob_buffer(p), 1, blob_size(p), f);
          974  +      fclose(f);
          975  +      return 1;
          976  +    }else if( cReply!='y' && cReply!='Y' ){
          977  +      fossil_fatal("Abandoning commit due to %s in %s",
          978  +                   zWarning, blob_str(&fname));
          979  +    }
          980  +    blob_reset(&ans);
          981  +    blob_reset(&fname);
          982  +  }
          983  +  return 0;
          984  +}
          985  +
          986  +/*
          987  +** qsort() comparison routine for an array of pointers to strings.
          988  +*/
          989  +static int tagCmp(const void *a, const void *b){
          990  +  char **pA = (char**)a;
          991  +  char **pB = (char**)b;
          992  +  return fossil_strcmp(pA[0], pB[0]);
   518    993   }
   519    994   
   520    995   /*
   521         -** COMMAND: ci
          996  +** COMMAND: ci*
   522    997   ** COMMAND: commit
   523    998   **
   524    999   ** Usage: %fossil commit ?OPTIONS? ?FILE...?
   525   1000   **
   526   1001   ** Create a new version containing all of the changes in the current
   527   1002   ** checkout.  You will be prompted to enter a check-in comment unless
   528         -** the comment has been specified on the command-line using "-m".
   529         -** The editor defined in the "editor" fossil option (see %fossil help set)
   530         -** will be used, or from the "VISUAL" or "EDITOR" environment variables
   531         -** (in that order) if no editor is set.
         1003  +** the comment has been specified on the command-line using "-m" or a
         1004  +** file containing the comment using -M.  The editor defined in the
         1005  +** "editor" fossil option (see %fossil help set) will be used, or from
         1006  +** the "VISUAL" or "EDITOR" environment variables (in that order) if
         1007  +** no editor is set.
         1008  +**
         1009  +** All files that have changed will be committed unless some subset of
         1010  +** files is specified on the command line.
         1011  +**
         1012  +** The --branch option followed by a branch name causes the new
         1013  +** check-in to be placed in a newly-created branch with the name
         1014  +** passed to the --branch option.
         1015  +**
         1016  +** Use the --branchcolor option followed by a color name (ex:
         1017  +** '#ffc0c0') to specify the background color of entries in the new
         1018  +** branch when shown in the web timeline interface.  The use of
         1019  +** the --branchcolor option is not recommend.  Instead, let Fossil
         1020  +** choose the branch color automatically.
   532   1021   **
   533         -** You will be prompted for your GPG passphrase in order to sign the
   534         -** new manifest unless the "--nosign" options is used.  All files that
   535         -** have changed will be committed unless some subset of files is
   536         -** specified on the command line.
         1022  +** The --bgcolor option works like --branchcolor but only sets the
         1023  +** background color for a single check-in.  Subsequent check-ins revert
         1024  +** to the default color.
   537   1025   **
   538         -** The --branch option followed by a branch name cases the new check-in
   539         -** to be placed in the named branch.  The --bgcolor option can be followed
   540         -** by a color name (ex:  '#ffc0c0') to specify the background color of
   541         -** entries in the new branch when shown in the web timeline interface.
   542         -**
   543         -** A check-in is not permitted to fork unless the --force or -f
   544         -** option appears.  A check-in is not allowed against a closed check-in.
         1026  +** A check-in is not permitted to fork unless the --allow-fork option
         1027  +** appears.  An empty check-in (i.e. with nothing changed) is not
         1028  +** allowed unless the --allow-empty option appears.  A check-in may not
         1029  +** be older than its ancestor unless the --allow-older option appears.
         1030  +** If any of files in the check-in appear to contain unresolved merge
         1031  +** conflicts, the check-in will not be allowed unless the
         1032  +** --allow-conflict option is present.  In addition, the entire
         1033  +** check-in process may be aborted if a file contains content that
         1034  +** appears to be binary, Unicode text, or text with CR/NL line endings
         1035  +** unless the interactive user chooses to proceed.  If there is no
         1036  +** interactive user or these warnings should be skipped for some other
         1037  +** reason, the --no-warnings option may be used.  A check-in is not
         1038  +** allowed against a closed leaf.
   545   1039   **
   546   1040   ** The --private option creates a private check-in that is never synced.
   547   1041   ** Children of private check-ins are automatically private.
   548   1042   **
         1043  +** the --tag option applies the symbolic tag name to the check-in.
         1044  +**
   549   1045   ** Options:
         1046  +**    --allow-conflict           allow unresolved merge conflicts
         1047  +**    --allow-empty              allow a commit with no changes
         1048  +**    --allow-fork               allow the commit to fork
         1049  +**    --allow-older              allow a commit older than its ancestor
         1050  +**    --baseline                 use a baseline manifest in the commit process
         1051  +**    --bgcolor COLOR            apply COLOR to this one check-in only
         1052  +**    --branch NEW-BRANCH-NAME   check in to this new branch
         1053  +**    --branchcolor COLOR        apply given COLOR to the branch
         1054  +**    --comment|-m COMMENT-TEXT  use COMMENT-TEXT as commit comment
         1055  +**    --delta                    use a delta manifest in the commit process
         1056  +**    --message-file|-M FILE     read the commit comment from given file
         1057  +**    --no-warnings              omit all warnings about file contents
         1058  +**    --nosign                   do not attempt to sign this commit with gpg
         1059  +**    --private                  do not sync changes and their descendants
         1060  +**    --tag TAG-NAME             assign given tag TAG-NAME to the checkin
   550   1061   **
   551         -**    --comment|-m COMMENT-TEXT
   552         -**    --branch NEW-BRANCH-NAME
   553         -**    --bgcolor COLOR
   554         -**    --nosign
   555         -**    --force|-f
   556         -**    --private
   557         -**    
         1062  +** See also: branch, changes, checkout, extra, sync
   558   1063   */
   559   1064   void commit_cmd(void){
   560         -  int rc;
   561         -  int vid, nrid, nvid;
   562         -  Blob comment;
   563         -  const char *zComment;
   564         -  Stmt q;
   565         -  Stmt q2;
   566         -  char *zUuid, *zDate;
         1065  +  int hasChanges;        /* True if unsaved changes exist */
         1066  +  int vid;               /* blob-id of parent version */
         1067  +  int nrid;              /* blob-id of a modified file */
         1068  +  int nvid;              /* Blob-id of the new check-in */
         1069  +  Blob comment;          /* Check-in comment */
         1070  +  const char *zComment;  /* Check-in comment */
         1071  +  Stmt q;                /* Query to find files that have been modified */
         1072  +  char *zUuid;           /* UUID of the new check-in */
   567   1073     int noSign = 0;        /* True to omit signing the manifest using GPG */
   568   1074     int isAMerge = 0;      /* True if checking in a merge */
   569         -  int forceFlag = 0;     /* Force a fork */
         1075  +  int noWarningFlag = 0; /* True if skipping all warnings */
         1076  +  int forceFlag = 0;     /* Undocumented: Disables all checks */
         1077  +  int forceDelta = 0;    /* Force a delta-manifest */
         1078  +  int forceBaseline = 0; /* Force a baseline-manifest */
         1079  +  int allowConflict = 0; /* Allow unresolve merge conflicts */
         1080  +  int allowEmpty = 0;    /* Allow a commit with no changes */
         1081  +  int allowFork = 0;     /* Allow the commit to fork */
         1082  +  int allowOlder = 0;    /* Allow a commit older than its ancestor */
   570   1083     char *zManifestFile;   /* Name of the manifest file */
   571         -  int nBasename;         /* Length of "g.zLocalRoot/" */
         1084  +  int useCksum;          /* True if checksums should be computed and verified */
         1085  +  int outputManifest;    /* True to output "manifest" and "manifest.uuid" */
         1086  +  int testRun;           /* True for a test run.  Debugging only */
   572   1087     const char *zBranch;   /* Create a new branch with this name */
   573         -  const char *zBgColor;  /* Set background color when branching */
         1088  +  const char *zBrClr;    /* Set background color when branching */
         1089  +  const char *zColor;    /* One-time check-in color */
   574   1090     const char *zDateOvrd; /* Override date string */
   575   1091     const char *zUserOvrd; /* Override user name */
   576   1092     const char *zComFile;  /* Read commit message from this file */
   577         -  Blob filename;         /* complete filename */
   578         -  Blob manifest;
         1093  +  int nTag = 0;          /* Number of --tag arguments */
         1094  +  const char *zTag;      /* A single --tag argument */
         1095  +  const char **azTag = 0;/* Array of all --tag arguments */
         1096  +  Blob manifest;         /* Manifest in baseline form */
   579   1097     Blob muuid;            /* Manifest uuid */
   580         -  Blob mcksum;           /* Self-checksum on the manifest */
   581   1098     Blob cksum1, cksum2;   /* Before and after commit checksums */
   582   1099     Blob cksum1b;          /* Checksum recorded in the manifest */
   583         - 
         1100  +  int szD;               /* Size of the delta manifest */
         1101  +  int szB;               /* Size of the baseline manifest */
         1102  +  int nConflict = 0;     /* Number of unresolved merge conflicts */
         1103  +  int abortCommit = 0;
         1104  +  Blob ans;
         1105  +  char cReply;
         1106  +
   584   1107     url_proxy_options();
   585   1108     noSign = find_option("nosign",0,0)!=0;
         1109  +  forceDelta = find_option("delta",0,0)!=0;
         1110  +  forceBaseline = find_option("baseline",0,0)!=0;
         1111  +  if( forceDelta && forceBaseline ){
         1112  +    fossil_fatal("cannot use --delta and --baseline together");
         1113  +  }
         1114  +  testRun = find_option("test",0,0)!=0;
   586   1115     zComment = find_option("comment","m",1);
   587   1116     forceFlag = find_option("force", "f", 0)!=0;
         1117  +  allowConflict = find_option("allow-conflict",0,0)!=0;
         1118  +  allowEmpty = find_option("allow-empty",0,0)!=0;
         1119  +  allowFork = find_option("allow-fork",0,0)!=0;
         1120  +  allowOlder = find_option("allow-older",0,0)!=0;
         1121  +  noWarningFlag = find_option("no-warnings", 0, 0)!=0;
   588   1122     zBranch = find_option("branch","b",1);
   589         -  zBgColor = find_option("bgcolor",0,1);
         1123  +  zColor = find_option("bgcolor",0,1);
         1124  +  zBrClr = find_option("branchcolor",0,1);
         1125  +  while( (zTag = find_option("tag",0,1))!=0 ){
         1126  +    if( zTag[0]==0 ) continue;
         1127  +    azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
         1128  +    azTag[nTag++] = zTag;
         1129  +    azTag[nTag] = 0;
         1130  +  }
   590   1131     zComFile = find_option("message-file", "M", 1);
   591   1132     if( find_option("private",0,0) ){
   592   1133       g.markPrivate = 1;
   593   1134       if( zBranch==0 ) zBranch = "private";
   594         -    if( zBgColor==0 ) zBgColor = "#fec084";  /* Orange */
         1135  +    if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084";  /* Orange */
   595   1136     }
   596   1137     zDateOvrd = find_option("date-override",0,1);
   597   1138     zUserOvrd = find_option("user-override",0,1);
   598   1139     db_must_be_within_tree();
   599   1140     noSign = db_get_boolean("omitsign", 0)|noSign;
   600   1141     if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
         1142  +  useCksum = db_get_boolean("repo-cksum", 1);
         1143  +  outputManifest = db_get_boolean("manifest", 0);
   601   1144     verify_all_options();
         1145  +
         1146  +  /* Escape special characters in tags and put all tags in sorted order */
         1147  +  if( nTag ){
         1148  +    int i;
         1149  +    for(i=0; i<nTag; i++) azTag[i] = mprintf("%F", azTag[i]);
         1150  +    qsort((void*)azTag, nTag, sizeof(azTag[0]), tagCmp);
         1151  +  }
         1152  +
         1153  +  /* So that older versions of Fossil (that do not understand delta-
         1154  +  ** manifest) can continue to use this repository, do not create a new
         1155  +  ** delta-manifest unless this repository already contains one or more
         1156  +  ** delta-manifests, or unless the delta-manifest is explicitly requested
         1157  +  ** by the --delta option.
         1158  +  */
         1159  +  if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
         1160  +    forceBaseline = 1;
         1161  +  }
   602   1162   
   603   1163     /* Get the ID of the parent manifest artifact */
   604   1164     vid = db_lget_int("checkout", 0);
   605   1165     if( content_is_private(vid) ){
   606   1166       g.markPrivate = 1;
   607   1167     }
   608   1168   
   609   1169     /*
   610   1170     ** Autosync if autosync is enabled and this is not a private check-in.
   611   1171     */
   612   1172     if( !g.markPrivate ){
   613         -    autosync(AUTOSYNC_PULL);
         1173  +    if( autosync(SYNC_PULL) ){
         1174  +      blob_zero(&ans);
         1175  +      prompt_user("continue in spite of sync failure (y/N)? ", &ans);
         1176  +      cReply = blob_str(&ans)[0];
         1177  +      if( cReply!='y' && cReply!='Y' ){
         1178  +        fossil_exit(1);
         1179  +      }
         1180  +    }
         1181  +  }
         1182  +
         1183  +  /* Require confirmation to continue with the check-in if there is
         1184  +  ** clock skew
         1185  +  */
         1186  +  if( g.clockSkewSeen ){
         1187  +    blob_zero(&ans);
         1188  +    prompt_user("continue in spite of time skew (y/N)? ", &ans);
         1189  +    cReply = blob_str(&ans)[0];
         1190  +    if( cReply!='y' && cReply!='Y' ){
         1191  +      fossil_exit(1);
         1192  +    }
   614   1193     }
   615   1194   
   616   1195     /* There are two ways this command may be executed. If there are
   617   1196     ** no arguments following the word "commit", then all modified files
   618   1197     ** in the checked out directory are committed. If one or more arguments
   619   1198     ** follows "commit", then only those files are committed.
   620   1199     **
   621   1200     ** After the following function call has returned, the Global.aCommitFile[]
   622   1201     ** array is allocated to contain the "id" field from the vfile table
   623   1202     ** for each file to be committed. Or, if aCommitFile is NULL, all files
   624   1203     ** should be committed.
   625   1204     */
   626         -  select_commit_files();
   627         -  isAMerge = db_exists("SELECT 1 FROM vmerge");
         1205  +  if ( select_commit_files() ){
         1206  +    blob_zero(&ans);
         1207  +    prompt_user("continue (y/N)? ", &ans);
         1208  +    cReply = blob_str(&ans)[0];
         1209  +    if( cReply!='y' && cReply!='Y' ) fossil_exit(1);;
         1210  +  }
         1211  +  isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0");
   628   1212     if( g.aCommitFile && isAMerge ){
   629   1213       fossil_fatal("cannot do a partial commit of a merge");
   630   1214     }
         1215  +
         1216  +  /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
         1217  +  ** will generate a manifest that has two fileA entries, which is illegal.
         1218  +  ** When you think about it, the sequence above makes no sense.  So detect
         1219  +  ** it and disallow it.  Ticket [0ff64b0a5fc8].
         1220  +  */
         1221  +  if( g.aCommitFile ){
         1222  +    Stmt qRename;
         1223  +    db_prepare(&qRename,
         1224  +       "SELECT v1.pathname, v2.pathname"
         1225  +       "  FROM vfile AS v1, vfile AS v2"
         1226  +       " WHERE is_selected(v1.id)"
         1227  +       "   AND v2.origname IS NOT NULL"
         1228  +       "   AND v2.origname=v1.pathname"
         1229  +       "   AND NOT is_selected(v2.id)");
         1230  +    if( db_step(&qRename)==SQLITE_ROW ){
         1231  +      const char *zFrom = db_column_text(&qRename, 0);
         1232  +      const char *zTo = db_column_text(&qRename, 1);
         1233  +      fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
         1234  +                   "'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
         1235  +    }
         1236  +    db_finalize(&qRename);
         1237  +  }
   631   1238   
   632   1239     user_select();
   633   1240     /*
   634   1241     ** Check that the user exists.
   635   1242     */
   636   1243     if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
   637   1244       fossil_fatal("no such user: %s", g.zLogin);
   638   1245     }
   639         -  
         1246  +
         1247  +  hasChanges = unsaved_changes();
   640   1248     db_begin_transaction();
   641   1249     db_record_repository_filename(0);
   642         -  rc = unsaved_changes();
   643         -  if( rc==0 && !isAMerge && !forceFlag ){
   644         -    fossil_panic("nothing has changed");
         1250  +  if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
         1251  +    fossil_fatal("nothing has changed; use --allow-empty to override");
   645   1252     }
   646   1253   
   647         -  /* If one or more files that were named on the command line have not
   648         -  ** been modified, bail out now.
         1254  +  /* If none of the files that were named on the command line have
         1255  +  ** been modified, bail out now unless the --allow-empty or --force
         1256  +  ** flags is used.
         1257  +  */
         1258  +  if( g.aCommitFile
         1259  +   && !allowEmpty
         1260  +   && !forceFlag
         1261  +   && !db_exists(
         1262  +        "SELECT 1 FROM vfile "
         1263  +        " WHERE is_selected(id)"
         1264  +        "   AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
         1265  +  ){
         1266  +    fossil_fatal("none of the selected files have changed; use "
         1267  +                 "--allow-empty to override.");
         1268  +  }
         1269  +
         1270  +  /*
         1271  +  ** Do not allow a commit that will cause a fork unless the --allow-fork
         1272  +  ** or --force flags is used, or unless this is a private check-in.
   649   1273     */
   650         -  if( g.aCommitFile ){
   651         -    Blob unmodified;
   652         -    memset(&unmodified, 0, sizeof(Blob));
   653         -    blob_init(&unmodified, 0, 0);
   654         -    db_blob(&unmodified, 
   655         -      "SELECT pathname FROM vfile WHERE chnged = 0 AND file_is_selected(id)"
   656         -    );
   657         -    if( strlen(blob_str(&unmodified)) ){
   658         -      fossil_panic("file %s has not changed", blob_str(&unmodified));
   659         -    }
         1274  +  if( zBranch==0 && allowFork==0 && forceFlag==0
         1275  +    && g.markPrivate==0 && !is_a_leaf(vid)
         1276  +  ){
         1277  +    fossil_fatal("would fork.  \"update\" first or use --allow-fork.");
   660   1278     }
   661   1279   
   662   1280     /*
   663         -  ** Do not allow a commit that will cause a fork unless the --force flag
   664         -  ** is used or unless this is a private check-in.
   665         -  */
   666         -  if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){
   667         -    fossil_fatal("would fork.  \"update\" first or use -f or --force.");
   668         -  }
   669         -
   670         -  /*
   671         -  ** Do not allow a commit against a closed leaf 
         1281  +  ** Do not allow a commit against a closed leaf
   672   1282     */
   673   1283     if( db_exists("SELECT 1 FROM tagxref"
   674   1284                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
   675   1285                   TAG_CLOSED, vid) ){
   676   1286       fossil_fatal("cannot commit against a closed leaf");
   677   1287     }
   678   1288   
   679         -  vfile_aggregate_checksum_disk(vid, &cksum1);
         1289  +  if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
   680   1290     if( zComment ){
   681   1291       blob_zero(&comment);
   682   1292       blob_append(&comment, zComment, -1);
   683   1293     }else if( zComFile ){
   684   1294       blob_zero(&comment);
   685   1295       blob_read_from_file(&comment, zComFile);
         1296  +    blob_to_utf8_no_bom(&comment, 1);
   686   1297     }else{
   687   1298       char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
   688         -    prepare_commit_comment(&comment, zInit, zBranch, vid);
         1299  +    prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
         1300  +    if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
         1301  +      blob_zero(&ans);
         1302  +      prompt_user("unchanged check-in comment.  continue (y/N)? ", &ans);
         1303  +      cReply = blob_str(&ans)[0];
         1304  +      if( cReply!='y' && cReply!='Y' ) fossil_exit(1);;
         1305  +    }
   689   1306       free(zInit);
   690   1307     }
   691   1308     if( blob_size(&comment)==0 ){
   692         -    Blob ans;
   693   1309       blob_zero(&ans);
   694   1310       prompt_user("empty check-in comment.  continue (y/N)? ", &ans);
   695         -    if( blob_str(&ans)[0]!='y' ){
   696         -      db_end_transaction(1);
   697         -      exit(1);
         1311  +    cReply = blob_str(&ans)[0];
         1312  +    if( cReply!='y' && cReply!='Y' ){
         1313  +      fossil_exit(1);
   698   1314       }
   699   1315     }else{
   700   1316       db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
   701   1317       db_end_transaction(0);
   702   1318       db_begin_transaction();
   703   1319     }
   704   1320   
   705         -  /* Step 1: Insert records for all modified files into the blob 
         1321  +  /* Step 1: Insert records for all modified files into the blob
   706   1322     ** table. If there were arguments passed to this command, only
   707         -  ** the identified fils are inserted (if they have been modified).
         1323  +  ** the identified files are inserted (if they have been modified).
   708   1324     */
   709   1325     db_prepare(&q,
   710         -    "SELECT id, %Q || pathname, mrid FROM vfile "
   711         -    "WHERE chnged==1 AND NOT deleted AND file_is_selected(id)"
   712         -    , g.zLocalRoot
         1326  +    "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile "
         1327  +    "WHERE chnged==1 AND NOT deleted AND is_selected(id)",
         1328  +    g.zLocalRoot,
         1329  +    glob_expr("pathname", db_get("crnl-glob","")),
         1330  +    glob_expr("pathname", db_get("binary-glob","")),
         1331  +    glob_expr("pathname", db_get("encoding-glob",""))
   713   1332     );
   714   1333     while( db_step(&q)==SQLITE_ROW ){
   715   1334       int id, rid;
   716   1335       const char *zFullname;
   717   1336       Blob content;
         1337  +    int crnlOk, binOk, encodingOk, chnged;
   718   1338   
   719   1339       id = db_column_int(&q, 0);
   720   1340       zFullname = db_column_text(&q, 1);
   721   1341       rid = db_column_int(&q, 2);
         1342  +    crnlOk = db_column_int(&q, 3);
         1343  +    chnged = db_column_int(&q, 4);
         1344  +    binOk = db_column_int(&q, 5);
         1345  +    encodingOk = db_column_int(&q, 6);
   722   1346   
   723   1347       blob_zero(&content);
   724         -    blob_read_from_file(&content, zFullname);
   725         -    nrid = content_put(&content, 0, 0);
         1348  +    if( file_wd_islink(zFullname) ){
         1349  +      /* Instead of file content, put link destination path */
         1350  +      blob_read_link(&content, zFullname);
         1351  +    }else{
         1352  +      blob_read_from_file(&content, zFullname);
         1353  +    }
         1354  +    /* Do not emit any warnings when they are disabled. */
         1355  +    if( !noWarningFlag ){
         1356  +      abortCommit |= commit_warning(&content, crnlOk, binOk,
         1357  +                                    encodingOk, zFullname);
         1358  +    }
         1359  +    if( chnged==1 && contains_merge_marker(&content) ){
         1360  +      Blob fname; /* Relative pathname of the file */
         1361  +
         1362  +      nConflict++;
         1363  +      file_relative_name(zFullname, &fname, 0);
         1364  +      fossil_print("possible unresolved merge conflict in %s\n",
         1365  +                   blob_str(&fname));
         1366  +      blob_reset(&fname);
         1367  +    }
         1368  +    nrid = content_put(&content);
   726   1369       blob_reset(&content);
   727   1370       if( rid>0 ){
   728   1371         content_deltify(rid, nrid, 0);
   729   1372       }
   730   1373       db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
   731   1374       db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   732   1375     }
   733   1376     db_finalize(&q);
         1377  +  if( nConflict && !allowConflict ){
         1378  +    fossil_fatal("abort due to unresolved merge conflicts; "
         1379  +                 "use --allow-conflict to override");
         1380  +  } else if( abortCommit ){
         1381  +    fossil_fatal("one or more files were converted on your request; "
         1382  +                 "please re-test before committing");
         1383  +  }
   734   1384   
   735         -  /* Create the manifest */
   736         -  blob_zero(&manifest);
         1385  +  /* Create the new manifest */
   737   1386     if( blob_size(&comment)==0 ){
   738   1387       blob_append(&comment, "(no comment)", -1);
   739   1388     }
   740         -  blob_appendf(&manifest, "C %F\n", blob_str(&comment));
   741         -  zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
   742         -  zDate[10] = 'T';
   743         -  blob_appendf(&manifest, "D %s\n", zDate);
   744         -  zDate[10] = ' ';
   745         -  db_prepare(&q,
   746         -    "SELECT pathname, uuid, origname, blob.rid, isexe"
   747         -    "  FROM vfile JOIN blob ON vfile.mrid=blob.rid"
   748         -    " WHERE NOT deleted AND vfile.vid=%d"
   749         -    " ORDER BY 1", vid);
   750         -  blob_zero(&filename);
   751         -  blob_appendf(&filename, "%s", g.zLocalRoot);
   752         -  nBasename = blob_size(&filename);
   753         -  while( db_step(&q)==SQLITE_ROW ){
   754         -    const char *zName = db_column_text(&q, 0);
   755         -    const char *zUuid = db_column_text(&q, 1);
   756         -    const char *zOrig = db_column_text(&q, 2);
   757         -    int frid = db_column_int(&q, 3);
   758         -    int isexe = db_column_int(&q, 4);
   759         -    const char *zPerm;
   760         -    blob_append(&filename, zName, -1);
   761         -#ifndef __MINGW32__
   762         -    /* For unix, extract the "executable" permission bit directly from
   763         -    ** the filesystem.  On windows, the "executable" bit is retained
   764         -    ** unchanged from the original. */
   765         -    isexe = file_isexe(blob_str(&filename));
   766         -#endif
   767         -    if( isexe ){
   768         -      zPerm = " x";
         1389  +  if( forceDelta ){
         1390  +    blob_zero(&manifest);
         1391  +  }else{
         1392  +    create_manifest(&manifest, 0, 0, &comment, vid,
         1393  +                    !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
         1394  +                    zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
         1395  +                    azTag, &szB);
         1396  +  }
         1397  +
         1398  +  /* See if a delta-manifest would be more appropriate */
         1399  +  if( !forceBaseline ){
         1400  +    const char *zBaselineUuid;
         1401  +    Manifest *pParent;
         1402  +    Manifest *pBaseline;
         1403  +    pParent = manifest_get(vid, CFTYPE_MANIFEST);
         1404  +    if( pParent && pParent->zBaseline ){
         1405  +      zBaselineUuid = pParent->zBaseline;
         1406  +      pBaseline = manifest_get_by_name(zBaselineUuid, 0);
   769   1407       }else{
   770         -      zPerm = "";
         1408  +      zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
         1409  +      pBaseline = pParent;
   771   1410       }
   772         -    blob_resize(&filename, nBasename);
   773         -    if( zOrig==0 || strcmp(zOrig,zName)==0 ){
   774         -      blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
   775         -    }else{
   776         -      if( zPerm[0]==0 ){ zPerm = " w"; }
   777         -      blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
         1411  +    if( pBaseline ){
         1412  +      Blob delta;
         1413  +      create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
         1414  +                      !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
         1415  +                      zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
         1416  +                      azTag, &szD);
         1417  +      /*
         1418  +      ** At this point, two manifests have been constructed, either of
         1419  +      ** which would work for this checkin.  The first manifest (held
         1420  +      ** in the "manifest" variable) is a baseline manifest and the second
         1421  +      ** (held in variable named "delta") is a delta manifest.  The
         1422  +      ** question now is: which manifest should we use?
         1423  +      **
         1424  +      ** Let B be the number of F-cards in the baseline manifest and
         1425  +      ** let D be the number of F-cards in the delta manifest, plus one for
         1426  +      ** the B-card.  (B is held in the szB variable and D is held in the
         1427  +      ** szD variable.)  Assume that all delta manifests adds X new F-cards.
         1428  +      ** Then to minimize the total number of F- and B-cards in the repository,
         1429  +      ** we should use the delta manifest if and only if:
         1430  +      **
         1431  +      **      D*D < B*X - X*X
         1432  +      **
         1433  +      ** X is an unknown here, but for most repositories, we will not be
         1434  +      ** far wrong if we assume X=3.
         1435  +      */
         1436  +      if( forceDelta || (szD*szD)<(szB*3-9) ){
         1437  +        blob_reset(&manifest);
         1438  +        manifest = delta;
         1439  +      }else{
         1440  +        blob_reset(&delta);
         1441  +      }
         1442  +    }else if( forceDelta ){
         1443  +      fossil_panic("unable to find a baseline-manifest for the delta");
   778   1444       }
   779         -    if( !g.markPrivate ) content_make_public(frid);
   780   1445     }
   781         -  blob_reset(&filename);
   782         -  db_finalize(&q);
   783         -  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
   784         -  blob_appendf(&manifest, "P %s", zUuid);
   785         -  checkin_verify_younger(vid, zUuid, zDate);
   786         -
   787         -  db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
   788         -  db_bind_int(&q2, ":id", 0);
   789         -  while( db_step(&q2)==SQLITE_ROW ){
   790         -    int mid = db_column_int(&q2, 0);
   791         -    if( !g.markPrivate && content_is_private(mid) ) continue;
   792         -    zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
   793         -    if( zUuid ){
   794         -      blob_appendf(&manifest, " %s", zUuid);
   795         -      checkin_verify_younger(mid, zUuid, zDate);
   796         -      free(zUuid);
         1446  +  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
         1447  +    blob_zero(&ans);
         1448  +    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
         1449  +    cReply = blob_str(&ans)[0];
         1450  +    if( cReply!='y' && cReply!='Y' ){
         1451  +      fossil_exit(1);
   797   1452       }
   798   1453     }
   799         -  db_reset(&q2);
   800   1454   
   801         -  blob_appendf(&manifest, "\n");
   802         -  blob_appendf(&manifest, "R %b\n", &cksum1);
   803         -  if( zBranch && zBranch[0] ){
   804         -    Stmt q;
   805         -    if( zBgColor && zBgColor[0] ){
   806         -      blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
   807         -    }
   808         -    blob_appendf(&manifest, "T *branch * %F\n", zBranch);
   809         -    blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
         1455  +  /* If the --test option is specified, output the manifest file
         1456  +  ** and rollback the transaction.
         1457  +  */
         1458  +  if( testRun ){
         1459  +    blob_write_to_file(&manifest, "");
         1460  +  }
   810   1461   
   811         -    /* Cancel all other symbolic tags */
   812         -    db_prepare(&q,
   813         -        "SELECT tagname FROM tagxref, tag"
   814         -        " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
   815         -        "   AND tagtype>0 AND tagname GLOB 'sym-*'"
   816         -        "   AND tagname!='sym-'||%Q"
   817         -        " ORDER BY tagname",
   818         -        vid, zBranch);
   819         -    while( db_step(&q)==SQLITE_ROW ){
   820         -      const char *zTag = db_column_text(&q, 0);
   821         -      blob_appendf(&manifest, "T -%F *\n", zTag);
   822         -    }
   823         -    db_finalize(&q);
   824         -  }  
   825         -  blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
   826         -  md5sum_blob(&manifest, &mcksum);
   827         -  blob_appendf(&manifest, "Z %b\n", &mcksum);
   828         -  zManifestFile = mprintf("%smanifest", g.zLocalRoot);
   829         -  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
   830         -    Blob ans;
   831         -    blob_zero(&ans);
   832         -    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
   833         -    if( blob_str(&ans)[0]!='y' ){
   834         -      db_end_transaction(1);
   835         -      exit(1);
   836         -    }
         1462  +  if( outputManifest ){
         1463  +    zManifestFile = mprintf("%smanifest", g.zLocalRoot);
         1464  +    blob_write_to_file(&manifest, zManifestFile);
         1465  +    blob_reset(&manifest);
         1466  +    blob_read_from_file(&manifest, zManifestFile);
         1467  +    free(zManifestFile);
   837   1468     }
   838         -  blob_write_to_file(&manifest, zManifestFile);
   839         -  blob_reset(&manifest);
   840         -  blob_read_from_file(&manifest, zManifestFile);
   841         -  free(zManifestFile);
   842         -  nvid = content_put(&manifest, 0, 0);
         1469  +  nvid = content_put(&manifest);
   843   1470     if( nvid==0 ){
   844   1471       fossil_panic("trouble committing manifest: %s", g.zErrMsg);
   845   1472     }
   846   1473     db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
   847   1474     manifest_crosslink(nvid, &manifest);
         1475  +  assert( blob_is_reset(&manifest) );
   848   1476     content_deltify(vid, nvid, 0);
   849   1477     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
   850         -  printf("New_Version: %s\n", zUuid);
   851         -  zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
   852         -  blob_zero(&muuid);
   853         -  blob_appendf(&muuid, "%s\n", zUuid);
   854         -  blob_write_to_file(&muuid, zManifestFile);
   855         -  free(zManifestFile);
   856         -  blob_reset(&muuid);
         1478  +  fossil_print("New_Version: %s\n", zUuid);
         1479  +  if( outputManifest ){
         1480  +    zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
         1481  +    blob_zero(&muuid);
         1482  +    blob_appendf(&muuid, "%s\n", zUuid);
         1483  +    blob_write_to_file(&muuid, zManifestFile);
         1484  +    free(zManifestFile);
         1485  +    blob_reset(&muuid);
         1486  +  }
   857   1487   
   858         -  
         1488  +
   859   1489     /* Update the vfile and vmerge tables */
   860   1490     db_multi_exec(
   861         -    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);"
   862         -    "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;"
         1491  +    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
         1492  +    "DELETE FROM vmerge;"
   863   1493       "UPDATE vfile SET vid=%d;"
   864   1494       "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
   865         -    " WHERE file_is_selected(id);"
         1495  +    " WHERE is_selected(id);"
   866   1496       , vid, nvid
   867   1497     );
   868   1498     db_lset_int("checkout", nvid);
   869   1499   
   870         -  /* Verify that the repository checksum matches the expected checksum
   871         -  ** calculated before the checkin started (and stored as the R record
   872         -  ** of the manifest file).
   873         -  */
   874         -  vfile_aggregate_checksum_repository(nvid, &cksum2);
   875         -  if( blob_compare(&cksum1, &cksum2) ){
   876         -    fossil_panic("tree checksum does not match repository after commit");
   877         -  }
         1500  +  if( useCksum ){
         1501  +    /* Verify that the repository checksum matches the expected checksum
         1502  +    ** calculated before the checkin started (and stored as the R record
         1503  +    ** of the manifest file).
         1504  +    */
         1505  +    vfile_aggregate_checksum_repository(nvid, &cksum2);
         1506  +    if( blob_compare(&cksum1, &cksum2) ){
         1507  +      vfile_compare_repository_to_disk(nvid);
         1508  +      fossil_fatal("working checkout does not match what would have ended "
         1509  +                   "up in the repository:  %b versus %b",
         1510  +                   &cksum1, &cksum2);
         1511  +    }
   878   1512   
   879         -  /* Verify that the manifest checksum matches the expected checksum */
   880         -  vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
   881         -  if( blob_compare(&cksum1, &cksum1b) ){
   882         -    fossil_panic("manifest checksum does not agree with manifest: "
   883         -                 "%b versus %b", &cksum1, &cksum1b);
   884         -  }
   885         -  if( blob_compare(&cksum1, &cksum2) ){
   886         -    fossil_panic("tree checksum does not match manifest after commit: "
   887         -                 "%b versus %b", &cksum1, &cksum2);
   888         -  }
         1513  +    /* Verify that the manifest checksum matches the expected checksum */
         1514  +    vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
         1515  +    if( blob_compare(&cksum1, &cksum1b) ){
         1516  +      fossil_fatal("manifest checksum self-test failed: "
         1517  +                   "%b versus %b", &cksum1, &cksum1b);
         1518  +    }
         1519  +    if( blob_compare(&cksum1, &cksum2) ){
         1520  +      fossil_fatal(
         1521  +         "working checkout does not match manifest after commit: "
         1522  +         "%b versus %b", &cksum1, &cksum2);
         1523  +    }
   889   1524   
   890         -  /* Verify that the commit did not modify any disk images. */
   891         -  vfile_aggregate_checksum_disk(nvid, &cksum2);
   892         -  if( blob_compare(&cksum1, &cksum2) ){
   893         -    fossil_panic("tree checksums before and after commit do not match");
         1525  +    /* Verify that the commit did not modify any disk images. */
         1526  +    vfile_aggregate_checksum_disk(nvid, &cksum2);
         1527  +    if( blob_compare(&cksum1, &cksum2) ){
         1528  +      fossil_fatal("working checkout before and after commit does not match");
         1529  +    }
   894   1530     }
   895   1531   
   896   1532     /* Clear the undo/redo stack */
   897   1533     undo_reset();
   898   1534   
   899   1535     /* Commit */
   900   1536     db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
         1537  +  if( testRun ){
         1538  +    db_end_transaction(1);
         1539  +    exit(1);
         1540  +  }
   901   1541     db_end_transaction(0);
   902   1542   
   903   1543     if( !g.markPrivate ){
   904         -    autosync(AUTOSYNC_PUSH);  
         1544  +    autosync(SYNC_PUSH|SYNC_PULL);
   905   1545     }
   906   1546     if( count_nonbranch_children(vid)>1 ){
   907         -    printf("**** warning: a fork has occurred *****\n");
         1547  +    fossil_print("**** warning: a fork has occurred *****\n");
   908   1548     }
   909   1549   }

Changes to src/checkout.c.

    31     31   **     2:   There is no existing checkout
    32     32   */
    33     33   int unsaved_changes(void){
    34     34     int vid;
    35     35     db_must_be_within_tree();
    36     36     vid = db_lget_int("checkout",0);
    37     37     if( vid==0 ) return 2;
    38         -  vfile_check_signature(vid, 1);
           38  +  vfile_check_signature(vid, CKSIG_ENOTFILE);
    39     39     return db_exists("SELECT 1 FROM vfile WHERE chnged"
    40     40                      " OR coalesce(origname!=pathname,0)");
    41     41   }
    42     42   
    43     43   /*
    44     44   ** Undo the current check-out.  Unlink all files from the disk.
    45     45   ** Clear the VFILE table.
................................................................................
    58     58   ** If anything goes wrong, panic.
    59     59   */
    60     60   int load_vfile(const char *zName){
    61     61     Blob uuid;
    62     62     int vid;
    63     63   
    64     64     blob_init(&uuid, zName, -1);
    65         -  if( name_to_uuid(&uuid, 1) ){
           65  +  if( name_to_uuid(&uuid, 1, "ci") ){
    66     66       fossil_panic(g.zErrMsg);
    67     67     }
    68     68     vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
    69     69     if( vid==0 ){
    70     70       fossil_fatal("no such check-in: %s", g.argv[2]);
    71     71     }
    72     72     if( !is_a_version(vid) ){
    73     73       fossil_fatal("object [%.10s] is not a check-in", blob_str(&uuid));
    74     74     }
    75     75     load_vfile_from_rid(vid);
    76     76     return vid;
    77     77   }
    78     78   
    79         -/*
    80         -** Load a vfile from a record ID.
    81         -*/
    82         -void load_vfile_from_rid(int vid){
    83         -  Blob manifest;
    84         -
    85         -  if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
    86         -    return;
    87         -  }
    88         -  content_get(vid, &manifest);
    89         -  vfile_build(vid, &manifest);
    90         -  blob_reset(&manifest);
    91         -}
    92         -
    93     79   /*
    94     80   ** Set or clear the vfile.isexe flag for a file.
    95     81   */
    96     82   static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
    97         -  db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
    98         -                onoff, vid, zFilename);
           83  +  static Stmt s;
           84  +  db_static_prepare(&s,
           85  +    "UPDATE vfile SET isexe=:isexe"
           86  +    " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
           87  +  );
           88  +  db_bind_int(&s, ":isexe", onoff);
           89  +  db_bind_int(&s, ":vid", vid);
           90  +  db_bind_text(&s, ":path", zFilename);
           91  +  db_step(&s);
           92  +  db_reset(&s);
           93  +}
           94  +
           95  +/*
           96  +** Set or clear the execute permission bit (as appropriate) for all
           97  +** files in the current check-out, and replace files that have
           98  +** symlink bit with actual symlinks.
           99  +*/
          100  +void checkout_set_all_exe(int vid){
          101  +  Blob filename;
          102  +  int baseLen;
          103  +  Manifest *pManifest;
          104  +  ManifestFile *pFile;
          105  +
          106  +  /* Check the EXE permission status of all files
          107  +  */
          108  +  pManifest = manifest_get(vid, CFTYPE_MANIFEST);
          109  +  if( pManifest==0 ) return;
          110  +  blob_zero(&filename);
          111  +  blob_appendf(&filename, "%s", g.zLocalRoot);
          112  +  baseLen = blob_size(&filename);
          113  +  manifest_file_rewind(pManifest);
          114  +  while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
          115  +    int isExe;
          116  +    blob_append(&filename, pFile->zName, -1);
          117  +    isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
          118  +    file_wd_setexe(blob_str(&filename), isExe);
          119  +    set_or_clear_isexe(pFile->zName, vid, isExe);
          120  +    blob_resize(&filename, baseLen);
          121  +  }
          122  +  blob_reset(&filename);
          123  +  manifest_destroy(pManifest);
    99    124   }
          125  +
   100    126   
   101    127   /*
   102         -** Read the manifest file given by vid out of the repository
   103         -** and store it in the root of the local check-out.
          128  +** If the "manifest" setting is true, then automatically generate
          129  +** files named "manifest" and "manifest.uuid" containing, respectively,
          130  +** the text of the manifest and the artifact ID of the manifest.
   104    131   */
   105    132   void manifest_to_disk(int vid){
   106    133     char *zManFile;
   107    134     Blob manifest;
   108    135     Blob hash;
   109         -  Blob filename;
   110         -  int baseLen;
   111         -  int i;
   112         -  Manifest m;
   113    136   
   114         -  blob_zero(&manifest);
   115         -  zManFile = mprintf("%smanifest", g.zLocalRoot);
   116         -  content_get(vid, &manifest);
   117         -  blob_write_to_file(&manifest, zManFile);
   118         -  free(zManFile);
   119         -  blob_zero(&hash);
   120         -  sha1sum_blob(&manifest, &hash);
   121         -  zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
   122         -  blob_append(&hash, "\n", 1);
   123         -  blob_write_to_file(&hash, zManFile);
   124         -  free(zManFile);
   125         -  blob_reset(&hash);
   126         -  manifest_parse(&m, &manifest);
   127         -  blob_zero(&filename);
   128         -  blob_appendf(&filename, "%s/", g.zLocalRoot);
   129         -  baseLen = blob_size(&filename);
   130         -  for(i=0; i<m.nFile; i++){ 
   131         -    int isExe;
   132         -    blob_append(&filename, m.aFile[i].zName, -1);
   133         -    isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
   134         -    file_setexe(blob_str(&filename), isExe);
   135         -    set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
   136         -    blob_resize(&filename, baseLen);
   137         -  }
   138         -  blob_reset(&filename);
   139         -  manifest_clear(&m);
          137  +  if( db_get_boolean("manifest",0) ){
          138  +    blob_zero(&manifest);
          139  +    content_get(vid, &manifest);
          140  +    zManFile = mprintf("%smanifest", g.zLocalRoot);
          141  +    blob_write_to_file(&manifest, zManFile);
          142  +    free(zManFile);
          143  +    blob_zero(&hash);
          144  +    sha1sum_blob(&manifest, &hash);
          145  +    zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
          146  +    blob_append(&hash, "\n", 1);
          147  +    blob_write_to_file(&hash, zManFile);
          148  +    free(zManFile);
          149  +    blob_reset(&hash);
          150  +  }else{
          151  +    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
          152  +      zManFile = mprintf("%smanifest", g.zLocalRoot);
          153  +      file_delete(zManFile);
          154  +      free(zManFile);
          155  +    }
          156  +    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
          157  +      zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
          158  +      file_delete(zManFile);
          159  +      free(zManFile);
          160  +    }
          161  +  }
          162  +    
   140    163   }
   141    164   
   142    165   /*
   143         -** COMMAND: checkout
          166  +** COMMAND: checkout*
          167  +** COMMAND: co*
   144    168   **
   145         -** Usage: %fossil checkout VERSION ?-f|--force? ?--keep?
          169  +** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
          170  +**    or: %fossil co ?VERSION | --latest? ?OPTIONS?
   146    171   **
   147    172   ** Check out a version specified on the command-line.  This command
   148    173   ** will abort if there are edited files in the current checkout unless
   149    174   ** the --force option appears on the command-line.  The --keep option
   150    175   ** leaves files on disk unchanged, except the manifest and manifest.uuid
   151    176   ** files.
   152    177   **
   153    178   ** The --latest flag can be used in place of VERSION to checkout the
   154    179   ** latest version in the repository.
          180  +** 
          181  +** Options:
          182  +**    --force   Ignore edited files in the current checkout
          183  +**    --keep    Only update the manifest and manifest.uuid files
   155    184   **
   156         -** See also the "update" command.
          185  +** See also: update
   157    186   */
   158    187   void checkout_cmd(void){
   159    188     int forceFlag;                 /* Force checkout even if edits exist */
   160    189     int keepFlag;                  /* Do not change any files on disk */
   161    190     int latestFlag;                /* Checkout the latest version */
   162    191     char *zVers;                   /* Version to checkout */
          192  +  int promptFlag;                /* True to prompt before overwriting */
   163    193     int vid, prior;
   164    194     Blob cksum1, cksum1b, cksum2;
   165    195     
   166    196     db_must_be_within_tree();
   167    197     db_begin_transaction();
   168    198     forceFlag = find_option("force","f",0)!=0;
   169    199     keepFlag = find_option("keep",0,0)!=0;
   170    200     latestFlag = find_option("latest",0,0)!=0;
          201  +  promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
   171    202     if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
   172    203        usage("VERSION|--latest ?--force? ?--keep?");
   173    204     }
   174    205     if( !forceFlag && unsaved_changes()==1 ){
   175    206       fossil_fatal("there are unsaved changes in the current checkout");
   176    207     }
   177    208     if( forceFlag ){
................................................................................
   201    232       return;
   202    233     }
   203    234     if( !keepFlag ){
   204    235       uncheckout(prior);
   205    236     }
   206    237     db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   207    238     if( !keepFlag ){
   208         -    vfile_to_disk(vid, 0, 1);
          239  +    vfile_to_disk(vid, 0, 1, promptFlag);
   209    240     }
          241  +  checkout_set_all_exe(vid);
   210    242     manifest_to_disk(vid);
          243  +  ensure_empty_dirs_created();
   211    244     db_lset_int("checkout", vid);
   212    245     undo_reset();
   213    246     db_multi_exec("DELETE FROM vmerge");
   214         -  if( !keepFlag ){
          247  +  if( !keepFlag && db_get_boolean("repo-cksum",1) ){
   215    248       vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
   216    249       vfile_aggregate_checksum_disk(vid, &cksum2);
   217    250       if( blob_compare(&cksum1, &cksum2) ){
   218         -      printf("WARNING: manifest checksum does not agree with disk\n");
          251  +      fossil_print("WARNING: manifest checksum does not agree with disk\n");
   219    252       }
   220         -    if( blob_compare(&cksum1, &cksum1b) ){
   221         -      printf("WARNING: manifest checksum does not agree with manifest\n");
          253  +    if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){
          254  +      fossil_print("WARNING: manifest checksum does not agree with manifest\n");
   222    255       }
   223    256     }
   224    257     db_end_transaction(0);
   225    258   }
   226    259   
   227    260   /*
   228    261   ** Unlink the local database file
   229    262   */
   230         -void unlink_local_database(void){
   231         -  static const char *azFile[] = {
   232         -     "%s_FOSSIL_",
   233         -     "%s_FOSSIL_-journal",
   234         -     "%s.fos",
   235         -     "%s.fos-journal",
   236         -  };
          263  +static void unlink_local_database(int manifestOnly){
          264  +  const char *zReserved;
   237    265     int i;
   238         -  for(i=0; i<sizeof(azFile)/sizeof(azFile[0]); i++){
   239         -    char *z = mprintf(azFile[i], g.zLocalRoot);
   240         -    unlink(z);
   241         -    free(z);
          266  +  for(i=0; (zReserved = fossil_reserved_name(i, 1))!=0; i++){
          267  +    if( manifestOnly==0 || zReserved[0]=='m' ){
          268  +      char *z;
          269  +      z = mprintf("%s%s", g.zLocalRoot, zReserved);
          270  +      file_delete(z);
          271  +      free(z);
          272  +    }
   242    273     }
   243    274   }
   244    275   
   245    276   /*
   246         -** COMMAND: close
          277  +** COMMAND: close*
   247    278   **
   248         -** Usage: %fossil close ?-f|--force?
          279  +** Usage: %fossil close ?OPTIONS?
   249    280   **
   250    281   ** The opposite of "open".  Close the current database connection.
   251    282   ** Require a -f or --force flag if there are unsaved changed in the
   252    283   ** current check-out.
          284  +**
          285  +** Options:
          286  +**   --force|-f  necessary to close a check out with uncommitted changes
          287  +**
          288  +** See also: open
   253    289   */
   254    290   void close_cmd(void){
   255    291     int forceFlag = find_option("force","f",0)!=0;
   256    292     db_must_be_within_tree();
   257    293     if( !forceFlag && unsaved_changes()==1 ){
   258    294       fossil_fatal("there are unsaved changes in the current checkout");
   259    295     }
   260         -  db_close();
   261         -  unlink_local_database();
          296  +  if( db_is_writeable("repository") ){
          297  +    db_multi_exec("DELETE FROM config WHERE name='ckout:%q'", g.zLocalRoot);
          298  +  }
          299  +  unlink_local_database(1);
          300  +  db_close(1);
          301  +  unlink_local_database(0);
   262    302   }

Changes to src/clearsign.c.

    37     37       return 0;
    38     38     }
    39     39     zRand = db_text(0, "SELECT hex(randomblob(10))");
    40     40     zOut = mprintf("out-%s", zRand);
    41     41     zIn = mprintf("in-%z", zRand);
    42     42     blob_write_to_file(pIn, zOut);
    43     43     zCmd = mprintf("%s %s %s", zBase, zIn, zOut);
    44         -  rc = portable_system(zCmd);
           44  +  rc = fossil_system(zCmd);
    45     45     free(zCmd);
    46     46     if( rc==0 ){
    47     47       if( pOut==pIn ){
    48     48         blob_reset(pIn);
    49     49       }
    50     50       blob_zero(pOut);
    51     51       blob_read_from_file(pOut, zIn);
    52     52     }else{
    53     53       if( pOut!=pIn ){
    54     54         blob_copy(pOut, pIn);
    55     55       }
    56     56     }
    57         -  unlink(zOut);
    58         -  unlink(zIn);
           57  +  file_delete(zOut);
           58  +  file_delete(zIn);
    59     59     free(zOut);
    60     60     free(zIn);
    61     61     return rc;
    62     62   }

Changes to src/clone.c.

    17     17   **
    18     18   ** This file contains code used to clone a repository
    19     19   */
    20     20   #include "config.h"
    21     21   #include "clone.h"
    22     22   #include <assert.h>
    23     23   
           24  +/*
           25  +** If there are public BLOBs that deltas from private BLOBs, then
           26  +** undeltify the public BLOBs so that the private BLOBs may be safely
           27  +** deleted.
           28  +*/
           29  +void fix_private_blob_dependencies(int showWarning){
           30  +  Bag toUndelta;
           31  +  Stmt q;
           32  +  int rid;
           33  +
           34  +  /* Careful:  We are about to delete all BLOB entries that are private.
           35  +  ** So make sure that any no public BLOBs are deltas from a private BLOB.
           36  +  ** Otherwise after the deletion, we won't be able to recreate the public
           37  +  ** BLOBs.
           38  +  */
           39  +  db_prepare(&q,
           40  +    "SELECT "
           41  +    "   rid, (SELECT uuid FROM blob WHERE rid=delta.rid),"
           42  +    "   srcid, (SELECT uuid FROM blob WHERE rid=delta.srcid)"
           43  +    "  FROM delta"
           44  +    " WHERE srcid in private AND rid NOT IN private"
           45  +  );
           46  +  bag_init(&toUndelta);
           47  +  while( db_step(&q)==SQLITE_ROW ){
           48  +    int rid = db_column_int(&q, 0);
           49  +    const char *zId = db_column_text(&q, 1);
           50  +    int srcid = db_column_int(&q, 2);
           51  +    const char *zSrc = db_column_text(&q, 3);
           52  +    if( showWarning ){
           53  +      fossil_warning(
           54  +        "public artifact %S (%d) is a delta from private artifact %S (%d)",
           55  +        zId, rid, zSrc, srcid
           56  +      );
           57  +    }
           58  +    bag_insert(&toUndelta, rid);
           59  +  }
           60  +  db_finalize(&q);
           61  +  while( (rid = bag_first(&toUndelta))>0 ){
           62  +    content_undelta(rid);
           63  +    bag_remove(&toUndelta, rid);
           64  +  }
           65  +  bag_clear(&toUndelta);
           66  +}
           67  +
           68  +/*
           69  +** Delete all private content from a repository.
           70  +*/
           71  +void delete_private_content(void){
           72  +  fix_private_blob_dependencies(1);
           73  +  db_multi_exec(
           74  +    "DELETE FROM blob WHERE rid IN private;"
           75  +    "DELETE FROM delta WHERE rid IN private;"
           76  +    "DELETE FROM private;"
           77  +    "DROP TABLE IF EXISTS modreq;"
           78  +  );
           79  +}
    24     80   
    25     81   
    26     82   /*
    27     83   ** COMMAND: clone
    28     84   **
    29     85   ** Usage: %fossil clone ?OPTIONS? URL FILENAME
    30     86   **
................................................................................
    32     88   ** file named FILENAME.  
    33     89   **
    34     90   ** By default, your current login name is used to create the default
    35     91   ** admin user. This can be overridden using the -A|--admin-user
    36     92   ** parameter.
    37     93   **
    38     94   ** Options:
    39         -**
    40         -**    --admin-user|-A USERNAME
           95  +**    --admin-user|-A USERNAME   Make USERNAME the administrator
           96  +**    --private                  Also clone private branches 
           97  +**    --ssl-identity=filename    Use the SSL identity if requested by the server
    41     98   **
           99  +** See also: init
    42    100   */
    43    101   void clone_cmd(void){
    44    102     char *zPassword;
    45    103     const char *zDefaultUser;   /* Optional name of the default user */
          104  +  const char *zPw;            /* The user clone password */
          105  +  int nErr = 0;
          106  +  int bPrivate = 0;           /* Also clone private branches */
    46    107   
          108  +  if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
    47    109     url_proxy_options();
    48    110     if( g.argc < 4 ){
    49    111       usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
    50    112     }
    51    113     db_open_config(0);
    52    114     if( file_size(g.argv[3])>0 ){
    53    115       fossil_panic("file already exists: %s", g.argv[3]);
................................................................................
    54    116     }
    55    117   
    56    118     zDefaultUser = find_option("admin-user","A",1);
    57    119   
    58    120     url_parse(g.argv[2]);
    59    121     if( g.urlIsFile ){
    60    122       file_copy(g.urlName, g.argv[3]);
    61         -    db_close();
          123  +    db_close(1);
    62    124       db_open_repository(g.argv[3]);
    63    125       db_record_repository_filename(g.argv[3]);
    64    126       db_multi_exec(
    65         -      "REPLACE INTO config(name,value)"
    66         -      " VALUES('server-code', lower(hex(randomblob(20))));"
    67         -      "REPLACE INTO config(name,value)"
    68         -      " VALUES('last-sync-url', '%q');",
          127  +      "REPLACE INTO config(name,value,mtime)"
          128  +      " VALUES('server-code', lower(hex(randomblob(20))),now());"
          129  +      "REPLACE INTO config(name,value,mtime)"
          130  +      " VALUES('last-sync-url', '%q',now());",
    69    131         g.urlCanonical
    70    132       );
    71         -    db_multi_exec(
    72         -       "DELETE FROM blob WHERE rid IN private;"
    73         -       "DELETE FROM delta wHERE rid IN private;"
    74         -       "DELETE FROM private;"
    75         -    );
          133  +    if( !bPrivate ) delete_private_content();
    76    134       shun_artifacts();
    77         -    g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'");
    78         -    if( g.zLogin==0 ){
    79         -      db_create_default_users(1,zDefaultUser);
          135  +    db_create_default_users(1, zDefaultUser);
          136  +    if( zDefaultUser ){
          137  +      g.zLogin = zDefaultUser;
          138  +    }else{
          139  +      g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'");
    80    140       }
    81         -    printf("Repository cloned into %s\n", g.argv[3]);
          141  +    fossil_print("Repository cloned into %s\n", g.argv[3]);
    82    142     }else{
    83    143       db_create_repository(g.argv[3]);
    84    144       db_open_repository(g.argv[3]);
    85    145       db_begin_transaction();
    86    146       db_record_repository_filename(g.argv[3]);
    87         -    db_initial_setup(0, zDefaultUser, 0);
          147  +    db_initial_setup(0, 0, zDefaultUser, 0);
    88    148       user_select();
    89    149       db_set("content-schema", CONTENT_SCHEMA, 0);
    90    150       db_set("aux-schema", AUX_SCHEMA, 0);
    91    151       db_set("last-sync-url", g.argv[2], 0);
          152  +    if( g.zSSLIdentity!=0 ){
          153  +      /* If the --ssl-identity option was specified, store it as a setting */
          154  +      Blob fn;
          155  +      blob_zero(&fn);
          156  +      file_canonical_name(g.zSSLIdentity, &fn, 0);
          157  +      db_set("ssl-identity", blob_str(&fn), 0);
          158  +      blob_reset(&fn);
          159  +    }
    92    160       db_multi_exec(
    93         -      "REPLACE INTO config(name,value)"
    94         -      " VALUES('server-code', lower(hex(randomblob(20))));"
          161  +      "REPLACE INTO config(name,value,mtime)"
          162  +      " VALUES('server-code', lower(hex(randomblob(20))), now());"
    95    163       );
    96    164       url_enable_proxy(0);
          165  +    url_get_password_if_needed();
    97    166       g.xlinkClusterOnly = 1;
    98         -    client_sync(0,0,1,CONFIGSET_ALL,0);
          167  +    nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
    99    168       g.xlinkClusterOnly = 0;
   100    169       verify_cancel();
   101    170       db_end_transaction(0);
   102         -    db_close();
          171  +    db_close(1);
          172  +    if( nErr ){
          173  +      file_delete(g.argv[3]);
          174  +      fossil_fatal("server returned an error - clone aborted");
          175  +    }
   103    176       db_open_repository(g.argv[3]);
   104    177     }
   105    178     db_begin_transaction();
   106         -  printf("Rebuilding repository meta-data...\n");
   107         -  rebuild_db(0, 1);
   108         -  printf("project-id: %s\n", db_get("project-code", 0));
   109         -  printf("server-id:  %s\n", db_get("server-code", 0));
          179  +  fossil_print("Rebuilding repository meta-data...\n");
          180  +  rebuild_db(0, 1, 0);
          181  +  fossil_print("project-id: %s\n", db_get("project-code", 0));
          182  +  fossil_print("server-id:  %s\n", db_get("server-code", 0));
   110    183     zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
   111         -  printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
          184  +  fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
          185  +  zPw = g.urlPasswd;
          186  +  if( !g.dontKeepUrl && zPw) db_set("last-sync-pw", obscure(zPw), 0);
   112    187     db_end_transaction(0);
   113    188   }

Changes to src/comformat.c.

    36     36     int tlen = lineLength - indent;
    37     37     int si, sk, i, k;
    38     38     int doIndent = 0;
    39     39     char zBuf[400];
    40     40     int lineCnt = 0; 
    41     41   
    42     42     for(;;){
    43         -    while( isspace(zText[0]) ){ zText++; }
           43  +    while( fossil_isspace(zText[0]) ){ zText++; }
    44     44       if( zText[0]==0 ){
    45     45         if( doIndent==0 ){
    46         -        printf("\n");
           46  +        fossil_print("\n");
    47     47           lineCnt = 1;
    48     48         }
    49     49         return lineCnt;
    50     50       }
    51     51       for(sk=si=i=k=0; zText[i] && k<tlen; i++){
    52     52         char c = zText[i];
    53         -      if( isspace(c) ){
           53  +      if( fossil_isspace(c) ){
    54     54           si = i;
    55     55           sk = k;
    56     56           if( k==0 || zBuf[k-1]!=' ' ){
    57     57             zBuf[k++] = ' ';
    58     58           }
    59     59         }else{
    60     60           zBuf[k] = c;
    61         -        if( c=='-' && k>0 && isalpha(zBuf[k-1]) ){
           61  +        if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){
    62     62             si = i+1;
    63     63             sk = k+1;
    64     64           }
    65     65           k++;
    66     66         }
    67     67       }
    68     68       if( doIndent ){
    69         -      printf("%*s", indent, "");
           69  +      fossil_print("%*s", indent, "");
    70     70       }
    71     71       doIndent = 1;
    72     72       if( sk>0 && zText[i] ){
    73     73         zText += si;
    74     74         zBuf[sk++] =  '\n';
    75     75         zBuf[sk] = 0;
    76         -      printf("%s", zBuf);
           76  +      fossil_print("%s", zBuf);
    77     77       }else{
    78     78         zText += i;
    79     79         zBuf[k++] =  '\n';
    80     80         zBuf[k] = 0;
    81         -      printf("%s", zBuf);
           81  +      fossil_print("%s", zBuf);
    82     82       }
    83     83       lineCnt++;
    84     84     }
    85     85   }
    86     86   
    87     87   /*
    88     88   ** Test the comment printing
................................................................................
    91     91   */
    92     92   void test_comment_format(void){
    93     93     int indent;
    94     94     if( g.argc!=4 ){
    95     95       usage("PREFIX TEXT");
    96     96     }
    97     97     indent = strlen(g.argv[2]) + 1;
    98         -  printf("%s ", g.argv[2]);
    99         -  printf("(%d lines output)\n", comment_print(g.argv[3], indent, 79));
           98  +  fossil_print("%s ", g.argv[2]);
           99  +  fossil_print("(%d lines output)\n", comment_print(g.argv[3], indent, 79));
   100    100   }

Changes to src/config.h.

    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** A common header file used by all modules.
    19     19   */
    20     20   
           21  +/* The following macros are necessary for large-file support under
           22  +** some linux distributions, and possibly other unixes as well.
           23  +*/
           24  +#define _LARGE_FILE       1
           25  +#ifndef _FILE_OFFSET_BITS
           26  +#  define _FILE_OFFSET_BITS 64
           27  +#endif
           28  +#define _LARGEFILE_SOURCE 1
           29  +
           30  +#ifdef HAVE_AUTOCONFIG_H
           31  +#include "autoconfig.h"
           32  +#endif
           33  +
           34  +#ifndef _RC_COMPILE_
           35  +
    21     36   /*
    22     37   ** System header files used by all modules
    23     38   */
    24     39   #include <unistd.h>
    25     40   #include <stdio.h>
    26     41   #include <stdlib.h>
    27         -#include <ctype.h>
           42  +/* #include <ctype.h> // do not use - causes problems */
    28     43   #include <string.h>
    29     44   #include <stdarg.h>
    30     45   #include <assert.h>
    31         -#ifdef __MINGW32__
    32         -# include <windows.h>
           46  +
           47  +#endif
           48  +
           49  +#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
           50  +#  if defined(__DMC__)  || defined(_MSC_VER) || defined(__POCC__)
           51  +     typedef int socklen_t;
           52  +#  endif
           53  +#  ifndef _WIN32
           54  +#    define _WIN32
           55  +#  endif
    33     56   #else
           57  +# include <sys/types.h>
           58  +# include <signal.h>
    34     59   # include <pwd.h>
    35     60   #endif
    36     61   
           62  +/*
           63  +** Define the compiler variant, used to compile the project
           64  +*/
           65  +#if !defined(COMPILER_NAME)
           66  +#  if defined(__DMC__)
           67  +#    define COMPILER_NAME "dmc"
           68  +#  elif defined(__POCC__)
           69  +#    if defined(_M_X64)
           70  +#      define COMPILER_NAME "pellesc64"
           71  +#    else
           72  +#      define COMPILER_NAME "pellesc32"
           73  +#    endif
           74  +#  elif defined(_MSC_VER)
           75  +#    define COMPILER_NAME "msc"
           76  +#  elif defined(__MINGW32__)
           77  +#    define COMPILER_NAME "mingw32"
           78  +#  elif defined(_WIN32)
           79  +#    define COMPILER_NAME "win32"
           80  +#  elif defined(__GNUC__)
           81  +#    define COMPILER_NAME "gcc-" __VERSION__
           82  +#  else
           83  +#    define COMPILER_NAME "unknown"
           84  +#  endif
           85  +#endif
           86  +
           87  +#ifndef _RC_COMPILE_
           88  +
    37     89   #include "sqlite3.h"
    38     90   
    39     91   /*
    40         -** Typedef for a 64-bit integer
           92  +** On Solaris, getpass() will only return up to 8 characters. getpassphrase() returns up to 257.
    41     93   */
    42         -typedef sqlite_int64 i64;
    43         -typedef sqlite_uint64 u64;
           94  +#if HAVE_GETPASSPHRASE
           95  +  #define getpass getpassphrase
           96  +#endif
    44     97   
    45     98   /*
    46         -** Unsigned character type
           99  +** Typedef for a 64-bit integer
    47    100   */
    48         -typedef unsigned char u8;
          101  +typedef sqlite3_int64 i64;
          102  +typedef sqlite3_uint64 u64;
    49    103   
    50    104   /*
    51         -** Standard colors.  These colors can also be changed using a stylesheet.
          105  +** 8-bit types
    52    106   */
    53         -
    54         -/* A blue border and background.  Used for the title bar and for dates
    55         -** in a timeline.
    56         -*/
    57         -#define BORDER1       "#a0b5f4"      /* Stylesheet class: border1 */
    58         -#define BG1           "#d0d9f4"      /* Stylesheet class: bkgnd1 */
    59         -
    60         -/* A red border and background.  Use for releases in the timeline.
    61         -*/
    62         -#define BORDER2       "#ec9898"      /* Stylesheet class: border2 */
    63         -#define BG2           "#f7c0c0"      /* Stylesheet class: bkgnd2 */
    64         -
    65         -/* A gray background.  Used for column headers in the Wiki Table of Contents
    66         -** and to highlight ticket properties.
    67         -*/
    68         -#define BG3           "#d0d0d0"      /* Stylesheet class: bkgnd3 */
    69         -
    70         -/* A light-gray background.  Used for title bar, menus, and rlog alternation
    71         -*/
    72         -#define BG4           "#f0f0f0"      /* Stylesheet class: bkgnd4 */
    73         -
    74         -/* A deeper gray background.  Used for branches
    75         -*/
    76         -#define BG5           "#dddddd"      /* Stylesheet class: bkgnd5 */
    77         -
    78         -/* Default HTML page header */
    79         -#define HEADER "<html>\n" \
    80         -               "<head>\n" \
    81         -               "<link rel=\"alternate\" type=\"application/rss+xml\"\n" \
    82         -               "   title=\"%N Timeline Feed\" href=\"%B/timeline.rss\">\n" \
    83         -               "<title>%N: %T</title>\n</head>\n" \
    84         -               "<body bgcolor=\"white\">"
    85         -
    86         -/* Default HTML page footer */
    87         -#define FOOTER "<div id=\"footer\"><small><small>\n" \
    88         -               "<a href=\"about\">Fossil version %V</a>\n" \
    89         -               "</small></small></div>\n" \
    90         -               "</body></html>\n"
          107  +typedef unsigned char u8;
          108  +typedef signed char i8;
    91    109   
    92    110   /* In the timeline, check-in messages are truncated at the first space
    93    111   ** that is more than MX_CKIN_MSG from the beginning, or at the first
    94    112   ** paragraph break that is more than MN_CKIN_MSG from the beginning.
    95    113   */
    96    114   #define MN_CKIN_MSG   100
    97    115   #define MX_CKIN_MSG   300
    98    116   
    99         -/* Unset the following to disable internationalization code. */
   100         -#ifndef FOSSIL_I18N
   101         -# define FOSSIL_I18N 1
          117  +/*
          118  +** The following macros are used to cast pointers to integers and
          119  +** integers to pointers.  The way you do this varies from one compiler
          120  +** to the next, so we have developed the following set of #if statements
          121  +** to generate appropriate macros for a wide range of compilers.
          122  +**
          123  +** The correct "ANSI" way to do this is to use the intptr_t type. 
          124  +** Unfortunately, that typedef is not available on all compilers, or
          125  +** if it is available, it requires an #include of specific headers
          126  +** that vary from one machine to the next.
          127  +*/
          128  +#if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
          129  +# define FOSSIL_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
          130  +# define FOSSIL_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
          131  +#elif !defined(__GNUC__)       /* Works for compilers other than LLVM */
          132  +# define FOSSIL_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
          133  +# define FOSSIL_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
          134  +#else                          /* Generates a warning - but it always works */
          135  +# define FOSSIL_INT_TO_PTR(X)  ((void*)(X))
          136  +# define FOSSIL_PTR_TO_INT(X)  ((int)(X))
          137  +#endif
          138  +
          139  +/*
          140  +** A marker for functions that never return.
          141  +*/
          142  +#if defined(__GNUC__) || defined(__clang__)
          143  +# define NORETURN __attribute__((__noreturn__))
          144  +#else
          145  +# define NORETURN
   102    146   #endif
   103    147   
   104         -#if FOSSIL_I18N
   105         -# include <locale.h>
   106         -# include <langinfo.h>
   107         -#endif
   108         -#ifndef CODESET
   109         -# undef FOSSIL_I18N
   110         -# define FOSSIL_I18N 0
   111         -#endif
          148  +#endif /* _RC_COMPILE_ */

Changes to src/configure.c.

     1      1   /*
     2      2   ** Copyright (c) 2008 D. Richard Hipp
     3      3   **
     4      4   ** This program is free software; you can redistribute it and/or
     5      5   ** modify it under the terms of the Simplified BSD License (also
     6      6   ** known as the "2-Clause License" or "FreeBSD License".)
     7         -
            7  +**
     8      8   ** This program is distributed in the hope that it will be useful,
     9      9   ** but without any warranty; without even the implied warranty of
    10     10   ** merchantability or fitness for a particular purpose.
    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code used to manage repository configurations.
    19         -** By "responsitory configure" we mean the local state of a repository
           19  +**
           20  +** By "repository configure" we mean the local state of a repository
    20     21   ** distinct from the versioned files.
    21     22   */
    22     23   #include "config.h"
    23     24   #include "configure.h"
    24     25   #include <assert.h>
    25     26   
    26     27   #if INTERFACE
    27     28   /*
    28     29   ** Configuration transfers occur in groups.  These are the allowed
    29     30   ** groupings:
    30     31   */
    31         -#define CONFIGSET_SKIN   0x000001     /* WWW interface appearance */
    32         -#define CONFIGSET_TKT    0x000002     /* Ticket configuration */
    33         -#define CONFIGSET_PROJ   0x000004     /* Project name */
    34         -#define CONFIGSET_SHUN   0x000008     /* Shun settings */
    35         -#define CONFIGSET_USER   0x000010     /* The USER table */
    36         -#define CONFIGSET_ADDR   0x000020     /* The CONCEALED table */
           32  +#define CONFIGSET_CSS       0x000001     /* Style sheet only */
           33  +#define CONFIGSET_SKIN      0x000002     /* WWW interface appearance */
           34  +#define CONFIGSET_TKT       0x000004     /* Ticket configuration */
           35  +#define CONFIGSET_PROJ      0x000008     /* Project name */
           36  +#define CONFIGSET_SHUN      0x000010     /* Shun settings */
           37  +#define CONFIGSET_USER      0x000020     /* The USER table */
           38  +#define CONFIGSET_ADDR      0x000040     /* The CONCEALED table */
           39  +#define CONFIGSET_XFER      0x000080     /* Transfer configuration */
    37     40   
    38         -#define CONFIGSET_ALL    0xffffff     /* Everything */
           41  +#define CONFIGSET_ALL       0x0000ff     /* Everything */
           42  +
           43  +#define CONFIGSET_OVERWRITE 0x100000     /* Causes overwrite instead of merge */
           44  +#define CONFIGSET_OLDFORMAT 0x200000     /* Use the legacy format */
    39     45   
    40     46   #endif /* INTERFACE */
    41     47   
    42     48   /*
    43     49   ** Names of the configuration sets
    44     50   */
    45     51   static struct {
    46     52     const char *zName;   /* Name of the configuration set */
    47     53     int groupMask;       /* Mask for that configuration set */
    48     54     const char *zHelp;   /* What it does */
    49     55   } aGroupName[] = {
    50         -  { "email",        CONFIGSET_ADDR,  "Concealed email addresses in tickets" },
    51         -  { "project",      CONFIGSET_PROJ,  "Project name and description"         },
    52         -  { "skin",         CONFIGSET_SKIN,  "Web interface apparance settings"     },
    53         -  { "shun",         CONFIGSET_SHUN,  "List of shunned artifacts"            },
    54         -  { "ticket",       CONFIGSET_TKT,   "Ticket setup",                        },
    55         -  { "user",         CONFIGSET_USER,  "Users and privilege settings"         },
    56         -  { "all",          CONFIGSET_ALL,   "All of the above"                     },
           56  +  { "/email",        CONFIGSET_ADDR,  "Concealed email addresses in tickets" },
           57  +  { "/project",      CONFIGSET_PROJ,  "Project name and description"         },
           58  +  { "/skin",         CONFIGSET_SKIN | CONFIGSET_CSS,
           59  +                                      "Web interface appearance settings"    },
           60  +  { "/css",          CONFIGSET_CSS,   "Style sheet"                          },
           61  +  { "/shun",         CONFIGSET_SHUN,  "List of shunned artifacts"            },
           62  +  { "/ticket",       CONFIGSET_TKT,   "Ticket setup",                        },
           63  +  { "/user",         CONFIGSET_USER,  "Users and privilege settings"         },
           64  +  { "/xfer",         CONFIGSET_XFER,  "Transfer setup",                      },
           65  +  { "/all",          CONFIGSET_ALL,   "All of the above"                     },
    57     66   };
    58     67   
    59     68   
    60     69   /*
    61     70   ** The following is a list of settings that we are willing to
    62         -** transfer.  
           71  +** transfer.
    63     72   **
    64     73   ** Setting names that begin with an alphabetic characters refer to
    65     74   ** single entries in the CONFIG table.  Setting names that begin with
    66     75   ** "@" are for special processing.
    67     76   */
    68     77   static struct {
    69     78     const char *zName;   /* Name of the configuration parameter */
    70     79     int groupMask;       /* Which config groups is it part of */
    71     80   } aConfig[] = {
    72         -  { "css",                    CONFIGSET_SKIN },
           81  +  { "css",                    CONFIGSET_CSS  },
    73     82     { "header",                 CONFIGSET_SKIN },
    74     83     { "footer",                 CONFIGSET_SKIN },
    75     84     { "logo-mimetype",          CONFIGSET_SKIN },
    76     85     { "logo-image",             CONFIGSET_SKIN },
    77         -  { "project-name",           CONFIGSET_PROJ },
    78         -  { "project-description",    CONFIGSET_PROJ },
           86  +  { "background-mimetype",    CONFIGSET_SKIN },
           87  +  { "background-image",       CONFIGSET_SKIN },
    79     88     { "index-page",             CONFIGSET_SKIN },
    80     89     { "timeline-block-markup",  CONFIGSET_SKIN },
    81     90     { "timeline-max-comment",   CONFIGSET_SKIN },
           91  +  { "timeline-plaintext",     CONFIGSET_SKIN },
           92  +  { "adunit",                 CONFIGSET_SKIN },
           93  +  { "adunit-omit-if-admin",   CONFIGSET_SKIN },
           94  +  { "adunit-omit-if-user",    CONFIGSET_SKIN },
           95  +  { "th1-setup",              CONFIGSET_ALL },
           96  +
           97  +#ifdef FOSSIL_ENABLE_TCL
           98  +  { "tcl",                    CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
           99  +  { "tcl-setup",              CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
          100  +#endif
          101  +
          102  +  { "project-name",           CONFIGSET_PROJ },
          103  +  { "project-description",    CONFIGSET_PROJ },
          104  +  { "manifest",               CONFIGSET_PROJ },
          105  +  { "binary-glob",            CONFIGSET_PROJ },
          106  +  { "ignore-glob",            CONFIGSET_PROJ },
          107  +  { "crnl-glob",              CONFIGSET_PROJ },
          108  +  { "encoding-glob",          CONFIGSET_PROJ },
          109  +  { "empty-dirs",             CONFIGSET_PROJ },
          110  +  { "allow-symlinks",         CONFIGSET_PROJ },
          111  +
    82    112     { "ticket-table",           CONFIGSET_TKT  },
    83    113     { "ticket-common",          CONFIGSET_TKT  },
          114  +  { "ticket-change",          CONFIGSET_TKT  },
    84    115     { "ticket-newpage",         CONFIGSET_TKT  },
    85    116     { "ticket-viewpage",        CONFIGSET_TKT  },
    86    117     { "ticket-editpage",        CONFIGSET_TKT  },
    87    118     { "ticket-reportlist",      CONFIGSET_TKT  },
    88    119     { "ticket-report-template", CONFIGSET_TKT  },
    89    120     { "ticket-key-template",    CONFIGSET_TKT  },
    90    121     { "ticket-title-expr",      CONFIGSET_TKT  },
    91    122     { "ticket-closed-expr",     CONFIGSET_TKT  },
    92    123     { "@reportfmt",             CONFIGSET_TKT  },
          124  +
    93    125     { "@user",                  CONFIGSET_USER },
          126  +
    94    127     { "@concealed",             CONFIGSET_ADDR },
          128  +
    95    129     { "@shun",                  CONFIGSET_SHUN },
          130  +
          131  +  { "xfer-common-script",     CONFIGSET_XFER },
          132  +  { "xfer-push-script",       CONFIGSET_XFER },
          133  +
    96    134   };
    97    135   static int iConfig = 0;
    98    136   
    99    137   /*
   100    138   ** Return name of first configuration property matching the given mask.
   101    139   */
   102    140   const char *configure_first_name(int iMask){
   103    141     iConfig = 0;
   104    142     return configure_next_name(iMask);
   105    143   }
   106    144   const char *configure_next_name(int iMask){
   107         -  while( iConfig<count(aConfig) ){
   108         -    if( aConfig[iConfig].groupMask & iMask ){
   109         -      return aConfig[iConfig++].zName;
   110         -    }else{
   111         -      iConfig++;
          145  +  if( iMask & CONFIGSET_OLDFORMAT ){
          146  +    while( iConfig<count(aConfig) ){
          147  +      if( aConfig[iConfig].groupMask & iMask ){
          148  +        return aConfig[iConfig++].zName;
          149  +      }else{
          150  +        iConfig++;
          151  +      }
          152  +    }
          153  +  }else{
          154  +    if( iConfig==0 && (iMask & CONFIGSET_ALL)==CONFIGSET_ALL ){
          155  +      iConfig = count(aGroupName);
          156  +      return "/all";
          157  +    }
          158  +    while( iConfig<count(aGroupName)-1 ){
          159  +      if( aGroupName[iConfig].groupMask & iMask ){
          160  +        return aGroupName[iConfig++].zName;
          161  +      }else{
          162  +        iConfig++;
          163  +      }
   112    164       }
   113    165     }
   114    166     return 0;
   115    167   }
          168  +
          169  +/*
          170  +** Return a pointer to a string that contains the RHS of an IN operator
          171  +** that will select CONFIG table names that are part of the configuration
          172  +** that matches iMatch.
          173  +*/
          174  +const char *configure_inop_rhs(int iMask){
          175  +  Blob x;
          176  +  int i;
          177  +  const char *zSep = "";
          178  +
          179  +  blob_zero(&x);
          180  +  blob_append(&x, "(", 1);
          181  +  for(i=0; i<count(aConfig); i++){
          182  +    if( (aConfig[i].groupMask & iMask)==0 ) continue;
          183  +    if( aConfig[i].zName[0]=='@' ) continue;
          184  +    blob_appendf(&x, "%s'%s'", zSep, aConfig[i].zName);
          185  +    zSep = ",";
          186  +  }
          187  +  blob_append(&x, ")", 1);
          188  +  return blob_str(&x);
          189  +}
   116    190   
   117    191   /*
   118    192   ** Return the mask for the named configuration parameter if it can be
   119    193   ** safely exported.  Return 0 if the parameter is not safe to export.
          194  +**
          195  +** "Safe" in the previous paragraph means the permission is created to
          196  +** export the property.  In other words, the requesting side has presented
          197  +** login credentials and has sufficient capabilities to access the requested
          198  +** information.
   120    199   */
   121    200   int configure_is_exportable(const char *zName){
   122    201     int i;
          202  +  int n = strlen(zName);
          203  +  if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){
          204  +    zName++;
          205  +    n -= 2;
          206  +  }
   123    207     for(i=0; i<count(aConfig); i++){
   124         -    if( strcmp(zName, aConfig[i].zName)==0 ){
          208  +    if( memcmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
   125    209         int m = aConfig[i].groupMask;
   126         -      if( !g.okAdmin ){
          210  +      if( !g.perm.Admin ){
   127    211           m &= ~CONFIGSET_USER;
   128    212         }
   129         -      if( !g.okRdAddr ){
          213  +      if( !g.perm.RdAddr ){
   130    214           m &= ~CONFIGSET_ADDR;
   131    215         }
   132    216         return m;
   133    217       }
   134    218     }
   135    219     return 0;
   136    220   }
................................................................................
   139    223   ** zName is one of the special configuration names that refers to an entire
   140    224   ** table rather than a single entry in CONFIG.  Special names are "@reportfmt"
   141    225   ** and "@shun" and "@user".  This routine writes SQL text into pOut that when
   142    226   ** evaluated will populate the corresponding table with data.
   143    227   */
   144    228   void configure_render_special_name(const char *zName, Blob *pOut){
   145    229     Stmt q;
   146         -  if( strcmp(zName, "@shun")==0 ){
          230  +  if( fossil_strcmp(zName, "@shun")==0 ){
   147    231       db_prepare(&q, "SELECT uuid FROM shun");
   148    232       while( db_step(&q)==SQLITE_ROW ){
   149         -      blob_appendf(pOut, "INSERT OR IGNORE INTO shun VALUES('%s');\n", 
          233  +      blob_appendf(pOut, "INSERT OR IGNORE INTO shun VALUES('%s');\n",
   150    234           db_column_text(&q, 0)
   151    235         );
   152    236       }
   153    237       db_finalize(&q);
   154         -  }else if( strcmp(zName, "@reportfmt")==0 ){
          238  +  }else if( fossil_strcmp(zName, "@reportfmt")==0 ){
   155    239       db_prepare(&q, "SELECT title, cols, sqlcode FROM reportfmt");
   156    240       while( db_step(&q)==SQLITE_ROW ){
   157    241         blob_appendf(pOut, "INSERT INTO _xfer_reportfmt(title,cols,sqlcode)"
   158         -                         " VALUES(%Q,%Q,%Q);\n", 
          242  +                         " VALUES(%Q,%Q,%Q);\n",
   159    243           db_column_text(&q, 0),
   160    244           db_column_text(&q, 1),
   161    245           db_column_text(&q, 2)
   162    246         );
   163    247       }
   164    248       db_finalize(&q);
   165         -  }else if( strcmp(zName, "@user")==0 ){
   166         -    db_prepare(&q, 
          249  +  }else if( fossil_strcmp(zName, "@user")==0 ){
          250  +    db_prepare(&q,
   167    251           "SELECT login, CASE WHEN length(pw)==40 THEN pw END,"
   168    252           "       cap, info, quote(photo) FROM user");
   169    253       while( db_step(&q)==SQLITE_ROW ){
   170    254         blob_appendf(pOut, "INSERT INTO _xfer_user(login,pw,cap,info,photo)"
   171    255                            " VALUES(%Q,%Q,%Q,%Q,%s);\n",
   172    256           db_column_text(&q, 0),
   173    257           db_column_text(&q, 1),
   174    258           db_column_text(&q, 2),
   175    259           db_column_text(&q, 3),
   176    260           db_column_text(&q, 4)
   177    261         );
   178    262       }
   179    263       db_finalize(&q);
   180         -  }else if( strcmp(zName, "@concealed")==0 ){
          264  +  }else if( fossil_strcmp(zName, "@concealed")==0 ){
   181    265       db_prepare(&q, "SELECT hash, content FROM concealed");
   182    266       while( db_step(&q)==SQLITE_ROW ){
   183    267         blob_appendf(pOut, "INSERT OR IGNORE INTO concealed(hash,content)"
   184    268                            " VALUES(%Q,%Q);\n",
   185    269           db_column_text(&q, 0),
   186    270           db_column_text(&q, 1)
   187    271         );
................................................................................
   189    273       db_finalize(&q);
   190    274     }
   191    275   }
   192    276   
   193    277   /*
   194    278   ** Two SQL functions:
   195    279   **
   196         -**        flag_test(int)
   197         -**        flag_clear(int)
          280  +**        config_is_reset(int)
          281  +**        config_reset(int)
   198    282   **
   199         -** The flag_test() function takes the integer valued argument and
   200         -** ANDs it against the static variable "flag_value" below.  The
   201         -** function returns TRUE or false depending on the result.  The
   202         -** flag_clear() function masks off the bits from "flag_value" that
          283  +** The config_is_reset() function takes the integer valued argument and
          284  +** ANDs it against the static variable "configHasBeenReset" below.  The
          285  +** function returns TRUE or FALSE depending on the result depending on
          286  +** whether or not the corresponding configuration table has been reset.  The
          287  +** config_reset() function adds the bits to "configHasBeenReset" that
   203    288   ** are given in the argument.
   204    289   **
   205    290   ** These functions are used below in the WHEN clause of a trigger to
   206    291   ** get the trigger to fire exactly once.
   207    292   */
   208         -static int flag_value = 0xffff;
   209         -static void flag_test_function(
          293  +static int configHasBeenReset = 0;
          294  +static void config_is_reset_function(
   210    295     sqlite3_context *context,
   211    296     int argc,
   212    297     sqlite3_value **argv
   213    298   ){
   214    299     int m = sqlite3_value_int(argv[0]);
   215         -  sqlite3_result_int(context, (flag_value&m)!=0 );
          300  +  sqlite3_result_int(context, (configHasBeenReset&m)!=0 );
   216    301   }
   217         -static void flag_clear_function(
          302  +static void config_reset_function(
   218    303     sqlite3_context *context,
   219    304     int argc,
   220    305     sqlite3_value **argv
   221    306   ){
   222    307     int m = sqlite3_value_int(argv[0]);
   223         -  flag_value &= ~m;
          308  +  configHasBeenReset |= m;
   224    309   }
   225    310   
   226    311   /*
   227    312   ** Create the temporary _xfer_reportfmt and _xfer_user tables that are
   228         -** necessary in order to evalute the SQL text generated by the
          313  +** necessary in order to evaluate the SQL text generated by the
   229    314   ** configure_render_special_name() routine.
   230    315   **
   231    316   ** If replaceFlag is true, then the setup is such that the content in
   232    317   ** the SQL text will completely replace the current repository configuration.
   233    318   ** If replaceFlag is false, then the SQL text will be merged with the
   234    319   ** existing configuration.  When merging, existing values take priority
   235    320   ** over SQL text values.
................................................................................
   250    335       @   cap TEXT,                       -- Capabilities of this user
   251    336       @   cookie TEXT,                    -- WWW login cookie
   252    337       @   ipaddr TEXT,                    -- IP address for which cookie is valid
   253    338       @   cexpire DATETIME,               -- Time when cookie expires
   254    339       @   info TEXT,                      -- contact information
   255    340       @   photo BLOB                      -- JPEG image of this user
   256    341       @ );
   257         -    @ INSERT INTO _xfer_reportfmt SELECT * FROM reportfmt;
   258         -    @ INSERT INTO _xfer_user SELECT * FROM user;
          342  +    @ INSERT INTO _xfer_reportfmt
          343  +    @    SELECT rn,owner,title,cols,sqlcode FROM reportfmt;
          344  +    @ INSERT INTO _xfer_user
          345  +    @    SELECT uid,login,pw,cap,cookie,ipaddr,cexpire,info,photo FROM user;
   259    346     ;
   260    347     db_multi_exec(zSQL1);
   261         -  
          348  +
   262    349     /* When the replace flag is set, add triggers that run the first time
   263    350     ** that new data is seen.  The triggers run only once and delete all the
   264    351     ** existing data.
   265    352     */
   266    353     if( replaceFlag ){
   267    354       static const char zSQL2[] =
   268    355         @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
   269         -      @ WHEN flag_test(1) BEGIN
          356  +      @ WHEN NOT config_is_reset(2) BEGIN
   270    357         @   DELETE FROM _xfer_reportfmt;
   271         -      @   SELECT flag_clear(1);
          358  +      @   SELECT config_reset(2);
   272    359         @ END;
   273    360         @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
   274         -      @ WHEN flag_test(2) BEGIN
          361  +      @ WHEN NOT config_is_reset(16) BEGIN
   275    362         @   DELETE FROM _xfer_user;
   276         -      @   SELECT flag_clear(2);
          363  +      @   SELECT config_reset(16);
   277    364         @ END;
   278    365         @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
   279         -      @ WHEN flag_test(4) BEGIN
          366  +      @ WHEN NOT config_is_reset(8) BEGIN
   280    367         @   DELETE FROM shun;
   281         -      @   SELECT flag_clear(4);
          368  +      @   SELECT config_reset(8);
   282    369         @ END;
   283    370       ;
   284         -    sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0,
   285         -         flag_test_function, 0, 0);
   286         -    sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0,
   287         -         flag_clear_function, 0, 0);
   288         -    flag_value = 0xffff;
          371  +    sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0,
          372  +         config_is_reset_function, 0, 0);
          373  +    sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0,
          374  +         config_reset_function, 0, 0);
          375  +    configHasBeenReset = 0;
   289    376       db_multi_exec(zSQL2);
   290    377     }
   291    378   }
   292    379   
   293    380   /*
   294    381   ** After receiving configuration data, call this routine to transfer
   295    382   ** the results into the main database.
................................................................................
   301    388       @ DELETE FROM reportfmt;
   302    389       @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt;
   303    390       @ DROP TABLE _xfer_user;
   304    391       @ DROP TABLE _xfer_reportfmt;
   305    392     ;
   306    393     db_multi_exec(zSQL);
   307    394   }
          395  +
          396  +/*
          397  +** Return true if z[] is not a "safe" SQL token.  A safe token is one of:
          398  +**
          399  +**   *   A string literal
          400  +**   *   A blob literal
          401  +**   *   An integer literal  (no floating point)
          402  +**   *   NULL
          403  +*/
          404  +static int safeSql(const char *z){
          405  +  int i;
          406  +  if( z==0 || z[0]==0 ) return 0;
          407  +  if( (z[0]=='x' || z[0]=='X') && z[1]=='\'' ) z++;
          408  +  if( z[0]=='\'' ){
          409  +    for(i=1; z[i]; i++){
          410  +      if( z[i]=='\'' ){
          411  +        i++;
          412  +        if( z[i]=='\'' ){ continue; }
          413  +        return z[i]==0;
          414  +      }
          415  +    }
          416  +    return 0;
          417  +  }else{
          418  +    char c;
          419  +    for(i=0; (c = z[i])!=0; i++){
          420  +      if( !fossil_isalnum(c) ) return 0;
          421  +    }
          422  +  }
          423  +  return 1;
          424  +}
          425  +
          426  +/*
          427  +** Return true if z[] consists of nothing but digits
          428  +*/
          429  +static int safeInt(const char *z){
          430  +  int i;
          431  +  if( z==0 || z[0]==0 ) return 0;
          432  +  for(i=0; fossil_isdigit(z[i]); i++){}
          433  +  return z[i]==0;
          434  +}
          435  +
          436  +/*
          437  +** Process a single "config" card received from the other side of a
          438  +** sync session.
          439  +**
          440  +** Mask consists of one or more CONFIGSET_* values ORed together, to
          441  +** designate what types of configuration we are allowed to receive.
          442  +**
          443  +** NEW FORMAT:
          444  +**
          445  +** zName is one of "/config", "/user", "/shun", "/reportfmt", or "/concealed".
          446  +** zName indicates the table that holds the configuration information being
          447  +** transferred.  pContent is a string that consist of alternating Fossil
          448  +** and SQL tokens.  The First token is a timestamp in seconds since 1970.
          449  +** The second token is a primary key for the table identified by zName.  If
          450  +** The entry with the corresponding primary key exists and has a more recent
          451  +** mtime, then nothing happens.  If the entry does not exist or if it has
          452  +** an older mtime, then the content described by subsequent token pairs is
          453  +** inserted.  The first element of each token pair is a column name and
          454  +** the second is its value.
          455  +**
          456  +** In overview, we have:
          457  +**
          458  +**    NAME        CONTENT
          459  +**    -------     -----------------------------------------------------------
          460  +**    /config     $MTIME $NAME value $VALUE
          461  +**    /user       $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
          462  +**    /shun       $MTIME $UUID scom $VALUE
          463  +**    /reportfmt  $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
          464  +**    /concealed  $MTIME $HASH content $VALUE
          465  +**
          466  +** OLD FORMAT:
          467  +**
          468  +** The old format is retained for backwards compatibility, but is deprecated.
          469  +** The cutover from old format to new was on 2011-04-25.  After sufficient
          470  +** time has passed, support for the old format will be removed.
          471  +**
          472  +** zName is either the NAME of an element of the CONFIG table, or else
          473  +** one of the special names "@shun", "@reportfmt", "@user", or "@concealed".
          474  +** If zName is a CONFIG table name, then CONTENT replaces (overwrites) the
          475  +** element in the CONFIG table.  For one of the @-labels, CONTENT is raw
          476  +** SQL that is evaluated.  Note that the raw SQL in CONTENT might not
          477  +** insert directly into the target table but might instead use a proxy
          478  +** table like _fer_reportfmt or _xfer_user.  Such tables must be created
          479  +** ahead of time using configure_prepare_to_receive().  Then after multiple
          480  +** calls to this routine, configure_finalize_receive() to transfer the
          481  +** information received into the true target table.
          482  +*/
          483  +void configure_receive(const char *zName, Blob *pContent, int groupMask){
          484  +  if( zName[0]=='/' ){
          485  +    /* The new format */
          486  +    char *azToken[12];
          487  +    int nToken = 0;
          488  +    int ii, jj;
          489  +    int thisMask;
          490  +    Blob name, value, sql;
          491  +    static const struct receiveType {
          492  +      const char *zName;
          493  +      const char *zPrimKey;
          494  +      int nField;
          495  +      const char *azField[4];
          496  +    } aType[] = {
          497  +      { "/config",    "name",  1, { "value", 0, 0, 0 }              },
          498  +      { "@user",      "login", 4, { "pw", "cap", "info", "photo" }  },
          499  +      { "@shun",      "uuid",  1, { "scom", 0, 0, 0 }               },
          500  +      { "@reportfmt", "title", 3, { "owner", "cols", "sqlcode", 0 } },
          501  +      { "@concealed", "hash",  1, { "content", 0, 0, 0 }            },
          502  +    };
          503  +    for(ii=0; ii<count(aType); ii++){
          504  +      if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
          505  +    }
          506  +    if( ii>=count(aType) ) return;
          507  +    while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){
          508  +      char *z = blob_terminate(&name);
          509  +      if( !safeSql(z) ) return;
          510  +      if( nToken>0 ){
          511  +        for(jj=0; jj<aType[ii].nField; jj++){
          512  +          if( fossil_strcmp(aType[ii].azField[jj], z)==0 ) break;
          513  +        }
          514  +        if( jj>=aType[ii].nField ) continue;
          515  +      }else{
          516  +        if( !safeInt(z) ) return;
          517  +      }
          518  +      azToken[nToken++] = z;
          519  +      azToken[nToken++] = z = blob_terminate(&value);
          520  +      if( !safeSql(z) ) return;
          521  +      if( nToken>=count(azToken) ) break;
          522  +    }
          523  +    if( nToken<2 ) return;
          524  +    if( aType[ii].zName[0]=='/' ){
          525  +      thisMask = configure_is_exportable(azToken[1]);
          526  +    }else{
          527  +      thisMask = configure_is_exportable(aType[ii].zName);
          528  +    }
          529  +    if( (thisMask & groupMask)==0 ) return;
          530  +
          531  +    blob_zero(&sql);
          532  +    if( groupMask & CONFIGSET_OVERWRITE ){
          533  +      if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
          534  +        db_multi_exec("DELETE FROM %s", &aType[ii].zName[1]);
          535  +        configHasBeenReset |= thisMask;
          536  +      }
          537  +      blob_append(&sql, "REPLACE INTO ", -1);
          538  +    }else{
          539  +      blob_append(&sql, "INSERT OR IGNORE INTO ", -1);
          540  +    }
          541  +    blob_appendf(&sql, "%s(%s, mtime", &zName[1], aType[ii].zPrimKey);
          542  +    for(jj=2; jj<nToken; jj+=2){
          543  +       blob_appendf(&sql, ",%s", azToken[jj]);
          544  +    }
          545  +    blob_appendf(&sql,") VALUES(%s,%s", azToken[1], azToken[0]);
          546  +    for(jj=2; jj<nToken; jj+=2){
          547  +       blob_appendf(&sql, ",%s", azToken[jj+1]);
          548  +    }
          549  +    db_multi_exec("%s)", blob_str(&sql));
          550  +    if( db_changes()==0 ){
          551  +      blob_reset(&sql);
          552  +      blob_appendf(&sql, "UPDATE %s SET mtime=%s", &zName[1], azToken[0]);
          553  +      for(jj=2; jj<nToken; jj+=2){
          554  +        blob_appendf(&sql, ", %s=%s", azToken[jj], azToken[jj+1]);
          555  +      }
          556  +      blob_appendf(&sql, " WHERE %s=%s AND mtime<%s",
          557  +                   aType[ii].zPrimKey, azToken[1], azToken[0]);
          558  +      db_multi_exec("%s", blob_str(&sql));
          559  +    }
          560  +    blob_reset(&sql);
          561  +  }else{
          562  +    /* Otherwise, the old format */
          563  +    if( (configure_is_exportable(zName) & groupMask)==0 ) return;
          564  +    if( fossil_strcmp(zName, "logo-image")==0 ){
          565  +      Stmt ins;
          566  +      db_prepare(&ins,
          567  +        "REPLACE INTO config(name, value, mtime) VALUES(:name, :value, now())"
          568  +      );
          569  +      db_bind_text(&ins, ":name", zName);
          570  +      db_bind_blob(&ins, ":value", pContent);
          571  +      db_step(&ins);
          572  +      db_finalize(&ins);
          573  +    }else if( zName[0]=='@' ){
          574  +      /* Notice that we are evaluating arbitrary SQL received from the
          575  +      ** client.  But this can only happen if the client has authenticated
          576  +      ** as an administrator, so presumably we trust the client at this
          577  +      ** point.
          578  +      */
          579  +      db_multi_exec("%s", blob_str(pContent));
          580  +    }else{
          581  +      db_multi_exec(
          582  +         "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
          583  +         zName, blob_str(pContent)
          584  +      );
          585  +    }
          586  +  }
          587  +}
          588  +
          589  +/*
          590  +** Process a file full of "config" cards.
          591  +*/
          592  +void configure_receive_all(Blob *pIn, int groupMask){
          593  +  Blob line;
          594  +  int nToken;
          595  +  int size;
          596  +  Blob aToken[4];
          597  +
          598  +  configHasBeenReset = 0;
          599  +  while( blob_line(pIn, &line) ){
          600  +    if( blob_buffer(&line)[0]=='#' ) continue;
          601  +    nToken = blob_tokenize(&line, aToken, count(aToken));
          602  +    if( blob_eq(&aToken[0],"config")
          603  +     && nToken==3
          604  +     && blob_is_int(&aToken[2], &size)
          605  +    ){
          606  +      const char *zName = blob_str(&aToken[1]);
          607  +      Blob content;
          608  +      blob_zero(&content);
          609  +      blob_extract(pIn, size, &content);
          610  +      g.perm.Admin = g.perm.RdAddr = 1;
          611  +      configure_receive(zName, &content, groupMask);
          612  +      blob_reset(&content);
          613  +      blob_seek(pIn, 1, BLOB_SEEK_CUR);
          614  +    }
          615  +  }
          616  +}
          617  +
          618  +
          619  +/*
          620  +** Send "config" cards using the new format for all elements of a group
          621  +** that have recently changed.
          622  +**
          623  +** Output goes into pOut.  The groupMask identifies the group(s) to be sent.
          624  +** Send only entries whose timestamp is later than or equal to iStart.
          625  +**
          626  +** Return the number of cards sent.
          627  +*/
          628  +int configure_send_group(
          629  +  Blob *pOut,              /* Write output here */
          630  +  int groupMask,           /* Mask of groups to be send */
          631  +  sqlite3_int64 iStart     /* Only write values changed since this time */
          632  +){
          633  +  Stmt q;
          634  +  Blob rec;
          635  +  int ii;
          636  +  int nCard = 0;
          637  +
          638  +  blob_zero(&rec);
          639  +  if( groupMask & CONFIGSET_SHUN ){
          640  +    db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun"
          641  +                   " WHERE mtime>=%lld", iStart);
          642  +    while( db_step(&q)==SQLITE_ROW ){
          643  +      blob_appendf(&rec,"%s %s scom %s",
          644  +        db_column_text(&q, 0),
          645  +        db_column_text(&q, 1),
          646  +        db_column_text(&q, 2)
          647  +      );
          648  +      blob_appendf(pOut, "config /shun %d\n%s\n",
          649  +                   blob_size(&rec), blob_str(&rec));
          650  +      nCard++;
          651  +      blob_reset(&rec);
          652  +    }
          653  +    db_finalize(&q);
          654  +  }
          655  +  if( groupMask & CONFIGSET_USER ){
          656  +    db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
          657  +                   "       quote(info), quote(photo) FROM user"
          658  +                   " WHERE mtime>=%lld", iStart);
          659  +    while( db_step(&q)==SQLITE_ROW ){
          660  +      blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s",
          661  +        db_column_text(&q, 0),
          662  +        db_column_text(&q, 1),
          663  +        db_column_text(&q, 2),
          664  +        db_column_text(&q, 3),
          665  +        db_column_text(&q, 4),
          666  +        db_column_text(&q, 5)
          667  +      );
          668  +      blob_appendf(pOut, "config /user %d\n%s\n",
          669  +                   blob_size(&rec), blob_str(&rec));
          670  +      nCard++;
          671  +      blob_reset(&rec);
          672  +    }
          673  +    db_finalize(&q);
          674  +  }
          675  +  if( groupMask & CONFIGSET_TKT ){
          676  +    db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
          677  +                   "       quote(sqlcode) FROM reportfmt"
          678  +                   " WHERE mtime>=%lld", iStart);
          679  +    while( db_step(&q)==SQLITE_ROW ){
          680  +      blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s",
          681  +        db_column_text(&q, 0),
          682  +        db_column_text(&q, 1),
          683  +        db_column_text(&q, 2),
          684  +        db_column_text(&q, 3),
          685  +        db_column_text(&q, 4)
          686  +      );
          687  +      blob_appendf(pOut, "config /reportfmt %d\n%s\n",
          688  +                   blob_size(&rec), blob_str(&rec));
          689  +      nCard++;
          690  +      blob_reset(&rec);
          691  +    }
          692  +    db_finalize(&q);
          693  +  }
          694  +  if( groupMask & CONFIGSET_ADDR ){
          695  +    db_prepare(&q, "SELECT mtime, quote(hash), quote(content) FROM concealed"
          696  +                   " WHERE mtime>=%lld", iStart);
          697  +    while( db_step(&q)==SQLITE_ROW ){
          698  +      blob_appendf(&rec,"%s %s content %s",
          699  +        db_column_text(&q, 0),
          700  +        db_column_text(&q, 1),
          701  +        db_column_text(&q, 2)
          702  +      );
          703  +      blob_appendf(pOut, "config /concealed %d\n%s\n",
          704  +                   blob_size(&rec), blob_str(&rec));
          705  +      nCard++;
          706  +      blob_reset(&rec);
          707  +    }
          708  +    db_finalize(&q);
          709  +  }
          710  +  db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
          711  +                 " WHERE name=:name AND mtime>=%lld", iStart);
          712  +  for(ii=0; ii<count(aConfig); ii++){
          713  +    if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
          714  +      db_bind_text(&q, ":name", aConfig[ii].zName);
          715  +      while( db_step(&q)==SQLITE_ROW ){
          716  +        blob_appendf(&rec,"%s %s value %s",
          717  +          db_column_text(&q, 0),
          718  +          db_column_text(&q, 1),
          719  +          db_column_text(&q, 2)
          720  +        );
          721  +        blob_appendf(pOut, "config /config %d\n%s\n",
          722  +                     blob_size(&rec), blob_str(&rec));
          723  +        nCard++;
          724  +        blob_reset(&rec);
          725  +      }
          726  +      db_reset(&q);
          727  +    }
          728  +  }
          729  +  db_finalize(&q);
          730  +  return nCard;
          731  +}
   308    732   
   309    733   /*
   310    734   ** Identify a configuration group by name.  Return its mask.
   311    735   ** Throw an error if no match.
   312    736   */
   313         -static int find_area(const char *z){
          737  +int configure_name_to_mask(const char *z, int notFoundIsFatal){
   314    738     int i;
   315    739     int n = strlen(z);
   316    740     for(i=0; i<count(aGroupName); i++){
   317         -    if( strncmp(z, aGroupName[i].zName, n)==0 ){
          741  +    if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){
   318    742         return aGroupName[i].groupMask;
   319    743       }
   320    744     }
   321         -  printf("Available configuration areas:\n");
   322         -  for(i=0; i<count(aGroupName); i++){
   323         -    printf("  %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp);
          745  +  if( notFoundIsFatal ){
          746  +    fossil_print("Available configuration areas:\n");
          747  +    for(i=0; i<count(aGroupName); i++){
          748  +      fossil_print("  %-10s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp);
          749  +    }
          750  +    fossil_fatal("no such configuration area: \"%s\"", z);
   324    751     }
   325         -  fossil_fatal("no such configuration area: \"%s\"", z);
   326    752     return 0;
   327    753   }
   328    754   
   329    755   /*
   330    756   ** Write SQL text into file zFilename that will restore the configuration
   331    757   ** area identified by mask to its current state from any other state.
   332    758   */
   333    759   static void export_config(
   334         -  int mask,                 /* Mask indicating which configuration to export */
          760  +  int groupMask,            /* Mask indicating which configuration to export */
   335    761     const char *zMask,        /* Name of the configuration */
          762  +  sqlite3_int64 iStart,     /* Start date */
   336    763     const char *zFilename     /* Write into this file */
   337    764   ){
   338         -  int i;
   339    765     Blob out;
   340    766     blob_zero(&out);
   341         -  blob_appendf(&out, 
   342         -    "-- The \"%s\" configuration exported from\n"
   343         -    "-- repository \"%s\"\n"
   344         -    "-- on %s\n",
          767  +  blob_appendf(&out,
          768  +    "# The \"%s\" configuration exported from\n"
          769  +    "# repository \"%s\"\n"
          770  +    "# on %s\n",
   345    771       zMask, g.zRepositoryName,
   346    772       db_text(0, "SELECT datetime('now')")
   347    773     );
   348         -  for(i=0; i<count(aConfig); i++){
   349         -    if( (aConfig[i].groupMask & mask)!=0 ){
   350         -      const char *zName = aConfig[i].zName;
   351         -      if( zName[0]!='@' ){
   352         -        char *zValue = db_text(0, 
   353         -            "SELECT value FROM config WHERE name=%Q", zName);
   354         -        if( zValue ){
   355         -          blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%Q);\n", 
   356         -                       zName, zValue);
   357         -        }
   358         -        free(zValue);
   359         -      }else{
   360         -        configure_render_special_name(zName, &out);
   361         -      }
   362         -    }
   363         -  }
          774  +  configure_send_group(&out, groupMask, iStart);
   364    775     blob_write_to_file(&out, zFilename);
   365    776     blob_reset(&out);
   366    777   }
   367    778   
   368    779   
   369    780   /*
   370         -** COMMAND: configuration
          781  +** COMMAND: configuration*
   371    782   **
   372         -** Usage: %fossil configure METHOD ...
          783  +** Usage: %fossil configuration METHOD ... ?OPTIONS?
   373    784   **
   374    785   ** Where METHOD is one of: export import merge pull push reset.  All methods
   375    786   ** accept the -R or --repository option to specific a repository.
   376    787   **
   377    788   **    %fossil configuration export AREA FILENAME
   378    789   **
   379         -**         Write to FILENAME exported configuraton information for AREA.
   380         -**         AREA can be one of:  all ticket skin project
          790  +**         Write to FILENAME exported configuration information for AREA.
          791  +**         AREA can be one of:  all email project shun skin ticket user
   381    792   **
   382    793   **    %fossil configuration import FILENAME
   383    794   **
   384    795   **         Read a configuration from FILENAME, overwriting the current
   385    796   **         configuration.
   386    797   **
   387    798   **    %fossil configuration merge FILENAME
................................................................................
   390    801   **         the current configuration.  Existing values take priority over
   391    802   **         values read from FILENAME.
   392    803   **
   393    804   **    %fossil configuration pull AREA ?URL?
   394    805   **
   395    806   **         Pull and install the configuration from a different server
   396    807   **         identified by URL.  If no URL is specified, then the default
   397         -**         server is used. 
          808  +**         server is used. Use the --legacy option for the older protocol
          809  +**         (when talking to servers compiled prior to 2011-04-27.)  Use
          810  +**         the --overwrite flag to completely replace local settings with
          811  +**         content received from URL.
   398    812   **
   399    813   **    %fossil configuration push AREA ?URL?
   400    814   **
   401    815   **         Push the local configuration into the remote server identified
   402    816   **         by URL.  Admin privilege is required on the remote server for
   403         -**         this to work.
          817  +**         this to work.  When the same record exists both locally and on
          818  +**         the remote end, the one that was most recently changed wins.
          819  +**         Use the --legacy flag when talking to holder servers.
   404    820   **
   405    821   **    %fossil configuration reset AREA
   406    822   **
   407    823   **         Restore the configuration to the default.  AREA as above.
   408    824   **
   409         -** WARNING: Do not import, merge, or pull configurations from an untrusted
   410         -** source.  The inbound configuration is not checked for safety and can
   411         -** introduce security vulnerabilities.
          825  +**    %fossil configuration sync AREA ?URL?
          826  +**
          827  +**         Synchronize configuration changes in the local repository with
          828  +**         the remote repository at URL.
          829  +**
          830  +** Options:
          831  +**    -R|--repository FILE       Extract info from repository FILE
          832  +**
          833  +** See also: settings, unset
   412    834   */
   413    835   void configuration_cmd(void){
   414    836     int n;
   415    837     const char *zMethod;
   416    838     if( g.argc<3 ){
   417    839       usage("export|import|merge|pull|reset ...");
   418    840     }
   419         -  db_find_and_open_repository(1);
          841  +  db_find_and_open_repository(0, 0);
          842  +  db_open_config(0);
   420    843     zMethod = g.argv[2];
   421    844     n = strlen(zMethod);
   422    845     if( strncmp(zMethod, "export", n)==0 ){
   423    846       int mask;
          847  +    const char *zSince = find_option("since",0,1);
          848  +    sqlite3_int64 iStart;
   424    849       if( g.argc!=5 ){
   425    850         usage("export AREA FILENAME");
   426    851       }
   427         -    mask = find_area(g.argv[3]);
   428         -    export_config(mask, g.argv[3], g.argv[4]);
          852  +    mask = configure_name_to_mask(g.argv[3], 1);
          853  +    if( zSince ){
          854  +      iStart = db_multi_exec(
          855  +         "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0",
          856  +         zSince, zSince
          857  +      );
          858  +    }else{
          859  +      iStart = 0;
          860  +    }
          861  +    export_config(mask, g.argv[3], iStart, g.argv[4]);
   429    862     }else
   430         -  if( strncmp(zMethod, "import", n)==0 
          863  +  if( strncmp(zMethod, "import", n)==0
   431    864          || strncmp(zMethod, "merge", n)==0 ){
   432    865       Blob in;
          866  +    int groupMask;
   433    867       if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
   434    868       blob_read_from_file(&in, g.argv[3]);
   435    869       db_begin_transaction();
   436         -    configure_prepare_to_receive(zMethod[0]=='i');
   437         -    db_multi_exec("%s", blob_str(&in));
   438         -    configure_finalize_receive();
          870  +    if( zMethod[0]=='i' ){
          871  +      groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
          872  +    }else{
          873  +      groupMask = CONFIGSET_ALL;
          874  +    }
          875  +    configure_receive_all(&in, groupMask);
   439    876       db_end_transaction(0);
   440    877     }else
   441         -  if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
          878  +  if( strncmp(zMethod, "pull", n)==0
          879  +   || strncmp(zMethod, "push", n)==0
          880  +   || strncmp(zMethod, "sync", n)==0
          881  +  ){
   442    882       int mask;
   443    883       const char *zServer;
   444    884       const char *zPw;
          885  +    int legacyFlag = 0;
          886  +    int overwriteFlag = 0;
          887  +    if( zMethod[0]!='s' ) legacyFlag = find_option("legacy",0,0)!=0;
          888  +    if( strncmp(zMethod,"pull",n)==0 ){
          889  +      overwriteFlag = find_option("overwrite",0,0)!=0;
          890  +    }
   445    891       url_proxy_options();
   446    892       if( g.argc!=4 && g.argc!=5 ){
   447    893         usage("pull AREA ?URL?");
   448    894       }
   449         -    mask = find_area(g.argv[3]);
          895  +    mask = configure_name_to_mask(g.argv[3], 1);
   450    896       if( g.argc==5 ){
   451    897         zServer = g.argv[4];
   452    898         zPw = 0;
   453    899         g.dontKeepUrl = 1;
   454    900       }else{
   455    901         zServer = db_get("last-sync-url", 0);
   456    902         if( zServer==0 ){
   457    903           fossil_fatal("no server specified");
   458    904         }
   459         -      zPw = db_get("last-sync-pw", 0);
          905  +      zPw = unobscure(db_get("last-sync-pw", 0));
   460    906       }
   461    907       url_parse(zServer);
   462    908       if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
   463    909       user_select();
          910  +    url_enable_proxy("via proxy: ");
          911  +    if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
          912  +    if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
   464    913       if( strncmp(zMethod, "push", n)==0 ){
   465         -      client_sync(0,0,0,0,mask);
          914  +      client_sync(0,0,(unsigned)mask);
          915  +    }else if( strncmp(zMethod, "pull", n)==0 ){
          916  +      client_sync(0,(unsigned)mask,0);
   466    917       }else{
   467         -      client_sync(0,0,0,mask,0);
          918  +      client_sync(0,(unsigned)mask,(unsigned)mask);
   468    919       }
   469    920     }else
   470    921     if( strncmp(zMethod, "reset", n)==0 ){
   471    922       int mask, i;
   472    923       char *zBackup;
   473    924       if( g.argc!=4 ) usage("reset AREA");
   474         -    mask = find_area(g.argv[3]);
   475         -    zBackup = db_text(0, 
          925  +    mask = configure_name_to_mask(g.argv[3], 1);
          926  +    zBackup = db_text(0,
   476    927          "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
   477    928       db_begin_transaction();
   478         -    export_config(mask, g.argv[3], zBackup);
          929  +    export_config(mask, g.argv[3], 0, zBackup);
   479    930       for(i=0; i<count(aConfig); i++){
   480    931         const char *zName = aConfig[i].zName;
   481    932         if( (aConfig[i].groupMask & mask)==0 ) continue;
   482    933         if( zName[0]!='@' ){
   483    934           db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   484         -      }else if( strcmp(zName,"@user")==0 ){
          935  +      }else if( fossil_strcmp(zName,"@user")==0 ){
   485    936           db_multi_exec("DELETE FROM user");
   486    937           db_create_default_users(0, 0);
   487         -      }else if( strcmp(zName,"@concealed")==0 ){
          938  +      }else if( fossil_strcmp(zName,"@concealed")==0 ){
   488    939           db_multi_exec("DELETE FROM concealed");
   489         -      }else if( strcmp(zName,"@shun")==0 ){
          940  +      }else if( fossil_strcmp(zName,"@shun")==0 ){
   490    941           db_multi_exec("DELETE FROM shun");
   491         -      }else if( strcmp(zName,"@reportfmt")==0 ){
          942  +      }else if( fossil_strcmp(zName,"@reportfmt")==0 ){
   492    943           db_multi_exec("DELETE FROM reportfmt");
   493    944         }
   494    945       }
   495    946       db_end_transaction(0);
   496         -    printf("Configuration reset to factory defaults.\n");
   497         -    printf("To recover, use:  %s %s import %s\n", 
          947  +    fossil_print("Configuration reset to factory defaults.\n");
          948  +    fossil_print("To recover, use:  %s %s import %s\n",
   498    949               g.argv[0], g.argv[1], zBackup);
   499    950     }else
   500    951     {
   501    952       fossil_fatal("METHOD should be one of:"
   502    953                    " export import merge pull push reset");
   503    954     }
   504    955   }

Changes to src/content.c.

    18     18   ** Procedures store and retrieve records from the repository
    19     19   */
    20     20   #include "config.h"
    21     21   #include "content.h"
    22     22   #include <assert.h>
    23     23   
    24     24   /*
    25         -** Macros for debugging
           25  +** The artifact retrieval cache
    26     26   */
    27         -#if 0
    28         -# define CONTENT_TRACE(X)  printf X;
    29         -#else
    30         -# define CONTENT_TRACE(X)
    31         -#endif
    32         -
    33         -/*
    34         -** The artifact retrival cache
    35         -*/
    36         -#define MX_CACHE_CNT  50    /* Maximum number of positive cache entries */
    37         -#define EXPELL_INTERVAL 5   /* How often to expell from a full cache */
    38     27   static struct {
    39         -  int n;               /* Current number of positive cache entries */
           28  +  i64 szTotal;         /* Total size of all entries in the cache */
           29  +  int n;               /* Current number of cache entries */
           30  +  int nAlloc;          /* Number of slots allocated in a[] */
    40     31     int nextAge;         /* Age counter for implementing LRU */
    41     32     int skipCnt;         /* Used to limit entries expelled from cache */
    42         -  struct {             /* One instance of this for each cache entry */
           33  +  struct cacheLine {   /* One instance of this for each cache entry */
    43     34       int rid;                  /* Artifact id */
    44     35       int age;                  /* Age.  Newer is larger */
    45     36       Blob content;             /* Content of the artifact */
    46         -  } a[MX_CACHE_CNT];   /* The positive cache */
           37  +  } *a;                /* The positive cache */
           38  +  Bag inCache;         /* Set of artifacts currently in cache */
    47     39   
    48     40     /*
    49     41     ** The missing artifact cache.
    50     42     **
    51     43     ** Artifacts whose record ID are in missingCache cannot be retrieved
    52     44     ** either because they are phantoms or because they are a delta that
    53     45     ** depends on a phantom.  Artifacts whose content we are certain is
    54     46     ** available are in availableCache.  If an artifact is in neither cache
    55         -  ** then its current availablity is unknown.
           47  +  ** then its current availability is unknown.
    56     48     */
    57     49     Bag missing;         /* Cache of artifacts that are incomplete */
    58     50     Bag available;       /* Cache of artifacts that are complete */
    59     51   } contentCache;
    60     52   
           53  +/*
           54  +** Remove the oldest element from the content cache
           55  +*/
           56  +static void content_cache_expire_oldest(void){
           57  +  int i;
           58  +  int mnAge = contentCache.nextAge;
           59  +  int mn = -1;
           60  +  for(i=0; i<contentCache.n; i++){
           61  +    if( contentCache.a[i].age<mnAge ){
           62  +      mnAge = contentCache.a[i].age;
           63  +      mn = i;
           64  +    }
           65  +  }
           66  +  if( mn>=0 ){
           67  +    bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
           68  +    contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
           69  +    blob_reset(&contentCache.a[mn].content);
           70  +    contentCache.n--;
           71  +    contentCache.a[mn] = contentCache.a[contentCache.n];
           72  +  }
           73  +}
           74  +
           75  +/*
           76  +** Add an entry to the content cache.
           77  +**
           78  +** This routines hands responsibility for the artifact over to the cache.
           79  +** The cache will deallocate memory when it has finished with it.
           80  +*/
           81  +void content_cache_insert(int rid, Blob *pBlob){
           82  +  struct cacheLine *p;
           83  +  if( contentCache.n>500 || contentCache.szTotal>50000000 ){
           84  +    i64 szBefore;
           85  +    do{
           86  +      szBefore = contentCache.szTotal;
           87  +      content_cache_expire_oldest();
           88  +    }while( contentCache.szTotal>50000000 && contentCache.szTotal<szBefore );
           89  +  }
           90  +  if( contentCache.n>=contentCache.nAlloc ){
           91  +    contentCache.nAlloc = contentCache.nAlloc*2 + 10;
           92  +    contentCache.a = fossil_realloc(contentCache.a,
           93  +                             contentCache.nAlloc*sizeof(contentCache.a[0]));
           94  +  }
           95  +  p = &contentCache.a[contentCache.n++];
           96  +  p->rid = rid;
           97  +  p->age = contentCache.nextAge++;
           98  +  contentCache.szTotal += blob_size(pBlob);
           99  +  p->content = *pBlob;
          100  +  blob_zero(pBlob);
          101  +  bag_insert(&contentCache.inCache, rid);
          102  +}
    61    103   
    62    104   /*
    63    105   ** Clear the content cache.
    64    106   */
    65    107   void content_clear_cache(void){
    66    108     int i;
    67    109     for(i=0; i<contentCache.n; i++){
    68    110       blob_reset(&contentCache.a[i].content);
    69    111     }
    70    112     bag_clear(&contentCache.missing);
    71    113     bag_clear(&contentCache.available);
          114  +  bag_clear(&contentCache.inCache);
    72    115     contentCache.n = 0;
          116  +  contentCache.szTotal = 0;
    73    117   }
    74    118   
    75    119   /*
    76    120   ** Return the srcid associated with rid.  Or return 0 if rid is 
    77    121   ** original content and not a delta.
    78    122   */
    79    123   static int findSrcid(int rid){
................................................................................
    85    129       srcid = db_column_int(&q, 0);
    86    130     }else{
    87    131       srcid = 0;
    88    132     }
    89    133     db_reset(&q);
    90    134     return srcid;
    91    135   }
          136  +
          137  +/*
          138  +** Return the blob.size field given blob.rid
          139  +*/
          140  +int content_size(int rid, int dflt){
          141  +  static Stmt q;
          142  +  int sz = dflt;
          143  +  db_static_prepare(&q, "SELECT size FROM blob WHERE rid=:r");
          144  +  db_bind_int(&q, ":r", rid);
          145  +  if( db_step(&q)==SQLITE_ROW ){
          146  +    sz = db_column_int(&q, 0);
          147  +  }
          148  +  db_reset(&q);
          149  +  return sz;
          150  +}
    92    151   
    93    152   /*
    94    153   ** Check to see if content is available for artifact "rid".  Return
    95    154   ** true if it is.  Return false if rid is a phantom or depends on
    96    155   ** a phantom.
    97    156   */
    98    157   int content_is_available(int rid){
    99    158     int srcid;
   100         -  if( bag_find(&contentCache.missing, rid) ){
   101         -    return 0;
   102         -  }
   103         -  if( bag_find(&contentCache.available, rid) ){
   104         -    return 1;
   105         -  }
   106         -  if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
   107         -    bag_insert(&contentCache.missing, rid);
   108         -    return 0;
   109         -  }
   110         -  srcid = findSrcid(rid);
   111         -  if( srcid==0 ){
   112         -    bag_insert(&contentCache.available, rid);
   113         -    return 1;
   114         -  }
   115         -  if( content_is_available(srcid) ){
   116         -    bag_insert(&contentCache.available, rid);
   117         -    return 1;
   118         -  }else{
   119         -    bag_insert(&contentCache.missing, rid);
   120         -    return 0;
   121         -  }
          159  +  int depth = 0;  /* Limit to recursion depth */
          160  +  while( depth++ < 10000000 ){  
          161  +    if( bag_find(&contentCache.missing, rid) ){
          162  +      return 0;
          163  +    }
          164  +    if( bag_find(&contentCache.available, rid) ){
          165  +      return 1;
          166  +    }
          167  +    if( content_size(rid, -1)<0 ){
          168  +      bag_insert(&contentCache.missing, rid);
          169  +      return 0;
          170  +    }
          171  +    srcid = findSrcid(rid);
          172  +    if( srcid==0 ){
          173  +      bag_insert(&contentCache.available, rid);
          174  +      return 1;
          175  +    }
          176  +    rid = srcid;
          177  +  }
          178  +  fossil_panic("delta-loop in repository");
          179  +  return 0;
   122    180   }
   123    181   
   124    182   /*
   125    183   ** Mark artifact rid as being available now.  Update the cache to
   126    184   ** show that everything that was formerly unavailable because rid
   127    185   ** was missing is now available.
   128    186   */
   129    187   static void content_mark_available(int rid){
   130    188     Bag pending;
   131         -  Stmt q;
          189  +  static Stmt q;
   132    190     if( bag_find(&contentCache.available, rid) ) return;
   133    191     bag_init(&pending);
   134    192     bag_insert(&pending, rid);
   135    193     while( (rid = bag_first(&pending))!=0 ){
   136    194       bag_remove(&pending, rid);
   137    195       bag_remove(&contentCache.missing, rid);
   138    196       bag_insert(&contentCache.available, rid);
   139         -    db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
          197  +    db_static_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid");
          198  +    db_bind_int(&q, ":rid", rid);
   140    199       while( db_step(&q)==SQLITE_ROW ){
   141    200         int nx = db_column_int(&q, 0);
   142    201         bag_insert(&pending, nx);
   143    202       }
   144         -    db_finalize(&q);
          203  +    db_reset(&q);
   145    204     }
   146    205     bag_clear(&pending);
   147    206   }
   148    207   
   149    208   /*
   150    209   ** Get the blob.content value for blob.rid=rid.  Return 1 on success or
   151    210   ** 0 on failure.
................................................................................
   160    219       blob_uncompress(pBlob, pBlob);
   161    220       rc = 1;
   162    221     }
   163    222     db_reset(&q);
   164    223     return rc;
   165    224   }
   166    225   
   167         -
   168    226   /*
   169    227   ** Extract the content for ID rid and put it into the
   170    228   ** uninitialized blob.  Return 1 on success.  If the record
   171    229   ** is a phantom, zero pBlob and return 0.
   172    230   */
   173    231   int content_get(int rid, Blob *pBlob){
   174         -  Blob src;
   175         -  int srcid;
   176         -  int rc = 0;
          232  +  int rc;
   177    233     int i;
   178         -  static Bag inProcess;
          234  +  int nextRid;
   179    235   
   180    236     assert( g.repositoryOpen );
   181    237     blob_zero(pBlob);
   182    238     if( rid==0 ) return 0;
   183    239   
   184    240     /* Early out if we know the content is not available */
   185    241     if( bag_find(&contentCache.missing, rid) ){
   186         -    CONTENT_TRACE(("%*smiss from cache: %d\n",
   187         -                    bag_count(&inProcess), "", rid))
   188    242       return 0;
   189    243     }
   190    244   
   191    245     /* Look for the artifact in the cache first */
   192         -  for(i=0; i<contentCache.n; i++){
   193         -    if( contentCache.a[i].rid==rid ){
   194         -      *pBlob = contentCache.a[i].content;
   195         -      blob_zero(&contentCache.a[i].content);
   196         -      contentCache.n--;
   197         -      if( i<contentCache.n ){
   198         -        contentCache.a[i] = contentCache.a[contentCache.n];
          246  +  if( bag_find(&contentCache.inCache, rid) ){
          247  +    for(i=0; i<contentCache.n; i++){
          248  +      if( contentCache.a[i].rid==rid ){
          249  +        blob_copy(pBlob, &contentCache.a[i].content);
          250  +        contentCache.a[i].age = contentCache.nextAge++;
          251  +        return 1;
          252  +      }
          253  +    }
          254  +  }
          255  +
          256  +  nextRid = findSrcid(rid);
          257  +  if( nextRid==0 ){
          258  +    rc = content_of_blob(rid, pBlob);
          259  +  }else{
          260  +    int n = 1;
          261  +    int nAlloc = 10;
          262  +    int *a = 0;
          263  +    int mx;
          264  +    Blob delta, next;
          265  +
          266  +    a = fossil_malloc( sizeof(a[0])*nAlloc );
          267  +    a[0] = rid;
          268  +    a[1] = nextRid;
          269  +    n = 1;
          270  +    while( !bag_find(&contentCache.inCache, nextRid)
          271  +        && (nextRid = findSrcid(nextRid))>0 ){
          272  +      n++;
          273  +      if( n>=nAlloc ){
          274  +        if( n>db_int(0, "SELECT max(rid) FROM blob") ){
          275  +          fossil_panic("infinite loop in DELTA table");
          276  +        }
          277  +        nAlloc = nAlloc*2 + 10;
          278  +        a = fossil_realloc(a, nAlloc*sizeof(a[0]));
   199    279         }
   200         -      CONTENT_TRACE(("%*scache: %d\n", 
   201         -                    bag_count(&inProcess), "", rid))
   202         -      return 1;
          280  +      a[n] = nextRid;
   203    281       }
   204         -  }
   205         -
   206         -  /* See if we need to apply a delta to find this artifact */
   207         -  srcid = findSrcid(rid);
   208         -  CONTENT_TRACE(("%*ssearching for %d.  Need %d.\n",
   209         -                 bag_count(&inProcess), "", rid, srcid))
   210         -
   211         -
   212         -  if( srcid ){
   213         -    /* Yes, a delta is required */
   214         -    if( bag_find(&inProcess, srcid) ){
   215         -      db_multi_exec(
   216         -        "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
   217         -        "DELETE FROM delta WHERE rid=%d;"
   218         -        "INSERT OR IGNORE INTO phantom VALUES(%d);",
   219         -        srcid, srcid, srcid
   220         -      );
   221         -      blob_zero(pBlob);
   222         -      return 0;
   223         -    }
   224         -    bag_insert(&inProcess, srcid);
   225         -
   226         -    if( content_get(srcid, &src) ){
   227         -      Blob delta;
   228         -      if( content_of_blob(rid, &delta) ){
   229         -        blob_init(pBlob,0,0);
   230         -        blob_delta_apply(&src, &delta, pBlob);
          282  +    mx = n;
          283  +    rc = content_get(a[n], pBlob);
          284  +    n--;
          285  +    while( rc && n>=0 ){
          286  +      rc = content_of_blob(a[n], &delta);
          287  +      if( rc ){
          288  +        blob_delta_apply(pBlob, &delta, &next);
   231    289           blob_reset(&delta);
   232         -        rc = 1;
   233         -      }
   234         -
   235         -      /* Save the srcid artifact in the cache */
   236         -      if( contentCache.n<MX_CACHE_CNT ){
   237         -        i = contentCache.n++;
   238         -      }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){
   239         -        i = -1;
   240         -      }else{
   241         -        int j, best;
   242         -        best = contentCache.nextAge+1;
   243         -        i = -1;
   244         -        for(j=0; j<contentCache.n; j++){
   245         -          if( contentCache.a[j].age<best ){
   246         -            i = j;
   247         -            best = contentCache.a[j].age;
   248         -          }
          290  +        if( (mx-n)%8==0 ){
          291  +          content_cache_insert(a[n+1], pBlob);
          292  +        }else{
          293  +          blob_reset(pBlob);
   249    294           }
   250         -        CONTENT_TRACE(("%*sexpell %d from cache\n",
   251         -                       bag_count(&inProcess), "", contentCache.a[i].rid))
   252         -        blob_reset(&contentCache.a[i].content);
          295  +        *pBlob = next;
   253    296         }
   254         -      if( i>=0 ){
   255         -        contentCache.a[i].content = src;
   256         -        contentCache.a[i].age = contentCache.nextAge++;
   257         -        contentCache.a[i].rid = srcid;
   258         -        CONTENT_TRACE(("%*sadd %d to cache\n",
   259         -                       bag_count(&inProcess), "", srcid))
   260         -      }else{
   261         -        blob_reset(&src);
   262         -      }
          297  +      n--;
   263    298       }
   264         -    bag_remove(&inProcess, srcid);
   265         -  }else{
   266         -    /* No delta required.  Read content directly from the database */
   267         -    if( content_of_blob(rid, pBlob) ){
   268         -      rc = 1;
   269         -    }
          299  +    free(a);
          300  +    if( !rc ) blob_reset(pBlob);
   270    301     }
   271    302     if( rc==0 ){
   272    303       bag_insert(&contentCache.missing, rid);
   273    304     }else{
   274    305       bag_insert(&contentCache.available, rid);
   275    306     }
   276    307     return rc;
   277    308   }
   278    309   
   279    310   /*
   280         -** COMMAND:  artifact
          311  +** COMMAND: artifact*
   281    312   **
   282         -** Usage: %fossil artifact ARTIFACT-ID  ?OUTPUT-FILENAME?
          313  +** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
   283    314   **
   284    315   ** Extract an artifact by its SHA1 hash and write the results on
   285    316   ** standard output, or if the optional 4th argument is given, in
   286    317   ** the named output file.
          318  +**
          319  +** Options:
          320  +**    -R|--repository FILE       Extract artifacts from repository FILE
          321  +**
          322  +** See also: finfo
   287    323   */
   288    324   void artifact_cmd(void){
   289    325     int rid;
   290    326     Blob content;
   291    327     const char *zFile;
   292         -  if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?");
          328  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
          329  +  if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?");
   293    330     zFile = g.argc==4 ? g.argv[3] : "-";
   294         -  db_must_be_within_tree();
   295    331     rid = name_to_rid(g.argv[2]);
          332  +  if( rid==0 ){
          333  +    fossil_fatal("%s",g.zErrMsg);
          334  +  }
   296    335     content_get(rid, &content);
   297    336     blob_write_to_file(&content, zFile);
   298    337   }
   299    338   
   300    339   /*
   301    340   ** COMMAND:  test-content-rawget
   302    341   **
................................................................................
   313    352     rid = name_to_rid(g.argv[2]);
   314    353     blob_zero(&content);
   315    354     db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
   316    355     blob_uncompress(&content, &content);
   317    356     blob_write_to_file(&content, zFile);
   318    357   }
   319    358   
          359  +/*
          360  +** The following flag is set to disable the automatic calls to
          361  +** manifest_crosslink() when a record is dephantomized.  This
          362  +** flag can be set (for example) when doing a clone when we know
          363  +** that rebuild will be run over all records at the conclusion
          364  +** of the operation.
          365  +*/
          366  +static int ignoreDephantomizations = 0;
          367  +
   320    368   /*
   321    369   ** When a record is converted from a phantom to a real record,
   322    370   ** if that record has other records that are derived by delta,
   323    371   ** then call manifest_crosslink() on those other records.
          372  +**
          373  +** If the formerly phantom record or any of the other records
          374  +** derived by delta from the former phantom are a baseline manifest,
          375  +** then also invoke manifest_crosslink() on the delta-manifests
          376  +** associated with that baseline.
          377  +**
          378  +** Tail recursion is used to minimize stack depth.
   324    379   */
   325    380   void after_dephantomize(int rid, int linkFlag){
   326    381     Stmt q;
   327         -  db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
   328         -  while( db_step(&q)==SQLITE_ROW ){
   329         -    int tid = db_column_int(&q, 0);
   330         -    after_dephantomize(tid, 1);
   331         -  }
   332         -  db_finalize(&q);
   333         -  if( linkFlag ){
   334         -    Blob content;
   335         -    content_get(rid, &content);
   336         -    manifest_crosslink(rid, &content);
   337         -    blob_reset(&content);
   338         -  }
          382  +  int nChildAlloc = 0;
          383  +  int *aChild = 0;
          384  +  Blob content;
          385  +
          386  +  if( ignoreDephantomizations ) return;
          387  +  while( rid ){
          388  +    int nChildUsed = 0;
          389  +    int i;
          390  +
          391  +    /* Parse the object rid itself */
          392  +    if( linkFlag ){
          393  +      content_get(rid, &content);
          394  +      manifest_crosslink(rid, &content);
          395  +      assert( blob_is_reset(&content) );
          396  +    }
          397  +
          398  +    /* Parse all delta-manifests that depend on baseline-manifest rid */
          399  +    db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid);
          400  +    while( db_step(&q)==SQLITE_ROW ){
          401  +      int child = db_column_int(&q, 0);
          402  +      if( nChildUsed>=nChildAlloc ){
          403  +        nChildAlloc = nChildAlloc*2 + 10;
          404  +        aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild));
          405  +      }
          406  +      aChild[nChildUsed++] = child;
          407  +    }
          408  +    db_finalize(&q);
          409  +    for(i=0; i<nChildUsed; i++){
          410  +      content_get(aChild[i], &content);
          411  +      manifest_crosslink(aChild[i], &content);
          412  +      assert( blob_is_reset(&content) );
          413  +    }
          414  +    if( nChildUsed ){
          415  +      db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid);
          416  +    }
          417  +
          418  +    /* Recursively dephantomize all artifacts that are derived by
          419  +    ** delta from artifact rid and which have not already been
          420  +    ** cross-linked.  */
          421  +    nChildUsed = 0;
          422  +    db_prepare(&q, 
          423  +       "SELECT rid FROM delta"
          424  +       " WHERE srcid=%d"
          425  +       "   AND NOT EXISTS(SELECT 1 FROM mlink WHERE mid=delta.rid)",
          426  +       rid
          427  +    );
          428  +    while( db_step(&q)==SQLITE_ROW ){
          429  +      int child = db_column_int(&q, 0);
          430  +      if( nChildUsed>=nChildAlloc ){
          431  +        nChildAlloc = nChildAlloc*2 + 10;
          432  +        aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild));
          433  +      }
          434  +      aChild[nChildUsed++] = child;
          435  +    }
          436  +    db_finalize(&q);
          437  +    for(i=1; i<nChildUsed; i++){
          438  +      after_dephantomize(aChild[i], 1);
          439  +    }
          440  +
          441  +    /* Tail recursion for the common case where only a single artifact
          442  +    ** is derived by delta from rid... */
          443  +    rid = nChildUsed>0 ? aChild[0] : 0;
          444  +    linkFlag = 1;
          445  +  }
          446  +  free(aChild);
          447  +}
          448  +
          449  +/*
          450  +** Turn dephantomization processing on or off.
          451  +*/
          452  +void content_enable_dephantomize(int onoff){
          453  +  ignoreDephantomizations = !onoff;
   339    454   }
   340    455   
   341    456   /*
   342    457   ** Write content into the database.  Return the record ID.  If the
   343    458   ** content is already in the database, just return the record ID.
   344    459   **
   345    460   ** If srcId is specified, then pBlob is delta content from
   346         -** the srcId record.  srcId might be a phantom.
          461  +** the srcId record.  srcId might be a phantom.  
          462  +**
          463  +** pBlob is normally uncompressed text.  But if nBlob>0 then the
          464  +** pBlob value has already been compressed and nBlob is its uncompressed
          465  +** size.  If nBlob>0 then zUuid must be valid.
   347    466   **
   348    467   ** zUuid is the UUID of the artifact, if it is specified.  When srcId is
   349    468   ** specified then zUuid must always be specified.  If srcId is zero,
   350    469   ** and zUuid is zero then the correct zUuid is computed from pBlob.
   351    470   **
   352    471   ** If the record already exists but is a phantom, the pBlob content
   353    472   ** is inserted and the phatom becomes a real record.
          473  +**
          474  +** The original content of pBlob is not disturbed.  The caller continues
          475  +** to be responsible for pBlob.  This routine does *not* take over
          476  +** responsibility for freeing pBlob.
   354    477   */
   355         -int content_put(Blob *pBlob, const char *zUuid, int srcId){
          478  +int content_put_ex(
          479  +  Blob *pBlob,              /* Content to add to the repository */
          480  +  const char *zUuid,        /* SHA1 hash of reconstructed pBlob */
          481  +  int srcId,                /* pBlob is a delta from this entry */
          482  +  int nBlob,                /* pBlob is compressed. Original size is this */
          483  +  int isPrivate             /* The content should be marked private */
          484  +){
   356    485     int size;
   357    486     int rid;
   358    487     Stmt s1;
   359    488     Blob cmpr;
   360    489     Blob hash;
   361    490     int markAsUnclustered = 0;
   362    491     int isDephantomize = 0;
   363    492     
   364    493     assert( g.repositoryOpen );
   365    494     assert( pBlob!=0 );
   366    495     assert( srcId==0 || zUuid!=0 );
   367    496     if( zUuid==0 ){
   368    497       assert( pBlob!=0 );
          498  +    assert( nBlob==0 );
   369    499       sha1sum_blob(pBlob, &hash);
   370    500     }else{
   371    501       blob_init(&hash, zUuid, -1);
   372    502     }
   373         -  size = blob_size(pBlob);
          503  +  if( nBlob ){
          504  +    size = nBlob;
          505  +  }else{
          506  +    size = blob_size(pBlob);
          507  +    if( srcId ){
          508  +      size = delta_output_size(blob_buffer(pBlob), size);
          509  +    }
          510  +  }
   374    511     db_begin_transaction();
   375    512   
   376    513     /* Check to see if the entry already exists and if it does whether
   377    514     ** or not the entry is a phantom
   378    515     */
   379    516     db_prepare(&s1, "SELECT rid, size FROM blob WHERE uuid=%B", &hash);
   380    517     if( db_step(&s1)==SQLITE_ROW ){
................................................................................
   399    536          "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
   400    537          "VALUES(%d, julianday('now'), %Q, %Q)",
   401    538          g.userUid, g.zNonce, g.zIpAddr
   402    539       );
   403    540       g.rcvid = db_last_insert_rowid();
   404    541     }
   405    542   
   406         -  blob_compress(pBlob, &cmpr);
          543  +  if( nBlob ){
          544  +    cmpr = pBlob[0];
          545  +  }else{
          546  +    blob_compress(pBlob, &cmpr);
          547  +  }
   407    548     if( rid>0 ){
   408    549       /* We are just adding data to a phantom */
   409    550       db_prepare(&s1,
   410    551         "UPDATE blob SET rcvid=%d, size=%d, content=:data WHERE rid=%d",
   411    552          g.rcvid, size, rid
   412    553       );
   413    554       db_bind_blob(&s1, ":data", &cmpr);
................................................................................
   426    567       );
   427    568       db_bind_blob(&s1, ":data", &cmpr);
   428    569       db_exec(&s1);
   429    570       rid = db_last_insert_rowid();
   430    571       if( !pBlob ){
   431    572         db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
   432    573       }
   433         -    if( g.markPrivate ){
          574  +    if( g.markPrivate || isPrivate ){
   434    575         db_multi_exec("INSERT INTO private VALUES(%d)", rid);
   435    576         markAsUnclustered = 0;
   436    577       }
   437    578     }
   438         -  blob_reset(&cmpr);
          579  +  if( nBlob==0 ) blob_reset(&cmpr);
   439    580   
   440    581     /* If the srcId is specified, then the data we just added is
   441    582     ** really a delta.  Record this fact in the delta table.
   442    583     */
   443    584     if( srcId ){
   444    585       db_multi_exec("REPLACE INTO delta(rid,srcid) VALUES(%d,%d)", rid, srcId);
   445    586     }
................................................................................
   464    605     blob_reset(&hash);
   465    606   
   466    607     /* Make arrangements to verify that the data can be recovered
   467    608     ** before we commit */
   468    609     verify_before_commit(rid);
   469    610     return rid;
   470    611   }
          612  +
          613  +/*
          614  +** This is the simple common case for inserting content into the
          615  +** repository.  pBlob is the content to be inserted.
          616  +**
          617  +** pBlob is uncompressed and is not deltaed.  It is exactly the content
          618  +** to be inserted.
          619  +**
          620  +** The original content of pBlob is not disturbed.  The caller continues
          621  +** to be responsible for pBlob.  This routine does *not* take over
          622  +** responsiblity for freeing pBlob.
          623  +*/
          624  +int content_put(Blob *pBlob){
          625  +  return content_put_ex(pBlob, 0, 0, 0, 0);
          626  +}
          627  +
   471    628   
   472    629   /*
   473    630   ** Create a new phantom with the given UUID and return its artifact ID.
   474    631   */
   475         -int content_new(const char *zUuid){
          632  +int content_new(const char *zUuid, int isPrivate){
   476    633     int rid;
   477    634     static Stmt s1, s2, s3;
   478    635     
   479    636     assert( g.repositoryOpen );
   480    637     db_begin_transaction();
   481    638     if( uuid_is_shunned(zUuid) ){
   482    639       db_end_transaction(0);
................................................................................
   490    647     db_exec(&s1);
   491    648     rid = db_last_insert_rowid();
   492    649     db_static_prepare(&s2,
   493    650       "INSERT INTO phantom VALUES(:rid)"
   494    651     );
   495    652     db_bind_int(&s2, ":rid", rid);
   496    653     db_exec(&s2);
   497         -  if( g.markPrivate ){
          654  +  if( g.markPrivate || isPrivate ){
   498    655       db_multi_exec("INSERT INTO private VALUES(%d)", rid);
   499    656     }else{
   500    657       db_static_prepare(&s3,
   501    658         "INSERT INTO unclustered VALUES(:rid)"
   502    659       );
   503    660       db_bind_int(&s3, ":rid", rid);
   504    661       db_exec(&s3);
................................................................................
   508    665     return rid;
   509    666   }
   510    667   
   511    668   
   512    669   /*
   513    670   ** COMMAND:  test-content-put
   514    671   **
   515         -** Extract a blob from the database and write it into a file.
          672  +** Usage: %fossil test-content-put FILE
          673  +**
          674  +** Read the content of FILE and add it to the Blob table as a new
          675  +** artifact using a direct call to content_put().
   516    676   */
   517    677   void test_content_put_cmd(void){
   518    678     int rid;
   519    679     Blob content;
   520    680     if( g.argc!=3 ) usage("FILENAME");
   521    681     db_must_be_within_tree();
   522    682     user_select();
   523    683     blob_read_from_file(&content, g.argv[2]);
   524         -  rid = content_put(&content, 0, 0);
   525         -  printf("inserted as record %d\n", rid);
          684  +  rid = content_put(&content);
          685  +  fossil_print("inserted as record %d\n", rid);
   526    686   }
   527    687   
   528    688   /*
   529    689   ** Make sure the content at rid is the original content and is not a
   530    690   ** delta.
   531    691   */
   532    692   void content_undelta(int rid){
................................................................................
   598    758   ** the source of the delta.  It is OK to delta private->private and
   599    759   ** public->private and public->public.  Just no private->public delta.
   600    760   **
   601    761   ** If srcid is a delta that depends on rid, then srcid is
   602    762   ** converted to undeltaed text.
   603    763   **
   604    764   ** If either rid or srcid contain less than 50 bytes, or if the
   605         -** resulting delta does not achieve a compression of at least 25% on
   606         -** its own the rid is left untouched.
          765  +** resulting delta does not achieve a compression of at least 25% 
          766  +** the rid is left untouched.
          767  +**
          768  +** Return 1 if a delta is made and 0 if no delta occurs.
   607    769   */
   608         -void content_deltify(int rid, int srcid, int force){
          770  +int content_deltify(int rid, int srcid, int force){
   609    771     int s;
   610    772     Blob data, src, delta;
   611    773     Stmt s1, s2;
   612         -  if( srcid==rid ) return;
   613         -  if( !force && findSrcid(rid)>0 ) return;
          774  +  int rc = 0;
          775  +
          776  +  if( srcid==rid ) return 0;
          777  +  if( !force && findSrcid(rid)>0 ) return 0;
   614    778     if( content_is_private(srcid) && !content_is_private(rid) ){
   615         -    return;
          779  +    return 0;
   616    780     }
   617    781     s = srcid;
   618    782     while( (s = findSrcid(s))>0 ){
   619    783       if( s==rid ){
   620    784         content_undelta(srcid);
   621    785         break;
   622    786       }
   623    787     }
   624    788     content_get(srcid, &src);
   625    789     if( blob_size(&src)<50 ){
   626    790       blob_reset(&src);
   627         -    return;
          791  +    return 0;
   628    792     }
   629    793     content_get(rid, &data);
   630    794     if( blob_size(&data)<50 ){
   631    795       blob_reset(&src);
   632    796       blob_reset(&data);
   633         -    return;
          797  +    return 0;
   634    798     }
   635    799     blob_delta_create(&src, &data, &delta);
   636         -  if( blob_size(&delta) < blob_size(&data)*0.75 ){
          800  +  if( blob_size(&delta) <= blob_size(&data)*0.75 ){
   637    801       blob_compress(&delta, &delta);
   638    802       db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid);
   639    803       db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, srcid);
   640    804       db_bind_blob(&s1, ":data", &delta);
   641    805       db_begin_transaction();
   642    806       db_exec(&s1);
   643    807       db_exec(&s2);
   644    808       db_end_transaction(0);
   645    809       db_finalize(&s1);
   646    810       db_finalize(&s2);
   647    811       verify_before_commit(rid);
          812  +    rc = 1;
   648    813     }
   649    814     blob_reset(&src);
   650    815     blob_reset(&data);
   651    816     blob_reset(&delta);
          817  +  return rc;
   652    818   }
   653    819   
   654    820   /*
   655    821   ** COMMAND:  test-content-deltify
   656    822   **
   657    823   ** Convert the content at RID into a delta from SRCID.
   658    824   */
   659    825   void test_content_deltify_cmd(void){
   660    826     if( g.argc!=5 ) usage("RID SRCID FORCE");
   661    827     db_must_be_within_tree();
   662    828     content_deltify(atoi(g.argv[2]), atoi(g.argv[3]), atoi(g.argv[4]));
   663    829   }
          830  +
          831  +/*
          832  +** COMMAND: test-integrity
          833  +**
          834  +** Verify that all content can be extracted from the BLOB table correctly.
          835  +** If the BLOB table is correct, then the repository can always be
          836  +** successfully reconstructed using "fossil rebuild".
          837  +*/
          838  +void test_integrity(void){
          839  +  Stmt q;
          840  +  Blob content;
          841  +  Blob cksum;
          842  +  int n1 = 0;
          843  +  int n2 = 0;
          844  +  int nErr = 0;
          845  +  int total;
          846  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 2);
          847  +
          848  +  /* Make sure no public artifact is a delta from a private artifact */
          849  +  db_prepare(&q,
          850  +    "SELECT "
          851  +    "   rid, (SELECT uuid FROM blob WHERE rid=delta.rid),"
          852  +    "   srcid, (SELECT uuid FROM blob WHERE rid=delta.srcid)"
          853  +    "  FROM delta"
          854  +    " WHERE srcid in private AND rid NOT IN private"
          855  +  );
          856  +  while( db_step(&q)==SQLITE_ROW ){
          857  +    int rid = db_column_int(&q, 0);
          858  +    const char *zId = db_column_text(&q, 1);
          859  +    int srcid = db_column_int(&q, 2);
          860  +    const char *zSrc = db_column_text(&q, 3);
          861  +    fossil_print(
          862  +      "public artifact %S (%d) is a delta from private artifact %S (%d)\n",
          863  +      zId, rid, zSrc, srcid
          864  +    );
          865  +    nErr++;
          866  +  }
          867  +  db_finalize(&q);
          868  +    
          869  +  db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid");
          870  +  total = db_int(0, "SELECT max(rid) FROM blob");
          871  +  while( db_step(&q)==SQLITE_ROW ){
          872  +    int rid = db_column_int(&q, 0);
          873  +    const char *zUuid = db_column_text(&q, 1);
          874  +    int size = db_column_int(&q, 2);
          875  +    n1++;
          876  +    fossil_print("  %d/%d\r", n1, total);
          877  +    fflush(stdout);
          878  +    if( size<0 ){
          879  +      fossil_print("skip phantom %d %s\n", rid, zUuid);
          880  +      continue;  /* Ignore phantoms */
          881  +    }
          882  +    content_get(rid, &content);
          883  +    if( blob_size(&content)!=size ){
          884  +      fossil_print("size mismatch on artifact %d: wanted %d but got %d\n",
          885  +                     rid, size, blob_size(&content));
          886  +      nErr++;
          887  +    }
          888  +    sha1sum_blob(&content, &cksum);
          889  +    if( fossil_strcmp(blob_str(&cksum), zUuid)!=0 ){
          890  +      fossil_print("checksum mismatch on artifact %d: wanted %s but got %s\n",
          891  +                   rid, zUuid, blob_str(&cksum));
          892  +      nErr++;
          893  +    }
          894  +    blob_reset(&cksum);
          895  +    blob_reset(&content);
          896  +    n2++;
          897  +  }
          898  +  db_finalize(&q);
          899  +  fossil_print("%d non-phantom blobs (out of %d total) checked:  %d errors\n",
          900  +               n2, n1, nErr);
          901  +}
          902  +
          903  +/*
          904  +** COMMAND: test-orphans
          905  +**
          906  +** Search the repository for orphaned artifacts
          907  +*/
          908  +void test_orphans(void){
          909  +  Stmt q;
          910  +  int cnt = 0;
          911  +
          912  +  db_find_and_open_repository(0, 0);
          913  +  db_multi_exec(
          914  +    "CREATE TEMP TABLE used(id INTEGER PRIMARY KEY ON CONFLICT IGNORE);"
          915  +    "INSERT INTO used SELECT mid FROM mlink;"  /* Manifests */
          916  +    "INSERT INTO used SELECT fid FROM mlink;"  /* Files */
          917  +    "INSERT INTO used SELECT srcid FROM tagxref WHERE srcid>0;" /* Tags */
          918  +    "INSERT INTO used SELECT rid FROM tagxref;" /* Wiki & tickets */
          919  +    "INSERT INTO used SELECT rid FROM attachment JOIN blob ON src=uuid;"
          920  +    "INSERT INTO used SELECT attachid FROM attachment;"
          921  +    "INSERT INTO used SELECT objid FROM event;"
          922  +  );
          923  +  db_prepare(&q, "SELECT rid, uuid, size FROM blob WHERE rid NOT IN used");
          924  +  while( db_step(&q)==SQLITE_ROW ){
          925  +    fossil_print("%7d %s size: %d\n",
          926  +      db_column_int(&q, 0),
          927  +      db_column_text(&q, 1),
          928  +      db_column_int(&q,2));
          929  +    cnt++;
          930  +  }
          931  +  db_finalize(&q);
          932  +  fossil_print("%d orphans\n", cnt);
          933  +}
          934  +
          935  +/* Allowed flags for check_exists */
          936  +#define MISSING_SHUNNED   0x0001    /* Do not report shunned artifacts */
          937  +
          938  +/* This is a helper routine for test-artifacts.
          939  +**
          940  +** Check to see that artifact zUuid exists in the repository.  If it does,
          941  +** return 0.  If it does not, generate an error message and return 1.
          942  +*/
          943  +static int check_exists(
          944  +  const char *zUuid,     /* The artifact we are checking for */
          945  +  unsigned flags,        /* Flags */
          946  +  Manifest *p,           /* The control artifact that references zUuid */
          947  +  const char *zRole,     /* Role of zUuid in p */
          948  +  const char *zDetail    /* Additional information, such as a filename */
          949  +){
          950  +  static Stmt q;
          951  +  int rc = 0;
          952  +
          953  +  db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid");
          954  +  if( zUuid==0 || zUuid[0]==0 ) return 0;
          955  +  db_bind_text(&q, ":uuid", zUuid);
          956  +  if( db_step(&q)==SQLITE_ROW ){
          957  +    int size = db_column_int(&q, 0);
          958  +    if( size<0 ) rc = 2;
          959  +  }else{
          960  +    rc = 1;
          961  +  }
          962  +  db_reset(&q);
          963  +  if( rc ){
          964  +    const char *zCFType = "control artifact";
          965  +    char *zSrc;
          966  +    char *zDate;
          967  +    char *zErrType = "MISSING";
          968  +    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
          969  +      if( flags & MISSING_SHUNNED ) return 0;
          970  +      zErrType = "SHUNNED";
          971  +    }
          972  +    switch( p->type ){
          973  +      case CFTYPE_MANIFEST:   zCFType = "check-in";    break;
          974  +      case CFTYPE_CLUSTER:    zCFType = "cluster";     break;
          975  +      case CFTYPE_CONTROL:    zCFType = "tag";         break;
          976  +      case CFTYPE_WIKI:       zCFType = "wiki";        break;
          977  +      case CFTYPE_TICKET:     zCFType = "ticket";      break;
          978  +      case CFTYPE_ATTACHMENT: zCFType = "attachment";  break;
          979  +      case CFTYPE_EVENT:      zCFType = "event";       break;
          980  +    }
          981  +    zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid);
          982  +    if( p->rDate>0.0 ){
          983  +      zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
          984  +    }else{
          985  +      zDate = db_text(0,
          986  +         "SELECT datetime(rcvfrom.mtime)"
          987  +         "  FROM blob, rcvfrom"
          988  +         " WHERE blob.rcvid=rcvfrom.rcvid"
          989  +         "   AND blob.rid=%d", p->rid);
          990  +    }
          991  +    fossil_print("%s: %s\n         %s %s %S (%d) %s\n",
          992  +                  zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate);
          993  +    if( zDetail && zDetail[0] ){
          994  +      fossil_print("         %s\n", zDetail);
          995  +    }
          996  +    fossil_free(zSrc);
          997  +    fossil_free(zDate);
          998  +    rc = 1; 
          999  +  }
         1000  +  return rc;
         1001  +}
         1002  +
         1003  +/*
         1004  +** COMMAND: test-missing
         1005  +**
         1006  +** Usage: %fossil test-missing
         1007  +**
         1008  +** Look at every artifact in the repository and verify that
         1009  +** all references are satisfied.  Report any referenced artifacts
         1010  +** that are missing or shunned.
         1011  +**
         1012  +** Options:
         1013  +**
         1014  +**    --notshunned          Do not report shunned artifacts
         1015  +**    --quiet               Only show output if there are errors
         1016  +*/
         1017  +void test_missing(void){
         1018  +  Stmt q;
         1019  +  Blob content;
         1020  +  int nErr = 0;
         1021  +  int nArtifact = 0;
         1022  +  int i;
         1023  +  Manifest *p;
         1024  +  unsigned flags = 0;
         1025  +  int quietFlag;
         1026  +
         1027  +  if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED;
         1028  +  quietFlag = find_option("quiet","q",0)!=0;
         1029  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
         1030  +  db_prepare(&q,
         1031  +     "SELECT mid FROM mlink UNION "
         1032  +     "SELECT srcid FROM tagxref WHERE srcid>0 UNION "
         1033  +     "SELECT rid FROM tagxref UNION "
         1034  +     "SELECT rid FROM attachment JOIN blob ON src=uuid UNION "
         1035  +     "SELECT objid FROM event");
         1036  +  while( db_step(&q)==SQLITE_ROW ){
         1037  +    int rid = db_column_int(&q, 0);
         1038  +    content_get(rid, &content);
         1039  +    p = manifest_parse(&content, rid, 0);
         1040  +    if( p ){
         1041  +      nArtifact++;
         1042  +      nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0);
         1043  +      nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0);
         1044  +      for(i=0; i<p->nFile; i++){
         1045  +        nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", 
         1046  +                             p->aFile[i].zName);
         1047  +      }
         1048  +      for(i=0; i<p->nParent; i++){
         1049  +        nErr += check_exists(p->azParent[i], flags, p, "parent of", 0);
         1050  +      }
         1051  +      for(i=0; i<p->nCherrypick; i++){
         1052  +        nErr +=  check_exists(p->aCherrypick[i].zCPTarget+1, flags, p,
         1053  +                              "cherry-pick target of", 0);
         1054  +        nErr +=  check_exists(p->aCherrypick[i].zCPBase, flags, p,
         1055  +                              "cherry-pick baseline of", 0);
         1056  +      }
         1057  +      for(i=0; i<p->nCChild; i++){
         1058  +        nErr += check_exists(p->azCChild[i], flags, p, "in", 0);
         1059  +      }
         1060  +      for(i=0; i<p->nTag; i++){
         1061  +        nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0);
         1062  +      }
         1063  +      manifest_destroy(p);      
         1064  +    }
         1065  +  }
         1066  +  db_finalize(&q);
         1067  +  if( nErr>0 || quietFlag==0 ){
         1068  +    fossil_print("%d missing or shunned references in %d control artifacts\n",
         1069  +                 nErr, nArtifact);
         1070  +  }
         1071  +}

Added src/cson_amalgamation.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/* auto-generated! Do not edit! */
            3  +#include "cson_amalgamation.h"
            4  +/* begin file parser/JSON_parser.h */
            5  +/* See JSON_parser.c for copyright information and licensing. */
            6  +
            7  +#ifndef JSON_PARSER_H
            8  +#define JSON_PARSER_H
            9  +
           10  +/* JSON_parser.h */
           11  +
           12  +
           13  +#include <stddef.h>
           14  +
           15  +/* Windows DLL stuff */
           16  +#ifdef JSON_PARSER_DLL
           17  +#   ifdef _MSC_VER
           18  +#	    ifdef JSON_PARSER_DLL_EXPORTS
           19  +#		    define JSON_PARSER_DLL_API __declspec(dllexport)
           20  +#	    else
           21  +#		    define JSON_PARSER_DLL_API __declspec(dllimport)
           22  +#       endif
           23  +#   else
           24  +#	    define JSON_PARSER_DLL_API 
           25  +#   endif
           26  +#else
           27  +#	define JSON_PARSER_DLL_API 
           28  +#endif
           29  +
           30  +/* Determine the integer type use to parse non-floating point numbers */
           31  +#if __STDC_VERSION__ >= 199901L || HAVE_LONG_LONG == 1
           32  +typedef long long JSON_int_t;
           33  +#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
           34  +#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
           35  +#else 
           36  +typedef long JSON_int_t;
           37  +#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
           38  +#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
           39  +#endif
           40  +
           41  +
           42  +#ifdef __cplusplus
           43  +extern "C" {
           44  +#endif 
           45  +
           46  +typedef enum 
           47  +{
           48  +    JSON_E_NONE = 0,
           49  +    JSON_E_INVALID_CHAR,
           50  +    JSON_E_INVALID_KEYWORD,
           51  +    JSON_E_INVALID_ESCAPE_SEQUENCE,
           52  +    JSON_E_INVALID_UNICODE_SEQUENCE,
           53  +    JSON_E_INVALID_NUMBER,
           54  +    JSON_E_NESTING_DEPTH_REACHED,
           55  +    JSON_E_UNBALANCED_COLLECTION,
           56  +    JSON_E_EXPECTED_KEY,
           57  +    JSON_E_EXPECTED_COLON,
           58  +    JSON_E_OUT_OF_MEMORY
           59  +} JSON_error;
           60  +
           61  +typedef enum 
           62  +{
           63  +    JSON_T_NONE = 0,
           64  +    JSON_T_ARRAY_BEGIN,
           65  +    JSON_T_ARRAY_END,
           66  +    JSON_T_OBJECT_BEGIN,
           67  +    JSON_T_OBJECT_END,
           68  +    JSON_T_INTEGER,
           69  +    JSON_T_FLOAT,
           70  +    JSON_T_NULL,
           71  +    JSON_T_TRUE,
           72  +    JSON_T_FALSE,
           73  +    JSON_T_STRING,
           74  +    JSON_T_KEY,
           75  +    JSON_T_MAX
           76  +} JSON_type;
           77  +
           78  +typedef struct JSON_value_struct {
           79  +    union {
           80  +        JSON_int_t integer_value;
           81  +        
           82  +        double float_value;
           83  +        
           84  +        struct {
           85  +            const char* value;
           86  +            size_t length;
           87  +        } str;
           88  +    } vu;
           89  +} JSON_value;
           90  +
           91  +typedef struct JSON_parser_struct* JSON_parser;
           92  +
           93  +/*! \brief JSON parser callback 
           94  +
           95  +    \param ctx The pointer passed to new_JSON_parser.
           96  +    \param type An element of JSON_type but not JSON_T_NONE.    
           97  +    \param value A representation of the parsed value. This parameter is NULL for
           98  +        JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
           99  +        JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
          100  +        as zero-terminated C strings.
          101  +
          102  +    \return Non-zero if parsing should continue, else zero.
          103  +*/    
          104  +typedef int (*JSON_parser_callback)(void* ctx, int type, const struct JSON_value_struct* value);
          105  +
          106  +
          107  +/**
          108  +   A typedef for allocator functions semantically compatible with malloc().
          109  +*/
          110  +typedef void* (*JSON_malloc_t)(size_t n);
          111  +/**
          112  +   A typedef for deallocator functions semantically compatible with free().
          113  +*/
          114  +typedef void (*JSON_free_t)(void* mem);
          115  +
          116  +/*! \brief The structure used to configure a JSON parser object 
          117  +*/
          118  +typedef struct {
          119  +    /** Pointer to a callback, called when the parser has something to tell
          120  +        the user. This parameter may be NULL. In this case the input is
          121  +        merely checked for validity.
          122  +    */
          123  +    JSON_parser_callback    callback;
          124  +    /**
          125  +       Callback context - client-specified data to pass to the
          126  +       callback function. This parameter may be NULL.
          127  +    */
          128  +    void*                   callback_ctx;
          129  +    /** Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting.
          130  +        If negative, the parser can parse arbitrary levels of JSON, otherwise
          131  +        the depth is the limit.
          132  +    */
          133  +    int                     depth;
          134  +    /**
          135  +       To allow C style comments in JSON, set to non-zero.
          136  +    */
          137  +    int                     allow_comments;
          138  +    /**
          139  +       To decode floating point numbers manually set this parameter to
          140  +       non-zero.
          141  +    */
          142  +    int                     handle_floats_manually;
          143  +    /**
          144  +       The memory allocation routine, which must be semantically
          145  +       compatible with malloc(3). If set to NULL, malloc(3) is used.
          146  +
          147  +       If this is set to a non-NULL value then the 'free' member MUST be
          148  +       set to the proper deallocation counterpart for this function.
          149  +       Failure to do so results in undefined behaviour at deallocation
          150  +       time.
          151  +    */
          152  +    JSON_malloc_t       malloc;
          153  +    /**
          154  +       The memory deallocation routine, which must be semantically
          155  +       compatible with free(3). If set to NULL, free(3) is used.
          156  +
          157  +       If this is set to a non-NULL value then the 'alloc' member MUST be
          158  +       set to the proper allocation counterpart for this function.
          159  +       Failure to do so results in undefined behaviour at deallocation
          160  +       time.
          161  +    */
          162  +    JSON_free_t         free;
          163  +} JSON_config;
          164  +
          165  +/*! \brief Initializes the JSON parser configuration structure to default values.
          166  +
          167  +    The default configuration is
          168  +    - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c)
          169  +    - no parsing, just checking for JSON syntax
          170  +    - no comments
          171  +    - Uses realloc() for memory de/allocation.
          172  +
          173  +    \param config. Used to configure the parser.
          174  +*/
          175  +JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
          176  +
          177  +/*! \brief Create a JSON parser object 
          178  +
          179  +    \param config. Used to configure the parser. Set to NULL to use
          180  +        the default configuration. See init_JSON_config.  Its contents are
          181  +        copied by this function, so it need not outlive the returned
          182  +        object.
          183  +    
          184  +    \return The parser object, which is owned by the caller and must eventually
          185  +    be freed by calling delete_JSON_parser().
          186  +*/
          187  +JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
          188  +
          189  +/*! \brief Destroy a previously created JSON parser object. */
          190  +JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc);
          191  +
          192  +/*! \brief Parse a character.
          193  +
          194  +    \return Non-zero, if all characters passed to this function are part of are valid JSON.
          195  +*/
          196  +JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
          197  +
          198  +/*! \brief Finalize parsing.
          199  +
          200  +    Call this method once after all input characters have been consumed.
          201  +    
          202  +    \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
          203  +*/
          204  +JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
          205  +
          206  +/*! \brief Determine if a given string is valid JSON white space 
          207  +
          208  +    \return Non-zero if the string is valid, zero otherwise.
          209  +*/
          210  +JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
          211  +
          212  +/*! \brief Gets the last error that occurred during the use of JSON_parser.
          213  +
          214  +    \return A value from the JSON_error enum.
          215  +*/
          216  +JSON_PARSER_DLL_API int JSON_parser_get_last_error(JSON_parser jc);
          217  +
          218  +/*! \brief Re-sets the parser to prepare it for another parse run.
          219  +
          220  +    \return True (non-zero) on success, 0 on error (e.g. !jc).
          221  +*/
          222  +JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
          223  +
          224  +
          225  +#ifdef __cplusplus
          226  +}
          227  +#endif 
          228  +    
          229  +
          230  +#endif /* JSON_PARSER_H */
          231  +/* end file parser/JSON_parser.h */
          232  +/* begin file parser/JSON_parser.c */
          233  +/*
          234  +Copyright (c) 2005 JSON.org
          235  +
          236  +Permission is hereby granted, free of charge, to any person obtaining a copy
          237  +of this software and associated documentation files (the "Software"), to deal
          238  +in the Software without restriction, including without limitation the rights
          239  +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
          240  +copies of the Software, and to permit persons to whom the Software is
          241  +furnished to do so, subject to the following conditions:
          242  +
          243  +The above copyright notice and this permission notice shall be included in all
          244  +copies or substantial portions of the Software.
          245  +
          246  +The Software shall be used for Good, not Evil.
          247  +
          248  +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
          249  +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
          250  +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
          251  +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
          252  +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
          253  +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
          254  +SOFTWARE.
          255  +*/
          256  +
          257  +/*
          258  +    Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2010.
          259  +    
          260  +    
          261  +    Changelog:
          262  +        2010-11-25
          263  +            Support for custom memory allocation (sgbeal@googlemail.com).
          264  +                        
          265  +        2010-05-07
          266  +            Added error handling for memory allocation failure (sgbeal@googlemail.com). 
          267  +            Added diagnosis errors for invalid JSON.
          268  +            
          269  +        2010-03-25
          270  +            Fixed buffer overrun in grow_parse_buffer & cleaned up code.
          271  +            
          272  +        2009-10-19
          273  +            Replaced long double in JSON_value_struct with double after reports 
          274  +            of strtold being broken on some platforms (charles@transmissionbt.com).
          275  +            
          276  +        2009-05-17 
          277  +            Incorporated benrudiak@googlemail.com fix for UTF16 decoding.
          278  +            
          279  +        2009-05-14 
          280  +            Fixed float parsing bug related to a locale being set that didn't
          281  +            use '.' as decimal point character (charles@transmissionbt.com).
          282  +            
          283  +        2008-10-14 
          284  +            Renamed states.IN to states.IT to avoid name clash which IN macro
          285  +            defined in windef.h (alexey.pelykh@gmail.com)
          286  +            
          287  +        2008-07-19 
          288  +            Removed some duplicate code & debugging variable (charles@transmissionbt.com)
          289  +        
          290  +        2008-05-28 
          291  +            Made JSON_value structure ansi C compliant. This bug was report by 
          292  +            trisk@acm.jhu.edu
          293  +        
          294  +        2008-05-20 
          295  +            Fixed bug reported by charles@transmissionbt.com where the switching 
          296  +            from static to dynamic parse buffer did not copy the static parse 
          297  +            buffer's content.
          298  +*/
          299  +
          300  +
          301  +
          302  +#include <assert.h>
          303  +#include <ctype.h>
          304  +#include <float.h>
          305  +#include <stddef.h>
          306  +#include <stdio.h>
          307  +#include <stdlib.h>
          308  +#include <string.h>
          309  +#include <locale.h>
          310  +
          311  +
          312  +#ifdef _MSC_VER
          313  +#   if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
          314  +#      pragma warning(disable:4996) /* unsecure sscanf */
          315  +#      pragma warning(disable:4127) /* conditional expression is constant */
          316  +#   endif
          317  +#endif
          318  +
          319  +
          320  +#define true  1
          321  +#define false 0
          322  +#define __   -1     /* the universal error code */
          323  +
          324  +/* values chosen so that the object size is approx equal to one page (4K) */
          325  +#ifndef JSON_PARSER_STACK_SIZE
          326  +#   define JSON_PARSER_STACK_SIZE 128
          327  +#endif
          328  +
          329  +#ifndef JSON_PARSER_PARSE_BUFFER_SIZE
          330  +#   define JSON_PARSER_PARSE_BUFFER_SIZE 3500
          331  +#endif
          332  +
          333  +typedef void* (*JSON_debug_malloc_t)(size_t bytes, const char* reason);
          334  +
          335  +#ifdef JSON_PARSER_DEBUG_MALLOC
          336  +#   define JSON_parser_malloc(func, bytes, reason) ((JSON_debug_malloc_t)func)(bytes, reason)
          337  +#else
          338  +#   define JSON_parser_malloc(func, bytes, reason) func(bytes)
          339  +#endif
          340  +
          341  +typedef unsigned short UTF16;
          342  +
          343  +struct JSON_parser_struct {
          344  +    JSON_parser_callback callback;
          345  +    void* ctx;
          346  +    signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually, error;
          347  +    char decimal_point;
          348  +    UTF16 utf16_high_surrogate;
          349  +    int current_char;
          350  +    int depth;
          351  +    int top;
          352  +    int stack_capacity;
          353  +    signed char* stack;
          354  +    char* parse_buffer;
          355  +    size_t parse_buffer_capacity;
          356  +    size_t parse_buffer_count;
          357  +    signed char static_stack[JSON_PARSER_STACK_SIZE];
          358  +    char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE];
          359  +    JSON_malloc_t malloc;
          360  +    JSON_free_t free;
          361  +};
          362  +
          363  +#define COUNTOF(x) (sizeof(x)/sizeof(x[0])) 
          364  +
          365  +/*
          366  +    Characters are mapped into these character classes. This allows for
          367  +    a significant reduction in the size of the state transition table.
          368  +*/
          369  +
          370  +
          371  +
          372  +enum classes {
          373  +    C_SPACE,  /* space */
          374  +    C_WHITE,  /* other whitespace */
          375  +    C_LCURB,  /* {  */
          376  +    C_RCURB,  /* } */
          377  +    C_LSQRB,  /* [ */
          378  +    C_RSQRB,  /* ] */
          379  +    C_COLON,  /* : */
          380  +    C_COMMA,  /* , */
          381  +    C_QUOTE,  /* " */
          382  +    C_BACKS,  /* \ */
          383  +    C_SLASH,  /* / */
          384  +    C_PLUS,   /* + */
          385  +    C_MINUS,  /* - */
          386  +    C_POINT,  /* . */
          387  +    C_ZERO ,  /* 0 */
          388  +    C_DIGIT,  /* 123456789 */
          389  +    C_LOW_A,  /* a */
          390  +    C_LOW_B,  /* b */
          391  +    C_LOW_C,  /* c */
          392  +    C_LOW_D,  /* d */
          393  +    C_LOW_E,  /* e */
          394  +    C_LOW_F,  /* f */
          395  +    C_LOW_L,  /* l */
          396  +    C_LOW_N,  /* n */
          397  +    C_LOW_R,  /* r */
          398  +    C_LOW_S,  /* s */
          399  +    C_LOW_T,  /* t */
          400  +    C_LOW_U,  /* u */
          401  +    C_ABCDF,  /* ABCDF */
          402  +    C_E,      /* E */
          403  +    C_ETC,    /* everything else */
          404  +    C_STAR,   /* * */   
          405  +    NR_CLASSES
          406  +};
          407  +
          408  +static const signed char ascii_class[128] = {
          409  +/*
          410  +    This array maps the 128 ASCII characters into character classes.
          411  +    The remaining Unicode characters should be mapped to C_ETC.
          412  +    Non-whitespace control characters are errors.
          413  +*/
          414  +    __,      __,      __,      __,      __,      __,      __,      __,
          415  +    __,      C_WHITE, C_WHITE, __,      __,      C_WHITE, __,      __,
          416  +    __,      __,      __,      __,      __,      __,      __,      __,
          417  +    __,      __,      __,      __,      __,      __,      __,      __,
          418  +
          419  +    C_SPACE, C_ETC,   C_QUOTE, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
          420  +    C_ETC,   C_ETC,   C_STAR,   C_PLUS,  C_COMMA, C_MINUS, C_POINT, C_SLASH,
          421  +    C_ZERO,  C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
          422  +    C_DIGIT, C_DIGIT, C_COLON, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
          423  +
          424  +    C_ETC,   C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E,     C_ABCDF, C_ETC,
          425  +    C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
          426  +    C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
          427  +    C_ETC,   C_ETC,   C_ETC,   C_LSQRB, C_BACKS, C_RSQRB, C_ETC,   C_ETC,
          428  +
          429  +    C_ETC,   C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
          430  +    C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_LOW_L, C_ETC,   C_LOW_N, C_ETC,
          431  +    C_ETC,   C_ETC,   C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC,   C_ETC,
          432  +    C_ETC,   C_ETC,   C_ETC,   C_LCURB, C_ETC,   C_RCURB, C_ETC,   C_ETC
          433  +};
          434  +
          435  +
          436  +/*
          437  +    The state codes.
          438  +*/
          439  +enum states {
          440  +    GO,  /* start    */
          441  +    OK,  /* ok       */
          442  +    OB,  /* object   */
          443  +    KE,  /* key      */
          444  +    CO,  /* colon    */
          445  +    VA,  /* value    */
          446  +    AR,  /* array    */
          447  +    ST,  /* string   */
          448  +    ES,  /* escape   */
          449  +    U1,  /* u1       */
          450  +    U2,  /* u2       */
          451  +    U3,  /* u3       */
          452  +    U4,  /* u4       */
          453  +    MI,  /* minus    */
          454  +    ZE,  /* zero     */
          455  +    IT,  /* integer  */
          456  +    FR,  /* fraction */
          457  +    E1,  /* e        */
          458  +    E2,  /* ex       */
          459  +    E3,  /* exp      */
          460  +    T1,  /* tr       */
          461  +    T2,  /* tru      */
          462  +    T3,  /* true     */
          463  +    F1,  /* fa       */
          464  +    F2,  /* fal      */
          465  +    F3,  /* fals     */
          466  +    F4,  /* false    */
          467  +    N1,  /* nu       */
          468  +    N2,  /* nul      */
          469  +    N3,  /* null     */
          470  +    C1,  /* /        */
          471  +    C2,  /* / *     */
          472  +    C3,  /* *        */
          473  +    FX,  /* *.* *eE* */
          474  +    D1,  /* second UTF-16 character decoding started by \ */
          475  +    D2,  /* second UTF-16 character proceeded by u */
          476  +    NR_STATES
          477  +};
          478  +
          479  +enum actions
          480  +{
          481  +    CB = -10, /* comment begin */
          482  +    CE = -11, /* comment end */
          483  +    FA = -12, /* false */
          484  +    TR = -13, /* false */
          485  +    NU = -14, /* null */
          486  +    DE = -15, /* double detected by exponent e E */
          487  +    DF = -16, /* double detected by fraction . */
          488  +    SB = -17, /* string begin */
          489  +    MX = -18, /* integer detected by minus */
          490  +    ZX = -19, /* integer detected by zero */
          491  +    IX = -20, /* integer detected by 1-9 */
          492  +    EX = -21, /* next char is escaped */
          493  +    UC = -22  /* Unicode character read */
          494  +};
          495  +
          496  +
          497  +static const signed char state_transition_table[NR_STATES][NR_CLASSES] = {
          498  +/*
          499  +    The state transition table takes the current state and the current symbol,
          500  +    and returns either a new state or an action. An action is represented as a
          501  +    negative number. A JSON text is accepted if at the end of the text the
          502  +    state is OK and if the mode is MODE_DONE.
          503  +
          504  +                 white                                      1-9                                   ABCDF  etc
          505  +             space |  {  }  [  ]  :  ,  "  \  /  +  -  .  0  |  a  b  c  d  e  f  l  n  r  s  t  u  |  E  |  * */
          506  +/*start  GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          507  +/*ok     OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          508  +/*object OB*/ {OB,OB,__,-9,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          509  +/*key    KE*/ {KE,KE,__,__,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          510  +/*colon  CO*/ {CO,CO,__,__,__,__,-2,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          511  +/*value  VA*/ {VA,VA,-6,__,-5,__,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
          512  +/*array  AR*/ {AR,AR,-6,__,-5,-7,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
          513  +/*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
          514  +/*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__},
          515  +/*u1     U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__},
          516  +/*u2     U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__},
          517  +/*u3     U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__},
          518  +/*u4     U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__},
          519  +/*minus  MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IT,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          520  +/*zero   ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          521  +/*int    IT*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,IT,IT,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__},
          522  +/*frac   FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
          523  +/*e      E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          524  +/*ex     E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          525  +/*exp    E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          526  +/*tr     T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__},
          527  +/*tru    T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__},
          528  +/*true   T3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
          529  +/*fa     F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          530  +/*fal    F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__},
          531  +/*fals   F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__},
          532  +/*false  F4*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
          533  +/*nu     N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__},
          534  +/*nul    N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__},
          535  +/*null   N3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__},
          536  +/*/      C1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,C2},
          537  +/*/*     C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
          538  +/**      C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
          539  +/*_.     FX*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
          540  +/*\      D1*/ {__,__,__,__,__,__,__,__,__,D2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
          541  +/*\      D2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U1,__,__,__,__},
          542  +};
          543  +
          544  +
          545  +/*
          546  +    These modes can be pushed on the stack.
          547  +*/
          548  +enum modes {
          549  +    MODE_ARRAY = 1, 
          550  +    MODE_DONE = 2,  
          551  +    MODE_KEY = 3,   
          552  +    MODE_OBJECT = 4
          553  +};
          554  +
          555  +static void set_error(JSON_parser jc)
          556  +{
          557  +    switch (jc->state) {
          558  +        case GO:
          559  +            switch (jc->current_char) {
          560  +            case '{': case '}': case '[': case ']': 
          561  +                jc->error = JSON_E_UNBALANCED_COLLECTION;
          562  +                break;
          563  +            default:
          564  +                jc->error = JSON_E_INVALID_CHAR;
          565  +                break;    
          566  +            }
          567  +            break;
          568  +        case OB:
          569  +            jc->error = JSON_E_EXPECTED_KEY;
          570  +            break;
          571  +        case AR:
          572  +            jc->error = JSON_E_UNBALANCED_COLLECTION;
          573  +            break;
          574  +        case CO:
          575  +            jc->error = JSON_E_EXPECTED_COLON;
          576  +            break;
          577  +        case KE:
          578  +            jc->error = JSON_E_EXPECTED_KEY;
          579  +            break;
          580  +        /* \uXXXX\uYYYY */
          581  +        case U1: case U2: case U3: case U4: case D1: case D2:
          582  +            jc->error = JSON_E_INVALID_UNICODE_SEQUENCE;
          583  +            break;
          584  +        /* true, false, null */
          585  +        case T1: case T2: case T3: case F1: case F2: case F3: case F4: case N1: case N2: case N3:
          586  +            jc->error = JSON_E_INVALID_KEYWORD;
          587  +            break;
          588  +        /* minus, integer, fraction, exponent */
          589  +        case MI: case ZE: case IT: case FR: case E1: case E2: case E3:
          590  +            jc->error = JSON_E_INVALID_NUMBER;
          591  +            break;
          592  +        default:
          593  +            jc->error = JSON_E_INVALID_CHAR;
          594  +            break;
          595  +    }
          596  +}
          597  +
          598  +static int
          599  +push(JSON_parser jc, int mode)
          600  +{
          601  +/*
          602  +    Push a mode onto the stack. Return false if there is overflow.
          603  +*/
          604  +    assert(jc->top <= jc->stack_capacity);
          605  +    
          606  +    if (jc->depth < 0) {
          607  +        if (jc->top == jc->stack_capacity) {
          608  +            const size_t bytes_to_copy = jc->stack_capacity * sizeof(jc->stack[0]);
          609  +            const size_t new_capacity = jc->stack_capacity * 2;
          610  +            const size_t bytes_to_allocate = new_capacity * sizeof(jc->stack[0]);
          611  +            void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "stack");
          612  +            if (!mem) {
          613  +                jc->error = JSON_E_OUT_OF_MEMORY;
          614  +                return false;
          615  +            }
          616  +            jc->stack_capacity = (int)new_capacity;
          617  +            memcpy(mem, jc->stack, bytes_to_copy);
          618  +            if (jc->stack != &jc->static_stack[0]) {
          619  +                jc->free(jc->stack);
          620  +            }
          621  +            jc->stack = (signed char*)mem;
          622  +        }
          623  +    } else {
          624  +        if (jc->top == jc->depth) {
          625  +            jc->error = JSON_E_NESTING_DEPTH_REACHED;
          626  +            return false;
          627  +        }
          628  +    }
          629  +    jc->stack[++jc->top] = (signed char)mode;
          630  +    return true;
          631  +}
          632  +
          633  +
          634  +static int
          635  +pop(JSON_parser jc, int mode)
          636  +{
          637  +/*
          638  +    Pop the stack, assuring that the current mode matches the expectation.
          639  +    Return false if there is underflow or if the modes mismatch.
          640  +*/
          641  +    if (jc->top < 0 || jc->stack[jc->top] != mode) {
          642  +        return false;
          643  +    }
          644  +    jc->top -= 1;
          645  +    return true;
          646  +}
          647  +
          648  +
          649  +#define parse_buffer_clear(jc) \
          650  +    do {\
          651  +        jc->parse_buffer_count = 0;\
          652  +        jc->parse_buffer[0] = 0;\
          653  +    } while (0)
          654  +    
          655  +#define parse_buffer_pop_back_char(jc)\
          656  +    do {\
          657  +        assert(jc->parse_buffer_count >= 1);\
          658  +        --jc->parse_buffer_count;\
          659  +        jc->parse_buffer[jc->parse_buffer_count] = 0;\
          660  +    } while (0)    
          661  +
          662  +
          663  +
          664  +void delete_JSON_parser(JSON_parser jc)
          665  +{
          666  +    if (jc) {
          667  +        if (jc->stack != &jc->static_stack[0]) {
          668  +            jc->free((void*)jc->stack);
          669  +        }
          670  +        if (jc->parse_buffer != &jc->static_parse_buffer[0]) {
          671  +            jc->free((void*)jc->parse_buffer);
          672  +        }
          673  +        jc->free((void*)jc);
          674  +     }   
          675  +}
          676  +
          677  +int JSON_parser_reset(JSON_parser jc)
          678  +{
          679  +    if (NULL == jc) {
          680  +        return false;
          681  +    }
          682  +    
          683  +    jc->state = GO;
          684  +    jc->top = -1;
          685  +
          686  +    /* parser has been used previously? */
          687  +    if (NULL == jc->parse_buffer) {
          688  +    
          689  +        /* Do we want non-bound stack? */
          690  +        if (jc->depth > 0) {
          691  +            jc->stack_capacity = jc->depth;
          692  +            if (jc->depth <= (int)COUNTOF(jc->static_stack)) {
          693  +                jc->stack = &jc->static_stack[0];
          694  +            } else {
          695  +                const size_t bytes_to_alloc = jc->stack_capacity * sizeof(jc->stack[0]);
          696  +                jc->stack = (signed char*)JSON_parser_malloc(jc->malloc, bytes_to_alloc, "stack");
          697  +                if (jc->stack == NULL) {
          698  +                    return false;
          699  +                }
          700  +            }
          701  +        } else {
          702  +            jc->stack_capacity = (int)COUNTOF(jc->static_stack);
          703  +            jc->depth = -1;
          704  +            jc->stack = &jc->static_stack[0];
          705  +        }
          706  +        
          707  +        /* set up the parse buffer */
          708  +        jc->parse_buffer = &jc->static_parse_buffer[0];
          709  +        jc->parse_buffer_capacity = COUNTOF(jc->static_parse_buffer);
          710  +    }
          711  +    
          712  +    /* set parser to start */
          713  +    push(jc, MODE_DONE);
          714  +    parse_buffer_clear(jc);
          715  +    
          716  +    return true;
          717  +}
          718  +
          719  +JSON_parser
          720  +new_JSON_parser(JSON_config const * config)
          721  +{
          722  +/*
          723  +    new_JSON_parser starts the checking process by constructing a JSON_parser
          724  +    object. It takes a depth parameter that restricts the level of maximum
          725  +    nesting.
          726  +
          727  +    To continue the process, call JSON_parser_char for each character in the
          728  +    JSON text, and then call JSON_parser_done to obtain the final result.
          729  +    These functions are fully reentrant.
          730  +*/
          731  +
          732  +    int use_std_malloc = false;
          733  +    JSON_config default_config;
          734  +    JSON_parser jc;
          735  +    JSON_malloc_t alloc;
          736  +    
          737  +    /* set to default configuration if none was provided */
          738  +    if (NULL == config) {
          739  +        /* initialize configuration */
          740  +        init_JSON_config(&default_config);
          741  +        config = &default_config;
          742  +    }
          743  +    
          744  +    /* use std malloc if either the allocator or deallocator function isn't set */
          745  +    use_std_malloc = NULL == config->malloc || NULL == config->free;
          746  +    
          747  +    alloc = use_std_malloc ? malloc : config->malloc;
          748  +    
          749  +    jc = JSON_parser_malloc(alloc, sizeof(*jc), "parser");    
          750  +    
          751  +    if (NULL == jc) {
          752  +        return NULL;
          753  +    }
          754  +    
          755  +    /* configure the parser */
          756  +    memset(jc, 0, sizeof(*jc));
          757  +    jc->malloc = alloc;
          758  +    jc->free = use_std_malloc ? free : config->free;
          759  +    jc->callback = config->callback;
          760  +    jc->ctx = config->callback_ctx;
          761  +    jc->allow_comments = (signed char)(config->allow_comments != 0);
          762  +    jc->handle_floats_manually = (signed char)(config->handle_floats_manually != 0);
          763  +    jc->decimal_point = *localeconv()->decimal_point;
          764  +    /* We need to be able to push at least one object */
          765  +    jc->depth = config->depth == 0 ? 1 : config->depth;
          766  +    
          767  +    /* reset the parser */
          768  +    if (!JSON_parser_reset(jc)) {
          769  +        jc->free(jc);
          770  +        return NULL;
          771  +    }
          772  +    
          773  +    return jc;
          774  +}
          775  +
          776  +static int parse_buffer_grow(JSON_parser jc)
          777  +{
          778  +    const size_t bytes_to_copy = jc->parse_buffer_count * sizeof(jc->parse_buffer[0]);
          779  +    const size_t new_capacity = jc->parse_buffer_capacity * 2;
          780  +    const size_t bytes_to_allocate = new_capacity * sizeof(jc->parse_buffer[0]);
          781  +    void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "parse buffer");
          782  +    
          783  +    if (mem == NULL) {
          784  +        jc->error = JSON_E_OUT_OF_MEMORY;
          785  +        return false;
          786  +    }
          787  +    
          788  +    assert(new_capacity > 0);
          789  +    memcpy(mem, jc->parse_buffer, bytes_to_copy);
          790  +    
          791  +    if (jc->parse_buffer != &jc->static_parse_buffer[0]) {
          792  +        jc->free(jc->parse_buffer);
          793  +    }
          794  +    
          795  +    jc->parse_buffer = (char*)mem;
          796  +    jc->parse_buffer_capacity = new_capacity;
          797  +    
          798  +    return true;
          799  +}
          800  +
          801  +static int parse_buffer_reserve_for(JSON_parser jc, unsigned chars)
          802  +{
          803  +    while (jc->parse_buffer_count + chars + 1 > jc->parse_buffer_capacity) {
          804  +        if (!parse_buffer_grow(jc)) {
          805  +            assert(jc->error == JSON_E_OUT_OF_MEMORY);
          806  +            return false;
          807  +        }
          808  +    }
          809  +    
          810  +    return true;
          811  +}
          812  +
          813  +#define parse_buffer_has_space_for(jc, count) \
          814  +    (jc->parse_buffer_count + (count) + 1 <= jc->parse_buffer_capacity)
          815  +
          816  +#define parse_buffer_push_back_char(jc, c)\
          817  +    do {\
          818  +        assert(parse_buffer_has_space_for(jc, 1)); \
          819  +        jc->parse_buffer[jc->parse_buffer_count++] = c;\
          820  +        jc->parse_buffer[jc->parse_buffer_count]   = 0;\
          821  +    } while (0)
          822  +
          823  +#define assert_is_non_container_type(jc) \
          824  +    assert( \
          825  +        jc->type == JSON_T_NULL || \
          826  +        jc->type == JSON_T_FALSE || \
          827  +        jc->type == JSON_T_TRUE || \
          828  +        jc->type == JSON_T_FLOAT || \
          829  +        jc->type == JSON_T_INTEGER || \
          830  +        jc->type == JSON_T_STRING)
          831  +    
          832  +
          833  +static int parse_parse_buffer(JSON_parser jc)
          834  +{
          835  +    if (jc->callback) {
          836  +        JSON_value value, *arg = NULL;
          837  +        
          838  +        if (jc->type != JSON_T_NONE) {
          839  +            assert_is_non_container_type(jc);
          840  +        
          841  +            switch(jc->type) {
          842  +                case JSON_T_FLOAT:
          843  +                    arg = &value;
          844  +                    if (jc->handle_floats_manually) {
          845  +                        value.vu.str.value = jc->parse_buffer;
          846  +                        value.vu.str.length = jc->parse_buffer_count;
          847  +                    } else { 
          848  +                        /* not checking with end pointer b/c there may be trailing ws */
          849  +                        value.vu.float_value = strtod(jc->parse_buffer, NULL);
          850  +                    }
          851  +                    break;
          852  +                case JSON_T_INTEGER:
          853  +                    arg = &value;
          854  +                    sscanf(jc->parse_buffer, JSON_PARSER_INTEGER_SSCANF_TOKEN, &value.vu.integer_value);
          855  +                    break;
          856  +                case JSON_T_STRING:
          857  +                    arg = &value;
          858  +                    value.vu.str.value = jc->parse_buffer;
          859  +                    value.vu.str.length = jc->parse_buffer_count;
          860  +                    break;
          861  +            }
          862  +            
          863  +            if (!(*jc->callback)(jc->ctx, jc->type, arg)) {
          864  +                return false;
          865  +            }
          866  +        }
          867  +    }
          868  +    
          869  +    parse_buffer_clear(jc);
          870  +    
          871  +    return true;
          872  +}
          873  +
          874  +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800)
          875  +#define IS_LOW_SURROGATE(uc)  (((uc) & 0xFC00) == 0xDC00)
          876  +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000)
          877  +static const unsigned char utf8_lead_bits[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
          878  +
          879  +static int decode_unicode_char(JSON_parser jc)
          880  +{
          881  +    int i;
          882  +    unsigned uc = 0;
          883  +    char* p;
          884  +    int trail_bytes;
          885  +    
          886  +    assert(jc->parse_buffer_count >= 6);
          887  +    
          888  +    p = &jc->parse_buffer[jc->parse_buffer_count - 4];
          889  +    
          890  +    for (i = 12; i >= 0; i -= 4, ++p) {
          891  +        unsigned x = *p;
          892  +        
          893  +        if (x >= 'a') {
          894  +            x -= ('a' - 10);
          895  +        } else if (x >= 'A') {
          896  +            x -= ('A' - 10);
          897  +        } else {
          898  +            x &= ~0x30u;
          899  +        }
          900  +        
          901  +        assert(x < 16);
          902  +        
          903  +        uc |= x << i;
          904  +    }
          905  +    
          906  +    /* clear UTF-16 char from buffer */
          907  +    jc->parse_buffer_count -= 6;
          908  +    jc->parse_buffer[jc->parse_buffer_count] = 0;
          909  +    
          910  +    /* attempt decoding ... */
          911  +    if (jc->utf16_high_surrogate) {
          912  +        if (IS_LOW_SURROGATE(uc)) {
          913  +            uc = DECODE_SURROGATE_PAIR(jc->utf16_high_surrogate, uc);
          914  +            trail_bytes = 3;
          915  +            jc->utf16_high_surrogate = 0;
          916  +        } else {
          917  +            /* high surrogate without a following low surrogate */
          918  +            return false;
          919  +        }
          920  +    } else {
          921  +        if (uc < 0x80) {
          922  +            trail_bytes = 0;
          923  +        } else if (uc < 0x800) {
          924  +            trail_bytes = 1;
          925  +        } else if (IS_HIGH_SURROGATE(uc)) {
          926  +            /* save the high surrogate and wait for the low surrogate */
          927  +            jc->utf16_high_surrogate = (UTF16)uc;
          928  +            return true;
          929  +        } else if (IS_LOW_SURROGATE(uc)) {
          930  +            /* low surrogate without a preceding high surrogate */
          931  +            return false;
          932  +        } else {
          933  +            trail_bytes = 2;
          934  +        }
          935  +    }
          936  +    
          937  +    jc->parse_buffer[jc->parse_buffer_count++] = (char) ((uc >> (trail_bytes * 6)) | utf8_lead_bits[trail_bytes]);
          938  +    
          939  +    for (i = trail_bytes * 6 - 6; i >= 0; i -= 6) {
          940  +        jc->parse_buffer[jc->parse_buffer_count++] = (char) (((uc >> i) & 0x3F) | 0x80);
          941  +    }
          942  +
          943  +    jc->parse_buffer[jc->parse_buffer_count] = 0;
          944  +    
          945  +    return true;
          946  +}
          947  +
          948  +static int add_escaped_char_to_parse_buffer(JSON_parser jc, int next_char)
          949  +{
          950  +    assert(parse_buffer_has_space_for(jc, 1));
          951  +    
          952  +    jc->escaped = 0;
          953  +    /* remove the backslash */
          954  +    parse_buffer_pop_back_char(jc);
          955  +    switch(next_char) {
          956  +        case 'b':
          957  +            parse_buffer_push_back_char(jc, '\b');
          958  +            break;
          959  +        case 'f':
          960  +            parse_buffer_push_back_char(jc, '\f');
          961  +            break;
          962  +        case 'n':
          963  +            parse_buffer_push_back_char(jc, '\n');
          964  +            break;
          965  +        case 'r':
          966  +            parse_buffer_push_back_char(jc, '\r');
          967  +            break;
          968  +        case 't':
          969  +            parse_buffer_push_back_char(jc, '\t');
          970  +            break;
          971  +        case '"':
          972  +            parse_buffer_push_back_char(jc, '"');
          973  +            break;
          974  +        case '\\':
          975  +            parse_buffer_push_back_char(jc, '\\');
          976  +            break;
          977  +        case '/':
          978  +            parse_buffer_push_back_char(jc, '/');
          979  +            break;
          980  +        case 'u':
          981  +            parse_buffer_push_back_char(jc, '\\');
          982  +            parse_buffer_push_back_char(jc, 'u');
          983  +            break;
          984  +        default:
          985  +            return false;
          986  +    }
          987  +
          988  +    return true;
          989  +}
          990  +
          991  +static int add_char_to_parse_buffer(JSON_parser jc, int next_char, int next_class)
          992  +{
          993  +    if (!parse_buffer_reserve_for(jc, 1)) {
          994  +        assert(JSON_E_OUT_OF_MEMORY == jc->error);
          995  +        return false;
          996  +    }
          997  +    
          998  +    if (jc->escaped) {
          999  +        if (!add_escaped_char_to_parse_buffer(jc, next_char)) {
         1000  +            jc->error = JSON_E_INVALID_ESCAPE_SEQUENCE;
         1001  +            return false; 
         1002  +        }
         1003  +    } else if (!jc->comment) {
         1004  +        if ((jc->type != JSON_T_NONE) | !((next_class == C_SPACE) | (next_class == C_WHITE)) /* non-white-space */) {
         1005  +            parse_buffer_push_back_char(jc, (char)next_char);
         1006  +        }
         1007  +    }
         1008  +    
         1009  +    return true;
         1010  +}
         1011  +
         1012  +#define assert_type_isnt_string_null_or_bool(jc) \
         1013  +    assert(jc->type != JSON_T_FALSE); \
         1014  +    assert(jc->type != JSON_T_TRUE); \
         1015  +    assert(jc->type != JSON_T_NULL); \
         1016  +    assert(jc->type != JSON_T_STRING)
         1017  +
         1018  +
         1019  +int
         1020  +JSON_parser_char(JSON_parser jc, int next_char)
         1021  +{
         1022  +/*
         1023  +    After calling new_JSON_parser, call this function for each character (or
         1024  +    partial character) in your JSON text. It can accept UTF-8, UTF-16, or
         1025  +    UTF-32. It returns true if things are looking ok so far. If it rejects the
         1026  +    text, it returns false.
         1027  +*/
         1028  +    int next_class, next_state;
         1029  +
         1030  +/*
         1031  +    Store the current char for error handling
         1032  +*/    
         1033  +    jc->current_char = next_char;
         1034  +    
         1035  +/*
         1036  +    Determine the character's class.
         1037  +*/
         1038  +    if (next_char < 0) {
         1039  +        jc->error = JSON_E_INVALID_CHAR;
         1040  +        return false;
         1041  +    }
         1042  +    if (next_char >= 128) {
         1043  +        next_class = C_ETC;
         1044  +    } else {
         1045  +        next_class = ascii_class[next_char];
         1046  +        if (next_class <= __) {
         1047  +            set_error(jc);
         1048  +            return false;
         1049  +        }
         1050  +    }
         1051  +    
         1052  +    if (!add_char_to_parse_buffer(jc, next_char, next_class)) {
         1053  +        return false;
         1054  +    }
         1055  +    
         1056  +/*
         1057  +    Get the next state from the state transition table.
         1058  +*/
         1059  +    next_state = state_transition_table[jc->state][next_class];
         1060  +    if (next_state >= 0) {
         1061  +/*
         1062  +    Change the state.
         1063  +*/
         1064  +        jc->state = (signed char)next_state;
         1065  +    } else {
         1066  +/*
         1067  +    Or perform one of the actions.
         1068  +*/
         1069  +        switch (next_state) {
         1070  +/* Unicode character */        
         1071  +        case UC:
         1072  +            if(!decode_unicode_char(jc)) {
         1073  +                jc->error = JSON_E_INVALID_UNICODE_SEQUENCE;
         1074  +                return false;
         1075  +            }
         1076  +            /* check if we need to read a second UTF-16 char */
         1077  +            if (jc->utf16_high_surrogate) {
         1078  +                jc->state = D1;
         1079  +            } else {
         1080  +                jc->state = ST;
         1081  +            }
         1082  +            break;
         1083  +/* escaped char */
         1084  +        case EX:
         1085  +            jc->escaped = 1;
         1086  +            jc->state = ES;
         1087  +            break;
         1088  +/* integer detected by minus */
         1089  +        case MX:
         1090  +            jc->type = JSON_T_INTEGER;
         1091  +            jc->state = MI;
         1092  +            break;  
         1093  +/* integer detected by zero */            
         1094  +        case ZX:
         1095  +            jc->type = JSON_T_INTEGER;
         1096  +            jc->state = ZE;
         1097  +            break;  
         1098  +/* integer detected by 1-9 */            
         1099  +        case IX:
         1100  +            jc->type = JSON_T_INTEGER;
         1101  +            jc->state = IT;
         1102  +            break;  
         1103  +            
         1104  +/* floating point number detected by exponent*/
         1105  +        case DE:
         1106  +            assert_type_isnt_string_null_or_bool(jc);
         1107  +            jc->type = JSON_T_FLOAT;
         1108  +            jc->state = E1;
         1109  +            break;   
         1110  +        
         1111  +/* floating point number detected by fraction */
         1112  +        case DF:
         1113  +            assert_type_isnt_string_null_or_bool(jc);
         1114  +            if (!jc->handle_floats_manually) {
         1115  +/*
         1116  +    Some versions of strtod (which underlies sscanf) don't support converting 
         1117  +    C-locale formated floating point values.
         1118  +*/           
         1119  +                assert(jc->parse_buffer[jc->parse_buffer_count-1] == '.');
         1120  +                jc->parse_buffer[jc->parse_buffer_count-1] = jc->decimal_point;
         1121  +            }            
         1122  +            jc->type = JSON_T_FLOAT;
         1123  +            jc->state = FX;
         1124  +            break;   
         1125  +/* string begin " */
         1126  +        case SB:
         1127  +            parse_buffer_clear(jc);
         1128  +            assert(jc->type == JSON_T_NONE);
         1129  +            jc->type = JSON_T_STRING;
         1130  +            jc->state = ST;
         1131  +            break;        
         1132  +        
         1133  +/* n */
         1134  +        case NU:
         1135  +            assert(jc->type == JSON_T_NONE);
         1136  +            jc->type = JSON_T_NULL;
         1137  +            jc->state = N1;
         1138  +            break;        
         1139  +/* f */
         1140  +        case FA:
         1141  +            assert(jc->type == JSON_T_NONE);
         1142  +            jc->type = JSON_T_FALSE;
         1143  +            jc->state = F1;
         1144  +            break;        
         1145  +/* t */
         1146  +        case TR:
         1147  +            assert(jc->type == JSON_T_NONE);
         1148  +            jc->type = JSON_T_TRUE;
         1149  +            jc->state = T1;
         1150  +            break;        
         1151  +        
         1152  +/* closing comment */
         1153  +        case CE:
         1154  +            jc->comment = 0;
         1155  +            assert(jc->parse_buffer_count == 0);
         1156  +            assert(jc->type == JSON_T_NONE);
         1157  +            jc->state = jc->before_comment_state;
         1158  +            break;        
         1159  +        
         1160  +/* opening comment  */
         1161  +        case CB:
         1162  +            if (!jc->allow_comments) {
         1163  +                return false;
         1164  +            }
         1165  +            parse_buffer_pop_back_char(jc);
         1166  +            if (!parse_parse_buffer(jc)) {
         1167  +                return false;
         1168  +            }
         1169  +            assert(jc->parse_buffer_count == 0);
         1170  +            assert(jc->type != JSON_T_STRING);
         1171  +            switch (jc->stack[jc->top]) {
         1172  +            case MODE_ARRAY:
         1173  +            case MODE_OBJECT:   
         1174  +                switch(jc->state) {
         1175  +                case VA:
         1176  +                case AR:
         1177  +                    jc->before_comment_state = jc->state;
         1178  +                    break;
         1179  +                default:
         1180  +                    jc->before_comment_state = OK;
         1181  +                    break;
         1182  +                }
         1183  +                break;
         1184  +            default:
         1185  +                jc->before_comment_state = jc->state;
         1186  +                break;
         1187  +            }
         1188  +            jc->type = JSON_T_NONE;
         1189  +            jc->state = C1;
         1190  +            jc->comment = 1;
         1191  +            break;
         1192  +/* empty } */
         1193  +        case -9:        
         1194  +            parse_buffer_clear(jc);
         1195  +            if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) {
         1196  +                return false;
         1197  +            }
         1198  +            if (!pop(jc, MODE_KEY)) {
         1199  +                return false;
         1200  +            }
         1201  +            jc->state = OK;
         1202  +            break;
         1203  +
         1204  +/* } */ case -8:
         1205  +            parse_buffer_pop_back_char(jc);
         1206  +            if (!parse_parse_buffer(jc)) {
         1207  +                return false;
         1208  +            }
         1209  +            if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) {
         1210  +                return false;
         1211  +            }
         1212  +            if (!pop(jc, MODE_OBJECT)) {
         1213  +                jc->error = JSON_E_UNBALANCED_COLLECTION;
         1214  +                return false;
         1215  +            }
         1216  +            jc->type = JSON_T_NONE;
         1217  +            jc->state = OK;
         1218  +            break;
         1219  +
         1220  +/* ] */ case -7:
         1221  +            parse_buffer_pop_back_char(jc);
         1222  +            if (!parse_parse_buffer(jc)) {
         1223  +                return false;
         1224  +            }
         1225  +            if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_END, NULL)) {
         1226  +                return false;
         1227  +            }
         1228  +            if (!pop(jc, MODE_ARRAY)) {
         1229  +                jc->error = JSON_E_UNBALANCED_COLLECTION;
         1230  +                return false;
         1231  +            }
         1232  +            
         1233  +            jc->type = JSON_T_NONE;
         1234  +            jc->state = OK;
         1235  +            break;
         1236  +
         1237  +/* { */ case -6:
         1238  +            parse_buffer_pop_back_char(jc);
         1239  +            if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_BEGIN, NULL)) {
         1240  +                return false;
         1241  +            }
         1242  +            if (!push(jc, MODE_KEY)) {
         1243  +                return false;
         1244  +            }
         1245  +            assert(jc->type == JSON_T_NONE);
         1246  +            jc->state = OB;
         1247  +            break;
         1248  +
         1249  +/* [ */ case -5:
         1250  +            parse_buffer_pop_back_char(jc);
         1251  +            if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_BEGIN, NULL)) {
         1252  +                return false;
         1253  +            }
         1254  +            if (!push(jc, MODE_ARRAY)) {
         1255  +                return false;
         1256  +            }
         1257  +            assert(jc->type == JSON_T_NONE);
         1258  +            jc->state = AR;
         1259  +            break;
         1260  +
         1261  +/* string end " */ case -4:
         1262  +            parse_buffer_pop_back_char(jc);
         1263  +            switch (jc->stack[jc->top]) {
         1264  +            case MODE_KEY:
         1265  +                assert(jc->type == JSON_T_STRING);
         1266  +                jc->type = JSON_T_NONE;
         1267  +                jc->state = CO;
         1268  +                
         1269  +                if (jc->callback) {
         1270  +                    JSON_value value;
         1271  +                    value.vu.str.value = jc->parse_buffer;
         1272  +                    value.vu.str.length = jc->parse_buffer_count;
         1273  +                    if (!(*jc->callback)(jc->ctx, JSON_T_KEY, &value)) {
         1274  +                        return false;
         1275  +                    }
         1276  +                }
         1277  +                parse_buffer_clear(jc);
         1278  +                break;
         1279  +            case MODE_ARRAY:
         1280  +            case MODE_OBJECT:
         1281  +                assert(jc->type == JSON_T_STRING);
         1282  +                if (!parse_parse_buffer(jc)) {
         1283  +                    return false;
         1284  +                }
         1285  +                jc->type = JSON_T_NONE;
         1286  +                jc->state = OK;
         1287  +                break;
         1288  +            default:
         1289  +                return false;
         1290  +            }
         1291  +            break;
         1292  +
         1293  +/* , */ case -3:
         1294  +            parse_buffer_pop_back_char(jc);
         1295  +            if (!parse_parse_buffer(jc)) {
         1296  +                return false;
         1297  +            }
         1298  +            switch (jc->stack[jc->top]) {
         1299  +            case MODE_OBJECT:
         1300  +/*
         1301  +    A comma causes a flip from object mode to key mode.
         1302  +*/
         1303  +                if (!pop(jc, MODE_OBJECT) || !push(jc, MODE_KEY)) {
         1304  +                    return false;
         1305  +                }
         1306  +                assert(jc->type != JSON_T_STRING);
         1307  +                jc->type = JSON_T_NONE;
         1308  +                jc->state = KE;
         1309  +                break;
         1310  +            case MODE_ARRAY:
         1311  +                assert(jc->type != JSON_T_STRING);
         1312  +                jc->type = JSON_T_NONE;
         1313  +                jc->state = VA;
         1314  +                break;
         1315  +            default:
         1316  +                return false;
         1317  +            }
         1318  +            break;
         1319  +
         1320  +/* : */ case -2:
         1321  +/*
         1322  +    A colon causes a flip from key mode to object mode.
         1323  +*/
         1324  +            parse_buffer_pop_back_char(jc);
         1325  +            if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) {
         1326  +                return false;
         1327  +            }
         1328  +            assert(jc->type == JSON_T_NONE);
         1329  +            jc->state = VA;
         1330  +            break;
         1331  +/*
         1332  +    Bad action.
         1333  +*/
         1334  +        default:
         1335  +            set_error(jc);
         1336  +            return false;
         1337  +        }
         1338  +    }
         1339  +    return true;
         1340  +}
         1341  +
         1342  +int
         1343  +JSON_parser_done(JSON_parser jc)
         1344  +{
         1345  +    if ((jc->state == OK || jc->state == GO) && pop(jc, MODE_DONE))
         1346  +    {
         1347  +        return true;
         1348  +    }
         1349  +
         1350  +    jc->error = JSON_E_UNBALANCED_COLLECTION;
         1351  +    return false;
         1352  +}
         1353  +
         1354  +
         1355  +int JSON_parser_is_legal_white_space_string(const char* s)
         1356  +{
         1357  +    int c, char_class;
         1358  +    
         1359  +    if (s == NULL) {
         1360  +        return false;
         1361  +    }
         1362  +    
         1363  +    for (; *s; ++s) {   
         1364  +        c = *s;
         1365  +        
         1366  +        if (c < 0 || c >= 128) {
         1367  +            return false;
         1368  +        }
         1369  +        
         1370  +        char_class = ascii_class[c];
         1371  +        
         1372  +        if (char_class != C_SPACE && char_class != C_WHITE) {
         1373  +            return false;
         1374  +        }
         1375  +    }
         1376  +    
         1377  +    return true;
         1378  +}
         1379  +
         1380  +int JSON_parser_get_last_error(JSON_parser jc)
         1381  +{
         1382  +    return jc->error;
         1383  +}
         1384  +
         1385  +
         1386  +void init_JSON_config(JSON_config* config)
         1387  +{
         1388  +    if (config) {
         1389  +        memset(config, 0, sizeof(*config));
         1390  +        
         1391  +        config->depth = JSON_PARSER_STACK_SIZE - 1;
         1392  +        config->malloc = malloc;
         1393  +        config->free = free;
         1394  +    }
         1395  +}
         1396  +/* end file parser/JSON_parser.c */
         1397  +/* begin file ./cson.c */
         1398  +#include <assert.h>
         1399  +#include <stdlib.h> /* malloc()/free() */
         1400  +#include <string.h>
         1401  +#include <errno.h>
         1402  +
         1403  +#ifdef _MSC_VER
         1404  +#   if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
         1405  +#     pragma warning( push )
         1406  +#     pragma warning(disable:4996) /* unsecure sscanf (but snscanf() isn't in c89) */
         1407  +#     pragma warning(disable:4244) /* complaining about data loss due
         1408  +                                      to integer precision in the
         1409  +                                      sqlite3 utf decoding routines */
         1410  +#   endif
         1411  +#endif
         1412  +
         1413  +#if 1
         1414  +#include <stdio.h>
         1415  +#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
         1416  +#else
         1417  +static void noop_printf(char const * fmt, ...) {}
         1418  +#define MARKER if(0) printf
         1419  +#endif
         1420  +
         1421  +#if defined(__cplusplus)
         1422  +extern "C" {
         1423  +#endif
         1424  +
         1425  +
         1426  +    
         1427  +/**
         1428  +   This type holds the "vtbl" for type-specific operations when
         1429  +   working with cson_value objects.
         1430  +
         1431  +   All cson_values of a given logical type share a pointer to a single
         1432  +   library-internal instance of this class.
         1433  +*/
         1434  +struct cson_value_api
         1435  +{
         1436  +    /**
         1437  +       The logical JavaScript/JSON type associated with
         1438  +       this object.
         1439  +     */
         1440  +    const cson_type_id typeID;
         1441  +    /**
         1442  +       Must free any memory associated with self,
         1443  +       but not free self. If self is NULL then
         1444  +       this function must do nothing.
         1445  +    */
         1446  +    void (*cleanup)( cson_value * self );
         1447  +    /**
         1448  +       POSSIBLE TODOs:
         1449  +
         1450  +       // Deep copy.
         1451  +       int (*clone)( cson_value const * self, cson_value ** tgt );
         1452  +
         1453  +       // Using JS semantics for true/value
         1454  +       char (*bool_value)( cson_value const * self );
         1455  +
         1456  +       // memcmp() return value semantics
         1457  +       int (*compare)( cson_value const * self, cson_value const * other );
         1458  +     */
         1459  +};
         1460  +
         1461  +typedef struct cson_value_api cson_value_api;
         1462  +
         1463  +/**
         1464  +   Empty-initialized cson_value_api object.
         1465  +*/
         1466  +#define cson_value_api_empty_m {           \
         1467  +        CSON_TYPE_UNDEF/*typeID*/,         \
         1468  +        NULL/*cleanup*/\
         1469  +      }
         1470  +/**
         1471  +   Empty-initialized cson_value_api object.
         1472  +*/
         1473  +static const cson_value_api cson_value_api_empty = cson_value_api_empty_m;
         1474  +
         1475  +
         1476  +typedef unsigned int cson_counter_t;
         1477  +struct cson_value
         1478  +{
         1479  +    /** The "vtbl" of type-specific operations. All instances
         1480  +        of a given logical value type share a single api instance.
         1481  +
         1482  +        Results are undefined if this value is NULL.
         1483  +    */
         1484  +    cson_value_api const * api;
         1485  +
         1486  +    /** The raw value. Its interpretation depends on the value of the
         1487  +        api member. Some value types require dynamically-allocated
         1488  +        memory, so one must always call cson_value_free() to destroy a
         1489  +        value when it is no longer needed. For stack-allocated values
         1490  +        (which client could SHOULD NOT USE unless they are intimately
         1491  +        familiar with the memory management rules and don't mind an
         1492  +        occasional leak or crash), use cson_value_clean() instead of
         1493  +        cson_value_free().
         1494  +    */
         1495  +    void * value;
         1496  +
         1497  +    /**
         1498  +       We use this to allow us to store cson_value instances in
         1499  +       multiple containers or multiple times within a single container
         1500  +       (provided no cycles are introduced).
         1501  +
         1502  +       Notes about the rc implementation:
         1503  +
         1504  +       - The refcount is for the cson_value instance itself, not its
         1505  +       value pointer.
         1506  +
         1507  +       - Instances start out with a refcount of 0 (not 1). Adding them
         1508  +       to a container will increase the refcount. Cleaning up the container
         1509  +       will decrement the count.
         1510  +
         1511  +       - cson_value_free() decrements the refcount (if it is not already
         1512  +       0) and cleans/frees the value only when the refcount is 0.
         1513  +
         1514  +       - Some places in the internals add an "extra" reference to
         1515  +       objects to avoid a premature deletion. Don't try this at home.
         1516  +    */
         1517  +    cson_counter_t refcount;
         1518  +};
         1519  +
         1520  +
         1521  +/**
         1522  +   Empty-initialized cson_value object.
         1523  +*/
         1524  +#define cson_value_empty_m { &cson_value_api_empty/*api*/, NULL/*value*/, 0/*refcount*/ }
         1525  +/**
         1526  +   Empty-initialized cson_value object.
         1527  +*/
         1528  +static const cson_value cson_value_empty = cson_value_empty_m;
         1529  +const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m;
         1530  +const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m;
         1531  +const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m;
         1532  +const cson_buffer cson_buffer_empty = cson_buffer_empty_m;
         1533  +const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m;
         1534  +
         1535  +static void cson_value_destroy_zero_it( cson_value * self );
         1536  +static void cson_value_destroy_object( cson_value * self );
         1537  +/**
         1538  +   If self is-a array then this function destroys its contents,
         1539  +   else this function does nothing.
         1540  +*/
         1541  +static void cson_value_destroy_array( cson_value * self );
         1542  +
         1543  +static const cson_value_api cson_value_api_null = { CSON_TYPE_NULL, cson_value_destroy_zero_it };
         1544  +static const cson_value_api cson_value_api_undef = { CSON_TYPE_UNDEF, cson_value_destroy_zero_it };
         1545  +static const cson_value_api cson_value_api_bool = { CSON_TYPE_BOOL, cson_value_destroy_zero_it };
         1546  +static const cson_value_api cson_value_api_integer = { CSON_TYPE_INTEGER, cson_value_destroy_zero_it };
         1547  +static const cson_value_api cson_value_api_double = { CSON_TYPE_DOUBLE, cson_value_destroy_zero_it };
         1548  +static const cson_value_api cson_value_api_string = { CSON_TYPE_STRING, cson_value_destroy_zero_it };
         1549  +static const cson_value_api cson_value_api_array = { CSON_TYPE_ARRAY, cson_value_destroy_array };
         1550  +static const cson_value_api cson_value_api_object = { CSON_TYPE_OBJECT, cson_value_destroy_object };
         1551  +
         1552  +static const cson_value cson_value_undef = { &cson_value_api_undef, NULL, 0 };
         1553  +static const cson_value cson_value_null_empty = { &cson_value_api_null, NULL, 0 };
         1554  +static const cson_value cson_value_bool_empty = { &cson_value_api_bool, NULL, 0 };
         1555  +static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NULL, 0 };
         1556  +static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 };
         1557  +static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 };
         1558  +static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 };
         1559  +static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 };
         1560  +
         1561  +/**
         1562  +   Strings are allocated as an instances of this class with N+1
         1563  +   trailing bytes, where N is the length of the string being
         1564  +   allocated. To convert a cson_string to c-string we simply increment
         1565  +   the cson_string pointer. To do the opposite we use (cstr -
         1566  +   sizeof(cson_string)). Zero-length strings are a special case
         1567  +   handled by a couple of the cson_string functions.
         1568  +*/
         1569  +struct cson_string
         1570  +{
         1571  +    unsigned int length;
         1572  +};
         1573  +#define cson_string_empty_m {0/*length*/}
         1574  +static const cson_string cson_string_empty = cson_string_empty_m;
         1575  +
         1576  +
         1577  +
         1578  +#define CSON_CAST(T,V) ((T*)((V)->value))
         1579  +#define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
         1580  +
         1581  +#if CSON_VOID_PTR_IS_BIG
         1582  +#  define CSON_INT(V) ((cson_int_t*)(&((V)->value)))
         1583  +#else
         1584  +#  define CSON_INT(V) ((cson_int_t*)(V)->value)
         1585  +#endif
         1586  +
         1587  +#define CSON_DBL(V) CSON_CAST(cson_double_t,(V))
         1588  +#define CSON_STR(V) CSON_CAST(cson_string,(V))
         1589  +#define CSON_OBJ(V) CSON_CAST(cson_object,(V))
         1590  +#define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
         1591  +
         1592  +/**
         1593  + 
         1594  + Holds special shared "constant" (though they are non-const)
         1595  + values.
         1596  + 
         1597  +*/
         1598  +static struct CSON_EMPTY_HOLDER_
         1599  +{
         1600  +    char trueValue;
         1601  +    cson_string stringValue;
         1602  +} CSON_EMPTY_HOLDER = {
         1603  +    1/*trueValue*/,
         1604  +    cson_string_empty_m
         1605  +};
         1606  +
         1607  +/**
         1608  +    Indexes into the CSON_SPECIAL_VALUES array.
         1609  +    
         1610  +    If this enum changes in any way,
         1611  +    makes damned sure that CSON_SPECIAL_VALUES is updated
         1612  +    to match!!!
         1613  +*/
         1614  +enum CSON_INTERNAL_VALUES {
         1615  +    
         1616  +    CSON_VAL_UNDEF = 0,
         1617  +    CSON_VAL_NULL = 1,
         1618  +    CSON_VAL_TRUE = 2,
         1619  +    CSON_VAL_FALSE = 3,
         1620  +    CSON_VAL_INT_0 = 4,
         1621  +    CSON_VAL_DBL_0 = 5,
         1622  +    CSON_VAL_STR_EMPTY = 6,
         1623  +    CSON_INTERNAL_VALUES_LENGTH
         1624  +};
         1625  +
         1626  +/**
         1627  +  Some "special" shared cson_value instances.
         1628  +
         1629  +  These values MUST be initialized in the order specified
         1630  +  by the CSON_INTERNAL_VALUES enum.
         1631  +   
         1632  +  Note that they are not const because they are used as
         1633  +  shared-allocation objects in non-const contexts. However, the
         1634  +  public API provides no way to modifying them, and clients who
         1635  +  modify values directly are subject to The Wrath of Undefined
         1636  +  Behaviour.
         1637  +*/
         1638  +static cson_value CSON_SPECIAL_VALUES[] = {
         1639  +{ &cson_value_api_undef, NULL, 0 }, /* UNDEF */
         1640  +{ &cson_value_api_null, NULL, 0 }, /* NULL */
         1641  +{ &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */
         1642  +{ &cson_value_api_bool, NULL, 0 }, /* FALSE */
         1643  +{ &cson_value_api_integer, NULL, 0 }, /* INT_0 */
         1644  +{ &cson_value_api_double, NULL, 0 }, /* DBL_0 */
         1645  +{ &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */
         1646  +{ NULL, NULL, 0 }
         1647  +};
         1648  +
         1649  +
         1650  +/**
         1651  +    Returns non-0 (true) if m is one of our special
         1652  +    "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
         1653  +    "empty" values.
         1654  +     
         1655  +    If this returns true, m MUST NOT be free()d!
         1656  + */
         1657  +static char cson_value_is_builtin( void const * m )
         1658  +{
         1659  +    if((m >= (void const *)&CSON_EMPTY_HOLDER)
         1660  +        && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
         1661  +        return 1;
         1662  +    else return
         1663  +        ((m >= (void const *)&CSON_SPECIAL_VALUES[0])
         1664  +        && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
         1665  +        ? 1
         1666  +        : 0;
         1667  +}
         1668  +
         1669  +char const * cson_rc_string(int rc)
         1670  +{
         1671  +    if(0 == rc) return "OK";
         1672  +#define CHECK(N) else if(cson_rc.N == rc ) return #N
         1673  +    CHECK(OK);
         1674  +    CHECK(ArgError);
         1675  +    CHECK(RangeError);
         1676  +    CHECK(TypeError);
         1677  +    CHECK(IOError);
         1678  +    CHECK(AllocError);
         1679  +    CHECK(NYIError);
         1680  +    CHECK(InternalError);
         1681  +    CHECK(UnsupportedError);
         1682  +    CHECK(NotFoundError);
         1683  +    CHECK(UnknownError);
         1684  +    CHECK(Parse_INVALID_CHAR);
         1685  +    CHECK(Parse_INVALID_KEYWORD);
         1686  +    CHECK(Parse_INVALID_ESCAPE_SEQUENCE);
         1687  +    CHECK(Parse_INVALID_UNICODE_SEQUENCE);
         1688  +    CHECK(Parse_INVALID_NUMBER);
         1689  +    CHECK(Parse_NESTING_DEPTH_REACHED);
         1690  +    CHECK(Parse_UNBALANCED_COLLECTION);
         1691  +    CHECK(Parse_EXPECTED_KEY);
         1692  +    CHECK(Parse_EXPECTED_COLON);
         1693  +    else return "UnknownError";
         1694  +#undef CHECK
         1695  +}
         1696  +
         1697  +/**
         1698  +   If CSON_LOG_ALLOC is true then the cson_malloc/realloc/free() routines
         1699  +   will log a message to stderr.
         1700  +*/
         1701  +#define CSON_LOG_ALLOC 0
         1702  +
         1703  +
         1704  +/**
         1705  +   CSON_FOSSIL_MODE is only for use in the Fossil
         1706  +   source tree, so that we can plug in to its allocators.
         1707  +   We can't do this by, e.g., defining macros for the
         1708  +   malloc/free funcs because fossil's lack of header files
         1709  +   means we would have to #include "main.c" here to
         1710  +   get the declarations.
         1711  + */
         1712  +#if defined(CSON_FOSSIL_MODE)
         1713  +extern void *fossil_malloc(size_t n);
         1714  +extern void fossil_free(void *p);
         1715  +extern void *fossil_realloc(void *p, size_t n);
         1716  +#  define CSON_MALLOC_IMPL fossil_malloc
         1717  +#  define CSON_FREE_IMPL fossil_free
         1718  +#  define CSON_REALLOC_IMPL fossil_realloc
         1719  +#endif
         1720  +
         1721  +#if !defined CSON_MALLOC_IMPL
         1722  +#  define CSON_MALLOC_IMPL malloc
         1723  +#endif
         1724  +#if !defined CSON_FREE_IMPL
         1725  +#  define CSON_FREE_IMPL free
         1726  +#endif
         1727  +#if !defined CSON_REALLOC_IMPL
         1728  +#  define CSON_REALLOC_IMPL realloc
         1729  +#endif
         1730  +
         1731  +/**
         1732  +   A test/debug macro for simulating an OOM after the given number of
         1733  +   bytes have been allocated.
         1734  +*/
         1735  +#define CSON_SIMULATE_OOM 0
         1736  +#if CSON_SIMULATE_OOM
         1737  +static unsigned int cson_totalAlloced = 0;
         1738  +#endif
         1739  +
         1740  +/** Simple proxy for malloc(). descr is a description of the allocation. */
         1741  +static void * cson_malloc( size_t n, char const * descr )
         1742  +{
         1743  +#if CSON_LOG_ALLOC
         1744  +    fprintf(stderr, "Allocating %u bytes [%s].\n", (unsigned int)n, descr);
         1745  +#endif
         1746  +#if CSON_SIMULATE_OOM
         1747  +    cson_totalAlloced += n;
         1748  +    if( cson_totalAlloced > CSON_SIMULATE_OOM )
         1749  +    {
         1750  +        return NULL;
         1751  +    }
         1752  +#endif
         1753  +    return CSON_MALLOC_IMPL(n);
         1754  +}
         1755  +
         1756  +/** Simple proxy for free(). descr is a description of the memory being freed. */
         1757  +static void cson_free( void * p, char const * descr )
         1758  +{
         1759  +#if CSON_LOG_ALLOC
         1760  +    fprintf(stderr, "Freeing @%p [%s].\n", p, descr);
         1761  +#endif
         1762  +    if( !cson_value_is_builtin(p) )
         1763  +    {
         1764  +        CSON_FREE_IMPL( p );
         1765  +    }
         1766  +}
         1767  +/** Simple proxy for realloc(). descr is a description of the (re)allocation. */
         1768  +static void * cson_realloc( void * hint, size_t n, char const * descr )
         1769  +{
         1770  +#if CSON_LOG_ALLOC
         1771  +    fprintf(stderr, "%sllocating %u bytes [%s].\n",
         1772  +            hint ? "Rea" : "A",
         1773  +            (unsigned int)n, descr);
         1774  +#endif
         1775  +#if CSON_SIMULATE_OOM
         1776  +    cson_totalAlloced += n;
         1777  +    if( cson_totalAlloced > CSON_SIMULATE_OOM )
         1778  +    {
         1779  +        return NULL;
         1780  +    }
         1781  +#endif
         1782  +    if( 0==n )
         1783  +    {
         1784  +         cson_free(hint, descr);
         1785  +         return NULL;
         1786  +    }
         1787  +    else
         1788  +    {
         1789  +        return CSON_REALLOC_IMPL( hint, n );
         1790  +    }
         1791  +}
         1792  +
         1793  +
         1794  +#undef CSON_LOG_ALLOC
         1795  +#undef CSON_SIMULATE_OOM
         1796  +
         1797  +
         1798  +
         1799  +/**
         1800  +   CLIENTS CODE SHOULD NEVER USE THIS because it opens up doors to
         1801  +   memory leaks if it is not used in very controlled circumstances.
         1802  +   Users must be very aware of how the underlying memory management
         1803  +   works.
         1804  +
         1805  +   Frees any resources owned by val, but does not free val itself
         1806  +   (which may be stack-allocated). If !val or val->api or
         1807  +   val->api->cleanup are NULL then this is a no-op.
         1808  +
         1809  +   If v is a container type (object or array) its children are also
         1810  +   cleaned up (BUT NOT FREED), recursively.
         1811  +
         1812  +   After calling this, val will have the special "undefined" type.
         1813  +*/
         1814  +static void cson_value_clean( cson_value * val );
         1815  +
         1816  +/**
         1817  +   Increments cv's reference count by 1.  As a special case, values
         1818  +   for which cson_value_is_builtin() returns true are not
         1819  +   modified. assert()s if (NULL==cv).
         1820  +*/
         1821  +static void cson_refcount_incr( cson_value * cv )
         1822  +{
         1823  +    assert( NULL != cv );
         1824  +    if( cson_value_is_builtin( cv ) )
         1825  +    { /* do nothing: we do not want to modify the shared
         1826  +         instances.
         1827  +      */
         1828  +        return;
         1829  +    }
         1830  +    else
         1831  +    {
         1832  +        ++cv->refcount;
         1833  +    }
         1834  +}
         1835  +
         1836  +#if 0
         1837  +int cson_value_refcount_set( cson_value * cv, unsigned short rc )
         1838  +{
         1839  +    if( NULL == cv ) return cson_rc.ArgError;
         1840  +    else
         1841  +    {
         1842  +        cv->refcount = rc;
         1843  +        return 0;
         1844  +    }
         1845  +}
         1846  +#endif
         1847  +
         1848  +int cson_value_add_reference( cson_value * cv )
         1849  +{
         1850  +    if( NULL == cv ) return cson_rc.ArgError;
         1851  +    else if( (cv->refcount+1) < cv->refcount )
         1852  +    {
         1853  +        return cson_rc.RangeError;
         1854  +    }
         1855  +    else
         1856  +    {
         1857  +        cson_refcount_incr( cv );
         1858  +        return 0;
         1859  +    }
         1860  +}
         1861  +
         1862  +/**
         1863  +   If cv is NULL or cson_value_is_builtin(cv) returns true then this
         1864  +   function does nothing and returns 0, otherwise...  If
         1865  +   cv->refcount is 0 or 1 then cson_value_clean(cv) is called, cv is
         1866  +   freed, and 0 is returned. If cv->refcount is any other value then
         1867  +   it is decremented and the new value is returned.
         1868  +*/
         1869  +static cson_counter_t cson_refcount_decr( cson_value * cv )
         1870  +{
         1871  +    if( (NULL == cv) || cson_value_is_builtin(cv) ) return 0;
         1872  +    else if( (0 == cv->refcount) || (0 == --cv->refcount) )
         1873  +    {
         1874  +        cson_value_clean(cv);
         1875  +        cson_free(cv,"cson_value::refcount=0");
         1876  +        return 0;
         1877  +    }
         1878  +    else return cv->refcount;
         1879  +}
         1880  +
         1881  +unsigned int cson_string_length_bytes( cson_string const * str )
         1882  +{
         1883  +    return str ? str->length : 0;
         1884  +}
         1885  +
         1886  +
         1887  +/**
         1888  +   Fetches v's string value as a non-const string.
         1889  +
         1890  +   cson_strings are supposed to be immutable, but this form provides
         1891  +   access to the immutable bits, which are v->length bytes long. A
         1892  +   length-0 string is returned as NULL from here, as opposed to
         1893  +   "". (This is a side-effect of the string allocation mechanism.)
         1894  +   Returns NULL if !v or if v is the internal empty-string singleton.
         1895  +*/
         1896  +static char * cson_string_str(cson_string *v)
         1897  +{
         1898  +    /*
         1899  +      See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a
         1900  +    */
         1901  +#if 1
         1902  +    if( !v || (&CSON_EMPTY_HOLDER.stringValue == v) ) return NULL;
         1903  +    else return (char *)((unsigned char *)( v+1 ));
         1904  +#else
         1905  +    static char empty[2] = {0,0};
         1906  +    return ( NULL == v )
         1907  +        ? NULL
         1908  +        : (v->length
         1909  +           ? (char *) (((unsigned char *)v) + sizeof(cson_string))
         1910  +           : empty)
         1911  +        ;
         1912  +#endif
         1913  +}
         1914  +
         1915  +/**
         1916  +   Fetches v's string value as a const string.
         1917  +*/
         1918  +char const * cson_string_cstr(cson_string const *v)
         1919  +{
         1920  +    /*
         1921  +      See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a
         1922  +    */
         1923  +#if 1
         1924  +    if( ! v ) return NULL;
         1925  +    else if( v == &CSON_EMPTY_HOLDER.stringValue ) return "";
         1926  +    else {
         1927  +        assert((0 < v->length) && "How do we have a non-singleton empty string?");
         1928  +        return (char const *)((unsigned char const *)(v+1));
         1929  +    }
         1930  +#else
         1931  +    return (NULL == v)
         1932  +        ? NULL
         1933  +        : (v->length
         1934  +           ? (char const *) ((unsigned char const *)(v+1))
         1935  +           : "");
         1936  +#endif
         1937  +}
         1938  +
         1939  +
         1940  +#if 0
         1941  +/**
         1942  +   Just like strndup(3), in that neither are C89/C99-standard and both
         1943  +   are documented in detail in strndup(3).
         1944  +*/
         1945  +static char * cson_strdup( char const * src, size_t n )
         1946  +{
         1947  +    char * rc = (char *)cson_malloc(n+1, "cson_strdup");
         1948  +    if( ! rc ) return NULL;
         1949  +    memset( rc, 0, n+1 );
         1950  +    rc[n] = 0;
         1951  +    return strncpy( rc, src, n );
         1952  +}
         1953  +#endif
         1954  +
         1955  +int cson_string_cmp_cstr_n( cson_string const * str, char const * other, unsigned int otherLen )
         1956  +{
         1957  +    if( ! other && !str ) return 0;
         1958  +    else if( other && !str ) return 1;
         1959  +    else if( str && !other ) return -1;
         1960  +    else if( !otherLen ) return  str->length ? 1 : 0;
         1961  +    else if( !str->length ) return otherLen ? -1 : 0;
         1962  +    else
         1963  +    {
         1964  +        unsigned const int max = (otherLen > str->length) ? otherLen : str->length;
         1965  +        int const rc = strncmp( cson_string_cstr(str), other, max );
         1966  +        return ( (0 == rc) && (otherLen != str->length) )
         1967  +            ? (str->length < otherLen) ? -1 : 1
         1968  +            : rc;
         1969  +    }
         1970  +}
         1971  +
         1972  +int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs )
         1973  +{
         1974  +    return cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs) ? strlen(rhs) : 0 );
         1975  +}
         1976  +int cson_string_cmp( cson_string const * lhs, cson_string const * rhs )
         1977  +{
         1978  +    return cson_string_cmp_cstr_n( lhs, cson_string_cstr(rhs), rhs ? rhs->length : 0 );
         1979  +}
         1980  +
         1981  +
         1982  +/**
         1983  +   If self is not NULL, *self is overwritten to have the undefined
         1984  +   type. self is not cleaned up or freed.
         1985  +*/
         1986  +void cson_value_destroy_zero_it( cson_value * self )
         1987  +{
         1988  +    if( self )
         1989  +    {
         1990  +        *self = cson_value_undef;
         1991  +    }
         1992  +}
         1993  +
         1994  +/**
         1995  +   A key/value pair collection.
         1996  +
         1997  +   Each of these objects owns its key/value pointers, and they
         1998  +   are cleaned up by cson_kvp_clean().
         1999  +*/
         2000  +struct cson_kvp
         2001  +{
         2002  +    cson_value * key;
         2003  +    cson_value * value;
         2004  +};
         2005  +#define cson_kvp_empty_m {NULL,NULL}
         2006  +static const cson_kvp cson_kvp_empty = cson_kvp_empty_m;
         2007  +
         2008  +/** @def CSON_OBJECT_PROPS_SORT
         2009  +
         2010  +    Don't use this - it has not been updated to account for internal
         2011  +    changes in cson_object.
         2012  +
         2013  +   If CSON_OBJECT_PROPS_SORT is set to a true value then
         2014  +   qsort() and bsearch() are used to sort (upon insertion)
         2015  +   and search cson_object::kvp property lists. This costs us
         2016  +   a re-sort on each insertion but searching is O(log n)
         2017  +   average/worst case (and O(1) best-case).
         2018  +
         2019  +   i'm not yet convinced that the overhead of the qsort() justifies
         2020  +   the potentially decreased search times - it has not been
         2021  +   measured. Object property lists tend to be relatively short in
         2022  +   JSON, and a linear search which uses the cson_string::length
         2023  +   property as a quick check is quite fast when one compares it with
         2024  +   the sort overhead required by the bsearch() approach.
         2025  +*/
         2026  +#define CSON_OBJECT_PROPS_SORT 0
         2027  +
         2028  +/** @def CSON_OBJECT_PROPS_SORT_USE_LENGTH
         2029  +
         2030  +    Don't use this - i'm not sure that it works how i'd like.
         2031  +
         2032  +    If CSON_OBJECT_PROPS_SORT_USE_LENGTH is true then
         2033  +    we use string lengths as quick checks when sorting
         2034  +    property keys. This leads to a non-intuitive sorting
         2035  +    order but "should" be faster.
         2036  +
         2037  +    This is ignored if CSON_OBJECT_PROPS_SORT is false.
         2038  +
         2039  +*/
         2040  +#define CSON_OBJECT_PROPS_SORT_USE_LENGTH 0
         2041  +
         2042  +#if CSON_OBJECT_PROPS_SORT
         2043  +
         2044  +/**
         2045  +   cson_kvp comparator for use with qsort(). ALMOST compares with
         2046  +   strcmp() semantics, but it uses the strings' lengths as a quicker
         2047  +   approach. This might give non-intuitive results, but it's faster.
         2048  + */
         2049  +static int cson_kvp_cmp( void const * lhs, void const * rhs )
         2050  +{
         2051  +    cson_kvp const * lk = *((cson_kvp const * const*)lhs);
         2052  +    cson_kvp const * rk = *((cson_kvp const * const*)rhs);
         2053  +    cson_string const * l = cson_string_value(lk->key);
         2054  +    cson_string const * r = cson_string_value(rk->key);
         2055  +#if CSON_OBJECT_PROPS_SORT_USE_LENGTH
         2056  +    if( l->length < r->length ) return -1;
         2057  +    else if( l->length > r->length ) return 1;
         2058  +    else return strcmp( cson_string_cstr( l ), cson_string_cstr( r ) );
         2059  +#else
         2060  +    return strcmp( cson_string_cstr( l ),
         2061  +                   cson_string_cstr( r ) );
         2062  +#endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/
         2063  +}
         2064  +#endif /*CSON_OBJECT_PROPS_SORT*/
         2065  +
         2066  +
         2067  +#if CSON_OBJECT_PROPS_SORT
         2068  +#error "Need to rework this for cson_string-to-cson_value refactoring"
         2069  +/**
         2070  +   A bsearch() comparison function which requires that lhs be a (char
         2071  +   const *) and rhs be-a (cson_kvp const * const *). It compares lhs
         2072  +   to rhs->key's value, using strcmp() semantics.
         2073  + */
         2074  +static int cson_kvp_cmp_vs_cstr( void const * lhs, void const * rhs )
         2075  +{
         2076  +    char const * lk = (char const *)lhs;
         2077  +    cson_kvp const * rk =
         2078  +        *((cson_kvp const * const*)rhs)
         2079  +        ;
         2080  +#if CSON_OBJECT_PROPS_SORT_USE_LENGTH
         2081  +    unsigned int llen = strlen(lk);
         2082  +    if( llen < rk->key->length ) return -1;
         2083  +    else if( llen > rk->key->length ) return 1;
         2084  +    else return strcmp( lk, cson_string_cstr( rk->key ) );
         2085  +#else
         2086  +    return strcmp( lk, cson_string_cstr( rk->key ) );
         2087  +#endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/
         2088  +}
         2089  +#endif /*CSON_OBJECT_PROPS_SORT*/
         2090  +
         2091  +
         2092  +struct cson_kvp_list
         2093  +{
         2094  +    cson_kvp ** list;
         2095  +    unsigned int count;
         2096  +    unsigned int alloced;
         2097  +};
         2098  +typedef struct cson_kvp_list cson_kvp_list;
         2099  +#define cson_kvp_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/}
         2100  +static const cson_kvp_list cson_kvp_list_empty = cson_kvp_list_empty_m;
         2101  +
         2102  +struct cson_object
         2103  +{
         2104  +    cson_kvp_list kvp;
         2105  +};
         2106  +/*typedef struct cson_object cson_object;*/
         2107  +#define cson_object_empty_m { cson_kvp_list_empty_m/*kvp*/ }
         2108  +static const cson_object cson_object_empty = cson_object_empty_m;
         2109  +
         2110  +struct cson_value_list
         2111  +{
         2112  +    cson_value ** list;
         2113  +    unsigned int count;
         2114  +    unsigned int alloced;
         2115  +};
         2116  +typedef struct cson_value_list cson_value_list;
         2117  +#define cson_value_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/}
         2118  +static const cson_value_list cson_value_list_empty = cson_value_list_empty_m;
         2119  +
         2120  +struct cson_array
         2121  +{
         2122  +    cson_value_list list;
         2123  +};
         2124  +/*typedef struct cson_array cson_array;*/
         2125  +#define cson_array_empty_m { cson_value_list_empty_m/*list*/ }
         2126  +static const cson_array cson_array_empty = cson_array_empty_m;
         2127  +
         2128  +
         2129  +struct cson_parser
         2130  +{
         2131  +    JSON_parser p;
         2132  +    cson_value * root;
         2133  +    cson_value * node;
         2134  +    cson_array stack;
         2135  +    cson_string * ckey;
         2136  +    int errNo;
         2137  +    unsigned int totalKeyCount;
         2138  +    unsigned int totalValueCount;
         2139  +};
         2140  +typedef struct cson_parser cson_parser;
         2141  +static const cson_parser cson_parser_empty = {
         2142  +NULL/*p*/,
         2143  +NULL/*root*/,
         2144  +NULL/*node*/,
         2145  +cson_array_empty_m/*stack*/,
         2146  +NULL/*ckey*/,
         2147  +0/*errNo*/,
         2148  +0/*totalKeyCount*/,
         2149  +0/*totalValueCount*/
         2150  +};
         2151  +
         2152  +#if 1
         2153  +/* The following funcs are declared in generated code (cson_lists.h),
         2154  +   but we need early access to their decls for the Amalgamation build.
         2155  +*/
         2156  +static unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n );
         2157  +static unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n );
         2158  +static int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp );
         2159  +static void cson_kvp_list_clean( cson_kvp_list * self,
         2160  +                                 void (*cleaner)(cson_kvp * obj) );
         2161  +#if 0
         2162  +static int cson_value_list_append( cson_value_list * self, cson_value * cp );
         2163  +static void cson_value_list_clean( cson_value_list * self, void (*cleaner)(cson_value * obj));
         2164  +static int cson_kvp_list_visit( cson_kvp_list * self,
         2165  +                                int (*visitor)(cson_kvp * obj, void * visitorState ),
         2166  +                                void * visitorState );
         2167  +static int cson_value_list_visit( cson_value_list * self,
         2168  +                                  int (*visitor)(cson_value * obj, void * visitorState ),
         2169  +                                  void * visitorState );
         2170  +#endif
         2171  +#endif
         2172  +    
         2173  +#if 0
         2174  +#  define LIST_T cson_value_list
         2175  +#  define VALUE_T cson_value *
         2176  +#  define VALUE_T_IS_PTR 1
         2177  +#  define LIST_T cson_kvp_list
         2178  +#  define VALUE_T cson_kvp *
         2179  +#  define VALUE_T_IS_PTR 1
         2180  +#else
         2181  +#endif
         2182  +
         2183  +/**
         2184  +   Allocates a new value of the specified type. Ownership is
         2185  +   transfered to the caller, who must eventually free it by passing it
         2186  +   to cson_value_free() or transfering ownership to a container.
         2187  +
         2188  +   extra is only valid for type CSON_TYPE_STRING, and must be the length
         2189  +   of the string to allocate + 1 byte (for the NUL).
         2190  +
         2191  +   The returned value->api member will be set appropriately and
         2192  +   val->value will be set to point to the memory allocated to hold the
         2193  +   native value type. Use the internal CSON_CAST() family of macros to
         2194  +   convert the cson_values to their corresponding native
         2195  +   representation.
         2196  +
         2197  +   Returns NULL on allocation error.
         2198  +
         2199  +   @see cson_value_new_array()
         2200  +   @see cson_value_new_object()
         2201  +   @see cson_value_new_string()
         2202  +   @see cson_value_new_integer()
         2203  +   @see cson_value_new_double()
         2204  +   @see cson_value_new_bool()
         2205  +   @see cson_value_free()
         2206  +*/
         2207  +static cson_value * cson_value_new(cson_type_id t, size_t extra)
         2208  +{
         2209  +    static const size_t vsz = sizeof(cson_value);
         2210  +    const size_t sz = vsz + extra;
         2211  +    size_t tx = 0;
         2212  +    cson_value def = cson_value_undef;
         2213  +    cson_value * v = NULL;
         2214  +    char const * reason = "cson_value_new";
         2215  +    switch(t)
         2216  +    {
         2217  +      case CSON_TYPE_ARRAY:
         2218  +          assert( 0 == extra );
         2219  +          def = cson_value_array_empty;
         2220  +          tx = sizeof(cson_array);
         2221  +          reason = "cson_value:array";
         2222  +          break;
         2223  +      case CSON_TYPE_DOUBLE:
         2224  +          assert( 0 == extra );
         2225  +          def = cson_value_double_empty;
         2226  +          tx = sizeof(cson_double_t);
         2227  +          reason = "cson_value:double";
         2228  +          break;
         2229  +      case CSON_TYPE_INTEGER:
         2230  +          assert( 0 == extra );
         2231  +          def = cson_value_integer_empty;
         2232  +#if !CSON_VOID_PTR_IS_BIG
         2233  +          tx = sizeof(cson_int_t);
         2234  +#endif
         2235  +          reason = "cson_value:int";
         2236  +          break;
         2237  +      case CSON_TYPE_STRING:
         2238  +          assert( 0 != extra );
         2239  +          def = cson_value_string_empty;
         2240  +          tx = sizeof(cson_string);
         2241  +          reason = "cson_value:string";
         2242  +          break;
         2243  +      case CSON_TYPE_OBJECT:
         2244  +          assert( 0 == extra );
         2245  +          def = cson_value_object_empty;
         2246  +          tx = sizeof(cson_object);
         2247  +          reason = "cson_value:object";
         2248  +          break;
         2249  +      default:
         2250  +          assert(0 && "Unhandled type in cson_value_new()!");
         2251  +          return NULL;
         2252  +    }
         2253  +    assert( def.api->typeID != CSON_TYPE_UNDEF );
         2254  +    v = (cson_value *)cson_malloc(sz+tx, reason);
         2255  +    if( v ) {
         2256  +        *v = def;
         2257  +        if(tx || extra){
         2258  +            memset(v+1, 0, tx + extra);
         2259  +            v->value = (void *)(v+1);
         2260  +        }
         2261  +    }
         2262  +    return v;
         2263  +}
         2264  +
         2265  +void cson_value_free(cson_value *v)
         2266  +{
         2267  +    cson_refcount_decr( v );
         2268  +}
         2269  +
         2270  +#if 0 /* we might actually want this later on. */
         2271  +/** Returns true if v is not NULL and has the given type ID. */
         2272  +static char cson_value_is_a( cson_value const * v, cson_type_id is )
         2273  +{
         2274  +    return (v && v->api && (v->api->typeID == is)) ? 1 : 0;
         2275  +}
         2276  +#endif
         2277  +
         2278  +cson_type_id cson_value_type_id( cson_value const * v )
         2279  +{
         2280  +    return (v && v->api) ? v->api->typeID : CSON_TYPE_UNDEF;
         2281  +}
         2282  +
         2283  +char cson_value_is_undef( cson_value const * v )
         2284  +{
         2285  +    return ( !v || !v->api || (v->api==&cson_value_api_undef))
         2286  +        ? 1 : 0;
         2287  +}
         2288  +#define ISA(T,TID) char cson_value_is_##T( cson_value const * v ) {       \
         2289  +        /*return (v && v->api) ? cson_value_is_a(v,CSON_TYPE_##TID) : 0;*/ \
         2290  +        return (v && (v->api == &cson_value_api_##T)) ? 1 : 0; \
         2291  +    } static const char bogusPlaceHolderForEmacsIndention##TID = CSON_TYPE_##TID
         2292  +ISA(null,NULL);
         2293  +ISA(bool,BOOL);
         2294  +ISA(integer,INTEGER);
         2295  +ISA(double,DOUBLE);
         2296  +ISA(string,STRING);
         2297  +ISA(array,ARRAY);
         2298  +ISA(object,OBJECT);
         2299  +#undef ISA
         2300  +char cson_value_is_number( cson_value const * v )
         2301  +{
         2302  +    return cson_value_is_integer(v) || cson_value_is_double(v);
         2303  +}
         2304  +
         2305  +
         2306  +void cson_value_clean( cson_value * val )
         2307  +{
         2308  +    if( val && val->api && val->api->cleanup )
         2309  +    {
         2310  +        if( ! cson_value_is_builtin( val ) )
         2311  +        {
         2312  +            cson_counter_t const rc = val->refcount;
         2313  +            val->api->cleanup(val);
         2314  +            *val = cson_value_undef;
         2315  +            val->refcount = rc;
         2316  +        }
         2317  +    }
         2318  +}
         2319  +
         2320  +static cson_value * cson_value_array_alloc()
         2321  +{
         2322  +    cson_value * v = cson_value_new(CSON_TYPE_ARRAY,0);
         2323  +    if( NULL != v )
         2324  +    {
         2325  +        cson_array * ar = CSON_ARRAY(v);
         2326  +        assert(NULL != ar);
         2327  +        *ar = cson_array_empty;
         2328  +    }
         2329  +    return v;
         2330  +}
         2331  +
         2332  +static cson_value * cson_value_object_alloc()
         2333  +{
         2334  +    cson_value * v = cson_value_new(CSON_TYPE_OBJECT,0);
         2335  +    if( NULL != v )
         2336  +    {
         2337  +        cson_object * obj = CSON_OBJ(v);
         2338  +        assert(NULL != obj);
         2339  +        *obj = cson_object_empty;
         2340  +    }
         2341  +    return v;
         2342  +}
         2343  +
         2344  +cson_value * cson_value_new_object()
         2345  +{
         2346  +    return cson_value_object_alloc();
         2347  +}
         2348  +
         2349  +cson_object * cson_new_object()
         2350  +{
         2351  +    
         2352  +    return cson_value_get_object( cson_value_new_object() );
         2353  +}
         2354  +
         2355  +cson_value * cson_value_new_array()
         2356  +{
         2357  +    return cson_value_array_alloc();
         2358  +}
         2359  +
         2360  +
         2361  +cson_array * cson_new_array()
         2362  +{
         2363  +    return cson_value_get_array( cson_value_new_array() );
         2364  +}
         2365  +
         2366  +/**
         2367  +   Frees kvp->key and kvp->value and sets them to NULL, but does not free
         2368  +   kvp. If !kvp then this is a no-op.
         2369  +*/
         2370  +static void cson_kvp_clean( cson_kvp * kvp )
         2371  +{
         2372  +    if( kvp )
         2373  +    {
         2374  +        if(kvp->key)
         2375  +        {
         2376  +            cson_value_free(kvp->key);
         2377  +            kvp->key = NULL;
         2378  +        }
         2379  +        if(kvp->value)
         2380  +        {
         2381  +            cson_value_free( kvp->value );
         2382  +            kvp->value = NULL;
         2383  +        }
         2384  +    }
         2385  +}
         2386  +
         2387  +cson_string * cson_kvp_key( cson_kvp const * kvp )
         2388  +{
         2389  +    return kvp ? cson_value_get_string(kvp->key) : NULL;
         2390  +}
         2391  +cson_value * cson_kvp_value( cson_kvp const * kvp )
         2392  +{
         2393  +    return kvp ? kvp->value : NULL;
         2394  +}
         2395  +
         2396  +
         2397  +/**
         2398  +   Calls cson_kvp_clean(kvp) and then frees kvp.
         2399  +*/
         2400  +static void cson_kvp_free( cson_kvp * kvp )
         2401  +{
         2402  +    if( kvp )
         2403  +    {
         2404  +        cson_kvp_clean(kvp);
         2405  +        cson_free(kvp,"cson_kvp");
         2406  +    }
         2407  +}
         2408  +
         2409  +
         2410  +/**
         2411  +   cson_value_api::destroy_value() impl for Object
         2412  +   values. Cleans up self-owned memory and overwrites
         2413  +   self to have the undefined value, but does not
         2414  +   free self.
         2415  +*/
         2416  +static void cson_value_destroy_object( cson_value * self )
         2417  +{
         2418  +    if(self && self->value) {
         2419  +        cson_object * obj = (cson_object *)self->value;
         2420  +        assert( self->value == obj );
         2421  +        cson_kvp_list_clean( &obj->kvp, cson_kvp_free );
         2422  +        *self = cson_value_undef;
         2423  +    }
         2424  +}
         2425  +
         2426  +/**
         2427  +   Cleans up the contents of ar->list, but does not free ar.
         2428  +
         2429  +   After calling this, ar will have a length of 0.
         2430  +
         2431  +   If properlyCleanValues is 1 then cson_value_free() is called on
         2432  +   each non-NULL item, otherwise the outer list is destroyed but the
         2433  +   individual items are assumed to be owned by someone else and are
         2434  +   not freed.
         2435  +*/
         2436  +static void cson_array_clean( cson_array * ar, char properlyCleanValues )
         2437  +{
         2438  +    if( ar )
         2439  +    {
         2440  +        unsigned int i = 0;
         2441  +        cson_value * val = NULL;
         2442  +        for( ; i < ar->list.count; ++i )
         2443  +        {
         2444  +            val = ar->list.list[i];
         2445  +            if(val)
         2446  +            {
         2447  +                ar->list.list[i] = NULL;
         2448  +                if( properlyCleanValues )
         2449  +                {
         2450  +                    cson_value_free( val );
         2451  +                }
         2452  +            }
         2453  +        }
         2454  +        cson_value_list_reserve(&ar->list,0);
         2455  +        ar->list = cson_value_list_empty
         2456  +            /* Pedantic note: reserve(0) already clears the list-specific
         2457  +               fields, but we do this just in case we ever add new fields
         2458  +               to cson_value_list which are not used in the reserve() impl.
         2459  +             */
         2460  +            ;
         2461  +    }
         2462  +}
         2463  +
         2464  +/**
         2465  +   cson_value_api::destroy_value() impl for Array
         2466  +   values. Cleans up self-owned memory and overwrites
         2467  +   self to have the undefined value, but does not
         2468  +   free self.
         2469  +*/
         2470  +static void cson_value_destroy_array( cson_value * self )
         2471  +{
         2472  +    cson_array * ar = cson_value_get_array(self);
         2473  +    if(ar) {
         2474  +        assert( self->value == ar );
         2475  +        cson_array_clean( ar, 1 );
         2476  +        *self = cson_value_undef;
         2477  +    }
         2478  +}
         2479  +
         2480  +int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state )
         2481  +{
         2482  +    int rc;
         2483  +    enum { BufSize = 1024 * 4 };
         2484  +    char rbuf[BufSize];
         2485  +    size_t total = 0;
         2486  +    unsigned int rlen = 0;
         2487  +    if( ! dest || ! src ) return cson_rc.ArgError;
         2488  +    dest->used = 0;
         2489  +    while(1)
         2490  +    {
         2491  +        rlen = BufSize;
         2492  +        rc = src( state, rbuf, &rlen );
         2493  +        if( rc ) break;
         2494  +        total += rlen;
         2495  +        if( dest->capacity < (total+1) )
         2496  +        {
         2497  +            rc = cson_buffer_reserve( dest, total + 1);
         2498  +            if( 0 != rc ) break;
         2499  +        }
         2500  +        memcpy( dest->mem + dest->used, rbuf, rlen );
         2501  +        dest->used += rlen;
         2502  +        if( rlen < BufSize ) break;
         2503  +    }
         2504  +    if( !rc && dest->used )
         2505  +    {
         2506  +        assert( dest->used < dest->capacity );
         2507  +        dest->mem[dest->used] = 0;
         2508  +    }
         2509  +    return rc;
         2510  +}
         2511  +
         2512  +int cson_data_source_FILE( void * state, void * dest, unsigned int * n )
         2513  +{
         2514  +    FILE * f = (FILE*) state;
         2515  +    if( ! state || ! n || !dest ) return cson_rc.ArgError;
         2516  +    else if( !*n ) return cson_rc.RangeError;
         2517  +    *n = (unsigned int)fread( dest, 1, *n, f );
         2518  +    if( !*n )
         2519  +    {
         2520  +        return feof(f) ? 0 : cson_rc.IOError;
         2521  +    }
         2522  +    return 0;
         2523  +}
         2524  +
         2525  +int cson_parse_FILE( cson_value ** tgt, FILE * src,
         2526  +                     cson_parse_opt const * opt, cson_parse_info * err )
         2527  +{
         2528  +    return cson_parse( tgt, cson_data_source_FILE, src, opt, err );
         2529  +}
         2530  +
         2531  +
         2532  +int cson_value_fetch_bool( cson_value const * val, char * v )
         2533  +{
         2534  +    /**
         2535  +       FIXME: move the to-bool operation into cson_value_api, like we
         2536  +       do in the C++ API.
         2537  +     */
         2538  +    if( ! val || !val->api ) return cson_rc.ArgError;
         2539  +    else
         2540  +    {
         2541  +        int rc = 0;
         2542  +        char b = 0;
         2543  +        switch( val->api->typeID )
         2544  +        {
         2545  +          case CSON_TYPE_ARRAY:
         2546  +          case CSON_TYPE_OBJECT:
         2547  +              b = 1;
         2548  +              break;
         2549  +          case CSON_TYPE_STRING: {
         2550  +              char const * str = cson_string_cstr(cson_value_get_string(val));
         2551  +              b = (str && *str) ? 1 : 0;
         2552  +              break;
         2553  +          }
         2554  +          case CSON_TYPE_UNDEF:
         2555  +          case CSON_TYPE_NULL:
         2556  +              break;
         2557  +          case CSON_TYPE_BOOL:
         2558  +              b = (NULL==val->value) ? 0 : 1;
         2559  +              break;
         2560  +          case CSON_TYPE_INTEGER: {
         2561  +              cson_int_t i = 0;
         2562  +              cson_value_fetch_integer( val, &i );
         2563  +              b = i ? 1 : 0;
         2564  +              break;
         2565  +          }
         2566  +          case CSON_TYPE_DOUBLE: {
         2567  +              cson_double_t d = 0.0;
         2568  +              cson_value_fetch_double( val, &d );
         2569  +              b = (0.0==d) ? 0 : 1;
         2570  +              break;
         2571  +          }
         2572  +          default:
         2573  +              rc = cson_rc.TypeError;
         2574  +              break;
         2575  +        }
         2576  +        if( v ) *v = b;
         2577  +        return rc;
         2578  +    }
         2579  +}
         2580  +
         2581  +char cson_value_get_bool( cson_value const * val )
         2582  +{
         2583  +    char i = 0;
         2584  +    cson_value_fetch_bool( val, &i );
         2585  +    return i;
         2586  +}
         2587  +
         2588  +int cson_value_fetch_integer( cson_value const * val, cson_int_t * v )
         2589  +{
         2590  +    if( ! val || !val->api ) return cson_rc.ArgError;
         2591  +    else
         2592  +    {
         2593  +        cson_int_t i = 0;
         2594  +        int rc = 0;
         2595  +        switch(val->api->typeID)
         2596  +        {
         2597  +            case CSON_TYPE_UNDEF: 
         2598  +            case CSON_TYPE_NULL:
         2599  +              i = 0;
         2600  +              break;
         2601  +            case CSON_TYPE_BOOL: {
         2602  +              char b = 0;
         2603  +              cson_value_fetch_bool( val, &b );
         2604  +              i = b;
         2605  +              break;
         2606  +            }
         2607  +            case CSON_TYPE_INTEGER: {
         2608  +                cson_int_t const * x = CSON_INT(val);
         2609  +                if(!x)
         2610  +                {
         2611  +                    assert( val == &CSON_SPECIAL_VALUES[CSON_VAL_INT_0] );
         2612  +                }
         2613  +                i = x ? *x : 0;
         2614  +                break;
         2615  +            }
         2616  +            case CSON_TYPE_DOUBLE: {
         2617  +              cson_double_t d = 0.0;
         2618  +              cson_value_fetch_double( val, &d );
         2619  +              i = (cson_int_t)d;
         2620  +              break;
         2621  +            }
         2622  +            case CSON_TYPE_STRING:
         2623  +            case CSON_TYPE_ARRAY:
         2624  +            case CSON_TYPE_OBJECT:
         2625  +            default:
         2626  +                rc = cson_rc.TypeError;
         2627  +                break;
         2628  +        }
         2629  +        if(!rc && v) *v = i;
         2630  +        return rc;
         2631  +    }
         2632  +}
         2633  +
         2634  +cson_int_t cson_value_get_integer( cson_value const * val )
         2635  +{
         2636  +    cson_int_t i = 0;
         2637  +    cson_value_fetch_integer( val, &i );
         2638  +    return i;
         2639  +}
         2640  +
         2641  +int cson_value_fetch_double( cson_value const * val, cson_double_t * v )
         2642  +{
         2643  +    if( ! val || !val->api ) return cson_rc.ArgError;
         2644  +    else
         2645  +    {
         2646  +        cson_double_t d = 0.0;
         2647  +        int rc = 0;
         2648  +        switch(val->api->typeID)
         2649  +        {
         2650  +          case CSON_TYPE_UNDEF: 
         2651  +          case CSON_TYPE_NULL:
         2652  +              d = 0;
         2653  +              break;
         2654  +          case CSON_TYPE_BOOL: {
         2655  +              char b = 0;
         2656  +              cson_value_fetch_bool( val, &b );
         2657  +              d = b ? 1.0 : 0.0;
         2658  +              break;
         2659  +          }
         2660  +          case CSON_TYPE_INTEGER: {
         2661  +              cson_int_t i = 0;
         2662  +              cson_value_fetch_integer( val, &i );
         2663  +              d = i;
         2664  +              break;
         2665  +          }
         2666  +          case CSON_TYPE_DOUBLE: {
         2667  +              cson_double_t const* dv = CSON_DBL(val);
         2668  +              d = dv ? *dv : 0.0;
         2669  +              break;
         2670  +          }
         2671  +          default:
         2672  +              rc = cson_rc.TypeError;
         2673  +              break;
         2674  +        }
         2675  +        if(v) *v = d;
         2676  +        return rc;
         2677  +    }
         2678  +}
         2679  +
         2680  +cson_double_t cson_value_get_double( cson_value const * val )
         2681  +{
         2682  +    cson_double_t i = 0.0;
         2683  +    cson_value_fetch_double( val, &i );
         2684  +    return i;
         2685  +}
         2686  +
         2687  +int cson_value_fetch_string( cson_value const * val, cson_string ** dest )
         2688  +{
         2689  +    if( ! val || ! dest ) return cson_rc.ArgError;
         2690  +    else if( ! cson_value_is_string(val) ) return cson_rc.TypeError;
         2691  +    else
         2692  +    {
         2693  +        if( dest ) *dest = CSON_STR(val);
         2694  +        return 0;
         2695  +    }
         2696  +}
         2697  +
         2698  +cson_string * cson_value_get_string( cson_value const * val )
         2699  +{
         2700  +    cson_string * rc = NULL;
         2701  +    cson_value_fetch_string( val, &rc );
         2702  +    return rc;
         2703  +}
         2704  +
         2705  +char const * cson_value_get_cstr( cson_value const * val )
         2706  +{
         2707  +    return cson_string_cstr( cson_value_get_string(val) );
         2708  +}
         2709  +
         2710  +int cson_value_fetch_object( cson_value const * val, cson_object ** obj )
         2711  +{
         2712  +    if( ! val ) return cson_rc.ArgError;
         2713  +    else if( ! cson_value_is_object(val) ) return cson_rc.TypeError;
         2714  +    else
         2715  +    {
         2716  +        if(obj) *obj = CSON_OBJ(val);
         2717  +        return 0;
         2718  +    }
         2719  +}
         2720  +cson_object * cson_value_get_object( cson_value const * v )
         2721  +{
         2722  +    cson_object * obj = NULL;
         2723  +    cson_value_fetch_object( v, &obj );
         2724  +    return obj;
         2725  +}
         2726  +
         2727  +int cson_value_fetch_array( cson_value const * val, cson_array ** ar)
         2728  +{
         2729  +    if( ! val ) return cson_rc.ArgError;
         2730  +    else if( !cson_value_is_array(val) ) return cson_rc.TypeError;
         2731  +    else
         2732  +    {
         2733  +        if(ar) *ar = CSON_ARRAY(val);
         2734  +        return 0;
         2735  +    }
         2736  +}
         2737  +
         2738  +cson_array * cson_value_get_array( cson_value const * v )
         2739  +{
         2740  +    cson_array * ar = NULL;
         2741  +    cson_value_fetch_array( v, &ar );
         2742  +    return ar;
         2743  +}
         2744  +
         2745  +cson_kvp * cson_kvp_alloc()
         2746  +{
         2747  +    cson_kvp * kvp = (cson_kvp*)cson_malloc(sizeof(cson_kvp),"cson_kvp");
         2748  +    if( kvp )
         2749  +    {
         2750  +        *kvp = cson_kvp_empty;
         2751  +    }
         2752  +    return kvp;
         2753  +}
         2754  +
         2755  +
         2756  +
         2757  +int cson_array_append( cson_array * ar, cson_value * v )
         2758  +{
         2759  +    if( !ar || !v ) return cson_rc.ArgError;
         2760  +    else if( (ar->list.count+1) < ar->list.count ) return cson_rc.RangeError;
         2761  +    else
         2762  +    {
         2763  +        if( !ar->list.alloced || (ar->list.count == ar->list.alloced-1))
         2764  +        {
         2765  +            unsigned int const n = ar->list.count ? (ar->list.count*2) : 7;
         2766  +            if( n > cson_value_list_reserve( &ar->list, n ) )
         2767  +            {
         2768  +                return cson_rc.AllocError;
         2769  +            }
         2770  +        }
         2771  +        return cson_array_set( ar, ar->list.count, v );
         2772  +    }
         2773  +}
         2774  +
         2775  +#if 0
         2776  +/**
         2777  +   Removes and returns the last value from the given array,
         2778  +   shrinking its size by 1. Returns NULL if ar is NULL,
         2779  +   ar->list.count is 0, or the element at that index is NULL.
         2780  +   
         2781  +
         2782  +   If removeRef is true then cson_value_free() is called to remove
         2783  +   ar's reference count for the value. In that case NULL is returned,
         2784  +   even if the object still has live references. If removeRef is false
         2785  +   then the caller takes over ownership of that reference count point.
         2786  +
         2787  +   If removeRef is false then the caller takes over ownership
         2788  +   of the return value, otherwise ownership is effectively
         2789  +   determined by any remaining references for the returned
         2790  +   value.
         2791  +*/
         2792  +static cson_value * cson_array_pop_back( cson_array * ar,
         2793  +                                         char removeRef )
         2794  +{
         2795  +    if( !ar ) return NULL;
         2796  +    else if( ! ar->list.count ) return NULL;
         2797  +    else
         2798  +    {
         2799  +        unsigned int const ndx = --ar->list.count;
         2800  +        cson_value * v = ar->list.list[ndx];
         2801  +        ar->list.list[ndx] = NULL;
         2802  +        if( removeRef )
         2803  +        {
         2804  +            cson_value_free( v );
         2805  +            v = NULL;
         2806  +        }
         2807  +        return v;
         2808  +    }
         2809  +}
         2810  +#endif
         2811  +
         2812  +cson_value * cson_value_new_bool( char v )
         2813  +{
         2814  +    return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
         2815  +}
         2816  +
         2817  +cson_value * cson_value_true()
         2818  +{
         2819  +    return &CSON_SPECIAL_VALUES[CSON_VAL_TRUE];
         2820  +}
         2821  +cson_value * cson_value_false()
         2822  +{
         2823  +    return &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
         2824  +}
         2825  +
         2826  +cson_value * cson_value_null()
         2827  +{
         2828  +    return &CSON_SPECIAL_VALUES[CSON_VAL_NULL];
         2829  +}
         2830  +
         2831  +cson_value * cson_new_int( cson_int_t v )
         2832  +{
         2833  +    return cson_value_new_integer(v);
         2834  +}
         2835  +
         2836  +cson_value * cson_value_new_integer( cson_int_t v )
         2837  +{
         2838  +    if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0];
         2839  +    else
         2840  +    {
         2841  +        cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0);
         2842  +#if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
         2843  +        assert( sizeof(cson_int_t) <= sizeof(void *) );
         2844  +#endif
         2845  +        if( c )
         2846  +        {
         2847  +            *CSON_INT(c) = v;
         2848  +        }
         2849  +        return c;
         2850  +    }
         2851  +}
         2852  +
         2853  +cson_value * cson_new_double( cson_double_t v )
         2854  +{
         2855  +    return cson_value_new_double(v);
         2856  +}
         2857  +
         2858  +cson_value * cson_value_new_double( cson_double_t v )
         2859  +{
         2860  +    if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0];
         2861  +    else
         2862  +    {
         2863  +        cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
         2864  +        if( c )
         2865  +        {
         2866  +            *CSON_DBL(c) = v;
         2867  +        }
         2868  +        return c;
         2869  +    }
         2870  +}
         2871  +
         2872  +cson_string * cson_new_string(char const * str, unsigned int len)
         2873  +{
         2874  +    if( !str || !*str || !len ) return &CSON_EMPTY_HOLDER.stringValue;
         2875  +    else
         2876  +    {
         2877  +        cson_value * c = cson_value_new(CSON_TYPE_STRING, len + 1/*NUL byte*/);
         2878  +        cson_string * s = NULL;
         2879  +        if( c )
         2880  +        {
         2881  +            char * dest = NULL;
         2882  +            s = CSON_STR(c);
         2883  +            *s = cson_string_empty;
         2884  +            assert( NULL != s );
         2885  +            s->length = len;
         2886  +            dest = cson_string_str(s);
         2887  +            assert( NULL != dest );
         2888  +            memcpy( dest, str, len );
         2889  +            dest[len] = 0;
         2890  +        }
         2891  +        return s;
         2892  +    }
         2893  +}
         2894  +
         2895  +cson_value * cson_value_new_string( char const * str, unsigned int len )
         2896  +{
         2897  +    return cson_string_value( cson_new_string(str, len) );
         2898  +}
         2899  +
         2900  +int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v )
         2901  +{
         2902  +    if( !ar) return cson_rc.ArgError;
         2903  +    if( pos >= ar->list.count ) return cson_rc.RangeError;
         2904  +    else
         2905  +    {
         2906  +        if(v) *v = ar->list.list[pos];
         2907  +        return 0;
         2908  +    }
         2909  +}
         2910  +
         2911  +cson_value * cson_array_get( cson_array const * ar, unsigned int pos )
         2912  +{
         2913  +    cson_value *v = NULL;
         2914  +    cson_array_value_fetch(ar, pos, &v);
         2915  +    return v;
         2916  +}
         2917  +
         2918  +int cson_array_length_fetch( cson_array const * ar, unsigned int * v )
         2919  +{
         2920  +    if( ! ar || !v ) return cson_rc.ArgError;
         2921  +    else
         2922  +    {
         2923  +        if(v) *v = ar->list.count;
         2924  +        return 0;
         2925  +    }
         2926  +}
         2927  +
         2928  +unsigned int cson_array_length_get( cson_array const * ar )
         2929  +{
         2930  +    unsigned int i = 0;
         2931  +    cson_array_length_fetch(ar, &i);
         2932  +    return i;
         2933  +}
         2934  +
         2935  +int cson_array_reserve( cson_array * ar, unsigned int size )
         2936  +{
         2937  +    if( ! ar ) return cson_rc.ArgError;
         2938  +    else if( size <= ar->list.alloced )
         2939  +    {
         2940  +        /* We don't want to introduce a can of worms by trying to
         2941  +           handle the cleanup from here.
         2942  +        */
         2943  +        return 0;
         2944  +    }
         2945  +    else
         2946  +    {
         2947  +        return (ar->list.alloced > cson_value_list_reserve( &ar->list, size ))
         2948  +            ? cson_rc.AllocError
         2949  +            : 0
         2950  +            ;
         2951  +    }
         2952  +}
         2953  +
         2954  +int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v )
         2955  +{
         2956  +    if( !ar || !v ) return cson_rc.ArgError;
         2957  +    else if( (ndx+1) < ndx) /* overflow */return cson_rc.RangeError;
         2958  +    else
         2959  +    {
         2960  +        unsigned const int len = cson_value_list_reserve( &ar->list, ndx+1 );
         2961  +        if( len <= ndx ) return cson_rc.AllocError;
         2962  +        else
         2963  +        {
         2964  +            cson_value * old = ar->list.list[ndx];
         2965  +            if( old )
         2966  +            {
         2967  +                if(old == v) return 0;
         2968  +                else cson_value_free(old);
         2969  +            }
         2970  +            cson_refcount_incr( v );
         2971  +            ar->list.list[ndx] = v;
         2972  +            if( ndx >= ar->list.count )
         2973  +            {
         2974  +                ar->list.count = ndx+1;
         2975  +            }
         2976  +            return 0;
         2977  +        }
         2978  +    }
         2979  +}
         2980  +
         2981  +/** @internal
         2982  +
         2983  +   Searchs for the given key in the given object.
         2984  +
         2985  +   Returns the found item on success, NULL on error.  If ndx is not
         2986  +   NULL, it is set to the index (in obj->kvp.list) of the found
         2987  +   item. *ndx is not modified if no entry is found.
         2988  +*/
         2989  +static cson_kvp * cson_object_search_impl( cson_object const * obj, char const * key, unsigned int * ndx )
         2990  +{
         2991  +    if( obj && key && *key && obj->kvp.count)
         2992  +    {
         2993  +#if CSON_OBJECT_PROPS_SORT
         2994  +        cson_kvp ** s = (cson_kvp**)
         2995  +            bsearch( key, obj->kvp.list,
         2996  +                     obj->kvp.count, sizeof(cson_kvp*),
         2997  +                     cson_kvp_cmp_vs_cstr );
         2998  +        if( ndx && s )
         2999  +        { /* index of found record is required by
         3000  +             cson_object_unset(). Calculate the offset based on s...*/
         3001  +#if 0
         3002  +            *ndx = (((unsigned char const *)s - ((unsigned char const *)obj->kvp.list))
         3003  +                   / sizeof(cson_kvp*));
         3004  +#else
         3005  +            *ndx = s - obj->kvp.list;
         3006  +#endif
         3007  +        }
         3008  +        return s ? *s : NULL;
         3009  +#else
         3010  +        cson_kvp_list const * li = &obj->kvp;
         3011  +        unsigned int i = 0;
         3012  +        cson_kvp * kvp;
         3013  +        const unsigned int klen = strlen(key);
         3014  +        for( ; i < li->count; ++i )
         3015  +        {
         3016  +            cson_string const * sKey;
         3017  +            kvp = li->list[i];
         3018  +            assert( kvp && kvp->key );
         3019  +            sKey = cson_value_get_string(kvp->key);
         3020  +            assert(sKey);
         3021  +            if( sKey->length != klen ) continue;
         3022  +            else if(0==strcmp(key,cson_string_cstr(sKey)))
         3023  +            {
         3024  +                if(ndx) *ndx = i;
         3025  +                return kvp;
         3026  +            }
         3027  +        }
         3028  +#endif
         3029  +    }
         3030  +    return NULL;
         3031  +}
         3032  +
         3033  +cson_value * cson_object_get( cson_object const * obj, char const * key )
         3034  +{
         3035  +    cson_kvp * kvp = cson_object_search_impl( obj, key, NULL );
         3036  +    return kvp ? kvp->value : NULL;
         3037  +}
         3038  +
         3039  +cson_value * cson_object_get_s( cson_object const * obj, cson_string const *key )
         3040  +{
         3041  +    cson_kvp * kvp = cson_object_search_impl( obj, cson_string_cstr(key), NULL );
         3042  +    return kvp ? kvp->value : NULL;
         3043  +}
         3044  +
         3045  +
         3046  +#if CSON_OBJECT_PROPS_SORT
         3047  +static void cson_object_sort_props( cson_object * obj )
         3048  +{
         3049  +    assert( NULL != obj );
         3050  +    if( obj->kvp.count )
         3051  +    {
         3052  +        qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
         3053  +               cson_kvp_cmp );
         3054  +    }
         3055  +
         3056  +}
         3057  +#endif    
         3058  +
         3059  +int cson_object_unset( cson_object * obj, char const * key )
         3060  +{
         3061  +    if( ! obj || !key || !*key ) return cson_rc.ArgError;
         3062  +    else
         3063  +    {
         3064  +        unsigned int ndx = 0;
         3065  +        cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx );
         3066  +        if( ! kvp )
         3067  +        {
         3068  +            return cson_rc.NotFoundError;
         3069  +        }
         3070  +        assert( obj->kvp.count > 0 );
         3071  +        assert( obj->kvp.list[ndx] == kvp );
         3072  +        cson_kvp_free( kvp );
         3073  +        obj->kvp.list[ndx] = NULL;
         3074  +        { /* if my brain were bigger i'd use memmove(). */
         3075  +            unsigned int i = ndx;
         3076  +            for( ; i < obj->kvp.count; ++i )
         3077  +            {
         3078  +                obj->kvp.list[i] =
         3079  +                    (i < (obj->kvp.alloced-1))
         3080  +                    ? obj->kvp.list[i+1]
         3081  +                    : NULL;
         3082  +            }
         3083  +        }
         3084  +        obj->kvp.list[--obj->kvp.count] = NULL;
         3085  +#if CSON_OBJECT_PROPS_SORT
         3086  +        cson_object_sort_props( obj );
         3087  +#endif
         3088  +        return 0;
         3089  +    }
         3090  +}
         3091  +
         3092  +int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v )
         3093  +{
         3094  +    if( !obj || !key ) return cson_rc.ArgError;
         3095  +    else if( NULL == v ) return cson_object_unset( obj, cson_string_cstr(key) );
         3096  +    else
         3097  +    {
         3098  +        char const * cKey;
         3099  +        cson_value * vKey;
         3100  +        cson_kvp * kvp;
         3101  +        vKey = cson_string_value(key);
         3102  +        assert(vKey && (key==CSON_STR(vKey)));
         3103  +        if( vKey == CSON_VCAST(obj) ){
         3104  +            return cson_rc.ArgError;
         3105  +        }
         3106  +        cKey =  cson_string_cstr(key);
         3107  +        kvp = cson_object_search_impl( obj, cKey, NULL );
         3108  +        if( kvp )
         3109  +        { /* "I told 'em we've already got one!" */
         3110  +            if( kvp->key != vKey ){
         3111  +                cson_value_free( kvp->key );
         3112  +                cson_refcount_incr(vKey);
         3113  +                kvp->key = vKey;
         3114  +            }
         3115  +            if(kvp->value != v){
         3116  +                cson_value_free( kvp->value );
         3117  +                cson_refcount_incr( v );
         3118  +                kvp->value = v;
         3119  +            }
         3120  +            return 0;
         3121  +        }
         3122  +        if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
         3123  +        { /* reserve space */
         3124  +            unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6;
         3125  +            if( n > cson_kvp_list_reserve( &obj->kvp, n ) )
         3126  +            {
         3127  +                return cson_rc.AllocError;
         3128  +            }
         3129  +        }
         3130  +        { /* insert new item... */
         3131  +            int rc = 0;
         3132  +            kvp = cson_kvp_alloc();
         3133  +            if( ! kvp )
         3134  +            {
         3135  +                return cson_rc.AllocError;
         3136  +            }
         3137  +            rc = cson_kvp_list_append( &obj->kvp, kvp );
         3138  +            if( 0 != rc )
         3139  +            {
         3140  +                cson_kvp_free(kvp);
         3141  +            }
         3142  +            else
         3143  +            {
         3144  +                cson_refcount_incr(vKey);
         3145  +                cson_refcount_incr(v);
         3146  +                kvp->key = vKey;
         3147  +                kvp->value = v;
         3148  +#if CSON_OBJECT_PROPS_SORT
         3149  +                cson_object_sort_props( obj );
         3150  +#endif
         3151  +            }
         3152  +            return rc;
         3153  +        }
         3154  +    }
         3155  +
         3156  +}
         3157  +int cson_object_set( cson_object * obj, char const * key, cson_value * v )
         3158  +{
         3159  +    if( ! obj || !key || !*key ) return cson_rc.ArgError;
         3160  +    else if( NULL == v )
         3161  +    {
         3162  +        return cson_object_unset( obj, key );
         3163  +    }
         3164  +    else
         3165  +    {
         3166  +        cson_string * cs = cson_new_string(key,strlen(key));
         3167  +        if(!cs) return cson_rc.AllocError;
         3168  +        else
         3169  +        {
         3170  +            int const rc = cson_object_set_s(obj, cs, v);
         3171  +            if(rc) cson_value_free(cson_string_value(cs));
         3172  +            return rc;
         3173  +        }
         3174  +    }
         3175  +}
         3176  +
         3177  +cson_value * cson_object_take( cson_object * obj, char const * key )
         3178  +{
         3179  +    if( ! obj || !key || !*key ) return NULL;
         3180  +    else
         3181  +    {
         3182  +        /* FIXME: this is 90% identical to cson_object_unset(),
         3183  +           only with different refcount handling.
         3184  +           Consolidate them.
         3185  +        */
         3186  +        unsigned int ndx = 0;
         3187  +        cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx );
         3188  +        cson_value * rc = NULL;
         3189  +        if( ! kvp )
         3190  +        {
         3191  +            return NULL;
         3192  +        }
         3193  +        assert( obj->kvp.count > 0 );
         3194  +        assert( obj->kvp.list[ndx] == kvp );
         3195  +        rc = kvp->value;
         3196  +        assert( rc );
         3197  +        kvp->value = NULL;
         3198  +        cson_kvp_free( kvp );
         3199  +        assert( rc->refcount > 0 );
         3200  +        --rc->refcount;
         3201  +        obj->kvp.list[ndx] = NULL;
         3202  +        { /* if my brain were bigger i'd use memmove(). */
         3203  +            unsigned int i = ndx;
         3204  +            for( ; i < obj->kvp.count; ++i )
         3205  +            {
         3206  +                obj->kvp.list[i] =
         3207  +                    (i < (obj->kvp.alloced-1))
         3208  +                    ? obj->kvp.list[i+1]
         3209  +                    : NULL;
         3210  +            }
         3211  +        }
         3212  +        obj->kvp.list[--obj->kvp.count] = NULL;
         3213  +#if CSON_OBJECT_PROPS_SORT
         3214  +        cson_object_sort_props( obj );
         3215  +#endif
         3216  +        return rc;
         3217  +    }
         3218  +}
         3219  +/** @internal
         3220  +
         3221  +   If p->node is-a Object then value is inserted into the object
         3222  +   using p->key. In any other case cson_rc.InternalError is returned.
         3223  +
         3224  +   Returns cson_rc.AllocError if an allocation fails.
         3225  +
         3226  +   Returns 0 on success. On error, parsing must be ceased immediately.
         3227  +   
         3228  +   Ownership of val is ALWAYS TRANSFERED to this function. If this
         3229  +   function fails, val will be cleaned up and destroyed. (This
         3230  +   simplifies error handling in the core parser.)
         3231  +*/
         3232  +static int cson_parser_set_key( cson_parser * p, cson_value * val )
         3233  +{
         3234  +    assert( p && val );
         3235  +
         3236  +    if( p->ckey && cson_value_is_object(p->node) )
         3237  +    {
         3238  +        int rc;
         3239  +        cson_object * obj = cson_value_get_object(p->node);
         3240  +        cson_kvp * kvp = NULL;
         3241  +        assert( obj && (p->node->value == obj) );
         3242  +        /**
         3243  +           FIXME? Use cson_object_set() instead of our custom
         3244  +           finagling with the object? We do it this way to avoid an
         3245  +           extra alloc/strcpy of the key data.
         3246  +        */
         3247  +        if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
         3248  +        {
         3249  +            if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) )
         3250  +            {
         3251  +                cson_value_free(val);
         3252  +                return cson_rc.AllocError;
         3253  +            }
         3254  +        }
         3255  +        kvp = cson_kvp_alloc();
         3256  +        if( ! kvp )
         3257  +        {
         3258  +            cson_value_free(val);
         3259  +            return cson_rc.AllocError;
         3260  +        }
         3261  +        kvp->key = cson_string_value(p->ckey)/*transfer ownership*/;
         3262  +        assert(0 == kvp->key->refcount);
         3263  +        cson_refcount_incr(kvp->key);
         3264  +        p->ckey = NULL;
         3265  +        kvp->value = val;
         3266  +        cson_refcount_incr( val );
         3267  +        rc = cson_kvp_list_append( &obj->kvp, kvp );
         3268  +        if( 0 != rc )
         3269  +        {
         3270  +            cson_kvp_free( kvp );
         3271  +        }
         3272  +        else
         3273  +        {
         3274  +            ++p->totalValueCount;
         3275  +        }
         3276  +        return rc;
         3277  +    }
         3278  +    else
         3279  +    {
         3280  +        if(val) cson_value_free(val);
         3281  +        return p->errNo = cson_rc.InternalError;
         3282  +    }
         3283  +
         3284  +}
         3285  +
         3286  +/** @internal
         3287  +
         3288  +    Pushes val into the current object/array parent node, depending on the
         3289  +    internal state of the parser.
         3290  +
         3291  +    Ownership of val is always transfered to this function, regardless of
         3292  +    success or failure.
         3293  +
         3294  +    Returns 0 on success. On error, parsing must be ceased immediately.
         3295  +*/
         3296  +static int cson_parser_push_value( cson_parser * p, cson_value * val )
         3297  +{
         3298  +    if( p->ckey )
         3299  +    { /* we're in Object mode */
         3300  +        assert( cson_value_is_object( p->node ) );
         3301  +        return cson_parser_set_key( p, val );
         3302  +    }
         3303  +    else if( cson_value_is_array( p->node ) )
         3304  +    { /* we're in Array mode */
         3305  +        cson_array * ar = cson_value_get_array( p->node );
         3306  +        int rc;
         3307  +        assert( ar && (ar == p->node->value) );
         3308  +        rc = cson_array_append( ar, val );
         3309  +        if( 0 != rc )
         3310  +        {
         3311  +            cson_value_free(val);
         3312  +        }
         3313  +        else
         3314  +        {
         3315  +            ++p->totalValueCount;
         3316  +        }
         3317  +        return rc;
         3318  +    }
         3319  +    else
         3320  +    { /* WTF? */
         3321  +        assert( 0 && "Internal error in cson_parser code" );
         3322  +        return p->errNo = cson_rc.InternalError;
         3323  +    }
         3324  +}
         3325  +
         3326  +/**
         3327  +   Callback for JSON_parser API. Reminder: it returns 0 (meaning false)
         3328  +   on error!
         3329  +*/
         3330  +static int cson_parse_callback( void * cx, int type, JSON_value const * value )
         3331  +{
         3332  +    cson_parser * p = (cson_parser *)cx;
         3333  +    int rc = 0;
         3334  +#define ALLOC_V(T,V) cson_value * v = cson_value_new_##T(V); if( ! v ) { rc = cson_rc.AllocError; break; }
         3335  +    switch(type) {
         3336  +      case JSON_T_ARRAY_BEGIN:
         3337  +      case JSON_T_OBJECT_BEGIN: {
         3338  +          cson_value * obja = (JSON_T_ARRAY_BEGIN == type)
         3339  +              ? cson_value_new_array()
         3340  +              : cson_value_new_object();
         3341  +          if( ! obja )
         3342  +          {
         3343  +              p->errNo = cson_rc.AllocError;
         3344  +              break;
         3345  +          }
         3346  +          if( 0 != rc ) break;
         3347  +          if( ! p->root )
         3348  +          {
         3349  +              p->root = p->node = obja;
         3350  +              rc = cson_array_append( &p->stack, obja );
         3351  +              if( 0 != rc )
         3352  +              { /* work around a (potential) corner case in the cleanup code. */
         3353  +                  cson_value_free( p->root );
         3354  +                  p->root = NULL;
         3355  +              }
         3356  +              else
         3357  +              {
         3358  +                  cson_refcount_incr( p->root )
         3359  +                      /* simplifies cleanup later on. */
         3360  +                      ;
         3361  +                  ++p->totalValueCount;
         3362  +              }
         3363  +          }
         3364  +          else
         3365  +          {
         3366  +              rc = cson_array_append( &p->stack, obja );
         3367  +              if(rc) cson_value_free( obja );
         3368  +              else
         3369  +              {
         3370  +                  rc = cson_parser_push_value( p, obja );
         3371  +                  if( 0 == rc ) p->node = obja;
         3372  +              }
         3373  +          }
         3374  +          break;
         3375  +      }
         3376  +      case JSON_T_ARRAY_END:
         3377  +      case JSON_T_OBJECT_END: {
         3378  +          if( 0 == p->stack.list.count )
         3379  +          {
         3380  +              rc = cson_rc.RangeError;
         3381  +              break;
         3382  +          }
         3383  +#if CSON_OBJECT_PROPS_SORT
         3384  +          if( cson_value_is_object(p->node) )
         3385  +          {/* kludge: the parser uses custom cson_object property
         3386  +              insertion as a malloc/strcpy-reduction optimization.
         3387  +              Because of that, we have to sort the property list
         3388  +              ourselves...
         3389  +           */
         3390  +              cson_object * obj = cson_value_get_object(p->node);
         3391  +              assert( NULL != obj );
         3392  +              cson_object_sort_props( obj );
         3393  +          }
         3394  +#endif
         3395  +
         3396  +#if 1
         3397  +          /* Reminder: do not use cson_array_pop_back( &p->stack )
         3398  +             because that will clean up the object, and we don't want
         3399  +             that.  We just want to forget this reference
         3400  +             to it. The object is either the root or was pushed into
         3401  +             an object/array in the parse tree (and is owned by that
         3402  +             object/array).
         3403  +          */
         3404  +          --p->stack.list.count;
         3405  +          assert( p->node == p->stack.list.list[p->stack.list.count] );
         3406  +          cson_refcount_decr( p->node )
         3407  +              /* p->node might be owned by an outer object but we
         3408  +                 need to remove the list's reference. For the
         3409  +                 root node we manually add a reference to
         3410  +                 avoid a special case here. Thus when we close
         3411  +                 the root node, its refcount is still 1.
         3412  +              */;
         3413  +          p->stack.list.list[p->stack.list.count] = NULL;
         3414  +          if( p->stack.list.count )
         3415  +          {
         3416  +              p->node = p->stack.list.list[p->stack.list.count-1];
         3417  +          }
         3418  +          else
         3419  +          {
         3420  +              p->node = p->root;
         3421  +          }
         3422  +#else
         3423  +          /*
         3424  +             Causing a leak?
         3425  +           */
         3426  +          cson_array_pop_back( &p->stack, 1 );
         3427  +          if( p->stack.list.count )
         3428  +          {
         3429  +              p->node = p->stack.list.list[p->stack.list.count-1];
         3430  +          }
         3431  +          else
         3432  +          {
         3433  +              p->node = p->root;
         3434  +          }
         3435  +          assert( p->node && (1==p->node->refcount) );
         3436  +#endif
         3437  +          break;
         3438  +      }
         3439  +      case JSON_T_INTEGER: {
         3440  +          ALLOC_V(integer, value->vu.integer_value );
         3441  +          rc = cson_parser_push_value( p, v );
         3442  +          break;
         3443  +      }
         3444  +      case JSON_T_FLOAT: {
         3445  +          ALLOC_V(double, value->vu.float_value );
         3446  +          rc =  cson_parser_push_value( p, v );
         3447  +          break;
         3448  +      }
         3449  +      case JSON_T_NULL: {
         3450  +          rc = cson_parser_push_value( p, cson_value_null() );
         3451  +          break;
         3452  +      }
         3453  +      case JSON_T_TRUE: {
         3454  +          rc = cson_parser_push_value( p, cson_value_true() );
         3455  +          break;
         3456  +      }
         3457  +      case JSON_T_FALSE: {
         3458  +          rc = cson_parser_push_value( p, cson_value_false() );
         3459  +          break;
         3460  +      }
         3461  +      case JSON_T_KEY: {
         3462  +          assert(!p->ckey);
         3463  +          p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length );
         3464  +          if( ! p->ckey )
         3465  +          {
         3466  +              rc = cson_rc.AllocError;
         3467  +              break;
         3468  +          }
         3469  +          ++p->totalKeyCount;
         3470  +          break;
         3471  +      }
         3472  +      case JSON_T_STRING: {
         3473  +          cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
         3474  +          rc = ( NULL == v ) 
         3475  +            ? cson_rc.AllocError
         3476  +            : cson_parser_push_value( p, v );
         3477  +          break;
         3478  +      }
         3479  +      default:
         3480  +          assert(0);
         3481  +          rc = cson_rc.InternalError;
         3482  +          break;
         3483  +    }
         3484  +#undef ALLOC_V
         3485  +    return ((p->errNo = rc)) ? 0 : 1;
         3486  +}
         3487  +
         3488  +
         3489  +/**
         3490  +   Converts a JSON_error code to one of the cson_rc values.
         3491  +*/
         3492  +static int cson_json_err_to_rc( JSON_error jrc )
         3493  +{
         3494  +    switch(jrc)
         3495  +    {
         3496  +      case JSON_E_NONE: return 0;
         3497  +      case JSON_E_INVALID_CHAR: return cson_rc.Parse_INVALID_CHAR;
         3498  +      case JSON_E_INVALID_KEYWORD: return cson_rc.Parse_INVALID_KEYWORD;
         3499  +      case JSON_E_INVALID_ESCAPE_SEQUENCE: return cson_rc.Parse_INVALID_ESCAPE_SEQUENCE;
         3500  +      case JSON_E_INVALID_UNICODE_SEQUENCE: return cson_rc.Parse_INVALID_UNICODE_SEQUENCE;
         3501  +      case JSON_E_INVALID_NUMBER: return cson_rc.Parse_INVALID_NUMBER;
         3502  +      case JSON_E_NESTING_DEPTH_REACHED: return cson_rc.Parse_NESTING_DEPTH_REACHED;
         3503  +      case JSON_E_UNBALANCED_COLLECTION: return cson_rc.Parse_UNBALANCED_COLLECTION;
         3504  +      case JSON_E_EXPECTED_KEY: return cson_rc.Parse_EXPECTED_KEY;
         3505  +      case JSON_E_EXPECTED_COLON: return cson_rc.Parse_EXPECTED_COLON;
         3506  +      case JSON_E_OUT_OF_MEMORY: return cson_rc.AllocError;
         3507  +      default:
         3508  +          return cson_rc.InternalError;
         3509  +    }
         3510  +}
         3511  +
         3512  +/** @internal
         3513  +
         3514  +   Cleans up all contents of p but does not free p.
         3515  +
         3516  +   To properly take over ownership of the parser's root node on a
         3517  +   successful parse:
         3518  +
         3519  +   - Copy p->root's pointer and set p->root to NULL.
         3520  +   - Eventually free up p->root with cson_value_free().
         3521  +   
         3522  +   If you do not set p->root to NULL, p->root will be freed along with
         3523  +   any other items inserted into it (or under it) during the parsing
         3524  +   process.
         3525  +*/
         3526  +static int cson_parser_clean( cson_parser * p )
         3527  +{
         3528  +    if( ! p ) return cson_rc.ArgError;
         3529  +    else
         3530  +    {
         3531  +        if( p->p )
         3532  +        {
         3533  +            delete_JSON_parser(p->p);
         3534  +            p->p = NULL;
         3535  +        }
         3536  +        if( p->ckey ){
         3537  +            cson_value_free(cson_string_value(p->ckey));
         3538  +        }
         3539  +        cson_array_clean( &p->stack, 1 );
         3540  +        if( p->root )
         3541  +        {
         3542  +            cson_value_free( p->root );
         3543  +        }
         3544  +        *p = cson_parser_empty;
         3545  +        return 0;
         3546  +    }
         3547  +}
         3548  +
         3549  +
         3550  +int cson_parse( cson_value ** tgt, cson_data_source_f src, void * state,
         3551  +                cson_parse_opt const * opt_, cson_parse_info * info_ )
         3552  +{
         3553  +    unsigned char ch[2] = {0,0};
         3554  +    cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
         3555  +    int rc = 0;
         3556  +    unsigned int len = 1;
         3557  +    cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
         3558  +    cson_parser p = cson_parser_empty;
         3559  +    if( ! tgt || ! src ) return cson_rc.ArgError;
         3560  +    
         3561  +    {
         3562  +        JSON_config jopt = {0};
         3563  +        init_JSON_config( &jopt );
         3564  +        jopt.allow_comments = opt.allowComments;
         3565  +        jopt.depth = opt.maxDepth;
         3566  +        jopt.callback_ctx = &p;
         3567  +        jopt.handle_floats_manually = 0;
         3568  +        jopt.callback = cson_parse_callback;
         3569  +        p.p = new_JSON_parser(&jopt);
         3570  +        if( ! p.p )
         3571  +        {
         3572  +            return cson_rc.AllocError;
         3573  +        }
         3574  +    }
         3575  +
         3576  +    do
         3577  +    { /* FIXME: buffer the input in multi-kb chunks. */
         3578  +        len = 1;
         3579  +        ch[0] = 0;
         3580  +        rc = src( state, ch, &len );
         3581  +        if( 0 != rc ) break;
         3582  +        else if( !len /* EOF */ ) break;
         3583  +        ++info.length;
         3584  +        if('\n' == ch[0])
         3585  +        {
         3586  +            ++info.line;
         3587  +            info.col = 0;
         3588  +        }
         3589  +        if( ! JSON_parser_char(p.p, ch[0]) )
         3590  +        {
         3591  +            rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
         3592  +            if(0==rc) rc = p.errNo;
         3593  +            if(0==rc) rc = cson_rc.InternalError;
         3594  +            info.errorCode = rc;
         3595  +            break;
         3596  +        }
         3597  +        if( '\n' != ch[0]) ++info.col;
         3598  +    } while(1);
         3599  +    if( info_ )
         3600  +    {
         3601  +        info.totalKeyCount = p.totalKeyCount;
         3602  +        info.totalValueCount = p.totalValueCount;
         3603  +        *info_ = info;
         3604  +    }
         3605  +    if( 0 != rc )
         3606  +    {
         3607  +        cson_parser_clean(&p);
         3608  +        return rc;
         3609  +    }
         3610  +    if( ! JSON_parser_done(p.p) )
         3611  +    {
         3612  +        rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
         3613  +        cson_parser_clean(&p);
         3614  +        if(0==rc) rc = p.errNo;
         3615  +        if(0==rc) rc = cson_rc.InternalError;
         3616  +    }
         3617  +    else
         3618  +    {
         3619  +        cson_value * root = p.root;
         3620  +        p.root = NULL;
         3621  +        cson_parser_clean(&p);
         3622  +        if( root )
         3623  +        {
         3624  +            assert( (1 == root->refcount) && "Detected memory mismanagement in the parser." );
         3625  +            root->refcount = 0
         3626  +                /* HUGE KLUDGE! Avoids having one too many references
         3627  +                   in some client code, leading to a leak. Here we're
         3628  +                   accommodating a memory management workaround in the
         3629  +                   parser code which manually adds a reference to the
         3630  +                   root node to keep it from being cleaned up
         3631  +                   prematurely.
         3632  +                */;
         3633  +            *tgt = root;
         3634  +        }
         3635  +        else
         3636  +        { /* then can happen on empty input. */
         3637  +            rc = cson_rc.UnknownError;
         3638  +        }
         3639  +    }
         3640  +    return rc;
         3641  +}
         3642  +
         3643  +/**
         3644  +   The UTF code was originally taken from sqlite3's public-domain
         3645  +   source code (http://sqlite.org), modified only slightly for use
         3646  +   here. This code generates some "possible data loss" warnings on
         3647  +   MSVC, but if this code is good enough for sqlite3 then it's damned
         3648  +   well good enough for me, so we disable that warning for Windows
         3649  +   builds.
         3650  +*/
         3651  +
         3652  +/*
         3653  +** This lookup table is used to help decode the first byte of
         3654  +** a multi-byte UTF8 character.
         3655  +*/
         3656  +static const unsigned char cson_utfTrans1[] = {
         3657  +  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         3658  +  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
         3659  +  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
         3660  +  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
         3661  +  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         3662  +  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
         3663  +  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         3664  +  0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
         3665  +};
         3666  +
         3667  +
         3668  +/*
         3669  +** Translate a single UTF-8 character.  Return the unicode value.
         3670  +**
         3671  +** During translation, assume that the byte that zTerm points
         3672  +** is a 0x00.
         3673  +**
         3674  +** Write a pointer to the next unread byte back into *pzNext.
         3675  +**
         3676  +** Notes On Invalid UTF-8:
         3677  +**
         3678  +**  *  This routine never allows a 7-bit character (0x00 through 0x7f) to
         3679  +**     be encoded as a multi-byte character.  Any multi-byte character that
         3680  +**     attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
         3681  +**
         3682  +**  *  This routine never allows a UTF16 surrogate value to be encoded.
         3683  +**     If a multi-byte character attempts to encode a value between
         3684  +**     0xd800 and 0xe000 then it is rendered as 0xfffd.
         3685  +**
         3686  +**  *  Bytes in the range of 0x80 through 0xbf which occur as the first
         3687  +**     byte of a character are interpreted as single-byte characters
         3688  +**     and rendered as themselves even though they are technically
         3689  +**     invalid characters.
         3690  +**
         3691  +**  *  This routine accepts an infinite number of different UTF8 encodings
         3692  +**     for unicode values 0x80 and greater.  It do not change over-length
         3693  +**     encodings to 0xfffd as some systems recommend.
         3694  +*/
         3695  +#define READ_UTF8(zIn, zTerm, c)                           \
         3696  +  c = *(zIn++);                                            \
         3697  +  if( c>=0xc0 ){                                           \
         3698  +    c = cson_utfTrans1[c-0xc0];                          \
         3699  +    while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){            \
         3700  +      c = (c<<6) + (0x3f & *(zIn++));                      \
         3701  +    }                                                      \
         3702  +    if( c<0x80                                             \
         3703  +        || (c&0xFFFFF800)==0xD800                          \
         3704  +        || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }        \
         3705  +  }
         3706  +static int cson_utf8Read(
         3707  +  const unsigned char *z,         /* First byte of UTF-8 character */
         3708  +  const unsigned char *zTerm,     /* Pretend this byte is 0x00 */
         3709  +  const unsigned char **pzNext    /* Write first byte past UTF-8 char here */
         3710  +){
         3711  +  int c;
         3712  +  READ_UTF8(z, zTerm, c);
         3713  +  *pzNext = z;
         3714  +  return c;
         3715  +}
         3716  +#undef READ_UTF8
         3717  +
         3718  +#ifdef _MSC_VER
         3719  +#  if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
         3720  +#    pragma warning( pop )
         3721  +#  endif
         3722  +#endif
         3723  +
         3724  +unsigned int cson_string_length_utf8( cson_string const * str )
         3725  +{
         3726  +    if( ! str ) return 0;
         3727  +    else
         3728  +    {
         3729  +        char unsigned const * pos = (char unsigned const *)cson_string_cstr(str);
         3730  +        char unsigned const * end = pos + str->length;
         3731  +        unsigned int rc = 0;
         3732  +        for( ; (pos < end) && cson_utf8Read(pos, end, &pos);
         3733  +            ++rc )
         3734  +        {
         3735  +        };
         3736  +        return rc;
         3737  +    }
         3738  +}
         3739  +
         3740  +/**
         3741  +   Escapes the first len bytes of the given string as JSON and sends
         3742  +   it to the given output function (which will be called often - once
         3743  +   for each logical character). The output is also surrounded by
         3744  +   double-quotes.
         3745  +
         3746  +   A NULL str will be escaped as an empty string, though we should
         3747  +   arguably export it as "null" (without quotes). We do this because
         3748  +   in JavaScript (typeof null === "object"), and by outputing null
         3749  +   here we would effectively change the data type from string to
         3750  +   object.
         3751  +*/
         3752  +static int cson_str_to_json( char const * str, unsigned int len,
         3753  +                             char escapeFwdSlash,
         3754  +                             cson_data_dest_f f, void * state )
         3755  +{
         3756  +    if( NULL == f ) return cson_rc.ArgError;
         3757  +    else if( !str || !*str || (0 == len) )
         3758  +    { /* special case for 0-length strings. */
         3759  +        return f( state, "\"\"", 2 );
         3760  +    }
         3761  +    else
         3762  +    {
         3763  +        unsigned char const * pos = (unsigned char const *)str;
         3764  +        unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
         3765  +        unsigned char const * next = NULL;
         3766  +        int ch;
         3767  +        unsigned char clen = 0;
         3768  +        char escChar[3] = {'\\',0,0};
         3769  +        enum { UBLen = 8 };
         3770  +        char ubuf[UBLen];
         3771  +        int rc = 0;
         3772  +        rc = f(state, "\"", 1 );
         3773  +        for( ; (pos < end) && (0 == rc); pos += clen )
         3774  +        {
         3775  +            ch = cson_utf8Read(pos, end, &next);
         3776  +            if( 0 == ch ) break;
         3777  +            assert( next > pos );
         3778  +            clen = next - pos;
         3779  +            assert( clen );
         3780  +            if( 1 == clen )
         3781  +            { /* ASCII */
         3782  +                assert( *pos == ch );
         3783  +                escChar[1] = 0;
         3784  +                switch(ch)
         3785  +                {
         3786  +                  case '\t': escChar[1] = 't'; break;
         3787  +                  case '\r': escChar[1] = 'r'; break;
         3788  +                  case '\n': escChar[1] = 'n'; break;
         3789  +                  case '\f': escChar[1] = 'f'; break;
         3790  +                  case '\b': escChar[1] = 'b'; break;
         3791  +                  case '/':
         3792  +      /*
         3793  +        Regarding escaping of forward-slashes. See the main exchange below...
         3794  +
         3795  +        --------------
         3796  +        From: Douglas Crockford <douglas@crockford.com>
         3797  +        To: Stephan Beal <sgbeal@googlemail.com>
         3798  +        Subject: Re: Is escaping of forward slashes required?
         3799  +
         3800  +        It is allowed, not required. It is allowed so that JSON can be safely
         3801  +        embedded in HTML, which can freak out when seeing strings containing
         3802  +        "</". JSON tolerates "<\/" for this reason.
         3803  +
         3804  +        On 4/8/2011 2:09 PM, Stephan Beal wrote:
         3805  +        > Hello, Jsonites,
         3806  +        >
         3807  +        > i'm a bit confused on a small grammatic detail of JSON:
         3808  +        >
         3809  +        > if i'm reading the grammar chart on http://www.json.org/ correctly,
         3810  +        > forward slashes (/) are supposed to be escaped in JSON. However, the
         3811  +        > JSON class provided with my browsers (Chrome and FF, both of which i
         3812  +        > assume are fairly standards/RFC-compliant) do not escape such characters.
         3813  +        >
         3814  +        > Is backslash-escaping forward slashes required? If so, what is the
         3815  +        > justification for it? (i ask because i find it unnecessary and hard to
         3816  +        > look at.)
         3817  +        --------------
         3818  +      */
         3819  +                      if( escapeFwdSlash ) escChar[1] = '/';
         3820  +                      break;
         3821  +                  case '\\': escChar[1] = '\\'; break;
         3822  +                  case '"': escChar[1] = '"'; break;
         3823  +                  default: break;
         3824  +                }
         3825  +                if( escChar[1])
         3826  +                {
         3827  +                    rc = f(state, escChar, 2);
         3828  +                }
         3829  +                else
         3830  +                {
         3831  +                    rc = f(state, (char const *)pos, clen);
         3832  +                }
         3833  +                continue;
         3834  +            }
         3835  +            else
         3836  +            { /* UTF: transform it to \uXXXX */
         3837  +                memset(ubuf,0,UBLen);
         3838  +                rc = sprintf(ubuf, "\\u%04x",ch);
         3839  +                if( rc != 6 )
         3840  +                {
         3841  +                    rc = cson_rc.RangeError;
         3842  +                    break;
         3843  +                }
         3844  +                rc = f( state, ubuf, 6 );
         3845  +                continue;
         3846  +            }
         3847  +        }
         3848  +        if( 0 == rc )
         3849  +        {
         3850  +            rc = f(state, "\"", 1 );
         3851  +        }
         3852  +        return rc;
         3853  +    }
         3854  +}
         3855  +
         3856  +int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter )
         3857  +{
         3858  +    if( ! obj || !iter ) return cson_rc.ArgError;
         3859  +    else
         3860  +    {
         3861  +        iter->obj = obj;
         3862  +        iter->pos = 0;
         3863  +        return 0;
         3864  +    }
         3865  +}
         3866  +
         3867  +cson_kvp * cson_object_iter_next( cson_object_iterator * iter )
         3868  +{
         3869  +    if( ! iter || !iter->obj ) return NULL;
         3870  +    else if( iter->pos >= iter->obj->kvp.count ) return NULL;
         3871  +    else
         3872  +    {
         3873  +        cson_kvp * rc = iter->obj->kvp.list[iter->pos++];
         3874  +        while( (NULL==rc) && (iter->pos < iter->obj->kvp.count))
         3875  +        {
         3876  +            rc = iter->obj->kvp.list[iter->pos++];
         3877  +        }
         3878  +        return rc;
         3879  +    }
         3880  +}
         3881  +
         3882  +static int cson_output_null( cson_data_dest_f f, void * state )
         3883  +{
         3884  +    if( !f ) return cson_rc.ArgError;
         3885  +    else
         3886  +    {
         3887  +        return f(state, "null", 4);
         3888  +    }
         3889  +}
         3890  +
         3891  +static int cson_output_bool( cson_value const * src, cson_data_dest_f f, void * state )
         3892  +{
         3893  +    if( !f ) return cson_rc.ArgError;
         3894  +    else
         3895  +    {
         3896  +        char const v = cson_value_get_bool(src);
         3897  +        return f(state, v ? "true" : "false", v ? 4 : 5);
         3898  +    }
         3899  +}
         3900  +
         3901  +static int cson_output_integer( cson_value const * src, cson_data_dest_f f, void * state )
         3902  +{
         3903  +    if( !f ) return cson_rc.ArgError;
         3904  +    else if( !cson_value_is_integer(src) ) return cson_rc.TypeError;
         3905  +    else
         3906  +    {
         3907  +        enum { BufLen = 100 };
         3908  +        char b[BufLen];
         3909  +        int rc;
         3910  +        memset( b, 0, BufLen );
         3911  +        rc = sprintf( b, "%"CSON_INT_T_PFMT, cson_value_get_integer(src) )
         3912  +            /* Reminder: snprintf() is C99 */
         3913  +            ;
         3914  +        return ( rc<=0 )
         3915  +            ? cson_rc.RangeError
         3916  +            : f( state, b, (unsigned int)rc )
         3917  +            ;
         3918  +    }
         3919  +}
         3920  +
         3921  +static int cson_output_double( cson_value const * src, cson_data_dest_f f, void * state )
         3922  +{
         3923  +    if( !f ) return cson_rc.ArgError;
         3924  +    else if( !cson_value_is_double(src) ) return cson_rc.TypeError;
         3925  +    else
         3926  +    {
         3927  +        enum { BufLen = 128 /* this must be relatively large or huge
         3928  +                               doubles can cause us to overrun here,
         3929  +                               resulting in stack-smashing errors.
         3930  +                            */};
         3931  +        char b[BufLen];
         3932  +        int rc;
         3933  +        memset( b, 0, BufLen );
         3934  +        rc = sprintf( b, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(src) )
         3935  +            /* Reminder: snprintf() is C99 */
         3936  +            ;
         3937  +        if( rc<=0 ) return cson_rc.RangeError;
         3938  +        else if(1)
         3939  +        { /* Strip trailing zeroes before passing it on... */
         3940  +            unsigned int urc = (unsigned int)rc;
         3941  +            char * pos = b + urc - 1;
         3942  +            for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
         3943  +            {
         3944  +                *pos = 0;
         3945  +            }
         3946  +            assert(urc && *pos);
         3947  +            return f( state, b, urc );
         3948  +        }
         3949  +        else
         3950  +        {
         3951  +            unsigned int urc = (unsigned int)rc;
         3952  +            return f( state, b, urc );
         3953  +        }
         3954  +        return 0;
         3955  +    }
         3956  +}
         3957  +
         3958  +static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state )
         3959  +{
         3960  +    if( !f ) return cson_rc.ArgError;
         3961  +    else if( ! cson_value_is_string(src) ) return cson_rc.TypeError;
         3962  +    else
         3963  +    {
         3964  +        cson_string const * str = cson_value_get_string(src);
         3965  +        assert( NULL != str );
         3966  +        return cson_str_to_json(cson_string_cstr(str), str->length, escapeFwdSlash, f, state);
         3967  +    }
         3968  +}
         3969  +
         3970  +
         3971  +/**
         3972  +   Outputs indention spacing to f().
         3973  +
         3974  +   blanks: (0)=no indentation, (1)=1 TAB per/level, (>1)=n spaces/level
         3975  +
         3976  +   depth is the current depth of the output tree, and determines how much
         3977  +   indentation to generate.
         3978  +
         3979  +   If blanks is 0 this is a no-op. Returns non-0 on error, and the
         3980  +   error code will always come from f().
         3981  +*/
         3982  +static int cson_output_indent( cson_data_dest_f f, void * state,
         3983  +                               unsigned char blanks, unsigned int depth )
         3984  +{
         3985  +    if( 0 == blanks ) return 0;
         3986  +    else
         3987  +    {
         3988  +#if 0
         3989  +        /* FIXME: stuff the indention into the buffer and make a single
         3990  +           call to f().
         3991  +        */
         3992  +        enum { BufLen = 200 };
         3993  +        char buf[BufLen];
         3994  +#endif
         3995  +        unsigned int i;
         3996  +        unsigned int x;
         3997  +        char const ch = (1==blanks) ? '\t' : ' ';
         3998  +        int rc = f(state, "\n", 1 );
         3999  +        for( i = 0; (i < depth) && (0 == rc); ++i )
         4000  +        {
         4001  +            for( x = 0; (x < blanks) && (0 == rc); ++x )
         4002  +            {
         4003  +                rc = f(state, &ch, 1);
         4004  +            }
         4005  +        }
         4006  +        return rc;
         4007  +    }
         4008  +}
         4009  +
         4010  +static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state,
         4011  +                              cson_output_opt const * fmt, unsigned int level );
         4012  +static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
         4013  +                               cson_output_opt const * fmt, unsigned int level );
         4014  +/**
         4015  +   Main cson_output() implementation. Dispatches to a different impl depending
         4016  +   on src->api->typeID.
         4017  +
         4018  +   Returns 0 on success.
         4019  +*/
         4020  +static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state,
         4021  +                             cson_output_opt const * fmt, unsigned int level )
         4022  +{
         4023  +    if( ! src || !f || !src->api ) return cson_rc.ArgError;
         4024  +    else
         4025  +    {
         4026  +        int rc = 0;
         4027  +        assert(fmt);
         4028  +        switch( src->api->typeID )
         4029  +        {
         4030  +          case CSON_TYPE_UNDEF:
         4031  +          case CSON_TYPE_NULL:
         4032  +              rc = cson_output_null(f, state);
         4033  +              break;
         4034  +          case CSON_TYPE_BOOL:
         4035  +              rc = cson_output_bool(src, f, state);
         4036  +              break;
         4037  +          case CSON_TYPE_INTEGER:
         4038  +              rc = cson_output_integer(src, f, state);
         4039  +              break;
         4040  +          case CSON_TYPE_DOUBLE:
         4041  +              rc = cson_output_double(src, f, state);
         4042  +              break;
         4043  +          case CSON_TYPE_STRING:
         4044  +              rc = cson_output_string(src, fmt->escapeForwardSlashes, f, state);
         4045  +              break;
         4046  +          case CSON_TYPE_ARRAY:
         4047  +              rc = cson_output_array( src, f, state, fmt, level );
         4048  +              break;
         4049  +          case CSON_TYPE_OBJECT:
         4050  +              rc = cson_output_object( src, f, state, fmt, level );
         4051  +              break;
         4052  +          default:
         4053  +              rc = cson_rc.TypeError;
         4054  +              break;
         4055  +        }
         4056  +        return rc;
         4057  +    }
         4058  +}
         4059  +
         4060  +
         4061  +static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state,
         4062  +                              cson_output_opt const * fmt, unsigned int level )
         4063  +{
         4064  +    if( !src || !f || !fmt ) return cson_rc.ArgError;
         4065  +    else if( ! cson_value_is_array(src) ) return cson_rc.TypeError;
         4066  +    else if( level > fmt->maxDepth ) return cson_rc.RangeError;
         4067  +    else
         4068  +    {
         4069  +        int rc;
         4070  +        unsigned int i;
         4071  +        cson_value const * v;
         4072  +        char doIndent = fmt->indentation ? 1 : 0;
         4073  +        cson_array const * ar = cson_value_get_array(src);
         4074  +        assert( NULL != ar );
         4075  +        if( 0 == ar->list.count )
         4076  +        {
         4077  +            return f(state, "[]", 2 );
         4078  +        }
         4079  +        else if( (1 == ar->list.count) && !fmt->indentSingleMemberValues ) doIndent = 0;
         4080  +        rc = f(state, "[", 1);
         4081  +        ++level;
         4082  +        if( doIndent )
         4083  +        {
         4084  +            rc = cson_output_indent( f, state, fmt->indentation, level );
         4085  +        }
         4086  +        for( i = 0; (i < ar->list.count) && (0 == rc); ++i )
         4087  +        {
         4088  +            v = ar->list.list[i];
         4089  +            if( v )
         4090  +            {
         4091  +                rc = cson_output_impl( v, f, state, fmt, level );
         4092  +            }
         4093  +            else
         4094  +            {
         4095  +                rc = cson_output_null( f, state );
         4096  +            }
         4097  +            if( 0 == rc )
         4098  +            {
         4099  +                if(i < (ar->list.count-1))
         4100  +                {
         4101  +                    rc = f(state, ",", 1);
         4102  +                    if( 0 == rc )
         4103  +                    {
         4104  +                        rc = doIndent
         4105  +                            ? cson_output_indent( f, state, fmt->indentation, level )
         4106  +                            : f( state, " ", 1 );
         4107  +                    }
         4108  +                }
         4109  +            }
         4110  +        }
         4111  +        --level;
         4112  +        if( doIndent && (0 == rc) )
         4113  +        {
         4114  +            rc = cson_output_indent( f, state, fmt->indentation, level );
         4115  +        }
         4116  +        return (0 == rc)
         4117  +            ? f(state, "]", 1)
         4118  +            : rc;
         4119  +    }
         4120  +}
         4121  +
         4122  +static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
         4123  +                               cson_output_opt const * fmt, unsigned int level )
         4124  +{
         4125  +    if( !src || !f || !fmt ) return cson_rc.ArgError;
         4126  +    else if( ! cson_value_is_object(src) ) return cson_rc.TypeError;
         4127  +    else if( level > fmt->maxDepth ) return cson_rc.RangeError;
         4128  +    else
         4129  +    {
         4130  +        int rc;
         4131  +        unsigned int i;
         4132  +        cson_kvp const * kvp;
         4133  +        char doIndent = fmt->indentation ? 1 : 0;
         4134  +        cson_object const * obj = cson_value_get_object(src);
         4135  +        assert( (NULL != obj) && (NULL != fmt));
         4136  +        if( 0 == obj->kvp.count )
         4137  +        {
         4138  +            return f(state, "{}", 2 );
         4139  +        }
         4140  +        else if( (1 == obj->kvp.count) && !fmt->indentSingleMemberValues ) doIndent = 0;
         4141  +        rc = f(state, "{", 1);
         4142  +        ++level;
         4143  +        if( doIndent )
         4144  +        {
         4145  +            rc = cson_output_indent( f, state, fmt->indentation, level );
         4146  +        }
         4147  +        for( i = 0; (i < obj->kvp.count) && (0 == rc); ++i )
         4148  +        {
         4149  +            kvp = obj->kvp.list[i];
         4150  +            if( kvp && kvp->key )
         4151  +            {
         4152  +                cson_string const * sKey = cson_value_get_string(kvp->key);
         4153  +                char const * cKey = cson_string_cstr(sKey);
         4154  +                rc = cson_str_to_json(cKey, sKey->length,
         4155  +                                      fmt->escapeForwardSlashes, f, state);
         4156  +                if( 0 == rc )
         4157  +                {
         4158  +                    rc = fmt->addSpaceAfterColon
         4159  +                        ? f(state, ": ", 2 )
         4160  +                        : f(state, ":", 1 )
         4161  +                        ;
         4162  +                }
         4163  +                if( 0 == rc)
         4164  +                {
         4165  +                    rc = ( kvp->value )
         4166  +                        ? cson_output_impl( kvp->value, f, state, fmt, level )
         4167  +                        : cson_output_null( f, state );
         4168  +                }
         4169  +            }
         4170  +            else
         4171  +            {
         4172  +                assert( 0 && "Possible internal error." );
         4173  +                continue /* internal error? */;
         4174  +            }
         4175  +            if( 0 == rc )
         4176  +            {
         4177  +                if(i < (obj->kvp.count-1))
         4178  +                {
         4179  +                    rc = f(state, ",", 1);
         4180  +                    if( 0 == rc )
         4181  +                    {
         4182  +                        rc = doIndent
         4183  +                            ? cson_output_indent( f, state, fmt->indentation, level )
         4184  +                            : f( state, " ", 1 );
         4185  +                    }
         4186  +                }
         4187  +            }
         4188  +        }
         4189  +        --level;
         4190  +        if( doIndent && (0 == rc) )
         4191  +        {
         4192  +            rc = cson_output_indent( f, state, fmt->indentation, level );
         4193  +        }
         4194  +        return (0 == rc)
         4195  +            ? f(state, "}", 1)
         4196  +            : rc;
         4197  +    }
         4198  +}
         4199  +
         4200  +int cson_output( cson_value const * src, cson_data_dest_f f,
         4201  +                 void * state, cson_output_opt const * fmt )
         4202  +{
         4203  +    int rc;
         4204  +    if(! fmt ) fmt = &cson_output_opt_empty;
         4205  +    rc = cson_output_impl(src, f, state, fmt, 0 );
         4206  +    if( (0 == rc) && fmt->addNewline )
         4207  +    {
         4208  +        rc = f(state, "\n", 1);
         4209  +    }
         4210  +    return rc;
         4211  +}
         4212  +
         4213  +int cson_data_dest_FILE( void * state, void const * src, unsigned int n )
         4214  +{
         4215  +    if( ! state ) return cson_rc.ArgError;
         4216  +    else if( !src || !n ) return 0;
         4217  +    else
         4218  +    {
         4219  +        return ( 1 == fwrite( src, n, 1, (FILE*) state ) )
         4220  +            ? 0
         4221  +            : cson_rc.IOError;
         4222  +    }
         4223  +}
         4224  +
         4225  +int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * fmt )
         4226  +{
         4227  +    int rc = 0;
         4228  +    if( fmt )
         4229  +    {
         4230  +        rc = cson_output( src, cson_data_dest_FILE, dest, fmt );
         4231  +    }
         4232  +    else
         4233  +    {
         4234  +        /* We normally want a newline on FILE output. */
         4235  +        cson_output_opt opt = cson_output_opt_empty;
         4236  +        opt.addNewline = 1;
         4237  +        rc = cson_output( src, cson_data_dest_FILE, dest, &opt );
         4238  +    }
         4239  +    if( 0 == rc )
         4240  +    {
         4241  +        fflush( dest );
         4242  +    }
         4243  +    return rc;
         4244  +}
         4245  +
         4246  +int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt )
         4247  +{
         4248  +    if( !src || !dest ) return cson_rc.ArgError;
         4249  +    else
         4250  +    {
         4251  +        FILE * f = fopen(dest,"wb");
         4252  +        if( !f ) return cson_rc.IOError;
         4253  +        else
         4254  +        {
         4255  +            int const rc = cson_output_FILE( src, f, fmt );
         4256  +            fclose(f);
         4257  +            return rc;
         4258  +        }
         4259  +    }
         4260  +}
         4261  +
         4262  +int cson_parse_filename( cson_value ** tgt, char const * src,
         4263  +                         cson_parse_opt const * opt, cson_parse_info * err )
         4264  +{
         4265  +    if( !src || !tgt ) return cson_rc.ArgError;
         4266  +    else
         4267  +    {
         4268  +        FILE * f = fopen(src, "r");
         4269  +        if( !f ) return cson_rc.IOError;
         4270  +        else
         4271  +        {
         4272  +            int const rc = cson_parse_FILE( tgt, f, opt, err );
         4273  +            fclose(f);
         4274  +            return rc;
         4275  +        }
         4276  +    }
         4277  +}
         4278  +
         4279  +/** Internal type to hold state for a JSON input string.
         4280  + */
         4281  +typedef struct cson_data_source_StringSource_
         4282  +{
         4283  +    /** Start of input string. */
         4284  +    char const * str;
         4285  +    /** Current iteration position. Must initially be == str. */
         4286  +    char const * pos;
         4287  +    /** Logical EOF, one-past-the-end of str. */
         4288  +    char const * end;
         4289  +}  cson_data_source_StringSource_t;
         4290  +
         4291  +/**
         4292  +   A cson_data_source_f() implementation which requires the state argument
         4293  +   to be a properly populated (cson_data_source_StringSource_t*).
         4294  +*/
         4295  +static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n )
         4296  +{
         4297  +    if( !state || !n || !dest ) return cson_rc.ArgError;
         4298  +    else if( !*n ) return 0 /* ignore this */;
         4299  +    else
         4300  +    {
         4301  +        unsigned int i;
         4302  +        cson_data_source_StringSource_t * ss = (cson_data_source_StringSource_t*) state;
         4303  +        unsigned char * tgt = (unsigned char *)dest;
         4304  +        for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt )
         4305  +        {
         4306  +            *tgt = *ss->pos;
         4307  +        }
         4308  +        *n = i;
         4309  +        return 0;
         4310  +    }
         4311  +}
         4312  +
         4313  +int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len,
         4314  +                       cson_parse_opt const * opt, cson_parse_info * err )
         4315  +{
         4316  +    if( ! tgt || !src ) return cson_rc.ArgError;
         4317  +    else if( !*src || (len<2/*2==len of {} and []*/) ) return cson_rc.RangeError;
         4318  +    else
         4319  +    {
         4320  +        cson_data_source_StringSource_t ss;
         4321  +        ss.str = ss.pos = src;
         4322  +        ss.end = src + len;
         4323  +        return cson_parse( tgt, cson_data_source_StringSource, &ss, opt, err );
         4324  +    }
         4325  +
         4326  +}
         4327  +
         4328  +int cson_parse_buffer( cson_value ** tgt,
         4329  +                       cson_buffer const * buf,
         4330  +                       cson_parse_opt const * opt,
         4331  +                       cson_parse_info * err )
         4332  +{
         4333  +    return ( !tgt || !buf || !buf->mem || !buf->used )
         4334  +        ? cson_rc.ArgError
         4335  +        : cson_parse_string( tgt, (char const *)buf->mem,
         4336  +                             buf->used, opt, err );
         4337  +}
         4338  +
         4339  +int cson_buffer_reserve( cson_buffer * buf, cson_size_t n )
         4340  +{
         4341  +    if( ! buf ) return cson_rc.ArgError;
         4342  +    else if( 0 == n )
         4343  +    {
         4344  +        cson_free(buf->mem, "cson_buffer::mem");
         4345  +        *buf = cson_buffer_empty;
         4346  +        return 0;
         4347  +    }
         4348  +    else if( buf->capacity >= n )
         4349  +    {
         4350  +        return 0;
         4351  +    }
         4352  +    else
         4353  +    {
         4354  +        unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" );
         4355  +        if( ! x ) return cson_rc.AllocError;
         4356  +        memset( x + buf->used, 0, n - buf->used );
         4357  +        buf->mem = x;
         4358  +        buf->capacity = n;
         4359  +        ++buf->timesExpanded;
         4360  +        return 0;
         4361  +    }
         4362  +}
         4363  +
         4364  +cson_size_t cson_buffer_fill( cson_buffer * buf, char c )
         4365  +{
         4366  +    if( !buf || !buf->capacity || !buf->mem ) return 0;
         4367  +    else
         4368  +    {
         4369  +        memset( buf->mem, c, buf->capacity );
         4370  +        return buf->capacity;
         4371  +    }
         4372  +}
         4373  +
         4374  +/**
         4375  +   cson_data_dest_f() implementation, used by cson_output_buffer().
         4376  +
         4377  +   arg MUST be a (cson_buffer*). This function appends n bytes at
         4378  +   position arg->used, expanding the buffer as necessary.
         4379  +*/
         4380  +static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
         4381  +{
         4382  +    if( !arg ) return cson_rc.ArgError;
         4383  +    else if( ! n ) return 0;
         4384  +    else
         4385  +    {
         4386  +        cson_buffer * sb = (cson_buffer*)arg;
         4387  +        char const * data = (char const *)data_;
         4388  +        cson_size_t npos = sb->used + n;
         4389  +        unsigned int i;
         4390  +        if( npos >= sb->capacity )
         4391  +        {
         4392  +            const cson_size_t oldCap = sb->capacity;
         4393  +            const cson_size_t asz = npos * 2;
         4394  +            if( asz < npos ) return cson_rc.ArgError; /* overflow */
         4395  +            else if( 0 != cson_buffer_reserve( sb, asz ) ) return cson_rc.AllocError;
         4396  +            assert( (sb->capacity > oldCap) && "Internal error in memory buffer management!" );
         4397  +            /* make sure it gets NULL terminated. */
         4398  +            memset( sb->mem + oldCap, 0, (sb->capacity - oldCap) );
         4399  +        }
         4400  +        for( i = 0; i < n; ++i, ++sb->used )
         4401  +        {
         4402  +            sb->mem[sb->used] = data[i];
         4403  +        }
         4404  +        return 0;
         4405  +    }
         4406  +}
         4407  +
         4408  +
         4409  +int cson_output_buffer( cson_value const * v, cson_buffer * buf,
         4410  +                        cson_output_opt const * opt )
         4411  +{
         4412  +    int rc = cson_output( v, cson_data_dest_cson_buffer, buf, opt );
         4413  +    if( 0 == rc )
         4414  +    { /* Ensure that the buffer is null-terminated. */
         4415  +        rc = cson_buffer_reserve( buf, buf->used + 1 );
         4416  +        if( 0 == rc )
         4417  +        {
         4418  +            buf->mem[buf->used] = 0;
         4419  +        }
         4420  +    }
         4421  +    return rc;
         4422  +}
         4423  +
         4424  +/** @internal
         4425  +
         4426  +Tokenizes an input string on a given separator. Inputs are:
         4427  +
         4428  +- (inp) = is a pointer to the pointer to the start of the input.
         4429  +
         4430  +- (separator) = the separator character
         4431  +
         4432  +- (end) = a pointer to NULL. i.e. (*end == NULL)
         4433  +
         4434  +This function scans *inp for the given separator char or a NUL char.
         4435  +Successive separators at the start of *inp are skipped. The effect is
         4436  +that, when this function is called in a loop, all neighboring
         4437  +separators are ignored. e.g. the string "aa.bb...cc" will tokenize to
         4438  +the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the
         4439  +separator is 'b'.
         4440  +
         4441  +Returns 0 (false) if it finds no token, else non-0 (true).
         4442  +
         4443  +Output:
         4444  +
         4445  +- (*inp) will be set to the first character of the next token.
         4446  +
         4447  +- (*end) will point to the one-past-the-end point of the token.
         4448  +
         4449  +If (*inp == *end) then the end of the string has been reached
         4450  +without finding a token.
         4451  +
         4452  +Post-conditions:
         4453  +
         4454  +- (*end == *inp) if no token is found.
         4455  +
         4456  +- (*end > *inp) if a token is found.
         4457  +
         4458  +It is intolerant of NULL values for (inp, end), and will assert() in
         4459  +debug builds if passed NULL as either parameter.
         4460  +*/
         4461  +static char cson_next_token( char const ** inp, char separator, char const ** end )
         4462  +{
         4463  +    char const * pos = NULL;
         4464  +    assert( inp && end && *inp );
         4465  +    if( *inp == *end ) return 0;
         4466  +    pos = *inp;
         4467  +    if( !*pos )
         4468  +    {
         4469  +        *end = pos;
         4470  +        return 0;
         4471  +    }
         4472  +    for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ }
         4473  +    *inp = pos;
         4474  +    for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
         4475  +    *end = pos;
         4476  +    return (pos > *inp) ? 1 : 0;
         4477  +}
         4478  +
         4479  +int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path )
         4480  +{
         4481  +    if( ! obj || !path ) return cson_rc.ArgError;
         4482  +    else if( !*path || !*(1+path) ) return cson_rc.RangeError;
         4483  +    else return cson_object_fetch_sub(obj, tgt, path+1, *path);
         4484  +}
         4485  +
         4486  +int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char sep )
         4487  +{
         4488  +    if( ! obj || !path ) return cson_rc.ArgError;
         4489  +    else if( !*path || !sep ) return cson_rc.RangeError;
         4490  +    else
         4491  +    {
         4492  +        char const * beg = path;
         4493  +        char const * end = NULL;
         4494  +        int rc;
         4495  +        unsigned int i, len;
         4496  +        unsigned int tokenCount = 0;
         4497  +        cson_value * cv = NULL;
         4498  +        cson_object const * curObj = obj;
         4499  +        enum { BufSize = 128 };
         4500  +        char buf[BufSize];
         4501  +        memset( buf, 0, BufSize );
         4502  +
         4503  +        while( cson_next_token( &beg, sep, &end ) )
         4504  +        {
         4505  +            if( beg == end ) break;
         4506  +            else
         4507  +            {
         4508  +                ++tokenCount;
         4509  +                beg = end;
         4510  +                end = NULL;
         4511  +            }
         4512  +        }
         4513  +        if( 0 == tokenCount ) return cson_rc.RangeError;
         4514  +        beg = path;
         4515  +        end = NULL;
         4516  +        for( i = 0; i < tokenCount; ++i, beg=end, end=NULL )
         4517  +        {
         4518  +            rc = cson_next_token( &beg, sep, &end );
         4519  +            assert( 1 == rc );
         4520  +            assert( beg != end );
         4521  +            assert( end > beg );
         4522  +            len = end - beg;
         4523  +            if( len > (BufSize-1) ) return cson_rc.RangeError;
         4524  +            memset( buf, 0, len + 1 );
         4525  +            memcpy( buf, beg, len );
         4526  +            buf[len] = 0;
         4527  +            cv = cson_object_get( curObj, buf );
         4528  +            if( NULL == cv ) return cson_rc.NotFoundError;
         4529  +            else if( i == (tokenCount-1) )
         4530  +            {
         4531  +                if(tgt) *tgt = cv;
         4532  +                return 0;
         4533  +            }
         4534  +            else if( cson_value_is_object(cv) )
         4535  +            {
         4536  +                curObj = cson_value_get_object(cv);
         4537  +                assert((NULL != curObj) && "Detected mis-management of internal memory!");
         4538  +            }
         4539  +            /* TODO: arrays. Requires numeric parsing for the index. */
         4540  +            else
         4541  +            {
         4542  +                return cson_rc.NotFoundError;
         4543  +            }
         4544  +        }
         4545  +        assert( i == tokenCount );
         4546  +        return cson_rc.NotFoundError;
         4547  +    }
         4548  +}
         4549  +
         4550  +cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep )
         4551  +{
         4552  +    cson_value * v = NULL;
         4553  +    cson_object_fetch_sub( obj, &v, path, sep );
         4554  +    return v;
         4555  +}
         4556  +
         4557  +cson_value * cson_object_get_sub2( cson_object const * obj, char const * path )
         4558  +{
         4559  +    cson_value * v = NULL;
         4560  +    cson_object_fetch_sub2( obj, &v, path );
         4561  +    return v;
         4562  +}
         4563  +
         4564  +
         4565  +/**
         4566  +   If v is-a Object or Array then this function returns a deep
         4567  +   clone, otherwise it returns v. In either case, the refcount
         4568  +   of the returned value is increased by 1 by this call.
         4569  +*/
         4570  +static cson_value * cson_value_clone_ref( cson_value * v )
         4571  +{
         4572  +    cson_value * rc = NULL;
         4573  +#define TRY_SHARING 1
         4574  +#if TRY_SHARING
         4575  +    if(!v ) return rc;
         4576  +    else if( cson_value_is_object(v)
         4577  +             || cson_value_is_array(v))
         4578  +    {
         4579  +        rc = cson_value_clone( v );
         4580  +    }
         4581  +    else
         4582  +    {
         4583  +        rc = v;
         4584  +    }
         4585  +#else
         4586  +    rc = cson_value_clone(v);
         4587  +#endif
         4588  +#undef TRY_SHARING
         4589  +    cson_value_add_reference(rc);
         4590  +    return rc;
         4591  +}
         4592  +    
         4593  +static cson_value * cson_value_clone_array( cson_value const * orig )
         4594  +{
         4595  +    unsigned int i = 0;
         4596  +    cson_array const * asrc = cson_value_get_array( orig );
         4597  +    unsigned int alen = cson_array_length_get( asrc );
         4598  +    cson_value * destV = NULL;
         4599  +    cson_array * destA = NULL;
         4600  +    assert( orig && asrc );
         4601  +    destV = cson_value_new_array();
         4602  +    if( NULL == destV ) return NULL;
         4603  +    destA = cson_value_get_array( destV );
         4604  +    assert( destA );
         4605  +    if( 0 != cson_array_reserve( destA, alen ) )
         4606  +    {
         4607  +        cson_value_free( destV );
         4608  +        return NULL;
         4609  +    }
         4610  +    for( ; i < alen; ++i )
         4611  +    {
         4612  +        cson_value * ch = cson_array_get( asrc, i );
         4613  +        if( NULL != ch )
         4614  +        {
         4615  +            cson_value * cl = cson_value_clone_ref( ch );
         4616  +            if( NULL == cl )
         4617  +            {
         4618  +                cson_value_free( destV );
         4619  +                return NULL;
         4620  +            }
         4621  +            if( 0 != cson_array_set( destA, i, cl ) )
         4622  +            {
         4623  +                cson_value_free( cl );
         4624  +                cson_value_free( destV );
         4625  +                return NULL;
         4626  +            }
         4627  +            cson_value_free(cl)/*remove our artificial reference */;
         4628  +        }
         4629  +    }
         4630  +    return destV;
         4631  +}
         4632  +    
         4633  +static cson_value * cson_value_clone_object( cson_value const * orig )
         4634  +{
         4635  +    cson_object const * src = cson_value_get_object( orig );
         4636  +    cson_value * destV = NULL;
         4637  +    cson_object * dest = NULL;
         4638  +    cson_kvp const * kvp = NULL;
         4639  +    cson_object_iterator iter = cson_object_iterator_empty;
         4640  +    assert( orig && src );
         4641  +    if( 0 != cson_object_iter_init( src, &iter ) )
         4642  +    {
         4643  +        return NULL;
         4644  +    }
         4645  +    destV = cson_value_new_object();
         4646  +    if( NULL == destV ) return NULL;
         4647  +    dest = cson_value_get_object( destV );
         4648  +    assert( dest );
         4649  +    if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){
         4650  +        cson_value_free( destV );
         4651  +        return NULL;
         4652  +    }
         4653  +    while( (kvp = cson_object_iter_next( &iter )) )
         4654  +    {
         4655  +        cson_value * key = NULL;
         4656  +        cson_value * val = NULL;
         4657  +        assert( kvp->key && (kvp->key->refcount>0) );
         4658  +        key = cson_value_clone_ref(kvp->key);
         4659  +        val = key ? cson_value_clone_ref(kvp->value) : NULL;
         4660  +        if( ! key || !val ){
         4661  +            goto error;
         4662  +        }
         4663  +        assert( CSON_STR(key) );
         4664  +        if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) )
         4665  +        {
         4666  +            goto error;
         4667  +        }
         4668  +        /* remove our references */
         4669  +        cson_value_free(key);
         4670  +        cson_value_free(val);
         4671  +        continue;
         4672  +        error:
         4673  +        cson_value_free(key);
         4674  +        cson_value_free(val);
         4675  +        cson_value_free(destV);
         4676  +        destV = NULL;
         4677  +        break;
         4678  +    }
         4679  +    return destV;
         4680  +}
         4681  +
         4682  +cson_value * cson_value_clone( cson_value const * orig )
         4683  +{
         4684  +    if( NULL == orig ) return NULL;
         4685  +    else
         4686  +    {
         4687  +        switch( orig->api->typeID )
         4688  +        {
         4689  +          case CSON_TYPE_UNDEF:
         4690  +              assert(0 && "This should never happen.");
         4691  +              return NULL;
         4692  +          case CSON_TYPE_NULL:
         4693  +              return cson_value_null();
         4694  +          case CSON_TYPE_BOOL:
         4695  +              return cson_value_new_bool( cson_value_get_bool( orig ) );
         4696  +          case CSON_TYPE_INTEGER:
         4697  +              return cson_value_new_integer( cson_value_get_integer( orig ) );
         4698  +              break;
         4699  +          case CSON_TYPE_DOUBLE:
         4700  +              return cson_value_new_double( cson_value_get_double( orig ) );
         4701  +              break;
         4702  +          case CSON_TYPE_STRING: {
         4703  +              cson_string const * str = cson_value_get_string( orig );
         4704  +              return cson_value_new_string( cson_string_cstr( str ),
         4705  +                                            cson_string_length_bytes( str ) );
         4706  +          }
         4707  +          case CSON_TYPE_ARRAY:
         4708  +              return cson_value_clone_array( orig );
         4709  +          case CSON_TYPE_OBJECT:
         4710  +              return cson_value_clone_object( orig );
         4711  +        }
         4712  +        assert( 0 && "We can't get this far." );
         4713  +        return NULL;
         4714  +    }
         4715  +}
         4716  +
         4717  +cson_value * cson_string_value(cson_string const * s)
         4718  +{
         4719  +#define MT CSON_SPECIAL_VALUES[CSON_VAL_STR_EMPTY]
         4720  +    return s
         4721  +        ? ((s==MT.value) ? &MT : CSON_VCAST(s))
         4722  +        : NULL;
         4723  +#undef MT
         4724  +}
         4725  +
         4726  +cson_value * cson_object_value(cson_object const * s)
         4727  +{
         4728  +    return s
         4729  +        ? CSON_VCAST(s)
         4730  +        : NULL;
         4731  +}
         4732  +
         4733  +
         4734  +cson_value * cson_array_value(cson_array const * s)
         4735  +{
         4736  +    return s
         4737  +        ? CSON_VCAST(s)
         4738  +        : NULL;
         4739  +}
         4740  +
         4741  +void cson_free_object(cson_object *x)
         4742  +{
         4743  +    if(x) cson_value_free(cson_object_value(x));
         4744  +}
         4745  +void cson_free_array(cson_array *x)
         4746  +{
         4747  +    if(x) cson_value_free(cson_array_value(x));
         4748  +}
         4749  +
         4750  +void cson_free_string(cson_string *x)
         4751  +{
         4752  +    if(x) cson_value_free(cson_string_value(x));
         4753  +}
         4754  +void cson_free_value(cson_value *x)
         4755  +{
         4756  +    if(x) cson_value_free(x);
         4757  +}
         4758  +
         4759  +
         4760  +#if 0
         4761  +/* i'm not happy with this... */
         4762  +char * cson_pod_to_string( cson_value const * orig )
         4763  +{
         4764  +    if( ! orig ) return NULL;
         4765  +    else
         4766  +    {
         4767  +        enum { BufSize = 64 };
         4768  +        char * v = NULL;
         4769  +        switch( orig->api->typeID )
         4770  +        {
         4771  +          case CSON_TYPE_BOOL: {
         4772  +              char const bv = cson_value_get_bool(orig);
         4773  +              v = cson_strdup( bv ? "true" : "false",
         4774  +                               bv ? 4 : 5 );
         4775  +              break;
         4776  +          }
         4777  +          case CSON_TYPE_UNDEF:
         4778  +          case CSON_TYPE_NULL: {
         4779  +              v = cson_strdup( "null", 4 );
         4780  +              break;
         4781  +          }
         4782  +          case CSON_TYPE_STRING: {
         4783  +              cson_string const * jstr = cson_value_get_string(orig);
         4784  +              unsigned const int slen = cson_string_length_bytes( jstr );
         4785  +              assert( NULL != jstr );
         4786  +              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
         4787  +              break;
         4788  +          }
         4789  +          case CSON_TYPE_INTEGER: {
         4790  +              char buf[BufSize] = {0};
         4791  +              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
         4792  +              {
         4793  +                  v = cson_strdup( buf, strlen(buf) );
         4794  +              }
         4795  +              break;
         4796  +          }
         4797  +          case CSON_TYPE_DOUBLE: {
         4798  +              char buf[BufSize] = {0};
         4799  +              if( 0 < sprintf( v, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) )
         4800  +              {
         4801  +                  v = cson_strdup( buf, strlen(buf) );
         4802  +              }
         4803  +              break;
         4804  +          }
         4805  +          default:
         4806  +              break;
         4807  +        }
         4808  +        return v;
         4809  +    }
         4810  +}
         4811  +#endif
         4812  +
         4813  +#if 0
         4814  +/* i'm not happy with this... */
         4815  +char * cson_pod_to_string( cson_value const * orig )
         4816  +{
         4817  +    if( ! orig ) return NULL;
         4818  +    else
         4819  +    {
         4820  +        enum { BufSize = 64 };
         4821  +        char * v = NULL;
         4822  +        switch( orig->api->typeID )
         4823  +        {
         4824  +          case CSON_TYPE_BOOL: {
         4825  +              char const bv = cson_value_get_bool(orig);
         4826  +              v = cson_strdup( bv ? "true" : "false",
         4827  +                               bv ? 4 : 5 );
         4828  +              break;
         4829  +          }
         4830  +          case CSON_TYPE_UNDEF:
         4831  +          case CSON_TYPE_NULL: {
         4832  +              v = cson_strdup( "null", 4 );
         4833  +              break;
         4834  +          }
         4835  +          case CSON_TYPE_STRING: {
         4836  +              cson_string const * jstr = cson_value_get_string(orig);
         4837  +              unsigned const int slen = cson_string_length_bytes( jstr );
         4838  +              assert( NULL != jstr );
         4839  +              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
         4840  +              break;
         4841  +          }
         4842  +          case CSON_TYPE_INTEGER: {
         4843  +              char buf[BufSize] = {0};
         4844  +              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
         4845  +              {
         4846  +                  v = cson_strdup( buf, strlen(buf) );
         4847  +              }
         4848  +              break;
         4849  +          }
         4850  +          case CSON_TYPE_DOUBLE: {
         4851  +              char buf[BufSize] = {0};
         4852  +              if( 0 < sprintf( v, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) )
         4853  +              {
         4854  +                  v = cson_strdup( buf, strlen(buf) );
         4855  +              }
         4856  +              break;
         4857  +          }
         4858  +          default:
         4859  +              break;
         4860  +        }
         4861  +        return v;
         4862  +    }
         4863  +}
         4864  +#endif
         4865  +
         4866  +unsigned int cson_value_msize(cson_value const * v)
         4867  +{
         4868  +    if(!v) return 0;
         4869  +    else if( cson_value_is_builtin(v) ) return 0;
         4870  +    else {
         4871  +        unsigned int rc = sizeof(cson_value);
         4872  +        assert(NULL != v->api);
         4873  +        switch(v->api->typeID){
         4874  +          case CSON_TYPE_INTEGER:
         4875  +              assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]);
         4876  +              rc += sizeof(cson_int_t);
         4877  +              break;
         4878  +          case CSON_TYPE_DOUBLE:
         4879  +              assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0]);
         4880  +              rc += sizeof(cson_double_t);
         4881  +              break;
         4882  +          case CSON_TYPE_STRING:
         4883  +              rc += sizeof(cson_string)
         4884  +                  + CSON_STR(v)->length + 1/*NUL*/;
         4885  +              break;
         4886  +          case CSON_TYPE_ARRAY:{
         4887  +              cson_array const * ar = CSON_ARRAY(v);
         4888  +              cson_value_list const * li;
         4889  +              unsigned int i = 0;
         4890  +              assert( NULL != ar );
         4891  +              li = &ar->list;
         4892  +              rc += sizeof(cson_array)
         4893  +                  + (li->alloced * sizeof(cson_value *));
         4894  +              for( ; i < li->count; ++i ){
         4895  +                  cson_value const * e = ar->list.list[i];
         4896  +                  if( e ) rc += cson_value_msize( e );
         4897  +              }
         4898  +              break;
         4899  +          }
         4900  +          case CSON_TYPE_OBJECT:{
         4901  +              cson_object const * obj = CSON_OBJ(v);
         4902  +              unsigned int i = 0;
         4903  +              cson_kvp_list const * kl;
         4904  +              assert(NULL != obj);
         4905  +              kl = &obj->kvp;
         4906  +              rc += sizeof(cson_object)
         4907  +                  + (kl->alloced * sizeof(cson_kvp*));
         4908  +              for( ; i < kl->count; ++i ){
         4909  +                  cson_kvp const * kvp = kl->list[i];
         4910  +                  assert(NULL != kvp);
         4911  +                  rc += cson_value_msize(kvp->key);
         4912  +                  rc += cson_value_msize(kvp->value);
         4913  +              }
         4914  +              break;
         4915  +          }
         4916  +          case CSON_TYPE_UNDEF:
         4917  +          case CSON_TYPE_NULL:
         4918  +          case CSON_TYPE_BOOL:
         4919  +              assert( 0 && "Should have been caught by is-builtin check!" );
         4920  +              break;
         4921  +          default:
         4922  +              assert(0 && "Invalid typeID!");
         4923  +              return 0;
         4924  +#undef RCCHECK
         4925  +        }
         4926  +        return rc;
         4927  +    }
         4928  +}
         4929  +
         4930  +int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){
         4931  +    cson_object_iterator iter = cson_object_iterator_empty;
         4932  +    int rc;
         4933  +    char const replace = (flags & CSON_MERGE_REPLACE);
         4934  +    char const recurse = !(flags & CSON_MERGE_NO_RECURSE);
         4935  +    cson_kvp const * kvp;
         4936  +    if((!dest || !src) || (dest==src)) return cson_rc.ArgError;
         4937  +    rc = cson_object_iter_init( src, &iter );
         4938  +    if(rc) return rc;
         4939  +    while( (kvp = cson_object_iter_next(&iter) ) )
         4940  +    {
         4941  +        cson_string * key = cson_kvp_key(kvp);
         4942  +        cson_value * val = cson_kvp_value(kvp);
         4943  +        cson_value * check = cson_object_get_s( dest, key );
         4944  +        if(!check){
         4945  +            cson_object_set_s( dest, key, val );
         4946  +            continue;
         4947  +        }
         4948  +        else if(!replace && !recurse) continue;
         4949  +        else if(replace && !recurse){
         4950  +            cson_object_set_s( dest, key, val );
         4951  +            continue;
         4952  +        }
         4953  +        else if( recurse ){
         4954  +            if( cson_value_is_object(check) &&
         4955  +                cson_value_is_object(val) ){
         4956  +                rc = cson_object_merge( cson_value_get_object(check),
         4957  +                                        cson_value_get_object(val),
         4958  +                                        flags );
         4959  +                if(rc) return rc;
         4960  +                else continue;
         4961  +            }
         4962  +            else continue;
         4963  +        }
         4964  +        else continue;
         4965  +    }
         4966  +    return 0;
         4967  +}
         4968  +
         4969  +static cson_value * cson_guess_arg_type(char const *arg){
         4970  +    char * end = NULL;
         4971  +    if(!arg || !*arg) return cson_value_null();
         4972  +    else if(('0'>*arg) || ('9'<*arg)){
         4973  +        goto do_string;
         4974  +    }
         4975  +    else{ /* try numbers... */
         4976  +        long const val = strtol(arg, &end, 10);
         4977  +        if(!*end){
         4978  +            return cson_value_new_integer( (cson_int_t)val);
         4979  +        }
         4980  +        else if( '.' != *end ) {
         4981  +            goto do_string;
         4982  +        }
         4983  +        else {
         4984  +            double const val = strtod(arg, &end);
         4985  +            if(!*end){
         4986  +                return cson_value_new_double(val);
         4987  +            }
         4988  +        }
         4989  +    }
         4990  +    do_string:
         4991  +    return cson_value_new_string(arg, strlen(arg));
         4992  +}
         4993  +
         4994  +
         4995  +int cson_parse_argv_flags( int argc, char const * const * argv,
         4996  +                           cson_object ** tgt, unsigned int * count ){
         4997  +    cson_object * o = NULL;
         4998  +    int rc = 0;
         4999  +    int i = 0;
         5000  +    if(argc<1 || !argc || !tgt) return cson_rc.ArgError;
         5001  +    o = *tgt ? *tgt : cson_new_object();
         5002  +    if(count) *count = 0;
         5003  +    for( i = 0; i < argc; ++i ){
         5004  +        char const * arg = argv[i];
         5005  +        char const * key = arg;
         5006  +        char const * pos;
         5007  +        cson_string * k = NULL;
         5008  +        cson_value * v = NULL;
         5009  +        if('-' != *arg) continue;
         5010  +        while('-'==*key) ++key;
         5011  +        if(!*key) continue;
         5012  +        pos = key;
         5013  +        while( *pos && ('=' != *pos)) ++pos;
         5014  +        k = cson_new_string(key, pos-key);
         5015  +        if(!k){
         5016  +            rc = cson_rc.AllocError;
         5017  +            break;
         5018  +        }
         5019  +        if(!*pos){ /** --key */
         5020  +            v = cson_value_true();
         5021  +        }else{ /** --key=...*/
         5022  +            assert('=' == *pos);
         5023  +            ++pos /*skip '='*/;
         5024  +            v = cson_guess_arg_type(pos);
         5025  +        }
         5026  +        if(0 != (rc=cson_object_set_s(o, k, v))){
         5027  +            cson_free_string(k);
         5028  +            cson_value_free(v);
         5029  +            break;
         5030  +        }
         5031  +        else if(count) ++*count;
         5032  +    }
         5033  +    if(o != *tgt){
         5034  +        if(rc) cson_free_object(o);
         5035  +        else *tgt = o;
         5036  +    }
         5037  +    return rc;
         5038  +}
         5039  +
         5040  +#if defined(__cplusplus)
         5041  +} /*extern "C"*/
         5042  +#endif
         5043  +
         5044  +#undef MARKER
         5045  +#undef CSON_OBJECT_PROPS_SORT
         5046  +#undef CSON_OBJECT_PROPS_SORT_USE_LENGTH
         5047  +#undef CSON_CAST
         5048  +#undef CSON_INT
         5049  +#undef CSON_DBL
         5050  +#undef CSON_STR
         5051  +#undef CSON_OBJ
         5052  +#undef CSON_ARRAY
         5053  +#undef CSON_VCAST
         5054  +#undef CSON_MALLOC_IMPL
         5055  +#undef CSON_FREE_IMPL
         5056  +#undef CSON_REALLOC_IMPL
         5057  +/* end file ./cson.c */
         5058  +/* begin file ./cson_lists.h */
         5059  +/* Auto-generated from cson_list.h. Edit at your own risk! */
         5060  +unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n )
         5061  +{
         5062  +    if( !self ) return 0;
         5063  +    else if(0 == n)
         5064  +    {
         5065  +        if(0 == self->alloced) return 0;
         5066  +        cson_free(self->list, "cson_value_list_reserve");
         5067  +        self->list = NULL;
         5068  +        self->alloced = self->count = 0;
         5069  +        return 0;
         5070  +    }
         5071  +    else if( self->alloced >= n )
         5072  +    {
         5073  +        return self->alloced;
         5074  +    }
         5075  +    else
         5076  +    {
         5077  +        size_t const sz = sizeof(cson_value *) * n;
         5078  +        cson_value * * m = (cson_value **)cson_realloc( self->list, sz, "cson_value_list_reserve" );
         5079  +        if( ! m ) return self->alloced;
         5080  +
         5081  +        memset( m + self->alloced, 0, (sizeof(cson_value *)*(n-self->alloced)));
         5082  +        self->alloced = n;
         5083  +        self->list = m;
         5084  +        return n;
         5085  +    }
         5086  +}
         5087  +int cson_value_list_append( cson_value_list * self, cson_value * cp )
         5088  +{
         5089  +    if( !self || !cp ) return cson_rc.ArgError;
         5090  +    else if( self->alloced > cson_value_list_reserve(self, self->count+1) )
         5091  +    {
         5092  +        return cson_rc.AllocError;
         5093  +    }
         5094  +    else
         5095  +    {
         5096  +        self->list[self->count++] = cp;
         5097  +        return 0;
         5098  +    }
         5099  +}
         5100  +int cson_value_list_visit( cson_value_list * self,
         5101  +
         5102  +                        int (*visitor)(cson_value * obj, void * visitorState ),
         5103  +
         5104  +
         5105  +
         5106  +                        void * visitorState )
         5107  +{
         5108  +    int rc = cson_rc.ArgError;
         5109  +    if( self && visitor )
         5110  +    {
         5111  +        unsigned int i = 0;
         5112  +        for( rc = 0; (i < self->count) && (0 == rc); ++i )
         5113  +        {
         5114  +
         5115  +            cson_value * obj = self->list[i];
         5116  +
         5117  +
         5118  +
         5119  +            if(obj) rc = visitor( obj, visitorState );
         5120  +        }
         5121  +    }
         5122  +    return rc;
         5123  +}
         5124  +void cson_value_list_clean( cson_value_list * self,
         5125  +
         5126  +                         void (*cleaner)(cson_value * obj)
         5127  +
         5128  +
         5129  +
         5130  +                         )
         5131  +{
         5132  +    if( self && cleaner && self->count )
         5133  +    {
         5134  +        unsigned int i = 0;
         5135  +        for( ; i < self->count; ++i )
         5136  +        {
         5137  +
         5138  +            cson_value * obj = self->list[i];
         5139  +
         5140  +
         5141  +
         5142  +            if(obj) cleaner(obj);
         5143  +        }
         5144  +    }
         5145  +    cson_value_list_reserve(self,0);
         5146  +}
         5147  +unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n )
         5148  +{
         5149  +    if( !self ) return 0;
         5150  +    else if(0 == n)
         5151  +    {
         5152  +        if(0 == self->alloced) return 0;
         5153  +        cson_free(self->list, "cson_kvp_list_reserve");
         5154  +        self->list = NULL;
         5155  +        self->alloced = self->count = 0;
         5156  +        return 0;
         5157  +    }
         5158  +    else if( self->alloced >= n )
         5159  +    {
         5160  +        return self->alloced;
         5161  +    }
         5162  +    else
         5163  +    {
         5164  +        size_t const sz = sizeof(cson_kvp *) * n;
         5165  +        cson_kvp * * m = (cson_kvp **)cson_realloc( self->list, sz, "cson_kvp_list_reserve" );
         5166  +        if( ! m ) return self->alloced;
         5167  +
         5168  +        memset( m + self->alloced, 0, (sizeof(cson_kvp *)*(n-self->alloced)));
         5169  +        self->alloced = n;
         5170  +        self->list = m;
         5171  +        return n;
         5172  +    }
         5173  +}
         5174  +int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp )
         5175  +{
         5176  +    if( !self || !cp ) return cson_rc.ArgError;
         5177  +    else if( self->alloced > cson_kvp_list_reserve(self, self->count+1) )
         5178  +    {
         5179  +        return cson_rc.AllocError;
         5180  +    }
         5181  +    else
         5182  +    {
         5183  +        self->list[self->count++] = cp;
         5184  +        return 0;
         5185  +    }
         5186  +}
         5187  +int cson_kvp_list_visit( cson_kvp_list * self,
         5188  +
         5189  +                        int (*visitor)(cson_kvp * obj, void * visitorState ),
         5190  +
         5191  +
         5192  +
         5193  +                        void * visitorState )
         5194  +{
         5195  +    int rc = cson_rc.ArgError;
         5196  +    if( self && visitor )
         5197  +    {
         5198  +        unsigned int i = 0;
         5199  +        for( rc = 0; (i < self->count) && (0 == rc); ++i )
         5200  +        {
         5201  +
         5202  +            cson_kvp * obj = self->list[i];
         5203  +
         5204  +
         5205  +
         5206  +            if(obj) rc = visitor( obj, visitorState );
         5207  +        }
         5208  +    }
         5209  +    return rc;
         5210  +}
         5211  +void cson_kvp_list_clean( cson_kvp_list * self,
         5212  +
         5213  +                         void (*cleaner)(cson_kvp * obj)
         5214  +
         5215  +
         5216  +
         5217  +                         )
         5218  +{
         5219  +    if( self && cleaner && self->count )
         5220  +    {
         5221  +        unsigned int i = 0;
         5222  +        for( ; i < self->count; ++i )
         5223  +        {
         5224  +
         5225  +            cson_kvp * obj = self->list[i];
         5226  +
         5227  +
         5228  +
         5229  +            if(obj) cleaner(obj);
         5230  +        }
         5231  +    }
         5232  +    cson_kvp_list_reserve(self,0);
         5233  +}
         5234  +/* end file ./cson_lists.h */
         5235  +/* begin file ./cson_sqlite3.c */
         5236  +/** @file cson_sqlite3.c
         5237  +
         5238  +This file contains the implementation code for the cson
         5239  +sqlite3-to-JSON API.
         5240  +
         5241  +License: the same as the cson core library.
         5242  +
         5243  +Author: Stephan Beal (http://wanderinghorse.net/home/stephan)
         5244  +*/
         5245  +#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
         5246  +#include <assert.h>
         5247  +#include <string.h> /* strlen() */
         5248  +
         5249  +#if 0
         5250  +#include <stdio.h>
         5251  +#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
         5252  +#else
         5253  +#define MARKER if(0) printf
         5254  +#endif
         5255  +
         5256  +#if defined(__cplusplus)
         5257  +extern "C" {
         5258  +#endif
         5259  +
         5260  +cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col )
         5261  +{
         5262  +    if( ! st ) return NULL;
         5263  +    else
         5264  +    {
         5265  +#if 0
         5266  +        sqlite3_value * val = sqlite3_column_type(st,col);
         5267  +        int const vtype = val ? sqlite3_value_type(val) : -1;
         5268  +        if( ! val ) return cson_value_null();
         5269  +#else
         5270  +        int const vtype = sqlite3_column_type(st,col);
         5271  +#endif
         5272  +        switch( vtype )
         5273  +        {
         5274  +          case SQLITE_NULL:
         5275  +              return cson_value_null();
         5276  +          case SQLITE_INTEGER:
         5277  +              /* FIXME: for large integers fall back to Double instead. */
         5278  +              return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col)  );
         5279  +          case SQLITE_FLOAT:
         5280  +              return cson_value_new_double( sqlite3_column_double(st, col) );
         5281  +          case SQLITE_BLOB: /* arguably fall through... */
         5282  +          case SQLITE_TEXT: {
         5283  +              char const * str = (char const *)sqlite3_column_text(st,col);
         5284  +              return cson_value_new_string(str, str ? strlen(str) : 0);
         5285  +          }
         5286  +          default:
         5287  +              return NULL;
         5288  +        }
         5289  +    }
         5290  +}
         5291  +
         5292  +cson_value * cson_sqlite3_column_names( sqlite3_stmt * st )
         5293  +{
         5294  +    cson_value * aryV = NULL;
         5295  +    cson_array * ary = NULL;
         5296  +    char const * colName = NULL;
         5297  +    int i = 0;
         5298  +    int rc = 0;
         5299  +    int colCount = 0;
         5300  +    assert(st);
         5301  +    colCount = sqlite3_column_count(st);
         5302  +    if( colCount <= 0 ) return NULL;
         5303  +    
         5304  +    aryV = cson_value_new_array();
         5305  +    if( ! aryV ) return NULL;
         5306  +    ary = cson_value_get_array(aryV);
         5307  +    assert(ary);
         5308  +    for( i = 0; (0 ==rc) && (i < colCount); ++i )
         5309  +    {
         5310  +        colName = sqlite3_column_name( st, i );
         5311  +        if( ! colName ) rc = cson_rc.AllocError;
         5312  +        else
         5313  +        {
         5314  +            rc = cson_array_set( ary, (unsigned int)i,
         5315  +                    cson_value_new_string(colName, strlen(colName)) );
         5316  +        }
         5317  +    }
         5318  +    if( 0 == rc ) return aryV;
         5319  +    else
         5320  +    {
         5321  +        cson_value_free(aryV);
         5322  +        return NULL;
         5323  +    }
         5324  +}
         5325  +
         5326  +
         5327  +cson_value * cson_sqlite3_row_to_object2( sqlite3_stmt * st,
         5328  +                                          cson_array * colNames )
         5329  +{
         5330  +    cson_value * rootV = NULL;
         5331  +    cson_object * root = NULL;
         5332  +    cson_string * colName = NULL;
         5333  +    int i = 0;
         5334  +    int rc = 0;
         5335  +    cson_value * currentValue = NULL;
         5336  +    int const colCount = sqlite3_column_count(st);
         5337  +    if( !colCount || (colCount>cson_array_length_get(colNames)) ) {
         5338  +        return NULL;
         5339  +    }
         5340  +    rootV = cson_value_new_object();
         5341  +    if(!rootV) return NULL;
         5342  +    root = cson_value_get_object(rootV);
         5343  +    for( i = 0; i < colCount; ++i )
         5344  +    {
         5345  +        colName = cson_value_get_string( cson_array_get( colNames, i ) );
         5346  +        if( ! colName ) goto error;
         5347  +        currentValue = cson_sqlite3_column_to_value(st,i);
         5348  +        if( ! currentValue ) currentValue = cson_value_null();
         5349  +        rc = cson_object_set_s( root, colName, currentValue );
         5350  +        if( 0 != rc )
         5351  +        {
         5352  +            cson_value_free( currentValue );
         5353  +            goto error;
         5354  +        }
         5355  +    }
         5356  +    goto end;
         5357  +    error:
         5358  +    cson_value_free( rootV );
         5359  +    rootV = NULL;
         5360  +    end:
         5361  +    return rootV;
         5362  +}
         5363  +
         5364  +
         5365  +cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st )
         5366  +{
         5367  +#if 0
         5368  +    cson_value * arV = cson_sqlite3_column_names(st);
         5369  +    cson_array * ar = NULL;
         5370  +    cson_value * rc = NULL;
         5371  +    if(!arV) return NULL;
         5372  +    ar = cson_value_get_array(arV);
         5373  +    assert( NULL != ar );
         5374  +    rc = cson_sqlite3_row_to_object2(st, ar);
         5375  +    cson_value_free(arV);
         5376  +    return rc;
         5377  +#else
         5378  +    cson_value * rootV = NULL;
         5379  +    cson_object * root = NULL;
         5380  +    char const * colName = NULL;
         5381  +    int i = 0;
         5382  +    int rc = 0;
         5383  +    cson_value * currentValue = NULL;
         5384  +    int const colCount = sqlite3_column_count(st);
         5385  +    if( !colCount ) return NULL;
         5386  +    rootV = cson_value_new_object();
         5387  +    if(!rootV) return NULL;
         5388  +    root = cson_value_get_object(rootV);
         5389  +    for( i = 0; i < colCount; ++i )
         5390  +    {
         5391  +        colName = sqlite3_column_name( st, i );
         5392  +        if( ! colName ) goto error;
         5393  +        currentValue = cson_sqlite3_column_to_value(st,i);
         5394  +        if( ! currentValue ) currentValue = cson_value_null();
         5395  +        rc = cson_object_set( root, colName, currentValue );
         5396  +        if( 0 != rc )
         5397  +        {
         5398  +            cson_value_free( currentValue );
         5399  +            goto error;
         5400  +        }
         5401  +    }
         5402  +    goto end;
         5403  +    error:
         5404  +    cson_value_free( rootV );
         5405  +    rootV = NULL;
         5406  +    end:
         5407  +    return rootV;
         5408  +#endif
         5409  +}
         5410  +
         5411  +cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st )
         5412  +{
         5413  +    cson_value * aryV = NULL;
         5414  +    cson_array * ary = NULL;
         5415  +    int i = 0;
         5416  +    int rc = 0;
         5417  +    int const colCount = sqlite3_column_count(st);
         5418  +    if( ! colCount ) return NULL;
         5419  +    aryV = cson_value_new_array();
         5420  +    if( ! aryV ) return NULL;
         5421  +    ary = cson_value_get_array(aryV);
         5422  +    rc = cson_array_reserve(ary, (unsigned int) colCount );
         5423  +    if( 0 != rc ) goto error;
         5424  +
         5425  +    for( i = 0; i < colCount; ++i ){
         5426  +        cson_value * elem = cson_sqlite3_column_to_value(st,i);
         5427  +        if( ! elem ) goto error;
         5428  +        rc = cson_array_append(ary,elem);
         5429  +        if(0!=rc)
         5430  +        {
         5431  +            cson_value_free( elem );
         5432  +            goto end;
         5433  +        }
         5434  +    }
         5435  +    goto end;
         5436  +    error:
         5437  +    cson_value_free(aryV);
         5438  +    aryV = NULL;
         5439  +    end:
         5440  +    return aryV;
         5441  +}
         5442  +
         5443  +    
         5444  +/**
         5445  +    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
         5446  +    parameter is non-0.
         5447  +*/
         5448  +static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
         5449  +{
         5450  +#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
         5451  +    if( ! tgt || !st ) return cson_rc.ArgError;
         5452  +    else
         5453  +    {
         5454  +        cson_value * rootV = NULL;
         5455  +        cson_object * root = NULL;
         5456  +        cson_value * colsV = NULL;
         5457  +        cson_array * cols = NULL;
         5458  +        cson_value * rowsV = NULL;
         5459  +        cson_array * rows = NULL;
         5460  +        cson_value * objV = NULL;
         5461  +        int rc = 0;
         5462  +        int const colCount = sqlite3_column_count(st);
         5463  +        if( colCount <= 0 ) return cson_rc.ArgError;
         5464  +        rootV = cson_value_new_object();
         5465  +        if( ! rootV ) return cson_rc.AllocError;
         5466  +        colsV = cson_sqlite3_column_names(st);
         5467  +        if( ! colsV )
         5468  +        {
         5469  +            cson_value_free( rootV );
         5470  +            RETURN(cson_rc.AllocError);
         5471  +        }
         5472  +        cols = cson_value_get_array(colsV);
         5473  +        assert(NULL != cols);
         5474  +        root = cson_value_get_object(rootV);
         5475  +        rc = cson_object_set( root, "columns", colsV );
         5476  +        if( rc )
         5477  +        {
         5478  +            cson_value_free( colsV );
         5479  +            RETURN(rc);
         5480  +        }
         5481  +        rowsV = cson_value_new_array();
         5482  +        if( ! rowsV ) RETURN(cson_rc.AllocError);
         5483  +        rc = cson_object_set( root, "rows", rowsV );
         5484  +        if( rc )
         5485  +        {
         5486  +            cson_value_free( rowsV );
         5487  +            RETURN(rc);
         5488  +        }
         5489  +        rows = cson_value_get_array(rowsV);
         5490  +        assert(rows);
         5491  +        while( SQLITE_ROW == sqlite3_step(st) )
         5492  +        {
         5493  +            objV = cson_sqlite3_row_to_object2(st, cols);
         5494  +            if( ! objV ) RETURN(cson_rc.UnknownError);
         5495  +            rc = cson_array_append( rows, objV );
         5496  +            if( rc )
         5497  +            {
         5498  +                cson_value_free( objV );
         5499  +                RETURN(rc);
         5500  +            }
         5501  +        }
         5502  +        *tgt = rootV;
         5503  +        return 0;
         5504  +    }
         5505  +#undef RETURN
         5506  +}
         5507  +
         5508  +/**
         5509  +    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
         5510  +    parameter is 0.
         5511  +*/
         5512  +static int cson_sqlite3_stmt_to_json_slim( sqlite3_stmt * st, cson_value ** tgt )
         5513  +{
         5514  +#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
         5515  +    if( ! tgt || !st ) return cson_rc.ArgError;
         5516  +    else
         5517  +    {
         5518  +        cson_value * rootV = NULL;
         5519  +        cson_object * root = NULL;
         5520  +        cson_value * aryV = NULL;
         5521  +        cson_value * rowsV = NULL;
         5522  +        cson_array * rows = NULL;
         5523  +        int rc = 0;
         5524  +        int const colCount = sqlite3_column_count(st);
         5525  +        if( colCount <= 0 ) return cson_rc.ArgError;
         5526  +        rootV = cson_value_new_object();
         5527  +        if( ! rootV ) return cson_rc.AllocError;
         5528  +        aryV = cson_sqlite3_column_names(st);
         5529  +        if( ! aryV )
         5530  +        {
         5531  +            cson_value_free( rootV );
         5532  +            RETURN(cson_rc.AllocError);
         5533  +        }
         5534  +        root = cson_value_get_object(rootV);
         5535  +        rc = cson_object_set( root, "columns", aryV );
         5536  +        if( rc )
         5537  +        {
         5538  +            cson_value_free( aryV );
         5539  +            RETURN(rc);
         5540  +        }
         5541  +        aryV = NULL;
         5542  +        rowsV = cson_value_new_array();
         5543  +        if( ! rowsV ) RETURN(cson_rc.AllocError);
         5544  +        rc = cson_object_set( root, "rows", rowsV );
         5545  +        if( 0 != rc )
         5546  +        {
         5547  +            cson_value_free( rowsV );
         5548  +            RETURN(rc);
         5549  +        }
         5550  +        rows = cson_value_get_array(rowsV);
         5551  +        assert(rows);
         5552  +        while( SQLITE_ROW == sqlite3_step(st) )
         5553  +        {
         5554  +            aryV = cson_sqlite3_row_to_array(st);
         5555  +            if( ! aryV ) RETURN(cson_rc.UnknownError);
         5556  +            rc = cson_array_append( rows, aryV );
         5557  +            if( 0 != rc )
         5558  +            {
         5559  +                cson_value_free( aryV );
         5560  +                RETURN(rc);
         5561  +            }
         5562  +        }
         5563  +        *tgt = rootV;
         5564  +        return 0;
         5565  +    }
         5566  +#undef RETURN
         5567  +}
         5568  +
         5569  +int cson_sqlite3_stmt_to_json( sqlite3_stmt * st, cson_value ** tgt, char fat )
         5570  +{
         5571  +    return fat
         5572  +        ? cson_sqlite3_stmt_to_json_fat(st,tgt)
         5573  +        : cson_sqlite3_stmt_to_json_slim(st,tgt)
         5574  +        ;
         5575  +}
         5576  +
         5577  +int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, char fat )
         5578  +{
         5579  +    if( !db || !tgt || !sql || !*sql ) return cson_rc.ArgError;
         5580  +    else
         5581  +    {
         5582  +        sqlite3_stmt * st = NULL;
         5583  +        int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
         5584  +        if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
         5585  +        rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
         5586  +        sqlite3_finalize( st );
         5587  +        return rc;
         5588  +    }        
         5589  +}
         5590  +
         5591  +int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
         5592  +{
         5593  +    int rc = 0;
         5594  +    char convertErr = 0;
         5595  +    if(!st) return cson_rc.ArgError;
         5596  +    else if( ndx < 1 ) {
         5597  +        rc = cson_rc.RangeError;
         5598  +    }
         5599  +    else if( cson_value_is_array(v) ){
         5600  +        cson_array * ar = cson_value_get_array(v);
         5601  +        unsigned int len = cson_array_length_get(ar);
         5602  +        unsigned int i;
         5603  +        assert(NULL != ar);
         5604  +        for( i = 0; !rc && (i < len); ++i ){
         5605  +            rc = cson_sqlite3_bind_value( st, (int)i+ndx,
         5606  +                                          cson_array_get(ar, i));
         5607  +        }
         5608  +    }
         5609  +    else if(!v || cson_value_is_null(v)){
         5610  +        rc = sqlite3_bind_null(st,ndx);
         5611  +        convertErr = 1;
         5612  +    }
         5613  +    else if( cson_value_is_double(v) ){
         5614  +        rc = sqlite3_bind_double( st, ndx, cson_value_get_double(v) );
         5615  +        convertErr = 1;
         5616  +    }
         5617  +    else if( cson_value_is_bool(v) ){
         5618  +        rc = sqlite3_bind_int( st, ndx, cson_value_get_bool(v) ? 1 : 0 );
         5619  +        convertErr = 1;
         5620  +    }
         5621  +    else if( cson_value_is_integer(v) ){
         5622  +        rc = sqlite3_bind_int64( st, ndx, cson_value_get_integer(v) );
         5623  +        convertErr = 1;
         5624  +    }
         5625  +    else if( cson_value_is_string(v) ){
         5626  +        cson_string const * s = cson_value_get_string(v);
         5627  +        rc = sqlite3_bind_text( st, ndx,
         5628  +                                cson_string_cstr(s),
         5629  +                                cson_string_length_bytes(s),
         5630  +                                SQLITE_TRANSIENT);
         5631  +        convertErr = 1;
         5632  +    }
         5633  +    else {
         5634  +        rc = cson_rc.TypeError;
         5635  +    }
         5636  +    if(convertErr && rc) switch(rc){
         5637  +      case SQLITE_TOOBIG:
         5638  +      case SQLITE_RANGE: rc = cson_rc.RangeError; break;
         5639  +      case SQLITE_NOMEM: rc = cson_rc.AllocError; break;
         5640  +      case SQLITE_IOERR: rc = cson_rc.IOError; break;
         5641  +      default: rc = cson_rc.UnknownError; break;
         5642  +    };
         5643  +    return rc;
         5644  +}
         5645  +
         5646  +
         5647  +#if defined(__cplusplus)
         5648  +} /*extern "C"*/
         5649  +#endif
         5650  +#undef MARKER
         5651  +#endif /* CSON_ENABLE_SQLITE3 */
         5652  +/* end file ./cson_sqlite3.c */
         5653  +#endif /* FOSSIL_ENABLE_JSON */

Added src/cson_amalgamation.h.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +#ifndef CSON_FOSSIL_MODE
            3  +#define CSON_FOSSIL_MODE
            4  +#endif
            5  +/* auto-generated! Do not edit! */
            6  +/* begin file include/wh/cson/cson.h */
            7  +#if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED)
            8  +#define WANDERINGHORSE_NET_CSON_H_INCLUDED 1
            9  +
           10  +/*#include <stdint.h> C99: fixed-size int types. */
           11  +#include <stdio.h> /* FILE decl */
           12  +
           13  +/** @page page_cson cson JSON API
           14  +
           15  +cson (pronounced "season") is an object-oriented C API for generating
           16  +and consuming JSON (http://www.json.org) data.
           17  +
           18  +Its main claim to fame is that it can parse JSON from, and output it
           19  +to, damned near anywhere. The i/o routines use a callback function to
           20  +fetch/emit JSON data, allowing clients to easily plug in their own
           21  +implementations. Implementations are provided for string- and
           22  +FILE-based i/o.
           23  +
           24  +Project home page: http://fossil.wanderinghorse.net/repos/cson
           25  +
           26  +Author: Stephan Beal (http://www.wanderinghorse.net/home/stephan/)
           27  +
           28  +License: Dual Public Domain/MIT
           29  +
           30  +The full license text is at the bottom of the main header file
           31  +(cson.h).
           32  +
           33  +Examples of how to use the library are scattered throughout
           34  +the API documentation, in the test.c file in the source repo,
           35  +and in the wiki on the project's home page.
           36  +
           37  +
           38  +*/
           39  +
           40  +#if defined(__cplusplus)
           41  +extern "C" {
           42  +#endif
           43  +
           44  +#if defined(_WIN32) || defined(_WIN64)
           45  +#  define CSON_ENABLE_UNIX 0
           46  +#else
           47  +#  define CSON_ENABLE_UNIX 1
           48  +#endif
           49  +
           50  +
           51  +/** @typedef some_long_int_type cson_int_t
           52  +
           53  +Typedef for JSON-like integer types. This is (long long) where feasible,
           54  +otherwise (long).
           55  +*/
           56  +#if (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
           57  +typedef long long cson_int_t;
           58  +#define CSON_INT_T_SFMT "lld"
           59  +#define CSON_INT_T_PFMT "lld"
           60  +#else 
           61  +typedef long cson_int_t;
           62  +#define CSON_INT_T_SFMT "ld"
           63  +#define CSON_INT_T_PFMT "ld"
           64  +#endif
           65  +
           66  +/** @typedef double_or_long_double cson_double_t
           67  +
           68  +This is the type of double value used by the library.
           69  +It is only lightly tested with long double, and when using
           70  +long double the memory requirements for such values goes
           71  +up.
           72  +*/
           73  +#if 0
           74  +typedef long double cson_double_t;
           75  +#define CSON_DOUBLE_T_SFMT "Lf"
           76  +#define CSON_DOUBLE_T_PFMT "Lf"
           77  +#else
           78  +typedef double cson_double_t;
           79  +#define CSON_DOUBLE_T_SFMT "f"
           80  +#define CSON_DOUBLE_T_PFMT "f"
           81  +#endif
           82  +
           83  +/** @def CSON_VOID_PTR_IS_BIG
           84  +
           85  +ONLY define this to a true value if you know that
           86  +
           87  +(sizeof(cson_int_t) <= sizeof(void*))
           88  +
           89  +If that is the case, cson does not need to dynamically
           90  +allocate integers. However, enabling this may cause
           91  +compilation warnings in 32-bit builds even though the code
           92  +being warned about cannot ever be called. To get around such
           93  +warnings, when building on a 64-bit environment you can define
           94  +this to 1 to get "big" integer support. HOWEVER, all clients must
           95  +also use the same value for this macro. If i knew a halfway reliable
           96  +way to determine this automatically at preprocessor-time, i would
           97  +automate this. We might be able to do halfway reliably by looking
           98  +for a large INT_MAX value?
           99  +*/
          100  +#if !defined(CSON_VOID_PTR_IS_BIG)
          101  +
          102  +/* Largely taken from http://predef.sourceforge.net/prearch.html
          103  +
          104  +See also: http://poshlib.hookatooka.com/poshlib/trac.cgi/browser/posh.h
          105  +*/
          106  +#  if defined(_WIN64) || defined(__LP64__)/*gcc*/ \
          107  +    || defined(_M_X64) || defined(__amd64__) || defined(__amd64) \
          108  +    ||  defined(__x86_64__) || defined(__x86_64) \
          109  +    || defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) \
          110  +    || defined(_M_IA64) \
          111  +    || defined(__sparc_v9__) || defined(__sparcv9) || defined(_ADDR64) \
          112  +    || defined(__64BIT__)
          113  +#    define CSON_VOID_PTR_IS_BIG 1
          114  +#  else
          115  +#    define CSON_VOID_PTR_IS_BIG 0
          116  +#  endif
          117  +#endif
          118  +
          119  +/** @def CSON_INT_T_SFMT
          120  +
          121  +scanf()-compatible format token for cson_int_t.
          122  +*/
          123  +
          124  +/** @def CSON_INT_T_PFMT
          125  +
          126  +printf()-compatible format token for cson_int_t.
          127  +*/
          128  +
          129  +
          130  +/** @def CSON_DOUBLE_T_SFMT
          131  +
          132  +scanf()-compatible format token for cson_double_t.
          133  +*/
          134  +
          135  +/** @def CSON_DOUBLE_T_PFMT
          136  +
          137  +printf()-compatible format token for cson_double_t.
          138  +*/
          139  +
          140  +/**
          141  +    Type IDs corresponding to JavaScript/JSON types.
          142  +
          143  +    These are only in the public API to allow O(1) client-side
          144  +    dispatching based on cson_value types.
          145  +*/
          146  +enum cson_type_id {
          147  +  /**
          148  +    The special "undefined" value constant.
          149  +
          150  +    Its value must be 0 for internal reasons.
          151  + */
          152  + CSON_TYPE_UNDEF = 0,
          153  + /**
          154  +    The special "null" value constant.
          155  + */
          156  + CSON_TYPE_NULL = 1,
          157  + /**
          158  +    The bool value type.
          159  + */
          160  + CSON_TYPE_BOOL = 2,
          161  + /**
          162  +    The integer value type, represented in this library
          163  +    by cson_int_t.
          164  + */
          165  + CSON_TYPE_INTEGER = 3,
          166  + /**
          167  +    The double value type, represented in this library
          168  +    by cson_double_t.
          169  + */
          170  + CSON_TYPE_DOUBLE = 4,
          171  + /** The immutable string type. This library stores strings
          172  +    as immutable UTF8.
          173  + */
          174  + CSON_TYPE_STRING = 5,
          175  + /** The "Array" type. */
          176  + CSON_TYPE_ARRAY = 6,
          177  + /** The "Object" type. */
          178  + CSON_TYPE_OBJECT = 7
          179  +};
          180  +/**
          181  +   Convenience typedef.
          182  +*/
          183  +typedef enum cson_type_id cson_type_id;
          184  +
          185  +
          186  +/**
          187  +   Convenience typedef.
          188  +*/
          189  +typedef struct cson_value cson_value;
          190  +
          191  +/** @struct cson_value
          192  +   
          193  +   The core value type of this API. It is opaque to clients, and
          194  +   only the cson public API should be used for setting or
          195  +   inspecting their values.
          196  +
          197  +   This class is opaque because stack-based usage can easily cause
          198  +   leaks if one does not intimately understand the underlying
          199  +   internal memory management (which sometimes changes).
          200  +
          201  +   It is (as of 20110323) legal to insert a given value instance into
          202  +   multiple containers (they will share ownership using reference
          203  +   counting) as long as those insertions do not cause cycles. However,
          204  +   be very aware that such value re-use uses a reference to the
          205  +   original copy, meaning that if its value is changed once, it is
          206  +   changed everywhere. Also beware that multi-threaded write
          207  +   operations on such references leads to undefined behaviour.
          208  +   
          209  +   PLEASE read the ACHTUNGEN below...
          210  +
          211  +   ACHTUNG #1:
          212  +
          213  +   cson_values MUST NOT form cycles (e.g. via object or array
          214  +   entries).
          215  +
          216  +   Not abiding th Holy Law Of No Cycles will lead to double-frees and
          217  +   the like (i.e. undefined behaviour, likely crashes due to infinite
          218  +   recursion or stepping on invalid (freed) pointers).
          219  +
          220  +   ACHTUNG #2:
          221  +   
          222  +   ALL cson_values returned as non-const cson_value pointers from any
          223  +   public functions in the cson API are to be treated as if they are
          224  +   heap-allocated, and MUST be freed by client by doing ONE of:
          225  +   
          226  +   - Passing it to cson_value_free().
          227  +   
          228  +   - Adding it to an Object or Array, in which case the object/array
          229  +   takes over ownership. As of 20110323, a value may be inserted into
          230  +   a single container multiple times, or into multiple containers,
          231  +   in which case they all share ownership (via reference counting)
          232  +   of the original value (meaning any changes to it are visible in
          233  +   all references to it).
          234  +   
          235  +   Each call to cson_value_new_xxx() MUST eventually be followed up
          236  +   by one of those options.
          237  +   
          238  +   Some cson_value_new_XXX() implementations do not actually allocate
          239  +   memory, but this is an internal implementation detail. Client code
          240  +   MUST NOT rely on this behaviour and MUST treat each object
          241  +   returned by such a function as if it was a freshly-allocated copy
          242  +   (even if their pointer addresses are the same).
          243  +   
          244  +   ACHTUNG #3:
          245  +
          246  +   Note that ACHTUNG #2 tells us that we must always free (or transfer
          247  +   ownership of) all pointers returned bycson_value_new_xxx(), but
          248  +   that two calls to (e.g.) cson_value_new_bool(1) will (or might)
          249  +   return the same address. The client must not rely on the
          250  +   "non-allocation" policy of such special cases, and must pass each
          251  +   returned value to cson_value_free(), even if two of them have the
          252  +   same address.  Some special values (e.g. null, true, false, integer
          253  +   0, double 0.0, and empty strings) use shared copies and in other
          254  +   places reference counting is used internally to figure out when it
          255  +   is safe to destroy an object.
          256  +
          257  +
          258  +   @see cson_value_new_array()
          259  +   @see cson_value_new_object()
          260  +   @see cson_value_new_string()
          261  +   @see cson_value_new_integer()
          262  +   @see cson_value_new_double()
          263  +   @see cson_value_new_bool()
          264  +   @see cson_value_true()
          265  +   @see cson_value_false()
          266  +   @see cson_value_null()
          267  +   @see cson_value_free()
          268  +   @see cson_value_type_id()
          269  +*/
          270  +
          271  +/** @var cson_rc
          272  +
          273  +   This object defines the error codes used by cson.
          274  +
          275  +   Library routines which return int values almost always return a
          276  +   value from this structure. None of the members in this struct have
          277  +   published values except for the OK member, which has the value 0.
          278  +   All other values might be incidentally defined where clients
          279  +   can see them, but the numbers might change from release to
          280  +   release, so clients should only use the symbolic names.
          281  +
          282  +   Client code is expected to access these values via the shared
          283  +   cson_rc object, and use them as demonstrated here:
          284  +
          285  +   @code
          286  +   int rc = cson_some_func(...);
          287  +   if( 0 == rc ) {...success...}
          288  +   else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
          289  +   else if( cson_rc.AllocError == rc ) { ... allocation error ... }
          290  +   ...
          291  +   @endcode
          292  +   
          293  +   The entries named Parse_XXX are generally only returned by
          294  +   cson_parse() and friends.
          295  +*/
          296  +
          297  +/** @struct cson_rc_
          298  +   See \ref cson_rc for details.
          299  +*/
          300  +static const struct cson_rc_
          301  +{
          302  +    /** The generic success value. Guaranteed to be 0. */
          303  +    const int OK;
          304  +    /** Signifies an error in one or more arguments (e.g. NULL where it is not allowed). */
          305  +    const int ArgError;
          306  +    /** Signifies that some argument is not in a valid range. */
          307  +    const int RangeError;
          308  +    /** Signifies that some argument is not of the correct logical cson type. */
          309  +    const int TypeError;
          310  +    /** Signifies an input/ouput error. */
          311  +    const int IOError;
          312  +    /** Signifies an out-of-memory error. */
          313  +    const int AllocError;
          314  +    /** Signifies that the called code is "NYI" (Not Yet Implemented). */
          315  +    const int NYIError;
          316  +    /** Signifies that an internal error was triggered. If it happens, please report this as a bug! */
          317  +    const int InternalError;
          318  +    /** Signifies that the called operation is not supported in the
          319  +        current environment. e.g.  missing support from 3rd-party or
          320  +        platform-specific code.
          321  +    */
          322  +    const int UnsupportedError;
          323  +    /**
          324  +       Signifies that the request resource could not be found.
          325  +     */
          326  +    const int NotFoundError;
          327  +    /**
          328  +       Signifies an unknown error, possibly because an underlying
          329  +       3rd-party API produced an error and we have no other reasonable
          330  +       error code to convert it to.
          331  +     */
          332  +    const int UnknownError;
          333  +    /**
          334  +       Signifies that the parser found an unexpected character.
          335  +     */
          336  +    const int Parse_INVALID_CHAR;
          337  +    /**
          338  +       Signifies that the parser found an invalid keyword (possibly
          339  +       an unquoted string).
          340  +     */
          341  +    const int Parse_INVALID_KEYWORD;
          342  +    /**
          343  +       Signifies that the parser found an invalid escape sequence.
          344  +     */
          345  +    const int Parse_INVALID_ESCAPE_SEQUENCE;
          346  +    /**
          347  +       Signifies that the parser found an invalid Unicode character
          348  +       sequence.
          349  +     */
          350  +    const int Parse_INVALID_UNICODE_SEQUENCE;
          351  +    /**
          352  +       Signifies that the parser found an invalid numeric token.
          353  +     */
          354  +    const int Parse_INVALID_NUMBER;
          355  +    /**
          356  +       Signifies that the parser reached its maximum defined
          357  +       parsing depth before finishing the input.
          358  +     */
          359  +    const int Parse_NESTING_DEPTH_REACHED;
          360  +    /**
          361  +       Signifies that the parser found an unclosed object or array.
          362  +     */
          363  +    const int Parse_UNBALANCED_COLLECTION;
          364  +    /**
          365  +       Signifies that the parser found an key in an unexpected place.
          366  +     */
          367  +    const int Parse_EXPECTED_KEY;
          368  +    /**
          369  +       Signifies that the parser expected to find a colon but
          370  +       found none (e.g. between keys and values in an object).
          371  +     */
          372  +    const int Parse_EXPECTED_COLON;
          373  +} cson_rc = {
          374  +0/*OK*/,
          375  +1/*ArgError*/,
          376  +2/*RangeError*/,
          377  +3/*TypeError*/,
          378  +4/*IOError*/,
          379  +5/*AllocError*/,
          380  +6/*NYIError*/,
          381  +7/*InternalError*/,
          382  +8/*UnsupportedError*/,
          383  +9/*NotFoundError*/,
          384  +10/*UnknownError*/,
          385  +11/*Parse_INVALID_CHAR*/,
          386  +12/*Parse_INVALID_KEYWORD*/,
          387  +13/*Parse_INVALID_ESCAPE_SEQUENCE*/,
          388  +14/*Parse_INVALID_UNICODE_SEQUENCE*/,
          389  +15/*Parse_INVALID_NUMBER*/,
          390  +16/*Parse_NESTING_DEPTH_REACHED*/,
          391  +17/*Parse_UNBALANCED_COLLECTION*/,
          392  +18/*Parse_EXPECTED_KEY*/,
          393  +19/*Parse_EXPECTED_COLON*/
          394  +};
          395  +
          396  +/**
          397  +   Returns the string form of the cson_rc code corresponding to rc, or
          398  +   some unspecified, non-NULL string if it is an unknown code.
          399  +
          400  +   The returned bytes are static and do not changing during the
          401  +   lifetime of the application.
          402  +*/
          403  +char const * cson_rc_string(int rc);
          404  +
          405  +/** @struct cson_parse_opt
          406  +   Client-configurable options for the cson_parse() family of
          407  +   functions.
          408  +*/
          409  +struct cson_parse_opt
          410  +{
          411  +    /**
          412  +       Maximum object/array depth to traverse.
          413  +    */
          414  +    unsigned short maxDepth;
          415  +    /**
          416  +       Whether or not to allow C-style comments.  Do not rely on this
          417  +       option being available. If the underlying parser is replaced,
          418  +       this option might no longer be supported.
          419  +    */
          420  +    char allowComments;
          421  +};
          422  +typedef struct cson_parse_opt cson_parse_opt;
          423  +
          424  +/**
          425  +   Empty-initialized cson_parse_opt object.
          426  +*/
          427  +#define cson_parse_opt_empty_m { 25/*maxDepth*/, 0/*allowComments*/}
          428  +
          429  +
          430  +/**
          431  +   A class for holding JSON parser information. It is primarily
          432  +   intended for finding the position of a parse error.
          433  +*/
          434  +struct cson_parse_info
          435  +{
          436  +    /**
          437  +       1-based line number.
          438  +    */
          439  +    unsigned int line;
          440  +    /**
          441  +       0-based column number.
          442  +     */
          443  +    unsigned int col;
          444  +
          445  +    /**
          446  +       Length, in bytes.
          447  +    */
          448  +    unsigned int length;
          449  +    
          450  +    /**
          451  +       Error code of the parse run (0 for no error).
          452  +    */
          453  +    int errorCode;
          454  +
          455  +    /**
          456  +       The total number of object keys successfully processed by the
          457  +       parser.
          458  +    */
          459  +    unsigned int totalKeyCount;
          460  +
          461  +    /**
          462  +       The total number of object/array values successfully processed
          463  +       by the parser, including the root node.
          464  +     */
          465  +    unsigned int totalValueCount;
          466  +};
          467  +typedef struct cson_parse_info cson_parse_info;
          468  +
          469  +/**
          470  +   Empty-initialized cson_parse_info object.
          471  +*/
          472  +#define cson_parse_info_empty_m {1/*line*/,\
          473  +            0/*col*/,                                   \
          474  +            0/*length*/,                                \
          475  +            0/*errorCode*/,                             \
          476  +            0/*totalKeyCount*/,                         \
          477  +            0/*totalValueCount*/                        \
          478  +            }
          479  +/**
          480  +   Empty-initialized cson_parse_info object.
          481  +*/
          482  +extern const cson_parse_info cson_parse_info_empty;
          483  +
          484  +/**
          485  +   Empty-initialized cson_parse_opt object.
          486  +*/
          487  +extern const cson_parse_opt cson_parse_opt_empty;
          488  +
          489  +/**
          490  +    Client-configurable options for the cson_output() family of
          491  +    functions.
          492  +*/
          493  +struct cson_output_opt
          494  +{
          495  +    /**
          496  +       Specifies how to indent (or not) output. The values
          497  +       are:
          498  +
          499  +       (0) == no extra indentation.
          500  +       
          501  +       (1) == 1 TAB character for each level.
          502  +
          503  +       (>1) == that number of SPACES for each level.
          504  +    */
          505  +    unsigned char indentation;
          506  +
          507  +    /**
          508  +       Maximum object/array depth to traverse. Traversing deeply can
          509  +       be indicative of cycles in the object/array tree, and this
          510  +       value is used to figure out when to abort the traversal.
          511  +    */
          512  +    unsigned short maxDepth;
          513  +    
          514  +    /**
          515  +       If true, a newline will be added to generated output,
          516  +       else not.
          517  +    */
          518  +    char addNewline;
          519  +
          520  +    /**
          521  +       If true, a space will be added after the colon operator
          522  +       in objects' key/value pairs.
          523  +    */
          524  +    char addSpaceAfterColon;
          525  +
          526  +    /**
          527  +       If set to 1 then objects/arrays containing only a single value
          528  +       will not indent an extra level for that value (but will indent
          529  +       on subsequent levels if that value contains multiple values).
          530  +    */
          531  +    char indentSingleMemberValues;
          532  +
          533  +    /**
          534  +       The JSON format allows, but does not require, JSON generators
          535  +       to backslash-escape forward slashes. This option enables/disables
          536  +       that feature. According to JSON's inventor, Douglas Crockford:
          537  +
          538  +       <quote>
          539  +       It is allowed, not required. It is allowed so that JSON can be
          540  +       safely embedded in HTML, which can freak out when seeing
          541  +       strings containing "</". JSON tolerates "<\/" for this reason.
          542  +       </quote>
          543  +
          544  +       (from an email on 2011-04-08)
          545  +
          546  +       The default value is 0 (because it's just damned ugly).
          547  +    */
          548  +    char escapeForwardSlashes;
          549  +};
          550  +typedef struct cson_output_opt cson_output_opt;
          551  +
          552  +/**
          553  +   Empty-initialized cson_output_opt object.
          554  +*/
          555  +#define cson_output_opt_empty_m { 0/*indentation*/,\
          556  +            25/*maxDepth*/,                             \
          557  +            0/*addNewline*/,                            \
          558  +            0/*addSpaceAfterColon*/,                    \
          559  +            0/*indentSingleMemberValues*/,              \
          560  +            0/*escapeForwardSlashes*/                   \
          561  +            }
          562  +
          563  +/**
          564  +   Empty-initialized cson_output_opt object.
          565  +*/
          566  +extern const cson_output_opt cson_output_opt_empty;
          567  +
          568  +/**
          569  +   Typedef for functions which act as an input source for
          570  +   the cson JSON parser.
          571  +
          572  +   The arguments are:
          573  +
          574  +   - state: implementation-specific state needed by the function.
          575  +
          576  +   - n: when called, *n will be the number of bytes the function
          577  +   should read and copy to dest. The function MUST NOT copy more than
          578  +   *n bytes to dest. Before returning, *n must be set to the number of
          579  +   bytes actually copied to dest. If that number is smaller than the
          580  +   original *n value, the input is assumed to be completed (thus this
          581  +   is not useful with non-blocking readers).
          582  +
          583  +   - dest: the destination memory to copy the data do.
          584  +
          585  +   Must return 0 on success, non-0 on error (preferably a value from
          586  +   cson_rc).
          587  +
          588  +   The parser allows this routine to return a partial character from a
          589  +   UTF multi-byte character. The input routine does not need to
          590  +   concern itself with character boundaries.
          591  +*/
          592  +typedef int (*cson_data_source_f)( void * state, void * dest, unsigned int * n );
          593  +
          594  +/**
          595  +   Typedef for functions which act as an output destination for
          596  +   generated JSON.
          597  +
          598  +   The arguments are:
          599  +
          600  +   - state: implementation-specific state needed by the function.
          601  +
          602  +   - n: the length, in bytes, of src.
          603  +
          604  +   - src: the source bytes which the output function should consume.
          605  +   The src pointer will be invalidated shortly after this function
          606  +   returns, so the implementation must copy or ignore the data, but not
          607  +   hold a copy of the src pointer.
          608  +
          609  +   Must return 0 on success, non-0 on error (preferably a value from
          610  +   cson_rc).
          611  +
          612  +   These functions are called relatively often during the JSON-output
          613  +   process, and should try to be fast.   
          614  +*/
          615  +typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n );
          616  +
          617  +/**
          618  +    Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the
          619  +    src function to fetch all input. This function fetches each input character
          620  +    from the source function, which is calls like src(srcState, buffer, bufferSize),
          621  +    and processes them. If anything is not JSON-kosher then this function
          622  +    fails and returns one of the non-0 cson_rc codes.
          623  +
          624  +    This function is only intended to read root nodes of a JSON tree, either
          625  +    a single object or a single array, containing any number of child elements.
          626  +
          627  +    On success, *tgt is assigned the value of the root node of the
          628  +    JSON input, and the caller takes over ownership of that memory.
          629  +    On error, *tgt is not modified and the caller need not do any
          630  +    special cleanup, except possibly for the input source.
          631  +
          632  +
          633  +    The opt argument may point to an initialized cson_parse_opt object
          634  +    which contains any settings the caller wants. If it is NULL then
          635  +    default settings (the values defined in cson_parse_opt_empty) are
          636  +    used.
          637  +
          638  +    The info argument may be NULL. If it is not NULL then the parser
          639  +    populates it with information which is useful in error
          640  +    reporting. Namely, it contains the line/column of parse errors.
          641  +    
          642  +    The srcState argument is ignored by this function but is passed on to src,
          643  +    so any output-destination-specific state can be stored there and accessed
          644  +    via the src callback.
          645  +    
          646  +    Non-parse error conditions include:
          647  +
          648  +    - (!tgt) or !src: cson_rc.ArgError
          649  +    - cson_rc.AllocError can happen at any time during the input phase
          650  +
          651  +    Here's a complete example of using a custom input source:
          652  +
          653  +    @code
          654  +    // Internal type to hold state for a JSON input string.
          655  +    typedef struct
          656  +    {
          657  +        char const * str; // start of input string
          658  +        char const * pos; // current internal cursor position
          659  +        char const * end; // logical EOF (one-past-the-end)
          660  +    } StringSource;
          661  +
          662  +    // cson_data_source_f() impl which uses StringSource.
          663  +    static int cson_data_source_StringSource( void * state, void * dest,
          664  +                                              unsigned int * n )
          665  +    {
          666  +        StringSource * ss = (StringSource*) state;
          667  +        unsigned int i;
          668  +        unsigned char * tgt = (unsigned char *)dest;
          669  +        if( ! ss || ! n || !dest ) return cson_rc.ArgError;
          670  +        else if( !*n ) return cson_rc.RangeError;
          671  +        for( i = 0;
          672  +             (i < *n) && (ss->pos < ss->end);
          673  +             ++i, ++ss->pos, ++tgt )
          674  +        {
          675  +             *tgt = *ss->pos;
          676  +        }
          677  +        *n = i;
          678  +        return 0;
          679  +    }
          680  +
          681  +    ...
          682  +    // Now use StringSource together with cson_parse()
          683  +    StringSource ss;
          684  +    cson_value * root = NULL;
          685  +    char const * json = "{\"k1\":123}";
          686  +    ss.str = ss.pos = json;
          687  +    ss.end = json + strlen(json);
          688  +    int rc = cson_parse( &root, cson_data_source_StringSource, &ss, NULL, NULL );
          689  +    @endcode
          690  +
          691  +    It is recommended that clients wrap such utility code into
          692  +    type-safe wrapper functions which also initialize the internal
          693  +    state object and check the user-provided parameters for legality
          694  +    before passing them on to cson_parse(). For examples of this, see
          695  +    cson_parse_FILE() or cson_parse_string().
          696  +
          697  +    TODOs:
          698  +
          699  +    - Buffer the input in larger chunks. We currently read
          700  +    byte-by-byte, but i'm too tired to write/test the looping code for
          701  +    the buffering.
          702  +    
          703  +    @see cson_parse_FILE()
          704  +    @see cson_parse_string()
          705  +*/
          706  +int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState,
          707  +                cson_parse_opt const * opt, cson_parse_info * info );
          708  +/**
          709  +   A cson_data_source_f() implementation which requires the state argument
          710  +   to be a readable (FILE*) handle.
          711  +*/
          712  +int cson_data_source_FILE( void * state, void * dest, unsigned int * n );
          713  +
          714  +/**
          715  +   Equivalent to cson_parse( tgt, cson_data_source_FILE, src, opt ).
          716  +
          717  +   @see cson_parse_filename()
          718  +*/
          719  +int cson_parse_FILE( cson_value ** tgt, FILE * src,
          720  +                     cson_parse_opt const * opt, cson_parse_info * info );
          721  +
          722  +/**
          723  +   Convenience wrapper around cson_parse_FILE() which opens the given filename.
          724  +
          725  +   Returns cson_rc.IOError if the file cannot be opened.
          726  +
          727  +   @see cson_parse_FILE()
          728  +*/
          729  +int cson_parse_filename( cson_value ** tgt, char const * src,
          730  +                         cson_parse_opt const * opt, cson_parse_info * info );
          731  +
          732  +/**
          733  +   Uses an internal helper class to pass src through cson_parse().
          734  +   See that function for the return value and argument semantics.
          735  +
          736  +   src must be a string containing JSON code, at least len bytes long,
          737  +   and the parser will attempt to parse exactly len bytes from src.
          738  +
          739  +   If len is less than 2 (the minimum length of a legal top-node JSON
          740  +   object) then cson_rc.RangeError is returned.
          741  +*/
          742  +int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len,
          743  +                       cson_parse_opt const * opt, cson_parse_info * info );
          744  +
          745  +
          746  +
          747  +/**
          748  +   Outputs the given value as a JSON-formatted string, sending all
          749  +   output to the given callback function. It is intended for top-level
          750  +   objects or arrays, but can be used with any cson_value.
          751  +
          752  +   If opt is NULL then default options (the values defined in
          753  +   cson_output_opt_empty) are used.
          754  +
          755  +   If opt->maxDepth is exceeded while traversing the value tree,
          756  +   cson_rc.RangeError is returned.
          757  +
          758  +   The destState parameter is ignored by this function and is passed
          759  +   on to the dest function.
          760  +
          761  +   Returns 0 on success. On error, any amount of output might have been
          762  +   generated before the error was triggered.
          763  +   
          764  +   Example:
          765  +
          766  +   @code
          767  +   int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL );
          768  +   // basically equivalent to: cson_output_FILE( myValue, stdout, NULL );
          769  +   // but note that cson_output_FILE() actually uses different defaults
          770  +   // for the output options.
          771  +   @endcode
          772  +*/
          773  +int cson_output( cson_value const * src, cson_data_dest_f dest, void * destState, cson_output_opt const * opt );
          774  +
          775  +
          776  +/**
          777  +   A cson_data_dest_f() implementation which requires the state argument
          778  +   to be a writable (FILE*) handle.
          779  +*/
          780  +int cson_data_dest_FILE( void * state, void const * src, unsigned int n );
          781  +
          782  +/**
          783  +   Almost equivalent to cson_output( src, cson_data_dest_FILE, dest, opt ),
          784  +   with one minor difference: if opt is NULL then the default options
          785  +   always include the addNewline option, since that is normally desired
          786  +   for FILE output.
          787  +
          788  +   @see cson_output_filename()
          789  +*/
          790  +int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt );
          791  +/**
          792  +   Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying
          793  +   any existing contents. Returns cson_rc.IOError if the file cannot be opened.
          794  +
          795  +   @see cson_output_FILE()
          796  +*/
          797  +int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt );
          798  +
          799  +/**
          800  +   Returns the virtual type of v, or CSON_TYPE_UNDEF if !v.
          801  +*/
          802  +cson_type_id cson_value_type_id( cson_value const * v );
          803  +
          804  +
          805  +/** Returns true if v is null, v->api is NULL, or v holds the special undefined value. */
          806  +char cson_value_is_undef( cson_value const * v );
          807  +/** Returns true if v contains a null value. */
          808  +char cson_value_is_null( cson_value const * v );
          809  +/** Returns true if v contains a bool value. */
          810  +char cson_value_is_bool( cson_value const * v );
          811  +/** Returns true if v contains an integer value. */
          812  +char cson_value_is_integer( cson_value const * v );
          813  +/** Returns true if v contains a double value. */
          814  +char cson_value_is_double( cson_value const * v );
          815  +/** Returns true if v contains a number (double, integer) value. */
          816  +char cson_value_is_number( cson_value const * v );
          817  +/** Returns true if v contains a string value. */
          818  +char cson_value_is_string( cson_value const * v );
          819  +/** Returns true if v contains an array value. */
          820  +char cson_value_is_array( cson_value const * v );
          821  +/** Returns true if v contains an object value. */
          822  +char cson_value_is_object( cson_value const * v );
          823  +
          824  +/** @struct cson_object
          825  +
          826  +    cson_object is an opaque handle to an Object value.
          827  +
          828  +    They are used like:
          829  +
          830  +    @code
          831  +    cson_object * obj = cson_value_get_object(myValue);
          832  +    ...
          833  +    @endcode
          834  +
          835  +    They can be created like:
          836  +
          837  +    @code
          838  +    cson_value * objV = cson_value_new_object();
          839  +    cson_object * obj = cson_value_get_object(objV);
          840  +    // obj is owned by objV and objV must eventually be freed
          841  +    // using cson_value_free() or added to a container
          842  +    // object/array (which transfers ownership to that container).
          843  +    @endcode
          844  +
          845  +    @see cson_value_new_object()
          846  +    @see cson_value_get_object()
          847  +    @see cson_value_free()
          848  +*/
          849  +
          850  +typedef struct cson_object cson_object;
          851  +
          852  +/** @struct cson_array
          853  +
          854  +    cson_array is an opaque handle to an Array value.
          855  +
          856  +    They are used like:
          857  +
          858  +    @code
          859  +    cson_array * obj = cson_value_get_array(myValue);
          860  +    ...
          861  +    @endcode
          862  +
          863  +    They can be created like:
          864  +
          865  +    @code
          866  +    cson_value * arV = cson_value_new_array();
          867  +    cson_array * ar = cson_value_get_array(arV);
          868  +    // ar is owned by arV and arV must eventually be freed
          869  +    // using cson_value_free() or added to a container
          870  +    // object/array (which transfers ownership to that container).
          871  +    @endcode
          872  +
          873  +    @see cson_value_new_array()
          874  +    @see cson_value_get_array()
          875  +    @see cson_value_free()
          876  +
          877  +*/
          878  +typedef struct cson_array cson_array;
          879  +
          880  +/** @struct cson_string
          881  +
          882  +   cson-internal string type, opaque to client code. Strings in cson
          883  +   are immutable and allocated only by library internals, never
          884  +   directly by client code.
          885  +
          886  +   The actual string bytes are to be allocated together in the same
          887  +   memory chunk as the cson_string object, which saves us 1 malloc()
          888  +   and 1 pointer member in this type (because we no longer have a
          889  +   direct pointer to the memory).
          890  +
          891  +   Potential TODOs:
          892  +
          893  +   @see cson_string_cstr()
          894  +*/
          895  +typedef struct cson_string cson_string;
          896  +
          897  +/**
          898  +   Converts the given value to a boolean, using JavaScript semantics depending
          899  +   on the concrete type of val:
          900  +
          901  +   undef or null: false
          902  +   
          903  +   boolean: same
          904  +   
          905  +   integer, double: 0 or 0.0 == false, else true
          906  +   
          907  +   object, array: true
          908  +
          909  +   string: length-0 string is false, else true.
          910  +
          911  +   Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1.
          912  +   On error (val is NULL) then v is not modified.
          913  +*/
          914  +int cson_value_fetch_bool( cson_value const * val, char * v );
          915  +
          916  +/**
          917  +   Similar to cson_value_fetch_bool(), but fetches an integer value.
          918  +
          919  +   The conversion, if any, depends on the concrete type of val:
          920  +
          921  +   NULL, null, undefined: *v is set to 0 and 0 is returned.
          922  +   
          923  +   string, object, array: *v is set to 0 and
          924  +   cson_rc.TypeError is returned. The error may normally be safely
          925  +   ignored, but it is provided for those wanted to know whether a direct
          926  +   conversion was possible.
          927  +
          928  +   integer: *v is set to the int value and 0 is returned.
          929  +   
          930  +   double: *v is set to the value truncated to int and 0 is returned.
          931  +*/
          932  +int cson_value_fetch_integer( cson_value const * val, cson_int_t * v );
          933  +
          934  +/**
          935  +   The same conversions and return values as
          936  +   cson_value_fetch_integer(), except that the roles of int/double are
          937  +   swapped.
          938  +*/
          939  +int cson_value_fetch_double( cson_value const * val, cson_double_t * v );
          940  +
          941  +/**
          942  +   If cson_value_is_string(val) then this function assigns *str to the
          943  +   contents of the string. str may be NULL, in which case this function
          944  +   functions like cson_value_is_string() but returns 0 on success.
          945  +
          946  +   Returns 0 if val is-a string, else non-0, in which case *str is not
          947  +   modified.
          948  +
          949  +   The bytes are owned by the given value and may be invalidated in any of
          950  +   the following ways:
          951  +
          952  +   - The value is cleaned up or freed.
          953  +
          954  +   - An array or object containing the value peforms a re-allocation
          955  +   (it shrinks or grows).
          956  +
          957  +   And thus the bytes should be consumed before any further operations
          958  +   on val or any container which holds it.
          959  +
          960  +   Note that this routine does not convert non-String values to their
          961  +   string representations. (Adding that ability would add more
          962  +   overhead to every cson_value instance.)
          963  +*/
          964  +int cson_value_fetch_string( cson_value const * val, cson_string ** str );
          965  +
          966  +/**
          967  +   If cson_value_is_object(val) then this function assigns *obj to the underlying
          968  +   object value and returns 0, otherwise non-0 is returned and *obj is not modified.
          969  +
          970  +   obj may be NULL, in which case this function works like cson_value_is_object()
          971  +   but with inverse return value semantics (0==success) (and it's a few
          972  +   CPU cycles slower).
          973  +
          974  +   The *obj pointer is owned by val, and will be invalidated when val
          975  +   is cleaned up.
          976  +
          977  +   Achtung: for best results, ALWAYS pass a pointer to NULL as the
          978  +   second argument, e.g.:
          979  +
          980  +   @code
          981  +   cson_object * obj = NULL;
          982  +   int rc = cson_value_fetch_object( val, &obj );
          983  +
          984  +   // Or, more simply:
          985  +   obj = cson_value_get_object( val );
          986  +   @endcode
          987  +
          988  +   @see cson_value_get_object()
          989  +*/
          990  +int cson_value_fetch_object( cson_value const * val, cson_object ** obj );
          991  +
          992  +/**
          993  +   Identical to cson_value_fetch_object(), but works on array values.
          994  +
          995  +   @see cson_value_get_array()
          996  +*/
          997  +int cson_value_fetch_array( cson_value const * val, cson_array ** tgt );
          998  +
          999  +/**
         1000  +   Simplified form of cson_value_fetch_bool(). Returns 0 if val
         1001  +   is NULL.
         1002  +*/
         1003  +char cson_value_get_bool( cson_value const * val );
         1004  +
         1005  +/**
         1006  +   Simplified form of cson_value_fetch_integer(). Returns 0 if val
         1007  +   is NULL.
         1008  +*/
         1009  +cson_int_t cson_value_get_integer( cson_value const * val );
         1010  +
         1011  +/**
         1012  +   Simplified form of cson_value_fetch_double(). Returns 0.0 if val
         1013  +   is NULL.
         1014  +*/
         1015  +cson_double_t cson_value_get_double( cson_value const * val );
         1016  +
         1017  +/**
         1018  +   Simplified form of cson_value_fetch_string(). Returns NULL if val
         1019  +   is-not-a string value.
         1020  +*/
         1021  +cson_string * cson_value_get_string( cson_value const * val );
         1022  +
         1023  +/**
         1024  +   Returns a pointer to the NULL-terminated string bytes of str.
         1025  +   The bytes are owned by string and will be invalided when it
         1026  +   is cleaned up.
         1027  +
         1028  +   If str is NULL then NULL is returned. If the string has a length
         1029  +   of 0 then "" is returned.
         1030  +
         1031  +   @see cson_string_length_bytes()
         1032  +   @see cson_value_get_string()
         1033  +*/
         1034  +char const * cson_string_cstr( cson_string const * str );
         1035  +
         1036  +/**
         1037  +   Convenience function which returns the string bytes of
         1038  +   the given value if it is-a string, otherwise it returns
         1039  +   NULL. Note that this does no conversion of non-string types
         1040  +   to strings.
         1041  +
         1042  +   Equivalent to cson_string_cstr(cson_value_get_string(val)).
         1043  +*/
         1044  +char const * cson_value_get_cstr( cson_value const * val );
         1045  +
         1046  +/**
         1047  +   Equivalent to cson_string_cmp_cstr_n(lhs, cson_string_cstr(rhs), cson_string_length_bytes(rhs)).
         1048  +*/
         1049  +int cson_string_cmp( cson_string const * lhs, cson_string const * rhs );
         1050  +
         1051  +/**
         1052  +   Compares lhs to rhs using memcmp()/strcmp() semantics. Generically
         1053  +   speaking it returns a negative number if lhs is less-than rhs, 0 if
         1054  +   they are equivalent, or a positive number if lhs is greater-than
         1055  +   rhs. It has the following rules for equivalence:
         1056  +
         1057  +   - The maximum number of bytes compared is the lesser of rhsLen and
         1058  +   the length of lhs. If the strings do not match, but compare equal
         1059  +   up to the just-described comparison length, the shorter string is
         1060  +   considered to be less-than the longer one.
         1061  +   
         1062  +   - If lhs and rhs are both NULL, or both have a length of 0 then they will
         1063  +   compare equal.
         1064  +
         1065  +   - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than
         1066  +   rhs.
         1067  +
         1068  +   - If rhs is null/length-0 but lhs is not then rhs is considered to be less-than
         1069  +   rhs.
         1070  +
         1071  +   - i have no clue if the results are exactly correct for UTF strings.
         1072  +
         1073  +*/
         1074  +int cson_string_cmp_cstr_n( cson_string const * lhs, char const * rhs, unsigned int rhsLen );
         1075  +
         1076  +/**
         1077  +   Equivalent to cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs)?strlen(rhs):0 ).
         1078  +*/
         1079  +int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs );
         1080  +
         1081  +/**
         1082  +   Returns the length, in bytes, of str, or 0 if str is NULL. This is
         1083  +   an O(1) operation.
         1084  +
         1085  +   TODO: add cson_string_length_chars() (is O(N) unless we add another
         1086  +   member to store the char length).
         1087  +   
         1088  +   @see cson_string_cstr()
         1089  +*/
         1090  +unsigned int cson_string_length_bytes( cson_string const * str );
         1091  +
         1092  +/**
         1093  +    Returns the number of UTF8 characters in str. This value will
         1094  +    be at most as long as cson_string_length_bytes() for the
         1095  +    same string, and less if it has multi-byte characters.
         1096  +
         1097  +    Returns 0 if str is NULL.
         1098  +*/
         1099  +unsigned int cson_string_length_utf8( cson_string const * str );
         1100  +
         1101  +/**
         1102  +   Like cson_value_get_string(), but returns a copy of the underying
         1103  +   string bytes, which the caller owns and must eventually free
         1104  +   using free().
         1105  +*/
         1106  +char * cson_value_get_string_copy( cson_value const * val );
         1107  +
         1108  +/**
         1109  +   Simplified form of cson_value_fetch_object(). Returns NULL if val
         1110  +   is-not-a object value.
         1111  +*/
         1112  +cson_object * cson_value_get_object( cson_value const * val );
         1113  +
         1114  +/**
         1115  +   Simplified form of cson_value_fetch_array(). Returns NULL if val
         1116  +   is-not-a array value.
         1117  +*/
         1118  +cson_array * cson_value_get_array( cson_value const * val );
         1119  +
         1120  +/**
         1121  +   Const-correct form of cson_value_get_array().
         1122  +*/
         1123  +cson_array const * cson_value_get_array_c( cson_value const * val );
         1124  +
         1125  +/**
         1126  +   If ar is-a array and is at least (pos+1) entries long then *v (if v is not NULL)
         1127  +   is assigned to the value at that position (which may be NULL).
         1128  +
         1129  +   Ownership of the *v return value is unchanged by this call. (The
         1130  +   containing array may share ownership of the value with other
         1131  +   containers.)
         1132  +
         1133  +   If pos is out of range, non-0 is returned and *v is not modified.
         1134  +
         1135  +   If v is NULL then this function returns 0 if pos is in bounds, but does not
         1136  +   otherwise return a value to the caller.
         1137  +*/
         1138  +int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v );
         1139  +
         1140  +/**
         1141  +   Simplified form of cson_array_value_fetch() which returns NULL if
         1142  +   ar is NULL, pos is out of bounds or if ar has no element at that
         1143  +   position.
         1144  +*/
         1145  +cson_value * cson_array_get( cson_array const * ar, unsigned int pos );
         1146  +
         1147  +/**
         1148  +   Ensures that ar has allocated space for at least the given
         1149  +   number of entries. This never shrinks the array and never
         1150  +   changes its logical size, but may pre-allocate space in the
         1151  +   array for storing new (as-yet-unassigned) values.
         1152  +
         1153  +   Returns 0 on success, or non-zero on error:
         1154  +
         1155  +   - If ar is NULL: cson_rc.ArgError
         1156  +
         1157  +   - If allocation fails: cson_rc.AllocError
         1158  +*/
         1159  +int cson_array_reserve( cson_array * ar, unsigned int size );
         1160  +
         1161  +/**
         1162  +   If ar is not NULL, sets *v (if v is not NULL) to the length of the array
         1163  +   and returns 0. Returns cson_rc.ArgError if ar is NULL.
         1164  +*/
         1165  +int cson_array_length_fetch( cson_array const * ar, unsigned int * v );
         1166  +
         1167  +/**
         1168  +   Simplified form of cson_array_length_fetch() which returns 0 if ar
         1169  +   is NULL.
         1170  +*/
         1171  +unsigned int cson_array_length_get( cson_array const * ar );
         1172  +
         1173  +/**
         1174  +   Sets the given index of the given array to the given value.
         1175  +
         1176  +   If ar already has an item at that index then it is cleaned up and
         1177  +   freed before inserting the new item.
         1178  +
         1179  +   ar is expanded, if needed, to be able to hold at least (ndx+1)
         1180  +   items, and any new entries created by that expansion are empty
         1181  +   (NULL values).
         1182  +
         1183  +   On success, 0 is returned and ownership of v is transfered to ar.
         1184  +  
         1185  +   On error ownership of v is NOT modified, and the caller may still
         1186  +   need to clean it up. For example, the following code will introduce
         1187  +   a leak if this function fails:
         1188  +
         1189  +   @code
         1190  +   cson_array_append( myArray, cson_value_new_integer(42) );
         1191  +   @endcode
         1192  +
         1193  +   Because the value created by cson_value_new_integer() has no owner
         1194  +   and is not cleaned up. The "more correct" way to do this is:
         1195  +
         1196  +   @code
         1197  +   cson_value * v = cson_value_new_integer(42);
         1198  +   int rc = cson_array_append( myArray, v );
         1199  +   if( 0 != rc ) {
         1200  +      cson_value_free( v );
         1201  +      ... handle error ...
         1202  +   }
         1203  +   @endcode
         1204  +
         1205  +*/
         1206  +int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v );
         1207  +
         1208  +/**
         1209  +   Appends the given value to the given array, transfering ownership of
         1210  +   v to ar. On error, ownership of v is not modified. Ownership of ar
         1211  +   is never changed by this function.
         1212  +
         1213  +   This is functionally equivalent to
         1214  +   cson_array_set(ar,cson_array_length_get(ar),v), but this
         1215  +   implementation has slightly different array-preallocation policy
         1216  +   (it grows more eagerly).
         1217  +   
         1218  +   Returns 0 on success, non-zero on error. Error cases include:
         1219  +
         1220  +   - ar or v are NULL: cson_rc.ArgError
         1221  +
         1222  +   - Array cannot be expanded to hold enough elements: cson_rc.AllocError.
         1223  +
         1224  +   - Appending would cause a numeric overlow in the array's size:
         1225  +   cson_rc.RangeError.  (However, you'll get an AllocError long before
         1226  +   that happens!)
         1227  +
         1228  +   On error ownership of v is NOT modified, and the caller may still
         1229  +   need to clean it up. See cson_array_set() for the details.
         1230  +
         1231  +*/
         1232  +int cson_array_append( cson_array * ar, cson_value * v );
         1233  +
         1234  +
         1235  +/**
         1236  +   Creates a new cson_value from the given boolean value.
         1237  +
         1238  +   Ownership of the new value is passed to the caller, who must
         1239  +   eventually either free the value using cson_value_free() or
         1240  +   inserting it into a container (array or object), which transfers
         1241  +   ownership to the container. See the cson_value class documentation
         1242  +   for more details.
         1243  +
         1244  +   Semantically speaking this function Returns NULL on allocation
         1245  +   error, but the implementation never actually allocates for this
         1246  +   case. Nonetheless, it must be treated as if it were an allocated
         1247  +   value.
         1248  +*/
         1249  +cson_value * cson_value_new_bool( char v );
         1250  +
         1251  +
         1252  +/**
         1253  +   Alias for cson_value_new_bool(v).
         1254  +*/
         1255  +cson_value * cson_new_bool(char v);
         1256  +
         1257  +/**
         1258  +   Returns the special JSON "null" value. When outputing JSON,
         1259  +   its string representation is "null" (without the quotes).
         1260  +   
         1261  +   See cson_value_new_bool() for notes regarding the returned
         1262  +   value's memory.
         1263  +*/
         1264  +cson_value * cson_value_null();
         1265  +
         1266  +/**
         1267  +   Equivalent to cson_value_new_bool(1).
         1268  +*/
         1269  +cson_value * cson_value_true();
         1270  +
         1271  +/**
         1272  +   Equivalent to cson_value_new_bool(0).
         1273  +*/
         1274  +cson_value * cson_value_false();
         1275  +
         1276  +/**
         1277  +   Semantically the same as cson_value_new_bool(), but for integers.
         1278  +*/
         1279  +cson_value * cson_value_new_integer( cson_int_t v );
         1280  +
         1281  +/**
         1282  +   Alias for cson_value_new_integer(v).
         1283  +*/
         1284  +cson_value * cson_new_int(cson_int_t v);
         1285  +
         1286  +/**
         1287  +   Semantically the same as cson_value_new_bool(), but for doubles.
         1288  +*/
         1289  +cson_value * cson_value_new_double( cson_double_t v );
         1290  +
         1291  +/**
         1292  +   Alias for cson_value_new_double(v).
         1293  +*/
         1294  +cson_value * cson_new_double(cson_double_t v);
         1295  +
         1296  +/**
         1297  +   Semantically the same as cson_value_new_bool(), but for strings.
         1298  +   This creates a JSON value which copies the first n bytes of str.
         1299  +   The string will automatically be NUL-terminated.
         1300  +   
         1301  +   Note that if str is NULL or n is 0, this function still
         1302  +   returns non-NULL value representing that empty string.
         1303  +   
         1304  +   Returns NULL on allocation error.
         1305  +   
         1306  +   See cson_value_new_bool() for important information about the
         1307  +   returned memory.
         1308  +*/
         1309  +cson_value * cson_value_new_string( char const * str, unsigned int n );
         1310  +
         1311  +/**
         1312  +   Allocates a new "object" value and transfers ownership of it to the
         1313  +   caller. It must eventually be destroyed, by the caller or its
         1314  +   owning container, by passing it to cson_value_free().
         1315  +
         1316  +   Returns NULL on allocation error.
         1317  +
         1318  +   Post-conditions: cson_value_is_object(value) will return true.
         1319  +
         1320  +   @see cson_value_new_array()
         1321  +   @see cson_value_free()
         1322  +*/
         1323  +cson_value * cson_value_new_object();
         1324  +
         1325  +/**
         1326  +   This works like cson_value_new_object() but returns an Object
         1327  +   handle directly.
         1328  +
         1329  +   The value handle for the returned object can be fetched with
         1330  +   cson_object_value(theObject).
         1331  +   
         1332  +   Ownership is transfered to the caller, who must eventually free it
         1333  +   by passing the Value handle (NOT the Object handle) to
         1334  +   cson_value_free() or passing ownership to a parent container.
         1335  +
         1336  +   Returns NULL on error (out of memory).
         1337  +*/
         1338  +cson_object * cson_new_object();
         1339  +
         1340  +/**
         1341  +   Identical to cson_new_object() except that it creates
         1342  +   an Array.
         1343  +*/
         1344  +cson_array * cson_new_array();
         1345  +
         1346  +/**
         1347  +   Identical to cson_new_object() except that it creates
         1348  +   a String.
         1349  +*/
         1350  +cson_string * cson_new_string(char const * val, unsigned int len);
         1351  +
         1352  +/**
         1353  +   Equivalent to cson_value_free(cson_object_value(x)).
         1354  +*/
         1355  +void cson_free_object(cson_object *x);
         1356  +
         1357  +/**
         1358  +   Equivalent to cson_value_free(cson_array_value(x)).
         1359  +*/
         1360  +void cson_free_array(cson_array *x);
         1361  +
         1362  +/**
         1363  +   Equivalent to cson_value_free(cson_string_value(x)).
         1364  +*/
         1365  +void cson_free_string(cson_string *x);
         1366  +
         1367  +
         1368  +/**
         1369  +   Allocates a new "array" value and transfers ownership of it to the
         1370  +   caller. It must eventually be destroyed, by the caller or its
         1371  +   owning container, by passing it to cson_value_free().
         1372  +
         1373  +   Returns NULL on allocation error.
         1374  +
         1375  +   Post-conditions: cson_value_is_array(value) will return true.
         1376  +
         1377  +   @see cson_value_new_object()
         1378  +   @see cson_value_free()
         1379  +*/
         1380  +cson_value * cson_value_new_array();
         1381  +
         1382  +/**
         1383  +   Frees any resources owned by v, then frees v. If v is a container
         1384  +   type (object or array) its children are also freed (recursively).
         1385  +
         1386  +   If v is NULL, this is a no-op.
         1387  +
         1388  +   This function decrements a reference count and only destroys the
         1389  +   value if its reference count drops to 0. Reference counts are
         1390  +   increased by either inserting the value into a container or via
         1391  +   cson_value_add_reference(). Even if this function does not
         1392  +   immediately destroy the value, the value must be considered, from
         1393  +   the perspective of that client code, to have been
         1394  +   destroyed/invalidated by this call.
         1395  +
         1396  +   
         1397  +   @see cson_value_new_object()
         1398  +   @see cson_value_new_array()
         1399  +   @see cson_value_add_reference()
         1400  +*/
         1401  +void cson_value_free(cson_value * v);
         1402  +
         1403  +/**
         1404  +   Alias for cson_value_free().
         1405  +*/
         1406  +void cson_free_value(cson_value * v);
         1407  +
         1408  +
         1409  +/**
         1410  +   Functionally similar to cson_array_set(), but uses a string key
         1411  +   as an index. Like arrays, if a value already exists for the given key,
         1412  +   it is destroyed by this function before inserting the new value.
         1413  +
         1414  +   If v is NULL then this call is equivalent to
         1415  +   cson_object_unset(obj,key). Note that (v==NULL) is treated
         1416  +   differently from v having the special null value. In the latter
         1417  +   case, the key is set to the special null value.
         1418  +
         1419  +   The key may be encoded as ASCII or UTF8. Results are undefined
         1420  +   with other encodings, and the errors won't show up here, but may
         1421  +   show up later, e.g. during output.
         1422  +   
         1423  +   Returns 0 on success, non-0 on error. It has the following error
         1424  +   cases:
         1425  +
         1426  +   - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0.
         1427  +
         1428  +   - cson_rc.AllocError: an out-of-memory error
         1429  +
         1430  +   On error ownership of v is NOT modified, and the caller may still
         1431  +   need to clean it up. For example, the following code will introduce
         1432  +   a leak if this function fails:
         1433  +
         1434  +   @code
         1435  +   cson_object_set( myObj, "foo", cson_value_new_integer(42) );
         1436  +   @endcode
         1437  +
         1438  +   Because the value created by cson_value_new_integer() has no owner
         1439  +   and is not cleaned up. The "more correct" way to do this is:
         1440  +
         1441  +   @code
         1442  +   cson_value * v = cson_value_new_integer(42);
         1443  +   int rc = cson_object_set( myObj, "foo", v );
         1444  +   if( 0 != rc ) {
         1445  +      cson_value_free( v );
         1446  +      ... handle error ...
         1447  +   }
         1448  +   @endcode
         1449  +
         1450  +   Potential TODOs:
         1451  +
         1452  +   - Add an overload which takes a cson_value key instead. To get
         1453  +   any value out of that we first need to be able to convert arbitrary
         1454  +   value types to strings. We could simply to-JSON them and use those
         1455  +   as keys.
         1456  +*/
         1457  +int cson_object_set( cson_object * obj, char const * key, cson_value * v );
         1458  +
         1459  +/**
         1460  +   Functionaly equivalent to cson_object_set(), but takes a
         1461  +   cson_string() as its KEY type. The string will be reference-counted
         1462  +   like any other values, and the key may legally be used within this
         1463  +   same container (as a value) or others (as a key or value) at the
         1464  +   same time.
         1465  +
         1466  +   Returns 0 on success. On error, ownership (i.e. refcounts) of key
         1467  +   and value are not modified. On success key and value will get
         1468  +   increased refcounts unless they are replacing themselves (which is
         1469  +   a harmless no-op).
         1470  +*/
         1471  +int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v );
         1472  +
         1473  +/**
         1474  +   Removes a property from an object.
         1475  +   
         1476  +   If obj contains the given key, it is removed and 0 is returned. If
         1477  +   it is not found, cson_rc.NotFoundError is returned (which can
         1478  +   normally be ignored by client code).
         1479  +
         1480  +   cson_rc.ArgError is returned if obj or key are NULL or key has
         1481  +   a length of 0.
         1482  +
         1483  +   Returns 0 if the given key is found and removed.
         1484  +
         1485  +   This is functionally equivalent calling
         1486  +   cson_object_set(obj,key,NULL).
         1487  +*/
         1488  +int cson_object_unset( cson_object * obj, char const * key );
         1489  +
         1490  +/**
         1491  +   Searches the given object for a property with the given key. If found,
         1492  +   it is returned. If no match is found, or any arguments are NULL, NULL is
         1493  +   returned. The returned object is owned by obj, and may be invalidated
         1494  +   by ANY operations which change obj's property list (i.e. add or remove
         1495  +   properties).
         1496  +
         1497  +   FIXME: allocate the key/value pairs like we do for cson_array,
         1498  +   to get improve the lifetimes of fetched values.
         1499  +
         1500  +   @see cson_object_fetch_sub()
         1501  +   @see cson_object_get_sub()
         1502  +*/
         1503  +cson_value * cson_object_get( cson_object const * obj, char const * key );
         1504  +
         1505  +/**
         1506  +   Equivalent to cson_object_get() but takes a cson_string argument
         1507  +   instead of a C-style string.
         1508  +*/
         1509  +cson_value * cson_object_get_s( cson_object const * obj, cson_string const *key );
         1510  +
         1511  +/**
         1512  +   Similar to cson_object_get(), but removes the value from the parent
         1513  +   object's ownership. If no item is found then NULL is returned, else
         1514  +   the object (now owned by the caller or possibly shared with other
         1515  +   containers) is returned.
         1516  +
         1517  +   Returns NULL if either obj or key are NULL or key has a length
         1518  +   of 0.
         1519  +
         1520  +   This function reduces the returned value's reference count but has
         1521  +   the specific property that it does not treat refcounts 0 and 1
         1522  +   identically, meaning that the returned object may have a refcount
         1523  +   of 0. This behaviour works around a corner-case where we want to
         1524  +   extract a child element from its parent and then destroy the parent
         1525  +   (which leaves us in an undesireable (normally) reference count
         1526  +   state).
         1527  +*/
         1528  +cson_value * cson_object_take( cson_object * obj, char const * key );
         1529  +
         1530  +/**
         1531  +    Fetches a property from a child (or [great-]*grand-child) object.
         1532  +
         1533  +    obj is the object to search.
         1534  +
         1535  +    path is a delimited string, where the delimiter is the given
         1536  +    separator character.
         1537  +
         1538  +    This function searches for the given path, starting at the given object
         1539  +    and traversing its properties as the path specifies. If a given part of the
         1540  +    path is not found, then this function fails with cson_rc.NotFoundError.
         1541  +
         1542  +    If it finds the given path, it returns the value by assiging *tgt
         1543  +    to it.  If tgt is NULL then this function has no side-effects but
         1544  +    will return 0 if the given path is found within the object, so it can be used
         1545  +    to test for existence without fetching it.
         1546  +    
         1547  +    Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds
         1548  +    no item, and any other non-zero error code on a "real" error. Errors include:
         1549  +
         1550  +   - obj or path are NULL: cson_rc.ArgError
         1551  +    
         1552  +    - separator is 0, or path is an empty string or contains only
         1553  +    separator characters: cson_rc.RangeError
         1554  +
         1555  +    - There is an upper limit on how long a single path component may
         1556  +    be (some "reasonable" internal size), and cson_rc.RangeError is
         1557  +    returned if that length is violated.
         1558  +
         1559  +    
         1560  +    Limitations:
         1561  +
         1562  +    - It has no way to fetch data from arrays this way. i could
         1563  +    imagine, e.g., a path of "subobj.subArray.0" for
         1564  +    subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too
         1565  +    lazy/tired to add this.
         1566  +
         1567  +    Example usage:
         1568  +    
         1569  +
         1570  +    Assume we have a JSON structure which abstractly looks like:
         1571  +
         1572  +    @code
         1573  +    {"subobj":{"subsubobj":{"myValue":[1,2,3]}}}
         1574  +    @endcode
         1575  +
         1576  +    Out goal is to get the value of myValue. We can do that with:
         1577  +
         1578  +    @code
         1579  +    cson_value * v = NULL;
         1580  +    int rc = cson_object_fetch_sub( object, &v, "subobj.subsubobj.myValue", '.' );
         1581  +    @endcode
         1582  +
         1583  +    Note that because keys in JSON may legally contain a '.', the
         1584  +    separator must be specified by the caller. e.g. the path
         1585  +    "subobj/subsubobj/myValue" with separator='/' is equivalent the
         1586  +    path "subobj.subsubobj.myValue" with separator='.'. The value of 0
         1587  +    is not legal as a separator character because we cannot
         1588  +    distinguish that use from the real end-of-string without requiring
         1589  +    the caller to also pass in the length of the string.
         1590  +   
         1591  +    Multiple successive separators in the list are collapsed into a
         1592  +    single separator for parsing purposes. e.g. the path "a...b...c"
         1593  +    (separator='.') is equivalent to "a.b.c".
         1594  +
         1595  +    @see cson_object_get_sub()
         1596  +    @see cson_object_get_sub2()
         1597  +*/
         1598  +int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char separator );
         1599  +
         1600  +/**
         1601  +   Similar to cson_object_fetch_sub(), but derives the path separator
         1602  +   character from the first byte of the path argument. e.g. the
         1603  +   following arg equivalent:
         1604  +
         1605  +   @code
         1606  +   cson_object_fetch_sub( obj, &tgt, "foo.bar.baz", '.' );
         1607  +   cson_object_fetch_sub2( obj, &tgt, ".foo.bar.baz" );
         1608  +   @endcode
         1609  +*/
         1610  +int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path );
         1611  +
         1612  +/**
         1613  +   Convenience form of cson_object_fetch_sub() which returns NULL if the given
         1614  +   item is not found.
         1615  +*/
         1616  +cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep );
         1617  +
         1618  +/**
         1619  +   Convenience form of cson_object_fetch_sub2() which returns NULL if the given
         1620  +   item is not found.
         1621  +*/
         1622  +cson_value * cson_object_get_sub2( cson_object const * obj, char const * path );
         1623  +
         1624  +/** @enum CSON_MERGE_FLAGS
         1625  +
         1626  +    Flags for cson_object_merge().
         1627  +*/
         1628  +enum CSON_MERGE_FLAGS {
         1629  +    CSON_MERGE_DEFAULT = 0,
         1630  +    CSON_MERGE_REPLACE = 0x01,
         1631  +    CSON_MERGE_NO_RECURSE = 0x02
         1632  +};
         1633  +
         1634  +/**
         1635  +   "Merges" the src object's properties into dest. Each property in
         1636  +   src is copied (using reference counting, not cloning) into dest. If
         1637  +   dest already has the given property then behaviour depends on the
         1638  +   flags argument:
         1639  +
         1640  +   If flag has the CSON_MERGE_REPLACE bit set then this function will
         1641  +   by default replace non-object properties with the src property. If
         1642  +   src and dest both have the property AND it is an Object then this
         1643  +   function operates recursively on those objects. If
         1644  +   CSON_MERGE_NO_RECURSE is set then objects are not recursed in this
         1645  +   manner, and will be completely replaced if CSON_MERGE_REPLACE is
         1646  +   set.
         1647  +
         1648  +   Array properties in dest are NOT recursed for merging - they are
         1649  +   either replaced or left as-is, depending on whether flags contains
         1650  +   he CSON_MERGE_REPLACE bit.
         1651  +
         1652  +   Returns 0 on success. The error conditions are:
         1653  +
         1654  +   - dest or src are NULL or (dest==src) returns cson_rc.ArgError.
         1655  +
         1656  +   - dest or src contain cyclic references - this will likely cause a
         1657  +   crash due to endless recursion.
         1658  +
         1659  +   Potential TODOs:
         1660  +
         1661  +   - Add a flag to copy clones, not the original values.
         1662  +*/
         1663  +int cson_object_merge( cson_object * dest, cson_object const * src, int flags );
         1664  +
         1665  +
         1666  +/**
         1667  +   An iterator type for traversing object properties.
         1668  +
         1669  +   Its values must be considered private, not to be touched by client
         1670  +   code.
         1671  +
         1672  +   @see cson_object_iter_init()
         1673  +   @see cson_object_iter_next()
         1674  +*/
         1675  +struct cson_object_iterator
         1676  +{
         1677  +    
         1678  +    /** @internal
         1679  +        The underlying object.
         1680  +    */
         1681  +    cson_object const * obj;
         1682  +    /** @internal
         1683  +        Current position in the property list.
         1684  +     */
         1685  +    unsigned int pos;
         1686  +};
         1687  +typedef struct cson_object_iterator cson_object_iterator;
         1688  +
         1689  +/**
         1690  +   Empty-initialized cson_object_iterator object.
         1691  +*/
         1692  +#define cson_object_iterator_empty_m {NULL/*obj*/,0/*pos*/}
         1693  +
         1694  +/**
         1695  +   Empty-initialized cson_object_iterator object.
         1696  +*/
         1697  +extern const cson_object_iterator cson_object_iterator_empty;
         1698  +
         1699  +/**
         1700  +   Initializes the given iterator to point at the start of obj's
         1701  +   properties. Returns 0 on success or cson_rc.ArgError if !obj
         1702  +   or !iter.
         1703  +
         1704  +   obj must outlive iter, or results are undefined. Results are also
         1705  +   undefined if obj is modified while the iterator is active.
         1706  +
         1707  +   @see cson_object_iter_next()
         1708  +*/
         1709  +int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter );
         1710  +
         1711  +/** @struct cson_kvp
         1712  +
         1713  +This class represents a key/value pair and is used for storing
         1714  +object properties. It is opaque to client code, and the public
         1715  +API only uses this type for purposes of iterating over cson_object
         1716  +properties using the cson_object_iterator interfaces.
         1717  +*/
         1718  +
         1719  +typedef struct cson_kvp cson_kvp;
         1720  +
         1721  +/**
         1722  +   Returns the next property from the given iterator's object, or NULL
         1723  +   if the end of the property list as been reached.
         1724  +
         1725  +   Note that the order of object properties is undefined by the API,
         1726  +   and may change from version to version.
         1727  +
         1728  +   The returned memory belongs to the underlying object and may be
         1729  +   invalidated by any changes to that object.
         1730  +
         1731  +   Example usage:
         1732  +
         1733  +   @code
         1734  +   cson_object_iterator it;
         1735  +   cson_object_iter_init( myObject, &it ); // only fails if either arg is 0
         1736  +   cson_kvp * kvp;
         1737  +   cson_string const * key;
         1738  +   cson_value const * val;
         1739  +   while( (kvp = cson_object_iter_next(&it) ) )
         1740  +   {
         1741  +       key = cson_kvp_key(kvp);
         1742  +       val = cson_kvp_value(kvp);
         1743  +       ...
         1744  +   }
         1745  +   @endcode
         1746  +
         1747  +   There is no need to clean up an iterator, as it holds no dynamic resources.
         1748  +   
         1749  +   @see cson_kvp_key()
         1750  +   @see cson_kvp_value()
         1751  +*/
         1752  +cson_kvp * cson_object_iter_next( cson_object_iterator * iter );
         1753  +
         1754  +
         1755  +/**
         1756  +   Returns the key associated with the given key/value pair,
         1757  +   or NULL if !kvp. The memory is owned by the object which contains
         1758  +   the key/value pair, and may be invalidated by any modifications
         1759  +   to that object.
         1760  +*/
         1761  +cson_string * cson_kvp_key( cson_kvp const * kvp );
         1762  +
         1763  +/**
         1764  +   Returns the value associated with the given key/value pair,
         1765  +   or NULL if !kvp. The memory is owned by the object which contains
         1766  +   the key/value pair, and may be invalidated by any modifications
         1767  +   to that object.
         1768  +*/
         1769  +cson_value * cson_kvp_value( cson_kvp const * kvp );
         1770  +
         1771  +/** @typedef some unsigned int type cson_size_t
         1772  +
         1773  +*/
         1774  +typedef unsigned int cson_size_t;
         1775  +
         1776  +/**
         1777  +   A generic buffer class.
         1778  +
         1779  +   They can be used like this:
         1780  +
         1781  +   @code
         1782  +   cson_buffer b = cson_buffer_empty;
         1783  +   int rc = cson_buffer_reserve( &buf, 100 );
         1784  +   if( 0 != rc ) { ... allocation error ... }
         1785  +   ... use buf.mem ...
         1786  +   ... then free it up ...
         1787  +   cson_buffer_reserve( &buf, 0 );
         1788  +   @endcode
         1789  +
         1790  +   To take over ownership of a buffer's memory:
         1791  +
         1792  +   @code
         1793  +   void * mem = b.mem;
         1794  +   // mem is b.capacity bytes long, but only b.used
         1795  +   // bytes of it has been "used" by the API.
         1796  +   b = cson_buffer_empty;
         1797  +   @endcode
         1798  +
         1799  +   The memory now belongs to the caller and must eventually be
         1800  +   free()d.
         1801  +*/
         1802  +struct cson_buffer
         1803  +{
         1804  +    /**
         1805  +       The number of bytes allocated for this object.
         1806  +       Use cson_buffer_reserve() to change its value.
         1807  +     */
         1808  +    cson_size_t capacity;
         1809  +    /**
         1810  +       The number of bytes "used" by this object. It is not needed for
         1811  +       all use cases, and management of this value (if needed) is up
         1812  +       to the client. The cson_buffer public API does not use this
         1813  +       member. The intention is that this can be used to track the
         1814  +       length of strings which are allocated via cson_buffer, since
         1815  +       they need an explicit length and/or null terminator.
         1816  +     */
         1817  +    cson_size_t used;
         1818  +
         1819  +    /**
         1820  +       This is a debugging/metric-counting value
         1821  +       intended to help certain malloc()-conscious
         1822  +       clients tweak their memory reservation sizes.
         1823  +       Each time cson_buffer_reserve() expands the
         1824  +       buffer, it increments this value by 1.
         1825  +    */
         1826  +    cson_size_t timesExpanded;
         1827  +
         1828  +    /**
         1829  +       The memory allocated for and owned by this buffer.
         1830  +       Use cson_buffer_reserve() to change its size or
         1831  +       free it. To take over ownership, do:
         1832  +
         1833  +       @code
         1834  +       void * myptr = buf.mem;
         1835  +       buf = cson_buffer_empty;
         1836  +       @endcode
         1837  +
         1838  +       (You might also need to store buf.used and buf.capacity,
         1839  +       depending on what you want to do with the memory.)
         1840  +       
         1841  +       When doing so, the memory must eventually be passed to free()
         1842  +       to deallocate it.
         1843  +    */
         1844  +    unsigned char * mem;
         1845  +};
         1846  +/** Convenience typedef. */
         1847  +typedef struct cson_buffer cson_buffer;
         1848  +
         1849  +/** An empty-initialized cson_buffer object. */
         1850  +#define cson_buffer_empty_m {0/*capacity*/,0/*used*/,0/*timesExpanded*/,NULL/*mem*/}
         1851  +/** An empty-initialized cson_buffer object. */
         1852  +extern const cson_buffer cson_buffer_empty;
         1853  +
         1854  +/**
         1855  +   Uses cson_output() to append all JSON output to the given buffer
         1856  +   object. The semantics for the (v, opt) parameters, and the return
         1857  +   value, are as documented for cson_output(). buf must be a non-NULL
         1858  +   pointer to a properly initialized buffer (see example below).
         1859  +
         1860  +   Ownership of buf is not changed by calling this.
         1861  +
         1862  +   On success 0 is returned and the contents of buf.mem are guaranteed
         1863  +   to be NULL-terminated. On error the buffer might contain partial
         1864  +   contents, and it should not be used except to free its contents.
         1865  +
         1866  +   On error non-zero is returned. Errors include:
         1867  +
         1868  +   - Invalid arguments: cson_rc.ArgError
         1869  +
         1870  +   - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError
         1871  +   
         1872  +   Example usage:
         1873  +
         1874  +   @code
         1875  +   cson_buffer buf = cson_buffer_empty;
         1876  +   // optional: cson_buffer_reserve(&buf, 1024 * 10);
         1877  +   int rc = cson_output_buffer( myValue, &buf, NULL );
         1878  +   if( 0 != rc ) {
         1879  +       ... error! ...
         1880  +   }
         1881  +   else {
         1882  +       ... use buffer ...
         1883  +       puts((char const*)buf.mem);
         1884  +   }
         1885  +   // In both cases, we eventually need to clean up the buffer:
         1886  +   cson_buffer_reserve( &buf, 0 );
         1887  +   // Or take over ownership of its memory:
         1888  +   {
         1889  +       char * mem = (char *)buf.mem;
         1890  +       buf = cson_buffer_empty;
         1891  +       ...
         1892  +       free(mem);
         1893  +   }
         1894  +   @endcode
         1895  +   
         1896  +   @see cson_output()
         1897  +   
         1898  +*/
         1899  +int cson_output_buffer( cson_value const * v, cson_buffer * buf,
         1900  +                        cson_output_opt const * opt );
         1901  +
         1902  +/**
         1903  +   This works identically to cson_parse_string(), but takes a
         1904  +   cson_buffer object as its input.  buf->used bytes of buf->mem are
         1905  +   assumed to be valid JSON input, but it need not be NUL-terminated
         1906  +   (we only read up to buf->used bytes). The value of buf->used is
         1907  +   assumed to be the "string length" of buf->mem, i.e. not including
         1908  +   the NUL terminator.
         1909  +
         1910  +   Returns 0 on success, non-0 on error.
         1911  +
         1912  +   See cson_parse() for the semantics of the tgt, opt, and err
         1913  +   parameters.
         1914  +*/
         1915  +int cson_parse_buffer( cson_value ** tgt, cson_buffer const * buf,
         1916  +                       cson_parse_opt const * opt, cson_parse_info * err );
         1917  +
         1918  +
         1919  +/**
         1920  +   Reserves the given amount of memory for the given buffer object.
         1921  +
         1922  +   If n is 0 then buf->mem is freed and its state is set to
         1923  +   NULL/0 values.
         1924  +
         1925  +   If buf->capacity is less than or equal to n then 0 is returned and
         1926  +   buf is not modified.
         1927  +
         1928  +   If n is larger than buf->capacity then buf->mem is (re)allocated
         1929  +   and buf->capacity contains the new length. Newly-allocated bytes
         1930  +   are filled with zeroes.
         1931  +
         1932  +   On success 0 is returned. On error non-0 is returned and buf is not
         1933  +   modified.
         1934  +
         1935  +   buf->mem is owned by buf and must eventually be freed by passing an
         1936  +   n value of 0 to this function.
         1937  +
         1938  +   buf->used is never modified by this function unless n is 0, in which case
         1939  +   it is reset.
         1940  +*/
         1941  +int cson_buffer_reserve( cson_buffer * buf, cson_size_t n );
         1942  +
         1943  +/**
         1944  +   Fills all bytes of the given buffer with the given character.
         1945  +   Returns the number of bytes set (buf->capacity), or 0 if
         1946  +   !buf or buf has no memory allocated to it.
         1947  +*/
         1948  +cson_size_t cson_buffer_fill( cson_buffer * buf, char c );
         1949  +
         1950  +/**
         1951  +    Uses a cson_data_source_f() function to buffer input into a
         1952  +    cson_buffer.
         1953  +
         1954  +   dest must be a non-NULL, initialized (though possibly empty)
         1955  +   cson_buffer object. Its contents, if any, will be overwritten by
         1956  +   this function, and any memory it holds might be re-used.
         1957  +
         1958  +   The src function is called, and passed the state parameter, to
         1959  +   fetch the input. If it returns non-0, this function returns that
         1960  +   error code. src() is called, possibly repeatedly, until it reports
         1961  +   that there is no more data.
         1962  +
         1963  +   Whether or not this function succeeds, dest still owns any memory
         1964  +   pointed to by dest->mem, and the client must eventually free it by
         1965  +   calling cson_buffer_reserve(dest,0).
         1966  +
         1967  +   dest->mem might (and possibly will) be (re)allocated by this
         1968  +   function, so any pointers to it held from before this call might be
         1969  +   invalidated by this call.
         1970  +   
         1971  +   On error non-0 is returned and dest has almost certainly been
         1972  +   modified but its state must be considered incomplete.
         1973  +
         1974  +   Errors include:
         1975  +
         1976  +   - dest or src are NULL (cson_rc.ArgError)
         1977  +
         1978  +   - Allocation error (cson_rc.AllocError)
         1979  +
         1980  +   - src() returns an error code
         1981  +
         1982  +   Whether or not the state parameter may be NULL depends on
         1983  +   the src implementation requirements.
         1984  +
         1985  +   On success dest will contain the contents read from the input
         1986  +   source. dest->used will be the length of the read-in data, and
         1987  +   dest->mem will point to the memory. dest->mem is automatically
         1988  +   NUL-terminated if this function succeeds, but dest->used does not
         1989  +   count that terminator. On error the state of dest->mem must be
         1990  +   considered incomplete, and is not guaranteed to be NUL-terminated.
         1991  +
         1992  +    Example usage:
         1993  +
         1994  +    @code
         1995  +    cson_buffer buf = cson_buffer_empty;
         1996  +    int rc = cson_buffer_fill_from( &buf,
         1997  +                                    cson_data_source_FILE,
         1998  +                                    stdin );
         1999  +    if( rc )
         2000  +    {
         2001  +        fprintf(stderr,"Error %d (%s) while filling buffer.\n",
         2002  +                rc, cson_rc_string(rc));
         2003  +        cson_buffer_reserve( &buf, 0 );
         2004  +        return ...;
         2005  +    }
         2006  +    ... use the buf->mem ...
         2007  +    ... clean up the buffer ...
         2008  +    cson_buffer_reserve( &buf, 0 );
         2009  +    @endcode
         2010  +
         2011  +    To take over ownership of the buffer's memory, do:
         2012  +
         2013  +    @code
         2014  +    void * mem = buf.mem;
         2015  +    buf = cson_buffer_empty;
         2016  +    @endcode
         2017  +
         2018  +    In which case the memory must eventually be passed to free() to
         2019  +    free it.    
         2020  +*/
         2021  +int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state );
         2022  +
         2023  +
         2024  +/**
         2025  +   Increments the reference count for the given value. This is a
         2026  +   low-level operation and should not normally be used by client code
         2027  +   without understanding exactly what side-effects it introduces.
         2028  +   Mis-use can lead to premature destruction or cause a value instance
         2029  +   to never be properly destructed (i.e. a memory leak).
         2030  +
         2031  +   This function is probably only useful for the following cases:
         2032  +
         2033  +   - You want to hold a reference to a value which is itself contained
         2034  +   in one or more containers, and you need to be sure that your
         2035  +   reference outlives the container(s) and/or that you can free your
         2036  +   copy of the reference without invaliding any references to the same
         2037  +   value held in containers.
         2038  +
         2039  +   - You want to implement "value sharing" behaviour without using an
         2040  +   object or array to contain the shared value. This can be used to
         2041  +   ensure the lifetime of the shared value instance. Each sharing
         2042  +   point adds a reference and simply passed the value to
         2043  +   cson_value_free() when they're done. The object will be kept alive
         2044  +   for other sharing points which added a reference.
         2045  +
         2046  +   Normally any such value handles would be invalidated when the
         2047  +   parent container(s) is/are cleaned up, but this function can be
         2048  +   used to effectively delay the cleanup.
         2049  +   
         2050  +   This function, at its lowest level, increments the value's
         2051  +   reference count by 1.
         2052  +
         2053  +   To decrement the reference count, pass the value to
         2054  +   cson_value_free(), after which the value must be considered, from
         2055  +   the perspective of that client code, to be destroyed (though it
         2056  +   will not be if there are still other live references to
         2057  +   it). cson_value_free() will not _actually_ destroy the value until
         2058  +   its reference count drops to 0.
         2059  +
         2060  +   Returns 0 on success. The only error conditions are if v is NULL
         2061  +   (cson_rc.ArgError) or if the reference increment would overflow
         2062  +   (cson_rc.RangeError). In theory a client would get allocation
         2063  +   errors long before the reference count could overflow (assuming
         2064  +   those reference counts come from container insertions, as opposed
         2065  +   to via this function).
         2066  +
         2067  +   Insider notes which clients really need to know:
         2068  +   
         2069  +   For shared/constant value instances, such as those returned by
         2070  +   cson_value_true() and cson_value_null(), this function has no side
         2071  +   effects - it does not actually modify the reference count because
         2072  +   (A) those instances are shared across all client code and (B) those
         2073  +   objects are static and never get cleaned up. However, that is an
         2074  +   implementation detail which client code should not rely on. In
         2075  +   other words, if you call cson_value_add_reference() 3 times using
         2076  +   the value returned by cson_value_true() (which is incidentally a
         2077  +   shared cson_value instance), you must eventually call
         2078  +   cson_value_free() 3 times to (semantically) remove those
         2079  +   references. However, internally the reference count for that
         2080  +   specific cson_value instance will not be modified and those
         2081  +   objects will never be freed (they're stack-allocated).
         2082  +
         2083  +   It might be interesting to note that newly-created objects
         2084  +   have a reference count of 0 instead of 1. This is partly because
         2085  +   if the initial reference is counted then it makes ownership
         2086  +   problematic when inserting values into containers. e.g. consider the
         2087  +   following code:
         2088  +
         2089  +   @code
         2090  +   // ACHTUNG: this code is hypothetical and does not reflect
         2091  +   // what actually happens!
         2092  +   cson_value * v =
         2093  +        cson_value_new_integer( 42 ); // v's refcount = 1
         2094  +   cson_array_append( myArray, v ); // v's refcount = 2
         2095  +   @endcode
         2096  +
         2097  +   If that were the case, the client would be forced to free his own
         2098  +   reference after inserting it into the container (which is a bit
         2099  +   counter-intuitive as well as intrusive). It would look a bit like
         2100  +   the following and would have to be done after every create/insert
         2101  +   operation:
         2102  +
         2103  +   @code
         2104  +   // ACHTUNG: this code is hypothetical and does not reflect
         2105  +   // what actually happens!
         2106  +   cson_array_append( myArray, v ); // v's refcount = 2
         2107  +   cson_value_free( v ); // v's refcount = 1
         2108  +   @endcode
         2109  +
         2110  +   (As i said: it's counter-intuitive and intrusive.)
         2111  +
         2112  +   Instead, values start with a refcount of 0 and it is only increased
         2113  +   when the value is added to an object/array container or when this
         2114  +   function is used to manually increment it. cson_value_free() treats
         2115  +   a refcount of 0 or 1 equivalently, destroying the value
         2116  +   instance. The only semantic difference between 0 and 1, for
         2117  +   purposes of cleaning up, is that a value with a non-0 refcount has
         2118  +   been had its refcount adjusted, whereas a 0 refcount indicates a
         2119  +   fresh, "unowned" reference.
         2120  +*/
         2121  +int cson_value_add_reference( cson_value * v );
         2122  +
         2123  +#if 0
         2124  +/**
         2125  +   DO NOT use this unless you know EXACTLY what you're doing.
         2126  +   It is only in the public API to work around a couple corner
         2127  +   cases involving extracting child elements and discarding
         2128  +   their parents.
         2129  +
         2130  +   This function sets v's reference count to the given value.
         2131  +   It does not clean up the object if rc is 0.
         2132  +
         2133  +   Returns 0 on success, non-0 on error.
         2134  +*/
         2135  +int cson_value_refcount_set( cson_value * v, unsigned short rc );
         2136  +#endif
         2137  +
         2138  +/**
         2139  +   Deeply copies a JSON value, be it an object/array or a "plain"
         2140  +   value (e.g. number/string/boolean). If cv is not NULL then this
         2141  +   function makes a deep clone of it and returns that clone. Ownership
         2142  +   of the clone is identical t transfered to the caller, who must
         2143  +   eventually free the value using cson_value_free() or add it to a
         2144  +   container object/array to transfer ownership to the container. The
         2145  +   returned object will be of the same logical type as orig.
         2146  +
         2147  +   ACHTUNG: if orig contains any cyclic references at any depth level
         2148  +   this function will endlessly recurse. (Having _any_ cyclic
         2149  +   references violates this library's requirements.)
         2150  +   
         2151  +   Returns NULL if orig is NULL or if cloning fails. Assuming that
         2152  +   orig is in a valid state, the only "likely" error case is that an
         2153  +   allocation fails while constructing the clone. In other words, if
         2154  +   cloning fails due to something other than an allocation error then
         2155  +   either orig is in an invalid state or there is a bug.
         2156  +
         2157  +   When this function clones Objects or Arrays it shares any immutable
         2158  +   values (including object keys) between the parent and the
         2159  +   clone. Mutable values (Objects and Arrays) are copied, however.
         2160  +   For example, if we clone:
         2161  +
         2162  +   @code
         2163  +   { a: 1, b: 2, c:["hi"] }
         2164  +   @endcode
         2165  +
         2166  +   The cloned object and the array "c" would be a new Object/Array
         2167  +   instances but the object keys (a,b,b) and the values of (a,b), as
         2168  +   well as the string value within the "c" array, would be shared
         2169  +   between the original and the clone. The "c" array itself would be
         2170  +   deeply cloned, such that future changes to the clone are not
         2171  +   visible to the parent, and vice versa, but immutable values within
         2172  +   the array are shared (in this case the string "hi"). The
         2173  +   justification for this heuristic is that immutable values can never
         2174  +   be changed, so there is no harm in sharing them across
         2175  +   clones. Additionally, such types can never contribute to cycles in
         2176  +   a JSON tree, so they are safe to share this way. Objects and
         2177  +   Arrays, on the other hand, can be modified later and can contribute
         2178  +   to cycles, and thus the clone needs to be an independent instance.
         2179  +   Note, however, that if this function directly passed a
         2180  +   non-Object/Array, that value is deeply cloned. The sharing
         2181  +   behaviour only applies when traversing Objects/Arrays.
         2182  +*/
         2183  +cson_value * cson_value_clone( cson_value const * orig );
         2184  +
         2185  +/**
         2186  +   Returns the value handle associated with s. The handle itself owns
         2187  +   s, and ownership of the handle is not changed by calling this
         2188  +   function. If the returned handle is part of a container, calling
         2189  +   cson_value_free() on the returned handle invoked undefined
         2190  +   behaviour (quite possibly downstream when the container tries to
         2191  +   use it).
         2192  +
         2193  +   This function only returns NULL if s is NULL. The length of the
         2194  +   returned string is cson_string_length_bytes().
         2195  +*/
         2196  +cson_value * cson_string_value(cson_string const * s);
         2197  +/**
         2198  +   The Object form of cson_string_value(). See that function
         2199  +   for full details.
         2200  +*/
         2201  +cson_value * cson_object_value(cson_object const * s);
         2202  +
         2203  +/**
         2204  +   The Array form of cson_string_value(). See that function
         2205  +   for full details.
         2206  +*/
         2207  +cson_value * cson_array_value(cson_array const * s);
         2208  +
         2209  +
         2210  +/**
         2211  +   Calculates the approximate in-memory-allocated size of v,
         2212  +   recursively if it is a container type, with the following caveats
         2213  +   and limitations:
         2214  +
         2215  +   If a given value is reference counted then it is only and multiple
         2216  +   times within a traversed container, each reference is counted at
         2217  +   full cost. We have no way of knowing if a given reference has been
         2218  +   visited already and whether it should or should not be counted, so
         2219  +   we pessimistically count them even though the _might_ not really
         2220  +   count for the given object tree (it depends on where the other open
         2221  +   references live).
         2222  +
         2223  +   This function returns 0 if any of the following are true:
         2224  +
         2225  +   - v is NULL
         2226  +
         2227  +   - v is one of the special singleton values (null, bools, empty
         2228  +   string, int 0, double 0.0)
         2229  +
         2230  +   All other values require an allocation, and this will return their
         2231  +   total memory cost, including the cson-specific internals and the
         2232  +   native value(s).
         2233  +
         2234  +   Note that because arrays and objects might have more internal slots
         2235  +   allocated than used, the alloced size of a container does not
         2236  +   necessarily increase when a new item is inserted into it. An interesting
         2237  +   side-effect of this is that when cson_clone()ing an array or object, the
         2238  +   size of the clone can actually be less than the original.
         2239  +*/
         2240  +unsigned int cson_value_msize(cson_value const * v);
         2241  +
         2242  +/**
         2243  +   Parses command-line-style arguments into a JSON object.
         2244  +
         2245  +   It expects arguments to be in any of these forms, and any number
         2246  +   of leading dashes are treated identically:
         2247  +
         2248  +   --key : Treats key as a boolean with a true value.
         2249  +
         2250  +   --key=VAL : Treats VAL as either a double, integer, or string.
         2251  +
         2252  +   --key= : Treats key as a JSON null (not literal NULL) value.
         2253  +
         2254  +   Arguments not starting with a dash are skipped.
         2255  +   
         2256  +   Each key/value pair is inserted into an object.  If a given key
         2257  +   appears more than once then only the final entry is actually
         2258  +   stored.
         2259  +
         2260  +   argc and argv are expected to be values from main() (or similar,
         2261  +   possibly adjusted to remove argv[0]).
         2262  +
         2263  +   tgt must be either a pointer to NULL or a pointer to a
         2264  +   client-provided Object. If (NULL==*tgt) then this function
         2265  +   allocates a new object and on success it stores the new object in
         2266  +   *tgt (it is owned by the caller). If (NULL!=*tgt) then it is
         2267  +   assumed to be a properly allocated object. DO NOT pass a pointer to
         2268  +   an unitialized pointer, as that will fool this function into
         2269  +   thinking it is a valid object and Undefined Behaviour will ensue.
         2270  +
         2271  +   If count is not NULL then the number of arugments parsed by this
         2272  +   function are assigned to it. On error, count will be the number of
         2273  +   options successfully parsed before the error was encountered.
         2274  +
         2275  +   On success:
         2276  +
         2277  +   - 0 is returned.
         2278  +
         2279  +   - If (*tgt==NULL) then *tgt is assigned to a newly-allocated
         2280  +   object, owned by the caller. Note that even if no arguments are
         2281  +   parsed, the object is still created.
         2282  +
         2283  +   On error:
         2284  +
         2285  +   - non-0 is returned
         2286  +
         2287  +   - If (*tgt==NULL) then it is not modified.
         2288  +
         2289  +   - If (*tgt!=NULL) (i.e., the caller provides his own object) then
         2290  +   it might contain partial results.
         2291  +*/
         2292  +int cson_parse_argv_flags( int argc, char const * const * argv,
         2293  +                           cson_object ** tgt, unsigned int * count );
         2294  +
         2295  +
         2296  +/* LICENSE
         2297  +
         2298  +This software's source code, including accompanying documentation and
         2299  +demonstration applications, are licensed under the following
         2300  +conditions...
         2301  +
         2302  +Certain files are imported from external projects and have their own
         2303  +licensing terms. Namely, the JSON_parser.* files. See their files for
         2304  +their official licenses, but the summary is "do what you want [with
         2305  +them] but leave the license text and copyright in place."
         2306  +
         2307  +The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
         2308  +explicitly disclaims copyright in all jurisdictions which recognize
         2309  +such a disclaimer. In such jurisdictions, this software is released
         2310  +into the Public Domain.
         2311  +
         2312  +In jurisdictions which do not recognize Public Domain property
         2313  +(e.g. Germany as of 2011), this software is Copyright (c) 2011 by
         2314  +Stephan G. Beal, and is released under the terms of the MIT License
         2315  +(see below).
         2316  +
         2317  +In jurisdictions which recognize Public Domain property, the user of
         2318  +this software may choose to accept it either as 1) Public Domain, 2)
         2319  +under the conditions of the MIT License (see below), or 3) under the
         2320  +terms of dual Public Domain/MIT License conditions described here, as
         2321  +they choose.
         2322  +
         2323  +The MIT License is about as close to Public Domain as a license can
         2324  +get, and is described in clear, concise terms at:
         2325  +
         2326  +    http://en.wikipedia.org/wiki/MIT_License
         2327  +
         2328  +The full text of the MIT License follows:
         2329  +
         2330  +--
         2331  +Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)
         2332  +
         2333  +Permission is hereby granted, free of charge, to any person
         2334  +obtaining a copy of this software and associated documentation
         2335  +files (the "Software"), to deal in the Software without
         2336  +restriction, including without limitation the rights to use,
         2337  +copy, modify, merge, publish, distribute, sublicense, and/or sell
         2338  +copies of the Software, and to permit persons to whom the
         2339  +Software is furnished to do so, subject to the following
         2340  +conditions:
         2341  +
         2342  +The above copyright notice and this permission notice shall be
         2343  +included in all copies or substantial portions of the Software.
         2344  +
         2345  +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         2346  +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
         2347  +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         2348  +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
         2349  +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
         2350  +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
         2351  +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
         2352  +OTHER DEALINGS IN THE SOFTWARE.
         2353  +
         2354  +--END OF MIT LICENSE--
         2355  +
         2356  +For purposes of the above license, the term "Software" includes
         2357  +documentation and demonstration source code which accompanies
         2358  +this software. ("Accompanies" = is contained in the Software's
         2359  +primary public source code repository.)
         2360  +
         2361  +*/
         2362  +
         2363  +#if defined(__cplusplus)
         2364  +} /*extern "C"*/
         2365  +#endif
         2366  +
         2367  +#endif /* WANDERINGHORSE_NET_CSON_H_INCLUDED */
         2368  +/* end file include/wh/cson/cson.h */
         2369  +/* begin file include/wh/cson/cson_sqlite3.h */
         2370  +/** @file cson_sqlite3.h
         2371  +
         2372  +This file contains cson's public sqlite3-to-JSON API declarations
         2373  +and API documentation. If CSON_ENABLE_SQLITE3 is not defined,
         2374  +or is defined to 0, then including this file will have no side-effects
         2375  +other than defining CSON_ENABLE_SQLITE3 (if it was not defined) to 0
         2376  +and defining a few include guard macros. i.e. if CSON_ENABLE_SQLITE3
         2377  +is not set to a true value then the API is not visible.
         2378  +
         2379  +This API requires that <sqlite3.h> be in the INCLUDES path and that
         2380  +the client eventually link to (or directly embed) the sqlite3 library.
         2381  +*/
         2382  +#if !defined(WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED)
         2383  +#define WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED 1
         2384  +#if !defined(CSON_ENABLE_SQLITE3)
         2385  +#  if defined(DOXYGEN)
         2386  +#define CSON_ENABLE_SQLITE3 1
         2387  +#  else
         2388  +#define CSON_ENABLE_SQLITE3 1
         2389  +#  endif
         2390  +#endif
         2391  +
         2392  +#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
         2393  +#include <sqlite3.h>
         2394  +
         2395  +#if defined(__cplusplus)
         2396  +extern "C" {
         2397  +#endif
         2398  +
         2399  +/**
         2400  +   Converts a single value from a single 0-based column index to its JSON
         2401  +   equivalent.
         2402  +
         2403  +   On success it returns a new JSON value, which will have a different concrete
         2404  +   type depending on the field type reported by sqlite3_column_type(st,col):
         2405  +
         2406  +   Integer, double, null, or string (TEXT and BLOB data, though not
         2407  +   all blob data is legal for a JSON string).
         2408  +
         2409  +   st must be a sqlite3_step()'d row and col must be a 0-based column
         2410  +   index within that result row.
         2411  + */       
         2412  +cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
         2413  +
         2414  +/**
         2415  +   Creates a JSON Array object containing the names of all columns
         2416  +   of the given prepared statement handle. 
         2417  +    
         2418  +   Returns a new array value on success, which the caller owns. Its elements
         2419  +   are in the same order as in the underlying query.
         2420  +
         2421  +   On error NULL is returned.
         2422  +    
         2423  +   st is not traversed or freed by this function - only the column
         2424  +   count and names are read.
         2425  +*/
         2426  +cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
         2427  +
         2428  +/**
         2429  +   Creates a JSON Object containing key/value pairs corresponding
         2430  +   to the result columns in the current row of the given statement
         2431  +   handle. st must be a sqlite3_step()'d row result.
         2432  +
         2433  +   On success a new Object is returned which is owned by the
         2434  +   caller. On error NULL is returned.
         2435  +
         2436  +   cson_sqlite3_column_to_value() is used to convert each column to a
         2437  +   JSON value, and the column names are taken from
         2438  +   sqlite3_column_name().
         2439  +*/
         2440  +cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st );
         2441  +/**
         2442  +   Functionally almost identical to cson_sqlite3_row_to_object(), the
         2443  +   only difference being how the result objects gets its column names.
         2444  +   st must be a freshly-step()'d handle holding a result row.
         2445  +   colNames must be an Array with at least the same number of columns
         2446  +   as st. If it has fewer, NULL is returned and this function has
         2447  +   no side-effects.
         2448  +
         2449  +   For each column in the result set, the colNames entry at the same
         2450  +   index is used for the column key. If a given entry is-not-a String
         2451  +   then conversion will fail and NULL will be returned.
         2452  +
         2453  +   The one reason to prefer this over cson_sqlite3_row_to_object() is
         2454  +   that this one can share the keys across multiple rows (or even
         2455  +   other JSON containers), whereas the former makes fresh copies of
         2456  +   the column names for each row.
         2457  +
         2458  +*/
         2459  +cson_value * cson_sqlite3_row_to_object2( sqlite3_stmt * st,
         2460  +                                          cson_array * colNames );
         2461  +
         2462  +/**
         2463  +   Similar to cson_sqlite3_row_to_object(), but creates an Array
         2464  +   value which contains the JSON-form values of the given result
         2465  +   set row.
         2466  +*/
         2467  +cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
         2468  +/**
         2469  +    Converts the results of an sqlite3 SELECT statement to JSON,
         2470  +    in the form of a cson_value object tree.
         2471  +    
         2472  +    st must be a prepared, but not yet traversed, SELECT query.
         2473  +    tgt must be a pointer to NULL (see the example below). If
         2474  +    either of those arguments are NULL, cson_rc.ArgError is returned.
         2475  +    
         2476  +    This walks the query results and returns a JSON object which
         2477  +    has a different structure depending on the value of the 'fat'
         2478  +    argument.
         2479  +    
         2480  +    
         2481  +    If 'fat' is 0 then the structure is:
         2482  +    
         2483  +    @code
         2484  +    {
         2485  +        "columns":["colName1",..."colNameN"],
         2486  +        "rows":[
         2487  +            [colVal0, ... colValN],
         2488  +            [colVal0, ... colValN],
         2489  +            ...
         2490  +        ]
         2491  +    }
         2492  +    @endcode
         2493  +    
         2494  +    In the "non-fat" format the order of the columns and row values is
         2495  +    guaranteed to be the same as that of the underlying query.
         2496  +    
         2497  +    If 'fat' is not 0 then the structure is:
         2498  +    
         2499  +    @code
         2500  +    {
         2501  +        "columns":["colName1",..."colNameN"],
         2502  +        "rows":[
         2503  +            {"colName1":value1,..."colNameN":valueN},
         2504  +            {"colName1":value1,..."colNameN":valueN},
         2505  +            ...
         2506  +        ]
         2507  +    }
         2508  +    @endcode
         2509  +
         2510  +    In the "fat" format, the order of the "columns" entries is guaranteed
         2511  +    to be the same as the underlying query fields, but the order
         2512  +    of the keys in the "rows" might be different and might in fact
         2513  +    change when passed through different JSON implementations,
         2514  +    depending on how they implement object key/value pairs.
         2515  +
         2516  +    On success it returns 0 and assigns *tgt to a newly-allocated
         2517  +    JSON object tree (using the above structure), which the caller owns.
         2518  +    If the query returns no rows, the "rows" value will be an empty
         2519  +    array, as opposed to null.
         2520  +    
         2521  +    On error non-0 is returned and *tgt is not modified.
         2522  +    
         2523  +    The error code cson_rc.IOError is used to indicate a db-level
         2524  +    error, and cson_rc.TypeError is returned if sqlite3_column_count(st)
         2525  +    returns 0 or less (indicating an invalid or non-SELECT statement).
         2526  +    
         2527  +    The JSON data types are determined by the column type as reported
         2528  +    by sqlite3_column_type():
         2529  +    
         2530  +    SQLITE_INTEGER: integer
         2531  +    
         2532  +    SQLITE_FLOAT: double
         2533  +    
         2534  +    SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if
         2535  +    the data is UTF8 compatible.
         2536  +    
         2537  +    If the db returns a literal or SQL NULL for a value it is converted
         2538  +    to a JSON null. If it somehow finds a column type it cannot handle,
         2539  +    the value is also converted to a NULL in the output.
         2540  +
         2541  +    Example
         2542  +    
         2543  +    @code
         2544  +    cson_value * json = NULL;
         2545  +    int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 );
         2546  +    if( 0 != rc ) { ... error ... }
         2547  +    else {
         2548  +        cson_output_FILE( json, stdout, NULL );
         2549  +        cson_value_free( json );
         2550  +    }
         2551  +    @endcode
         2552  +*/
         2553  +int cson_sqlite3_stmt_to_json( sqlite3_stmt * st, cson_value ** tgt, char fat );
         2554  +
         2555  +/**
         2556  +    A convenience wrapper around cson_sqlite3_stmt_to_json(), which
         2557  +    takes SQL instead of a sqlite3_stmt object. It has the same
         2558  +    return value and argument semantics as that function.
         2559  +*/
         2560  +int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, char fat );
         2561  +
         2562  +/**
         2563  +   Binds a JSON value to a 1-based parameter index in a prepared SQL
         2564  +   statement. v must be NULL or one of one of the types (null, string,
         2565  +   integer, double, boolean, array). Booleans are bound as integer 0
         2566  +   or 1. NULL or null are bound as SQL NULL. Integers are bound as
         2567  +   64-bit ints. Strings are bound using sqlite3_bind_text() (as
         2568  +   opposed to text16), but we could/should arguably bind them as
         2569  +   blobs.
         2570  +
         2571  +   If v is an Array then ndx is is used as a starting position
         2572  +   (1-based) and each item in the array is bound to the next parameter
         2573  +   position (starting and ndx, though the array uses 0-based offsets).
         2574  +
         2575  +   TODO: add Object support for named parameters.
         2576  +
         2577  +   Returns 0 on success, non-0 on error.
         2578  + */
         2579  +int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v );
         2580  +    
         2581  +#if defined(__cplusplus)
         2582  +} /*extern "C"*/
         2583  +#endif
         2584  +    
         2585  +#endif /* CSON_ENABLE_SQLITE3 */
         2586  +#endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
         2587  +/* end file include/wh/cson/cson_sqlite3.h */
         2588  +#endif /* FOSSIL_ENABLE_JSON */

Changes to src/db.c.

    20     20   ** There are three separate database files that fossil interacts
    21     21   ** with:
    22     22   **
    23     23   **    (1)  The "user" database in ~/.fossil
    24     24   **
    25     25   **    (2)  The "repository" database
    26     26   **
    27         -**    (3)  A local checkout database named "_FOSSIL_" or ".fos"
           27  +**    (3)  A local checkout database named "_FOSSIL_" or ".fslckout"
    28     28   **         and located at the root of the local copy of the source tree.
    29     29   **
    30     30   */
    31     31   #include "config.h"
           32  +#if ! defined(_WIN32)
           33  +#  include <pwd.h>
           34  +#endif
    32     35   #include <sqlite3.h>
    33     36   #include <sys/types.h>
    34     37   #include <sys/stat.h>
    35     38   #include <unistd.h>
           39  +#include <time.h>
    36     40   #include "db.h"
    37     41   
    38     42   #if INTERFACE
    39     43   /*
    40     44   ** An single SQL statement is represented as an instance of the following
    41     45   ** structure.
    42     46   */
    43     47   struct Stmt {
    44     48     Blob sql;               /* The SQL for this statement */
    45     49     sqlite3_stmt *pStmt;    /* The results of sqlite3_prepare() */
    46     50     Stmt *pNext, *pPrev;    /* List of all unfinalized statements */
    47     51     int nStep;              /* Number of sqlite3_step() calls */
    48     52   };
           53  +
           54  +/*
           55  +** Copy this to initialize a Stmt object to a clean/empty state. This
           56  +** is useful to help avoid assertions when performing cleanup in some
           57  +** error handling cases.
           58  +*/
           59  +#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0}
    49     60   #endif /* INTERFACE */
           61  +const struct Stmt empty_Stmt = empty_Stmt_m;
    50     62   
    51     63   /*
    52     64   ** Call this routine when a database error occurs.
    53     65   */
    54     66   static void db_err(const char *zFormat, ...){
    55     67     va_list ap;
    56     68     char *z;
    57         -  static const char zRebuildMsg[] = 
           69  +  int rc = 1;
           70  +  static const char zRebuildMsg[] =
    58     71         "If you have recently updated your fossil executable, you might\n"
    59     72         "need to run \"fossil all rebuild\" to bring the repository\n"
    60     73         "schemas up to date.\n";
    61     74     va_start(ap, zFormat);
    62     75     z = vmprintf(zFormat, ap);
    63     76     va_end(ap);
           77  +#ifdef FOSSIL_ENABLE_JSON
           78  +  if( g.json.isJsonMode ){
           79  +    json_err( 0, z, 1 );
           80  +    if( g.isHTTP ){
           81  +      rc = 0 /* avoid HTTP 500 */;
           82  +    }
           83  +  }
           84  +  else
           85  +#endif /* FOSSIL_ENABLE_JSON */
    64     86     if( g.xferPanic ){
    65     87       cgi_reset_content();
    66     88       @ error Database\serror:\s%F(z)
    67         -    cgi_reply();
           89  +      cgi_reply();
    68     90     }
    69         -  if( g.cgiOutput ){
           91  +  else if( g.cgiOutput ){
    70     92       g.cgiOutput = 0;
    71     93       cgi_printf("<h1>Database Error</h1>\n"
    72         -               "<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
           94  +               "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg);
    73     95       cgi_reply();
    74     96     }else{
    75     97       fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg);
    76     98     }
           99  +  free(z);
    77    100     db_force_rollback();
    78         -  exit(1);
          101  +  fossil_exit(rc);
    79    102   }
    80    103   
    81         -static int nBegin = 0;      /* Nesting depth of BEGIN */
    82         -static int isNewRepo = 0;   /* True if the repository is newly created */
    83         -static int doRollback = 0;  /* True to force a rollback */
    84         -static int nCommitHook = 0; /* Number of commit hooks */
    85         -static struct sCommitHook {
    86         -  int (*xHook)(void);  /* Functions to call at db_end_transaction() */
    87         -  int sequence;        /* Call functions in sequence order */
    88         -} aHook[5];
    89         -static Stmt *pAllStmt = 0;  /* List of all unfinalized statements */
          104  +/*
          105  +** All static variable that a used by only this file are gathered into
          106  +** the following structure.
          107  +*/
          108  +static struct DbLocalData {
          109  +  int nBegin;               /* Nesting depth of BEGIN */
          110  +  int doRollback;           /* True to force a rollback */
          111  +  int nCommitHook;          /* Number of commit hooks */
          112  +  Stmt *pAllStmt;           /* List of all unfinalized statements */
          113  +  int nPrepare;             /* Number of calls to sqlite3_prepare() */
          114  +  int nDeleteOnFail;        /* Number of entries in azDeleteOnFail[] */
          115  +  struct sCommitHook {
          116  +    int (*xHook)(void);         /* Functions to call at db_end_transaction() */
          117  +    int sequence;               /* Call functions in sequence order */
          118  +  } aHook[5];
          119  +  char *azDeleteOnFail[3];  /* Files to delete on a failure */
          120  +  char *azBeforeCommit[5];  /* Commands to run prior to COMMIT */
          121  +  int nBeforeCommit;        /* Number of entries in azBeforeCommit */
          122  +  int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
          123  +} db = {0, 0, 0, 0, 0, 0, };
          124  +
          125  +/*
          126  +** Arrange for the given file to be deleted on a failure.
          127  +*/
          128  +void db_delete_on_failure(const char *zFilename){
          129  +  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
          130  +  db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
          131  +}
    90    132   
    91    133   /*
    92    134   ** This routine is called by the SQLite commit-hook mechanism
    93    135   ** just prior to each commit.  All this routine does is verify
    94    136   ** that nBegin really is zero.  That insures that transactions
    95    137   ** cannot commit by any means other than by calling db_end_transaction()
    96    138   ** below.
    97    139   **
    98    140   ** This is just a safety and sanity check.
    99    141   */
   100    142   static int db_verify_at_commit(void *notUsed){
   101         -  if( nBegin ){
          143  +  if( db.nBegin ){
   102    144       fossil_panic("illegal commit attempt");
   103    145       return 1;
   104    146     }
   105    147     return 0;
   106    148   }
   107    149   
   108    150   /*
   109    151   ** Begin and end a nested transaction
   110    152   */
   111    153   void db_begin_transaction(void){
   112         -  if( nBegin==0 ){
          154  +  if( db.nBegin==0 ){
   113    155       db_multi_exec("BEGIN");
   114    156       sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
          157  +    db.nPriorChanges = sqlite3_total_changes(g.db);
   115    158     }
   116         -  nBegin++;
          159  +  db.nBegin++;
   117    160   }
   118    161   void db_end_transaction(int rollbackFlag){
   119         -  if( nBegin<=0 ) return;
   120         -  if( rollbackFlag ) doRollback = 1;
   121         -  nBegin--;
   122         -  if( nBegin==0 ){
          162  +  if( g.db==0 ) return;
          163  +  if( db.nBegin<=0 ) return;
          164  +  if( rollbackFlag ) db.doRollback = 1;
          165  +  db.nBegin--;
          166  +  if( db.nBegin==0 ){
   123    167       int i;
   124         -    for(i=0; doRollback==0 && i<nCommitHook; i++){
   125         -      doRollback |= aHook[i].xHook();
          168  +    if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){
          169  +      while( db.nBeforeCommit ){
          170  +        db.nBeforeCommit--;
          171  +        sqlite3_exec(g.db, db.azBeforeCommit[db.nBeforeCommit], 0, 0, 0);
          172  +        sqlite3_free(db.azBeforeCommit[db.nBeforeCommit]);
          173  +      }
          174  +      leaf_do_pending_checks();
   126    175       }
   127         -    db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
   128         -    doRollback = 0;
          176  +    for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){
          177  +      db.doRollback |= db.aHook[i].xHook();
          178  +    }
          179  +    while( db.pAllStmt ){
          180  +      db_finalize(db.pAllStmt);
          181  +    }
          182  +    db_multi_exec(db.doRollback ? "ROLLBACK" : "COMMIT");
          183  +    db.doRollback = 0;
   129    184     }
   130    185   }
          186  +
          187  +/*
          188  +** Force a rollback and shutdown the database
          189  +*/
   131    190   void db_force_rollback(void){
          191  +  int i;
   132    192     static int busy = 0;
   133         -  if( busy ) return;
          193  +  sqlite3_stmt *pStmt = 0;
          194  +  if( busy || g.db==0 ) return;
   134    195     busy = 1;
   135    196     undo_rollback();
   136         -  if( nBegin ){
          197  +  while( (pStmt = sqlite3_next_stmt(g.db,pStmt))!=0 ){
          198  +    sqlite3_reset(pStmt);
          199  +  }
          200  +  while( db.pAllStmt ){
          201  +    db_finalize(db.pAllStmt);
          202  +  }
          203  +  if( db.nBegin ){
   137    204       sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
   138         -    if( isNewRepo ){
   139         -      db_close();
   140         -      unlink(g.zRepositoryName);
   141         -    }
          205  +    db.nBegin = 0;
   142    206     }
   143         -  nBegin = 0;
   144    207     busy = 0;
          208  +  db_close(0);
          209  +  for(i=0; i<db.nDeleteOnFail; i++){
          210  +    file_delete(db.azDeleteOnFail[i]);
          211  +  }
   145    212   }
   146    213   
   147    214   /*
   148    215   ** Install a commit hook.  Hooks are installed in sequence order.
   149    216   ** It is an error to install the same commit hook more than once.
   150    217   **
   151         -** Each commit hook is called (in order of accending sequence) at
          218  +** Each commit hook is called (in order of ascending sequence) at
   152    219   ** each commit operation.  If any commit hook returns non-zero,
   153    220   ** the subsequence commit hooks are omitted and the transaction
   154    221   ** rolls back rather than commit.  It is the responsibility of the
   155    222   ** hooks themselves to issue any error messages.
   156    223   */
   157    224   void db_commit_hook(int (*x)(void), int sequence){
   158    225     int i;
   159         -  assert( nCommitHook < sizeof(aHook)/sizeof(aHook[1]) );
   160         -  for(i=0; i<nCommitHook; i++){
   161         -    assert( x!=aHook[i].xHook );
   162         -    if( aHook[i].sequence>sequence ){
          226  +  assert( db.nCommitHook < sizeof(db.aHook)/sizeof(db.aHook[1]) );
          227  +  for(i=0; i<db.nCommitHook; i++){
          228  +    assert( x!=db.aHook[i].xHook );
          229  +    if( db.aHook[i].sequence>sequence ){
   163    230         int s = sequence;
   164    231         int (*xS)(void) = x;
   165         -      sequence = aHook[i].sequence;
   166         -      x = aHook[i].xHook;
   167         -      aHook[i].sequence = s;
   168         -      aHook[i].xHook = xS;
          232  +      sequence = db.aHook[i].sequence;
          233  +      x = db.aHook[i].xHook;
          234  +      db.aHook[i].sequence = s;
          235  +      db.aHook[i].xHook = xS;
   169    236       }
   170    237     }
   171         -  aHook[nCommitHook].sequence = sequence;
   172         -  aHook[nCommitHook].xHook = x;
   173         -  nCommitHook++;
          238  +  db.aHook[db.nCommitHook].sequence = sequence;
          239  +  db.aHook[db.nCommitHook].xHook = x;
          240  +  db.nCommitHook++;
   174    241   }
   175    242   
   176    243   /*
   177    244   ** Prepare a Stmt.  Assume that the Stmt is previously uninitialized.
   178    245   ** If the input string contains multiple SQL statements, only the first
   179    246   ** one is processed.  All statements beyond the first are silently ignored.
   180    247   */
   181         -int db_vprepare(Stmt *pStmt, const char *zFormat, va_list ap){
          248  +int db_vprepare(Stmt *pStmt, int errOk, const char *zFormat, va_list ap){
          249  +  int rc;
   182    250     char *zSql;
   183    251     blob_zero(&pStmt->sql);
   184    252     blob_vappendf(&pStmt->sql, zFormat, ap);
   185    253     va_end(ap);
   186    254     zSql = blob_str(&pStmt->sql);
   187         -  if( sqlite3_prepare_v2(g.db, zSql, -1, &pStmt->pStmt, 0)!=0 ){
          255  +  db.nPrepare++;
          256  +  rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt->pStmt, 0);
          257  +  if( rc!=0 && !errOk ){
   188    258       db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
   189    259     }
   190    260     pStmt->pNext = pStmt->pPrev = 0;
   191    261     pStmt->nStep = 0;
   192         -  return 0;
          262  +  return rc;
   193    263   }
   194    264   int db_prepare(Stmt *pStmt, const char *zFormat, ...){
   195    265     int rc;
   196    266     va_list ap;
   197    267     va_start(ap, zFormat);
   198         -  rc = db_vprepare(pStmt, zFormat, ap);
          268  +  rc = db_vprepare(pStmt, 0, zFormat, ap);
          269  +  va_end(ap);
          270  +  return rc;
          271  +}
          272  +int db_prepare_ignore_error(Stmt *pStmt, const char *zFormat, ...){
          273  +  int rc;
          274  +  va_list ap;
          275  +  va_start(ap, zFormat);
          276  +  rc = db_vprepare(pStmt, 1, zFormat, ap);
   199    277     va_end(ap);
   200    278     return rc;
   201    279   }
   202    280   int db_static_prepare(Stmt *pStmt, const char *zFormat, ...){
   203    281     int rc = SQLITE_OK;
   204    282     if( blob_size(&pStmt->sql)==0 ){
   205    283       va_list ap;
   206    284       va_start(ap, zFormat);
   207         -    rc = db_vprepare(pStmt, zFormat, ap);
   208         -    pStmt->pNext = pAllStmt;
          285  +    rc = db_vprepare(pStmt, 0, zFormat, ap);
          286  +    pStmt->pNext = db.pAllStmt;
   209    287       pStmt->pPrev = 0;
   210         -    if( pAllStmt ) pAllStmt->pPrev = pStmt;
   211         -    pAllStmt = pStmt;
          288  +    if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
          289  +    db.pAllStmt = pStmt;
   212    290       va_end(ap);
   213    291     }
   214    292     return rc;
   215    293   }
   216    294   
   217    295   /*
   218    296   ** Return the index of a bind parameter
................................................................................
   245    323   }
   246    324   int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){
   247    325     return sqlite3_bind_blob(pStmt->pStmt, paramIdx(pStmt, zParamName),
   248    326                             blob_buffer(pBlob), blob_size(pBlob), SQLITE_STATIC);
   249    327   }
   250    328   
   251    329   /* bind_str() treats a Blob object like a TEXT string and binds it
   252         -** to the SQL variable.  Constrast this to bind_blob() which treats
          330  +** to the SQL variable.  Contrast this to bind_blob() which treats
   253    331   ** the Blob object like an SQL BLOB.
   254    332   */
   255    333   int db_bind_str(Stmt *pStmt, const char *zParamName, Blob *pBlob){
   256    334     return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName),
   257    335                             blob_buffer(pBlob), blob_size(pBlob), SQLITE_STATIC);
   258    336   }
   259    337   
................................................................................
   308    386     db_check_result(rc);
   309    387     pStmt->pStmt = 0;
   310    388     if( pStmt->pNext ){
   311    389       pStmt->pNext->pPrev = pStmt->pPrev;
   312    390     }
   313    391     if( pStmt->pPrev ){
   314    392       pStmt->pPrev->pNext = pStmt->pNext;
   315         -  }else if( pAllStmt==pStmt ){
   316         -    pAllStmt = pStmt->pNext;
          393  +  }else if( db.pAllStmt==pStmt ){
          394  +    db.pAllStmt = pStmt->pNext;
   317    395     }
   318    396     pStmt->pNext = 0;
   319    397     pStmt->pPrev = 0;
   320    398     return rc;
   321    399   }
   322    400   
   323    401   /*
................................................................................
   351    429   }
   352    430   double db_column_double(Stmt *pStmt, int N){
   353    431     return sqlite3_column_double(pStmt->pStmt, N);
   354    432   }
   355    433   const char *db_column_text(Stmt *pStmt, int N){
   356    434     return (char*)sqlite3_column_text(pStmt->pStmt, N);
   357    435   }
          436  +const char *db_column_raw(Stmt *pStmt, int N){
          437  +  return (const char*)sqlite3_column_blob(pStmt->pStmt, N);
          438  +}
   358    439   const char *db_column_name(Stmt *pStmt, int N){
   359    440     return (char*)sqlite3_column_name(pStmt->pStmt, N);
   360    441   }
   361    442   int db_column_count(Stmt *pStmt){
   362    443     return sqlite3_column_count(pStmt->pStmt);
   363    444   }
   364    445   char *db_column_malloc(Stmt *pStmt, int N){
................................................................................
   366    447   }
   367    448   void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
   368    449     blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
   369    450                 sqlite3_column_bytes(pStmt->pStmt, N));
   370    451   }
   371    452   
   372    453   /*
   373         -** Initialize a blob to an ephermeral copy of the content of a
          454  +** Initialize a blob to an ephemeral copy of the content of a
   374    455   ** column in the current row.  The data in the blob will become
   375    456   ** invalid when the statement is stepped or reset.
   376    457   */
   377    458   void db_ephemeral_blob(Stmt *pStmt, int N, Blob *pBlob){
   378    459     blob_init(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
   379    460                 sqlite3_column_bytes(pStmt->pStmt, N));
   380    461   }
................................................................................
   401    482   }
   402    483   
   403    484   /*
   404    485   ** Execute multiple SQL statements.
   405    486   */
   406    487   int db_multi_exec(const char *zSql, ...){
   407    488     Blob sql;
   408         -  int rc;
          489  +  int rc = SQLITE_OK;
   409    490     va_list ap;
   410         -  char *zErr = 0;
          491  +  const char *z, *zEnd;
          492  +  sqlite3_stmt *pStmt;
   411    493     blob_init(&sql, 0, 0);
   412    494     va_start(ap, zSql);
   413    495     blob_vappendf(&sql, zSql, ap);
   414    496     va_end(ap);
   415         -  rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr);
   416         -  if( rc!=SQLITE_OK ){
   417         -    db_err("%s\n%s", zErr, blob_buffer(&sql));
          497  +  z = blob_str(&sql);
          498  +  while( rc==SQLITE_OK && z[0] ){
          499  +    pStmt = 0;
          500  +    rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
          501  +    if( rc!=SQLITE_OK ) break;
          502  +    if( pStmt ){
          503  +      db.nPrepare++;
          504  +      while( sqlite3_step(pStmt)==SQLITE_ROW ){}
          505  +      rc = sqlite3_finalize(pStmt);
          506  +      if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
          507  +    }
          508  +    z = zEnd;
   418    509     }
   419    510     blob_reset(&sql);
   420    511     return rc;
   421    512   }
          513  +
          514  +/*
          515  +** Optionally make the following changes to the database if feasible and
          516  +** convenient.  Do not start a transaction for these changes, but only
          517  +** make these changes if other changes are also being made.
          518  +*/
          519  +void db_optional_sql(const char *zDb, const char *zSql, ...){
          520  +  if( db_is_writeable(zDb) && db.nBeforeCommit < count(db.azBeforeCommit) ){
          521  +    va_list ap;
          522  +    va_start(ap, zSql);
          523  +    db.azBeforeCommit[db.nBeforeCommit++] = sqlite3_vmprintf(zSql, ap);
          524  +    va_end(ap);
          525  +  }
          526  +}
   422    527   
   423    528   /*
   424    529   ** Execute a query and return a single integer value.
   425    530   */
   426    531   i64 db_int64(i64 iDflt, const char *zSql, ...){
   427    532     va_list ap;
   428    533     Stmt s;
   429    534     i64 rc;
   430    535     va_start(ap, zSql);
   431         -  db_vprepare(&s, zSql, ap);
          536  +  db_vprepare(&s, 0, zSql, ap);
   432    537     va_end(ap);
   433    538     if( db_step(&s)!=SQLITE_ROW ){
   434    539       rc = iDflt;
   435    540     }else{
   436    541       rc = db_column_int64(&s, 0);
   437    542     }
   438    543     db_finalize(&s);
................................................................................
   439    544     return rc;
   440    545   }
   441    546   int db_int(int iDflt, const char *zSql, ...){
   442    547     va_list ap;
   443    548     Stmt s;
   444    549     int rc;
   445    550     va_start(ap, zSql);
   446         -  db_vprepare(&s, zSql, ap);
          551  +  db_vprepare(&s, 0, zSql, ap);
   447    552     va_end(ap);
   448    553     if( db_step(&s)!=SQLITE_ROW ){
   449    554       rc = iDflt;
   450    555     }else{
   451    556       rc = db_column_int(&s, 0);
   452    557     }
   453    558     db_finalize(&s);
................................................................................
   459    564   ** FALSE if the query result would be an empty set.
   460    565   */
   461    566   int db_exists(const char *zSql, ...){
   462    567     va_list ap;
   463    568     Stmt s;
   464    569     int rc;
   465    570     va_start(ap, zSql);
   466         -  db_vprepare(&s, zSql, ap);
          571  +  db_vprepare(&s, 0, zSql, ap);
   467    572     va_end(ap);
   468    573     if( db_step(&s)!=SQLITE_ROW ){
   469    574       rc = 0;
   470    575     }else{
   471    576       rc = 1;
   472    577     }
   473    578     db_finalize(&s);
................................................................................
   479    584   ** Execute a query and return a floating-point value.
   480    585   */
   481    586   double db_double(double rDflt, const char *zSql, ...){
   482    587     va_list ap;
   483    588     Stmt s;
   484    589     double r;
   485    590     va_start(ap, zSql);
   486         -  db_vprepare(&s, zSql, ap);
          591  +  db_vprepare(&s, 0, zSql, ap);
   487    592     va_end(ap);
   488    593     if( db_step(&s)!=SQLITE_ROW ){
   489    594       r = rDflt;
   490    595     }else{
   491    596       r = db_column_double(&s, 0);
   492    597     }
   493    598     db_finalize(&s);
................................................................................
   498    603   ** Execute a query and append the first column of the first row
   499    604   ** of the result set to blob given in the first argument.
   500    605   */
   501    606   void db_blob(Blob *pResult, const char *zSql, ...){
   502    607     va_list ap;
   503    608     Stmt s;
   504    609     va_start(ap, zSql);
   505         -  db_vprepare(&s, zSql, ap);
          610  +  db_vprepare(&s, 0, zSql, ap);
   506    611     va_end(ap);
   507    612     if( db_step(&s)==SQLITE_ROW ){
   508    613       blob_append(pResult, sqlite3_column_blob(s.pStmt, 0),
   509    614                            sqlite3_column_bytes(s.pStmt, 0));
   510    615     }
   511    616     db_finalize(&s);
   512    617   }
................................................................................
   513    618   
   514    619   /*
   515    620   ** Execute a query.  Return the first column of the first row
   516    621   ** of the result set as a string.  Space to hold the string is
   517    622   ** obtained from malloc().  If the result set is empty, return
   518    623   ** zDefault instead.
   519    624   */
   520         -char *db_text(char *zDefault, const char *zSql, ...){
          625  +char *db_text(char const *zDefault, const char *zSql, ...){
   521    626     va_list ap;
   522    627     Stmt s;
   523    628     char *z;
   524    629     va_start(ap, zSql);
   525         -  db_vprepare(&s, zSql, ap);
          630  +  db_vprepare(&s, 0, zSql, ap);
   526    631     va_end(ap);
   527    632     if( db_step(&s)==SQLITE_ROW ){
   528    633       z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
   529    634     }else if( zDefault ){
   530    635       z = mprintf("%s", zDefault);
   531    636     }else{
   532    637       z = 0;
   533    638     }
   534    639     db_finalize(&s);
   535    640     return z;
   536    641   }
   537    642   
   538         -#ifdef __MINGW32__
   539         -extern char *sqlite3_win32_mbcs_to_utf8(const char*);
   540         -#endif
   541         -
   542    643   /*
   543    644   ** Initialize a new database file with the given schema.  If anything
   544    645   ** goes wrong, call db_err() to exit.
   545    646   */
   546    647   void db_init_database(
   547    648     const char *zFileName,   /* Name of database file to create */
   548    649     const char *zSchema,     /* First part of schema */
................................................................................
   549    650     ...                      /* Additional SQL to run.  Terminate with NULL. */
   550    651   ){
   551    652     sqlite3 *db;
   552    653     int rc;
   553    654     const char *zSql;
   554    655     va_list ap;
   555    656   
   556         -#ifdef __MINGW32__
   557         -  zFileName = sqlite3_win32_mbcs_to_utf8(zFileName);
   558         -#endif
   559         -  rc = sqlite3_open(zFileName, &db);
   560         -  if( rc!=SQLITE_OK ){
   561         -    db_err(sqlite3_errmsg(db));
   562         -  }
   563         -  sqlite3_busy_timeout(db, 5000);
          657  +  db = db_open(zFileName);
   564    658     sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
   565    659     rc = sqlite3_exec(db, zSchema, 0, 0, 0);
   566    660     if( rc!=SQLITE_OK ){
   567    661       db_err(sqlite3_errmsg(db));
   568    662     }
   569    663     va_start(ap, zSchema);
   570    664     while( (zSql = va_arg(ap, const char*))!=0 ){
................................................................................
   573    667         db_err(sqlite3_errmsg(db));
   574    668       }
   575    669     }
   576    670     va_end(ap);
   577    671     sqlite3_exec(db, "COMMIT", 0, 0, 0);
   578    672     sqlite3_close(db);
   579    673   }
          674  +
          675  +/*
          676  +** Function to return the number of seconds since 1970.  This is
          677  +** the same as strftime('%s','now') but is more compact.
          678  +*/
          679  +void db_now_function(
          680  +  sqlite3_context *context,
          681  +  int argc,
          682  +  sqlite3_value **argv
          683  +){
          684  +  sqlite3_result_int64(context, time(0));
          685  +}
          686  +
          687  +/*
          688  +** Function to return the check-in time for a file.
          689  +*/
          690  +void db_checkin_mtime_function(
          691  +  sqlite3_context *context,
          692  +  int argc,
          693  +  sqlite3_value **argv
          694  +){
          695  +  i64 mtime;
          696  +  int rc = mtime_of_manifest_file(sqlite3_value_int(argv[0]),
          697  +                                  sqlite3_value_int(argv[1]), &mtime);
          698  +  if( rc==0 ){
          699  +    sqlite3_result_int64(context, mtime);
          700  +  }
          701  +}
          702  +
   580    703   
   581    704   /*
   582    705   ** Open a database file.  Return a pointer to the new database
   583    706   ** connection.  An error results in process abort.
   584    707   */
   585         -static sqlite3 *openDatabase(const char *zDbName){
          708  +LOCAL sqlite3 *db_open(const char *zDbName){
   586    709     int rc;
   587    710     const char *zVfs;
   588    711     sqlite3 *db;
   589    712   
   590         -  zVfs = getenv("FOSSIL_VFS");
   591         -#ifdef __MINGW32__
   592         -  zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
   593         -#endif
          713  +  if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
          714  +  zVfs = fossil_getenv("FOSSIL_VFS");
   594    715     rc = sqlite3_open_v2(
   595    716          zDbName, &db,
   596    717          SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
   597    718          zVfs
   598    719     );
   599    720     if( rc!=SQLITE_OK ){
   600         -    db_err(sqlite3_errmsg(db));
          721  +    db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
   601    722     }
   602         -  sqlite3_busy_timeout(db, 5000); 
          723  +  sqlite3_busy_timeout(db, 5000);
          724  +  sqlite3_wal_autocheckpoint(db, 1);  /* Set to checkpoint frequently */
          725  +  sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
          726  +  sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0,
          727  +                          db_checkin_mtime_function, 0, 0);
          728  +  sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
          729  +  sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
          730  +  sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
          731  +  sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
          732  +  sqlite3_create_function(
          733  +    db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
          734  +  );
          735  +  sqlite3_create_function(
          736  +    db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
          737  +  );
          738  +  if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
          739  +  re_add_sql_func(db);
          740  +  sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
   603    741     return db;
   604    742   }
   605    743   
          744  +
          745  +/*
          746  +** Detaches the zLabel database.
          747  +*/
          748  +void db_detach(const char *zLabel){
          749  +  db_multi_exec("DETACH DATABASE %s", zLabel);
          750  +}
          751  +
          752  +/*
          753  +** zDbName is the name of a database file.  Attach zDbName using
          754  +** the name zLabel.
          755  +*/
          756  +void db_attach(const char *zDbName, const char *zLabel){
          757  +  db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
          758  +}
   606    759   
   607    760   /*
   608    761   ** zDbName is the name of a database file.  If no other database
   609    762   ** file is open, then open this one.  If another database file is
   610    763   ** already open, then attach zDbName using the name zLabel.
   611    764   */
   612         -void db_open_or_attach(const char *zDbName, const char *zLabel){
          765  +void db_open_or_attach(
          766  +  const char *zDbName,
          767  +  const char *zLabel,
          768  +  int *pWasAttached
          769  +){
   613    770     if( !g.db ){
   614         -    g.db = openDatabase(zDbName);
   615         -    db_connection_init();
          771  +    assert( g.zMainDbType==0 );
          772  +    g.db = db_open(zDbName);
          773  +    g.zMainDbType = zLabel;
          774  +    if ( pWasAttached ) *pWasAttached = 0;
   616    775     }else{
   617         -#ifdef __MINGW32__
   618         -    zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
   619         -#endif
   620         -    db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
          776  +    assert( g.zMainDbType!=0 );
          777  +    db_attach(zDbName, zLabel);
          778  +    if ( pWasAttached ) *pWasAttached = 1;
   621    779     }
   622    780   }
   623    781   
   624    782   /*
   625    783   ** Open the user database in "~/.fossil".  Create the database anew if
   626    784   ** it does not already exist.
   627    785   **
................................................................................
   633    791   ** connection so that we can join between the various databases.  In that
   634    792   ** case, invoke this routine with useAttach as 1.
   635    793   */
   636    794   void db_open_config(int useAttach){
   637    795     char *zDbName;
   638    796     const char *zHome;
   639    797     if( g.configOpen ) return;
   640         -#ifdef __MINGW32__
   641         -  zHome = getenv("LOCALAPPDATA");
          798  +#if defined(_WIN32)
          799  +  zHome = fossil_getenv("LOCALAPPDATA");
   642    800     if( zHome==0 ){
   643         -    zHome = getenv("APPDATA");
          801  +    zHome = fossil_getenv("APPDATA");
   644    802       if( zHome==0 ){
   645         -      zHome = getenv("HOMEPATH");
          803  +      char *zDrive = fossil_getenv("HOMEDRIVE");
          804  +      zHome = fossil_getenv("HOMEPATH");
          805  +      if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome);
   646    806       }
   647    807     }
   648    808     if( zHome==0 ){
   649    809       fossil_fatal("cannot locate home directory - "
   650         -                "please set the HOMEPATH environment variable");
          810  +                "please set the LOCALAPPDATA or APPDATA or HOMEPATH "
          811  +                "environment variables");
   651    812     }
   652    813   #else
   653         -  zHome = getenv("HOME");
          814  +  zHome = fossil_getenv("HOME");
   654    815     if( zHome==0 ){
   655    816       fossil_fatal("cannot locate home directory - "
   656    817                    "please set the HOME environment variable");
   657    818     }
   658    819   #endif
   659    820     if( file_isdir(zHome)!=1 ){
   660    821       fossil_fatal("invalid home directory: %s", zHome);
   661    822     }
   662         -#ifndef __MINGW32__
          823  +#ifndef _WIN32
   663    824     if( access(zHome, W_OK) ){
   664    825       fossil_fatal("home directory %s must be writeable", zHome);
   665    826     }
   666    827   #endif
   667    828     g.zHome = mprintf("%/", zHome);
   668         -#ifdef __MINGW32__
          829  +#if defined(_WIN32)
   669    830     /* . filenames give some window systems problems and many apps problems */
   670    831     zDbName = mprintf("%//_fossil", zHome);
   671    832   #else
   672    833     zDbName = mprintf("%s/.fossil", zHome);
   673    834   #endif
   674    835     if( file_size(zDbName)<1024*3 ){
   675    836       db_init_database(zDbName, zConfigSchema, (char*)0);
   676    837     }
   677         -  g.useAttach = useAttach;
   678    838     if( useAttach ){
   679         -    db_open_or_attach(zDbName, "configdb");
          839  +    db_open_or_attach(zDbName, "configdb", &g.useAttach);
   680    840       g.dbConfig = 0;
          841  +    g.zConfigDbType = 0;
   681    842     }else{
   682         -    g.dbConfig = openDatabase(zDbName);
          843  +    g.useAttach = 0;
          844  +    g.dbConfig = db_open(zDbName);
          845  +    g.zConfigDbType = "configdb";
   683    846     }
   684    847     g.configOpen = 1;
          848  +  free(zDbName);
          849  +}
          850  +
          851  +
          852  +/*
          853  +** Returns TRUE if zTable exists in the local database but lacks column
          854  +** zColumn
          855  +*/
          856  +static int db_local_table_exists_but_lacks_column(
          857  +  const char *zTable,
          858  +  const char *zColumn
          859  +){
          860  +  char *zDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
          861  +                   " WHERE name=='%s' /*scan*/",
          862  +                   db_name("localdb"), zTable);
          863  +  int rc = 0;
          864  +  if( zDef ){
          865  +    char *zPattern = mprintf("* %s *", zColumn);
          866  +    rc = strglob(zPattern, zDef)==0;
          867  +    fossil_free(zPattern);
          868  +    fossil_free(zDef);
          869  +  }
          870  +  return rc;
   685    871   }
   686    872   
   687    873   /*
   688    874   ** If zDbName is a valid local database file, open it and return
   689    875   ** true.  If it is not a valid local database file, return 0.
   690    876   */
   691    877   static int isValidLocalDb(const char *zDbName){
   692    878     i64 lsize;
   693         -  int rc;
   694         -  sqlite3_stmt *pStmt;
          879  +  char *zVFileDef;
   695    880   
   696         -  if( access(zDbName, F_OK) ) return 0;
          881  +  if( file_access(zDbName, F_OK) ) return 0;
   697    882     lsize = file_size(zDbName);
   698    883     if( lsize%1024!=0 || lsize<4096 ) return 0;
   699         -  db_open_or_attach(zDbName, "localdb");
   700         -  g.localOpen = 1;
   701         -  db_open_config(0);
   702         -  db_open_repository(0);
          884  +  db_open_or_attach(zDbName, "localdb", 0);
          885  +  zVFileDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
          886  +                         " WHERE name=='vfile'", db_name("localdb"));
   703    887   
   704    888     /* If the "isexe" column is missing from the vfile table, then
   705    889     ** add it now.   This code added on 2010-03-06.  After all users have
   706         -  ** upgraded, this code can be safely deleted. 
          890  +  ** upgraded, this code can be safely deleted.
   707    891     */
   708         -  rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
   709         -  sqlite3_finalize(pStmt);
   710         -  if( rc==SQLITE_ERROR ){
   711         -    sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
          892  +  if( !strglob("* isexe *", zVFileDef) ){
          893  +    db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
   712    894     }
   713    895   
   714         -#if 0
   715         -  /* If the "mtime" column is missing from the vfile table, then
   716         -  ** add it now.   This code added on 2008-12-06.  After all users have
   717         -  ** upgraded, this code can be safely deleted. 
          896  +  /* If "islink"/"isLink" columns are missing from tables, then
          897  +  ** add them now.   This code added on 2011-01-17 and 2011-08-27.
          898  +  ** After all users have upgraded, this code can be safely deleted.
   718    899     */
   719         -  rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
   720         -  sqlite3_finalize(pStmt);
   721         -  if( rc==SQLITE_ERROR ){
   722         -    sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
          900  +  if( !strglob("* islink *", zVFileDef) ){
          901  +    db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
          902  +    if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
          903  +      db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
          904  +    }
          905  +    if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
          906  +      db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
          907  +    }
          908  +    if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
          909  +      db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
          910  +    }
   723    911     }
   724         -#endif
   725         -
   726         -#if 0
   727         -  /* If the "origname" column is missing from the vfile table, then
   728         -  ** add it now.   This code added on 2008-11-09.  After all users have
   729         -  ** upgraded, this code can be safely deleted. 
   730         -  */
   731         -  rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
   732         -  sqlite3_finalize(pStmt);
   733         -  if( rc==SQLITE_ERROR ){
   734         -    sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
   735         -  }
   736         -#endif
   737         -
   738    912     return 1;
   739    913   }
   740    914   
   741    915   /*
   742    916   ** Locate the root directory of the local repository tree.  The root
   743         -** directory is found by searching for a file named "_FOSSIL_" or ".fos"
          917  +** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
   744    918   ** that contains a valid repository database.
   745    919   **
   746         -** If no valid _FOSSIL_ or .fos file is found, we move up one level and 
          920  +** For legacy, also look for ".fos".  The use of ".fos" is deprecated
          921  +** since "fos" has negative connotations in Hungarian, we are told.
          922  +**
          923  +** If no valid _FOSSIL_ or .fos file is found, we move up one level and
   747    924   ** try again. Once the file is found, the g.zLocalRoot variable is set
   748    925   ** to the root of the repository tree and this routine returns 1.  If
   749    926   ** no database is found, then this routine return 0.
   750    927   **
   751    928   ** This routine always opens the user database regardless of whether or
   752    929   ** not the repository database is found.  If the _FOSSIL_ or .fos file
   753    930   ** is found, it is attached to the open database connection too.
   754    931   */
   755    932   int db_open_local(void){
   756    933     int i, n;
   757    934     char zPwd[2000];
   758         -  char *zPwdConv;
   759         -  static const char *aDbName[] = { "/_FOSSIL_", "/.fos" };
   760         -  
          935  +  static const char *const aDbName[] = { "/_FOSSIL_", "/.fslckout", "/.fos" };
          936  +
   761    937     if( g.localOpen) return 1;
   762         -  if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
   763         -    db_err("pwd too big: max %d", sizeof(zPwd)-20);
   764         -  }
          938  +  file_getcwd(zPwd, sizeof(zPwd)-20);
   765    939     n = strlen(zPwd);
   766         -  zPwdConv = mprintf("%/", zPwd);
   767         -  strncpy(zPwd, zPwdConv, 2000-20);
   768         -  free(zPwdConv);
          940  +  if( n==1 && zPwd[0]=='/' ) zPwd[0] = '.';
   769    941     while( n>0 ){
   770         -    if( access(zPwd, W_OK) ) break;
          942  +    if( file_access(zPwd, W_OK) ) break;
   771    943       for(i=0; i<sizeof(aDbName)/sizeof(aDbName[0]); i++){
   772         -      strcpy(&zPwd[n], aDbName[i]);
          944  +      sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "%s", aDbName[i]);
   773    945         if( isValidLocalDb(zPwd) ){
   774    946           /* Found a valid checkout database file */
   775    947           zPwd[n] = 0;
   776    948           while( n>1 && zPwd[n-1]=='/' ){
   777    949             n--;
   778    950             zPwd[n] = 0;
   779    951           }
   780    952           g.zLocalRoot = mprintf("%s/", zPwd);
          953  +        g.localOpen = 1;
          954  +        db_open_config(0);
          955  +        db_open_repository(0);
   781    956           return 1;
   782    957         }
   783    958       }
   784    959       n--;
   785    960       while( n>0 && zPwd[n]!='/' ){ n--; }
   786    961       while( n>0 && zPwd[n-1]=='/' ){ n--; }
   787    962       zPwd[n] = 0;
   788    963     }
   789    964   
   790    965     /* A checkout database file could not be found */
   791    966     return 0;
   792    967   }
          968  +
          969  +/*
          970  +** Get the full pathname to the repository database file.  The
          971  +** local database (the _FOSSIL_ or .fslckout database) must have already
          972  +** been opened before this routine is called.
          973  +*/
          974  +const char *db_repository_filename(void){
          975  +  static char *zRepo = 0;
          976  +  assert( g.localOpen );
          977  +  assert( g.zLocalRoot );
          978  +  if( zRepo==0 ){
          979  +    zRepo = db_lget("repository", 0);
          980  +    if( zRepo && !file_is_absolute_path(zRepo) ){
          981  +      zRepo = mprintf("%s%s", g.zLocalRoot, zRepo);
          982  +    }
          983  +  }
          984  +  return zRepo;
          985  +}
   793    986   
   794    987   /*
   795    988   ** Open the repository database given by zDbName.  If zDbName==NULL then
   796    989   ** get the name from the already open local database.
   797    990   */
   798    991   void db_open_repository(const char *zDbName){
   799    992     if( g.repositoryOpen ) return;
   800    993     if( zDbName==0 ){
   801    994       if( g.localOpen ){
   802         -      zDbName = db_lget("repository", 0);
          995  +      zDbName = db_repository_filename();
   803    996       }
   804    997       if( zDbName==0 ){
   805    998         db_err("unable to find the name of a repository database");
   806    999       }
   807   1000     }
   808         -  if( access(zDbName, R_OK) || file_size(zDbName)<1024 ){
   809         -    if( access(zDbName, 0) ){
         1001  +  if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){
         1002  +    if( file_access(zDbName, 0) ){
         1003  +#ifdef FOSSIL_ENABLE_JSON
         1004  +      g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
         1005  +#endif
   810   1006         fossil_panic("repository does not exist or"
   811   1007                      " is in an unreadable directory: %s", zDbName);
   812         -    }else if( access(zDbName, R_OK) ){
         1008  +    }else if( file_access(zDbName, R_OK) ){
         1009  +#ifdef FOSSIL_ENABLE_JSON
         1010  +      g.json.resultCode = FSL_JSON_E_DENIED;
         1011  +#endif
   813   1012         fossil_panic("read permission denied for repository %s", zDbName);
   814   1013       }else{
         1014  +#ifdef FOSSIL_ENABLE_JSON
         1015  +      g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
         1016  +#endif
   815   1017         fossil_panic("not a valid repository: %s", zDbName);
   816   1018       }
   817   1019     }
   818         -  db_open_or_attach(zDbName, "repository");
         1020  +  db_open_or_attach(zDbName, "repository", 0);
   819   1021     g.repositoryOpen = 1;
   820   1022     g.zRepositoryName = mprintf("%s", zDbName);
         1023  +  /* Cache "allow-symlinks" option, because we'll need it on every stat call */
         1024  +  g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
   821   1025   }
   822   1026   
         1027  +/*
         1028  +** Flags for the db_find_and_open_repository() function.
         1029  +*/
         1030  +#if INTERFACE
         1031  +#define OPEN_OK_NOT_FOUND    0x001      /* Do not error out if not found */
         1032  +#define OPEN_ANY_SCHEMA      0x002      /* Do not error if schema is wrong */
         1033  +#endif
         1034  +
   823   1035   /*
   824   1036   ** Try to find the repository and open it.  Use the -R or --repository
   825   1037   ** option to locate the repository.  If no such option is available, then
   826   1038   ** use the repository of the open checkout if there is one.
   827   1039   **
   828   1040   ** Error out if the repository cannot be opened.
   829   1041   */
   830         -void db_find_and_open_repository(int errIfNotFound){
         1042  +void db_find_and_open_repository(int bFlags, int nArgUsed){
   831   1043     const char *zRep = find_option("repository", "R", 1);
         1044  +  if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){
         1045  +    zRep = g.argv[nArgUsed];
         1046  +  }
   832   1047     if( zRep==0 ){
   833   1048       if( db_open_local()==0 ){
   834   1049         goto rep_not_found;
   835   1050       }
   836         -    zRep = db_lget("repository", 0);
         1051  +    zRep = db_repository_filename();
   837   1052       if( zRep==0 ){
   838   1053         goto rep_not_found;
   839   1054       }
   840   1055     }
   841   1056     db_open_repository(zRep);
   842   1057     if( g.repositoryOpen ){
         1058  +    if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
   843   1059       return;
   844   1060     }
   845   1061   rep_not_found:
   846         -  if( errIfNotFound ){
   847         -    fossil_fatal("use --repository or -R to specify the repository database");
         1062  +  if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
         1063  +#ifdef FOSSIL_ENABLE_JSON
         1064  +    g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
         1065  +#endif
         1066  +    if( nArgUsed==0 ){
         1067  +      fossil_fatal("use --repository or -R to specify the repository database");
         1068  +    }else{
         1069  +      fossil_fatal("specify the repository name as a command-line argument");
         1070  +    }
   848   1071     }
   849   1072   }
         1073  +
         1074  +/*
         1075  +** Return the name of the database "localdb", "configdb", or "repository".
         1076  +*/
         1077  +const char *db_name(const char *zDb){
         1078  +  assert( fossil_strcmp(zDb,"localdb")==0
         1079  +       || fossil_strcmp(zDb,"configdb")==0
         1080  +       || fossil_strcmp(zDb,"repository")==0 );
         1081  +  if( fossil_strcmp(zDb, g.zMainDbType)==0 ) zDb = "main";
         1082  +  return zDb;
         1083  +}
         1084  +
         1085  +/*
         1086  +** Return TRUE if the schema is out-of-date
         1087  +*/
         1088  +int db_schema_is_outofdate(void){
         1089  +  return db_exists("SELECT 1 FROM config"
         1090  +                   " WHERE name='aux-schema'"
         1091  +                   "   AND value<>'%s'", AUX_SCHEMA);
         1092  +}
         1093  +
         1094  +/*
         1095  +** Return true if the database is writeable
         1096  +*/
         1097  +int db_is_writeable(const char *zName){
         1098  +  return g.db!=0 && !sqlite3_db_readonly(g.db, db_name(zName));
         1099  +}
         1100  +
         1101  +/*
         1102  +** Verify that the repository schema is correct.  If it is not correct,
         1103  +** issue a fatal error and die.
         1104  +*/
         1105  +void db_verify_schema(void){
         1106  +  if( db_schema_is_outofdate() ){
         1107  +#ifdef FOSSIL_ENABLE_JSON
         1108  +    g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD;
         1109  +#endif
         1110  +    fossil_warning("incorrect repository schema version");
         1111  +    fossil_warning("your repository has schema version \"%s\" "
         1112  +          "but this binary expects version \"%s\"",
         1113  +          db_get("aux-schema",0), AUX_SCHEMA);
         1114  +    fossil_fatal("run \"fossil rebuild\" to fix this problem");
         1115  +  }
         1116  +}
         1117  +
         1118  +
         1119  +/*
         1120  +** COMMAND: test-move-repository
         1121  +**
         1122  +** Usage: %fossil test-move-repository PATHNAME
         1123  +**
         1124  +** Change the location of the repository database on a local check-out.
         1125  +** Use this command to avoid having to close and reopen a checkout
         1126  +** when relocating the repository database.
         1127  +*/
         1128  +void move_repo_cmd(void){
         1129  +  Blob repo;
         1130  +  char *zRepo;
         1131  +  if( g.argc!=3 ){
         1132  +    usage("PATHNAME");
         1133  +  }
         1134  +  if( db_open_local()==0 ){
         1135  +    fossil_fatal("not in a local checkout");
         1136  +    return;
         1137  +  }
         1138  +  file_canonical_name(g.argv[2], &repo, 0);
         1139  +  zRepo = blob_str(&repo);
         1140  +  if( file_access(zRepo, 0) ){
         1141  +    fossil_fatal("no such file: %s", zRepo);
         1142  +  }
         1143  +  db_open_or_attach(zRepo, "test_repo", 0);
         1144  +  db_lset("repository", blob_str(&repo));
         1145  +  db_close(1);
         1146  +}
         1147  +
   850   1148   
   851   1149   /*
   852   1150   ** Open the local database.  If unable, exit with an error.
   853   1151   */
   854   1152   void db_must_be_within_tree(void){
   855   1153     if( db_open_local()==0 ){
   856         -    fossil_fatal("not within an open checkout");
         1154  +    fossil_fatal("current directory is not within an open checkout");
   857   1155     }
   858   1156     db_open_repository(0);
         1157  +  db_verify_schema();
   859   1158   }
   860   1159   
   861   1160   /*
   862   1161   ** Close the database connection.
         1162  +**
         1163  +** Check for unfinalized statements and report errors if the reportErrors
         1164  +** argument is true.  Ignore unfinalized statements when false.
   863   1165   */
   864         -void db_close(void){
         1166  +void db_close(int reportErrors){
         1167  +  sqlite3_stmt *pStmt;
   865   1168     if( g.db==0 ) return;
   866         -  while( pAllStmt ){
   867         -    db_finalize(pAllStmt);
         1169  +  if( g.fSqlStats ){
         1170  +    int cur, hiwtr;
         1171  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
         1172  +    fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
         1173  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
         1174  +    fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
         1175  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);
         1176  +    fprintf(stderr, "-- LOOKASIDE_MISS_SIZE               %10d\n", hiwtr);
         1177  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &cur,&hiwtr,0);
         1178  +    fprintf(stderr, "-- LOOKASIDE_MISS_FULL               %10d\n", hiwtr);
         1179  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiwtr, 0);
         1180  +    fprintf(stderr, "-- CACHE_USED             %10d\n", cur);
         1181  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &cur, &hiwtr, 0);
         1182  +    fprintf(stderr, "-- SCHEMA_USED            %10d\n", cur);
         1183  +    sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &cur, &hiwtr, 0);
         1184  +    fprintf(stderr, "-- STMT_USED              %10d\n", cur);
         1185  +    sqlite3_status(SQLITE_STATUS_MEMORY_USED, &cur, &hiwtr, 0);
         1186  +    fprintf(stderr, "-- MEMORY_USED            %10d %10d\n", cur, hiwtr);
         1187  +    sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &cur, &hiwtr, 0);
         1188  +    fprintf(stderr, "-- MALLOC_SIZE                       %10d\n", hiwtr);
         1189  +    sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &cur, &hiwtr, 0);
         1190  +    fprintf(stderr, "-- MALLOC_COUNT           %10d %10d\n", cur, hiwtr);
         1191  +    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0);
         1192  +    fprintf(stderr, "-- PCACHE_OVFLOW          %10d %10d\n", cur, hiwtr);
         1193  +    fprintf(stderr, "-- prepared statements    %10d\n", db.nPrepare);
         1194  +  }
         1195  +  while( db.pAllStmt ){
         1196  +    db_finalize(db.pAllStmt);
         1197  +  }
         1198  +  db_end_transaction(1);
         1199  +  pStmt = 0;
         1200  +  if( reportErrors ){
         1201  +    while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
         1202  +      fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
         1203  +    }
   868   1204     }
   869   1205     g.repositoryOpen = 0;
   870   1206     g.localOpen = 0;
   871   1207     g.configOpen = 0;
         1208  +  sqlite3_wal_checkpoint(g.db, 0);
   872   1209     sqlite3_close(g.db);
   873   1210     g.db = 0;
         1211  +  g.zMainDbType = 0;
         1212  +  if( g.dbConfig ){
         1213  +    sqlite3_close(g.dbConfig);
         1214  +    g.dbConfig = 0;
         1215  +    g.zConfigDbType = 0;
         1216  +  }
   874   1217   }
   875   1218   
   876   1219   
   877   1220   /*
   878   1221   ** Create a new empty repository database with the given name.
   879   1222   **
   880   1223   ** Only the schema is initialized.  The required VAR tables entries
................................................................................
   884   1227   void db_create_repository(const char *zFilename){
   885   1228     db_init_database(
   886   1229        zFilename,
   887   1230        zRepositorySchema1,
   888   1231        zRepositorySchema2,
   889   1232        (char*)0
   890   1233     );
   891         -  isNewRepo = 1;
         1234  +  db_delete_on_failure(zFilename);
   892   1235   }
   893   1236   
   894   1237   /*
   895   1238   ** Create the default user accounts in the USER table.
   896   1239   */
   897   1240   void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
   898         -  const char *zUser;
   899         -  zUser = db_get("default-user", 0);
         1241  +  const char *zUser = zDefaultUser;
         1242  +  if( zUser==0 ){
         1243  +    zUser = db_get("default-user", 0);
         1244  +  }
   900   1245     if( zUser==0 ){
   901         -    zUser = zDefaultUser;
         1246  +    zUser = fossil_getenv("FOSSIL_USER");
   902   1247     }
   903   1248     if( zUser==0 ){
   904         -#ifdef __MINGW32__
   905         -    zUser = getenv("USERNAME");
         1249  +#if defined(_WIN32)
         1250  +    zUser = fossil_getenv("USERNAME");
   906   1251   #else
   907         -    zUser = getenv("USER");
         1252  +    zUser = fossil_getenv("USER");
   908   1253   #endif
   909   1254     }
   910   1255     if( zUser==0 ){
   911   1256       zUser = "root";
   912   1257     }
   913   1258     db_multi_exec(
   914         -     "INSERT INTO user(login, pw, cap, info)"
   915         -     "VALUES(%Q,lower(hex(randomblob(3))),'s','')", zUser
         1259  +     "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
         1260  +  );
         1261  +  db_multi_exec(
         1262  +     "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))"
         1263  +     " WHERE login=%Q", zUser
   916   1264     );
   917   1265     if( !setupUserOnly ){
   918   1266       db_multi_exec(
   919   1267          "INSERT INTO user(login,pw,cap,info)"
   920         -       "   VALUES('anonymous',hex(randomblob(8)),'ghmncz','Anon');"
         1268  +       "   VALUES('anonymous',hex(randomblob(8)),'hmncz','Anon');"
   921   1269          "INSERT INTO user(login,pw,cap,info)"
   922         -       "   VALUES('nobody','','jor','Nobody');"
         1270  +       "   VALUES('nobody','','gjor','Nobody');"
   923   1271          "INSERT INTO user(login,pw,cap,info)"
   924   1272          "   VALUES('developer','','dei','Dev');"
   925   1273          "INSERT INTO user(login,pw,cap,info)"
   926   1274          "   VALUES('reader','','kptw','Reader');"
   927   1275       );
   928   1276     }
   929   1277   }
         1278  +
         1279  +/*
         1280  +** Return a pointer to a string that contains the RHS of an IN operator
         1281  +** that will select CONFIG table names that are in the list of control
         1282  +** settings.
         1283  +*/
         1284  +const char *db_setting_inop_rhs(){
         1285  +  Blob x;
         1286  +  int i;
         1287  +  const char *zSep = "";
         1288  +
         1289  +  blob_zero(&x);
         1290  +  blob_append(&x, "(", 1);
         1291  +  for(i=0; ctrlSettings[i].name; i++){
         1292  +    blob_appendf(&x, "%s'%s'", zSep, ctrlSettings[i].name);
         1293  +    zSep = ",";
         1294  +  }
         1295  +  blob_append(&x, ")", 1);
         1296  +  return blob_str(&x);
         1297  +}
   930   1298   
   931   1299   /*
   932   1300   ** Fill an empty repository database with the basic information for a
   933   1301   ** repository. This function is shared between 'create_repository_cmd'
   934   1302   ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
   935   1303   ** new repositories.
         1304  +**
         1305  +** The zTemplate parameter determines if the settings for the repository
         1306  +** should be copied from another repository.  If zTemplate is 0 then the
         1307  +** settings will have their normal default values.  If zTemplate is
         1308  +** non-zero, it is assumed that the caller of this function has already
         1309  +** attached a database using the label "settingSrc".  If not, the call to
         1310  +** this function will fail.
   936   1311   **
   937   1312   ** The zInitialDate parameter determines the date of the initial check-in
   938   1313   ** that is automatically created.  If zInitialDate is 0 then no initial
   939   1314   ** check-in is created. The makeServerCodes flag determines whether or
   940   1315   ** not server and project codes are invented for this repository.
   941   1316   */
   942   1317   void db_initial_setup(
         1318  +  const char *zTemplate,       /* Repository from which to copy settings. */
   943   1319     const char *zInitialDate,    /* Initial date of repository. (ex: "now") */
   944   1320     const char *zDefaultUser,    /* Default user for the repository */
   945   1321     int makeServerCodes          /* True to make new server & project codes */
   946   1322   ){
   947   1323     char *zDate;
   948   1324     Blob hash;
   949   1325     Blob manifest;
   950   1326   
   951   1327     db_set("content-schema", CONTENT_SCHEMA, 0);
   952   1328     db_set("aux-schema", AUX_SCHEMA, 0);
   953   1329     if( makeServerCodes ){
   954   1330       db_multi_exec(
   955         -      "INSERT INTO config(name,value)"
   956         -      " VALUES('server-code', lower(hex(randomblob(20))));"
   957         -      "INSERT INTO config(name,value)"
   958         -      " VALUES('project-code', lower(hex(randomblob(20))));"
         1331  +      "INSERT INTO config(name,value,mtime)"
         1332  +      " VALUES('server-code', lower(hex(randomblob(20))),now());"
         1333  +      "INSERT INTO config(name,value,mtime)"
         1334  +      " VALUES('project-code', lower(hex(randomblob(20))),now());"
   959   1335       );
   960   1336     }
   961   1337     if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
   962   1338     if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
         1339  +  if( !db_is_global("timeline-plaintext") ){
         1340  +    db_set_int("timeline-plaintext", 1, 0);
         1341  +  }
   963   1342     db_create_default_users(0, zDefaultUser);
         1343  +  if( zDefaultUser ) g.zLogin = zDefaultUser;
   964   1344     user_select();
         1345  +
         1346  +  if( zTemplate ){
         1347  +    /*
         1348  +    ** Copy all settings from the supplied template repository.
         1349  +    */
         1350  +    db_multi_exec(
         1351  +      "INSERT OR REPLACE INTO config"
         1352  +      " SELECT name,value,mtime FROM settingSrc.config"
         1353  +      "  WHERE (name IN %s OR name IN %s)"
         1354  +      "    AND name NOT GLOB 'project-*';",
         1355  +      configure_inop_rhs(CONFIGSET_ALL),
         1356  +      db_setting_inop_rhs()
         1357  +    );
         1358  +    db_multi_exec(
         1359  +      "REPLACE INTO reportfmt SELECT * FROM settingSrc.reportfmt;"
         1360  +    );
         1361  +
         1362  +    /*
         1363  +    ** Copy the user permissions, contact information, last modified
         1364  +    ** time, and photo for all the "system" users from the supplied
         1365  +    ** template repository into the one being setup.  The other columns
         1366  +    ** are not copied because they contain security information or other
         1367  +    ** data specific to the other repository.  The list of columns copied
         1368  +    ** by this SQL statement may need to be revised in the future.
         1369  +    */
         1370  +    db_multi_exec("UPDATE user SET"
         1371  +      "  cap = (SELECT u2.cap FROM settingSrc.user u2"
         1372  +      "         WHERE u2.login = user.login),"
         1373  +      "  info = (SELECT u2.info FROM settingSrc.user u2"
         1374  +      "          WHERE u2.login = user.login),"
         1375  +      "  mtime = (SELECT u2.mtime FROM settingSrc.user u2"
         1376  +      "           WHERE u2.login = user.login),"
         1377  +      "  photo = (SELECT u2.photo FROM settingSrc.user u2"
         1378  +      "           WHERE u2.login = user.login)"
         1379  +      " WHERE user.login IN ('anonymous','nobody','developer','reader');"
         1380  +    );
         1381  +  }
   965   1382   
   966   1383     if( zInitialDate ){
   967   1384       int rid;
   968   1385       blob_zero(&manifest);
   969   1386       blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
   970         -    zDate = db_text(0, "SELECT datetime(%Q)", zInitialDate);
   971         -    zDate[10]='T';
         1387  +    zDate = date_in_standard_format(zInitialDate);
   972   1388       blob_appendf(&manifest, "D %s\n", zDate);
   973   1389       blob_appendf(&manifest, "P\n");
   974   1390       md5sum_init();
   975   1391       blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
   976   1392       blob_appendf(&manifest, "T *branch * trunk\n");
   977   1393       blob_appendf(&manifest, "T *sym-trunk *\n");
   978   1394       blob_appendf(&manifest, "U %F\n", g.zLogin);
   979   1395       md5sum_blob(&manifest, &hash);
   980   1396       blob_appendf(&manifest, "Z %b\n", &hash);
   981   1397       blob_reset(&hash);
   982         -    rid = content_put(&manifest, 0, 0);
         1398  +    rid = content_put(&manifest);
   983   1399       manifest_crosslink(rid, &manifest);
   984   1400     }
   985   1401   }
   986   1402   
   987   1403   /*
   988         -** COMMAND: new
         1404  +** COMMAND: new*
         1405  +** COMMAND: init
   989   1406   **
   990   1407   ** Usage: %fossil new ?OPTIONS? FILENAME
         1408  +**    Or: %fossil init ?OPTIONS? FILENAME
   991   1409   **
   992   1410   ** Create a repository for a new project in the file named FILENAME.
   993   1411   ** This command is distinct from "clone".  The "clone" command makes
   994   1412   ** a copy of an existing project.  This command starts a new project.
   995   1413   **
   996   1414   ** By default, your current login name is used to create the default
   997   1415   ** admin user. This can be overridden using the -A|--admin-user
   998   1416   ** parameter.
   999   1417   **
         1418  +** By default, all settings will be initialized to their default values.
         1419  +** This can be overridden using the --template parameter to specify a
         1420  +** repository file from which to copy the initial settings.  When a template
         1421  +** repository is used, almost all of the settings accessible from the setup
         1422  +** page, either directly or indirectly, will be copied.  Normal users and
         1423  +** their associated permissions will not be copied; however, the system
         1424  +** default users "anonymous", "nobody", "reader", "developer", and their
         1425  +** associated permissions will be copied.
         1426  +**
  1000   1427   ** Options:
         1428  +**    --template      FILE      copy settings from repository file
         1429  +**    --admin-user|-A USERNAME  select given USERNAME as admin user
         1430  +**    --date-override DATETIME  use DATETIME as time of the initial checkin
  1001   1431   **
  1002         -**    --admin-user|-A USERNAME
  1003         -**    --date-override DATETIME
  1004         -**
         1432  +** See also: clone
  1005   1433   */
  1006   1434   void create_repository_cmd(void){
  1007   1435     char *zPassword;
         1436  +  const char *zTemplate;      /* Repository from which to copy settings */
  1008   1437     const char *zDate;          /* Date of the initial check-in */
  1009   1438     const char *zDefaultUser;   /* Optional name of the default user */
  1010   1439   
         1440  +  zTemplate = find_option("template",0,1);
  1011   1441     zDate = find_option("date-override",0,1);
  1012   1442     zDefaultUser = find_option("admin-user","A",1);
  1013   1443     if( zDate==0 ) zDate = "now";
  1014   1444     if( g.argc!=3 ){
  1015   1445       usage("REPOSITORY-NAME");
  1016   1446     }
  1017   1447     db_create_repository(g.argv[2]);
  1018   1448     db_open_repository(g.argv[2]);
  1019   1449     db_open_config(0);
         1450  +  if( zTemplate ) db_attach(zTemplate, "settingSrc");
  1020   1451     db_begin_transaction();
  1021         -  db_initial_setup(zDate, zDefaultUser, 1);
         1452  +  db_initial_setup(zTemplate, zDate, zDefaultUser, 1);
  1022   1453     db_end_transaction(0);
  1023         -  printf("project-id: %s\n", db_get("project-code", 0));
  1024         -  printf("server-id:  %s\n", db_get("server-code", 0));
         1454  +  if( zTemplate ) db_detach("settingSrc");
         1455  +  fossil_print("project-id: %s\n", db_get("project-code", 0));
         1456  +  fossil_print("server-id:  %s\n", db_get("server-code", 0));
  1025   1457     zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  1026         -  printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
         1458  +  fossil_print("admin-user: %s (initial password is \"%s\")\n",
         1459  +               g.zLogin, zPassword);
  1027   1460   }
  1028   1461   
  1029   1462   /*
  1030   1463   ** SQL functions for debugging.
  1031   1464   **
  1032   1465   ** The print() function writes its arguments on stdout, but only
  1033   1466   ** if the -sqlprint command-line option is turned on.
  1034   1467   */
  1035         -static void db_sql_print(
         1468  +LOCAL void db_sql_print(
  1036   1469     sqlite3_context *context,
  1037   1470     int argc,
  1038   1471     sqlite3_value **argv
  1039   1472   ){
  1040   1473     int i;
  1041   1474     if( g.fSqlPrint ){
  1042   1475       for(i=0; i<argc; i++){
  1043   1476         char c = i==argc-1 ? '\n' : ' ';
  1044         -      printf("%s%c", sqlite3_value_text(argv[i]), c);
         1477  +      fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
  1045   1478       }
  1046   1479     }
  1047   1480   }
  1048         -static void db_sql_trace(void *notUsed, const char *zSql){
         1481  +LOCAL void db_sql_trace(void *notUsed, const char *zSql){
  1049   1482     int n = strlen(zSql);
  1050         -  fprintf(stderr, "%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
         1483  +  fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
  1051   1484   }
  1052   1485   
  1053   1486   /*
  1054   1487   ** Implement the user() SQL function.  user() takes no arguments and
  1055   1488   ** returns the user ID of the current user.
  1056   1489   */
  1057         -static void db_sql_user(
         1490  +LOCAL void db_sql_user(
  1058   1491     sqlite3_context *context,
  1059   1492     int argc,
  1060   1493     sqlite3_value **argv
  1061   1494   ){
  1062   1495     if( g.zLogin!=0 ){
  1063   1496       sqlite3_result_text(context, g.zLogin, -1, SQLITE_STATIC);
  1064   1497     }
  1065   1498   }
  1066   1499   
  1067   1500   /*
  1068         -** Implement the cgi() SQL function.  cgi() takes a an argument which is
  1069         -** a name of CGI query parameter. The value of that parameter is returned, 
  1070         -** if available. optional second argument will be returned if the first
         1501  +** Implement the cgi() SQL function.  cgi() takes an argument which is
         1502  +** a name of CGI query parameter. The value of that parameter is returned,
         1503  +** if available. Optional second argument will be returned if the first
  1071   1504   ** doesn't exist as a CGI parameter.
  1072   1505   */
  1073         -static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
         1506  +LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
  1074   1507     const char* zP;
  1075   1508     if( argc!=1 && argc!=2 ) return;
  1076   1509     zP = P((const char*)sqlite3_value_text(argv[0]));
  1077   1510     if( zP ){
  1078   1511       sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
  1079   1512     }else if( argc==2 ){
  1080   1513       zP = (const char*)sqlite3_value_text(argv[1]);
  1081   1514       if( zP ) sqlite3_result_text(context, zP, -1, SQLITE_TRANSIENT);
  1082   1515     }
  1083   1516   }
  1084   1517   
  1085   1518   /*
  1086         -** This is used by the [commit] command.
         1519  +** SQL function:
  1087   1520   **
  1088         -** Return true if either:
         1521  +**       is_selected(id)
         1522  +**       if_selected(id, X, Y)
  1089   1523   **
  1090         -**     a) Global.aCommitFile is NULL, or
  1091         -**     b) Global.aCommitFile contains the integer passed as an argument.
         1524  +** On the commit command, when filenames are specified (in order to do
         1525  +** a partial commit) the vfile.id values for the named files are loaded
         1526  +** into the g.aCommitFile[] array.  This function looks at that array
         1527  +** to see if a file is named on the command-line.
  1092   1528   **
  1093         -** Otherwise return false.
         1529  +** In the first form (1 argument) return TRUE if either no files are
         1530  +** named on the command line (g.aCommitFile is NULL meaning that all
         1531  +** changes are to be committed) or if id is found in g.aCommitFile[]
         1532  +** (meaning that id was named on the command-line).
         1533  +**
         1534  +** In the second form (3 arguments) return argument X if true and Y
         1535  +** if false.  Except if Y is NULL then always return X.
  1094   1536   */
  1095         -static void file_is_selected(
         1537  +LOCAL void file_is_selected(
  1096   1538     sqlite3_context *context,
  1097   1539     int argc,
  1098   1540     sqlite3_value **argv
  1099   1541   ){
  1100         -  assert(argc==1);
         1542  +  int rc = 0;
         1543  +
         1544  +  assert(argc==1 || argc==3);
  1101   1545     if( g.aCommitFile ){
  1102   1546       int iId = sqlite3_value_int(argv[0]);
  1103   1547       int ii;
  1104   1548       for(ii=0; g.aCommitFile[ii]; ii++){
  1105   1549         if( iId==g.aCommitFile[ii] ){
  1106         -        sqlite3_result_int(context, 1);
  1107         -        return;
         1550  +        rc = 1;
         1551  +        break;
  1108   1552         }
  1109   1553       }
  1110         -    sqlite3_result_int(context, 0);
         1554  +  }else{
         1555  +    rc = 1;
         1556  +  }
         1557  +  if( argc==1 ){
         1558  +    sqlite3_result_int(context, rc);
  1111   1559     }else{
  1112         -    sqlite3_result_int(context, 1);
         1560  +    assert( argc==3 );
         1561  +    assert( rc==0 || rc==1 );
         1562  +    if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc;
         1563  +    sqlite3_result_value(context, argv[2-rc]);
  1113   1564     }
  1114   1565   }
  1115   1566   
  1116   1567   /*
  1117   1568   ** Convert the input string into an SHA1.  Make a notation in the
  1118   1569   ** CONCEALED table so that the hash can be undo using the db_reveal()
  1119   1570   ** function at some later time.
................................................................................
  1134   1585     Blob out;
  1135   1586     if( n==40 && validate16(zContent, n) ){
  1136   1587       memcpy(zHash, zContent, n);
  1137   1588       zHash[n] = 0;
  1138   1589     }else{
  1139   1590       sha1sum_step_text(zContent, n);
  1140   1591       sha1sum_finish(&out);
  1141         -    strcpy(zHash, blob_str(&out));
         1592  +    sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out));
  1142   1593       blob_reset(&out);
  1143   1594       db_multi_exec(
  1144         -       "INSERT OR IGNORE INTO concealed VALUES(%Q,%#Q)",
         1595  +       "INSERT OR IGNORE INTO concealed(hash,content,mtime)"
         1596  +       " VALUES(%Q,%#Q,now())",
  1145   1597          zHash, n, zContent
  1146   1598       );
  1147   1599     }
  1148   1600     return zHash;
  1149   1601   }
  1150   1602   
  1151   1603   /*
................................................................................
  1155   1607   ** false or if the lookup fails, return the original string content.
  1156   1608   **
  1157   1609   ** In either case, the string returned is stored in space obtained
  1158   1610   ** from malloc and should be freed by the calling function.
  1159   1611   */
  1160   1612   char *db_reveal(const char *zKey){
  1161   1613     char *zOut;
  1162         -  if( g.okRdAddr ){
         1614  +  if( g.perm.RdAddr ){
  1163   1615       zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey);
  1164   1616     }else{
  1165   1617       zOut = 0;
  1166   1618     }
  1167   1619     if( zOut==0 ){
  1168   1620       zOut = mprintf("%s", zKey);
  1169   1621     }
  1170   1622     return zOut;
  1171   1623   }
  1172   1624   
  1173         -/*
  1174         -** This function registers auxiliary functions when the SQLite
  1175         -** database connection is first established.
  1176         -*/
  1177         -LOCAL void db_connection_init(void){
  1178         -  sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
  1179         -  sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
  1180         -  sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
  1181         -  sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
  1182         -  sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
  1183         -  sqlite3_create_function(
  1184         -    g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
  1185         -  );
  1186         -  if( g.fSqlTrace ){
  1187         -    sqlite3_trace(g.db, db_sql_trace, 0);
  1188         -  }
  1189         -}
  1190         -
  1191   1625   /*
  1192   1626   ** Return true if the string zVal represents "true" (or "false").
  1193   1627   */
  1194   1628   int is_truth(const char *zVal){
  1195         -  static const char *azOn[] = { "on", "yes", "true", "1" };
         1629  +  static const char *const azOn[] = { "on", "yes", "true", "1" };
  1196   1630     int i;
  1197   1631     for(i=0; i<sizeof(azOn)/sizeof(azOn[0]); i++){
  1198         -    if( strcmp(zVal,azOn[i])==0 ) return 1;
         1632  +    if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
  1199   1633     }
  1200   1634     return 0;
  1201   1635   }
  1202   1636   int is_false(const char *zVal){
  1203         -  static const char *azOff[] = { "off", "no", "false", "0" };
         1637  +  static const char *const azOff[] = { "off", "no", "false", "0" };
  1204   1638     int i;
  1205   1639     for(i=0; i<sizeof(azOff)/sizeof(azOff[0]); i++){
  1206         -    if( strcmp(zVal,azOff[i])==0 ) return 1;
         1640  +    if( fossil_stricmp(zVal,azOff[i])==0 ) return 1;
  1207   1641     }
  1208   1642     return 0;
  1209   1643   }
  1210   1644   
  1211   1645   /*
  1212   1646   ** Swap the g.db and g.dbConfig connections so that the various db_* routines
  1213   1647   ** work on the ~/.fossil database instead of on the repository database.
  1214   1648   ** Be sure to swap them back after doing the operation.
  1215   1649   **
  1216         -** If g.useAttach that means the ~/.fossil database was opened with
  1217         -** the useAttach flag set to 1.  In that case no connection swap is required
  1218         -** so this routine is a no-op.
         1650  +** If the ~/.fossil database has already been opened as the main database or
         1651  +** is attached to the main database, no connection swaps are required so this
         1652  +** routine is a no-op.
  1219   1653   */
  1220   1654   void db_swap_connections(void){
  1221         -  if( !g.useAttach ){
         1655  +  /*
         1656  +  ** When swapping the main database connection with the config database
         1657  +  ** connection, the config database connection must be open (not simply
         1658  +  ** attached); otherwise, the swap would end up leaving the main database
         1659  +  ** connection invalid, defeating the very purpose of this routine.  This
         1660  +  ** same constraint also holds true when restoring the previously swapped
         1661  +  ** database connection; otherwise, it means that no swap was performed
         1662  +  ** because the main database connection was already pointing to the config
         1663  +  ** database.
         1664  +  */
         1665  +  if( g.dbConfig ){
  1222   1666       sqlite3 *dbTemp = g.db;
  1223         -    assert( g.dbConfig!=0 );
         1667  +    const char *zTempDbType = g.zMainDbType;
  1224   1668       g.db = g.dbConfig;
         1669  +    g.zMainDbType = g.zConfigDbType;
  1225   1670       g.dbConfig = dbTemp;
         1671  +    g.zConfigDbType = zTempDbType;
  1226   1672     }
  1227   1673   }
         1674  +
         1675  +/*
         1676  +** Logic for reading potentially versioned settings from
         1677  +** .fossil-settings/<name> , and emits warnings if necessary.
         1678  +** Returns the non-versioned value without modification if there is no
         1679  +** versioned value.
         1680  +*/
         1681  +static char *db_get_do_versionable(const char *zName, char *zNonVersionedSetting){
         1682  +  char *zVersionedSetting = 0;
         1683  +  int noWarn = 0;
         1684  +  struct _cacheEntry {
         1685  +    struct _cacheEntry *next;
         1686  +    const char *zName, *zValue;
         1687  +  } *cacheEntry = 0;
         1688  +  static struct _cacheEntry *cache = 0;
         1689  +
         1690  +  /* Look up name in cache */
         1691  +  cacheEntry = cache;
         1692  +  while( cacheEntry!=0 ){
         1693  +    if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
         1694  +      zVersionedSetting = fossil_strdup(cacheEntry->zValue);
         1695  +      break;
         1696  +    }
         1697  +    cacheEntry = cacheEntry->next;
         1698  +  }
         1699  +  /* Attempt to read value from file in checkout if there wasn't a cache hit
         1700  +  ** and a checkout is open. */
         1701  +  if( cacheEntry==0 ){
         1702  +    Blob versionedPathname;
         1703  +    char *zVersionedPathname;
         1704  +    blob_zero(&versionedPathname);
         1705  +    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
         1706  +                 g.zLocalRoot, zName);
         1707  +    zVersionedPathname = blob_str(&versionedPathname);
         1708  +    if( file_size(zVersionedPathname)>=0 ){
         1709  +      /* File exists, and contains the value for this setting. Load from
         1710  +      ** the file. */
         1711  +      Blob setting;
         1712  +      blob_zero(&setting);
         1713  +      if( blob_read_from_file(&setting, zVersionedPathname) >= 0 ){
         1714  +        blob_trim(&setting); /* Avoid non-obvious problems with line endings
         1715  +                             ** on boolean properties */
         1716  +        zVersionedSetting = strdup(blob_str(&setting));
         1717  +      }
         1718  +      blob_reset(&setting);
         1719  +      /* See if there's a no-warn flag */
         1720  +      blob_append(&versionedPathname, ".no-warn", -1);
         1721  +      if( file_size(blob_str(&versionedPathname))>=0 ){
         1722  +        noWarn = 1;
         1723  +      }
         1724  +    }
         1725  +    blob_reset(&versionedPathname);
         1726  +    /* Store result in cache, which can be the value or 0 if not found */
         1727  +    cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
         1728  +    cacheEntry->next = cache;
         1729  +    cacheEntry->zName = zName;
         1730  +    cacheEntry->zValue = fossil_strdup(zVersionedSetting);
         1731  +    cache = cacheEntry;
         1732  +  }
         1733  +  /* Display a warning? */
         1734  +  if( zVersionedSetting!=0 && zNonVersionedSetting!=0
         1735  +   && zNonVersionedSetting[0]!='\0' && !noWarn
         1736  +  ){
         1737  +    /* There's a versioned setting, and a non-versioned setting. Tell
         1738  +    ** the user about the conflict */
         1739  +    fossil_warning(
         1740  +        "setting %s has both versioned and non-versioned values: using "
         1741  +        "versioned value from file .fossil-settings/%s (to silence this "
         1742  +        "warning, either create an empty file named "
         1743  +        ".fossil-settings/%s.no-warn or delete the non-versioned setting "
         1744  +        " with \"fossil unset %s\")", zName, zName, zName, zName
         1745  +    );
         1746  +  }
         1747  +  /* Prefer the versioned setting */
         1748  +  return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
         1749  +}
         1750  +
  1228   1751   
  1229   1752   /*
  1230   1753   ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
  1231   1754   ** repository and local databases.
  1232   1755   */
  1233   1756   char *db_get(const char *zName, char *zDefault){
  1234   1757     char *z = 0;
         1758  +  int i;
         1759  +  const struct stControlSettings *ctrlSetting = 0;
         1760  +  /* Is this a setting? */
         1761  +  for(i=0; ctrlSettings[i].name; i++){
         1762  +    if( strcmp(ctrlSettings[i].name, zName)==0 ){
         1763  +      ctrlSetting = &(ctrlSettings[i]);
         1764  +      break;
         1765  +    }
         1766  +  }
  1235   1767     if( g.repositoryOpen ){
  1236   1768       z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
  1237   1769     }
  1238   1770     if( z==0 && g.configOpen ){
  1239   1771       db_swap_connections();
  1240   1772       z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
  1241   1773       db_swap_connections();
         1774  +  }
         1775  +  if( ctrlSetting!=0 && ctrlSetting->versionable && g.localOpen ){
         1776  +    /* This is a versionable setting, try and get the info from a checked out file */
         1777  +    z = db_get_do_versionable(zName, z);
  1242   1778     }
  1243   1779     if( z==0 ){
  1244   1780       z = zDefault;
  1245   1781     }
  1246   1782     return z;
  1247   1783   }
  1248   1784   void db_set(const char *zName, const char *zValue, int globalFlag){
................................................................................
  1249   1785     db_begin_transaction();
  1250   1786     if( globalFlag ){
  1251   1787       db_swap_connections();
  1252   1788       db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
  1253   1789                      zName, zValue);
  1254   1790       db_swap_connections();
  1255   1791     }else{
  1256         -    db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)",
         1792  +    db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
  1257   1793                      zName, zValue);
  1258   1794     }
  1259   1795     if( globalFlag && g.repositoryOpen ){
  1260   1796       db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
  1261   1797     }
  1262   1798     db_end_transaction(0);
  1263   1799   }
................................................................................
  1308   1844   void db_set_int(const char *zName, int value, int globalFlag){
  1309   1845     if( globalFlag ){
  1310   1846       db_swap_connections();
  1311   1847       db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
  1312   1848                     zName, value);
  1313   1849       db_swap_connections();
  1314   1850     }else{
  1315         -    db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)",
         1851  +    db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())",
  1316   1852                     zName, value);
  1317   1853     }
  1318   1854     if( globalFlag && g.repositoryOpen ){
  1319   1855       db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
  1320   1856     }
  1321   1857   }
  1322   1858   int db_get_boolean(const char *zName, int dflt){
................................................................................
  1337   1873   }
  1338   1874   void db_lset_int(const char *zName, int value){
  1339   1875     db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
  1340   1876   }
  1341   1877   
  1342   1878   /*
  1343   1879   ** Record the name of a local repository in the global_config() database.
  1344         -** The repostiroy filename %s is recorded as an entry with a "name" field
         1880  +** The repository filename %s is recorded as an entry with a "name" field
  1345   1881   ** of the following form:
  1346   1882   **
  1347   1883   **       repo:%s
  1348   1884   **
  1349   1885   ** The value field is set to 1.
         1886  +**
         1887  +** If running from a local checkout, also record the root of the checkout
         1888  +** as follows:
         1889  +**
         1890  +**       ckout:%s
         1891  +**
         1892  +** Where %s is the checkout root.  The value is the repository file.
  1350   1893   */
  1351   1894   void db_record_repository_filename(const char *zName){
  1352   1895     Blob full;
  1353   1896     if( zName==0 ){
  1354   1897       if( !g.localOpen ) return;
  1355         -    zName = db_lget("repository", 0);
         1898  +    zName = db_repository_filename();
  1356   1899     }
  1357         -  file_canonical_name(zName, &full);
         1900  +  file_canonical_name(zName, &full, 0);
  1358   1901     db_swap_connections();
  1359   1902     db_multi_exec(
  1360   1903        "INSERT OR IGNORE INTO global_config(name,value)"
  1361   1904        "VALUES('repo:%q',1)",
  1362   1905        blob_str(&full)
  1363   1906     );
  1364         -  db_swap_connections();
         1907  +  if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
         1908  +    Blob localRoot;
         1909  +    file_canonical_name(g.zLocalRoot, &localRoot, 1);
         1910  +    db_multi_exec(
         1911  +      "REPLACE INTO global_config(name, value)"
         1912  +      "VALUES('ckout:%q','%q');",
         1913  +      blob_str(&localRoot), blob_str(&full)
         1914  +    );
         1915  +    db_swap_connections();
         1916  +    db_optional_sql("repository",
         1917  +        "REPLACE INTO config(name,value,mtime)"
         1918  +        "VALUES('ckout:%q',1,now())",
         1919  +        blob_str(&localRoot)
         1920  +    );
         1921  +    blob_reset(&localRoot);
         1922  +  }else{
         1923  +    db_swap_connections();
         1924  +  }
  1365   1925     blob_reset(&full);
  1366   1926   }
  1367   1927   
  1368   1928   /*
  1369   1929   ** COMMAND: open
  1370   1930   **
  1371         -** Usage: %fossil open FILENAME ?VERSION? ?--keep?
         1931  +** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS?
  1372   1932   **
  1373   1933   ** Open a connection to the local repository in FILENAME.  A checkout
  1374   1934   ** for the repository is created with its root at the working directory.
  1375   1935   ** If VERSION is specified then that version is checked out.  Otherwise
  1376   1936   ** the latest version is checked out.  No files other than "manifest"
  1377   1937   ** and "manifest.uuid" are modified if the --keep option is present.
  1378   1938   **
  1379         -** See also the "close" command.
         1939  +** Options:
         1940  +**   --keep     Only modify the manifest and manifest.uuid files
         1941  +**   --nested   Allow opening a repository inside an opened checkout
         1942  +**
         1943  +** See also: close
  1380   1944   */
  1381   1945   void cmd_open(void){
  1382   1946     Blob path;
  1383   1947     int vid;
  1384   1948     int keepFlag;
  1385         -  static char *azNewArgv[] = { 0, "checkout", "--latest", 0, 0, 0 };
         1949  +  int allowNested;
         1950  +  static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0 };
         1951  +
  1386   1952     url_proxy_options();
  1387   1953     keepFlag = find_option("keep",0,0)!=0;
         1954  +  allowNested = find_option("nested",0,0)!=0;
  1388   1955     if( g.argc!=3 && g.argc!=4 ){
  1389   1956       usage("REPOSITORY-FILENAME ?VERSION?");
  1390   1957     }
  1391         -  if( db_open_local() ){
         1958  +  if( !allowNested && db_open_local() ){
  1392   1959       fossil_panic("already within an open tree rooted at %s", g.zLocalRoot);
  1393   1960     }
  1394         -  file_canonical_name(g.argv[2], &path);
         1961  +  file_canonical_name(g.argv[2], &path, 0);
  1395   1962     db_open_repository(blob_str(&path));
  1396         -  db_init_database("./_FOSSIL_", zLocalSchema, (char*)0);
         1963  +#if defined(_WIN32)
         1964  +# define LOCALDB_NAME "./_FOSSIL_"
         1965  +#else
         1966  +# define LOCALDB_NAME "./.fslckout"
         1967  +#endif
         1968  +  db_init_database(LOCALDB_NAME, zLocalSchema, (char*)0);
         1969  +  db_delete_on_failure(LOCALDB_NAME);
  1397   1970     db_open_local();
  1398         -  db_lset("repository", blob_str(&path));
         1971  +  db_lset("repository", g.argv[2]);
  1399   1972     db_record_repository_filename(blob_str(&path));
  1400   1973     vid = db_int(0, "SELECT pid FROM plink y"
  1401   1974                     " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)");
  1402   1975     if( vid==0 ){
  1403   1976       db_lset_int("checkout", 1);
  1404   1977     }else{
  1405   1978       char **oldArgv = g.argv;
................................................................................
  1406   1979       int oldArgc = g.argc;
  1407   1980       db_lset_int("checkout", vid);
  1408   1981       azNewArgv[0] = g.argv[0];
  1409   1982       g.argv = azNewArgv;
  1410   1983       g.argc = 3;
  1411   1984       if( oldArgc==4 ){
  1412   1985         azNewArgv[g.argc-1] = oldArgv[3];
         1986  +    }else{
         1987  +      azNewArgv[g.argc-1] = db_get("main-branch", "trunk");
  1413   1988       }
  1414   1989       if( keepFlag ){
  1415   1990         azNewArgv[g.argc++] = "--keep";
  1416   1991       }
  1417   1992       checkout_cmd();
  1418   1993       g.argc = 2;
  1419   1994       info_cmd();
  1420   1995     }
  1421   1996   }
  1422   1997   
  1423   1998   /*
  1424   1999   ** Print the value of a setting named zName
  1425   2000   */
  1426         -static void print_setting(const char *zName){
         2001  +static void print_setting(const struct stControlSettings *ctrlSetting, int localOpen){
  1427   2002     Stmt q;
  1428   2003     if( g.repositoryOpen ){
  1429   2004       db_prepare(&q,
  1430   2005          "SELECT '(local)', value FROM config WHERE name=%Q"
  1431   2006          " UNION ALL "
  1432   2007          "SELECT '(global)', value FROM global_config WHERE name=%Q",
  1433         -       zName, zName
         2008  +       ctrlSetting->name, ctrlSetting->name
  1434   2009       );
  1435   2010     }else{
  1436   2011       db_prepare(&q,
  1437   2012         "SELECT '(global)', value FROM global_config WHERE name=%Q",
  1438         -      zName
         2013  +      ctrlSetting->name
  1439   2014       );
  1440   2015     }
  1441   2016     if( db_step(&q)==SQLITE_ROW ){
  1442         -    printf("%-20s %-8s %s\n", zName, db_column_text(&q, 0),
         2017  +    fossil_print("%-20s %-8s %s\n", ctrlSetting->name, db_column_text(&q, 0),
  1443   2018           db_column_text(&q, 1));
  1444   2019     }else{
  1445         -    printf("%-20s\n", zName);
         2020  +    fossil_print("%-20s\n", ctrlSetting->name);
         2021  +  }
         2022  +  if( ctrlSetting->versionable && localOpen ){
         2023  +    /* Check to see if this is overridden by a versionable settings file */
         2024  +    Blob versionedPathname;
         2025  +    blob_zero(&versionedPathname);
         2026  +    blob_appendf(&versionedPathname, "%s/.fossil-settings/%s", g.zLocalRoot, ctrlSetting->name);
         2027  +    if( file_size(blob_str(&versionedPathname))>=0 ){
         2028  +      fossil_print("  (overridden by contents of file .fossil-settings/%s)\n", ctrlSetting->name);
         2029  +    }
  1446   2030     }
  1447   2031     db_finalize(&q);
  1448   2032   }
  1449   2033   
  1450   2034   
         2035  +/*
         2036  +** define all settings, which can be controlled via the set/unset
         2037  +** command. var is the name of the internal configuration name for db_(un)set.
         2038  +** If var is 0, the settings name is used.
         2039  +** width is the length for the edit field on the behavior page, 0
         2040  +** is used for on/off checkboxes.
         2041  +** The behaviour page doesn't use a special layout. It lists all
         2042  +** set-commands and displays the 'set'-help as info.
         2043  +*/
         2044  +#if INTERFACE
         2045  +struct stControlSettings {
         2046  +  char const *name;     /* Name of the setting */
         2047  +  char const *var;      /* Internal variable name used by db_set() */
         2048  +  int width;            /* Width of display.  0 for boolean values */
         2049  +  int versionable;      /* Is this setting versionable? */
         2050  +  char const *def;      /* Default value */
         2051  +};
         2052  +#endif /* INTERFACE */
         2053  +struct stControlSettings const ctrlSettings[] = {
         2054  +  { "access-log",    0,                0, 0, "off"                 },
         2055  +  { "allow-symlinks",0,                0, 1, "off"                 },
         2056  +  { "auto-captcha",  "autocaptcha",    0, 0, "on"                  },
         2057  +  { "auto-hyperlink",0,                0, 0, "on",                 },
         2058  +  { "auto-shun",     0,                0, 0, "on"                  },
         2059  +  { "autosync",      0,                0, 0, "on"                  },
         2060  +  { "binary-glob",   0,               40, 1, ""                    },
         2061  +  { "clearsign",     0,                0, 0, "off"                 },
         2062  +  { "case-sensitive",0,                0, 0, "on"                  },
         2063  +  { "crnl-glob",     0,               40, 1, ""                    },
         2064  +  { "default-perms", 0,               16, 0, "u"                   },
         2065  +  { "diff-binary",   0,                0, 0, "on"                  },
         2066  +  { "diff-command",  0,               40, 0, ""                    },
         2067  +  { "dont-push",     0,                0, 0, "off"                 },
         2068  +  { "editor",        0,               32, 0, ""                    },
         2069  +  { "empty-dirs",    0,               40, 1, ""                    },
         2070  +  { "encoding-glob",  0,              40, 1, ""                    },
         2071  +  { "gdiff-command", 0,               40, 0, "gdiff"               },
         2072  +  { "gmerge-command",0,               40, 0, ""                    },
         2073  +  { "http-port",     0,               16, 0, "8080"                },
         2074  +  { "https-login",   0,                0, 0, "off"                 },
         2075  +  { "ignore-glob",   0,               40, 1, ""                    },
         2076  +  { "localauth",     0,                0, 0, "off"                 },
         2077  +  { "main-branch",   0,               40, 0, "trunk"               },
         2078  +  { "manifest",      0,                0, 1, "off"                 },
         2079  +#ifdef FOSSIL_ENABLE_MARKDOWN
         2080  +  { "markdown",      0,                0, 0, "off"                 },
         2081  +#endif
         2082  +  { "max-upload",    0,               25, 0, "250000"              },
         2083  +  { "mtime-changes", 0,                0, 0, "on"                  },
         2084  +  { "pgp-command",   0,               40, 0, "gpg --clearsign -o " },
         2085  +  { "proxy",         0,               32, 0, "off"                 },
         2086  +  { "relative-paths",0,                0, 0, "on"                  },
         2087  +  { "repo-cksum",    0,                0, 0, "on"                  },
         2088  +  { "self-register", 0,                0, 0, "off"                 },
         2089  +  { "ssh-command",   0,               40, 0, ""                    },
         2090  +  { "ssl-ca-location",0,              40, 0, ""                    },
         2091  +  { "ssl-identity",  0,               40, 0, ""                    },
         2092  +#ifdef FOSSIL_ENABLE_TCL
         2093  +  { "tcl",           0,                0, 0, "off"                 },
         2094  +  { "tcl-setup",     0,               40, 0, ""                    },
         2095  +#endif
         2096  +  { "th1-setup",     0,               40, 0, ""                    },
         2097  +  { "web-browser",   0,               32, 0, ""                    },
         2098  +  { "white-foreground", 0,             0, 0, "off"                 },
         2099  +  { 0,0,0,0,0 }
         2100  +};
         2101  +
  1451   2102   /*
  1452   2103   ** COMMAND: settings
  1453         -** COMMAND: unset
  1454         -** %fossil setting ?PROPERTY? ?VALUE? ?-global?
  1455         -** %fossil unset PROPERTY ?-global?
         2104  +** COMMAND: unset*
  1456   2105   **
  1457         -** The "setting" command with no arguments lists all properties and their
         2106  +** %fossil settings ?PROPERTY? ?VALUE? ?OPTIONS?
         2107  +** %fossil unset PROPERTY ?OPTIONS?
         2108  +**
         2109  +** The "settings" command with no arguments lists all properties and their
  1458   2110   ** values.  With just a property name it shows the value of that property.
  1459   2111   ** With a value argument it changes the property for the current repository.
         2112  +**
         2113  +** Settings marked as versionable are overridden by the contents of the
         2114  +** file named .fossil-settings/PROPERTY in the checked out files, if that
         2115  +** file exists.
  1460   2116   **
  1461   2117   ** The "unset" command clears a property setting.
  1462   2118   **
         2119  +**
         2120  +**    access-log       If enabled, record successful and failed login attempts
         2121  +**                     in the "accesslog" table.  Default: off
         2122  +**
         2123  +**    allow-symlinks   If enabled, don't follow symlinks, and instead treat
         2124  +**     (versionable)   them as symlinks on Unix. Has no effect on Windows
         2125  +**                     (existing links in repository created on Unix become
         2126  +**                     plain-text files with link destination path inside).
         2127  +**                     Default: off
         2128  +**
         2129  +**    auto-captcha     If enabled, the Login page provides a button to
         2130  +**                     fill in the captcha password.  Default: on
         2131  +**
         2132  +**    auto-hyperlink   Use javascript to enable hyperlinks on web pages
         2133  +**                     for all users (regardless of the "h" privilege) if the
         2134  +**                     User-Agent string in the HTTP header look like it came
         2135  +**                     from real person, not a spider or bot.  Default: on
         2136  +**
         2137  +**    auto-shun        If enabled, automatically pull the shunning list
         2138  +**                     from a server to which the client autosyncs.
         2139  +**                     Default: on
  1463   2140   **
  1464   2141   **    autosync         If enabled, automatically pull prior to commit
  1465   2142   **                     or update and automatically push after commit or
  1466         -**                     tag or branch creation.  If the the value is "pullonly"
         2143  +**                     tag or branch creation.  If the value is "pullonly"
  1467   2144   **                     then only pull operations occur automatically.
         2145  +**                     Default: on
  1468   2146   **
  1469         -**    binary-glob      The VALUE is a comma-separated list of GLOB patterns
  1470         -**                     that should be treated as binary files for merging
  1471         -**                     purposes.  Example:   *.xml
         2147  +**    binary-glob      The VALUE is a comma or newline-separated list of
         2148  +**     (versionable)   GLOB patterns that should be treated as binary files
         2149  +**                     for committing and merging purposes.  Example: *.jpg
         2150  +**
         2151  +**    case-sensitive   If TRUE, the files whose names differ only in case
         2152  +**                     care considered distinct.  If FALSE files whose names
         2153  +**                     differ only in case are the same file.  Defaults to
         2154  +**                     TRUE for unix and FALSE for windows and mac.
  1472   2155   **
  1473   2156   **    clearsign        When enabled, fossil will attempt to sign all commits
  1474   2157   **                     with gpg.  When disabled (the default), commits will
  1475         -**                     be unsigned.
         2158  +**                     be unsigned.  Default: off
         2159  +**
         2160  +**    crnl-glob        A comma or newline-separated list of GLOB patterns for
         2161  +**     (versionable)   text files in which it is ok to have CR+NL line endings.
         2162  +**                     Set to "*" to disable CR+NL checking.
         2163  +**
         2164  +**    default-perms    Permissions given automatically to new users.  For more
         2165  +**                     information on permissions see Users page in Server
         2166  +**                     Administration of the HTTP UI. Default: u.
         2167  +**
         2168  +**    diff-binary      If TRUE (the default), permit files that may be binary
         2169  +**                     or that match the "binary-glob" setting to be used with
         2170  +**                     external diff programs.  If FALSE, skip these files.
  1476   2171   **
  1477   2172   **    diff-command     External command to run when performing a diff.
  1478   2173   **                     If undefined, the internal text diff will be used.
  1479   2174   **
  1480   2175   **    dont-push        Prevent this repository from pushing from client to
  1481   2176   **                     server.  Useful when setting up a private branch.
  1482   2177   **
  1483   2178   **    editor           Text editor command used for check-in comments.
         2179  +**
         2180  +**    empty-dirs       A comma or newline-separated list of pathnames. On
         2181  +**     (versionable)   update and checkout commands, if no file or directory
         2182  +**                     exists with that name, an empty directory will be
         2183  +**                     created.
         2184  +**
         2185  +**    encoding-glob    The VALUE is a comma or newline-separated list of GLOB
         2186  +**     (versionable)   patterns specifying files that the "commit" command will
         2187  +**                     ignore when issuing warnings about text files that may
         2188  +**                     use another encoding than ASCII or UTF-8. Set to "*"
         2189  +**                     to disable encoding checking.
  1484   2190   **
  1485   2191   **    gdiff-command    External command to run when performing a graphical
  1486   2192   **                     diff. If undefined, text diff will be used.
         2193  +**
         2194  +**    gmerge-command   A graphical merge conflict resolver command operating
         2195  +**                     on four files.
         2196  +**                     Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output"
         2197  +**                     Ex: xxdiff "%original" "%baseline" "%merge" -M "%output"
         2198  +**                     Ex: meld "%baseline" "%original" "%merge" "%output"
  1487   2199   **
  1488   2200   **    http-port        The TCP/IP port number to use by the "server"
  1489   2201   **                     and "ui" commands.  Default: 8080
  1490   2202   **
  1491         -**    ignore-glob      The VALUE is a comma-separated list of GLOB patterns
  1492         -**                     specifying files that the "extra" command will ignore.
  1493         -**                     Example:  *.o,*.obj,*.exe
         2203  +**    https-login      Send login credentials using HTTPS instead of HTTP
         2204  +**                     even if the login page request came via HTTP.
         2205  +**
         2206  +**    ignore-glob      The VALUE is a comma or newline-separated list of GLOB
         2207  +**     (versionable)   patterns specifying files that the "extra" command will
         2208  +**                     ignore.  Example:  *.o,*.obj,*.exe
  1494   2209   **
  1495   2210   **    localauth        If enabled, require that HTTP connections from
  1496   2211   **                     127.0.0.1 be authenticated by password.  If
  1497   2212   **                     false, all HTTP requests from localhost have
  1498   2213   **                     unrestricted access to the repository.
         2214  +**
         2215  +**    main-branch      The primary branch for the project.  Default: trunk
         2216  +**
         2217  +**    manifest         If enabled, automatically create files "manifest" and
         2218  +**     (versionable)   "manifest.uuid" in every checkout.  The SQLite and
         2219  +**                     Fossil repositories both require this.  Default: off.
         2220  +**
         2221  +**    markdown         If enabled (and Fossil was compiled with markdown
         2222  +**                     support), the markdown engine will be used to render
         2223  +**                     embedded documentation conforming to the appropriate
         2224  +**                     content types (e.g. "text/x-markdown"). Default: off.
         2225  +**
         2226  +**    max-upload       A limit on the size of uplink HTTP requests.  The
         2227  +**                     default is 250000 bytes.
  1499   2228   **
  1500   2229   **    mtime-changes    Use file modification times (mtimes) to detect when
  1501   2230   **                     files have been modified.  (Default "on".)
  1502   2231   **
  1503   2232   **    pgp-command      Command used to clear-sign manifests at check-in.
  1504   2233   **                     The default is "gpg --clearsign -o ".
  1505   2234   **
  1506   2235   **    proxy            URL of the HTTP proxy.  If undefined or "off" then
  1507   2236   **                     the "http_proxy" environment variable is consulted.
  1508   2237   **                     If the http_proxy environment variable is undefined
  1509   2238   **                     then a direct HTTP connection is used.
         2239  +**
         2240  +**    relative-paths   When showing changes and extras, report paths relative
         2241  +**                     to the current working directory.  Default: "on"
         2242  +**
         2243  +**    repo-cksum       Compute checksums over all files in each checkout
         2244  +**                     as a double-check of correctness.  Defaults to "on".
         2245  +**                     Disable on large repositories for a performance
         2246  +**                     improvement.
         2247  +**
         2248  +**    self-register    Allow users to register themselves through the HTTP UI.
         2249  +**                     This is useful if you want to see other names than
         2250  +**                     "Anonymous" in e.g. ticketing system. On the other hand
         2251  +**                     users can not be deleted. Default: off.
         2252  +**
         2253  +**    ssh-command      Command used to talk to a remote machine with
         2254  +**                     the "ssh://" protocol.
         2255  +**
         2256  +**    ssl-ca-location  The full pathname to a file containing PEM encoded
         2257  +**                     CA root certificates, or a directory of certificates
         2258  +**                     with filenames formed from the certificate hashes as
         2259  +**                     required by OpenSSL.
         2260  +**                     If set, this will override the OS default list of
         2261  +**                     OpenSSL CAs. If unset, the default list will be used.
         2262  +**                     Some platforms may add additional certificates.
         2263  +**                     Check your platform behaviour is as required if the
         2264  +**                     exact contents of the CA root is critical for your
         2265  +**                     application.
         2266  +**
         2267  +**    ssl-identity     The full pathname to a file containing a certificate
         2268  +**                     and private key in PEM format. Create by concatenating
         2269  +**                     the certificate and private key files.
         2270  +**                     This identity will be presented to SSL servers to
         2271  +**                     authenticate this client, in addition to the normal
         2272  +**                     password authentication.
         2273  +**
         2274  +**    tcl              If enabled (and Fossil was compiled with Tcl support),
         2275  +**                     Tcl integration commands will be added to the TH1
         2276  +**                     interpreter, allowing arbitrary Tcl expressions and
         2277  +**                     scripts to be evaluated from TH1.  Additionally, the Tcl
         2278  +**                     interpreter will be able to evaluate arbitrary TH1
         2279  +**                     expressions and scripts. Default: off.
         2280  +**
         2281  +**    tcl-setup        This is the setup script to be evaluated after creating
         2282  +**                     and initializing the Tcl interpreter.  By default, this
         2283  +**                     is empty and no extra setup is performed.
         2284  +**
         2285  +**    th1-setup        This is the setup script to be evaluated after creating
         2286  +**                     and initializing the TH1 interpreter.  By default, this
         2287  +**                     is empty and no extra setup is performed.
  1510   2288   **
  1511   2289   **    web-browser      A shell command used to launch your preferred
  1512   2290   **                     web browser when given a URL as an argument.
  1513   2291   **                     Defaults to "start" on windows, "open" on Mac,
  1514   2292   **                     and "firefox" on Unix.
         2293  +**
         2294  +** Options:
         2295  +**   --global   set or unset the given property globally instead of
         2296  +**              setting or unsetting it for the open repository only.
         2297  +**
         2298  +** See also: configuration
  1515   2299   */
  1516   2300   void setting_cmd(void){
  1517         -  static const char *azName[] = {
  1518         -    "autosync",
  1519         -    "binary-glob",
  1520         -    "clearsign",
  1521         -    "diff-command",
  1522         -    "dont-push",
  1523         -    "editor",
  1524         -    "gdiff-command",
  1525         -    "ignore-glob",
  1526         -    "http-port",
  1527         -    "localauth",
  1528         -    "mtime-changes",
  1529         -    "pgp-command",
  1530         -    "proxy",
  1531         -    "web-browser",
  1532         -  };
  1533   2301     int i;
  1534   2302     int globalFlag = find_option("global","g",0)!=0;
  1535   2303     int unsetFlag = g.argv[1][0]=='u';
  1536   2304     db_open_config(1);
  1537         -  db_find_and_open_repository(0);
         2305  +  if( !globalFlag ){
         2306  +    db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
         2307  +  }
  1538   2308     if( !g.repositoryOpen ){
  1539   2309       globalFlag = 1;
  1540   2310     }
  1541   2311     if( unsetFlag && g.argc!=3 ){
  1542   2312       usage("PROPERTY ?-global?");
  1543   2313     }
  1544   2314     if( g.argc==2 ){
  1545         -    for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
  1546         -      print_setting(azName[i]);
         2315  +    int openLocal = db_open_local();
         2316  +    for(i=0; ctrlSettings[i].name; i++){
         2317  +      print_setting(&ctrlSettings[i], openLocal);
  1547   2318       }
  1548   2319     }else if( g.argc==3 || g.argc==4 ){
  1549   2320       const char *zName = g.argv[2];
         2321  +    int isManifest;
  1550   2322       int n = strlen(zName);
  1551         -    for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
  1552         -      if( strncmp(azName[i], zName, n)==0 ) break;
         2323  +    for(i=0; ctrlSettings[i].name; i++){
         2324  +      if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
  1553   2325       }
  1554         -    if( i>=sizeof(azName)/sizeof(azName[0]) ){
         2326  +    if( !ctrlSettings[i].name ){
  1555   2327         fossil_fatal("no such setting: %s", zName);
  1556   2328       }
         2329  +    isManifest = fossil_strcmp(ctrlSettings[i].name, "manifest")==0;
         2330  +    if( isManifest && globalFlag ){
         2331  +      fossil_fatal("cannot set 'manifest' globally");
         2332  +    }
  1557   2333       if( unsetFlag ){
  1558         -      db_unset(azName[i], globalFlag);
         2334  +      db_unset(ctrlSettings[i].name, globalFlag);
  1559   2335       }else if( g.argc==4 ){
  1560         -      db_set(azName[i], g.argv[3], globalFlag);
         2336  +      db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
  1561   2337       }else{
  1562         -      print_setting(azName[i]);
         2338  +      isManifest = 0;
         2339  +      print_setting(&ctrlSettings[i], db_open_local());
         2340  +    }
         2341  +    if( isManifest && g.localOpen ){
         2342  +      manifest_to_disk(db_lget_int("checkout", 0));
  1563   2343       }
  1564   2344     }else{
  1565   2345       usage("?PROPERTY? ?VALUE?");
  1566   2346     }
  1567   2347   }
         2348  +
         2349  +/*
         2350  +** The input in a timespan measured in days.  Return a string which
         2351  +** describes that timespan in units of seconds, minutes, hours, days,
         2352  +** or years, depending on its duration.
         2353  +*/
         2354  +char *db_timespan_name(double rSpan){
         2355  +  if( rSpan<0 ) rSpan = -rSpan;
         2356  +  rSpan *= 24.0*3600.0;  /* Convert units to seconds */
         2357  +  if( rSpan<120.0 ){
         2358  +    return sqlite3_mprintf("%.1f seconds", rSpan);
         2359  +  }
         2360  +  rSpan /= 60.0;         /* Convert units to minutes */
         2361  +  if( rSpan<90.0 ){
         2362  +    return sqlite3_mprintf("%.1f minutes", rSpan);
         2363  +  }
         2364  +  rSpan /= 60.0;         /* Convert units to hours */
         2365  +  if( rSpan<=48.0 ){
         2366  +    return sqlite3_mprintf("%.1f hours", rSpan);
         2367  +  }
         2368  +  rSpan /= 24.0;         /* Convert units to days */
         2369  +  if( rSpan<=365.0 ){
         2370  +    return sqlite3_mprintf("%.1f days", rSpan);
         2371  +  }
         2372  +  rSpan /= 356.24;         /* Convert units to years */
         2373  +  return sqlite3_mprintf("%.1f years", rSpan);
         2374  +}
         2375  +
         2376  +/*
         2377  +** COMMAND: test-timespan
         2378  +** %fossil test-timespan TIMESTAMP
         2379  +**
         2380  +** Print the approximate span of time from now to TIMESTAMP.
         2381  +*/
         2382  +void test_timespan_cmd(void){
         2383  +  double rDiff;
         2384  +  if( g.argc!=3 ) usage("TIMESTAMP");
         2385  +  sqlite3_open(":memory:", &g.db);
         2386  +  rDiff = db_double(0.0, "SELECT julianday('now') - julianday(%Q)", g.argv[2]);
         2387  +  fossil_print("Time differences: %s\n", db_timespan_name(rDiff));
         2388  +  sqlite3_close(g.db);
         2389  +  g.db = 0;
         2390  +}

Changes to src/delta.c.

    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This module implements the delta compress algorithm.
    19     19   **
    20     20   ** Though developed specifically for fossil, the code in this file
    21         -** is generally appliable and is thus easily separated from the
           21  +** is generally applicable and is thus easily separated from the
    22     22   ** fossil source code base.  Nothing in this file depends on anything
    23     23   ** else in fossil.
    24     24   */
    25     25   #include <stdio.h>
    26     26   #include <assert.h>
    27     27   #include <stdlib.h>
    28     28   #include <string.h>
................................................................................
   193    193   }
   194    194   
   195    195   /*
   196    196   ** Compute a 32-bit checksum on the N-byte buffer.  Return the result.
   197    197   */
   198    198   static unsigned int checksum(const char *zIn, size_t N){
   199    199     const unsigned char *z = (const unsigned char *)zIn;
   200         -  unsigned sum = 0;
          200  +  unsigned sum0 = 0;
          201  +  unsigned sum1 = 0;
          202  +  unsigned sum2 = 0;
          203  +  unsigned sum3 = 0;
   201    204     while(N >= 16){
   202         -    sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24;
   203         -    sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16;
   204         -    sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8;
   205         -    sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
          205  +    sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
          206  +    sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
          207  +    sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
          208  +    sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
   206    209       z += 16;
   207    210       N -= 16;
   208    211     }
   209    212     while(N >= 4){
   210         -    sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3];
          213  +    sum0 += z[0];
          214  +    sum1 += z[1];
          215  +    sum2 += z[2];
          216  +    sum3 += z[3];
   211    217       z += 4;
   212    218       N -= 4;
   213    219     }
          220  +  sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
   214    221     switch(N){
   215         -    case 3:   sum += (z[2] << 8);
   216         -    case 2:   sum += (z[1] << 16);
   217         -    case 1:   sum += (z[0] << 24);
          222  +    case 3:   sum3 += (z[2] << 8);
          223  +    case 2:   sum3 += (z[1] << 16);
          224  +    case 1:   sum3 += (z[0] << 24);
   218    225       default:  ;
   219    226     }
   220         -  return sum;
          227  +  return sum3;
   221    228   }
   222    229   
   223    230   /*
   224    231   ** Create a new delta.
   225    232   **
   226    233   ** The delta is written into a preallocated buffer, zDelta, which 
   227    234   ** should be at least 60 bytes longer than the target file, zOut.
................................................................................
   315    322       return zDelta - zOrigDelta;
   316    323     }
   317    324   
   318    325     /* Compute the hash table used to locate matching sections in the
   319    326     ** source file.
   320    327     */
   321    328     nHash = lenSrc/NHASH;
   322         -  collide = malloc( nHash*2*sizeof(int) );
   323         -  if( collide==0 ) return -1;
          329  +  collide = fossil_malloc( nHash*2*sizeof(int) );
   324    330     landmark = &collide[nHash];
   325    331     memset(landmark, -1, nHash*sizeof(int));
   326    332     memset(collide, -1, nHash*sizeof(int));
   327    333     for(i=0; i<lenSrc-NHASH; i+=NHASH){
   328    334       int hv;
   329    335       hash_init(&h, &zSrc[i]);
   330    336       hv = hash_32bit(&h) % nHash;
................................................................................
   430    436             DEBUG2( printf("lastRead becomes %d\n", lastRead); )
   431    437           }
   432    438           bestCnt = 0;
   433    439           break;
   434    440         }
   435    441   
   436    442         /* If we reach this point, it means no match is found so far */
   437         -      if( base+i+NHASH>lenOut ){
          443  +      if( base+i+NHASH>=lenOut ){
   438    444           /* We have reached the end of the file and have not found any
   439    445           ** matches.  Do an "insert" for everything that does not match */
   440    446           putInt(lenOut-base, &zDelta);
   441    447           *(zDelta++) = ':';
   442    448           memcpy(zDelta, &zOut[base], lenOut-base);
   443    449           zDelta += lenOut-base;
   444    450           base = lenOut;
................................................................................
   511    517     int lenSrc,            /* Length of the source file */
   512    518     const char *zDelta,    /* Delta to apply to the pattern */
   513    519     int lenDelta,          /* Length of the delta */
   514    520     char *zOut             /* Write the output into this preallocated buffer */
   515    521   ){
   516    522     unsigned int limit;
   517    523     unsigned int total = 0;
          524  +#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
   518    525     char *zOrigOut = zOut;
          526  +#endif
   519    527   
   520    528     limit = getInt(&zDelta, &lenDelta);
   521    529     if( *zDelta!='\n' ){
   522    530       /* ERROR: size integer not terminated by "\n" */
   523    531       return -1;
   524    532     }
   525    533     zDelta++; lenDelta--;
................................................................................
   526    534     while( *zDelta && lenDelta>0 ){
   527    535       unsigned int cnt, ofst;
   528    536       cnt = getInt(&zDelta, &lenDelta);
   529    537       switch( zDelta[0] ){
   530    538         case '@': {
   531    539           zDelta++; lenDelta--;
   532    540           ofst = getInt(&zDelta, &lenDelta);
   533         -        if( zDelta[0]!=',' ){
          541  +        if( lenDelta>0 && zDelta[0]!=',' ){
   534    542             /* ERROR: copy command not terminated by ',' */
   535    543             return -1;
   536    544           }
   537    545           zDelta++; lenDelta--;
   538    546           DEBUG1( printf("COPY %d from %d\n", cnt, ofst); )
   539    547           total += cnt;
   540    548           if( total>limit ){
................................................................................
   566    574           zDelta += cnt;
   567    575           lenDelta -= cnt;
   568    576           break;
   569    577         }
   570    578         case ';': {
   571    579           zDelta++; lenDelta--;
   572    580           zOut[0] = 0;
          581  +#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
   573    582           if( cnt!=checksum(zOrigOut, total) ){
   574    583             /* ERROR:  bad checksum */
   575    584             return -1;
   576    585           }
          586  +#endif
   577    587           if( total!=limit ){
   578    588             /* ERROR: generated size does not match predicted size */
   579    589             return -1;
   580    590           }
   581    591           return total;
   582    592         }
   583    593         default: {

Changes to src/deltacmd.c.

    47     47   **
    48     48   ** Given two input files, create and output a delta that carries
    49     49   ** the first file into the second.
    50     50   */
    51     51   void delta_create_cmd(void){
    52     52     Blob orig, target, delta;
    53     53     if( g.argc!=5 ){
    54         -    fprintf(stderr,"Usage: %s %s ORIGIN TARGET DELTA\n", g.argv[0], g.argv[1]);
    55         -    exit(1);
           54  +    usage("ORIGIN TARGET DELTA");
    56     55     }
    57     56     if( blob_read_from_file(&orig, g.argv[2])<0 ){
    58         -    fprintf(stderr,"cannot read %s\n", g.argv[2]);
    59         -    exit(1);
           57  +    fossil_fatal("cannot read %s\n", g.argv[2]);
    60     58     }
    61     59     if( blob_read_from_file(&target, g.argv[3])<0 ){
    62         -    fprintf(stderr,"cannot read %s\n", g.argv[3]);
    63         -    exit(1);
           60  +    fossil_fatal("cannot read %s\n", g.argv[3]);
    64     61     }
    65     62     blob_delta_create(&orig, &target, &delta);
    66     63     if( blob_write_to_file(&delta, g.argv[4])<blob_size(&delta) ){
    67         -    fprintf(stderr,"cannot write %s\n", g.argv[4]);
    68         -    exit(1);
           64  +    fossil_fatal("cannot write %s\n", g.argv[4]);
    69     65     }
    70     66     blob_reset(&orig);
    71     67     blob_reset(&target);
    72     68     blob_reset(&delta);
    73     69   }
    74     70   
    75     71   /*
................................................................................
    83     79   */
    84     80   int blob_delta_apply(Blob *pOriginal, Blob *pDelta, Blob *pTarget){
    85     81     int len, n;
    86     82     Blob out;
    87     83   
    88     84     n = delta_output_size(blob_buffer(pDelta), blob_size(pDelta));
    89     85     blob_zero(&out);
           86  +  if( n<0 ) return -1;
    90     87     blob_resize(&out, n);
    91     88     len = delta_apply(
    92     89        blob_buffer(pOriginal), blob_size(pOriginal),
    93     90        blob_buffer(pDelta), blob_size(pDelta),
    94     91        blob_buffer(&out));
    95     92     if( len<0 ){
    96     93       blob_reset(&out);
................................................................................
   109    106   **
   110    107   ** Given an input files and a delta, apply the delta to the input file
   111    108   ** and write the result.
   112    109   */
   113    110   void delta_apply_cmd(void){
   114    111     Blob orig, target, delta;
   115    112     if( g.argc!=5 ){
   116         -    fprintf(stderr,"Usage: %s %s ORIGIN DELTA TARGET\n", g.argv[0], g.argv[1]);
   117         -    exit(1);
          113  +    usage("ORIGIN DELTA TARGET");
   118    114     }
   119    115     if( blob_read_from_file(&orig, g.argv[2])<0 ){
   120         -    fprintf(stderr,"cannot read %s\n", g.argv[2]);
   121         -    exit(1);
          116  +    fossil_fatal("cannot read %s\n", g.argv[2]);
   122    117     }
   123    118     if( blob_read_from_file(&delta, g.argv[3])<0 ){
   124         -    fprintf(stderr,"cannot read %s\n", g.argv[3]);
   125         -    exit(1);
          119  +    fossil_fatal("cannot read %s\n", g.argv[3]);
   126    120     }
   127    121     blob_delta_apply(&orig, &delta, &target);
   128    122     if( blob_write_to_file(&target, g.argv[4])<blob_size(&target) ){
   129         -    fprintf(stderr,"cannot write %s\n", g.argv[4]);
   130         -    exit(1);
          123  +    fossil_fatal("cannot write %s\n", g.argv[4]);
   131    124     }
   132    125     blob_reset(&orig);
   133    126     blob_reset(&target);
   134    127     blob_reset(&delta);
   135    128   }
   136    129   
   137    130   /*
................................................................................
   151    144     blob_delta_create(&f1, &f2, &d12);
   152    145     blob_delta_create(&f2, &f1, &d21);
   153    146     blob_delta_apply(&f1, &d12, &a2);
   154    147     blob_delta_apply(&f2, &d21, &a1);
   155    148     if( blob_compare(&f1,&a1) || blob_compare(&f2, &a2) ){
   156    149       fossil_panic("delta test failed");
   157    150     }
   158         -  printf("ok\n");
          151  +  fossil_print("ok\n");
   159    152   }

Changes to src/descendants.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** This file contains code used to find decendants of a version
           18  +** This file contains code used to find descendants of a version
    19     19   ** or leaves of a version tree.
    20     20   */
    21     21   #include "config.h"
    22     22   #include "descendants.h"
    23     23   #include <assert.h>
    24     24   
    25     25   
    26     26   /*
    27     27   ** Create a temporary table named "leaves" if it does not
    28     28   ** already exist.  Load this table with the RID of all
    29         -** check-ins that are leaves which are decended from
    30         -** check-in iBase.  If iBase==0, find all leaves within the
    31         -** entire check-in hierarchy.
           29  +** check-ins that are leaves which are descended from
           30  +** check-in iBase.
    32     31   **
    33     32   ** A "leaf" is a check-in that has no children in the same branch.
           33  +** There is a separate permanent table LEAF that contains all leaves
           34  +** in the tree.  This routine is used to compute a subset of that
           35  +** table consisting of leaves that are descended from a single checkin.
    34     36   **
    35     37   ** The closeMode flag determines behavior associated with the "closed"
    36     38   ** tag:
    37     39   **
    38     40   **    closeMode==0       Show all leaves regardless of the "closed" tag.
    39     41   **
    40     42   **    closeMode==1       Show only leaves without the "closed" tag.
................................................................................
    53     55     db_multi_exec(
    54     56       "CREATE TEMP TABLE IF NOT EXISTS leaves("
    55     57       "  rid INTEGER PRIMARY KEY"
    56     58       ");"
    57     59       "DELETE FROM leaves;"
    58     60     );
    59     61   
    60         -  /* We are checking all descendants of iBase.  If iBase==0, then
    61         -  ** use a short-cut to find all leaves anywhere in the hierarchy.
    62         -  */
    63         -  if( iBase<=0 ){
    64         -    db_multi_exec(
    65         -      "INSERT OR IGNORE INTO leaves"
    66         -      "  SELECT cid FROM plink"
    67         -      "  EXCEPT"
    68         -      "  SELECT pid FROM plink"
    69         -      "   WHERE coalesce((SELECT value FROM tagxref"
    70         -                         " WHERE tagid=%d AND rid=plink.pid),'trunk')"
    71         -           " == coalesce((SELECT value FROM tagxref"
    72         -                         " WHERE tagid=%d AND rid=plink.cid),'trunk');",
    73         -      TAG_BRANCH, TAG_BRANCH
    74         -    );
    75         -  }else{
           62  +  if( iBase>0 ){
    76     63       Bag seen;     /* Descendants seen */
    77     64       Bag pending;  /* Unpropagated descendants */
    78     65       Stmt q1;      /* Query to find children of a check-in */
    79     66       Stmt isBr;    /* Query to check to see if a check-in starts a new branch */
    80     67       Stmt ins;     /* INSERT statement for a new record */
    81     68   
    82     69       /* Initialize the bags. */
    83     70       bag_init(&seen);
    84     71       bag_init(&pending);
    85     72       bag_insert(&pending, iBase);
    86     73   
    87     74       /* This query returns all non-branch-merge children of check-in :rid.
    88     75       **
    89         -    ** If a a child is a merge of a fork within the same branch, it is 
           76  +    ** If a child is a merge of a fork within the same branch, it is 
    90     77       ** returned.  Only merge children in different branches are excluded.
    91     78       */
    92     79       db_prepare(&q1,
    93     80         "SELECT cid FROM plink"
    94     81         " WHERE pid=:rid"
    95     82         "   AND (isprim"
    96     83         "        OR coalesce((SELECT value FROM tagxref"
................................................................................
   167    154     }
   168    155   }
   169    156   
   170    157   /*
   171    158   ** Load the record ID rid and up to N-1 closest ancestors into
   172    159   ** the "ok" table.
   173    160   */
   174         -void compute_ancestors(int rid, int N){
          161  +void compute_ancestors(int rid, int N, int directOnly){
   175    162     Bag seen;
   176    163     PQueue queue;
          164  +  Stmt ins;
          165  +  Stmt q;
   177    166     bag_init(&seen);
   178         -  pqueue_init(&queue);
          167  +  pqueuex_init(&queue);
   179    168     bag_insert(&seen, rid);
   180         -  pqueue_insert(&queue, rid, 0.0);
   181         -  while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
   182         -    Stmt q;
   183         -    db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
   184         -    db_prepare(&q,
   185         -       "SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid"
   186         -       " WHERE a.cid=%d", rid
   187         -    );
          169  +  pqueuex_insert(&queue, rid, 0.0, 0);
          170  +  db_prepare(&ins, "INSERT OR IGNORE INTO ok VALUES(:rid)");
          171  +  db_prepare(&q,
          172  +    "SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid"
          173  +    " WHERE a.cid=:rid %s",
          174  +    directOnly ? " AND a.isprim" : ""
          175  +  );
          176  +  while( (N--)>0 && (rid = pqueuex_extract(&queue, 0))!=0 ){
          177  +    db_bind_int(&ins, ":rid", rid);
          178  +    db_step(&ins);
          179  +    db_reset(&ins);
          180  +    db_bind_int(&q, ":rid", rid);
   188    181       while( db_step(&q)==SQLITE_ROW ){
   189    182         int pid = db_column_int(&q, 0);
   190    183         double mtime = db_column_double(&q, 1);
   191    184         if( bag_insert(&seen, pid) ){
   192         -        pqueue_insert(&queue, pid, -mtime);
          185  +        pqueuex_insert(&queue, pid, -mtime, 0);
   193    186         }
   194    187       }
   195         -    db_finalize(&q);
          188  +    db_reset(&q);
   196    189     }
   197    190     bag_clear(&seen);
   198         -  pqueue_clear(&queue);
          191  +  pqueuex_clear(&queue);
          192  +  db_finalize(&ins);
          193  +  db_finalize(&q);
          194  +}
          195  +
          196  +/*
          197  +** Compute up to N direct ancestors (merge ancestors do not count)
          198  +** for the check-in rid and put them in a table named "ancestor".
          199  +** Label each generation with consecutive integers going backwards
          200  +** in time such that rid has the smallest generation number and the oldest
          201  +** direct ancestor as the largest generation number.
          202  +*/
          203  +void compute_direct_ancestors(int rid, int N){
          204  +  Stmt ins;
          205  +  Stmt q;
          206  +  int gen = 0;
          207  +  db_multi_exec(
          208  +    "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER,"
          209  +                                            " generation INTEGER PRIMARY KEY);"
          210  +    "DELETE FROM ancestor;"
          211  +    "INSERT INTO ancestor VALUES(%d, 0);", rid
          212  +  );
          213  +  db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)");
          214  +  db_prepare(&q, 
          215  +    "SELECT pid FROM plink"
          216  +    " WHERE cid=:rid AND isprim"
          217  +  );
          218  +  while( (N--)>0 ){
          219  +    db_bind_int(&q, ":rid", rid);
          220  +    if( db_step(&q)!=SQLITE_ROW ) break;
          221  +    rid = db_column_int(&q, 0);
          222  +    db_reset(&q);
          223  +    gen++;
          224  +    db_bind_int(&ins, ":rid", rid);
          225  +    db_bind_int(&ins, ":gen", gen);
          226  +    db_step(&ins);
          227  +    db_reset(&ins);
          228  +  }
          229  +  db_finalize(&ins);
          230  +  db_finalize(&q);
          231  +}
          232  +
          233  +/*
          234  +** Compute the "mtime" of the file given whose blob.rid is "fid" that
          235  +** is part of check-in "vid".  The mtime will be the mtime on vid or
          236  +** some ancestor of vid where fid first appears.
          237  +*/
          238  +int mtime_of_manifest_file(
          239  +  int vid,       /* The check-in that contains fid */
          240  +  int fid,       /* The id of the file whose check-in time is sought */
          241  +  i64 *pMTime    /* Write result here */
          242  +){
          243  +  static int prevVid = -1;
          244  +  static Stmt q;
          245  +
          246  +  if( prevVid!=vid ){
          247  +    prevVid = vid;
          248  +    db_multi_exec("DROP TABLE IF EXISTS temp.ok;"
          249  +                  "CREATE TEMP TABLE ok(x INTEGER PRIMARY KEY);");
          250  +    compute_ancestors(vid, 100000000, 1);
          251  +  }
          252  +  db_static_prepare(&q,
          253  +    "SELECT (max(event.mtime)-2440587.5)*86400 FROM mlink, event"
          254  +    " WHERE mlink.mid=event.objid"
          255  +    "   AND +mlink.mid IN ok"
          256  +    "   AND mlink.fid=:fid");
          257  +  db_bind_int(&q, ":fid", fid);
          258  +  if( db_step(&q)!=SQLITE_ROW ){
          259  +    db_reset(&q);
          260  +    return 1;
          261  +  }
          262  +  *pMTime = db_column_int64(&q, 0);
          263  +  db_reset(&q);
          264  +  return 0;
   199    265   }
   200    266   
   201    267   /*
   202    268   ** Load the record ID rid and up to N-1 closest descendants into
   203    269   ** the "ok" table.
   204    270   */
   205    271   void compute_descendants(int rid, int N){
   206    272     Bag seen;
   207    273     PQueue queue;
          274  +  Stmt ins;
          275  +  Stmt q;
          276  +
   208    277     bag_init(&seen);
   209         -  pqueue_init(&queue);
          278  +  pqueuex_init(&queue);
   210    279     bag_insert(&seen, rid);
   211         -  pqueue_insert(&queue, rid, 0.0);
   212         -  while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
   213         -    Stmt q;
   214         -    db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
   215         -    db_prepare(&q,"SELECT cid, mtime FROM plink WHERE pid=%d", rid);
          280  +  pqueuex_insert(&queue, rid, 0.0, 0);
          281  +  db_prepare(&ins, "INSERT OR IGNORE INTO ok VALUES(:rid)");
          282  +  db_prepare(&q, "SELECT cid, mtime FROM plink WHERE pid=:rid");
          283  +  while( (N--)>0 && (rid = pqueuex_extract(&queue, 0))!=0 ){
          284  +    db_bind_int(&ins, ":rid", rid);
          285  +    db_step(&ins);
          286  +    db_reset(&ins);
          287  +    db_bind_int(&q, ":rid", rid);
   216    288       while( db_step(&q)==SQLITE_ROW ){
   217    289         int pid = db_column_int(&q, 0);
   218    290         double mtime = db_column_double(&q, 1);
   219    291         if( bag_insert(&seen, pid) ){
   220         -        pqueue_insert(&queue, pid, mtime);
          292  +        pqueuex_insert(&queue, pid, mtime, 0);
   221    293         }
   222    294       }
   223         -    db_finalize(&q);
          295  +    db_reset(&q);
   224    296     }
   225    297     bag_clear(&seen);
   226         -  pqueue_clear(&queue);
          298  +  pqueuex_clear(&queue);
          299  +  db_finalize(&ins);
          300  +  db_finalize(&q);
   227    301   }
   228    302   
   229    303   /*
   230         -** COMMAND:  descendants
          304  +** COMMAND: descendants*
   231    305   **
   232         -** Usage: %fossil descendants ?BASELINE-ID?
          306  +** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
   233    307   **
   234    308   ** Find all leaf descendants of the baseline specified or if the argument
   235    309   ** is omitted, of the baseline currently checked out.
          310  +**
          311  +** Options:
          312  +**    -R|--repository FILE       Extract info from repository FILE
          313  +**
          314  +** See also: finfo, info, leaves
   236    315   */
   237    316   void descendants_cmd(void){
   238    317     Stmt q;
   239    318     int base;
   240    319   
   241         -  db_must_be_within_tree();
          320  +  db_find_and_open_repository(0,0);
   242    321     if( g.argc==2 ){
   243    322       base = db_lget_int("checkout", 0);
   244    323     }else{
   245         -    base = name_to_rid(g.argv[2]);
          324  +    base = name_to_typed_rid(g.argv[2], "ci");
   246    325     }
   247    326     if( base==0 ) return;
   248    327     compute_leaves(base, 0);
   249    328     db_prepare(&q,
   250    329       "%s"
   251    330       "   AND event.objid IN (SELECT rid FROM leaves)"
   252    331       " ORDER BY event.mtime DESC",
   253    332       timeline_query_for_tty()
   254    333     );
   255         -  print_timeline(&q, 20);
          334  +  print_timeline(&q, 20, 0);
   256    335     db_finalize(&q);
   257    336   }
   258    337   
   259    338   /*
   260         -** COMMAND:  leaves
          339  +** COMMAND: leaves*
   261    340   **
   262         -** Usage: %fossil leaves ?--all? ?--closed?
          341  +** Usage: %fossil leaves ?OPTIONS?
   263    342   **
   264    343   ** Find leaves of all branches.  By default show only open leaves.
   265    344   ** The --all flag causes all leaves (closed and open) to be shown.
   266    345   ** The --closed flag shows only closed leaves.
          346  +**
          347  +** The --recompute flag causes the content of the "leaf" table in the
          348  +** repository database to be recomputed.
          349  +**
          350  +** Options:
          351  +**   --all        show ALL leaves
          352  +**   --closed     show only closed leaves
          353  +**   --bybranch   order output by branch name
          354  +**   --recompute  recompute the "leaf" table in the repository DB
          355  +**
          356  +** See also: descendants, finfo, info, branch
   267    357   */
   268    358   void leaves_cmd(void){
   269    359     Stmt q;
          360  +  Blob sql;
   270    361     int showAll = find_option("all", 0, 0)!=0;
   271    362     int showClosed = find_option("closed", 0, 0)!=0;
          363  +  int recomputeFlag = find_option("recompute",0,0)!=0;
          364  +  int byBranch = find_option("bybranch",0,0)!=0;
          365  +  char *zLastBr = 0;
          366  +  int n;
          367  +  char zLineNo[10];
   272    368   
   273         -  db_must_be_within_tree();
   274         -  compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
   275         -  db_prepare(&q,
   276         -    "%s"
   277         -    "   AND blob.rid IN leaves"
   278         -    " ORDER BY event.mtime DESC",
   279         -    timeline_query_for_tty()
   280         -  );
   281         -  print_timeline(&q, 2000);
   282         -  db_finalize(&q);
   283         -}
          369  +  db_find_and_open_repository(0,0);
          370  +  if( recomputeFlag ) leaf_rebuild();
          371  +  blob_zero(&sql);
          372  +  blob_append(&sql, timeline_query_for_tty(), -1);
          373  +  blob_appendf(&sql, " AND blob.rid IN leaf");
          374  +  if( showClosed ){
          375  +    blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
          376  +  }else if( !showAll ){
          377  +    blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
          378  +  }
          379  +  if( byBranch ){
          380  +    db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
          381  +                   " event.mtime DESC",
          382  +                   blob_str(&sql));
          383  +  }else{
          384  +    db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
          385  +  }
          386  +  blob_reset(&sql);
          387  +  n = 0;
          388  +  while( db_step(&q)==SQLITE_ROW ){
          389  +    const char *zId = db_column_text(&q, 1);
          390  +    const char *zDate = db_column_text(&q, 2);
          391  +    const char *zCom = db_column_text(&q, 3);
          392  +    const char *zBr = db_column_text(&q, 7);
          393  +    char *z;
   284    394   
   285         -/*
   286         -** This routine is called while for each check-in that is rendered by
   287         -** the "leaves" page.  Add some additional hyperlink to show the 
   288         -** ancestors of the leaf.
   289         -*/
   290         -static void leaves_extra(int rid){
   291         -  if( g.okHistory ){
   292         -    @ <a href="%s(g.zBaseURL)/timeline?p=%d(rid)">[timeline]</a>
          395  +    if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
          396  +      fossil_print("*** %s ***\n", zBr);
          397  +      fossil_free(zLastBr);
          398  +      zLastBr = fossil_strdup(zBr);
          399  +    }
          400  +    n++;
          401  +    sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
          402  +    fossil_print("%6s ", zLineNo);
          403  +    z = mprintf("%s [%.10s] %s", zDate, zId, zCom);
          404  +    comment_print(z, 7, 79);
          405  +    fossil_free(z);
   293    406     }
          407  +  fossil_free(zLastBr);
          408  +  db_finalize(&q);
   294    409   }
   295    410   
   296    411   /*
   297    412   ** WEBPAGE:  leaves
   298    413   **
   299    414   ** Find leaves of all branches.
   300    415   */
   301    416   void leaves_page(void){
          417  +  Blob sql;
   302    418     Stmt q;
   303    419     int showAll = P("all")!=0;
   304    420     int showClosed = P("closed")!=0;
   305    421   
   306    422     login_check_credentials();
   307         -  if( !g.okRead ){ login_needed(); return; }
          423  +  if( !g.perm.Read ){ login_needed(); return; }
   308    424   
   309    425     if( !showAll ){
   310    426       style_submenu_element("All", "All", "leaves?all");
   311    427     }
   312    428     if( !showClosed ){
   313    429       style_submenu_element("Closed", "Closed", "leaves?closed");
   314    430     }
   315    431     if( showClosed || showAll ){
   316    432       style_submenu_element("Open", "Open", "leaves");
   317    433     }
   318    434     style_header("Leaves");
   319    435     login_anonymous_available();
   320         -  compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
   321    436     style_sidebox_begin("Nomenclature:", "33%");
   322    437     @ <ol>
   323         -  @ <li> A <b>leaf</b> is a check-in with no descendants.</li>
   324         -  @ <li> An <b>open leaf</b> is a leaf that does not have a "closed" tag
          438  +  @ <li> A <div class="sideboxDescribed">leaf</div>
          439  +  @ is a check-in with no descendants in the same branch.</li>
          440  +  @ <li> An <div class="sideboxDescribed">open leaf</div>
          441  +  @ is a leaf that does not have a "closed" tag
   325    442     @ and is thus assumed to still be in use.</li>
   326         -  @ <li> A <b>closed leaf</b> has a "closed" tag and is thus assumed to
          443  +  @ <li> A <div class="sideboxDescribed">closed leaf</div>
          444  +  @ has a "closed" tag and is thus assumed to
   327    445     @ be historical and no longer in active use.</li>
   328    446     @ </ol>
   329    447     style_sidebox_end();
   330    448   
   331    449     if( showAll ){
   332    450       @ <h1>All leaves, both open and closed:</h1>
   333    451     }else if( showClosed ){
   334    452       @ <h1>Closed leaves:</h1>
   335    453     }else{
   336    454       @ <h1>Open leaves:</h1>
   337    455     }
   338         -  db_prepare(&q,
   339         -    "%s"
   340         -    "   AND blob.rid IN leaves"
   341         -    " ORDER BY event.mtime DESC",
   342         -    timeline_query_for_www()
   343         -  );
   344         -  www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
          456  +  blob_zero(&sql);
          457  +  blob_append(&sql, timeline_query_for_www(), -1);
          458  +  blob_appendf(&sql, " AND blob.rid IN leaf");
          459  +  if( showClosed ){
          460  +    blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
          461  +  }else if( !showAll ){
          462  +    blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
          463  +  }
          464  +  db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
          465  +  blob_reset(&sql);
          466  +  www_print_timeline(&q, TIMELINE_LEAFONLY, 0, 0, 0);
   345    467     db_finalize(&q);
   346         -  @ <br clear="both">
   347         -  @ <script>
          468  +  @ <br />
          469  +  @ <script  type="text/JavaScript">
   348    470     @ function xin(id){
   349    471     @ }
   350    472     @ function xout(id){
   351    473     @ }
   352    474     @ </script>
   353    475     style_footer();
   354    476   }
          477  +
          478  +#if INTERFACE
          479  +/* Flag parameters to compute_uses_file() */
          480  +#define USESFILE_DELETE   0x01  /* Include the check-ins where file deleted */
          481  +
          482  +#endif
          483  +
          484  +
          485  +/*
          486  +** Add to table zTab the record ID (rid) of every check-in that contains
          487  +** the file fid.
          488  +*/
          489  +void compute_uses_file(const char *zTab, int fid, int usesFlags){
          490  +  Bag seen;
          491  +  Bag pending;
          492  +  Stmt ins;
          493  +  Stmt q;
          494  +  int rid;
          495  +
          496  +  bag_init(&seen);
          497  +  bag_init(&pending);
          498  +  db_prepare(&ins, "INSERT OR IGNORE INTO \"%s\" VALUES(:rid)", zTab);
          499  +  db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
          500  +  while( db_step(&q)==SQLITE_ROW ){
          501  +    int mid = db_column_int(&q, 0);
          502  +    bag_insert(&pending, mid);
          503  +    bag_insert(&seen, mid);
          504  +    db_bind_int(&ins, ":rid", mid);
          505  +    db_step(&ins);
          506  +    db_reset(&ins);
          507  +  }
          508  +  db_finalize(&q);
          509  +  
          510  +  db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
          511  +  while( db_step(&q)==SQLITE_ROW ){
          512  +    int mid = db_column_int(&q, 0);
          513  +    bag_insert(&seen, mid);
          514  +    if( usesFlags & USESFILE_DELETE ){
          515  +      db_bind_int(&ins, ":rid", mid);
          516  +      db_step(&ins);
          517  +      db_reset(&ins);
          518  +    }
          519  +  }
          520  +  db_finalize(&q);
          521  +  db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid");
          522  +
          523  +  while( (rid = bag_first(&pending))!=0 ){
          524  +    bag_remove(&pending, rid);
          525  +    db_bind_int(&q, ":rid", rid);
          526  +    while( db_step(&q)==SQLITE_ROW ){
          527  +      int mid = db_column_int(&q, 0);
          528  +      if( bag_find(&seen, mid) ) continue;
          529  +      bag_insert(&seen, mid);
          530  +      bag_insert(&pending, mid);
          531  +      db_bind_int(&ins, ":rid", mid);
          532  +      db_step(&ins);
          533  +      db_reset(&ins);
          534  +    }
          535  +    db_reset(&q);
          536  +  }
          537  +  db_finalize(&q);
          538  +  db_finalize(&ins);
          539  +  bag_clear(&seen);
          540  +  bag_clear(&pending);
          541  +}

Changes to src/diff.c.

    19     19   ** text files.
    20     20   */
    21     21   #include "config.h"
    22     22   #include "diff.h"
    23     23   #include <assert.h>
    24     24   
    25     25   
           26  +#if INTERFACE
    26     27   /*
    27         -** Maximum length of a line in a text file.  (8192)
           28  +** Flag parameters to the text_diff() routine used to control the formatting
           29  +** of the diff output.
           30  +*/
           31  +#define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
           32  +#define DIFF_WIDTH_MASK   ((u64)0x00ff0000) /* side-by-side column width */
           33  +#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
           34  +#define DIFF_SIDEBYSIDE   ((u64)0x02000000) /* Generate a side-by-side diff */
           35  +#define DIFF_NEWFILE      ((u64)0x04000000) /* Missing shown as empty files */
           36  +#define DIFF_BRIEF        ((u64)0x08000000) /* Show filenames only */
           37  +#define DIFF_INLINE       ((u64)0x00000000) /* Inline (not side-by-side) diff */
           38  +#define DIFF_HTML         ((u64)0x10000000) /* Render for HTML */
           39  +#define DIFF_LINENO       ((u64)0x20000000) /* Show line numbers */
           40  +#define DIFF_WS_WARNING   ((u64)0x40000000) /* Warn about whitespace */
           41  +#define DIFF_NOOPT        (((u64)0x01)<<32) /* Suppress optimizations (debug) */
           42  +#define DIFF_INVERT       (((u64)0x02)<<32) /* Invert the diff (debug) */
           43  +#define DIFF_CONTEXT_EX   (((u64)0x04)<<32) /* Use context even if zero */
           44  +
           45  +/*
           46  +** These error messages are shared in multiple locations.  They are defined
           47  +** here for consistency.
           48  +*/
           49  +#define DIFF_CANNOT_COMPUTE_BINARY \
           50  +    "cannot compute difference between binary files\n"
           51  +
           52  +#define DIFF_CANNOT_COMPUTE_SYMLINK \
           53  +    "cannot compute difference between symlink and regular file\n"
           54  +
           55  +#define looks_like_binary(blob) (looks_like_utf8((blob)) == 0)
           56  +#endif /* INTERFACE */
           57  +
           58  +/*
           59  +** Maximum length of a line in a text file, in bytes.  (2**13 = 8192 bytes)
    28     60   */
    29     61   #define LENGTH_MASK_SZ  13
    30     62   #define LENGTH_MASK     ((1<<LENGTH_MASK_SZ)-1)
    31     63   
    32     64   /*
    33     65   ** Information about each line of a file being diffed.
    34     66   **
................................................................................
    38     70   */
    39     71   typedef struct DLine DLine;
    40     72   struct DLine {
    41     73     const char *z;        /* The text of the line */
    42     74     unsigned int h;       /* Hash of the line */
    43     75     unsigned int iNext;   /* 1+(Index of next line with same the same hash) */
    44     76   
    45         -  /* an array of DLine elements services two purposes.  The fields
           77  +  /* an array of DLine elements serves two purposes.  The fields
    46     78     ** above are one per line of input text.  But each entry is also
    47     79     ** a bucket in a hash table, as follows: */
    48     80     unsigned int iHash;   /* 1+(first entry in the hash chain) */
    49     81   };
    50     82   
    51     83   /*
    52         -** A context for running a diff.
           84  +** Length of a dline
           85  +*/
           86  +#define LENGTH(X)   ((X)->h & LENGTH_MASK)
           87  +
           88  +/*
           89  +** A context for running a raw diff.
           90  +**
           91  +** The aEdit[] array describes the raw diff.  Each triple of integers in
           92  +** aEdit[] means:
           93  +**
           94  +**   (1) COPY:   Number of lines aFrom and aTo have in common
           95  +**   (2) DELETE: Number of lines found only in aFrom
           96  +**   (3) INSERT: Number of lines found only in aTo
           97  +**
           98  +** The triples repeat until all lines of both aFrom and aTo are accounted
           99  +** for.
    53    100   */
    54    101   typedef struct DContext DContext;
    55    102   struct DContext {
    56    103     int *aEdit;        /* Array of copy/delete/insert triples */
    57    104     int nEdit;         /* Number of integers (3x num of triples) in aEdit[] */
    58    105     int nEditAlloc;    /* Space allocated for aEdit[] */
    59    106     DLine *aFrom;      /* File on left side of the diff */
................................................................................
    60    107     int nFrom;         /* Number of lines in aFrom[] */
    61    108     DLine *aTo;        /* File on right side of the diff */
    62    109     int nTo;           /* Number of lines in aTo[] */
    63    110   };
    64    111   
    65    112   /*
    66    113   ** Return an array of DLine objects containing a pointer to the
    67         -** start of each line and a hash of that line.  The lower 
          114  +** start of each line and a hash of that line.  The lower
    68    115   ** bits of the hash store the length of each line.
    69    116   **
    70         -** Trailing whitespace is removed from each line.
          117  +** Trailing whitespace is removed from each line.  2010-08-20:  Not any
          118  +** more.  If trailing whitespace is ignored, the "patch" command gets
          119  +** confused by the diff output.  Ticket [a9f7b23c2e376af5b0e5b]
    71    120   **
    72    121   ** Return 0 if the file is binary or contains a line that is
    73    122   ** too long.
          123  +**
          124  +** Profiling show that in most cases this routine consumes the bulk of
          125  +** the CPU time on a diff.
    74    126   */
    75         -static DLine *break_into_lines(const char *z, int n, int *pnLine){
          127  +static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
    76    128     int nLine, i, j, k, x;
    77    129     unsigned int h, h2;
    78    130     DLine *a;
    79    131   
    80    132     /* Count the number of lines.  Allocate space to hold
    81    133     ** the returned array.
    82    134     */
................................................................................
    92    144         }
    93    145         j = 0;
    94    146       }
    95    147     }
    96    148     if( j>LENGTH_MASK ){
    97    149       return 0;
    98    150     }
    99         -  a = malloc( nLine*sizeof(a[0]) );
   100         -  if( a==0 ) fossil_panic("out of memory");
          151  +  a = fossil_malloc( nLine*sizeof(a[0]) );
   101    152     memset(a, 0, nLine*sizeof(a[0]) );
          153  +  if( n==0 ){
          154  +    *pnLine = 0;
          155  +    return a;
          156  +  }
   102    157   
   103    158     /* Fill in the array */
   104    159     for(i=0; i<nLine; i++){
   105    160       a[i].z = z;
   106    161       for(j=0; z[j] && z[j]!='\n'; j++){}
   107         -    for(k=j; k>0 && isspace(z[k-1]); k--){}
   108         -    for(h=0, x=0; x<k; x++){
          162  +    k = j;
          163  +    while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
          164  +    for(h=0, x=0; x<=k; x++){
   109    165         h = h ^ (h<<2) ^ z[x];
   110    166       }
   111    167       a[i].h = h = (h<<LENGTH_MASK_SZ) | k;;
   112    168       h2 = h % nLine;
   113    169       a[i].iNext = a[h2].iHash;
   114    170       a[h2].iHash = i+1;
   115    171       z += j+1;
   116    172     }
   117    173   
   118    174     /* Return results */
   119    175     *pnLine = nLine;
   120    176     return a;
   121    177   }
          178  +
          179  +/*
          180  +** This function attempts to scan each logical line within the blob to
          181  +** determine the type of content it appears to contain.  Possible return
          182  +** values are:
          183  +**
          184  +**  (1) -- The content appears to consist entirely of text, with lines
          185  +**         delimited by line-feed characters; however, the encoding may
          186  +**         not be UTF-8.
          187  +**
          188  +**  (0) -- The content appears to be binary because it contains embedded
          189  +**         NUL characters or an extremely long line.  Since this function
          190  +**         does not understand UTF-16, it may falsely consider UTF-16 text
          191  +**         to be binary.
          192  +**
          193  +** (-1) -- The content appears to consist entirely of text, with lines
          194  +**         delimited by carriage-return, line-feed pairs; however, the
          195  +**         encoding may not be UTF-8.
          196  +**
          197  +************************************ WARNING **********************************
          198  +**
          199  +** This function does not validate that the blob content is properly formed
          200  +** UTF-8.  It assumes that all code points are the same size.  It does not
          201  +** validate any code points.  It makes no attempt to detect if any [invalid]
          202  +** switches between UTF-8 and other encodings occur.
          203  +**
          204  +** The only code points that this function cares about are the NUL character,
          205  +** carriage-return, and line-feed.
          206  +**
          207  +************************************ WARNING **********************************
          208  +*/
          209  +int looks_like_utf8(const Blob *pContent){
          210  +  const char *z = blob_buffer(pContent);
          211  +  unsigned int n = blob_size(pContent);
          212  +  int j, c;
          213  +  int result = 1;  /* Assume UTF-8 text with no CR/NL */
          214  +
          215  +  /* Check individual lines.
          216  +  */
          217  +  if( n==0 ) return result;  /* Empty file -> text */
          218  +  c = *z;
          219  +  if( c==0 ) return 0;  /* Zero byte in a file -> binary */
          220  +  j = (c!='\n');
          221  +  while( --n>0 ){
          222  +    c = *++z; ++j;
          223  +    if( c==0 ) return 0;  /* Zero byte in a file -> binary */
          224  +    if( c=='\n' ){
          225  +      int c2 = z[-1];
          226  +      if( c2=='\r' ){
          227  +        result = -1;  /* Contains CR/NL, continue */
          228  +      }
          229  +      if( j>LENGTH_MASK ){
          230  +        return 0;  /* Very long line -> binary */
          231  +      }
          232  +      j = 0;
          233  +    }
          234  +  }
          235  +  if( j>LENGTH_MASK ){
          236  +    return 0;  /* Very long line -> binary */
          237  +  }
          238  +  return result;  /* No problems seen -> not binary */
          239  +}
          240  +
          241  +/*
          242  +** Define the type needed to represent a Unicode (UTF-16) character.
          243  +*/
          244  +#ifndef WCHAR_T
          245  +#  ifdef _WIN32
          246  +#    define WCHAR_T wchar_t
          247  +#  else
          248  +#    define WCHAR_T unsigned short
          249  +#  endif
          250  +#endif
          251  +
          252  +/*
          253  +** Maximum length of a line in a text file, in UTF-16 characters.  (4096)
          254  +** The number of bytes represented by this value cannot exceed LENGTH_MASK
          255  +** bytes, because that is the line buffer size used by the diff engine.
          256  +*/
          257  +#define UTF16_LENGTH_MASK_SZ  (LENGTH_MASK_SZ-(sizeof(WCHAR_T)-sizeof(char)))
          258  +#define UTF16_LENGTH_MASK     ((1<<UTF16_LENGTH_MASK_SZ)-1)
          259  +
          260  +/*
          261  +** The carriage-return / line-feed characters in the UTF-16be and UTF-16le
          262  +** encodings.
          263  +*/
          264  +#define UTF16BE_CR  ((WCHAR_T)'\r')
          265  +#define UTF16BE_LF  ((WCHAR_T)'\n')
          266  +#define UTF16LE_CR  (((WCHAR_T)'\r')<<(sizeof(char)<<3))
          267  +#define UTF16LE_LF  (((WCHAR_T)'\n')<<(sizeof(char)<<3))
          268  +
          269  +/*
          270  +** This function attempts to scan each logical line within the blob to
          271  +** determine the type of content it appears to contain.  Possible return
          272  +** values are:
          273  +**
          274  +**  (1) -- The content appears to consist entirely of text, with lines
          275  +**         delimited by line-feed characters; however, the encoding may
          276  +**         not be UTF-16.
          277  +**
          278  +**  (0) -- The content appears to be binary because it contains embedded
          279  +**         NUL characters or an extremely long line.  Since this function
          280  +**         does not understand UTF-8, it may falsely consider UTF-8 text
          281  +**         to be binary.
          282  +**
          283  +** (-1) -- The content appears to consist entirely of text, with lines
          284  +**         delimited by carriage-return, line-feed pairs; however, the
          285  +**         encoding may not be UTF-16.
          286  +**
          287  +************************************ WARNING **********************************
          288  +**
          289  +** This function does not validate that the blob content is properly formed
          290  +** UTF-16.  It assumes that all code points are the same size.  It does not
          291  +** validate any code points.  It makes no attempt to detect if any [invalid]
          292  +** switches between the UTF-16be and UTF-16le encodings occur.
          293  +**
          294  +** The only code points that this function cares about are the NUL character,
          295  +** carriage-return, and line-feed.
          296  +**
          297  +************************************ WARNING **********************************
          298  +*/
          299  +int looks_like_utf16(const Blob *pContent){
          300  +  const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
          301  +  unsigned int n = blob_size(pContent);
          302  +  int j, c;
          303  +  int result = 1;  /* Assume UTF-16 text with no CR/NL */
          304  +
          305  +  /* Check individual lines.
          306  +  */
          307  +  if( n==0 ) return result;  /* Empty file -> text */
          308  +  if( n%2 ) return 0;  /* Odd number of bytes -> binary (or UTF-8) */
          309  +  c = *z;
          310  +  if( c==0 ) return 0;  /* NUL character in a file -> binary */
          311  +  j = ((c!=UTF16BE_LF) && (c!=UTF16LE_LF));
          312  +  while( (n-=2)>0 ){
          313  +    c = *++z; ++j;
          314  +    if( c==0 ) return 0;  /* NUL character in a file -> binary */
          315  +    if( c==UTF16BE_LF || c==UTF16LE_LF ){
          316  +      int c2 = z[-1];
          317  +      if( c2==UTF16BE_CR || c2==UTF16LE_CR ){
          318  +        result = -1;  /* Contains CR/NL, continue */
          319  +      }
          320  +      if( j>UTF16_LENGTH_MASK ){
          321  +        return 0;  /* Very long line -> binary */
          322  +      }
          323  +      j = 0;
          324  +    }
          325  +  }
          326  +  if( j>UTF16_LENGTH_MASK ){
          327  +    return 0;  /* Very long line -> binary */
          328  +  }
          329  +  return result;  /* No problems seen -> not binary */
          330  +}
          331  +
          332  +/*
          333  +** This function returns an array of bytes representing the byte-order-mark
          334  +** for UTF-8.
          335  +*/
          336  +const unsigned char *get_utf8_bom(int *pnByte){
          337  +  static const unsigned char bom[] = {
          338  +    0xEF, 0xBB, 0xBF, 0x00, 0x00, 0x00
          339  +  };
          340  +  if( pnByte ) *pnByte = 3;
          341  +  return bom;
          342  +}
          343  +
          344  +/*
          345  +** This function returns non-zero if the blob starts with a UTF-8
          346  +** byte-order-mark (BOM).
          347  +*/
          348  +int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
          349  +  const char *z = blob_buffer(pContent);
          350  +  int bomSize = 0;
          351  +  const unsigned char *bom = get_utf8_bom(&bomSize);
          352  +
          353  +  if( pnByte ) *pnByte = bomSize;
          354  +  if( blob_size(pContent)<bomSize ) return 0;
          355  +  return memcmp(z, bom, bomSize)==0;
          356  +}
          357  +
          358  +/*
          359  +** This function returns non-zero if the blob starts with a UTF-16
          360  +** byte-order-mark (BOM), either in the endianness of the machine
          361  +** or in reversed byte order.
          362  +*/
          363  +int starts_with_utf16_bom(
          364  +  const Blob *pContent, /* IN: Blob content to perform BOM detection on. */
          365  +  int *pnByte,          /* OUT: The number of bytes used for the BOM. */
          366  +  int *pbReverse        /* OUT: Non-zero for BOM in reverse byte-order. */
          367  +){
          368  +  const char *z = blob_buffer(pContent);
          369  +  int bomSize = 2;
          370  +  static const unsigned short bom = 0xfeff;
          371  +  static const unsigned short bom_reversed = 0xfffe;
          372  +  static const unsigned short null = 0;
          373  +  int size;
          374  +
          375  +  if( pnByte ) *pnByte = bomSize;
          376  +  if( pbReverse ) *pbReverse = -1; /* Unknown. */
          377  +  size = blob_size(pContent);
          378  +  if( (size<bomSize) || (size%2) ) return 0;
          379  +  if( memcmp(z, &bom_reversed, bomSize)==0 ){
          380  +    if( pbReverse ) *pbReverse = 1;
          381  +    if( size<(2*bomSize) ) return 1;
          382  +    if( memcmp(z+bomSize, &null, bomSize)!=0 ) return 1;
          383  +  }else if( memcmp(z, &bom, bomSize)==0 ){
          384  +    if( pbReverse ) *pbReverse = 0;
          385  +    if( size<(2*bomSize) ) return 1;
          386  +    if( memcmp(z+bomSize, &null, bomSize)!=0 ) return 1;
          387  +  }
          388  +  return 0;
          389  +}
   122    390   
   123    391   /*
   124    392   ** Return true if two DLine elements are identical.
   125    393   */
   126    394   static int same_dline(DLine *pA, DLine *pB){
   127    395     return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
   128    396   }
   129    397   
   130    398   /*
   131         -** Append a single line of "diff" output to pOut.
          399  +** Return true if the regular expression *pRe matches any of the
          400  +** N dlines
   132    401   */
   133         -static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){
   134         -  blob_append(pOut, zPrefix, 1);
   135         -  blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
          402  +static int re_dline_match(
          403  +  ReCompiled *pRe,    /* The regular expression to be matched */
          404  +  DLine *aDLine,      /* First of N DLines to compare against */
          405  +  int N               /* Number of DLines to check */
          406  +){
          407  +  while( N-- ){
          408  +    if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
          409  +      return 1;
          410  +    }
          411  +    aDLine++;
          412  +  }
          413  +  return 0;
          414  +}
          415  +
          416  +/*
          417  +** Append a single line of context-diff output to pOut.
          418  +*/
          419  +static void appendDiffLine(
          420  +  Blob *pOut,         /* Where to write the line of output */
          421  +  char cPrefix,       /* One of " ", "+",  or "-" */
          422  +  DLine *pLine,       /* The line to be output */
          423  +  int html,           /* True if generating HTML.  False for plain text */
          424  +  ReCompiled *pRe     /* Colorize only if line matches this Regex */
          425  +){
          426  +  blob_append(pOut, &cPrefix, 1);
          427  +  if( html ){
          428  +    char *zHtml;
          429  +    if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
          430  +      cPrefix = ' ';
          431  +    }else if( cPrefix=='+' ){
          432  +      blob_append(pOut, "<span class=\"diffadd\">", -1);
          433  +    }else if( cPrefix=='-' ){
          434  +      blob_append(pOut, "<span class=\"diffrm\">", -1);
          435  +    }
          436  +    zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
          437  +    blob_append(pOut, zHtml, -1);
          438  +    fossil_free(zHtml);
          439  +    if( cPrefix!=' ' ){
          440  +      blob_append(pOut, "</span>", -1);
          441  +    }
          442  +  }else{
          443  +    blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
          444  +  }
   136    445     blob_append(pOut, "\n", 1);
   137    446   }
   138    447   
   139    448   /*
   140         -** Expand the size of aEdit[] array to hold nEdit elements.
   141         -*/
   142         -static void expandEdit(DContext *p, int nEdit){
   143         -  int *a;
   144         -  a = realloc(p->aEdit, nEdit*sizeof(int));
   145         -  if( a==0 ){
   146         -    free( p->aEdit );
   147         -    p->nEdit = 0;
   148         -    nEdit = 0;
   149         -  }
   150         -  p->aEdit = a;
   151         -  p->nEditAlloc = nEdit;
   152         -}
   153         -
   154         -/*
   155         -** Append a new COPY/DELETE/INSERT triple.
   156         -*/
   157         -static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){
   158         -  /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */
   159         -  if( p->nEdit>=3 ){
   160         -    if( p->aEdit[p->nEdit-1]==0 ){
   161         -      if( p->aEdit[p->nEdit-2]==0 ){
   162         -        p->aEdit[p->nEdit-3] += nCopy;
   163         -        p->aEdit[p->nEdit-2] += nDel;
   164         -        p->aEdit[p->nEdit-1] += nIns;
   165         -        return;
   166         -      }
   167         -      if( nCopy==0 ){
   168         -        p->aEdit[p->nEdit-2] += nDel;
   169         -        p->aEdit[p->nEdit-1] += nIns;
   170         -        return;
   171         -      }
   172         -    }
   173         -    if( nCopy==0 && nDel==0 ){
   174         -      p->aEdit[p->nEdit-1] += nIns;
   175         -      return;
   176         -    }
   177         -  }  
   178         -  if( p->nEdit+3>p->nEditAlloc ){
   179         -    expandEdit(p, p->nEdit*2 + 15);
   180         -    if( p->aEdit==0 ) return;
   181         -  }
   182         -  p->aEdit[p->nEdit++] = nCopy;
   183         -  p->aEdit[p->nEdit++] = nDel;
   184         -  p->aEdit[p->nEdit++] = nIns;
   185         -}
   186         -
   187         -
   188         -/*
   189         -** Given a diff context in which the aEdit[] array has been filled
          449  +** Add two line numbers to the beginning of an output line for a context
          450  +** diff.  One or the other of the two numbers might be zero, which means
          451  +** to leave that number field blank.  The "html" parameter means to format
          452  +** the output for HTML.
          453  +*/
          454  +static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
          455  +  if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
          456  +  if( lnA>0 ){
          457  +    blob_appendf(pOut, "%6d ", lnA);
          458  +  }else{
          459  +    blob_append(pOut, "       ", 7);
          460  +  }
          461  +  if( lnB>0 ){
          462  +    blob_appendf(pOut, "%6d  ", lnB);
          463  +  }else{
          464  +    blob_append(pOut, "        ", 8);
          465  +  }
          466  +  if( html ) blob_append(pOut, "</span>", -1);
          467  +}
          468  +
          469  +/*
          470  +** Given a raw diff p[] in which the p->aEdit[] array has been filled
   190    471   ** in, compute a context diff into pOut.
   191    472   */
   192         -static void contextDiff(DContext *p, Blob *pOut, int nContext){
          473  +static void contextDiff(
          474  +  DContext *p,      /* The difference */
          475  +  Blob *pOut,       /* Output a context diff to here */
          476  +  ReCompiled *pRe,  /* Only show changes that match this regex */
          477  +  u64 diffFlags     /* Flags controlling the diff format */
          478  +){
   193    479     DLine *A;     /* Left side of the diff */
   194         -  DLine *B;     /* Right side of the diff */  
          480  +  DLine *B;     /* Right side of the diff */
   195    481     int a = 0;    /* Index of next line in A[] */
   196    482     int b = 0;    /* Index of next line in B[] */
   197    483     int *R;       /* Array of COPY/DELETE/INSERT triples */
   198    484     int r;        /* Index into R[] */
   199    485     int nr;       /* Number of COPY/DELETE/INSERT triples to process */
   200    486     int mxr;      /* Maximum value for r */
   201    487     int na, nb;   /* Number of lines shown from A and B */
   202    488     int i, j;     /* Loop counters */
   203    489     int m;        /* Number of lines to output */
   204    490     int skip;     /* Number of lines to skip */
   205         -
          491  +  int nChunk = 0;  /* Number of diff chunks seen so far */
          492  +  int nContext;    /* Number of lines of context */
          493  +  int showLn;      /* Show line numbers */
          494  +  int html;        /* Render as HTML */
          495  +  int showDivider = 0;  /* True to show the divider between diff blocks */
          496  +
          497  +  nContext = diff_context_lines(diffFlags);
          498  +  showLn = (diffFlags & DIFF_LINENO)!=0;
          499  +  html = (diffFlags & DIFF_HTML)!=0;
          500  +  A = p->aFrom;
          501  +  B = p->aTo;
          502  +  R = p->aEdit;
          503  +  mxr = p->nEdit;
          504  +  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
          505  +  for(r=0; r<mxr; r += 3*nr){
          506  +    /* Figure out how many triples to show in a single block */
          507  +    for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
          508  +    /* printf("r=%d nr=%d\n", r, nr); */
          509  +
          510  +    /* If there is a regex, skip this block (generate no diff output)
          511  +    ** if the regex matches or does not match both insert and delete.
          512  +    ** Only display the block if one side matches but the other side does
          513  +    ** not.
          514  +    */
          515  +    if( pRe ){
          516  +      int hideBlock = 1;
          517  +      int xa = a, xb = b;
          518  +      for(i=0; hideBlock && i<nr; i++){
          519  +        int c1, c2;
          520  +        xa += R[r+i*3];
          521  +        xb += R[r+i*3];
          522  +        c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
          523  +        c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
          524  +        hideBlock = c1==c2;
          525  +        xa += R[r+i*3+1];
          526  +        xb += R[r+i*3+2];
          527  +      }
          528  +      if( hideBlock ){
          529  +        a = xa;
          530  +        b = xb;
          531  +        continue;
          532  +      }
          533  +    }
          534  +    
          535  +    /* For the current block comprising nr triples, figure out
          536  +    ** how many lines of A and B are to be displayed
          537  +    */
          538  +    if( R[r]>nContext ){
          539  +      na = nb = nContext;
          540  +      skip = R[r] - nContext;
          541  +    }else{
          542  +      na = nb = R[r];
          543  +      skip = 0;
          544  +    }
          545  +    for(i=0; i<nr; i++){
          546  +      na += R[r+i*3+1];
          547  +      nb += R[r+i*3+2];
          548  +    }
          549  +    if( R[r+nr*3]>nContext ){
          550  +      na += nContext;
          551  +      nb += nContext;
          552  +    }else{
          553  +      na += R[r+nr*3];
          554  +      nb += R[r+nr*3];
          555  +    }
          556  +    for(i=1; i<nr; i++){
          557  +      na += R[r+i*3];
          558  +      nb += R[r+i*3];
          559  +    }
          560  +
          561  +    /* Show the header for this block, or if we are doing a modified
          562  +    ** context diff that contains line numbers, show the separator from
          563  +    ** the previous block.
          564  +    */
          565  +    nChunk++;
          566  +    if( showLn ){
          567  +      if( !showDivider ){
          568  +        /* Do not show a top divider */
          569  +        showDivider = 1;
          570  +      }else if( html ){
          571  +        blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
          572  +        blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
          573  +      }else{
          574  +        blob_appendf(pOut, "%.80c\n", '.');
          575  +      }
          576  +    }else{
          577  +      if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
          578  +      /*
          579  +       * If the patch changes an empty file or results in an empty file,
          580  +       * the block header must use 0,0 as position indicator and not 1,0.
          581  +       * Otherwise, patch would be confused and may reject the diff.
          582  +       */
          583  +      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
          584  +        na ? a+skip+1 : 0, na,
          585  +        nb ? b+skip+1 : 0, nb);
          586  +      if( html ) blob_appendf(pOut, "</span>");
          587  +      blob_append(pOut, "\n", 1);
          588  +    }
          589  +
          590  +    /* Show the initial common area */
          591  +    a += skip;
          592  +    b += skip;
          593  +    m = R[r] - skip;
          594  +    for(j=0; j<m; j++){
          595  +      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          596  +      appendDiffLine(pOut, ' ', &A[a+j], html, 0);
          597  +    }
          598  +    a += m;
          599  +    b += m;
          600  +
          601  +    /* Show the differences */
          602  +    for(i=0; i<nr; i++){
          603  +      m = R[r+i*3+1];
          604  +      for(j=0; j<m; j++){
          605  +        if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
          606  +        appendDiffLine(pOut, '-', &A[a+j], html, pRe);
          607  +      }
          608  +      a += m;
          609  +      m = R[r+i*3+2];
          610  +      for(j=0; j<m; j++){
          611  +        if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
          612  +        appendDiffLine(pOut, '+', &B[b+j], html, pRe);
          613  +      }
          614  +      b += m;
          615  +      if( i<nr-1 ){
          616  +        m = R[r+i*3+3];
          617  +        for(j=0; j<m; j++){
          618  +          if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          619  +          appendDiffLine(pOut, ' ', &B[b+j], html, 0);
          620  +        }
          621  +        b += m;
          622  +        a += m;
          623  +      }
          624  +    }
          625  +
          626  +    /* Show the final common area */
          627  +    assert( nr==i );
          628  +    m = R[r+nr*3];
          629  +    if( m>nContext ) m = nContext;
          630  +    for(j=0; j<m; j++){
          631  +      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          632  +      appendDiffLine(pOut, ' ', &B[b+j], html, 0);
          633  +    }
          634  +  }
          635  +}
          636  +
          637  +/*
          638  +** Status of a single output line
          639  +*/
          640  +typedef struct SbsLine SbsLine;
          641  +struct SbsLine {
          642  +  char *zLine;             /* The output line under construction */
          643  +  int n;                   /* Index of next unused slot in the zLine[] */
          644  +  int width;               /* Maximum width of a column in the output */
          645  +  unsigned char escHtml;   /* True to escape html characters */
          646  +  int iStart;              /* Write zStart prior to character iStart */
          647  +  const char *zStart;      /* A <span> tag */
          648  +  int iEnd;                /* Write </span> prior to character iEnd */
          649  +  int iStart2;             /* Write zStart2 prior to character iStart2 */
          650  +  const char *zStart2;     /* A <span> tag */
          651  +  int iEnd2;               /* Write </span> prior to character iEnd2 */
          652  +  ReCompiled *pRe;         /* Only colorize matching lines, if not NULL */
          653  +};
          654  +
          655  +/*
          656  +** Flags for sbsWriteText()
          657  +*/
          658  +#define SBS_NEWLINE      0x0001   /* End with \n\000 */
          659  +#define SBS_PAD          0x0002   /* Pad output to width spaces */
          660  +
          661  +/*
          662  +** Write up to width characters of pLine into p->zLine[].  Translate tabs into
          663  +** spaces.  Add a newline if SBS_NEWLINE is set.  Translate HTML characters
          664  +** if SBS_HTML is set.  Pad the rendering out width bytes if SBS_PAD is set.
          665  +**
          666  +** This comment contains multibyte unicode characters (ü, Æ, ð) in order
          667  +** to test the ability of the diff code to handle such characters.
          668  +*/
          669  +static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
          670  +  int n = pLine->h & LENGTH_MASK;
          671  +  int i;   /* Number of input characters consumed */
          672  +  int j;   /* Number of output characters generated */
          673  +  int k;   /* Cursor position */
          674  +  int needEndSpan = 0;
          675  +  const char *zIn = pLine->z;
          676  +  char *z = &p->zLine[p->n];
          677  +  int w = p->width;
          678  +  int colorize = p->escHtml;
          679  +  if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
          680  +    colorize = 0;
          681  +  }
          682  +  for(i=j=k=0; k<w && i<n; i++, k++){
          683  +    char c = zIn[i];
          684  +    if( colorize ){
          685  +      if( i==p->iStart ){
          686  +        int x = strlen(p->zStart);
          687  +        memcpy(z+j, p->zStart, x);
          688  +        j += x;
          689  +        needEndSpan = 1;
          690  +        if( p->iStart2 ){
          691  +          p->iStart = p->iStart2;
          692  +          p->zStart = p->zStart2;
          693  +          p->iStart2 = 0;
          694  +        }
          695  +      }else if( i==p->iEnd ){
          696  +        memcpy(z+j, "</span>", 7);
          697  +        j += 7;
          698  +        needEndSpan = 0;
          699  +        if( p->iEnd2 ){
          700  +          p->iEnd = p->iEnd2;
          701  +          p->iEnd2 = 0;
          702  +        }
          703  +      }
          704  +    }
          705  +    if( c=='\t' ){
          706  +      z[j++] = ' ';
          707  +      while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; }
          708  +    }else if( c=='\r' || c=='\f' ){
          709  +      z[j++] = ' ';
          710  +    }else if( c=='<' && p->escHtml ){
          711  +      memcpy(&z[j], "&lt;", 4);
          712  +      j += 4;
          713  +    }else if( c=='&' && p->escHtml ){
          714  +      memcpy(&z[j], "&amp;", 5);
          715  +      j += 5;
          716  +    }else if( c=='>' && p->escHtml ){
          717  +      memcpy(&z[j], "&gt;", 4);
          718  +      j += 4;
          719  +    }else if( c=='"' && p->escHtml ){
          720  +      memcpy(&z[j], "&quot;", 6);
          721  +      j += 6;
          722  +    }else{
          723  +      z[j++] = c;
          724  +      if( (c&0xc0)==0x80 ) k--;
          725  +    }
          726  +  }
          727  +  if( needEndSpan ){
          728  +    memcpy(&z[j], "</span>", 7);
          729  +    j += 7;
          730  +  }
          731  +  if( (flags & SBS_PAD)!=0 ){
          732  +    while( k<w ){ k++;  z[j++] = ' '; }
          733  +  }
          734  +  if( flags & SBS_NEWLINE ){
          735  +    z[j++] = '\n';
          736  +  }
          737  +  p->n += j;
          738  +}
          739  +
          740  +/*
          741  +** Append a string to an SbSLine without coding, interpretation, or padding.
          742  +*/
          743  +static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
          744  +  memcpy(p->zLine+p->n, zIn, nIn);
          745  +  p->n += nIn;
          746  +}
          747  +
          748  +/*
          749  +** Append n spaces to the string.
          750  +*/
          751  +static void sbsWriteSpace(SbsLine *p, int n){
          752  +  while( n-- ) p->zLine[p->n++] = ' ';
          753  +}
          754  +
          755  +/*
          756  +** Append a string to the output only if we are rendering HTML.
          757  +*/
          758  +static void sbsWriteHtml(SbsLine *p, const char *zIn){
          759  +  if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
          760  +}
          761  +
          762  +/*
          763  +** Write a 6-digit line number followed by a single space onto the line.
          764  +*/
          765  +static void sbsWriteLineno(SbsLine *p, int ln){
          766  +  sbsWriteHtml(p, "<span class=\"diffln\">");
          767  +  sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1);
          768  +  p->n += 6;
          769  +  sbsWriteHtml(p, "</span>");
          770  +  p->zLine[p->n++] = ' ';
          771  +}
          772  +
          773  +/*
          774  +** The two text segments zLeft and zRight are known to be different on
          775  +** both ends, but they might have  a common segment in the middle.  If
          776  +** they do not have a common segment, return 0.  If they do have a large
          777  +** common segment, return 1 and before doing so set:
          778  +**
          779  +**   aLCS[0] = start of the common segment in zLeft
          780  +**   aLCS[1] = end of the common segment in zLeft
          781  +**   aLCS[2] = start of the common segment in zLeft
          782  +**   aLCS[3] = end of the common segment in zLeft
          783  +**
          784  +** This computation is for display purposes only and does not have to be
          785  +** optimal or exact.
          786  +*/
          787  +static int textLCS(
          788  +  const char *zLeft,  int nA,       /* String on the left */
          789  +  const char *zRight,  int nB,      /* String on the right */
          790  +  int *aLCS                         /* Identify bounds of LCS here */
          791  +){
          792  +  const unsigned char *zA = (const unsigned char*)zLeft;    /* left string */
          793  +  const unsigned char *zB = (const unsigned char*)zRight;   /* right string */
          794  +  int nt;                    /* Number of target points */
          795  +  int ti[3];                 /* Index for start of each 4-byte target */
          796  +  unsigned int target[3];    /* 4-byte alignment targets */
          797  +  unsigned int probe;        /* probe to compare against target */
          798  +  int iAS, iAE, iBS, iBE;    /* Range of common segment */
          799  +  int i, j;                  /* Loop counters */
          800  +  int rc = 0;                /* Result code.  1 for success */
          801  +
          802  +  if( nA<6 || nB<6 ) return 0;
          803  +  memset(aLCS, 0, sizeof(int)*4);
          804  +  ti[0] = i = nB/2-2;
          805  +  target[0] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
          806  +  probe = 0;
          807  +  if( nB<16 ){
          808  +    nt = 1;
          809  +  }else{
          810  +    ti[1] = i = nB/4-2;
          811  +    target[1] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
          812  +    ti[2] = i = (nB*3)/4-2;
          813  +    target[2] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
          814  +    nt = 3;
          815  +  }
          816  +  probe = (zA[0]<<16) | (zA[1]<<8) | zA[2];
          817  +  for(i=3; i<nA; i++){
          818  +    probe = (probe<<8) | zA[i];
          819  +    for(j=0; j<nt; j++){
          820  +      if( probe==target[j] ){
          821  +        iAS = i-3;
          822  +        iAE = i+1;
          823  +        iBS = ti[j];
          824  +        iBE = ti[j]+4;
          825  +        while( iAE<nA && iBE<nB && zA[iAE]==zB[iBE] ){ iAE++; iBE++; }
          826  +        while( iAS>0 && iBS>0 && zA[iAS-1]==zB[iBS-1] ){ iAS--; iBS--; }
          827  +        if( iAE-iAS > aLCS[1] - aLCS[0] ){
          828  +          aLCS[0] = iAS;
          829  +          aLCS[1] = iAE;
          830  +          aLCS[2] = iBS;
          831  +          aLCS[3] = iBE;
          832  +          rc = 1;
          833  +        }
          834  +      }
          835  +    }
          836  +  }
          837  +  return rc;
          838  +}
          839  +
          840  +/*
          841  +** Try to shift iStart as far as possible to the left.
          842  +*/
          843  +static void sbsShiftLeft(SbsLine *p, const char *z){
          844  +  int i, j;
          845  +  while( (i=p->iStart)>0 && z[i-1]==z[i] ){
          846  +    for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
          847  +    if( j<p->iEnd ) break;
          848  +    p->iStart--;
          849  +    p->iEnd--;
          850  +  }
          851  +}
          852  +
          853  +/*
          854  +** Simplify iStart and iStart2:
          855  +**
          856  +**    *  If iStart is a null-change then move iStart2 into iStart
          857  +**    *  Make sure any null-changes are in canonoical form.
          858  +*/
          859  +static void sbsSimplifyLine(SbsLine *p){
          860  +  if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
          861  +  if( p->iStart==p->iEnd ){
          862  +    p->iStart = p->iStart2;
          863  +    p->iEnd = p->iEnd2;
          864  +    p->zStart = p->zStart2;
          865  +    p->iStart2 = 0;
          866  +    p->iEnd2 = 0;
          867  +  }
          868  +  if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
          869  +}
          870  +
          871  +/*
          872  +** Write out lines that have been edited.  Adjust the highlight to cover
          873  +** only those parts of the line that actually changed.
          874  +*/
          875  +static void sbsWriteLineChange(
          876  +  SbsLine *p,          /* The SBS output line */
          877  +  DLine *pLeft,        /* Left line of the change */
          878  +  int lnLeft,          /* Line number for the left line */
          879  +  DLine *pRight,       /* Right line of the change */
          880  +  int lnRight          /* Line number of the right line */
          881  +){
          882  +  int nLeft;           /* Length of left line in bytes */
          883  +  int nRight;          /* Length of right line in bytes */
          884  +  int nPrefix;         /* Length of common prefix */
          885  +  int nSuffix;         /* Length of common suffix */
          886  +  const char *zLeft;   /* Text of the left line */
          887  +  const char *zRight;  /* Text of the right line */
          888  +  int nLeftDiff;       /* nLeft - nPrefix - nSuffix */
          889  +  int nRightDiff;      /* nRight - nPrefix - nSuffix */
          890  +  int aLCS[4];         /* Bounds of common middle segment */
          891  +  static const char zClassRm[]   = "<span class=\"diffrm\">";
          892  +  static const char zClassAdd[]  = "<span class=\"diffadd\">";
          893  +  static const char zClassChng[] = "<span class=\"diffchng\">";
          894  +
          895  +  nLeft = pLeft->h & LENGTH_MASK;
          896  +  zLeft = pLeft->z;
          897  +  nRight = pRight->h & LENGTH_MASK;
          898  +  zRight = pRight->z;
          899  +
          900  +  nPrefix = 0;
          901  +  while( nPrefix<nLeft && nPrefix<nRight && zLeft[nPrefix]==zRight[nPrefix] ){
          902  +    nPrefix++;
          903  +  }
          904  +  nSuffix = 0;
          905  +  if( nPrefix<nLeft && nPrefix<nRight ){
          906  +    while( nSuffix<nLeft && nSuffix<nRight
          907  +           && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
          908  +      nSuffix++;
          909  +    }
          910  +    if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
          911  +  }
          912  +  if( nPrefix+nSuffix > nLeft ) nPrefix = nLeft - nSuffix;
          913  +  if( nPrefix+nSuffix > nRight ) nPrefix = nRight - nSuffix;
          914  +
          915  +  /* A single chunk of text inserted on the right */
          916  +  if( nPrefix+nSuffix==nLeft ){
          917  +    sbsWriteLineno(p, lnLeft);
          918  +    p->iStart2 = p->iEnd2 = 0;
          919  +    p->iStart = p->iEnd = -1;
          920  +    sbsWriteText(p, pLeft, SBS_PAD);
          921  +    if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){
          922  +      sbsWrite(p, "   ", 3);
          923  +    }else{
          924  +      sbsWrite(p, " | ", 3);
          925  +    }
          926  +    sbsWriteLineno(p, lnRight);
          927  +    p->iStart = nPrefix;
          928  +    p->iEnd = nRight - nSuffix;
          929  +    p->zStart = zClassAdd;
          930  +    sbsWriteText(p, pRight, SBS_NEWLINE);
          931  +    return;
          932  +  }
          933  +
          934  +  /* A single chunk of text deleted from the left */
          935  +  if( nPrefix+nSuffix==nRight ){
          936  +    /* Text deleted from the left */
          937  +    sbsWriteLineno(p, lnLeft);
          938  +    p->iStart2 = p->iEnd2 = 0;
          939  +    p->iStart = nPrefix;
          940  +    p->iEnd = nLeft - nSuffix;
          941  +    p->zStart = zClassRm;
          942  +    sbsWriteText(p, pLeft, SBS_PAD);
          943  +    sbsWrite(p, " | ", 3);
          944  +    sbsWriteLineno(p, lnRight);
          945  +    p->iStart = p->iEnd = -1;
          946  +    sbsWriteText(p, pRight, SBS_NEWLINE);
          947  +    return;
          948  +  }
          949  +
          950  +  /* At this point we know that there is a chunk of text that has
          951  +  ** changed between the left and the right.  Check to see if there
          952  +  ** is a large unchanged section in the middle of that changed block.
          953  +  */
          954  +  nLeftDiff = nLeft - nSuffix - nPrefix;
          955  +  nRightDiff = nRight - nSuffix - nPrefix;
          956  +  if( p->escHtml
          957  +   && nLeftDiff >= 6
          958  +   && nRightDiff >= 6
          959  +   && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
          960  +  ){
          961  +    sbsWriteLineno(p, lnLeft);
          962  +    p->iStart = nPrefix;
          963  +    p->iEnd = nPrefix + aLCS[0];
          964  +    if( aLCS[2]==0 ){
          965  +      sbsShiftLeft(p, pLeft->z);
          966  +      p->zStart = zClassRm;
          967  +    }else{
          968  +      p->zStart = zClassChng;
          969  +    }
          970  +    p->iStart2 = nPrefix + aLCS[1];
          971  +    p->iEnd2 = nLeft - nSuffix;
          972  +    p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
          973  +    sbsSimplifyLine(p);
          974  +    sbsWriteText(p, pLeft, SBS_PAD);
          975  +    sbsWrite(p, " | ", 3);
          976  +    sbsWriteLineno(p, lnRight);
          977  +    p->iStart = nPrefix;
          978  +    p->iEnd = nPrefix + aLCS[2];
          979  +    if( aLCS[0]==0 ){
          980  +      sbsShiftLeft(p, pRight->z);
          981  +      p->zStart = zClassAdd;
          982  +    }else{
          983  +      p->zStart = zClassChng;
          984  +    }
          985  +    p->iStart2 = nPrefix + aLCS[3];
          986  +    p->iEnd2 = nRight - nSuffix;
          987  +    p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
          988  +    sbsSimplifyLine(p);
          989  +    sbsWriteText(p, pRight, SBS_NEWLINE);
          990  +    return;
          991  +  }
          992  +
          993  +  /* If all else fails, show a single big change between left and right */
          994  +  sbsWriteLineno(p, lnLeft);
          995  +  p->iStart2 = p->iEnd2 = 0;
          996  +  p->iStart = nPrefix;
          997  +  p->iEnd = nLeft - nSuffix;
          998  +  p->zStart = zClassChng;
          999  +  sbsWriteText(p, pLeft, SBS_PAD);
         1000  +  sbsWrite(p, " | ", 3);
         1001  +  sbsWriteLineno(p, lnRight);
         1002  +  p->iEnd = nRight - nSuffix;
         1003  +  sbsWriteText(p, pRight, SBS_NEWLINE);
         1004  +}
         1005  +
         1006  +/*
         1007  +** Minimum of two values
         1008  +*/
         1009  +static int minInt(int a, int b){ return a<b ? a : b; }
         1010  +
         1011  +/*
         1012  +** Return the number between 0 and 100 that is smaller the closer pA and
         1013  +** pB match.  Return 0 for a perfect match.  Return 100 if pA and pB are
         1014  +** completely different.
         1015  +**
         1016  +** The current algorithm is as follows:
         1017  +**
         1018  +** (1) Remove leading and trailing whitespace.
         1019  +** (2) Truncate both strings to at most 250 characters
         1020  +** (3) Find the length of the longest common subsequence
         1021  +** (4) Longer common subsequences yield lower scores.
         1022  +*/
         1023  +static int match_dline(DLine *pA, DLine *pB){
         1024  +  const char *zA;            /* Left string */
         1025  +  const char *zB;            /* right string */
         1026  +  int nA;                    /* Bytes in zA[] */
         1027  +  int nB;                    /* Bytes in zB[] */
         1028  +  int avg;                   /* Average length of A and B */
         1029  +  int i, j, k;               /* Loop counters */
         1030  +  int best = 0;              /* Longest match found so far */
         1031  +  int score;                 /* Final score.  0..100 */
         1032  +  unsigned char c;           /* Character being examined */
         1033  +  unsigned char aFirst[256]; /* aFirst[X] = index in zB[] of first char X */
         1034  +  unsigned char aNext[252];  /* aNext[i] = index in zB[] of next zB[i] char */
         1035  +
         1036  +  zA = pA->z;
         1037  +  zB = pB->z;
         1038  +  nA = pA->h & LENGTH_MASK;
         1039  +  nB = pB->h & LENGTH_MASK;
         1040  +  while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; }
         1041  +  while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; }
         1042  +  while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; }
         1043  +  while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; }
         1044  +  if( nA>250 ) nA = 250;
         1045  +  if( nB>250 ) nB = 250;
         1046  +  avg = (nA+nB)/2;
         1047  +  if( avg==0 ) return 0;
         1048  +  if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0;
         1049  +  memset(aFirst, 0, sizeof(aFirst));
         1050  +  zA--; zB--;   /* Make both zA[] and zB[] 1-indexed */
         1051  +  for(i=nB; i>0; i--){
         1052  +    c = (unsigned char)zB[i];
         1053  +    aNext[i] = aFirst[c];
         1054  +    aFirst[c] = i;
         1055  +  }
         1056  +  best = 0;
         1057  +  for(i=1; i<=nA-best; i++){
         1058  +    c = (unsigned char)zA[i];
         1059  +    for(j=aFirst[c]; j>0 && j<nB-best; j = aNext[j]){
         1060  +      int limit = minInt(nA-i, nB-j);
         1061  +      for(k=1; k<=limit && zA[k+i]==zB[k+j]; k++){}
         1062  +      if( k>best ) best = k;
         1063  +    }
         1064  +  }
         1065  +  score = (best>avg) ? 0 : (avg - best)*100/avg;
         1066  +
         1067  +#if 0
         1068  +  fprintf(stderr, "A: [%.*s]\nB: [%.*s]\nbest=%d avg=%d score=%d\n",
         1069  +  nA, zA+1, nB, zB+1, best, avg, score);
         1070  +#endif
         1071  +
         1072  +  /* Return the result */
         1073  +  return score;
         1074  +}
         1075  +
         1076  +/*
         1077  +** There is a change block in which nLeft lines of text on the left are
         1078  +** converted into nRight lines of text on the right.  This routine computes
         1079  +** how the lines on the left line up with the lines on the right.
         1080  +**
         1081  +** The return value is a buffer of unsigned characters, obtained from
         1082  +** fossil_malloc().  (The caller needs to free the return value using
         1083  +** fossil_free().)  Entries in the returned array have values as follows:
         1084  +**
         1085  +**    1.  Delete the next line of pLeft.
         1086  +**    2.  Insert the next line of pRight.
         1087  +**    3.  The next line of pLeft changes into the next line of pRight.
         1088  +**    4.  Delete one line from pLeft and add one line to pRight.
         1089  +**
         1090  +** Values larger than three indicate better matches.
         1091  +**
         1092  +** The length of the returned array will be just large enough to cause
         1093  +** all elements of pLeft and pRight to be consumed.
         1094  +**
         1095  +** Algorithm:  Wagner's minimum edit-distance algorithm, modified by
         1096  +** adding a cost to each match based on how well the two rows match
         1097  +** each other.  Insertion and deletion costs are 50.  Match costs
         1098  +** are between 0 and 100 where 0 is a perfect match 100 is a complete
         1099  +** mismatch.
         1100  +*/
         1101  +static unsigned char *sbsAlignment(
         1102  +   DLine *aLeft, int nLeft,       /* Text on the left */
         1103  +   DLine *aRight, int nRight      /* Text on the right */
         1104  +){
         1105  +  int i, j, k;                 /* Loop counters */
         1106  +  int *a;                      /* One row of the Wagner matrix */
         1107  +  int *pToFree;                /* Space that needs to be freed */
         1108  +  unsigned char *aM;           /* Wagner result matrix */
         1109  +  int nMatch, iMatch;          /* Number of matching lines and match score */
         1110  +  int mnLen;                   /* MIN(nLeft, nRight) */
         1111  +  int mxLen;                   /* MAX(nLeft, nRight) */
         1112  +  int aBuf[100];               /* Stack space for a[] if nRight not to big */
         1113  +
         1114  +  aM = fossil_malloc( (nLeft+1)*(nRight+1) );
         1115  +  if( nLeft==0 ){
         1116  +    memset(aM, 2, nRight);
         1117  +    return aM;
         1118  +  }
         1119  +  if( nRight==0 ){
         1120  +    memset(aM, 1, nLeft);
         1121  +    return aM;
         1122  +  }
         1123  +
         1124  +  /* This algorithm is O(N**2).  So if N is too big, bail out with a
         1125  +  ** simple (but stupid and ugly) result that doesn't take too long. */
         1126  +  mnLen = nLeft<nRight ? nLeft : nRight;
         1127  +  if( nLeft*nRight>100000 ){
         1128  +    memset(aM, 4, mnLen);
         1129  +    if( nLeft>mnLen )  memset(aM+mnLen, 1, nLeft-mnLen);
         1130  +    if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
         1131  +    return aM;
         1132  +  }
         1133  +
         1134  +  if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
         1135  +    pToFree = 0;
         1136  +    a = aBuf;
         1137  +  }else{
         1138  +    a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
         1139  +  }
         1140  +
         1141  +  /* Compute the best alignment */
         1142  +  for(i=0; i<=nRight; i++){
         1143  +    aM[i] = 2;
         1144  +    a[i] = i*50;
         1145  +  }
         1146  +  aM[0] = 0;
         1147  +  for(j=1; j<=nLeft; j++){
         1148  +    int p = a[0];
         1149  +    a[0] = p+50;
         1150  +    aM[j*(nRight+1)] = 1;
         1151  +    for(i=1; i<=nRight; i++){
         1152  +      int m = a[i-1]+50;
         1153  +      int d = 2;
         1154  +      if( m>a[i]+50 ){
         1155  +        m = a[i]+50;
         1156  +        d = 1;
         1157  +      }
         1158  +      if( m>p ){
         1159  +        int score = match_dline(&aLeft[j-1], &aRight[i-1]);
         1160  +        if( (score<=63 || (i<j+1 && i>j-1)) && m>p+score ){
         1161  +          m = p+score;
         1162  +          d = 3 | score*4;
         1163  +        }
         1164  +      }
         1165  +      p = a[i];
         1166  +      a[i] = m;
         1167  +      aM[j*(nRight+1)+i] = d;
         1168  +    }
         1169  +  }
         1170  +
         1171  +  /* Compute the lowest-cost path back through the matrix */
         1172  +  i = nRight;
         1173  +  j = nLeft;
         1174  +  k = (nRight+1)*(nLeft+1)-1;
         1175  +  nMatch = iMatch = 0;
         1176  +  while( i+j>0 ){
         1177  +    unsigned char c = aM[k];
         1178  +    if( c>=3 ){
         1179  +      assert( i>0 && j>0 );
         1180  +      i--;
         1181  +      j--;
         1182  +      nMatch++;
         1183  +      iMatch += (c>>2);
         1184  +      aM[k] = 3;
         1185  +    }else if( c==2 ){
         1186  +      assert( i>0 );
         1187  +      i--;
         1188  +    }else{
         1189  +      assert( j>0 );
         1190  +      j--;
         1191  +    }
         1192  +    k--;
         1193  +    aM[k] = aM[j*(nRight+1)+i];
         1194  +  }
         1195  +  k++;
         1196  +  i = (nRight+1)*(nLeft+1) - k;
         1197  +  memmove(aM, &aM[k], i);
         1198  +
         1199  +  /* If:
         1200  +  **   (1) the alignment is more than 25% longer than the longest side, and
         1201  +  **   (2) the average match cost exceeds 15
         1202  +  ** Then this is probably an alignment that will be difficult for humans
         1203  +  ** to read.  So instead, just show all of the right side inserted followed
         1204  +  ** by all of the left side deleted.
         1205  +  **
         1206  +  ** The coefficients for conditions (1) and (2) above are determined by
         1207  +  ** experimentation.  
         1208  +  */
         1209  +  mxLen = nLeft>nRight ? nLeft : nRight;
         1210  +  if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
         1211  +    memset(aM, 4, mnLen);
         1212  +    if( nLeft>mnLen )  memset(aM+mnLen, 1, nLeft-mnLen);
         1213  +    if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
         1214  +  }
         1215  +
         1216  +  /* Return the result */
         1217  +  fossil_free(pToFree);
         1218  +  return aM;
         1219  +}
         1220  +
         1221  +/*
         1222  +** R[] is an array of six integer, two COPY/DELETE/INSERT triples for a
         1223  +** pair of adjacent differences.  Return true if the gap between these
         1224  +** two differences is so small that they should be rendered as a single
         1225  +** edit.
         1226  +*/
         1227  +static int smallGap(int *R){
         1228  +  return R[3]<=2 || R[3]<=(R[1]+R[2]+R[4]+R[5])/8;
         1229  +}
         1230  +
         1231  +/*
         1232  +** Given a diff context in which the aEdit[] array has been filled
         1233  +** in, compute a side-by-side diff into pOut.
         1234  +*/
         1235  +static void sbsDiff(
         1236  +  DContext *p,       /* The computed diff */
         1237  +  Blob *pOut,        /* Write the results here */
         1238  +  ReCompiled *pRe,   /* Only show changes that match this regex */
         1239  +  u64 diffFlags      /* Flags controlling the diff */
         1240  +){
         1241  +  DLine *A;     /* Left side of the diff */
         1242  +  DLine *B;     /* Right side of the diff */
         1243  +  int a = 0;    /* Index of next line in A[] */
         1244  +  int b = 0;    /* Index of next line in B[] */
         1245  +  int *R;       /* Array of COPY/DELETE/INSERT triples */
         1246  +  int r;        /* Index into R[] */
         1247  +  int nr;       /* Number of COPY/DELETE/INSERT triples to process */
         1248  +  int mxr;      /* Maximum value for r */
         1249  +  int na, nb;   /* Number of lines shown from A and B */
         1250  +  int i, j;     /* Loop counters */
         1251  +  int m, ma, mb;/* Number of lines to output */
         1252  +  int skip;     /* Number of lines to skip */
         1253  +  int nChunk = 0; /* Number of chunks of diff output seen so far */
         1254  +  SbsLine s;    /* Output line buffer */
         1255  +  int nContext; /* Lines of context above and below each change */
         1256  +  int showDivider = 0;  /* True to show the divider */
         1257  +
         1258  +  memset(&s, 0, sizeof(s));
         1259  +  s.width = diff_width(diffFlags);
         1260  +  s.zLine = fossil_malloc( 15*s.width + 200 );
         1261  +  if( s.zLine==0 ) return;
         1262  +  nContext = diff_context_lines(diffFlags);
         1263  +  s.escHtml = (diffFlags & DIFF_HTML)!=0;
         1264  +  s.pRe = pRe;
         1265  +  s.iStart = -1;
         1266  +  s.iStart2 = 0;
         1267  +  s.iEnd = -1;
   206   1268     A = p->aFrom;
   207   1269     B = p->aTo;
   208   1270     R = p->aEdit;
   209   1271     mxr = p->nEdit;
   210   1272     while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
   211   1273     for(r=0; r<mxr; r += 3*nr){
   212   1274       /* Figure out how many triples to show in a single block */
   213   1275       for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
   214   1276       /* printf("r=%d nr=%d\n", r, nr); */
         1277  +
         1278  +    /* If there is a regex, skip this block (generate no diff output)
         1279  +    ** if the regex matches or does not match both insert and delete.
         1280  +    ** Only display the block if one side matches but the other side does
         1281  +    ** not.
         1282  +    */
         1283  +    if( pRe ){
         1284  +      int hideBlock = 1;
         1285  +      int xa = a, xb = b;
         1286  +      for(i=0; hideBlock && i<nr; i++){
         1287  +        int c1, c2;
         1288  +        xa += R[r+i*3];
         1289  +        xb += R[r+i*3];
         1290  +        c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
         1291  +        c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
         1292  +        hideBlock = c1==c2;
         1293  +        xa += R[r+i*3+1];
         1294  +        xb += R[r+i*3+2];
         1295  +      }
         1296  +      if( hideBlock ){
         1297  +        a = xa;
         1298  +        b = xb;
         1299  +        continue;
         1300  +      }
         1301  +    }
   215   1302   
   216   1303       /* For the current block comprising nr triples, figure out
   217   1304       ** how many lines of A and B are to be displayed
   218   1305       */
   219   1306       if( R[r]>nContext ){
   220   1307         na = nb = nContext;
   221   1308         skip = R[r] - nContext;
................................................................................
   234   1321         na += R[r+nr*3];
   235   1322         nb += R[r+nr*3];
   236   1323       }
   237   1324       for(i=1; i<nr; i++){
   238   1325         na += R[r+i*3];
   239   1326         nb += R[r+i*3];
   240   1327       }
   241         -    blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n", a+skip+1, na, b+skip+1, nb);
         1328  +
         1329  +    /* Draw the separator between blocks */
         1330  +    if( showDivider ){
         1331  +      if( s.escHtml ){
         1332  +        blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
         1333  +                           s.width*2+16, '.');
         1334  +      }else{
         1335  +        blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
         1336  +      }
         1337  +    }
         1338  +    showDivider = 1;
         1339  +    nChunk++;
         1340  +    if( s.escHtml ){
         1341  +      blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
         1342  +    }
   242   1343   
   243   1344       /* Show the initial common area */
   244   1345       a += skip;
   245   1346       b += skip;
   246   1347       m = R[r] - skip;
   247   1348       for(j=0; j<m; j++){
   248         -      appendDiffLine(pOut, " ", &A[a+j]);
         1349  +      s.n = 0;
         1350  +      sbsWriteLineno(&s, a+j);
         1351  +      s.iStart = s.iEnd = -1;
         1352  +      sbsWriteText(&s, &A[a+j], SBS_PAD);
         1353  +      sbsWrite(&s, "   ", 3);
         1354  +      sbsWriteLineno(&s, b+j);
         1355  +      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
         1356  +      blob_append(pOut, s.zLine, s.n);
   249   1357       }
   250   1358       a += m;
   251   1359       b += m;
   252   1360   
   253   1361       /* Show the differences */
   254   1362       for(i=0; i<nr; i++){
   255         -      m = R[r+i*3+1];
   256         -      for(j=0; j<m; j++){
   257         -        appendDiffLine(pOut, "-", &A[a+j]);
         1363  +      unsigned char *alignment;
         1364  +      ma = R[r+i*3+1];   /* Lines on left but not on right */
         1365  +      mb = R[r+i*3+2];   /* Lines on right but not on left */
         1366  +
         1367  +      /* If the gap between the current diff and then next diff within the
         1368  +      ** same block is not too great, then render them as if they are a
         1369  +      ** single diff. */
         1370  +      while( i<nr-1 && smallGap(&R[r+i*3]) ){
         1371  +        i++;
         1372  +        m = R[r+i*3];
         1373  +        ma += R[r+i*3+1] + m;
         1374  +        mb += R[r+i*3+2] + m;
   258   1375         }
   259         -      a += m;
   260         -      m = R[r+i*3+2];
   261         -      for(j=0; j<m; j++){
   262         -        appendDiffLine(pOut, "+", &B[b+j]);
         1376  +
         1377  +      alignment = sbsAlignment(&A[a], ma, &B[b], mb);
         1378  +      for(j=0; ma+mb>0; j++){
         1379  +        if( alignment[j]==1 ){
         1380  +          /* Delete one line from the left */
         1381  +          s.n = 0;
         1382  +          sbsWriteLineno(&s, a);
         1383  +          s.iStart = 0;
         1384  +          s.zStart = "<span class=\"diffrm\">";
         1385  +          s.iEnd = s.width;
         1386  +          sbsWriteText(&s, &A[a], SBS_PAD);
         1387  +          if( s.escHtml ){
         1388  +            sbsWrite(&s, " &lt;\n", 6);
         1389  +          }else{
         1390  +            sbsWrite(&s, " <\n", 3);
         1391  +          }
         1392  +          blob_append(pOut, s.zLine, s.n);
         1393  +          assert( ma>0 );
         1394  +          ma--;
         1395  +          a++;
         1396  +        }else if( alignment[j]==3 ){
         1397  +          /* The left line is changed into the right line */
         1398  +          s.n = 0;
         1399  +          sbsWriteLineChange(&s, &A[a], a, &B[b], b);
         1400  +          blob_append(pOut, s.zLine, s.n);
         1401  +          assert( ma>0 && mb>0 );
         1402  +          ma--;
         1403  +          mb--;
         1404  +          a++;
         1405  +          b++;
         1406  +        }else if( alignment[j]==2 ){
         1407  +          /* Insert one line on the right */
         1408  +          s.n = 0;
         1409  +          sbsWriteSpace(&s, s.width + 7);
         1410  +          if( s.escHtml ){
         1411  +            sbsWrite(&s, " &gt; ", 6);
         1412  +          }else{
         1413  +            sbsWrite(&s, " > ", 3);
         1414  +          }
         1415  +          sbsWriteLineno(&s, b);
         1416  +          s.iStart = 0;
         1417  +          s.zStart = "<span class=\"diffadd\">";
         1418  +          s.iEnd = s.width;
         1419  +          sbsWriteText(&s, &B[b], SBS_NEWLINE);
         1420  +          blob_append(pOut, s.zLine, s.n);
         1421  +          assert( mb>0 );
         1422  +          mb--;
         1423  +          b++;
         1424  +        }else{
         1425  +          /* Delete from the left and insert on the right */
         1426  +          s.n = 0;
         1427  +          sbsWriteLineno(&s, a);
         1428  +          s.iStart = 0;
         1429  +          s.zStart = "<span class=\"diffrm\">";
         1430  +          s.iEnd = s.width;
         1431  +          sbsWriteText(&s, &A[a], SBS_PAD);
         1432  +          sbsWrite(&s, " | ", 3);
         1433  +          sbsWriteLineno(&s, b);
         1434  +          s.iStart = 0;
         1435  +          s.zStart = "<span class=\"diffadd\">";
         1436  +          s.iEnd = s.width;
         1437  +          sbsWriteText(&s, &B[b], SBS_NEWLINE);
         1438  +          blob_append(pOut, s.zLine, s.n);
         1439  +          ma--;
         1440  +          mb--;
         1441  +          a++;
         1442  +          b++;
         1443  +        }
         1444  +          
   263   1445         }
   264         -      b += m;
         1446  +      fossil_free(alignment);
   265   1447         if( i<nr-1 ){
   266   1448           m = R[r+i*3+3];
   267   1449           for(j=0; j<m; j++){
   268         -          appendDiffLine(pOut, " ", &B[b+j]);
         1450  +          s.n = 0;
         1451  +          sbsWriteLineno(&s, a+j);
         1452  +          s.iStart = s.iEnd = -1;
         1453  +          sbsWriteText(&s, &A[a+j], SBS_PAD);
         1454  +          sbsWrite(&s, "   ", 3);
         1455  +          sbsWriteLineno(&s, b+j);
         1456  +          sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
         1457  +          blob_append(pOut, s.zLine, s.n);
   269   1458           }
   270   1459           b += m;
   271   1460           a += m;
   272   1461         }
   273   1462       }
   274   1463   
   275   1464       /* Show the final common area */
   276   1465       assert( nr==i );
   277   1466       m = R[r+nr*3];
   278   1467       if( m>nContext ) m = nContext;
   279   1468       for(j=0; j<m; j++){
   280         -      appendDiffLine(pOut, " ", &B[b+j]);
         1469  +      s.n = 0;
         1470  +      sbsWriteLineno(&s, a+j);
         1471  +      s.iStart = s.iEnd = -1;
         1472  +      sbsWriteText(&s, &A[a+j], SBS_PAD);
         1473  +      sbsWrite(&s, "   ", 3);
         1474  +      sbsWriteLineno(&s, b+j);
         1475  +      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
         1476  +      blob_append(pOut, s.zLine, s.n);
   281   1477       }
   282   1478     }
         1479  +  free(s.zLine);
         1480  +}
         1481  +
         1482  +/*
         1483  +** Compute the optimal longest common subsequence (LCS) using an
         1484  +** exhaustive search.  This version of the LCS is only used for
         1485  +** shorter input strings since runtime is O(N*N) where N is the
         1486  +** input string length.
         1487  +*/
         1488  +static void optimalLCS(
         1489  +  DContext *p,               /* Two files being compared */
         1490  +  int iS1, int iE1,          /* Range of lines in p->aFrom[] */
         1491  +  int iS2, int iE2,          /* Range of lines in p->aTo[] */
         1492  +  int *piSX, int *piEX,      /* Write p->aFrom[] common segment here */
         1493  +  int *piSY, int *piEY       /* Write p->aTo[] common segment here */
         1494  +){
         1495  +  int mxLength = 0;          /* Length of longest common subsequence */
         1496  +  int i, j;                  /* Loop counters */
         1497  +  int k;                     /* Length of a candidate subsequence */
         1498  +  int iSXb = iS1;            /* Best match so far */
         1499  +  int iSYb = iS2;            /* Best match so far */
         1500  +
         1501  +  for(i=iS1; i<iE1-mxLength; i++){
         1502  +    for(j=iS2; j<iE2-mxLength; j++){
         1503  +      if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
         1504  +      if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
         1505  +        continue;
         1506  +      }
         1507  +      k = 1;
         1508  +      while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
         1509  +        k++;
         1510  +      }
         1511  +      if( k>mxLength ){
         1512  +        iSXb = i;
         1513  +        iSYb = j;
         1514  +        mxLength = k;
         1515  +      }
         1516  +    }
         1517  +  }
         1518  +  *piSX = iSXb;
         1519  +  *piEX = iSXb + mxLength;
         1520  +  *piSY = iSYb;
         1521  +  *piEY = iSYb + mxLength;
   283   1522   }
   284   1523   
   285   1524   /*
   286   1525   ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
   287   1526   ** file and lines iS2 through iE2-1 of the aTo[] file.  Locate a sequence
   288   1527   ** of lines in these two blocks that are exactly the same.  Return
   289   1528   ** the bounds of the matching sequence.
         1529  +**
         1530  +** If there are two or more possible answers of the same length, the
         1531  +** returned sequence should be the one closest to the center of the
         1532  +** input range.
         1533  +**
         1534  +** Ideally, the common sequence should be the longest possible common
         1535  +** sequence.  However, an exact computation of LCS is O(N*N) which is
         1536  +** way too slow for larger files.  So this routine uses an O(N)
         1537  +** heuristic approximation based on hashing that usually works about
         1538  +** as well.  But if the O(N) algorithm doesn't get a good solution
         1539  +** and N is not too large, we fall back to an exact solution by
         1540  +** calling optimalLCS().
   290   1541   */
   291   1542   static void longestCommonSequence(
   292   1543     DContext *p,               /* Two files being compared */
   293   1544     int iS1, int iE1,          /* Range of lines in p->aFrom[] */
   294   1545     int iS2, int iE2,          /* Range of lines in p->aTo[] */
   295   1546     int *piSX, int *piEX,      /* Write p->aFrom[] common segment here */
   296   1547     int *piSY, int *piEY       /* Write p->aTo[] common segment here */
   297   1548   ){
   298   1549     double bestScore = -1e30;  /* Best score seen so far */
   299         -  int i, j;                  /* Loop counters */
         1550  +  int i, j, k;               /* Loop counters */
         1551  +  int n;                     /* Loop limit */
         1552  +  DLine *pA, *pB;            /* Pointers to lines */
   300   1553     int iSX, iSY, iEX, iEY;    /* Current match */
   301   1554     double score;              /* Current score */
   302   1555     int skew;                  /* How lopsided is the match */
   303   1556     int dist;                  /* Distance of match from center */
   304   1557     int mid;                   /* Center of the span */
   305   1558     int iSXb, iSYb, iEXb, iEYb;   /* Best match so far */
   306   1559     int iSXp, iSYp, iEXp, iEYp;   /* Previous match */
         1560  +
   307   1561   
   308   1562     iSXb = iSXp = iS1;
   309   1563     iEXb = iEXp = iS1;
   310   1564     iSYb = iSYp = iS2;
   311   1565     iEYb = iEYp = iS2;
   312   1566     mid = (iE1 + iS1)/2;
   313   1567     for(i=iS1; i<iE1; i++){
   314   1568       int limit = 0;
   315   1569       j = p->aTo[p->aFrom[i].h % p->nTo].iHash;
   316         -    while( j>0 
         1570  +    while( j>0
   317   1571         && (j-1<iS2 || j>=iE2 || !same_dline(&p->aFrom[i], &p->aTo[j-1]))
   318   1572       ){
   319   1573         if( limit++ > 10 ){
   320   1574           j = 0;
   321   1575           break;
   322   1576         }
   323   1577         j = p->aTo[j-1].iNext;
................................................................................
   324   1578       }
   325   1579       if( j==0 ) continue;
   326   1580       assert( i>=iSXb && i>=iSXp );
   327   1581       if( i<iEXb && j>=iSYb && j<iEYb ) continue;
   328   1582       if( i<iEXp && j>=iSYp && j<iEYp ) continue;
   329   1583       iSX = i;
   330   1584       iSY = j-1;
   331         -    while( iSX>iS1 && iSY>iS2 && same_dline(&p->aFrom[iSX-1],&p->aTo[iSY-1]) ){
   332         -      iSX--;
   333         -      iSY--;
   334         -    }
         1585  +    pA = &p->aFrom[iSX-1];
         1586  +    pB = &p->aTo[iSY-1];
         1587  +    n = minInt(iSX-iS1, iSY-iS2);
         1588  +    for(k=0; k<n && same_dline(pA,pB); k++, pA--, pB--){}
         1589  +    iSX -= k;
         1590  +    iSY -= k;
   335   1591       iEX = i+1;
   336   1592       iEY = j;
   337         -    while( iEX<iE1 && iEY<iE2 && same_dline(&p->aFrom[iEX],&p->aTo[iEY]) ){
   338         -      iEX++;
   339         -      iEY++;
   340         -    }
         1593  +    pA = &p->aFrom[iEX];
         1594  +    pB = &p->aTo[iEY];
         1595  +    n = minInt(iE1-iEX, iE2-iEY);
         1596  +    for(k=0; k<n && same_dline(pA,pB); k++, pA++, pB++){}
         1597  +    iEX += k;
         1598  +    iEY += k;
   341   1599       skew = (iSX-iS1) - (iSY-iS2);
   342   1600       if( skew<0 ) skew = -skew;
   343   1601       dist = (iSX+iEX)/2 - mid;
   344   1602       if( dist<0 ) dist = -dist;
   345   1603       score = (iEX - iSX) - 0.05*skew - 0.05*dist;
   346   1604       if( score>bestScore ){
   347   1605         bestScore = score;
   348   1606         iSXb = iSX;
   349   1607         iSYb = iSY;
   350   1608         iEXb = iEX;
   351   1609         iEYb = iEY;
   352         -    }else{
         1610  +    }else if( iEX>iEXp ){
   353   1611         iSXp = iSX;
   354   1612         iSYp = iSY;
   355   1613         iEXp = iEX;
   356   1614         iEYp = iEY;
   357   1615       }
   358   1616     }
   359         -  *piSX = iSXb;
   360         -  *piSY = iSYb;
   361         -  *piEX = iEXb;
   362         -  *piEY = iEYb;
   363         -  /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n", 
         1617  +  if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
         1618  +    /* If no common sequence is found using the hashing heuristic and
         1619  +    ** the input is not too big, use the expensive exact solution */
         1620  +    optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
         1621  +  }else{
         1622  +    *piSX = iSXb;
         1623  +    *piSY = iSYb;
         1624  +    *piEX = iEXb;
         1625  +    *piEY = iEYb;
         1626  +  }
         1627  +  /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
   364   1628        iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY);  */
   365   1629   }
         1630  +
         1631  +/*
         1632  +** Expand the size of aEdit[] array to hold at least nEdit elements.
         1633  +*/
         1634  +static void expandEdit(DContext *p, int nEdit){
         1635  +  p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int));
         1636  +  p->nEditAlloc = nEdit;
         1637  +}
         1638  +
         1639  +/*
         1640  +** Append a new COPY/DELETE/INSERT triple.
         1641  +*/
         1642  +static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){
         1643  +  /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */
         1644  +  if( p->nEdit>=3 ){
         1645  +    if( p->aEdit[p->nEdit-1]==0 ){
         1646  +      if( p->aEdit[p->nEdit-2]==0 ){
         1647  +        p->aEdit[p->nEdit-3] += nCopy;
         1648  +        p->aEdit[p->nEdit-2] += nDel;
         1649  +        p->aEdit[p->nEdit-1] += nIns;
         1650  +        return;
         1651  +      }
         1652  +      if( nCopy==0 ){
         1653  +        p->aEdit[p->nEdit-2] += nDel;
         1654  +        p->aEdit[p->nEdit-1] += nIns;
         1655  +        return;
         1656  +      }
         1657  +    }
         1658  +    if( nCopy==0 && nDel==0 ){
         1659  +      p->aEdit[p->nEdit-1] += nIns;
         1660  +      return;
         1661  +    }
         1662  +  }
         1663  +  if( p->nEdit+3>p->nEditAlloc ){
         1664  +    expandEdit(p, p->nEdit*2 + 15);
         1665  +    if( p->aEdit==0 ) return;
         1666  +  }
         1667  +  p->aEdit[p->nEdit++] = nCopy;
         1668  +  p->aEdit[p->nEdit++] = nDel;
         1669  +  p->aEdit[p->nEdit++] = nIns;
         1670  +}
   366   1671   
   367   1672   /*
   368   1673   ** Do a single step in the difference.  Compute a sequence of
   369   1674   ** copy/delete/insert steps that will convert lines iS1 through iE1-1 of
   370   1675   ** the input into lines iS2 through iE2-1 of the output and write
   371   1676   ** that sequence into the difference context.
   372   1677   **
................................................................................
   392   1697       return;
   393   1698     }
   394   1699   
   395   1700     /* Find the longest matching segment between the two sequences */
   396   1701     longestCommonSequence(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY);
   397   1702   
   398   1703     if( iEX>iSX ){
   399         -    /* A common segement has been found.
         1704  +    /* A common segment has been found.
   400   1705       ** Recursively diff either side of the matching segment */
   401   1706       diff_step(p, iS1, iSX, iS2, iSY);
   402   1707       if( iEX>iSX ){
   403   1708         appendTriple(p, iEX - iSX, 0, 0);
   404   1709       }
   405   1710       diff_step(p, iEX, iE1, iEY, iE2);
   406   1711     }else{
................................................................................
   451   1756     expandEdit(p, p->nEdit+3);
   452   1757     if( p->aEdit ){
   453   1758       p->aEdit[p->nEdit++] = 0;
   454   1759       p->aEdit[p->nEdit++] = 0;
   455   1760       p->aEdit[p->nEdit++] = 0;
   456   1761     }
   457   1762   }
         1763  +
         1764  +/*
         1765  +** Attempt to shift insertion or deletion blocks so that they begin and
         1766  +** end on lines that are pure whitespace.  In other words, try to transform
         1767  +** this:
         1768  +**
         1769  +**      int func1(int x){
         1770  +**         return x*10;
         1771  +**     +}
         1772  +**     +
         1773  +**     +int func2(int x){
         1774  +**     +   return x*20;
         1775  +**      }
         1776  +**
         1777  +**      int func3(int x){
         1778  +**         return x/5;
         1779  +**      }
         1780  +**
         1781  +** Into one of these:
         1782  +**
         1783  +**      int func1(int x){              int func1(int x){
         1784  +**         return x*10;                   return x*10;
         1785  +**      }                              }
         1786  +**     +
         1787  +**     +int func2(int x){             +int func2(int x){
         1788  +**     +   return x*20;               +   return x*20;
         1789  +**     +}                             +}
         1790  +**                                    +
         1791  +**      int func3(int x){              int func3(int x){
         1792  +**         return x/5;                    return x/5;
         1793  +**      }                              }
         1794  +*/
         1795  +static void diff_optimize(DContext *p){
         1796  +  int r;       /* Index of current triple */
         1797  +  int lnFrom;  /* Line number in p->aFrom */
         1798  +  int lnTo;    /* Line number in p->aTo */
         1799  +  int cpy, del, ins;
         1800  +
         1801  +  lnFrom = lnTo = 0;
         1802  +  for(r=0; r<p->nEdit; r += 3){
         1803  +    cpy = p->aEdit[r];
         1804  +    del = p->aEdit[r+1];
         1805  +    ins = p->aEdit[r+2];
         1806  +    lnFrom += cpy;
         1807  +    lnTo += cpy;
         1808  +
         1809  +    /* Shift insertions toward the beginning of the file */
         1810  +    while( cpy>0 && del==0 && ins>0 ){
         1811  +      DLine *pTop = &p->aFrom[lnFrom-1];  /* Line before start of insert */
         1812  +      DLine *pBtm = &p->aTo[lnTo+ins-1];  /* Last line inserted */
         1813  +      if( same_dline(pTop, pBtm)==0 ) break;
         1814  +      if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
         1815  +      lnFrom--;
         1816  +      lnTo--;
         1817  +      p->aEdit[r]--;
         1818  +      p->aEdit[r+3]++;
         1819  +      cpy--;
         1820  +    }
         1821  +
         1822  +    /* Shift insertions toward the end of the file */
         1823  +    while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){
         1824  +      DLine *pTop = &p->aTo[lnTo];       /* First line inserted */
         1825  +      DLine *pBtm = &p->aTo[lnTo+ins];   /* First line past end of insert */
         1826  +      if( same_dline(pTop, pBtm)==0 ) break;
         1827  +      if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break;
         1828  +      lnFrom++;
         1829  +      lnTo++;
         1830  +      p->aEdit[r]++;
         1831  +      p->aEdit[r+3]--;
         1832  +      cpy++;
         1833  +    }
         1834  +
         1835  +    /* Shift deletions toward the beginning of the file */
         1836  +    while( cpy>0 && del>0 && ins==0 ){
         1837  +      DLine *pTop = &p->aFrom[lnFrom-1];     /* Line before start of delete */
         1838  +      DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */
         1839  +      if( same_dline(pTop, pBtm)==0 ) break;
         1840  +      if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
         1841  +      lnFrom--;
         1842  +      lnTo--;
         1843  +      p->aEdit[r]--;
         1844  +      p->aEdit[r+3]++;
         1845  +      cpy--;
         1846  +    }
         1847  +
         1848  +    /* Shift deletions toward the end of the file */
         1849  +    while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){
         1850  +      DLine *pTop = &p->aFrom[lnFrom];     /* First line deleted */
         1851  +      DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */
         1852  +      if( same_dline(pTop, pBtm)==0 ) break;
         1853  +      if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break;
         1854  +      lnFrom++;
         1855  +      lnTo++;
         1856  +      p->aEdit[r]++;
         1857  +      p->aEdit[r+3]--;
         1858  +      cpy++;
         1859  +    }
         1860  +
         1861  +    lnFrom += del;
         1862  +    lnTo += ins;
         1863  +  }
         1864  +}
         1865  +
         1866  +/*
         1867  +** Extract the number of lines of context from diffFlags.  Supply an
         1868  +** appropriate default if no context width is specified.
         1869  +*/
         1870  +int diff_context_lines(u64 diffFlags){
         1871  +  int n = diffFlags & DIFF_CONTEXT_MASK;
         1872  +  if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
         1873  +  return n;
         1874  +}
         1875  +
         1876  +/*
         1877  +** Extract the width of columns for side-by-side diff.  Supply an
         1878  +** appropriate default if no width is given.
         1879  +*/
         1880  +int diff_width(u64 diffFlags){
         1881  +  int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
         1882  +  if( w==0 ) w = 80;
         1883  +  return w;
         1884  +}
   458   1885   
   459   1886   /*
   460   1887   ** Generate a report of the differences between files pA and pB.
   461   1888   ** If pOut is not NULL then a unified diff is appended there.  It
   462   1889   ** is assumed that pOut has already been initialized.  If pOut is
   463         -** NULL, then a pointer to an array of integers is returned.  
         1890  +** NULL, then a pointer to an array of integers is returned.
   464   1891   ** The integers come in triples.  For each triple,
   465   1892   ** the elements are the number of lines copied, the number of
   466   1893   ** lines deleted, and the number of lines inserted.  The vector
   467   1894   ** is terminated by a triple of all zeros.
   468   1895   **
   469   1896   ** This diff utility does not work on binary files.  If a binary
   470   1897   ** file is encountered, 0 is returned and pOut is written with
   471   1898   ** text "cannot compute difference between binary files".
   472   1899   */
   473   1900   int *text_diff(
   474   1901     Blob *pA_Blob,   /* FROM file */
   475   1902     Blob *pB_Blob,   /* TO file */
   476         -  Blob *pOut,      /* Write unified diff here if not NULL */
   477         -  int nContext     /* Amount of context to unified diff */
         1903  +  Blob *pOut,      /* Write diff here if not NULL */
         1904  +  ReCompiled *pRe, /* Only output changes where this Regexp matches */
         1905  +  u64 diffFlags    /* DIFF_* flags defined above */
   478   1906   ){
         1907  +  int ignoreEolWs; /* Ignore whitespace at the end of lines */
   479   1908     DContext c;
   480         - 
         1909  +
         1910  +  if( diffFlags & DIFF_INVERT ){
         1911  +    Blob *pTemp = pA_Blob;
         1912  +    pA_Blob = pB_Blob;
         1913  +    pB_Blob = pTemp;
         1914  +  }
         1915  +  ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
         1916  +
   481   1917     /* Prepare the input files */
   482   1918     memset(&c, 0, sizeof(c));
   483         -  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom);
   484         -  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo);
         1919  +  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
         1920  +                             &c.nFrom, ignoreEolWs);
         1921  +  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
         1922  +                           &c.nTo, ignoreEolWs);
   485   1923     if( c.aFrom==0 || c.aTo==0 ){
   486         -    free(c.aFrom);
   487         -    free(c.aTo);
         1924  +    fossil_free(c.aFrom);
         1925  +    fossil_free(c.aTo);
   488   1926       if( pOut ){
   489         -      blob_appendf(pOut, "cannot compute difference between binary files\n");
         1927  +      blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
   490   1928       }
   491   1929       return 0;
   492   1930     }
   493   1931   
   494   1932     /* Compute the difference */
   495   1933     diff_all(&c);
         1934  +  if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
   496   1935   
   497   1936     if( pOut ){
   498         -    /* Compute a context diff if requested */
   499         -    contextDiff(&c, pOut, nContext);
   500         -    free(c.aFrom);
   501         -    free(c.aTo);
   502         -    free(c.aEdit);
         1937  +    /* Compute a context or side-by-side diff into pOut */
         1938  +    if( diffFlags & DIFF_SIDEBYSIDE ){
         1939  +      sbsDiff(&c, pOut, pRe, diffFlags);
         1940  +    }else{
         1941  +      contextDiff(&c, pOut, pRe, diffFlags);
         1942  +    }
         1943  +    fossil_free(c.aFrom);
         1944  +    fossil_free(c.aTo);
         1945  +    fossil_free(c.aEdit);
   503   1946       return 0;
   504   1947     }else{
   505   1948       /* If a context diff is not requested, then return the
   506   1949       ** array of COPY/DELETE/INSERT triples.
   507   1950       */
   508   1951       free(c.aFrom);
   509   1952       free(c.aTo);
   510   1953       return c.aEdit;
   511   1954     }
   512   1955   }
         1956  +
         1957  +/*
         1958  +** Process diff-related command-line options and return an appropriate
         1959  +** "diffFlags" integer.
         1960  +**
         1961  +**   --brief                Show filenames only    DIFF_BRIEF
         1962  +**   --context|-c N         N lines of context.    DIFF_CONTEXT_MASK
         1963  +**   --html                 Format for HTML        DIFF_HTML
         1964  +**   --invert               Invert the diff        DIFF_INVERT
         1965  +**   --linenum|-n           Show line numbers      DIFF_LINENO
         1966  +**   --noopt                Disable optimization   DIFF_NOOPT
         1967  +**   --side-by-side|-y      Side-by-side diff.     DIFF_SIDEBYSIDE
         1968  +**   --unified              Unified diff.          ~DIFF_SIDEBYSIDE
         1969  +**   --width|-W N           N character lines.     DIFF_WIDTH_MASK
         1970  +*/
         1971  +u64 diff_options(void){
         1972  +  u64 diffFlags = 0;
         1973  +  const char *z;
         1974  +  int f;
         1975  +  if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
         1976  +  if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
         1977  +  if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
         1978  +    if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
         1979  +    diffFlags |= f + DIFF_CONTEXT_EX;
         1980  +  }
         1981  +  if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
         1982  +    f *= DIFF_CONTEXT_MASK+1;
         1983  +    if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
         1984  +    diffFlags |= f;
         1985  +  }
         1986  +  if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
         1987  +  if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
         1988  +  if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
         1989  +  if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
         1990  +  if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
         1991  +  return diffFlags;
         1992  +}
   513   1993   
   514   1994   /*
   515   1995   ** COMMAND: test-rawdiff
   516   1996   */
   517   1997   void test_rawdiff_cmd(void){
   518   1998     Blob a, b;
   519   1999     int r;
   520   2000     int i;
   521   2001     int *R;
         2002  +  u64 diffFlags = diff_options();
   522   2003     if( g.argc<4 ) usage("FILE1 FILE2 ...");
   523   2004     blob_read_from_file(&a, g.argv[2]);
   524   2005     for(i=3; i<g.argc; i++){
   525         -    if( i>3 ) printf("-------------------------------\n");
         2006  +    if( i>3 ) fossil_print("-------------------------------\n");
   526   2007       blob_read_from_file(&b, g.argv[i]);
   527         -    R = text_diff(&a, &b, 0, 0);
         2008  +    R = text_diff(&a, &b, 0, 0, diffFlags);
   528   2009       for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
   529         -      printf(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
         2010  +      fossil_print(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
   530   2011       }
   531   2012       /* free(R); */
   532   2013       blob_reset(&b);
   533   2014     }
   534   2015   }
   535   2016   
   536   2017   /*
   537         -** COMMAND: test-udiff
         2018  +** COMMAND: test-diff
         2019  +**
         2020  +** Usage: %fossil [options] FILE1 FILE2
         2021  +**
         2022  +** Print the difference between two files.  The usual diff options apply.
   538   2023   */
   539         -void test_udiff_cmd(void){
         2024  +void test_diff_cmd(void){
   540   2025     Blob a, b, out;
         2026  +  u64 diffFlag;
         2027  +  const char *zRe;           /* Regex filter for diff output */
         2028  +  ReCompiled *pRe = 0;       /* Regex filter for diff output */
         2029  +
         2030  +  if( find_option("tk",0,0)!=0 ){
         2031  +    diff_tk("test-diff", 2);
         2032  +    return;
         2033  +  }
         2034  +  find_option("i",0,0);
         2035  +  zRe = find_option("regexp","e",1);
         2036  +  if( zRe ){
         2037  +    const char *zErr = re_compile(&pRe, zRe, 0);
         2038  +    if( zErr ) fossil_fatal("regex error: %s", zErr);
         2039  +  }
         2040  +  diffFlag = diff_options();
         2041  +  verify_all_options();
   541   2042     if( g.argc!=4 ) usage("FILE1 FILE2");
         2043  +  diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
   542   2044     blob_read_from_file(&a, g.argv[2]);
   543   2045     blob_read_from_file(&b, g.argv[3]);
   544   2046     blob_zero(&out);
   545         -  text_diff(&a, &b, &out, 3);
         2047  +  text_diff(&a, &b, &out, pRe, diffFlag);
   546   2048     blob_write_to_file(&out, "-");
         2049  +  re_free(pRe);
   547   2050   }
   548   2051   
   549   2052   /**************************************************************************
   550   2053   ** The basic difference engine is above.  What follows is the annotation
   551   2054   ** engine.  Both are in the same file since they share many components.
   552   2055   */
   553   2056   
................................................................................
   554   2057   /*
   555   2058   ** The status of an annotation operation is recorded by an instance
   556   2059   ** of the following structure.
   557   2060   */
   558   2061   typedef struct Annotator Annotator;
   559   2062   struct Annotator {
   560   2063     DContext c;       /* The diff-engine context */
   561         -  struct {          /* Lines of the original files... */
         2064  +  struct AnnLine {  /* Lines of the original files... */
   562   2065       const char *z;       /* The text of the line */
   563         -    int n;               /* Number of bytes (omitting trailing space and \n) */
         2066  +    short int n;         /* Number of bytes (omitting trailing space and \n) */
         2067  +    short int iLevel;    /* Level at which tag was set */
   564   2068       const char *zSrc;    /* Tag showing origin of this line */
   565   2069     } *aOrig;
   566   2070     int nOrig;        /* Number of elements in aOrig[] */
   567   2071     int nNoSrc;       /* Number of entries where aOrig[].zSrc==NULL */
         2072  +  int iLevel;       /* Current level */
         2073  +  int nVers;        /* Number of versions analyzed */
         2074  +  char **azVers;    /* Names of versions analyzed */
   568   2075   };
   569   2076   
   570   2077   /*
   571   2078   ** Initialize the annotation process by specifying the file that is
   572   2079   ** to be annotated.  The annotator takes control of the input Blob and
   573   2080   ** will release it when it is finished with it.
   574   2081   */
   575   2082   static int annotation_start(Annotator *p, Blob *pInput){
   576   2083     int i;
   577   2084   
   578   2085     memset(p, 0, sizeof(*p));
   579         -  p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput), &p->c.nTo);
         2086  +  p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
   580   2087     if( p->c.aTo==0 ){
   581   2088       return 1;
   582   2089     }
   583         -  p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo );
   584         -  if( p->aOrig==0 ) fossil_panic("out of memory");
         2090  +  p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
   585   2091     for(i=0; i<p->c.nTo; i++){
   586   2092       p->aOrig[i].z = p->c.aTo[i].z;
   587   2093       p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
   588   2094       p->aOrig[i].zSrc = 0;
   589   2095     }
   590   2096     p->nOrig = p->c.nTo;
   591   2097     return 0;
................................................................................
   597   2103   ** if additional annotation is required.  zPName is the tag to insert
   598   2104   ** on each line of the file being annotated that was contributed by
   599   2105   ** pParent.  Memory to hold zPName is leaked.
   600   2106   */
   601   2107   static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
   602   2108     int i, j;
   603   2109     int lnTo;
         2110  +  int iPrevLevel;
         2111  +  int iThisLevel;
   604   2112   
   605   2113     /* Prepare the parent file to be diffed */
   606   2114     p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
   607         -                                &p->c.nFrom);
         2115  +                                &p->c.nFrom, 1);
   608   2116     if( p->c.aFrom==0 ){
   609   2117       return 1;
   610   2118     }
   611   2119   
   612   2120     /* Compute the differences going from pParent to the file being
   613   2121     ** annotated. */
   614   2122     diff_all(&p->c);
   615   2123   
   616   2124     /* Where new lines are inserted on this difference, record the
   617   2125     ** zPName as the source of the new line.
   618   2126     */
         2127  +  iPrevLevel = p->iLevel;
         2128  +  p->iLevel++;
         2129  +  iThisLevel = p->iLevel;
   619   2130     for(i=lnTo=0; i<p->c.nEdit; i+=3){
   620         -    for(j=0; j<p->c.aEdit[i]; j++, lnTo++){
   621         -      p->aOrig[lnTo].zSrc = zPName;
         2131  +    struct AnnLine *x = &p->aOrig[lnTo];
         2132  +    for(j=0; j<p->c.aEdit[i]; j++, lnTo++, x++){
         2133  +      if( x->zSrc==0 || x->iLevel==iPrevLevel ){
         2134  +         x->zSrc = zPName;
         2135  +         x->iLevel = iThisLevel;
         2136  +      }
   622   2137       }
   623   2138       lnTo += p->c.aEdit[i+2];
   624   2139     }
   625   2140   
   626   2141     /* Clear out the diff results */
   627         -  free(p->c.aEdit);
         2142  +  fossil_free(p->c.aEdit);
   628   2143     p->c.aEdit = 0;
   629   2144     p->c.nEdit = 0;
   630   2145     p->c.nEditAlloc = 0;
   631   2146   
   632   2147     /* Clear out the from file */
   633         -  free(p->c.aFrom);    
   634         -  blob_zero(pParent);
         2148  +  free(p->c.aFrom);
   635   2149   
   636   2150     /* Return no errors */
   637   2151     return 0;
   638   2152   }
   639   2153   
   640   2154   
   641   2155   /*
................................................................................
   659   2173       if( annotation_step(&x, &b, g.argv[i-1]) ){
   660   2174         fossil_fatal("binary file");
   661   2175       }
   662   2176     }
   663   2177     for(i=0; i<x.nOrig; i++){
   664   2178       const char *zSrc = x.aOrig[i].zSrc;
   665   2179       if( zSrc==0 ) zSrc = g.argv[g.argc-1];
   666         -    printf("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z);
         2180  +    fossil_print("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z);
   667   2181     }
   668   2182   }
   669   2183   
         2184  +/* Annotation flags */
         2185  +#define ANN_FILE_VERS  0x001  /* Show file version rather than commit version */
         2186  +
   670   2187   /*
   671   2188   ** Compute a complete annotation on a file.  The file is identified
   672   2189   ** by its filename number (filename.fnid) and the baseline in which
   673   2190   ** it was checked in (mlink.mid).
   674   2191   */
   675         -static void annotate_file(Annotator *p, int fnid, int mid, int webLabel){
   676         -  Blob toAnnotate;     /* Text of the final version of the file */
         2192  +static void annotate_file(
         2193  +  Annotator *p,        /* The annotator */
         2194  +  int fnid,            /* The name of the file to be annotated */
         2195  +  int mid,             /* Use the version of the file in this check-in */
         2196  +  int webLabel,        /* Use web-style annotations if true */
         2197  +  int iLimit,          /* Limit the number of levels if greater than zero */
         2198  +  int annFlags         /* Flags to alter the annotation */
         2199  +){
         2200  +  Blob toAnnotate;     /* Text of the final (mid) version of the file */
   677   2201     Blob step;           /* Text of previous revision */
   678   2202     int rid;             /* Artifact ID of the file being annotated */
   679   2203     char *zLabel;        /* Label to apply to a line */
   680   2204     Stmt q;              /* Query returning all ancestor versions */
         2205  +  int cnt = 0;         /* Number of versions examined */
   681   2206   
   682   2207     /* Initialize the annotation */
   683   2208     rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
   684   2209     if( rid==0 ){
   685   2210       fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
   686   2211     }
   687   2212     if( !content_get(rid, &toAnnotate) ){
   688   2213       fossil_panic("unable to retrieve content of artifact #%d", rid);
   689   2214     }
   690         -  db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
   691         -  compute_ancestors(mid, 1000000000);
         2215  +  if( iLimit<=0 ) iLimit = 1000000000;
   692   2216     annotation_start(p, &toAnnotate);
   693         -
   694         -  db_prepare(&q, 
   695         -    "SELECT mlink.fid, blob.uuid, date(event.mtime), "
   696         -    "       coalesce(event.euser,event.user) "
   697         -    "  FROM mlink, blob, event"
   698         -    " WHERE mlink.fnid=%d"
   699         -    "   AND mlink.mid IN ok"
   700         -    "   AND blob.rid=mlink.mid"
         2217  +  
         2218  +  db_prepare(&q,
         2219  +    "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
         2220  +    "       date(event.mtime),"
         2221  +    "       coalesce(event.euser,event.user),"
         2222  +    "       mlink.pid"
         2223  +    "  FROM mlink, event"
         2224  +    " WHERE mlink.fid=:rid"
   701   2225       "   AND event.objid=mlink.mid"
   702         -    " ORDER BY event.mtime DESC",
   703         -    fnid
         2226  +    " ORDER BY event.mtime",
         2227  +    (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
   704   2228     );
   705         -  while( db_step(&q)==SQLITE_ROW ){
   706         -    int pid = db_column_int(&q, 0);
   707         -    const char *zUuid = db_column_text(&q, 1);
   708         -    const char *zDate = db_column_text(&q, 2);
   709         -    const char *zUser = db_column_text(&q, 3);
         2229  +  
         2230  +  db_bind_int(&q, ":rid", rid);
         2231  +  if( iLimit==0 ) iLimit = 1000000000;
         2232  +  while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
         2233  +    const char *zUuid = db_column_text(&q, 0);
         2234  +    const char *zDate = db_column_text(&q, 1);
         2235  +    const char *zUser = db_column_text(&q, 2);
         2236  +    int prevId = db_column_int(&q, 3);
   710   2237       if( webLabel ){
   711         -      zLabel = mprintf("<a href='%s/info/%s'>%.10s</a> %s %9.9s", 
   712         -                       g.zBaseURL, zUuid, zUuid, zDate, zUser);
         2238  +      zLabel = mprintf(
         2239  +          "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
         2240  +          zUuid, zUuid, zDate, zUser
         2241  +      );
   713   2242       }else{
   714         -      zLabel = mprintf("%.10s %s %9.9s", zUuid, zDate, zUser);
         2243  +      zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
   715   2244       }
   716         -    content_get(pid, &step);
         2245  +    p->nVers++;
         2246  +    p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
         2247  +    p->azVers[p->nVers-1] = zLabel;
         2248  +    content_get(rid, &step);
   717   2249       annotation_step(p, &step, zLabel);
   718   2250       blob_reset(&step);
         2251  +    db_reset(&q);
         2252  +    rid = prevId;
         2253  +    db_bind_int(&q, ":rid", prevId);
         2254  +    cnt++;
   719   2255     }
   720   2256     db_finalize(&q);
   721   2257   }
   722   2258   
   723   2259   /*
   724   2260   ** WEBPAGE: annotate
   725   2261   **
................................................................................
   728   2264   **    checkin=ID          The manifest ID at which to start the annotation
   729   2265   **    filename=FILENAME   The filename.
   730   2266   */
   731   2267   void annotation_page(void){
   732   2268     int mid;
   733   2269     int fnid;
   734   2270     int i;
         2271  +  int iLimit;
         2272  +  int annFlags = 0;
         2273  +  int showLn = 0;        /* True if line numbers should be shown */
         2274  +  char zLn[10];          /* Line number buffer */
         2275  +  char zFormat[10];      /* Format string for line numbers */
   735   2276     Annotator ann;
   736   2277   
         2278  +  showLn = P("ln")!=0;
   737   2279     login_check_credentials();
   738         -  if( !g.okRead ){ login_needed(); return; }
   739         -  mid = name_to_rid(PD("checkin","0"));
         2280  +  if( !g.perm.Read ){ login_needed(); return; }
         2281  +  mid = name_to_typed_rid(PD("checkin","0"),"ci");
   740   2282     fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", P("filename"));
   741   2283     if( mid==0 || fnid==0 ){ fossil_redirect_home(); }
         2284  +  iLimit = atoi(PD("limit","-1"));
   742   2285     if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid) ){
   743   2286       fossil_redirect_home();
   744   2287     }
   745   2288     style_header("File Annotation");
   746         -  annotate_file(&ann, fnid, mid, g.okHistory);
         2289  +  if( P("filevers") ) annFlags |= ANN_FILE_VERS;
         2290  +  annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
         2291  +  if( P("log") ){
         2292  +    int i;
         2293  +    @ <h2>Versions analyzed:</h2>
         2294  +    @ <ol>
         2295  +    for(i=0; i<ann.nVers; i++){
         2296  +      @ <li><tt>%s(ann.azVers[i])</tt></li>
         2297  +    }
         2298  +    @ </ol>
         2299  +    @ <hr>
         2300  +    @ <h2>Annotation:</h2>
         2301  +  }
         2302  +  if( showLn ){
         2303  +    sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
         2304  +    sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn));
         2305  +  }else{
         2306  +    zLn[0] = 0;
         2307  +  }
   747   2308     @ <pre>
   748   2309     for(i=0; i<ann.nOrig; i++){
   749   2310       ((char*)ann.aOrig[i].z)[ann.aOrig[i].n] = 0;
   750         -    @ %s(ann.aOrig[i].zSrc): %h(ann.aOrig[i].z)
         2311  +    if( showLn ) sqlite3_snprintf(sizeof(zLn), zLn, zFormat, i+1);
         2312  +    @ %s(ann.aOrig[i].zSrc):%s(zLn) %h(ann.aOrig[i].z)
   751   2313     }
   752   2314     @ </pre>
   753   2315     style_footer();
   754   2316   }
   755   2317   
   756   2318   /*
   757   2319   ** COMMAND: annotate
   758   2320   **
   759         -** %fossil annotate FILENAME
         2321  +** %fossil annotate ?OPTIONS? FILENAME
   760   2322   **
   761   2323   ** Output the text of a file with markings to show when each line of
   762         -** the file was introduced.
         2324  +** the file was last modified.
         2325  +**
         2326  +** Options:
         2327  +**   --limit N       Only look backwards in time by N versions
         2328  +**   --log           List all versions analyzed
         2329  +**   --filevers      Show file version numbers rather than check-in versions
         2330  +**
         2331  +** See also: info, finfo, timeline
   763   2332   */
   764   2333   void annotate_cmd(void){
   765   2334     int fnid;         /* Filename ID */
   766   2335     int fid;          /* File instance ID */
   767   2336     int mid;          /* Manifest where file was checked in */
         2337  +  int cid;          /* Checkout ID */
   768   2338     Blob treename;    /* FILENAME translated to canonical form */
   769         -  char *zFilename;  /* Cannonical filename */
         2339  +  char *zFilename;  /* Canonical filename */
   770   2340     Annotator ann;    /* The annotation of the file */
   771   2341     int i;            /* Loop counter */
         2342  +  const char *zLimit; /* The value to the --limit option */
         2343  +  int iLimit;       /* How far back in time to look */
         2344  +  int showLog;      /* True to show the log */
         2345  +  int fileVers;     /* Show file version instead of check-in versions */
         2346  +  int annFlags = 0; /* Flags to control annotation properties */
   772   2347   
         2348  +  zLimit = find_option("limit",0,1);
         2349  +  if( zLimit==0 || zLimit[0]==0 ) zLimit = "-1";
         2350  +  iLimit = atoi(zLimit);
         2351  +  showLog = find_option("log",0,0)!=0;
         2352  +  fileVers = find_option("filevers",0,0)!=0;
   773   2353     db_must_be_within_tree();
   774   2354     if (g.argc<3) {
   775   2355       usage("FILENAME");
   776   2356     }
   777   2357     file_tree_name(g.argv[2], &treename, 1);
   778   2358     zFilename = blob_str(&treename);
   779   2359     fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
................................................................................
   780   2360     if( fnid==0 ){
   781   2361       fossil_fatal("no such file: %s", zFilename);
   782   2362     }
   783   2363     fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename);
   784   2364     if( fid==0 ){
   785   2365       fossil_fatal("not part of current checkout: %s", zFilename);
   786   2366     }
   787         -  mid = db_int(0, "SELECT mid FROM mlink WHERE fid=%d AND fnid=%d", fid, fnid);
         2367  +  cid = db_lget_int("checkout", 0);
         2368  +  if (cid == 0){
         2369  +    fossil_fatal("Not in a checkout");
         2370  +  }
         2371  +  if( iLimit<=0 ) iLimit = 1000000000;
         2372  +  compute_direct_ancestors(cid, iLimit);
         2373  +  mid = db_int(0, "SELECT mlink.mid FROM mlink, ancestor "
         2374  +          " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid"
         2375  +          " ORDER BY ancestor.generation ASC LIMIT 1",
         2376  +          fid, fnid);
   788   2377     if( mid==0 ){
   789   2378       fossil_panic("unable to find manifest");
   790   2379     }
   791         -  annotate_file(&ann, fnid, mid, 0);
         2380  +  if( fileVers ) annFlags |= ANN_FILE_VERS;
         2381  +  annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
         2382  +  if( showLog ){
         2383  +    for(i=0; i<ann.nVers; i++){
         2384  +      printf("version %3d: %s\n", i+1, ann.azVers[i]);
         2385  +    }
         2386  +    printf("---------------------------------------------------\n");
         2387  +  }
   792   2388     for(i=0; i<ann.nOrig; i++){
   793         -    printf("%s: %.*s\n", ann.aOrig[i].zSrc, ann.aOrig[i].n, ann.aOrig[i].z);
         2389  +    fossil_print("%s: %.*s\n",
         2390  +                 ann.aOrig[i].zSrc, ann.aOrig[i].n, ann.aOrig[i].z);
   794   2391     }
   795   2392   }

Changes to src/diffcmd.c.

    18     18   ** This file contains code used to implement the "diff" command
    19     19   */
    20     20   #include "config.h"
    21     21   #include "diffcmd.h"
    22     22   #include <assert.h>
    23     23   
    24     24   /*
    25         -** Shell-escape the given string.  Append the result to a blob.
           25  +** Use the right null device for the platform.
    26     26   */
    27         -static void shell_escape(Blob *pBlob, const char *zIn){
    28         -  int n = blob_size(pBlob);
    29         -  int k = strlen(zIn);
    30         -  int i, c;
    31         -  char *z;
    32         -  for(i=0; (c = zIn[i])!=0; i++){
    33         -    if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
    34         -      blob_appendf(pBlob, "\"%s\"", zIn);
    35         -      z = blob_buffer(pBlob);
    36         -      for(i=n+1; i<=n+k; i++){
    37         -        if( z[i]=='"' ) z[i] = '_';
    38         -      }
    39         -      return;
    40         -    }
    41         -  }
    42         -  blob_append(pBlob, zIn, -1);
    43         -}
    44         -
    45         -/*
    46         -** This function implements a cross-platform "system()" interface.
    47         -*/
    48         -int portable_system(const char *zOrigCmd){
    49         -  int rc;
    50         -#ifdef __MINGW32__
    51         -  /* On windows, we have to put double-quotes around the entire command.
    52         -  ** Who knows why - this is just the way windows works.
    53         -  */
    54         -  char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
    55         -  rc = system(zNewCmd);
    56         -  free(zNewCmd);
           27  +#if defined(_WIN32)
           28  +#  define NULL_DEVICE "NUL"
    57     29   #else
    58         -  /* On unix, evaluate the command directly.
    59         -  */
    60         -  rc = system(zOrigCmd);
    61         -#endif 
    62         -  return rc; 
           30  +#  define NULL_DEVICE "/dev/null"
           31  +#endif
           32  +
           33  +/*
           34  +** Print the "Index:" message that patches wants to see at the top of a diff.
           35  +*/
           36  +void diff_print_index(const char *zFile, u64 diffFlags){
           37  +  if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){
           38  +    char *z = mprintf("Index: %s\n%.66c\n", zFile, '=');
           39  +    fossil_print("%s", z);
           40  +    fossil_free(z);
           41  +  }
           42  +}
           43  +
           44  +/*
           45  +** Print the +++/--- filename lines for a diff operation.
           46  +*/
           47  +void diff_print_filenames(const char *zLeft, const char *zRight, u64 diffFlags){
           48  +  char *z = 0;
           49  +  if( diffFlags & DIFF_BRIEF ){
           50  +    /* no-op */
           51  +  }else if( diffFlags & DIFF_SIDEBYSIDE ){
           52  +    int w = diff_width(diffFlags);
           53  +    int n1 = strlen(zLeft);
           54  +    int x;
           55  +    if( n1>w*2 ) n1 = w*2;
           56  +    x = w*2+17 - (n1+2);
           57  +    z = mprintf("%.*c %.*s %.*c\n",
           58  +                x/2, '=', n1, zLeft, (x+1)/2, '=');
           59  +  }else{
           60  +    z = mprintf("--- %s\n+++ %s\n", zLeft, zRight);
           61  +  }
           62  +  fossil_print("%s", z);
           63  +  fossil_free(z);
    63     64   }
    64     65   
    65     66   /*
    66     67   ** Show the difference between two files, one in memory and one on disk.
    67     68   **
    68     69   ** The difference is the set of edits needed to transform pFile1 into
    69     70   ** zFile2.  The content of pFile1 is in memory.  zFile2 exists on disk.
    70     71   **
    71     72   ** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
    72     73   ** command zDiffCmd to do the diffing.
           74  +**
           75  +** When using an external diff program, zBinGlob contains the GLOB patterns
           76  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
           77  +** will be skipped in addition to files that may contain binary content.
    73     78   */
    74         -static void diff_file(
           79  +void diff_file(
    75     80     Blob *pFile1,             /* In memory content to compare from */
           81  +  int isBin1,               /* Does the 'from' content appear to be binary */
    76     82     const char *zFile2,       /* On disk content to compare to */
    77     83     const char *zName,        /* Display name of the file */
    78         -  const char *zDiffCmd      /* Command for comparison */
           84  +  const char *zDiffCmd,     /* Command for comparison */
           85  +  const char *zBinGlob,     /* Treat file names matching this as binary */
           86  +  int fIncludeBinary,       /* Include binary files for external diff */
           87  +  u64 diffFlags             /* Flags to control the diff */
    79     88   ){
    80     89     if( zDiffCmd==0 ){
    81         -    Blob out;      /* Diff output text */
    82         -    Blob file2;    /* Content of zFile2 */
           90  +    Blob out;                 /* Diff output text */
           91  +    Blob file2;               /* Content of zFile2 */
           92  +    const char *zName2;       /* Name of zFile2 for display */
    83     93   
    84     94       /* Read content of zFile2 into memory */
    85     95       blob_zero(&file2);
    86         -    blob_read_from_file(&file2, zFile2);
           96  +    if( file_wd_size(zFile2)<0 ){
           97  +      zName2 = NULL_DEVICE;
           98  +    }else{
           99  +      if( file_wd_islink(zFile2) ){
          100  +        blob_read_link(&file2, zFile2);
          101  +      }else{
          102  +        blob_read_from_file(&file2, zFile2);
          103  +      }
          104  +      zName2 = zName;
          105  +    }
    87    106   
    88    107       /* Compute and output the differences */
    89         -    blob_zero(&out);
    90         -    text_diff(pFile1, &file2, &out, 5);
    91         -    printf("--- %s\n+++ %s\n", zName, zName);
    92         -    printf("%s\n", blob_str(&out));
          108  +    if( diffFlags & DIFF_BRIEF ){
          109  +      if( blob_compare(pFile1, &file2) ){
          110  +        fossil_print("CHANGED  %s\n", zName);
          111  +      }
          112  +    }else{
          113  +      blob_zero(&out);
          114  +      text_diff(pFile1, &file2, &out, 0, diffFlags);
          115  +      if( blob_size(&out) ){
          116  +        diff_print_filenames(zName, zName2, diffFlags);
          117  +        fossil_print("%s\n", blob_str(&out));
          118  +      }
          119  +      blob_reset(&out);
          120  +    }
    93    121   
    94    122       /* Release memory resources */
    95    123       blob_reset(&file2);
    96         -    blob_reset(&out);
    97    124     }else{
    98    125       int cnt = 0;
    99    126       Blob nameFile1;    /* Name of temporary file to old pFile1 content */
   100    127       Blob cmd;          /* Text of command to run */
          128  +
          129  +    if( !fIncludeBinary ){
          130  +      Blob file2;
          131  +      if( isBin1 ){
          132  +        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          133  +        return;
          134  +      }
          135  +      if( zBinGlob ){
          136  +        Glob *pBinary = glob_create(zBinGlob);
          137  +        if( glob_match(pBinary, zName) ){
          138  +          fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          139  +          glob_free(pBinary);
          140  +          return;
          141  +        }
          142  +        glob_free(pBinary);
          143  +      }
          144  +      blob_zero(&file2);
          145  +      if( file_wd_size(zFile2)>=0 ){
          146  +        if( file_wd_islink(zFile2) ){
          147  +          blob_read_link(&file2, zFile2);
          148  +        }else{
          149  +          blob_read_from_file(&file2, zFile2);
          150  +        }
          151  +      }
          152  +      if( looks_like_binary(&file2) ){
          153  +        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          154  +        blob_reset(&file2);
          155  +        return;
          156  +      }
          157  +      blob_reset(&file2);
          158  +    }
   101    159   
   102    160       /* Construct a temporary file to hold pFile1 based on the name of
   103    161       ** zFile2 */
   104    162       blob_zero(&nameFile1);
   105    163       do{
   106    164         blob_reset(&nameFile1);
   107    165         blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++);
   108         -    }while( access(blob_str(&nameFile1),0)==0 );
          166  +    }while( file_access(blob_str(&nameFile1),0)==0 );
   109    167       blob_write_to_file(pFile1, blob_str(&nameFile1));
   110    168   
   111    169       /* Construct the external diff command */
   112    170       blob_zero(&cmd);
   113    171       blob_appendf(&cmd, "%s ", zDiffCmd);
   114    172       shell_escape(&cmd, blob_str(&nameFile1));
   115    173       blob_append(&cmd, " ", 1);
   116    174       shell_escape(&cmd, zFile2);
   117    175   
   118    176       /* Run the external diff command */
   119         -    portable_system(blob_str(&cmd));
          177  +    fossil_system(blob_str(&cmd));
   120    178   
   121    179       /* Delete the temporary file and clean up memory used */
   122         -    unlink(blob_str(&nameFile1));
          180  +    file_delete(blob_str(&nameFile1));
   123    181       blob_reset(&nameFile1);
   124    182       blob_reset(&cmd);
   125    183     }
   126    184   }
   127    185   
   128    186   /*
   129    187   ** Show the difference between two files, both in memory.
   130    188   **
   131    189   ** The difference is the set of edits needed to transform pFile1 into
   132    190   ** pFile2.
   133    191   **
   134    192   ** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
   135    193   ** command zDiffCmd to do the diffing.
          194  +**
          195  +** When using an external diff program, zBinGlob contains the GLOB patterns
          196  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          197  +** will be skipped in addition to files that may contain binary content.
   136    198   */
   137         -static void diff_file_mem(
          199  +void diff_file_mem(
   138    200     Blob *pFile1,             /* In memory content to compare from */
   139    201     Blob *pFile2,             /* In memory content to compare to */
          202  +  int isBin1,               /* Does the 'from' content appear to be binary */
          203  +  int isBin2,               /* Does the 'to' content appear to be binary */
   140    204     const char *zName,        /* Display name of the file */
   141         -  const char *zDiffCmd      /* Command for comparison */
          205  +  const char *zDiffCmd,     /* Command for comparison */
          206  +  const char *zBinGlob,     /* Treat file names matching this as binary */
          207  +  int fIncludeBinary,       /* Include binary files for external diff */
          208  +  u64 diffFlags             /* Diff flags */
   142    209   ){
          210  +  if( diffFlags & DIFF_BRIEF ) return;
   143    211     if( zDiffCmd==0 ){
   144    212       Blob out;      /* Diff output text */
   145    213   
   146    214       blob_zero(&out);
   147         -    text_diff(pFile1, pFile2, &out, 5);
   148         -    printf("--- %s\n+++ %s\n", zName, zName);
   149         -    printf("%s\n", blob_str(&out));
          215  +    text_diff(pFile1, pFile2, &out, 0, diffFlags);
          216  +    diff_print_filenames(zName, zName, diffFlags);
          217  +    fossil_print("%s\n", blob_str(&out));
   150    218   
   151    219       /* Release memory resources */
   152    220       blob_reset(&out);
   153    221     }else{
   154    222       Blob cmd;
   155    223       char zTemp1[300];
   156    224       char zTemp2[300];
          225  +
          226  +    if( !fIncludeBinary ){
          227  +      if( isBin1 || isBin2 ){
          228  +        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          229  +        return;
          230  +      }
          231  +      if( zBinGlob ){
          232  +        Glob *pBinary = glob_create(zBinGlob);
          233  +        if( glob_match(pBinary, zName) ){
          234  +          fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          235  +          glob_free(pBinary);
          236  +          return;
          237  +        }
          238  +        glob_free(pBinary);
          239  +      }
          240  +    }
   157    241   
   158    242       /* Construct a temporary file names */
   159    243       file_tempname(sizeof(zTemp1), zTemp1);
   160    244       file_tempname(sizeof(zTemp2), zTemp2);
   161    245       blob_write_to_file(pFile1, zTemp1);
   162    246       blob_write_to_file(pFile2, zTemp2);
   163    247   
................................................................................
   165    249       blob_zero(&cmd);
   166    250       blob_appendf(&cmd, "%s ", zDiffCmd);
   167    251       shell_escape(&cmd, zTemp1);
   168    252       blob_append(&cmd, " ", 1);
   169    253       shell_escape(&cmd, zTemp2);
   170    254   
   171    255       /* Run the external diff command */
   172         -    portable_system(blob_str(&cmd));
          256  +    fossil_system(blob_str(&cmd));
   173    257   
   174    258       /* Delete the temporary file and clean up memory used */
   175         -    unlink(zTemp1);
   176         -    unlink(zTemp2);
          259  +    file_delete(zTemp1);
          260  +    file_delete(zTemp2);
   177    261       blob_reset(&cmd);
   178    262     }
   179    263   }
   180    264   
   181    265   /*
   182         -** Do a diff against a single file named in g.argv[2] from version zFrom
          266  +** Do a diff against a single file named in zFileTreeName from version zFrom
   183    267   ** against the same file on disk.
          268  +**
          269  +** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
          270  +** command zDiffCmd to do the diffing.
          271  +**
          272  +** When using an external diff program, zBinGlob contains the GLOB patterns
          273  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          274  +** will be skipped in addition to files that may contain binary content.
   184    275   */
   185         -static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){
          276  +static void diff_one_against_disk(
          277  +  const char *zFrom,        /* Name of file */
          278  +  const char *zDiffCmd,     /* Use this "diff" command */
          279  +  const char *zBinGlob,     /* Treat file names matching this as binary */
          280  +  int fIncludeBinary,       /* Include binary files for external diff */
          281  +  u64 diffFlags,            /* Diff control flags */
          282  +  const char *zFileTreeName
          283  +){
   186    284     Blob fname;
   187    285     Blob content;
   188         -  file_tree_name(g.argv[2], &fname, 1);
   189         -  historical_version_of_file(zFrom, blob_str(&fname), &content, 0);
   190         -  diff_file(&content, g.argv[2], g.argv[2], zDiffCmd);
          286  +  int isLink;
          287  +  int isBin;
          288  +  file_tree_name(zFileTreeName, &fname, 1);
          289  +  historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
          290  +                             fIncludeBinary ? 0 : &isBin, 0);
          291  +  if( !isLink != !file_wd_islink(zFrom) ){
          292  +    fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
          293  +  }else{
          294  +    diff_file(&content, isBin, zFileTreeName, zFileTreeName,
          295  +              zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
          296  +  }
   191    297     blob_reset(&content);
   192    298     blob_reset(&fname);
   193    299   }
   194    300   
   195    301   /*
   196    302   ** Run a diff between the version zFrom and files on disk.  zFrom might
   197    303   ** be NULL which means to simply show the difference between the edited
   198    304   ** files on disk and the check-out on which they are based.
          305  +**
          306  +** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
          307  +** command zDiffCmd to do the diffing.
          308  +**
          309  +** When using an external diff program, zBinGlob contains the GLOB patterns
          310  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          311  +** will be skipped in addition to files that may contain binary content.
   199    312   */
   200         -static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
          313  +static void diff_all_against_disk(
          314  +  const char *zFrom,        /* Version to difference from */
          315  +  const char *zDiffCmd,     /* Use this diff command.  NULL for built-in */
          316  +  const char *zBinGlob,     /* Treat file names matching this as binary */
          317  +  int fIncludeBinary,       /* Treat file names matching this as binary */
          318  +  u64 diffFlags             /* Flags controlling diff output */
          319  +){
   201    320     int vid;
   202    321     Blob sql;
   203    322     Stmt q;
          323  +  int asNewFile;            /* Treat non-existant files as empty files */
   204    324   
          325  +  asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
   205    326     vid = db_lget_int("checkout", 0);
   206         -  vfile_check_signature(vid, 1);
          327  +  vfile_check_signature(vid, CKSIG_ENOTFILE);
   207    328     blob_zero(&sql);
   208    329     db_begin_transaction();
   209    330     if( zFrom ){
   210         -    int rid = name_to_rid(zFrom);
          331  +    int rid = name_to_typed_rid(zFrom, "ci");
   211    332       if( !is_a_version(rid) ){
   212    333         fossil_fatal("no such check-in: %s", zFrom);
   213    334       }
   214    335       load_vfile_from_rid(rid);
   215    336       blob_appendf(&sql,
   216         -      "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid"
          337  +      "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
   217    338         "  FROM vfile v1, vfile v2 "
   218    339         " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
   219         -      "   AND (v2.deleted OR v2.chnged OR v1.rid!=v2.rid)"
          340  +      "   AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
   220    341         "UNION "
   221         -      "SELECT pathname, 1, 0, 0, 0"
          342  +      "SELECT pathname, 1, 0, 0, 0, islink"
   222    343         "  FROM vfile v1"
   223    344         " WHERE v1.vid=%d"
   224    345         "   AND NOT EXISTS(SELECT 1 FROM vfile v2"
   225    346                           " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
   226    347         "UNION "
   227         -      "SELECT pathname, 0, 0, 1, 0"
          348  +      "SELECT pathname, 0, 0, 1, 0, islink"
   228    349         "  FROM vfile v2"
   229    350         " WHERE v2.vid=%d"
   230    351         "   AND NOT EXISTS(SELECT 1 FROM vfile v1"
   231    352                           " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
   232    353         " ORDER BY 1",
   233    354         rid, vid, rid, vid, vid, rid
   234    355       );
   235    356     }else{
   236    357       blob_appendf(&sql,
   237         -      "SELECT pathname, deleted, chnged , rid==0, rid"
          358  +      "SELECT pathname, deleted, chnged , rid==0, rid, islink"
   238    359         "  FROM vfile"
   239    360         " WHERE vid=%d"
   240    361         "   AND (deleted OR chnged OR rid==0)"
   241    362         " ORDER BY pathname",
   242    363         vid
   243    364       );
   244    365     }
   245    366     db_prepare(&q, blob_str(&sql));
   246    367     while( db_step(&q)==SQLITE_ROW ){
   247    368       const char *zPathname = db_column_text(&q,0);
   248    369       int isDeleted = db_column_int(&q, 1);
   249    370       int isChnged = db_column_int(&q,2);
   250    371       int isNew = db_column_int(&q,3);
          372  +    int srcid = db_column_int(&q, 4);
          373  +    int isLink = db_column_int(&q, 5);
   251    374       char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
          375  +    char *zToFree = zFullName;
          376  +    int showDiff = 1;
   252    377       if( isDeleted ){
   253         -      printf("DELETED  %s\n", zPathname);
   254         -    }else if( access(zFullName, 0) ){
   255         -      printf("MISSING  %s\n", zPathname);
          378  +      fossil_print("DELETED  %s\n", zPathname);
          379  +      if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
          380  +    }else if( file_access(zFullName, 0) ){
          381  +      fossil_print("MISSING  %s\n", zPathname);
          382  +      if( !asNewFile ){ showDiff = 0; }
   256    383       }else if( isNew ){
   257         -      printf("ADDED    %s\n", zPathname);
   258         -    }else if( isDeleted ){
   259         -      printf("DELETED  %s\n", zPathname);
          384  +      fossil_print("ADDED    %s\n", zPathname);
          385  +      srcid = 0;
          386  +      if( !asNewFile ){ showDiff = 0; }
   260    387       }else if( isChnged==3 ){
   261         -      printf("ADDED_BY_MERGE %s\n", zPathname);
   262         -    }else{
   263         -      int srcid = db_column_int(&q, 4);
          388  +      fossil_print("ADDED_BY_MERGE %s\n", zPathname);
          389  +      srcid = 0;
          390  +      if( !asNewFile ){ showDiff = 0; }
          391  +    }
          392  +    if( showDiff ){
   264    393         Blob content;
   265         -      content_get(srcid, &content);
   266         -      printf("Index: %s\n======================================="
   267         -             "============================\n",
   268         -             zPathname
   269         -      );
   270         -      diff_file(&content, zFullName, zPathname, zDiffCmd);
          394  +      int isBin;
          395  +      if( !isLink != !file_wd_islink(zFullName) ){
          396  +        diff_print_index(zPathname, diffFlags);
          397  +        diff_print_filenames(zPathname, zPathname, diffFlags);
          398  +        fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
          399  +        continue;
          400  +      }
          401  +      if( srcid>0 ){
          402  +        content_get(srcid, &content);
          403  +      }else{
          404  +        blob_zero(&content);
          405  +      }
          406  +      isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
          407  +      diff_print_index(zPathname, diffFlags);
          408  +      diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
          409  +                zBinGlob, fIncludeBinary, diffFlags);
   271    410         blob_reset(&content);
   272    411       }
   273         -    free(zFullName);
          412  +    free(zToFree);
   274    413     }
   275    414     db_finalize(&q);
   276         -  db_end_transaction(1);
          415  +  db_end_transaction(1);  /* ROLLBACK */
   277    416   }
   278    417   
   279    418   /*
   280    419   ** Output the differences between two versions of a single file.
   281    420   ** zFrom and zTo are the check-ins containing the two file versions.
   282         -** The filename is contained in g.argv[2].
          421  +**
          422  +** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
          423  +** command zDiffCmd to do the diffing.
          424  +**
          425  +** When using an external diff program, zBinGlob contains the GLOB patterns
          426  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          427  +** will be skipped in addition to files that may contain binary content.
   283    428   */
   284    429   static void diff_one_two_versions(
   285    430     const char *zFrom,
   286    431     const char *zTo,
   287         -  const char *zDiffCmd
          432  +  const char *zDiffCmd,
          433  +  const char *zBinGlob,
          434  +  int fIncludeBinary,
          435  +  u64 diffFlags,
          436  +  const char *zFileTreeName
   288    437   ){
   289    438     char *zName;
   290    439     Blob fname;
   291    440     Blob v1, v2;
   292         -  file_tree_name(g.argv[2], &fname, 1);
          441  +  int isLink1, isLink2;
          442  +  int isBin1, isBin2;
          443  +  if( diffFlags & DIFF_BRIEF ) return;
          444  +  file_tree_name(zFileTreeName, &fname, 1);
   293    445     zName = blob_str(&fname);
   294         -  historical_version_of_file(zFrom, zName, &v1, 0);
   295         -  historical_version_of_file(zTo, zName, &v2, 0);
   296         -  diff_file_mem(&v1, &v2, zName, zDiffCmd);
          446  +  historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
          447  +                             fIncludeBinary ? 0 : &isBin1, 0);
          448  +  historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
          449  +                             fIncludeBinary ? 0 : &isBin2, 0);
          450  +  if( isLink1 != isLink2 ){
          451  +    diff_print_filenames(zName, zName, diffFlags);
          452  +    fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
          453  +  }else{
          454  +    diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
          455  +                  zBinGlob, fIncludeBinary, diffFlags);
          456  +  }
   297    457     blob_reset(&v1);
   298    458     blob_reset(&v2);
   299    459     blob_reset(&fname);
   300    460   }
          461  +
          462  +/*
          463  +** Show the difference between two files identified by ManifestFile
          464  +** entries.
          465  +**
          466  +** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
          467  +** command zDiffCmd to do the diffing.
          468  +**
          469  +** When using an external diff program, zBinGlob contains the GLOB patterns
          470  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          471  +** will be skipped in addition to files that may contain binary content.
          472  +*/
          473  +static void diff_manifest_entry(
          474  +  struct ManifestFile *pFrom,
          475  +  struct ManifestFile *pTo,
          476  +  const char *zDiffCmd,
          477  +  const char *zBinGlob,
          478  +  int fIncludeBinary,
          479  +  u64 diffFlags
          480  +){
          481  +  Blob f1, f2;
          482  +  int isBin1, isBin2;
          483  +  int rid;
          484  +  const char *zName =  pFrom ? pFrom->zName : pTo->zName;
          485  +  if( diffFlags & DIFF_BRIEF ) return;
          486  +  diff_print_index(zName, diffFlags);
          487  +  if( pFrom ){
          488  +    rid = uuid_to_rid(pFrom->zUuid, 0);
          489  +    content_get(rid, &f1);
          490  +  }else{
          491  +    blob_zero(&f1);
          492  +  }
          493  +  if( pTo ){
          494  +    rid = uuid_to_rid(pTo->zUuid, 0);
          495  +    content_get(rid, &f2);
          496  +  }else{
          497  +    blob_zero(&f2);
          498  +  }
          499  +  isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
          500  +  isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
          501  +  diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
          502  +                zBinGlob, fIncludeBinary, diffFlags);
          503  +  blob_reset(&f1);
          504  +  blob_reset(&f2);
          505  +}
   301    506   
   302    507   /*
   303    508   ** Output the differences between two check-ins.
          509  +**
          510  +** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
          511  +** command zDiffCmd to do the diffing.
          512  +**
          513  +** When using an external diff program, zBinGlob contains the GLOB patterns
          514  +** for file names to treat as binary.  If fIncludeBinary is zero, these files
          515  +** will be skipped in addition to files that may contain binary content.
   304    516   */
   305    517   static void diff_all_two_versions(
   306    518     const char *zFrom,
   307    519     const char *zTo,
   308         -  const char *zDiffCmd
          520  +  const char *zDiffCmd,
          521  +  const char *zBinGlob,
          522  +  int fIncludeBinary,
          523  +  u64 diffFlags
   309    524   ){
          525  +  Manifest *pFrom, *pTo;
          526  +  ManifestFile *pFromFile, *pToFile;
          527  +  int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
          528  +
          529  +  pFrom = manifest_get_by_name(zFrom, 0);
          530  +  manifest_file_rewind(pFrom);
          531  +  pFromFile = manifest_file_next(pFrom,0);
          532  +  pTo = manifest_get_by_name(zTo, 0);
          533  +  manifest_file_rewind(pTo);
          534  +  pToFile = manifest_file_next(pTo,0);
          535  +
          536  +  while( pFromFile || pToFile ){
          537  +    int cmp;
          538  +    if( pFromFile==0 ){
          539  +      cmp = +1;
          540  +    }else if( pToFile==0 ){
          541  +      cmp = -1;
          542  +    }else{
          543  +      cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
          544  +    }
          545  +    if( cmp<0 ){
          546  +      fossil_print("DELETED %s\n", pFromFile->zName);
          547  +      if( asNewFlag ){
          548  +        diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
          549  +                            fIncludeBinary, diffFlags);
          550  +      }
          551  +      pFromFile = manifest_file_next(pFrom,0);
          552  +    }else if( cmp>0 ){
          553  +      fossil_print("ADDED   %s\n", pToFile->zName);
          554  +      if( asNewFlag ){
          555  +        diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
          556  +                            fIncludeBinary, diffFlags);
          557  +      }
          558  +      pToFile = manifest_file_next(pTo,0);
          559  +    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
          560  +      /* No changes */
          561  +      pFromFile = manifest_file_next(pFrom,0);
          562  +      pToFile = manifest_file_next(pTo,0);
          563  +    }else{
          564  +      if( diffFlags & DIFF_BRIEF ){
          565  +        fossil_print("CHANGED %s\n", pFromFile->zName);
          566  +      }else{
          567  +        diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
          568  +                            fIncludeBinary, diffFlags);
          569  +      }
          570  +      pFromFile = manifest_file_next(pFrom,0);
          571  +      pToFile = manifest_file_next(pTo,0);
          572  +    }
          573  +  }
          574  +  manifest_destroy(pFrom);
          575  +  manifest_destroy(pTo);
          576  +}
          577  +
          578  +/*
          579  +** Return the name of the external diff command, or return NULL if
          580  +** no external diff command is defined.
          581  +*/
          582  +const char *diff_command_external(int guiDiff){
          583  +  char *zDefault;
          584  +  const char *zName;
          585  +
          586  +  if( guiDiff ){
          587  +#if defined(_WIN32)
          588  +    zDefault = "WinDiff.exe";
          589  +#else
          590  +    zDefault = 0;
          591  +#endif
          592  +    zName = "gdiff-command";
          593  +  }else{
          594  +    zDefault = 0;
          595  +    zName = "diff-command";
          596  +  }
          597  +  return db_get(zName, zDefault);
          598  +}
          599  +
          600  +/* A Tcl/Tk script used to render diff output.
          601  +*/
          602  +static const char zDiffScript[] = 
          603  +@ package require Tk
          604  +@ wm withdraw .
          605  +@ wm title . {Fossil Diff}
          606  +@ wm iconname . {Fossil Diff}
          607  +@ set body {}
          608  +@ set mx 80          ;# Length of the longest line of text
          609  +@ set nLine 0        ;# Number of lines of text
          610  +@ text .t -width 180 -yscroll {.sb set}
          611  +@ if {$tcl_platform(platform)=="windows"} {.t config -font {courier 9}}
          612  +@ .t tag config ln -foreground gray
          613  +@ .t tag config chng -background {#d0d0ff}
          614  +@ .t tag config add -background {#c0ffc0}
          615  +@ .t tag config rm -background {#ffc0c0}
          616  +@ proc dehtml {x} {
          617  +@   return [string map {&amp; & &lt; < &gt; > &#39; ' &quot; \"} $x]
          618  +@ }
          619  +@ # puts $cmd
          620  +@ set in [open $cmd r]
          621  +@ while {![eof $in]} {
          622  +@   set line [gets $in]
          623  +@   if {[regexp {^<a name="chunk.*"></a>} $line]} continue
          624  +@   if {[regexp {^===} $line]} {
          625  +@     set n [string length $line]
          626  +@     if {$n>$mx} {set mx $n}
          627  +@   }
          628  +@   incr nLine
          629  +@   while {[regexp {^(.*?)<span class="diff([a-z]+)">(.*?)</span>(.*)$} $line \
          630  +@             all pre class mid tail]} {
          631  +@     .t insert end [dehtml $pre] {} [dehtml $mid] $class
          632  +@     set line $tail
          633  +@   }
          634  +@   .t insert end [dehtml $line]\n {}
          635  +@ }
          636  +@ close $in
          637  +@ if {$mx>250} {set mx 250}      ;# Limit window width to 200 characters
          638  +@ if {$nLine>55} {set nLine 55}  ;# Limit window height to 55 lines
          639  +@ .t config -height $nLine -width $mx
          640  +@ pack .t -side left -fill both -expand 1
          641  +@ scrollbar .sb -command {.t yview} -orient vertical
          642  +@ pack .sb -side left -fill y
          643  +@ wm deiconify .
          644  +;
          645  +
          646  +/*
          647  +** Show diff output in a Tcl/Tk window, in response to the --tk option
          648  +** to the diff command.
          649  +** 
          650  +** Steps:
          651  +** (1) Write the Tcl/Tk script used for rendering into a temp file.
          652  +** (2) Invoke "wish" on the temp file using fossil_system().
          653  +** (3) Delete the temp file.
          654  +*/
          655  +void diff_tk(const char *zSubCmd, int firstArg){
          656  +  int i;
          657  +  Blob script;
          658  +  char *zTempFile;
          659  +  char *zCmd;
          660  +  blob_zero(&script);
          661  +  blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
          662  +               g.nameOfExe, zSubCmd);
          663  +  for(i=firstArg; i<g.argc; i++){
          664  +    const char *z = g.argv[i];
          665  +    if( z[0]=='-' ){
          666  +      if( strglob("*-html",z) ) continue;
          667  +      if( strglob("*-y",z) ) continue;
          668  +      if( strglob("*-i",z) ) continue;
          669  +    }
          670  +    blob_append(&script, " ", 1);
          671  +    shell_escape(&script, z);
          672  +  }
          673  +  blob_appendf(&script, "}\n%s", zDiffScript);
          674  +  zTempFile = write_blob_to_temp_file(&script);
          675  +  zCmd = mprintf("tclsh \"%s\"", zTempFile);
          676  +  fossil_system(zCmd);
          677  +  file_delete(zTempFile);
          678  +  fossil_free(zCmd);
          679  +}
          680  +
          681  +/*
          682  +** Returns non-zero if files that may be binary should be used with external
          683  +** diff programs.
          684  +*/
          685  +int diff_include_binary_files(void){
          686  +  if( is_truth(find_option("diff-binary", 0, 1)) ){
          687  +    return 1;
          688  +  }
          689  +  if( db_get_boolean("diff-binary", 1) ){
          690  +    return 1;
          691  +  }
          692  +  return 0;
          693  +}
          694  +
          695  +/*
          696  +** Returns the GLOB pattern for file names that should be treated as binary
          697  +** by the diff subsystem, if any.
          698  +*/
          699  +const char *diff_get_binary_glob(void){
          700  +  const char *zBinGlob = find_option("binary", 0, 1);
          701  +  if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
          702  +  return zBinGlob;
   310    703   }
   311    704   
   312    705   /*
   313    706   ** COMMAND: diff
   314    707   ** COMMAND: gdiff
   315    708   **
   316         -** Usage: %fossil diff|gdiff ?options? ?FILE?
          709  +** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
   317    710   **
   318         -** Show the difference between the current version of FILE (as it
   319         -** exists on disk) and that same file as it was checked out.  Or
   320         -** if the FILE argument is omitted, show the unsaved changed currently
   321         -** in the working check-out.
          711  +** Show the difference between the current version of each of the FILEs
          712  +** specified (as they exist on disk) and that same file as it was checked
          713  +** out.  Or if the FILE arguments are omitted, show the unsaved changed
          714  +** currently in the working check-out.
   322    715   **
   323    716   ** If the "--from VERSION" or "-r VERSION" option is used it specifies
   324    717   ** the source check-in for the diff operation.  If not specified, the 
   325    718   ** source check-in is the base check-in for the current check-out.
   326    719   **
   327    720   ** If the "--to VERSION" option appears, it specifies the check-in from
   328    721   ** which the second version of the file or files is taken.  If there is
................................................................................
   329    722   ** no "--to" option then the (possibly edited) files in the current check-out
   330    723   ** are used.
   331    724   **
   332    725   ** The "-i" command-line option forces the use of the internal diff logic
   333    726   ** rather than any external diff program that might be configured using
   334    727   ** the "setting" command.  If no external diff program is configured, then
   335    728   ** the "-i" option is a no-op.  The "-i" option converts "gdiff" into "diff".
          729  +**
          730  +** The "-N" or "--new-file" option causes the complete text of added or
          731  +** deleted files to be displayed.
          732  +**
          733  +** The "--diff-binary" option enables or disables the inclusion of binary files
          734  +** when using an external diff program.
          735  +**
          736  +** The "--binary" option causes files matching the glob PATTERN to be treated
          737  +** as binary when considering if they should be used with external diff program.
          738  +** This option overrides the "binary-glob" setting.
          739  +**
          740  +** Options:
          741  +**   --branch BRANCH     Show diff of all changes on BRANCH
          742  +**   --brief             Show filenames only
          743  +**   --context|-c N      Use N lines of context 
          744  +**   --from|-r VERSION   select VERSION as source for the diff
          745  +**   -i                  use internal diff logic
          746  +**   --new-file|-N       output complete text of added or deleted files
          747  +**   --tk                Launch a Tcl/Tk GUI for display
          748  +**   --to VERSION        select VERSION as target for the diff
          749  +**   --side-by-side|-y   side-by-side diff
          750  +**   --unified           unified diff
          751  +**   --width|-W N        Width of lines in side-by-side diff 
          752  +**   --diff-binary BOOL  Include binary files when using external commands
          753  +**   --binary PATTERN    Treat files that match the glob PATTERN as binary
   336    754   */
   337    755   void diff_cmd(void){
   338    756     int isGDiff;               /* True for gdiff.  False for normal diff */
   339    757     int isInternDiff;          /* True for internal diff */
          758  +  int hasNFlag;              /* True if -N or --new-file flag is used */
   340    759     const char *zFrom;         /* Source version number */
   341    760     const char *zTo;           /* Target version number */
          761  +  const char *zBranch;       /* Branch to diff */
   342    762     const char *zDiffCmd = 0;  /* External diff command. NULL for internal diff */
          763  +  const char *zBinGlob = 0;  /* Treat file names matching this as binary */
          764  +  int fIncludeBinary = 0;    /* Include binary files for external diff */
          765  +  u64 diffFlags = 0;         /* Flags to control the DIFF */
          766  +  int f;
   343    767   
          768  +  if( find_option("tk",0,0)!=0 ){
          769  +    diff_tk("diff", 2);
          770  +    return;
          771  +  }
   344    772     isGDiff = g.argv[1][0]=='g';
   345    773     isInternDiff = find_option("internal","i",0)!=0;
   346    774     zFrom = find_option("from", "r", 1);
   347    775     zTo = find_option("to", 0, 1);
          776  +  zBranch = find_option("branch", 0, 1);
          777  +  diffFlags = diff_options();
          778  +  hasNFlag = find_option("new-file","N",0)!=0;
          779  +  if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
   348    780   
          781  +  if( zBranch ){
          782  +    if( zTo || zFrom ){
          783  +      fossil_fatal("cannot use --from or --to with --branch");
          784  +    }
          785  +    zTo = zBranch;
          786  +    zFrom = mprintf("root:%s", zBranch);
          787  +  }
   349    788     if( zTo==0 ){
   350    789       db_must_be_within_tree();
   351         -    verify_all_options();
   352         -    if( !isInternDiff && g.argc==3 ){
   353         -      zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
          790  +    if( !isInternDiff ){
          791  +      zDiffCmd = diff_command_external(isGDiff);
   354    792       }
   355         -    if( g.argc==3 ){
   356         -      diff_one_against_disk(zFrom, zDiffCmd);
          793  +    zBinGlob = diff_get_binary_glob();
          794  +    fIncludeBinary = diff_include_binary_files();
          795  +    verify_all_options();
          796  +    if( g.argc>=3 ){
          797  +      for(f=2; f<g.argc; ++f){
          798  +        diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
          799  +                              diffFlags, g.argv[f]);
          800  +      }
   357    801       }else{
   358         -      diff_all_against_disk(zFrom, zDiffCmd);
          802  +      diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
          803  +                            diffFlags);
   359    804       }
   360    805     }else if( zFrom==0 ){
   361    806       fossil_fatal("must use --from if --to is present");
   362    807     }else{
   363         -    db_find_and_open_repository(1);
   364         -    verify_all_options();
   365         -    if( !isInternDiff && g.argc==3 ){
   366         -      zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
          808  +    db_find_and_open_repository(0, 0);
          809  +    if( !isInternDiff ){
          810  +      zDiffCmd = diff_command_external(isGDiff);
   367    811       }
   368         -    if( g.argc==3 ){
   369         -      diff_one_two_versions(zFrom, zTo, zDiffCmd);
          812  +    zBinGlob = diff_get_binary_glob();
          813  +    fIncludeBinary = diff_include_binary_files();
          814  +    verify_all_options();
          815  +    if( g.argc>=3 ){
          816  +      for(f=2; f<g.argc; ++f){
          817  +        diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
          818  +                              diffFlags, g.argv[f]);
          819  +      }
   370    820       }else{
   371         -      fossil_fatal("--to on complete check-ins not yet implemented");
   372         -      diff_all_two_versions(zFrom, zTo, zDiffCmd);
          821  +      diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
          822  +                            diffFlags);
   373    823       }
   374    824     }
   375    825   }
          826  +
          827  +/*
          828  +** WEBPAGE: vpatch
          829  +** URL vpatch?from=UUID&to=UUID
          830  +*/
          831  +void vpatch_page(void){
          832  +  const char *zFrom = P("from");
          833  +  const char *zTo = P("to");
          834  +  login_check_credentials();
          835  +  if( !g.perm.Read ){ login_needed(); return; }
          836  +  if( zFrom==0 || zTo==0 ) fossil_redirect_home();
          837  +
          838  +  cgi_set_content_type("text/plain");
          839  +  diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
          840  +}

Changes to src/doc.c.

    33     33   ** For any other binary type, return "unknown/unknown".
    34     34   */
    35     35   const char *mimetype_from_content(Blob *pBlob){
    36     36     int i;
    37     37     int n;
    38     38     const unsigned char *x;
    39     39   
    40         -  static const char isBinary[] = {
    41         -     1, 1, 1, 1,  1, 1, 1, 1,    1, 0, 0, 1,  0, 0, 1, 1,
    42         -     1, 1, 1, 1,  1, 1, 1, 1,    1, 1, 1, 0,  1, 1, 1, 1,
           40  +  static const char isBinary[256] = {
           41  +     1, 1, 1, 1,  1, 1, 1, 1,    1, 0, 0, 0,  0, 0, 1, 1,
           42  +     1, 1, 1, 1,  1, 1, 1, 1,    1, 1, 0, 0,  1, 1, 1, 1
    43     43     };
    44     44   
    45     45     /* A table of mimetypes based on file content prefixes
    46     46     */
    47     47     static const struct {
    48     48       const char *zPrefix;       /* The file prefix */
    49     49       int size;                  /* Length of the prefix */
    50     50       const char *zMimetype;     /* The corresponding mimetype */
    51     51     } aMime[] = {
    52     52       { "GIF87a",                  6, "image/gif"  },
    53     53       { "GIF89a",                  6, "image/gif"  },
    54         -    { "\211PNG\r\n\032\r",       8, "image/png"  },
           54  +    { "\211PNG\r\n\032\n",       8, "image/png"  },
    55     55       { "\377\332\377",            3, "image/jpeg" },
           56  +    { "\377\330\377",            3, "image/jpeg" },
    56     57     };
    57     58   
    58     59     x = (const unsigned char*)blob_buffer(pBlob);
    59     60     n = blob_size(pBlob);
    60     61     for(i=0; i<n; i++){
    61     62       unsigned char c = x[i];
    62         -    if( c<=0x1f && isBinary[c] ){
           63  +    if( isBinary[c] ){
    63     64         break;
    64     65       }
    65     66     }
    66     67     if( i>=n ){
    67     68       return 0;   /* Plain text */
    68     69     }
    69     70     for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
................................................................................
   119    120       { "css",        3, "text/css"                          },
   120    121       { "dcr",        3, "application/x-director"            },
   121    122       { "deb",        3, "application/x-debian-package"      },
   122    123       { "dir",        3, "application/x-director"            },
   123    124       { "dl",         2, "video/dl"                          },
   124    125       { "dms",        3, "application/octet-stream"          },
   125    126       { "doc",        3, "application/msword"                },
          127  +    { "docx",       4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
          128  +    { "dot",        3, "application/msword"                },
          129  +    { "dotx",       4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
   126    130       { "drw",        3, "application/drafting"              },
   127    131       { "dvi",        3, "application/x-dvi"                 },
   128    132       { "dwg",        3, "application/acad"                  },
   129    133       { "dxf",        3, "application/dxf"                   },
   130    134       { "dxr",        3, "application/x-director"            },
   131    135       { "eps",        3, "application/postscript"            },
   132    136       { "etx",        3, "text/x-setext"                     },
................................................................................
   136    140       { "f90",        3, "text/plain"                        },
   137    141       { "fli",        3, "video/fli"                         },
   138    142       { "flv",        3, "video/flv"                         },
   139    143       { "gif",        3, "image/gif"                         },
   140    144       { "gl",         2, "video/gl"                          },
   141    145       { "gtar",       4, "application/x-gtar"                },
   142    146       { "gz",         2, "application/x-gzip"                },
          147  +    { "h",          1, "text/plain"                        },
   143    148       { "hdf",        3, "application/x-hdf"                 },
   144    149       { "hh",         2, "text/plain"                        },
   145    150       { "hqx",        3, "application/mac-binhex40"          },
   146         -    { "h",          1, "text/plain"                        },
   147    151       { "htm",        3, "text/html"                         },
   148    152       { "html",       4, "text/html"                         },
   149    153       { "ice",        3, "x-conference/x-cooltalk"           },
   150    154       { "ief",        3, "image/ief"                         },
   151    155       { "iges",       4, "model/iges"                        },
   152    156       { "igs",        3, "model/iges"                        },
   153    157       { "ips",        3, "application/x-ipscript"            },
   154    158       { "ipx",        3, "application/x-ipix"                },
   155    159       { "jad",        3, "text/vnd.sun.j2me.app-descriptor"  },
   156    160       { "jar",        3, "application/java-archive"          },
   157         -    { "jpeg",       4, "image/jpeg"                        },
   158    161       { "jpe",        3, "image/jpeg"                        },
          162  +    { "jpeg",       4, "image/jpeg"                        },
   159    163       { "jpg",        3, "image/jpeg"                        },
   160    164       { "js",         2, "application/x-javascript"          },
   161    165       { "kar",        3, "audio/midi"                        },
   162    166       { "latex",      5, "application/x-latex"               },
   163    167       { "lha",        3, "application/octet-stream"          },
   164    168       { "lsp",        3, "application/x-lisp"                },
   165    169       { "lzh",        3, "application/octet-stream"          },
   166    170       { "m",          1, "text/plain"                        },
   167    171       { "m3u",        3, "audio/x-mpegurl"                   },
   168    172       { "man",        3, "application/x-troff-man"           },
          173  +    { "markdown",   8, "text/x-markdown"                   },
   169    174       { "me",         2, "application/x-troff-me"            },
   170    175       { "mesh",       4, "model/mesh"                        },
   171    176       { "mid",        3, "audio/midi"                        },
   172    177       { "midi",       4, "audio/midi"                        },
   173    178       { "mif",        3, "application/x-mif"                 },
   174    179       { "mime",       4, "www/mime"                          },
   175         -    { "movie",      5, "video/x-sgi-movie"                 },
          180  +    { "mkd",        3, "text/x-markdown"                   },
   176    181       { "mov",        3, "video/quicktime"                   },
          182  +    { "movie",      5, "video/x-sgi-movie"                 },
   177    183       { "mp2",        3, "audio/mpeg"                        },
   178         -    { "mp2",        3, "video/mpeg"                        },
   179    184       { "mp3",        3, "audio/mpeg"                        },
   180         -    { "mpeg",       4, "video/mpeg"                        },
   181    185       { "mpe",        3, "video/mpeg"                        },
   182         -    { "mpga",       4, "audio/mpeg"                        },
          186  +    { "mpeg",       4, "video/mpeg"                        },
   183    187       { "mpg",        3, "video/mpeg"                        },
          188  +    { "mpga",       4, "audio/mpeg"                        },
   184    189       { "ms",         2, "application/x-troff-ms"            },
   185    190       { "msh",        3, "model/mesh"                        },
   186    191       { "nc",         2, "application/x-netcdf"              },
   187    192       { "oda",        3, "application/oda"                   },
   188    193       { "ogg",        3, "application/ogg"                   },
   189    194       { "ogm",        3, "application/ogg"                   },
   190    195       { "pbm",        3, "image/x-portable-bitmap"           },
................................................................................
   194    199       { "pgn",        3, "application/x-chess-pgn"           },
   195    200       { "pgp",        3, "application/pgp"                   },
   196    201       { "pl",         2, "application/x-perl"                },
   197    202       { "pm",         2, "application/x-perl"                },
   198    203       { "png",        3, "image/png"                         },
   199    204       { "pnm",        3, "image/x-portable-anymap"           },
   200    205       { "pot",        3, "application/mspowerpoint"          },
          206  +    { "potx",       4, "application/vnd.openxmlformats-officedocument.presentationml.template"},
   201    207       { "ppm",        3, "image/x-portable-pixmap"           },
   202    208       { "pps",        3, "application/mspowerpoint"          },
          209  +    { "ppsx",       4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
   203    210       { "ppt",        3, "application/mspowerpoint"          },
          211  +    { "pptx",       4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
   204    212       { "ppz",        3, "application/mspowerpoint"          },
   205    213       { "pre",        3, "application/x-freelance"           },
   206    214       { "prt",        3, "application/pro_eng"               },
   207    215       { "ps",         2, "application/postscript"            },
   208    216       { "qt",         2, "video/quicktime"                   },
   209    217       { "ra",         2, "audio/x-realaudio"                 },
   210    218       { "ram",        3, "audio/x-pn-realaudio"              },
   211    219       { "rar",        3, "application/x-rar-compressed"      },
   212    220       { "ras",        3, "image/cmu-raster"                  },
   213         -    { "ras",        3, "image/x-cmu-raster"                },
   214    221       { "rgb",        3, "image/x-rgb"                       },
   215    222       { "rm",         2, "audio/x-pn-realaudio"              },
   216    223       { "roff",       4, "application/x-troff"               },
   217    224       { "rpm",        3, "audio/x-pn-realaudio-plugin"       },
   218         -    { "rtf",        3, "application/rtf"                   },
   219    225       { "rtf",        3, "text/rtf"                          },
   220    226       { "rtx",        3, "text/richtext"                     },
   221    227       { "scm",        3, "application/x-lotusscreencam"      },
   222    228       { "set",        3, "application/set"                   },
   223         -    { "sgml",       4, "text/sgml"                         },
   224    229       { "sgm",        3, "text/sgml"                         },
          230  +    { "sgml",       4, "text/sgml"                         },
   225    231       { "sh",         2, "application/x-sh"                  },
   226    232       { "shar",       4, "application/x-shar"                },
   227    233       { "silo",       4, "model/mesh"                        },
   228    234       { "sit",        3, "application/x-stuffit"             },
   229    235       { "skd",        3, "application/x-koan"                },
   230    236       { "skm",        3, "application/x-koan"                },
   231    237       { "skp",        3, "application/x-koan"                },
................................................................................
   237    243       { "spl",        3, "application/x-futuresplash"        },
   238    244       { "src",        3, "application/x-wais-source"         },
   239    245       { "step",       4, "application/STEP"                  },
   240    246       { "stl",        3, "application/SLA"                   },
   241    247       { "stp",        3, "application/STEP"                  },
   242    248       { "sv4cpio",    7, "application/x-sv4cpio"             },
   243    249       { "sv4crc",     6, "application/x-sv4crc"              },
          250  +    { "svg",        3, "image/svg+xml"                     },
   244    251       { "swf",        3, "application/x-shockwave-flash"     },
   245    252       { "t",          1, "application/x-troff"               },
   246    253       { "tar",        3, "application/x-tar"                 },
   247    254       { "tcl",        3, "application/x-tcl"                 },
   248    255       { "tex",        3, "application/x-tex"                 },
   249    256       { "texi",       4, "application/x-texinfo"             },
   250    257       { "texinfo",    7, "application/x-texinfo"             },
   251    258       { "tgz",        3, "application/x-tar-gz"              },
   252         -    { "tiff",       4, "image/tiff"                        },
   253    259       { "tif",        3, "image/tiff"                        },
          260  +    { "tiff",       4, "image/tiff"                        },
   254    261       { "tr",         2, "application/x-troff"               },
   255    262       { "tsi",        3, "audio/TSP-audio"                   },
   256    263       { "tsp",        3, "application/dsptype"               },
   257    264       { "tsv",        3, "text/tab-separated-values"         },
   258    265       { "txt",        3, "text/plain"                        },
   259    266       { "unv",        3, "application/i-deas"                },
   260    267       { "ustar",      5, "application/x-ustar"               },
................................................................................
   272    279       { "wrl",        3, "model/vrml"                        },
   273    280       { "wvx",        3, "video/x-ms-wvx"                    },
   274    281       { "xbm",        3, "image/x-xbitmap"                   },
   275    282       { "xlc",        3, "application/vnd.ms-excel"          },
   276    283       { "xll",        3, "application/vnd.ms-excel"          },
   277    284       { "xlm",        3, "application/vnd.ms-excel"          },
   278    285       { "xls",        3, "application/vnd.ms-excel"          },
          286  +    { "xlsx",       4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
   279    287       { "xlw",        3, "application/vnd.ms-excel"          },
   280    288       { "xml",        3, "text/xml"                          },
   281    289       { "xpm",        3, "image/x-xpixmap"                   },
   282    290       { "xwd",        3, "image/x-xwindowdump"               },
   283    291       { "xyz",        3, "chemical/x-pdb"                    },
   284    292       { "zip",        3, "application/zip"                   },
   285    293     };
          294  +
          295  +#ifdef FOSSIL_DEBUG
          296  +  /* This is test code to make sure the table above is in the correct
          297  +  ** order
          298  +  */
          299  +  if( fossil_strcmp(zName, "mimetype-test")==0 ){
          300  +    for(i=1; i<sizeof(aMime)/sizeof(aMime[0]); i++){
          301  +      if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
          302  +        fossil_fatal("mimetypes out of sequence: %s before %s",
          303  +                     aMime[i-1].zSuffix, aMime[i].zSuffix);
          304  +      }
          305  +    }
          306  +    return "ok";
          307  +  }
          308  +#endif
   286    309   
   287    310     z = zName;
   288    311     for(i=0; zName[i]; i++){
   289    312       if( zName[i]=='.' ) z = &zName[i+1];
   290    313     }
   291    314     len = strlen(z);
   292    315     if( len<sizeof(zSuffix)-1 ){
   293         -    strcpy(zSuffix, z);
   294         -    for(i=0; zSuffix[i]; i++) zSuffix[i] = tolower(zSuffix[i]);
          316  +    sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
          317  +    for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
   295    318       first = 0;
   296    319       last = sizeof(aMime)/sizeof(aMime[0]);
   297    320       while( first<=last ){
   298    321         int c;
   299    322         i = (first+last)/2;
   300         -      c = strcmp(zSuffix, aMime[i].zSuffix);
          323  +      c = fossil_strcmp(zSuffix, aMime[i].zSuffix);
   301    324         if( c==0 ) return aMime[i].zMimetype;
   302    325         if( c<0 ){
   303    326           last = i-1;
   304    327         }else{
   305    328           first = i+1;
   306    329         }
   307    330       }
   308    331     }
   309    332     return "application/x-fossil-artifact";
   310    333   }
          334  +
          335  +/*
          336  +** COMMAND:  test-mimetype
          337  +**
          338  +** Usage: %fossil test-mimetype FILENAME...
          339  +**
          340  +** Return the deduced mimetype for each file listed.
          341  +**
          342  +** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test"
          343  +** filename is special and verifies the integrity of the mimetype table.
          344  +** It should return "ok".
          345  +*/
          346  +void mimetype_test_cmd(void){
          347  +  int i;
          348  +  for(i=2; i<g.argc; i++){
          349  +    fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
          350  +  }
          351  +}
   311    352   
   312    353   /*
   313    354   ** WEBPAGE: doc
   314    355   ** URL: /doc?name=BASELINE/PATH
   315    356   ** URL: /doc/BASELINE/PATH
   316    357   **
   317    358   ** BASELINE can be either a baseline uuid prefix or magic words "tip"
   318         -** to me the most recently checked in baseline or "ckout" to mean the
          359  +** to mean the most recently checked in baseline or "ckout" to mean the
   319    360   ** content of the local checkout, if any.  PATH is the relative pathname
   320    361   ** of some file.  This method returns the file content.
   321    362   **
   322    363   ** If PATH matches the patterns *.wiki or *.txt then formatting content
   323    364   ** is added before returning the file.  For all other names, the content
   324    365   ** is returned straight without any interpretation or processing.
   325    366   */
................................................................................
   329    370     int vid = 0;                      /* Artifact of baseline */
   330    371     int rid = 0;                      /* Artifact of file */
   331    372     int i;                            /* Loop counter */
   332    373     Blob filebody;                    /* Content of the documentation file */
   333    374     char zBaseline[UUID_SIZE+1];      /* Baseline UUID */
   334    375   
   335    376     login_check_credentials();
   336         -  if( !g.okRead ){ login_needed(); return; }
          377  +  if( !g.perm.Read ){ login_needed(); return; }
   337    378     zName = PD("name", "tip/index.wiki");
   338    379     for(i=0; zName[i] && zName[i]!='/'; i++){}
   339    380     if( zName[i]==0 || i>UUID_SIZE ){
          381  +    zName = "index.html";
   340    382       goto doc_not_found;
   341    383     }
          384  +  g.zPath = mprintf("%s/%s", g.zPath, zName);
   342    385     memcpy(zBaseline, zName, i);
   343    386     zBaseline[i] = 0;
   344    387     zName += i;
   345    388     while( zName[0]=='/' ){ zName++; }
   346         -  if( !file_is_simple_pathname(zName) ){
   347         -    goto doc_not_found;
          389  +  if( !file_is_simple_pathname(zName, 1) ){
          390  +    int n = strlen(zName);
          391  +    if( n>0 && zName[n-1]=='/' ){
          392  +      zName = mprintf("%sindex.html", zName);
          393  +      if( !file_is_simple_pathname(zName, 1) ){
          394  +        goto doc_not_found;
          395  +      }
          396  +    }else{
          397  +      goto doc_not_found;
          398  +    }
   348    399     }
   349         -  if( strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){
   350         -    strcpy(zBaseline,"tip");
          400  +  if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){
          401  +    sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip");
   351    402     }
   352         -  if( strcmp(zBaseline,"ckout")==0 ){
          403  +  if( fossil_strcmp(zBaseline,"ckout")==0 ){
   353    404       /* Read from the local checkout */
   354    405       char *zFullpath;
   355    406       db_must_be_within_tree();
   356    407       zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
   357    408       if( !file_isfile(zFullpath) ){
   358    409         goto doc_not_found;
   359    410       }
   360    411       if( blob_read_from_file(&filebody, zFullpath)<0 ){
   361    412         goto doc_not_found;
   362    413       }
   363    414     }else{
   364    415       db_begin_transaction();
   365         -    if( strcmp(zBaseline,"tip")==0 ){
          416  +    if( fossil_strcmp(zBaseline,"tip")==0 ){
   366    417         vid = db_int(0, "SELECT objid FROM event WHERE type='ci'"
   367    418                         " ORDER BY mtime DESC LIMIT 1");
   368    419       }else{
   369         -      vid = name_to_rid(zBaseline);
          420  +      vid = name_to_typed_rid(zBaseline, "ci");
   370    421       }
   371    422   
   372    423       /* Create the baseline cache if it does not already exist */
   373    424       db_multi_exec(
   374    425         "CREATE TABLE IF NOT EXISTS vcache(\n"
   375    426         "  vid INTEGER,         -- baseline ID\n"
   376    427         "  fname TEXT,          -- filename\n"
   377    428         "  rid INTEGER,         -- artifact ID\n"
   378    429         "  UNIQUE(vid,fname,rid)\n"
   379    430         ")"
   380    431       );
          432  +
          433  +
   381    434   
   382    435       /* Check to see if the documentation file artifact ID is contained
   383    436       ** in the baseline cache */
   384    437       rid = db_int(0, "SELECT rid FROM vcache"
   385    438                       " WHERE vid=%d AND fname=%Q", vid, zName);
   386    439       if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
   387    440         goto doc_not_found;
   388    441       }
   389    442   
   390    443       if( rid==0 ){
   391    444         Stmt s;
   392         -      Blob baseline;
   393         -      Manifest m;
          445  +      Manifest *pM;
          446  +      ManifestFile *pFile;
   394    447   
   395    448         /* Add the vid baseline to the cache */
   396    449         if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
   397    450           db_multi_exec("DELETE FROM vcache");
   398    451         }
   399         -      if( content_get(vid, &baseline)==0 ){
   400         -        goto doc_not_found;
   401         -      }
   402         -      if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
          452  +      pM = manifest_get(vid, CFTYPE_MANIFEST);
          453  +      if( pM==0 ){
   403    454           goto doc_not_found;
   404    455         }
   405    456         db_prepare(&s,
   406    457           "INSERT INTO vcache(vid,fname,rid)"
   407    458           " SELECT %d, :fname, rid FROM blob"
   408    459           "  WHERE uuid=:uuid",
   409    460           vid
   410    461         );
   411         -      for(i=0; i<m.nFile; i++){
   412         -        db_bind_text(&s, ":fname", m.aFile[i].zName);
   413         -        db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
          462  +      manifest_file_rewind(pM);
          463  +      while( (pFile = manifest_file_next(pM,0))!=0 ){
          464  +        db_bind_text(&s, ":fname", pFile->zName);
          465  +        db_bind_text(&s, ":uuid", pFile->zUuid);
   414    466           db_step(&s);
   415    467           db_reset(&s);
   416    468         }
   417    469         db_finalize(&s);
   418         -      manifest_clear(&m);
          470  +      manifest_destroy(pM);
   419    471   
   420    472         /* Try again to find the file */
   421    473         rid = db_int(0, "SELECT rid FROM vcache"
   422    474                         " WHERE vid=%d AND fname=%Q", vid, zName);
   423    475       }
   424    476       if( rid==0 ){
   425    477         goto doc_not_found;
................................................................................
   435    487     /* The file is now contained in the filebody blob.  Deliver the
   436    488     ** file to the user 
   437    489     */
   438    490     zMime = P("mimetype");
   439    491     if( zMime==0 ){
   440    492       zMime = mimetype_from_name(zName);
   441    493     }
   442         -  if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
          494  +  Th_Store("doc_name", zName);
          495  +  Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
          496  +                                     "  FROM blob WHERE rid=%d", vid));
          497  +  Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
          498  +                                  " WHERE objid=%d AND type='ci'", vid));
          499  +  if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
   443    500       Blob title, tail;
   444    501       if( wiki_find_title(&filebody, &title, &tail) ){
   445    502         style_header(blob_str(&title));
   446         -      wiki_convert(&tail, 0, 0);
          503  +      wiki_convert(&tail, 0, WIKI_BUTTONS);
          504  +    }else{
          505  +      style_header("Documentation");
          506  +      wiki_convert(&filebody, 0, WIKI_BUTTONS);
          507  +    }
          508  +    style_footer();
          509  +#ifdef FOSSIL_ENABLE_MARKDOWN
          510  +  }else if( fossil_strcmp(zMime, "text/x-markdown")==0
          511  +         && db_get_boolean("markdown", 0) ){
          512  +    Blob title = BLOB_INITIALIZER;
          513  +    Blob tail = BLOB_INITIALIZER;
          514  +    markdown_to_html(&filebody, &title, &tail);
          515  +    if( blob_size(&title)>0 ){
          516  +      style_header(blob_str(&title));
   447    517       }else{
   448    518         style_header("Documentation");
   449         -      wiki_convert(&filebody, 0, 0);
   450    519       }
          520  +    blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
   451    521       style_footer();
   452         -  }else if( strcmp(zMime, "text/plain")==0 ){
          522  +#endif
          523  +  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
   453    524       style_header("Documentation");
   454    525       @ <blockquote><pre>
   455    526       @ %h(blob_str(&filebody))
   456    527       @ </pre></blockquote>
   457    528       style_footer();
   458    529     }else{
   459    530       cgi_set_content_type(zMime);
................................................................................
   461    532     }
   462    533     return;
   463    534   
   464    535   doc_not_found:
   465    536     /* Jump here when unable to locate the document */
   466    537     db_end_transaction(0);
   467    538     style_header("Document Not Found");
   468         -  @ <p>No such document: %h(PD("name","tip/index.wiki"))</p>
          539  +  @ <p>No such document: %h(zName)</p>
   469    540     style_footer();
   470    541     return;  
   471    542   }
   472    543   
   473    544   /*
   474    545   ** The default logo.
   475    546   */
................................................................................
   555    626     if( blob_size(&logo)==0 ){
   556    627       blob_init(&logo, (char*)aLogo, sizeof(aLogo));
   557    628     }
   558    629     cgi_set_content_type(zMime);
   559    630     cgi_set_content(&logo);
   560    631     g.isConst = 1;
   561    632   }
          633  +
          634  +/*
          635  +** The default background image:  a 16x16 white GIF
          636  +*/
          637  +static const unsigned char aBackground[] = {
          638  +    71,  73,  70,  56,  57,  97,  16,   0,  16,   0,
          639  +   240,   0,   0, 255, 255, 255,   0,   0,   0,  33,
          640  +   254,   4, 119, 105, 115, 104,   0,  44,   0,   0,
          641  +     0,   0,  16,   0,  16,   0,   0,   2,  14, 132,
          642  +   143, 169, 203, 237,  15, 163, 156, 180, 218, 139,
          643  +   179,  62,   5,   0,  59,
          644  +};
          645  +
          646  +
          647  +/*
          648  +** WEBPAGE: background
          649  +**
          650  +** Return the background image.
          651  +*/
          652  +void background_page(void){
          653  +  Blob bgimg;
          654  +  char *zMime;
          655  +
          656  +  zMime = db_get("background-mimetype", "image/gif");
          657  +  blob_zero(&bgimg);
          658  +  db_blob(&bgimg, "SELECT value FROM config WHERE name='background-image'");
          659  +  if( blob_size(&bgimg)==0 ){
          660  +    blob_init(&bgimg, (char*)aBackground, sizeof(aBackground));
          661  +  }
          662  +  cgi_set_content_type(zMime);
          663  +  cgi_set_content(&bgimg);
          664  +  g.isConst = 1;
          665  +}

Changes to src/encode.c.

    42     42         case '&':   count += 5;       break;
    43     43         case '"':   count += 6;       break;
    44     44         default:    count++;          break;
    45     45       }
    46     46       i++;
    47     47     }
    48     48     i = 0;
    49         -  zOut = malloc( count+1 );
    50         -  if( zOut==0 ) return 0;
           49  +  zOut = fossil_malloc( count+1 );
    51     50     while( n-->0 && (c = *zIn)!=0 ){
    52     51       switch( c ){
    53     52         case '<':   
    54     53           zOut[i++] = '&';
    55     54           zOut[i++] = 'l';
    56     55           zOut[i++] = 't';
    57     56           zOut[i++] = ';';
................................................................................
    83     82       }
    84     83       zIn++;
    85     84     }
    86     85     zOut[i] = 0;
    87     86     return zOut;
    88     87   }
    89     88   
           89  +/*
           90  +** Append HTML-escaped text to a Blob.
           91  +*/
           92  +void htmlize_to_blob(Blob *p, const char *zIn, int n){
           93  +  int c, i, j;
           94  +  if( n<0 ) n = strlen(zIn);
           95  +  for(i=j=0; i<n; i++){
           96  +    c = zIn[i];
           97  +    switch( c ){
           98  +      case '<':
           99  +        if( j<i ) blob_append(p, zIn+j, i-j);
          100  +        blob_append(p, "&lt;", 4);
          101  +        j = i+1;
          102  +        break;
          103  +      case '>':
          104  +        if( j<i ) blob_append(p, zIn+j, i-j);
          105  +        blob_append(p, "&gt;", 4);
          106  +        j = i+1;
          107  +        break;
          108  +      case '&':
          109  +        if( j<i ) blob_append(p, zIn+j, i-j);
          110  +        blob_append(p, "&amp;", 5);
          111  +        j = i+1;
          112  +        break;
          113  +      case '"':
          114  +        if( j<i ) blob_append(p, zIn+j, i-j);
          115  +        blob_append(p, "&quot;", 6);
          116  +        j = i+1;
          117  +        break;
          118  +    }
          119  +  }
          120  +  if( j<i ) blob_append(p, zIn+j, i-j);
          121  +}
          122  +
    90    123   
    91    124   /*
    92    125   ** Encode a string for HTTP.  This means converting lots of
    93    126   ** characters into the "%HH" where H is a hex digit.  It also
    94    127   ** means converting spaces to "+".
    95    128   **
    96    129   ** This is the opposite of DeHttpizeString below.
    97    130   */
    98    131   static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
    99    132     int c;
   100    133     int i = 0;
   101    134     int count = 0;
   102    135     char *zOut;
   103         -  int other;
   104    136   # define IsSafeChar(X)  \
   105         -     (isalnum(X) || (X)=='.' || (X)=='$' || (X)=='-' || (X)=='_' || (X)==other)
          137  +     (fossil_isalnum(X) || (X)=='.' || (X)=='$' \
          138  +      || (X)=='~' || (X)=='-' || (X)=='_' \
          139  +      || (!encodeSlash && ((X)=='/' || (X)==':')))
   106    140   
   107    141     if( zIn==0 ) return 0;
   108    142     if( n<0 ) n = strlen(zIn);
   109         -  other = encodeSlash ? 'a' : '/';
   110    143     while( i<n && (c = zIn[i])!=0 ){
   111    144       if( IsSafeChar(c) || c==' ' ){
   112    145         count++;
   113    146       }else{
   114    147         count += 3;
   115    148       }
   116    149       i++;
   117    150     }
   118    151     i = 0;
   119         -  zOut = malloc( count+1 );
   120         -  if( zOut==0 ) return 0;
          152  +  zOut = fossil_malloc( count+1 );
   121    153     while( n-->0 && (c = *zIn)!=0 ){
   122    154       if( IsSafeChar(c) ){
   123    155         zOut[i++] = c;
   124    156       }else if( c==' ' ){
   125    157         zOut[i++] = '+';
   126    158       }else{
   127    159         zOut[i++] = '%';
................................................................................
   133    165     zOut[i] = 0;
   134    166     return zOut;
   135    167   }
   136    168   
   137    169   /*
   138    170   ** Convert the input string into a form that is suitable for use as
   139    171   ** a token in the HTTP protocol.  Spaces are encoded as '+' and special
   140         -** characters are encoded as "%HH" where HH is a two-digit hexidecimal
          172  +** characters are encoded as "%HH" where HH is a two-digit hexadecimal
   141    173   ** representation of the character.  The "/" character is encoded
   142    174   ** as "%2F".
   143    175   */
   144    176   char *httpize(const char *z, int n){
   145    177     return EncodeHttp(z, n, 1);
   146    178   }
   147    179   
................................................................................
   231    263     if( nIn<0 ) nIn = strlen(zIn);
   232    264     for(i=n=0; i<nIn; i++){
   233    265       c = zIn[i];
   234    266       if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v'
   235    267                || c=='\\' ) n++;
   236    268     }
   237    269     n += nIn;
   238         -  zOut = malloc( n+1 );
          270  +  zOut = fossil_malloc( n+1 );
   239    271     if( zOut ){
   240    272       for(i=j=0; i<nIn; i++){
   241    273         int c = zIn[i];
   242    274         if( c==0 ){
   243    275           zOut[j++] = '\\';
   244    276           zOut[j++] = '0';
   245    277         }else if( c=='\\' ){
   246    278           zOut[j++] = '\\';
   247    279           zOut[j++] = '\\';
   248         -      }else if( isspace(c) ){
          280  +      }else if( fossil_isspace(c) ){
   249    281           zOut[j++] = '\\';
   250    282           switch( c ){
   251    283             case '\n':  c = 'n'; break;
   252    284             case ' ':   c = 's'; break;
   253    285             case '\t':  c = 't'; break;
   254    286             case '\r':  c = 'r'; break;
   255    287             case '\v':  c = 'v'; break;
................................................................................
   266    298   }
   267    299   
   268    300   /*
   269    301   ** Decode a fossilized string in-place.
   270    302   */
   271    303   void defossilize(char *z){
   272    304     int i, j, c;
   273         -  for(i=j=0; z[i]; i++){
   274         -    c = z[i];
          305  +  for(i=0; (c=z[i])!=0 && c!='\\'; i++){}
          306  +  if( c==0 ) return;
          307  +  for(j=i; (c=z[i])!=0; i++){
   275    308       if( c=='\\' && z[i+1] ){
   276    309         i++;
   277    310         switch( z[i] ){
   278    311           case 'n':  c = '\n';  break;
   279    312           case 's':  c = ' ';   break;
   280    313           case 't':  c = '\t';  break;
   281    314           case 'r':  c = '\r';  break;
................................................................................
   307    340   char *encode64(const char *zData, int nData){
   308    341     char *z64;
   309    342     int i, n;
   310    343   
   311    344     if( nData<=0 ){
   312    345       nData = strlen(zData);
   313    346     }
   314         -  z64 = malloc( (nData*4)/3 + 8 );
          347  +  z64 = fossil_malloc( (nData*4)/3 + 8 );
   315    348     for(i=n=0; i+2<nData; i+=3){
   316    349       z64[n++] = zBase[ (zData[i]>>2) & 0x3f ];
   317    350       z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ];
   318    351       z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ];
   319    352       z64[n++] = zBase[ zData[i+2] & 0x3f ];
   320    353     }
   321    354     if( i+1<nData ){
................................................................................
   338    371   ** Usage: %fossil test-encode64 STRING
   339    372   */
   340    373   void test_encode64_cmd(void){
   341    374     char *z;
   342    375     int i;
   343    376     for(i=2; i<g.argc; i++){
   344    377       z = encode64(g.argv[i], -1);
   345         -    printf("%s\n", z);
          378  +    fossil_print("%s\n", z);
   346    379       free(z);
   347    380     }
   348    381   }
   349    382   
   350    383   
   351    384   /*
   352    385   ** This function treats its input as a base-64 string and returns the
................................................................................
   368    401     if( !isInit ){
   369    402       for(i=0; i<128; i++){ trans[i] = 0; }
   370    403       for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; }
   371    404       isInit = 1;
   372    405     }
   373    406     n64 = strlen(z64);
   374    407     while( n64>0 && z64[n64-1]=='=' ) n64--;
   375         -  zData = malloc( (n64*3)/4 + 4 );
          408  +  zData = fossil_malloc( (n64*3)/4 + 4 );
   376    409     for(i=j=0; i+3<n64; i+=4){
   377    410       a = trans[z64[i] & 0x7f];
   378    411       b = trans[z64[i+1] & 0x7f];
   379    412       c = trans[z64[i+2] & 0x7f];
   380    413       d = trans[z64[i+3] & 0x7f];
   381    414       zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
   382    415       zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f);
................................................................................
   403    436   ** Usage: %fossil test-decode64 STRING
   404    437   */
   405    438   void test_decode64_cmd(void){
   406    439     char *z;
   407    440     int i, n;
   408    441     for(i=2; i<g.argc; i++){
   409    442       z = decode64(g.argv[i], &n);
   410         -    printf("%d: %s\n", n, z);
          443  +    fossil_print("%d: %s\n", n, z);
   411    444       free(z);
   412    445     }
   413    446   }
   414    447   
   415    448   /*
   416    449   ** The base-16 encoding using the following characters:
   417    450   **
................................................................................
   444    477   ** case is the same. 
   445    478   */
   446    479   static const char zDecode[] = {
   447    480     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   448    481     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   449    482     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   450    483      0,  1,  2,  3,  4,  5,  6,  7,   8,  9, 64, 64, 64, 64, 64, 64, 
   451         -  64, 10, 11, 12, 13, 14, 15, 64,  64,  1, 64, 64,  1, 64, 64,  0,
          484  +  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
   452    485     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
   453         -  64, 10, 11, 12, 13, 14, 15, 64,  64,  1, 64, 64,  1, 64, 64,  0,
          486  +  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
   454    487     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
   455    488     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   456    489     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   457    490     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   458    491     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   459    492     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   460    493     64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
................................................................................
   503    536   */
   504    537   void canonical16(char *z, int n){
   505    538     while( *z && n-- ){
   506    539       *z = zEncode[zDecode[(*z)&0x7f]&0x1f];
   507    540       z++;
   508    541     }
   509    542   }
          543  +
          544  +/* Randomness used for XOR-ing by the obscure() and unobscure() routines */
          545  +static const unsigned char aObscurer[16] = {
          546  +    0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86,
          547  +    0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85
          548  +};
          549  + 
          550  +
          551  +/*
          552  +** Obscure plain text so that it is not easily readable.
          553  +**
          554  +** This is used for storing sensitive information (such as passwords) in a
          555  +** way that prevents their exposure through idle browsing.  This is not
          556  +** encryption.  Anybody who really wants the password can still get it.
          557  +**
          558  +** The text is XOR-ed with a repeating pattern then converted to hex.
          559  +** Space to hold the returned string is obtained from malloc and should
          560  +** be freed by the caller.
          561  +*/
          562  +char *obscure(const char *zIn){
          563  +  int n, i;
          564  +  unsigned char salt;
          565  +  char *zOut;
          566  +  
          567  +  if( zIn==0 ) return 0;
          568  +  n = strlen(zIn);
          569  +  zOut = fossil_malloc( n*2+3 );
          570  +  sqlite3_randomness(1, &salt);
          571  +  zOut[n+1] = (char)salt;
          572  +  for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt;
          573  +  encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1);
          574  +  return zOut;
          575  +}
          576  +
          577  +/*
          578  +** Undo the obscuring of text performed by obscure().  Or, if the input is
          579  +** not hexadecimal (meaning the input is not the output of obscure()) then
          580  +** do the equivalent of strdup().
          581  +**
          582  +** The result is memory obtained from malloc that should be freed by the caller. 
          583  +*/
          584  +char *unobscure(const char *zIn){
          585  +  int n, i;
          586  +  unsigned char salt;
          587  +  char *zOut;
          588  +  
          589  +  if( zIn==0 ) return 0;
          590  +  n = strlen(zIn);
          591  +  zOut = fossil_malloc( n + 1 );
          592  +  if( n<2
          593  +    || decode16((unsigned char*)zIn, &salt, 2)
          594  +    || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2)
          595  +  ){
          596  +    memcpy(zOut, zIn, n+1);
          597  +  }else{
          598  +    n = n/2 - 1;
          599  +    for(i=0; i<n; i++) zOut[i] = zOut[i]^aObscurer[i&0x0f]^salt;
          600  +    zOut[n] = 0;
          601  +  }
          602  +  return zOut;
          603  +}
          604  +
          605  +/*
          606  +** Command to test obscure() and unobscure().  These commands are also useful
          607  +** utilities for decoding passwords found in the database.
          608  +**
          609  +** COMMAND: test-obscure
          610  +*/
          611  +void test_obscure_cmd(void){
          612  +  int i;
          613  +  char *z, *z2;
          614  +  for(i=2; i<g.argc; i++){
          615  +    z = obscure(g.argv[i]);
          616  +    z2 = unobscure(z);
          617  +    fossil_print("OBSCURE:    %s -> %s (%s)\n", g.argv[i], z, z2);
          618  +    free(z);
          619  +    free(z2);
          620  +    z = unobscure(g.argv[i]);
          621  +    fossil_print("UNOBSCURE:  %s -> %s\n", g.argv[i], z);
          622  +    free(z);
          623  +  }
          624  +}

Added src/event.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code to do formatting of event messages:
           19  +**
           20  +**     Milestones
           21  +**     Blog posts
           22  +**     New articles
           23  +**     Process checkpoints
           24  +**     Announcements
           25  +*/
           26  +#include <assert.h>
           27  +#include <ctype.h>
           28  +#include "config.h"
           29  +#include "event.h"
           30  +
           31  +/*
           32  +** Output a hyperlink to an event given its tagid.
           33  +*/
           34  +void hyperlink_to_event_tagid(int tagid){
           35  +  char *zEventId;
           36  +  zEventId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
           37  +                     tagid);
           38  +  @ [%z(href("%R/event/%s",zEventId))%S(zEventId)</a>]
           39  +  free(zEventId);
           40  +}
           41  +
           42  +/*
           43  +** WEBPAGE: event
           44  +** URL: /event
           45  +** PARAMETERS:
           46  +**
           47  +**  name=EVENTID      // Identify the event to display EVENTID must be complete
           48  +**  detail=BOOLEAN    // Show details if TRUE.  Default is FALSE.  Optional.
           49  +**  aid=ARTIFACTID    // Which specific version of the event.  Optional.
           50  +**
           51  +** Display an existing event identified by EVENTID
           52  +*/
           53  +void event_page(void){
           54  +  int rid = 0;             /* rid of the event artifact */
           55  +  char *zUuid;             /* UUID corresponding to rid */
           56  +  const char *zEventId;    /* Event identifier */
           57  +  char *zETime;            /* Time of the event */
           58  +  char *zATime;            /* Time the artifact was created */
           59  +  int specRid;             /* rid specified by aid= parameter */
           60  +  int prevRid, nextRid;    /* Previous or next edits of this event */
           61  +  Manifest *pEvent;        /* Parsed event artifact */
           62  +  Blob fullbody;           /* Complete content of the event body */
           63  +  Blob title;              /* Title extracted from the event body */
           64  +  Blob tail;               /* Event body that comes after the title */
           65  +  Stmt q1;                 /* Query to search for the event */
           66  +  int showDetail;          /* True to show details */
           67  +
           68  +
           69  +  /* wiki-read privilege is needed in order to read events.
           70  +  */
           71  +  login_check_credentials();
           72  +  if( !g.perm.RdWiki ){
           73  +    login_needed();
           74  +    return;
           75  +  }
           76  +
           77  +  zEventId = P("name");
           78  +  if( zEventId==0 ){ fossil_redirect_home(); return; }
           79  +  zUuid = (char*)P("aid");
           80  +  specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0;
           81  +  rid = nextRid = prevRid = 0;
           82  +  db_prepare(&q1,
           83  +     "SELECT rid FROM tagxref"
           84  +     " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB 'event-%q*')"
           85  +     " ORDER BY mtime DESC",
           86  +     zEventId
           87  +  );
           88  +  while( db_step(&q1)==SQLITE_ROW ){
           89  +    nextRid = rid;
           90  +    rid = db_column_int(&q1, 0);
           91  +    if( specRid==0 || specRid==rid ){
           92  +      if( db_step(&q1)==SQLITE_ROW ){
           93  +        prevRid = db_column_int(&q1, 0);
           94  +      }
           95  +      break;
           96  +    }
           97  +  }
           98  +  db_finalize(&q1);
           99  +  if( rid==0 || (specRid!=0 && specRid!=rid) ){
          100  +    style_header("No Such Event");
          101  +    @ Cannot locate specified event
          102  +    style_footer();
          103  +    return;
          104  +  }
          105  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          106  +  showDetail = atoi(PD("detail","0"));
          107  +
          108  +  /* Extract the event content.
          109  +  */
          110  +  pEvent = manifest_get(rid, CFTYPE_EVENT);
          111  +  if( pEvent==0 ){
          112  +    fossil_panic("Object #%d is not an event", rid);
          113  +  }
          114  +  blob_init(&fullbody, pEvent->zWiki, -1);
          115  +  if( wiki_find_title(&fullbody, &title, &tail) ){
          116  +    style_header(blob_str(&title));
          117  +  }else{
          118  +    style_header("Event %S", zEventId);
          119  +    tail = fullbody;
          120  +  }
          121  +  if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
          122  +    style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
          123  +                          g.zTop, zEventId);
          124  +  }
          125  +  zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
          126  +  style_submenu_element("Context", "Context", "%s/timeline?c=%T",
          127  +                        g.zTop, zETime);
          128  +  if( g.perm.Hyperlink ){
          129  +    if( showDetail ){
          130  +      style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s",
          131  +                            g.zTop, zEventId, zUuid);
          132  +      if( nextRid ){
          133  +        char *zNext;
          134  +        zNext = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nextRid);
          135  +        style_submenu_element("Next", "Next",
          136  +                              "%s/event?name=%s&aid=%s&detail=1",
          137  +                              g.zTop, zEventId, zNext);
          138  +        free(zNext);
          139  +      }
          140  +      if( prevRid ){
          141  +        char *zPrev;
          142  +        zPrev = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", prevRid);
          143  +        style_submenu_element("Prev", "Prev",
          144  +                              "%s/event?name=%s&aid=%s&detail=1",
          145  +                              g.zTop, zEventId, zPrev);
          146  +        free(zPrev);
          147  +      }
          148  +    }else{
          149  +      style_submenu_element("Detail", "Detail",
          150  +                            "%s/event?name=%s&aid=%s&detail=1",
          151  +                            g.zTop, zEventId, zUuid);
          152  +    }
          153  +  }
          154  +
          155  +  if( showDetail && g.perm.Hyperlink ){
          156  +    int i;
          157  +    const char *zClr = 0;
          158  +    Blob comment;
          159  +
          160  +    zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
          161  +    @ <p>Event [%z(href("%R/artifact/%s",zUuid))%S(zUuid)</a>] at
          162  +    @ [%z(href("%R/timeline?c=%T",zETime))%s(zETime)</a>]
          163  +    @ entered by user <b>%h(pEvent->zUser)</b> on
          164  +    @ [%z(href("%R/timeline?c=%T",zATime))%s(zATime)</a>]:</p>
          165  +    @ <blockquote>
          166  +    for(i=0; i<pEvent->nTag; i++){
          167  +      if( fossil_strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
          168  +        zClr = pEvent->aTag[i].zValue;
          169  +      }
          170  +    }
          171  +    if( zClr && zClr[0]==0 ) zClr = 0;
          172  +    if( zClr ){
          173  +      @ <div style="background-color: %h(zClr);">
          174  +    }else{
          175  +      @ <div>
          176  +    }
          177  +    blob_init(&comment, pEvent->zComment, -1);
          178  +    wiki_convert(&comment, 0, WIKI_INLINE);
          179  +    blob_reset(&comment);
          180  +    @ </div>
          181  +    @ </blockquote><hr />
          182  +  }  
          183  +
          184  +  wiki_convert(&tail, 0, 0);
          185  +  style_footer();
          186  +  manifest_destroy(pEvent);
          187  +}
          188  +
          189  +/*
          190  +** WEBPAGE: eventedit
          191  +** URL: /eventedit?name=EVENTID
          192  +**
          193  +** Edit an event.  If name is omitted, create a new event.
          194  +*/
          195  +void eventedit_page(void){
          196  +  char *zTag;
          197  +  int rid = 0;
          198  +  Blob event;
          199  +  const char *zEventId;
          200  +  char *zHtmlPageName;
          201  +  int n;
          202  +  const char *z;
          203  +  char *zBody = (char*)P("w");
          204  +  char *zETime = (char*)P("t");
          205  +  const char *zComment = P("c");
          206  +  const char *zTags = P("g");
          207  +  const char *zClr;
          208  +
          209  +  if( zBody ){
          210  +    zBody = mprintf("%s", zBody);
          211  +  }
          212  +  login_check_credentials();
          213  +  zEventId = P("name");
          214  +  if( zEventId==0 ){
          215  +    zEventId = db_text(0, "SELECT lower(hex(randomblob(20)))");
          216  +  }else{
          217  +    int nEventId = strlen(zEventId);
          218  +    if( nEventId!=40 || !validate16(zEventId, 40) ){
          219  +      fossil_redirect_home();
          220  +      return;
          221  +    }
          222  +  }
          223  +  zTag = mprintf("event-%s", zEventId);
          224  +  rid = db_int(0, 
          225  +    "SELECT rid FROM tagxref"
          226  +    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
          227  +    " ORDER BY mtime DESC", zTag
          228  +  );
          229  +  free(zTag);
          230  +
          231  +  /* Need both check-in and wiki-write or wiki-create privileges in order
          232  +  ** to edit/create an event.
          233  +  */
          234  +  if( !g.perm.Write || (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
          235  +    login_needed();
          236  +    return;
          237  +  }
          238  +
          239  +  /* Figure out the color */
          240  +  if( rid ){
          241  +    zClr = db_text("", "SELECT bgcolor  FROM event WHERE objid=%d", rid);
          242  +  }else{
          243  +    zClr = "";
          244  +  }
          245  +  zClr = PD("clr",zClr);
          246  +  if( fossil_strcmp(zClr,"##")==0 ) zClr = PD("cclr","");
          247  +
          248  +
          249  +  /* If editing an existing event, extract the key fields to use as
          250  +  ** a starting point for the edit.
          251  +  */
          252  +  if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
          253  +    Manifest *pEvent;
          254  +    pEvent = manifest_get(rid, CFTYPE_EVENT);
          255  +    if( pEvent && pEvent->type==CFTYPE_EVENT ){
          256  +      if( zBody==0 ) zBody = pEvent->zWiki;
          257  +      if( zETime==0 ){
          258  +        zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
          259  +      }
          260  +      if( zComment==0 ) zComment = pEvent->zComment;
          261  +    }
          262  +    if( zTags==0 ){
          263  +      zTags = db_text(0,
          264  +        "SELECT group_concat(substr(tagname,5),', ')"
          265  +        "  FROM tagxref, tag"
          266  +        " WHERE tagxref.rid=%d"
          267  +        "   AND tagxref.tagid=tag.tagid"
          268  +        "   AND tag.tagname GLOB 'sym-*'",
          269  +        rid
          270  +      );
          271  +    }
          272  +  }
          273  +  zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
          274  +  if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
          275  +    char *zDate;
          276  +    Blob cksum;
          277  +    int nrid;
          278  +    blob_zero(&event);
          279  +    db_begin_transaction();
          280  +    login_verify_csrf_secret();
          281  +    blob_appendf(&event, "C %F\n", zComment);
          282  +    zDate = date_in_standard_format("now");
          283  +    blob_appendf(&event, "D %s\n", zDate);
          284  +    free(zDate);
          285  +    zETime[10] = 'T';
          286  +    blob_appendf(&event, "E %s %s\n", zETime, zEventId);
          287  +    zETime[10] = ' ';
          288  +    if( rid ){
          289  +      char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          290  +      blob_appendf(&event, "P %s\n", zUuid);
          291  +      free(zUuid);
          292  +    }
          293  +    if( zClr && zClr[0] ){
          294  +      blob_appendf(&event, "T +bgcolor * %F\n", zClr);
          295  +    }
          296  +    if( zTags && zTags[0] ){
          297  +      Blob tags, one;
          298  +      int i, j;
          299  +      Stmt q;
          300  +      char *zBlob;
          301  +
          302  +      /* Load the tags string into a blob */
          303  +      blob_zero(&tags);
          304  +      blob_append(&tags, zTags, -1); 
          305  +
          306  +      /* Collapse all sequences of whitespace and "," characters into
          307  +      ** a single space character */
          308  +      zBlob = blob_str(&tags);
          309  +      for(i=j=0; zBlob[i]; i++, j++){
          310  +        if( fossil_isspace(zBlob[i]) || zBlob[i]==',' ){
          311  +          while( fossil_isspace(zBlob[i+1]) ){ i++; }
          312  +          zBlob[j] = ' ';
          313  +        }else{
          314  +          zBlob[j] = zBlob[i];
          315  +        }
          316  +      }
          317  +      blob_resize(&tags, j);
          318  +
          319  +      /* Parse out each tag and load it into a temporary table for sorting */
          320  +      db_multi_exec("CREATE TEMP TABLE newtags(x);");
          321  +      while( blob_token(&tags, &one) ){
          322  +        db_multi_exec("INSERT INTO newtags VALUES(%B)", &one);
          323  +      }
          324  +      blob_reset(&tags);
          325  +
          326  +      /* Extract the tags in sorted order and make an entry in the
          327  +      ** artifact for each. */
          328  +      db_prepare(&q, "SELECT x FROM newtags ORDER BY x");
          329  +      while( db_step(&q)==SQLITE_ROW ){
          330  +        blob_appendf(&event, "T +sym-%F *\n", db_column_text(&q, 0));
          331  +      }
          332  +      db_finalize(&q);
          333  +    }        
          334  +    if( g.zLogin ){
          335  +      blob_appendf(&event, "U %F\n", g.zLogin);
          336  +    }
          337  +    blob_appendf(&event, "W %d\n%s\n", strlen(zBody), zBody);
          338  +    md5sum_blob(&event, &cksum);
          339  +    blob_appendf(&event, "Z %b\n", &cksum);
          340  +    blob_reset(&cksum);
          341  +    nrid = content_put(&event);
          342  +    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
          343  +    manifest_crosslink(nrid, &event);
          344  +    assert( blob_is_reset(&event) );
          345  +    content_deltify(rid, nrid, 0);
          346  +    db_end_transaction(0);
          347  +    cgi_redirectf("event?name=%T", zEventId);
          348  +  }
          349  +  if( P("cancel")!=0 ){
          350  +    cgi_redirectf("event?name=%T", zEventId);
          351  +    return;
          352  +  }
          353  +  if( zBody==0 ){
          354  +    zBody = mprintf("<i>Event Text</i>");
          355  +  }
          356  +  zHtmlPageName = mprintf("Edit Event %S", zEventId);
          357  +  style_header(zHtmlPageName);
          358  +  if( P("preview")!=0 ){
          359  +    Blob title, tail, com;
          360  +    @ <p><b>Timeline comment preview:</b></p>
          361  +    @ <blockquote>
          362  +    @ <table border="0">
          363  +    if( zClr && zClr[0] ){
          364  +      @ <tr><td style="background-color: %h(zClr);">
          365  +    }else{
          366  +      @ <tr><td>
          367  +    }
          368  +    blob_zero(&com);
          369  +    blob_append(&com, zComment, -1);
          370  +    wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
          371  +    @ </td></tr></table>
          372  +    @ </blockquote>
          373  +    @ <p><b>Page content preview:</b><p>
          374  +    @ <blockquote>
          375  +    blob_zero(&event);
          376  +    blob_append(&event, zBody, -1);
          377  +    if( wiki_find_title(&event, &title, &tail) ){
          378  +      @ <h2 align="center">%h(blob_str(&title))</h2>
          379  +      wiki_convert(&tail, 0, 0);
          380  +    }else{
          381  +      wiki_convert(&event, 0, 0);
          382  +    }
          383  +    @ </blockquote><hr />
          384  +    blob_reset(&event);
          385  +  }
          386  +  for(n=2, z=zBody; z[0]; z++){
          387  +    if( z[0]=='\n' ) n++;
          388  +  }
          389  +  if( n<20 ) n = 20;
          390  +  if( n>40 ) n = 40;
          391  +  @ <form method="post" action="%s(g.zTop)/eventedit"><div>
          392  +  login_insert_csrf_secret();
          393  +  @ <input type="hidden" name="name" value="%h(zEventId)" />
          394  +  @ <table border="0" cellspacing="10">
          395  +
          396  +  @ <tr><td align="right" valign="top"><b>Event&nbsp;Time:</b></td>
          397  +  @ <td valign="top">
          398  +  @   <input type="text" name="t" size="25" value="%h(zETime)" />
          399  +  @ </td></tr>
          400  +
          401  +  @ <tr><td align="right" valign="top"><b>Timeline&nbsp;Comment:</b></td>
          402  +  @ <td valign="top">
          403  +  @ <textarea name="c" class="eventedit" cols="80" 
          404  +  @  rows="3" wrap="virtual">%h(zComment)</textarea>
          405  +  @ </td></tr>
          406  +
          407  +  @ <tr><td align="right" valign="top"><b>Background&nbsp;Color:</b></td>
          408  +  @ <td valign="top">
          409  +  render_color_chooser(0, zClr, 0, "clr", "cclr");
          410  +  @ </td></tr>
          411  +  
          412  +  @ <tr><td align="right" valign="top"><b>Tags:</b></td>
          413  +  @ <td valign="top">
          414  +  @   <input type="text" name="g" size="40" value="%h(zTags)" />
          415  +  @ </td></tr>
          416  +  
          417  +  @ <tr><td align="right" valign="top"><b>Page&nbsp;Content:</b></td>
          418  +  @ <td valign="top">
          419  +  @ <textarea name="w" class="eventedit" cols="80" 
          420  +  @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
          421  +  @ </td></tr>
          422  +
          423  +  @ <tr><td colspan="2">
          424  +  @ <input type="submit" name="preview" value="Preview Your Changes" />
          425  +  @ <input type="submit" name="submit" value="Apply These Changes" />
          426  +  @ <input type="submit" name="cancel" value="Cancel" />
          427  +  @ </td></tr></table>
          428  +  @ </div></form>
          429  +  style_footer();
          430  +}

Added src/export.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@sqlite.org
           14  +**
           15  +*******************************************************************************
           16  +**
           17  +** This file contains code used to export the content of a Fossil
           18  +** repository in the git-fast-import format.
           19  +*/
           20  +#include "config.h"
           21  +#include "export.h"
           22  +#include <assert.h>
           23  +
           24  +/*
           25  +** Output a "committer" record for the given user.
           26  +*/
           27  +static void print_person(const char *zUser){
           28  +  static Stmt q;
           29  +  const char *zContact;
           30  +  char *zName;
           31  +  char *zEmail;
           32  +  int i, j;
           33  +
           34  +  if( zUser==0 ){
           35  +    printf(" <unknown>");
           36  +    return;
           37  +  }
           38  +  db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
           39  +  db_bind_text(&q, ":user", zUser);
           40  +  if( db_step(&q)!=SQLITE_ROW ){
           41  +    db_reset(&q);
           42  +    for(i=0; zUser[i] && zUser[i]!='>' && zUser[i]!='<'; i++){}
           43  +    if( zUser[i]==0 ){
           44  +      printf(" %s <%s>", zUser, zUser);
           45  +      return;
           46  +    }
           47  +    zName = mprintf("%s", zUser);
           48  +    for(i=j=0; zName[i]; i++){
           49  +      if( zName[i]!='<' && zName[i]!='>' ){
           50  +        zName[j++] = zName[i];
           51  +      }
           52  +    }
           53  +    zName[j] = 0;
           54  +    printf(" %s <%s>", zName, zUser);
           55  +    free(zName);
           56  +    return;
           57  +  }
           58  +  zContact = db_column_text(&q, 0);
           59  +  for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){}
           60  +  if( zContact[i]==0 ){
           61  +    printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser);
           62  +    db_reset(&q);
           63  +    return;
           64  +  }
           65  +  if( zContact[i]=='<' ){
           66  +    zEmail = mprintf("%s", &zContact[i]);
           67  +    for(i=0; zEmail[i] && zEmail[i]!='>'; i++){}
           68  +    if( zEmail[i]=='>' ) zEmail[i+1] = 0;
           69  +  }else{
           70  +    zEmail = mprintf("<%s>", zUser);
           71  +  }
           72  +  zName = mprintf("%.*s", i, zContact);
           73  +  for(i=j=0; zName[i]; i++){
           74  +    if( zName[i]!='"' ) zName[j++] = zName[i];
           75  +  }
           76  +  zName[j] = 0;
           77  +  printf(" %s %s", zName, zEmail);
           78  +  free(zName);
           79  +  free(zEmail);
           80  +  db_reset(&q);
           81  +}
           82  +
           83  +#define BLOBMARK(rid)   ((rid) * 2)
           84  +#define COMMITMARK(rid) ((rid) * 2 + 1)
           85  +
           86  +/*
           87  +** COMMAND: export
           88  +**
           89  +** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
           90  +**
           91  +** Write an export of all check-ins to standard output.  The export is
           92  +** written in the git-fast-export file format assuming the --git option is
           93  +** provided.  The git-fast-export format is currently the only VCS 
           94  +** interchange format supported, though other formats may be added in
           95  +** the future.
           96  +**
           97  +** Run this command within a checkout.  Or use the -R or --repository
           98  +** option to specify a Fossil repository to be exported.
           99  +**
          100  +** Only check-ins are exported using --git.  Git does not support tickets 
          101  +** or wiki or events or attachments, so none of those are exported.
          102  +**
          103  +** If the "--import-marks FILE" option is used, it contains a list of
          104  +** rids to skip.
          105  +**
          106  +** If the "--export-marks FILE" option is used, the rid of all commits and
          107  +** blobs written on exit for use with "--import-marks" on the next run.
          108  +**
          109  +** Options:
          110  +**   --export-marks FILE          export rids of exported data to FILE
          111  +**   --import-marks FILE          read rids of data to ignore from FILE
          112  +**   --repository|-R REPOSITORY   export the given REPOSITORY
          113  +**   
          114  +** See also: import
          115  +*/
          116  +void export_cmd(void){
          117  +  Stmt q, q2, q3;
          118  +  int i;
          119  +  Bag blobs, vers;
          120  +  const char *markfile_in;
          121  +  const char *markfile_out;
          122  +
          123  +  bag_init(&blobs);
          124  +  bag_init(&vers);
          125  +
          126  +  find_option("git", 0, 0);   /* Ignore the --git option for now */
          127  +  markfile_in = find_option("import-marks", 0, 1);
          128  +  markfile_out = find_option("export-marks", 0, 1);
          129  +
          130  +  db_find_and_open_repository(0, 2);
          131  +  verify_all_options();
          132  +  if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); }
          133  +
          134  +  db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)");
          135  +  db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)");
          136  +  if( markfile_in!=0 ){
          137  +    Stmt qb,qc;
          138  +    char line[100];
          139  +    FILE *f;
          140  +
          141  +    f = fossil_fopen(markfile_in, "r");
          142  +    if( f==0 ){
          143  +      fossil_panic("cannot open %s for reading", markfile_in);
          144  +    }
          145  +    db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
          146  +    db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
          147  +    while( fgets(line, sizeof(line), f)!=0 ){
          148  +      if( *line == 'b' ){
          149  +        db_bind_text(&qb, ":rid", line + 1);
          150  +        db_step(&qb);
          151  +        db_reset(&qb);
          152  +        bag_insert(&blobs, atoi(line + 1));
          153  +      }else if( *line == 'c' ){
          154  +        db_bind_text(&qc, ":rid", line + 1);
          155  +        db_step(&qc);
          156  +        db_reset(&qc);
          157  +        bag_insert(&vers, atoi(line + 1));
          158  +      }else{
          159  +        fossil_panic("bad input from %s: %s", markfile_in, line);
          160  +      }
          161  +    }
          162  +    db_finalize(&qb);
          163  +    db_finalize(&qc);
          164  +    fclose(f);
          165  +  }
          166  +
          167  +  /* Step 1:  Generate "blob" records for every artifact that is part
          168  +  ** of a check-in 
          169  +  */
          170  +  fossil_binary_mode(stdout);
          171  +  db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)");
          172  +  db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)");
          173  +  db_multi_exec(
          174  +    "INSERT INTO newblob"
          175  +    " SELECT DISTINCT fid,"
          176  +    "  CASE WHEN EXISTS(SELECT 1 FROM delta"
          177  +                       " WHERE rid=fid"
          178  +                       "   AND NOT EXISTS(SELECT 1 FROM oldblob"
          179  +                                         " WHERE srcid=fid))"
          180  +    "   THEN (SELECT srcid FROM delta WHERE rid=fid)"
          181  +    "   ELSE 0"
          182  +    "  END"
          183  +    " FROM mlink"
          184  +    " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)");
          185  +  db_prepare(&q,
          186  +    "SELECT DISTINCT fid FROM mlink"
          187  +    " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)");
          188  +  db_prepare(&q2, "INSERT INTO oldblob VALUES (:rid)");
          189  +  db_prepare(&q3, "SELECT rid FROM newblob WHERE srcid= (:srcid)");
          190  +  while( db_step(&q)==SQLITE_ROW ){
          191  +    int rid = db_column_int(&q, 0);
          192  +    Blob content;
          193  +
          194  +    while( !bag_find(&blobs, rid) ){
          195  +      content_get(rid, &content);
          196  +      db_bind_int(&q2, ":rid", rid);
          197  +      db_step(&q2);
          198  +      db_reset(&q2);
          199  +      printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content));
          200  +      bag_insert(&blobs, rid);
          201  +      fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
          202  +      printf("\n");
          203  +      blob_reset(&content);
          204  +
          205  +      db_bind_int(&q3, ":srcid", rid);
          206  +      if( db_step(&q3) != SQLITE_ROW ){
          207  +        db_reset(&q3);
          208  +        break;
          209  +      }
          210  +      rid = db_column_int(&q3, 0);
          211  +      db_reset(&q3);
          212  +    }
          213  +  }
          214  +  db_finalize(&q);
          215  +  db_finalize(&q2);
          216  +  db_finalize(&q3);
          217  +
          218  +  /* Output the commit records.
          219  +  */
          220  +  db_prepare(&q,
          221  +    "SELECT strftime('%%s',mtime), objid, coalesce(comment,ecomment),"
          222  +    "       coalesce(user,euser),"
          223  +    "       (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)"
          224  +    "  FROM event"
          225  +    " WHERE type='ci' AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE objid=rid)"
          226  +    " ORDER BY mtime ASC",
          227  +    TAG_BRANCH
          228  +  );
          229  +  db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)");
          230  +  while( db_step(&q)==SQLITE_ROW ){
          231  +    Stmt q4;
          232  +    const char *zSecondsSince1970 = db_column_text(&q, 0);
          233  +    int ckinId = db_column_int(&q, 1);
          234  +    const char *zComment = db_column_text(&q, 2);
          235  +    const char *zUser = db_column_text(&q, 3);
          236  +    const char *zBranch = db_column_text(&q, 4);
          237  +    char *zBr;
          238  +
          239  +    bag_insert(&vers, ckinId);
          240  +    db_bind_int(&q2, ":rid", ckinId);
          241  +    db_step(&q2);
          242  +    db_reset(&q2);
          243  +    if( zBranch==0 ) zBranch = "trunk";
          244  +    zBr = mprintf("%s", zBranch);
          245  +    for(i=0; zBr[i]; i++){
          246  +      if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
          247  +    }
          248  +    printf("commit refs/heads/%s\nmark :%d\n", zBr, COMMITMARK(ckinId));
          249  +    free(zBr);
          250  +    printf("committer");
          251  +    print_person(zUser);
          252  +    printf(" %s +0000\n", zSecondsSince1970);
          253  +    if( zComment==0 ) zComment = "null comment";
          254  +    printf("data %d\n%s\n", (int)strlen(zComment), zComment);
          255  +    db_prepare(&q3,
          256  +      "SELECT pid FROM plink"
          257  +      " WHERE cid=%d AND isprim"
          258  +      "   AND pid IN (SELECT objid FROM event)",
          259  +      ckinId
          260  +    );
          261  +    if( db_step(&q3) == SQLITE_ROW ){
          262  +      printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0)));
          263  +      db_prepare(&q4,
          264  +        "SELECT pid FROM plink"
          265  +        " WHERE cid=%d AND NOT isprim"
          266  +        "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
          267  +        " ORDER BY pid",
          268  +        ckinId);
          269  +      while( db_step(&q4)==SQLITE_ROW ){
          270  +        printf("merge :%d\n", COMMITMARK(db_column_int(&q4,0)));
          271  +      }
          272  +      db_finalize(&q4);
          273  +    }else{
          274  +      printf("deleteall\n");
          275  +    }
          276  +
          277  +    db_prepare(&q4,
          278  +      "SELECT filename.name, mlink.fid, mlink.mperm FROM mlink"
          279  +      " JOIN filename ON filename.fnid=mlink.fnid"
          280  +      " WHERE mlink.mid=%d",
          281  +      ckinId
          282  +    );
          283  +    while( db_step(&q4)==SQLITE_ROW ){
          284  +      const char *zName = db_column_text(&q4,0);
          285  +      int zNew = db_column_int(&q4,1);
          286  +      int mPerm = db_column_int(&q4,2);
          287  +      if( zNew==0)
          288  +        printf("D %s\n", zName);
          289  +      else if( bag_find(&blobs, zNew) ) {
          290  +        const char *zPerm;
          291  +        switch( mPerm ){
          292  +          case PERM_LNK:  zPerm = "120000";   break;
          293  +          case PERM_EXE:  zPerm = "100755";   break;
          294  +          default:        zPerm = "100644";   break;
          295  +        }
          296  +        printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
          297  +      }
          298  +    }
          299  +    db_finalize(&q4);
          300  +    db_finalize(&q3);
          301  +    printf("\n");
          302  +  }
          303  +  db_finalize(&q2);
          304  +  db_finalize(&q);
          305  +  bag_clear(&blobs);
          306  +  manifest_cache_clear();
          307  +
          308  +
          309  +  /* Output tags */
          310  +  db_prepare(&q,
          311  +     "SELECT tagname, rid, strftime('%%s',mtime)"
          312  +     "  FROM tagxref JOIN tag USING(tagid)"
          313  +     " WHERE tagtype=1 AND tagname GLOB 'sym-*'"
          314  +  );
          315  +  while( db_step(&q)==SQLITE_ROW ){
          316  +    const char *zTagname = db_column_text(&q, 0);
          317  +    char *zEncoded = 0;
          318  +    int rid = db_column_int(&q, 1);
          319  +    const char *zSecSince1970 = db_column_text(&q, 2);
          320  +    int i;
          321  +    if( rid==0 || !bag_find(&vers, rid) ) continue;
          322  +    zTagname += 4;
          323  +    zEncoded = mprintf("%s", zTagname);
          324  +    for(i=0; zEncoded[i]; i++){
          325  +      if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
          326  +    }
          327  +    printf("tag %s\n", zEncoded);
          328  +    printf("from :%d\n", COMMITMARK(rid));
          329  +    printf("tagger <tagger> %s +0000\n", zSecSince1970);
          330  +    printf("data 0\n");
          331  +    fossil_free(zEncoded);
          332  +  }
          333  +  db_finalize(&q);
          334  +  bag_clear(&vers);
          335  +
          336  +  if( markfile_out!=0 ){
          337  +    FILE *f;
          338  +    f = fossil_fopen(markfile_out, "w");
          339  +    if( f == 0 ){
          340  +      fossil_panic("cannot open %s for writing", markfile_out);
          341  +    }
          342  +    db_prepare(&q, "SELECT rid FROM oldblob");
          343  +    while( db_step(&q)==SQLITE_ROW ){
          344  +      fprintf(f, "b%d\n", db_column_int(&q, 0));
          345  +    }
          346  +    db_finalize(&q);
          347  +    db_prepare(&q, "SELECT rid FROM oldcommit");
          348  +    while( db_step(&q)==SQLITE_ROW ){
          349  +      fprintf(f, "c%d\n", db_column_int(&q, 0));
          350  +    }
          351  +    db_finalize(&q);
          352  +    if( ferror(f)!=0 || fclose(f)!=0 ) {
          353  +      fossil_panic("error while writing %s", markfile_out);
          354  +    }
          355  +  }
          356  +}

Changes to src/file.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** File utilities
           18  +** File utilities.
           19  +**
           20  +** Functions named file_* are generic functions that always follow symlinks.
           21  +**
           22  +** Functions named file_wd_* are to be used for files inside working
           23  +** directories. They follow symlinks depending on 'allow-symlinks' setting.
    19     24   */
    20     25   #include "config.h"
    21     26   #include <sys/types.h>
    22     27   #include <sys/stat.h>
    23     28   #include <unistd.h>
           29  +#include <string.h>
           30  +#include <errno.h>
    24     31   #include "file.h"
    25     32   
           33  +/*
           34  +** On Windows, include the Platform SDK header file.
           35  +*/
           36  +#ifdef _WIN32
           37  +# include <direct.h>
           38  +# include <windows.h>
           39  +# include <sys/utime.h>
           40  +#else
           41  +# include <sys/time.h>
           42  +#endif
           43  +
    26     44   /*
    27     45   ** The file status information from the most recent stat() call.
           46  +**
           47  +** Use _stati64 rather than stat on windows, in order to handle files
           48  +** larger than 2GB.
    28     49   */
    29         -static struct stat fileStat;
           50  +#if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER))
           51  +# undef stat
           52  +# define stat _stati64
           53  +#endif
           54  +/*
           55  +** On Windows S_ISLNK always returns FALSE.
           56  +*/
           57  +#if !defined(S_ISLNK)
           58  +# define S_ISLNK(x) (0)
           59  +#endif
    30     60   static int fileStatValid = 0;
           61  +static struct stat fileStat;
           62  +
           63  +/*
           64  +** Fill stat buf with information received from stat() or lstat().
           65  +** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
           66  +**
           67  +*/
           68  +static int fossil_stat(const char *zFilename, struct stat *buf, int isWd){
           69  +#if !defined(_WIN32)
           70  +  if( isWd && g.allowSymlinks ){
           71  +    return lstat(zFilename, buf);
           72  +  }else{
           73  +    return stat(zFilename, buf);
           74  +  }
           75  +#else
           76  +  int rc = 0;
           77  +  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
           78  +  rc = _wstati64(zMbcs, buf);
           79  +  fossil_unicode_free(zMbcs);
           80  +  return rc;
           81  +#endif
           82  +}
    31     83   
    32     84   /*
    33     85   ** Fill in the fileStat variable for the file named zFilename.
    34     86   ** If zFilename==0, then use the previous value of fileStat if
    35     87   ** there is a previous value.
    36     88   **
           89  +** If isWd is TRUE, do lstat() instead of stat() if allow-symlinks is on.
           90  +**
    37     91   ** Return the number of errors.  No error messages are generated.
    38     92   */
    39         -static int getStat(const char *zFilename){
           93  +static int getStat(const char *zFilename, int isWd){
    40     94     int rc = 0;
    41     95     if( zFilename==0 ){
    42     96       if( fileStatValid==0 ) rc = 1;
    43     97     }else{
    44         -    if( stat(zFilename, &fileStat)!=0 ){
           98  +    if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){
    45     99         fileStatValid = 0;
    46    100         rc = 1;
    47    101       }else{
    48    102         fileStatValid = 1;
    49    103         rc = 0;
    50    104       }
    51    105     }
    52    106     return rc;
    53    107   }
    54    108   
    55         -
    56    109   /*
    57    110   ** Return the size of a file in bytes.  Return -1 if the file does not
    58    111   ** exist.  If zFilename is NULL, return the size of the most recently
    59    112   ** stat-ed file.
    60    113   */
    61    114   i64 file_size(const char *zFilename){
    62         -  return getStat(zFilename) ? -1 : fileStat.st_size;
          115  +  return getStat(zFilename, 0) ? -1 : fileStat.st_size;
          116  +}
          117  +
          118  +/*
          119  +** Same as file_size(), but takes into account symlinks.
          120  +*/
          121  +i64 file_wd_size(const char *zFilename){
          122  +  return getStat(zFilename, 1) ? -1 : fileStat.st_size;
    63    123   }
    64    124   
    65    125   /*
    66    126   ** Return the modification time for a file.  Return -1 if the file
    67    127   ** does not exist.  If zFilename is NULL return the size of the most
    68    128   ** recently stat-ed file.
    69    129   */
    70    130   i64 file_mtime(const char *zFilename){
    71         -  return getStat(zFilename) ? -1 : fileStat.st_mtime;
          131  +  return getStat(zFilename, 0) ? -1 : fileStat.st_mtime;
          132  +}
          133  +
          134  +/*
          135  +** Same as file_mtime(), but takes into account symlinks.
          136  +*/
          137  +i64 file_wd_mtime(const char *zFilename){
          138  +  return getStat(zFilename, 1) ? -1 : fileStat.st_mtime;
          139  +}
          140  +
          141  +/*
          142  +** Return TRUE if the named file is an ordinary file or symlink
          143  +** and symlinks are allowed.
          144  +** Return false for directories, devices, fifos, etc.
          145  +*/
          146  +int file_wd_isfile_or_link(const char *zFilename){
          147  +  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode) ||
          148  +                                     S_ISLNK(fileStat.st_mode);
    72    149   }
    73    150   
    74    151   /*
    75    152   ** Return TRUE if the named file is an ordinary file.  Return false
    76    153   ** for directories, devices, fifos, symlinks, etc.
    77    154   */
    78    155   int file_isfile(const char *zFilename){
    79         -  return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
          156  +  return getStat(zFilename, 0) ? 0 : S_ISREG(fileStat.st_mode);
          157  +}
          158  +
          159  +/*
          160  +** Same as file_isfile(), but takes into account symlinks.
          161  +*/
          162  +int file_wd_isfile(const char *zFilename){
          163  +  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode);
          164  +}
          165  +
          166  +/*
          167  +** Create symlink to file on Unix, or plain-text file with
          168  +** symlink target if "allow-symlinks" is off or we're on Windows.
          169  +**
          170  +** Arguments: target file (symlink will point to it), link file
          171  +**/
          172  +void symlink_create(const char *zTargetFile, const char *zLinkFile){
          173  +#if !defined(_WIN32)
          174  +  if( g.allowSymlinks ){
          175  +    int i, nName;
          176  +    char *zName, zBuf[1000];
          177  +
          178  +    nName = strlen(zLinkFile);
          179  +    if( nName>=sizeof(zBuf) ){
          180  +      zName = mprintf("%s", zLinkFile);
          181  +    }else{
          182  +      zName = zBuf;
          183  +      memcpy(zName, zLinkFile, nName+1);
          184  +    }
          185  +    nName = file_simplify_name(zName, nName, 0);
          186  +    for(i=1; i<nName; i++){
          187  +      if( zName[i]=='/' ){
          188  +        zName[i] = 0;
          189  +          if( file_mkdir(zName, 1) ){
          190  +            fossil_fatal_recursive("unable to create directory %s", zName);
          191  +            return;
          192  +          }
          193  +        zName[i] = '/';
          194  +      }
          195  +    }
          196  +    if( zName!=zBuf ) free(zName);
          197  +
          198  +    if( symlink(zTargetFile, zName)!=0 ){
          199  +      fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
          200  +    }
          201  +  }else
          202  +#endif
          203  +  {
          204  +    Blob content;
          205  +    blob_set(&content, zTargetFile);
          206  +    blob_write_to_file(&content, zLinkFile);
          207  +    blob_reset(&content);
          208  +  }
          209  +}
          210  +
          211  +/*
          212  +** Copy symbolic link from zFrom to zTo.
          213  +*/
          214  +void symlink_copy(const char *zFrom, const char *zTo){
          215  +  Blob content;
          216  +  blob_read_link(&content, zFrom);
          217  +  symlink_create(blob_str(&content), zTo);
          218  +  blob_reset(&content);
          219  +}
          220  +
          221  +/*
          222  +** Return file permissions (normal, executable, or symlink):
          223  +**   - PERM_EXE if file is executable;
          224  +**   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
          225  +**   - PERM_REG for all other cases (regular file, directory, fifo, etc).
          226  +*/
          227  +int file_wd_perm(const char *zFilename){
          228  +  if( getStat(zFilename, 1) ) return PERM_REG;
          229  +#if defined(_WIN32)
          230  +#  ifndef S_IXUSR
          231  +#    define S_IXUSR  _S_IEXEC
          232  +#  endif
          233  +  if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
          234  +    return PERM_EXE;
          235  +  else
          236  +    return PERM_REG;
          237  +#else
          238  +  if( S_ISREG(fileStat.st_mode) &&
          239  +      ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0 )
          240  +    return PERM_EXE;
          241  +  else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) )
          242  +    return PERM_LNK;
          243  +  else
          244  +    return PERM_REG;
          245  +#endif
    80    246   }
    81    247   
    82    248   /*
    83    249   ** Return TRUE if the named file is an executable.  Return false
    84    250   ** for directories, devices, fifos, symlinks, etc.
    85    251   */
    86         -int file_isexe(const char *zFilename){
    87         -  if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
    88         -#ifdef __MINGW32__
    89         -  return ((S_IXUSR)&fileStat.st_mode)!=0;
    90         -#else
    91         -  return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
    92         -#endif
          252  +int file_wd_isexe(const char *zFilename){
          253  +  return file_wd_perm(zFilename)==PERM_EXE;
    93    254   }
    94    255   
          256  +/*
          257  +** Return TRUE if the named file is a symlink and symlinks are allowed.
          258  +** Return false for all other cases.
          259  +**
          260  +** On Windows, always return False.
          261  +*/
          262  +int file_wd_islink(const char *zFilename){
          263  +  return file_wd_perm(zFilename)==PERM_LNK;
          264  +}
    95    265   
    96    266   /*
    97    267   ** Return 1 if zFilename is a directory.  Return 0 if zFilename
    98    268   ** does not exist.  Return 2 if zFilename exists but is something
    99    269   ** other than a directory.
   100    270   */
   101    271   int file_isdir(const char *zFilename){
   102    272     int rc;
   103    273   
   104    274     if( zFilename ){
   105    275       char *zFN = mprintf("%s", zFilename);
   106         -    file_simplify_name(zFN, -1);
   107         -    rc = getStat(zFN);
          276  +    file_simplify_name(zFN, -1, 0);
          277  +    rc = getStat(zFN, 0);
          278  +    free(zFN);
          279  +  }else{
          280  +    rc = getStat(0, 0);
          281  +  }
          282  +  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
          283  +}
          284  +
          285  +/*
          286  +** Same as file_isdir(), but takes into account symlinks.
          287  +*/
          288  +int file_wd_isdir(const char *zFilename){
          289  +  int rc;
          290  +
          291  +  if( zFilename ){
          292  +    char *zFN = mprintf("%s", zFilename);
          293  +    file_simplify_name(zFN, -1, 0);
          294  +    rc = getStat(zFN, 1);
   108    295       free(zFN);
   109    296     }else{
   110         -    rc = getStat(0);
          297  +    rc = getStat(0, 1);
   111    298     }
   112    299     return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
   113    300   }
   114    301   
          302  +
          303  +/*
          304  +** Wrapper around the access() system call.
          305  +*/
          306  +int file_access(const char *zFilename, int flags){
          307  +#ifdef _WIN32
          308  +  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
          309  +  int rc = _waccess(zMbcs, flags);
          310  +  fossil_unicode_free(zMbcs);
          311  +#else
          312  +  int rc = access(zFilename, flags);
          313  +#endif
          314  +  return rc;
          315  +}
          316  +
          317  +/*
          318  +** Find an unused filename similar to zBase with zSuffix appended.
          319  +**
          320  +** Make the name relative to the working directory if relFlag is true.
          321  +**
          322  +** Space to hold the new filename is obtained form mprintf() and should
          323  +** be freed by the caller.
          324  +*/
          325  +char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
          326  +  char *z = 0;
          327  +  int cnt = 0;
          328  +  z = mprintf("%s-%s", zBase, zSuffix);
          329  +  while( file_size(z)>=0 ){
          330  +    fossil_free(z);
          331  +    z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
          332  +  }
          333  +  if( relFlag ){
          334  +    Blob x;
          335  +    file_relative_name(z, &x, 0);
          336  +    fossil_free(z);
          337  +    z = blob_str(&x);
          338  +  }
          339  +  return z;
          340  +}
          341  +
   115    342   /*
   116    343   ** Return the tail of a file pathname.  The tail is the last component
   117    344   ** of the path.  For example, the tail of "/a/b/c.d" is "c.d".
   118    345   */
   119    346   const char *file_tail(const char *z){
   120    347     const char *zTail = z;
   121    348     while( z[0] ){
................................................................................
   128    355   /*
   129    356   ** Copy the content of a file from one place to another.
   130    357   */
   131    358   void file_copy(const char *zFrom, const char *zTo){
   132    359     FILE *in, *out;
   133    360     int got;
   134    361     char zBuf[8192];
   135         -  in = fopen(zFrom, "rb");
          362  +  in = fossil_fopen(zFrom, "rb");
   136    363     if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom);
   137         -  out = fopen(zTo, "wb");
          364  +  out = fossil_fopen(zTo, "wb");
   138    365     if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo);
   139    366     while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
   140    367       fwrite(zBuf, 1, got, out);
   141    368     }
   142    369     fclose(in);
   143    370     fclose(out);
   144    371   }
   145    372   
   146    373   /*
   147         -** Set or clear the execute bit on a file.
          374  +** Set or clear the execute bit on a file.  Return true if a change
          375  +** occurred and false if this routine is a no-op.
   148    376   */
   149         -void file_setexe(const char *zFilename, int onoff){
   150         -#ifndef __MINGW32__
          377  +int file_wd_setexe(const char *zFilename, int onoff){
          378  +  int rc = 0;
          379  +#if !defined(_WIN32)
   151    380     struct stat buf;
   152         -  if( stat(zFilename, &buf)!=0 ) return;
          381  +  if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0;
   153    382     if( onoff ){
   154         -    if( (buf.st_mode & 0111)!=0111 ){
   155         -      chmod(zFilename, buf.st_mode | 0111);
          383  +    int targetMode = (buf.st_mode & 0444)>>2;
          384  +    if( (buf.st_mode & 0111)!=targetMode ){
          385  +      chmod(zFilename, buf.st_mode | targetMode);
          386  +      rc = 1;
   156    387       }
   157    388     }else{
   158    389       if( (buf.st_mode & 0111)!=0 ){
   159    390         chmod(zFilename, buf.st_mode & ~0111);
          391  +      rc = 1;
   160    392       }
   161    393     }
   162         -#endif /* __MINGW32__ */
          394  +#endif /* _WIN32 */
          395  +  return rc;
          396  +}
          397  +
          398  +/*
          399  +** Set the mtime for a file.
          400  +*/
          401  +void file_set_mtime(const char *zFilename, i64 newMTime){
          402  +#if !defined(_WIN32)
          403  +  struct timeval tv[2];
          404  +  memset(tv, 0, sizeof(tv[0])*2);
          405  +  tv[0].tv_sec = newMTime;
          406  +  tv[1].tv_sec = newMTime;
          407  +  utimes(zFilename, tv);
          408  +#else
          409  +  struct _utimbuf tb;
          410  +  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
          411  +  tb.actime = newMTime;
          412  +  tb.modtime = newMTime;
          413  +  _wutime(zMbcs, &tb);
          414  +  fossil_unicode_free(zMbcs);
          415  +#endif
          416  +}
          417  +
          418  +/*
          419  +** COMMAND: test-set-mtime
          420  +**
          421  +** Usage: %fossil test-set-mtime FILENAME DATE/TIME
          422  +**
          423  +** Sets the mtime of the named file to the date/time shown.
          424  +*/
          425  +void test_set_mtime(void){
          426  +  const char *zFile;
          427  +  char *zDate;
          428  +  i64 iMTime;
          429  +  if( g.argc!=4 ){
          430  +    usage("test-set-mtime FILENAME DATE/TIME");
          431  +  }
          432  +  db_open_or_attach(":memory:", "mem", 0);
          433  +  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
          434  +  zFile = g.argv[2];
          435  +  file_set_mtime(zFile, iMTime);
          436  +  iMTime = file_wd_mtime(zFile);
          437  +  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
          438  +  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
          439  +}
          440  +
          441  +/*
          442  +** Delete a file.
          443  +*/
          444  +void file_delete(const char *zFilename){
          445  +#ifdef _WIN32
          446  +  wchar_t *z = fossil_utf8_to_unicode(zFilename);
          447  +  _wunlink(z);
          448  +  fossil_unicode_free(z);
          449  +#else
          450  +  unlink(zFilename);
          451  +#endif
   163    452   }
   164    453   
   165    454   /*
   166    455   ** Create the directory named in the argument, if it does not already
   167         -** exist.  If forceFlag is 1, delete any prior non-directory object 
          456  +** exist.  If forceFlag is 1, delete any prior non-directory object
   168    457   ** with the same name.
   169    458   **
   170    459   ** Return the number of errors.
   171    460   */
   172    461   int file_mkdir(const char *zName, int forceFlag){
   173         -  int rc = file_isdir(zName);
          462  +  int rc = file_wd_isdir(zName);
   174    463     if( rc==2 ){
   175    464       if( !forceFlag ) return 1;
   176         -    unlink(zName);
          465  +    file_delete(zName);
   177    466     }
   178    467     if( rc!=1 ){
   179         -#ifdef __MINGW32__
   180         -    return mkdir(zName);
          468  +#if defined(_WIN32)
          469  +    int rc;
          470  +    wchar_t *zMbcs = fossil_utf8_to_unicode(zName);
          471  +    rc = _wmkdir(zMbcs);
          472  +    fossil_unicode_free(zMbcs);
          473  +    return rc;
   181    474   #else
   182    475       return mkdir(zName, 0755);
   183    476   #endif
   184    477     }
   185    478     return 0;
   186    479   }
   187    480   
................................................................................
   188    481   /*
   189    482   ** Return true if the filename given is a valid filename for
   190    483   ** a file in a repository.  Valid filenames follow all of the
   191    484   ** following rules:
   192    485   **
   193    486   **     *  Does not begin with "/"
   194    487   **     *  Does not contain any path element named "." or ".."
   195         -**     *  Does not contain any of these characters in the path: "\*[]?"
          488  +**     *  Does not contain any of these characters in the path: "\"
   196    489   **     *  Does not end with "/".
   197    490   **     *  Does not contain two or more "/" characters in a row.
   198    491   **     *  Contains at least one character
          492  +**
          493  +** Invalid UTF8 characters result in a false return if bStrictUtf8 is
          494  +** true.  If bStrictUtf8 is false, invalid UTF8 characters are silently
          495  +** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
          496  +** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters)
          497  +**
          498  +** The bStrictUtf8 flag is true for new inputs, but is false when parsing
          499  +** legacy manifests, for backwards compatibility.
   199    500   */
   200         -int file_is_simple_pathname(const char *z){
          501  +int file_is_simple_pathname(const char *z, int bStrictUtf8){
   201    502     int i;
   202         -  if( *z=='/' || *z==0 ) return 0;
   203         -  if( *z=='.' ){
          503  +  unsigned char c = (unsigned char) z[0];
          504  +  char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00;
          505  +  if( c=='/' || c==0 ) return 0;
          506  +  if( c=='.' ){
   204    507       if( z[1]=='/' || z[1]==0 ) return 0;
   205    508       if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
   206    509     }
   207         -  for(i=0; z[i]; i++){
   208         -    if( z[i]=='\\' || z[i]=='*' || z[i]=='[' || z[i]==']' || z[i]=='?' ){
          510  +  for(i=0; (c=(unsigned char)z[i])!=0; i++){
          511  +    if( c & maskNonAscii ){
          512  +      if( (z[++i]&0xc0)!=0x80 ){
          513  +        /* Invalid first continuation byte */
          514  +        return 0;
          515  +      }
          516  +      if( c<0xc2 ){
          517  +        /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */
          518  +        return 0;
          519  +      }else if( (c&0xe0)==0xe0 ){
          520  +        /* 3-byte or more */
          521  +        int unicode;
          522  +        if( c&0x10 ){
          523  +          /* Unicode characters > U+FFFF are not supported.
          524  +           * Windows XP and earlier cannot handle them.
          525  +           */
          526  +          return 0;
          527  +        }
          528  +        /* This is a 3-byte UTF-8 character */
          529  +        unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f);
          530  +        if( unicode <= 0x07ff ){
          531  +          /* overlong form */
          532  +          return 0;
          533  +        }else if( unicode>=0xe000 ){
          534  +          /* U+E000..U+FFFF */
          535  +          if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){
          536  +            /* U+E000..U+F8FF are for private use.
          537  +             * U+FFFE..U+FFFF are noncharacters. */
          538  +            return 0;
          539  +          } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){
          540  +            /* U+FDD0..U+FDEF are noncharacters. */
          541  +            return 0;
          542  +          }
          543  +        }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){
          544  +          /* U+D800..U+DFFF are for surrogate pairs. */
          545  +          return 0;
          546  +        }
          547  +        if( (z[++i]&0xc0)!=0x80 ){
          548  +          /* Invalid second continuation byte */
          549  +          return 0;
          550  +        }
          551  +      }
          552  +    }else if( bStrictUtf8 && (c=='\\') ){
   209    553         return 0;
   210    554       }
   211         -    if( z[i]=='/' ){
          555  +    if( c=='/' ){
   212    556         if( z[i+1]=='/' ) return 0;
   213    557         if( z[i+1]=='.' ){
   214    558           if( z[i+2]=='/' || z[i+2]==0 ) return 0;
   215    559           if( z[i+2]=='.' && (z[i+3]=='/' || z[i+3]==0) ) return 0;
   216    560         }
   217    561       }
   218    562     }
   219    563     if( z[i-1]=='/' ) return 0;
   220    564     return 1;
   221    565   }
          566  +
          567  +/*
          568  +** If the last component of the pathname in z[0]..z[j-1] is something
          569  +** other than ".." then back it out and return true.  If the last
          570  +** component is empty or if it is ".." then return false.
          571  +*/
          572  +static int backup_dir(const char *z, int *pJ){
          573  +  int j = *pJ;
          574  +  int i;
          575  +  if( j<=0 ) return 0;
          576  +  for(i=j-1; i>0 && z[i-1]!='/'; i--){}
          577  +  if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
          578  +  *pJ = i-1;
          579  +  return 1;
          580  +}
   222    581   
   223    582   /*
   224    583   ** Simplify a filename by
   225    584   **
          585  +**  * Convert all \ into / on windows
   226    586   **  * removing any trailing and duplicate /
   227    587   **  * removing /./
   228    588   **  * removing /A/../
   229    589   **
   230    590   ** Changes are made in-place.  Return the new name length.
          591  +** If the slash parameter is non-zero, the trailing slash, if any,
          592  +** is retained.
   231    593   */
   232         -int file_simplify_name(char *z, int n){
          594  +int file_simplify_name(char *z, int n, int slash){
   233    595     int i, j;
   234    596     if( n<0 ) n = strlen(z);
   235         -#ifdef __MINGW32__
          597  +
          598  +  /* On windows convert all \ characters to / */
          599  +#if defined(_WIN32)
   236    600     for(i=0; i<n; i++){
   237    601       if( z[i]=='\\' ) z[i] = '/';
   238    602     }
   239    603   #endif
   240         -  while( n>1 && z[n-1]=='/' ){ n--; }
   241         -  for(i=j=0; i<n; i++){
          604  +
          605  +  /* Removing trailing "/" characters */
          606  +  if( !slash ){
          607  +    while( n>1 && z[n-1]=='/' ){ n--; }
          608  +  }
          609  +
          610  +  /* Remove duplicate '/' characters.  Except, two // at the beginning
          611  +  ** of a pathname is allowed since this is important on windows. */
          612  +  for(i=j=1; i<n; i++){
          613  +    z[j++] = z[i];
          614  +    while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
          615  +  }
          616  +  n = j;
          617  +
          618  +  /* Skip over zero or more initial "./" sequences */
          619  +  for(i=0; i<n-1 && z[i]=='.' && z[i+1]=='/'; i+=2){}
          620  +
          621  +  /* Begin copying from z[i] back to z[j]... */
          622  +  for(j=0; i<n; i++){
   242    623       if( z[i]=='/' ){
   243         -      if( z[i+1]=='/' ) continue;
          624  +      /* Skip over internal "/." directory components */
   244    625         if( z[i+1]=='.' && (i+2==n || z[i+2]=='/') ){
   245    626           i += 1;
   246    627           continue;
   247    628         }
   248         -      if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') ){
   249         -        while( j>0 && z[j-1]!='/' ){ j--; }
   250         -        if( j>0 ){ j--; }
          629  +
          630  +      /* If this is a "/.." directory component then back out the
          631  +      ** previous term of the directory if it is something other than ".."
          632  +      ** or "."
          633  +      */
          634  +      if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/')
          635  +       && backup_dir(z, &j)
          636  +      ){
   251    637           i += 2;
   252    638           continue;
   253    639         }
   254    640       }
   255         -    z[j++] = z[i];
          641  +    if( j>=0 ) z[j] = z[i];
          642  +    j++;
   256    643     }
          644  +  if( j==0 ) z[j++] = '.';
   257    645     z[j] = 0;
   258    646     return j;
   259    647   }
          648  +
          649  +/*
          650  +** COMMAND: test-simplify-name
          651  +**
          652  +** %fossil test-simplify-name FILENAME...
          653  +**
          654  +** Print the simplified versions of each FILENAME.
          655  +*/
          656  +void cmd_test_simplify_name(void){
          657  +  int i;
          658  +  char *z;
          659  +  for(i=2; i<g.argc; i++){
          660  +    z = mprintf("%s", g.argv[i]);
          661  +    fossil_print("[%s] -> ", z);
          662  +    file_simplify_name(z, -1, 0);
          663  +    fossil_print("[%s]\n", z);
          664  +    fossil_free(z);
          665  +  }
          666  +}
          667  +
          668  +/*
          669  +** Get the current working directory.
          670  +**
          671  +** On windows, the name is converted from unicode to UTF8 and all '\\'
          672  +** characters are converted to '/'.  No conversions are needed on
          673  +** unix.
          674  +*/
          675  +void file_getcwd(char *zBuf, int nBuf){
          676  +#ifdef _WIN32
          677  +  char *zPwdUtf8;
          678  +  int nPwd;
          679  +  int i;
          680  +  wchar_t zPwd[2000];
          681  +  if( _wgetcwd(zPwd, sizeof(zPwd)/sizeof(zPwd[0])-1)==0 ){
          682  +    fossil_fatal("cannot find the current working directory.");
          683  +  }
          684  +  zPwdUtf8 = fossil_filename_to_utf8(zPwd);
          685  +  nPwd = strlen(zPwdUtf8);
          686  +  if( nPwd > nBuf-1 ){
          687  +    fossil_fatal("pwd too big: max %d\n", nBuf-1);
          688  +  }
          689  +  for(i=0; zPwdUtf8[i]; i++) if( zPwdUtf8[i]=='\\' ) zPwdUtf8[i] = '/';
          690  +  memcpy(zBuf, zPwdUtf8, nPwd+1);
          691  +  fossil_filename_free(zPwdUtf8);
          692  +#else
          693  +  if( getcwd(zBuf, nBuf-1)==0 ){
          694  +    if( errno==ERANGE ){
          695  +      fossil_fatal("pwd too big: max %d\n", nBuf-1);
          696  +    }else{
          697  +      fossil_fatal("cannot find current working directory; %s",
          698  +                   strerror(errno));
          699  +    }
          700  +  }
          701  +#endif
          702  +}
          703  +
          704  +/*
          705  +** Return true if zPath is an absolute pathname.  Return false
          706  +** if it is relative.
          707  +*/
          708  +int file_is_absolute_path(const char *zPath){
          709  +  if( zPath[0]=='/'
          710  +#if defined(_WIN32)
          711  +      || zPath[0]=='\\'
          712  +      || (strlen(zPath)>3 && zPath[1]==':'
          713  +           && (zPath[2]=='\\' || zPath[2]=='/'))
          714  +#endif
          715  +  ){
          716  +    return 1;
          717  +  }else{
          718  +    return 0;
          719  +  }
          720  +}
   260    721   
   261    722   /*
   262    723   ** Compute a canonical pathname for a file or directory.
   263    724   ** Make the name absolute if it is relative.
   264    725   ** Remove redundant / characters
   265    726   ** Remove all /./ path elements.
   266    727   ** Convert /A/../ to just /
          728  +** If the slash parameter is non-zero, the trailing slash, if any,
          729  +** is retained.
   267    730   */
   268         -void file_canonical_name(const char *zOrigName, Blob *pOut){
   269         -  if( zOrigName[0]=='/' 
   270         -#ifdef __MINGW32__
   271         -      || zOrigName[0]=='\\'
   272         -      || (strlen(zOrigName)>3 && zOrigName[1]==':'
   273         -           && (zOrigName[2]=='\\' || zOrigName[2]=='/'))
          731  +void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
          732  +  if( file_is_absolute_path(zOrigName) ){
          733  +#if defined(_WIN32)
          734  +    char *zOut;
   274    735   #endif
   275         -  ){
   276    736       blob_set(pOut, zOrigName);
   277    737       blob_materialize(pOut);
          738  +#if defined(_WIN32)
          739  +    /*
          740  +    ** On Windows, normalize the drive letter to upper case.
          741  +    */
          742  +    zOut = blob_str(pOut);
          743  +    if( fossil_isalpha(zOut[0]) && zOut[1]==':' ){
          744  +      zOut[0] = fossil_toupper(zOut[0]);
          745  +    }
          746  +#endif
   278    747     }else{
   279    748       char zPwd[2000];
   280         -    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
   281         -      fprintf(stderr, "pwd too big: max %d\n", (int)sizeof(zPwd)-20);
   282         -      exit(1);
          749  +    file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
          750  +#if defined(_WIN32)
          751  +    /*
          752  +    ** On Windows, normalize the drive letter to upper case.
          753  +    */
          754  +    if( fossil_isalpha(zPwd[0]) && zPwd[1]==':' ){
          755  +      zPwd[0] = fossil_toupper(zPwd[0]);
   283    756       }
          757  +#endif
   284    758       blob_zero(pOut);
   285    759       blob_appendf(pOut, "%//%/", zPwd, zOrigName);
   286    760     }
   287         -  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut)));
          761  +  blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
          762  +                                       blob_size(pOut), slash));
   288    763   }
   289    764   
   290    765   /*
   291    766   ** COMMAND:  test-canonical-name
          767  +** Usage: %fossil test-canonical-name FILENAME...
   292    768   **
   293    769   ** Test the operation of the canonical name generator.
          770  +** Also test Fossil's ability to measure attributes of a file.
   294    771   */
   295    772   void cmd_test_canonical_name(void){
   296    773     int i;
   297    774     Blob x;
   298    775     blob_zero(&x);
   299    776     for(i=2; i<g.argc; i++){
   300         -    file_canonical_name(g.argv[i], &x);
   301         -    printf("%s\n", blob_buffer(&x));
          777  +    char zBuf[100];
          778  +    const char *zName = g.argv[i];
          779  +    file_canonical_name(zName, &x, 0);
          780  +    fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
   302    781       blob_reset(&x);
          782  +    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName));
          783  +    fossil_print("  file_size   = %s\n", zBuf);
          784  +    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName));
          785  +    fossil_print("  file_mtime  = %s\n", zBuf);
          786  +    fossil_print("  file_isfile = %d\n", file_wd_isfile(zName));
          787  +    fossil_print("  file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName));
          788  +    fossil_print("  file_islink = %d\n", file_wd_islink(zName));
          789  +    fossil_print("  file_isexe  = %d\n", file_wd_isexe(zName));
          790  +    fossil_print("  file_isdir  = %d\n", file_wd_isdir(zName));
   303    791     }
   304    792   }
   305    793   
   306    794   /*
   307    795   ** Return TRUE if the given filename is canonical.
   308    796   **
   309    797   ** Canonical names are full pathnames using "/" not "\" and which
   310    798   ** contain no "/./" or "/../" terms.
   311    799   */
   312    800   int file_is_canonical(const char *z){
   313    801     int i;
   314    802     if( z[0]!='/'
   315         -#ifdef __MINGW32__
          803  +#if defined(_WIN32)
   316    804       && (z[0]==0 || z[1]!=':' || z[2]!='/')
   317    805   #endif
   318    806     ) return 0;
   319    807   
   320    808     for(i=0; z[i]; i++){
   321    809       if( z[i]=='\\' ) return 0;
   322    810       if( z[i]=='/' ){
................................................................................
   324    812           if( z[i+2]=='/' || z[i+2]==0 ) return 0;
   325    813           if( z[i+2]=='.' && (z[i+3]=='/' || z[i+3]==0) ) return 0;
   326    814         }
   327    815       }
   328    816     }
   329    817     return 1;
   330    818   }
          819  +
          820  +/*
          821  +** Return a pointer to the first character in a pathname past the
          822  +** drive letter.  This routine is a no-op on unix.
          823  +*/
          824  +char *file_without_drive_letter(char *zIn){
          825  +#ifdef _WIN32
          826  +  if( fossil_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2;
          827  +#endif
          828  +  return zIn;
          829  +}
   331    830   
   332    831   /*
   333    832   ** Compute a pathname for a file or directory that is relative
   334         -** to the current directory.
          833  +** to the current directory.  If the slash parameter is non-zero,
          834  +** the trailing slash, if any, is retained.
   335    835   */
   336         -void file_relative_name(const char *zOrigName, Blob *pOut){
          836  +void file_relative_name(const char *zOrigName, Blob *pOut, int slash){
   337    837     char *zPath;
   338    838     blob_set(pOut, zOrigName);
   339         -  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); 
   340         -  zPath = blob_buffer(pOut);
          839  +  blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
          840  +                                       blob_size(pOut), slash));
          841  +  zPath = file_without_drive_letter(blob_buffer(pOut));
   341    842     if( zPath[0]=='/' ){
   342    843       int i, j;
   343    844       Blob tmp;
   344         -    char zPwd[2000];
   345         -    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
   346         -      fprintf(stderr, "pwd too big: max %d\n", (int)sizeof(zPwd)-20);
   347         -      exit(1);
   348         -    }
   349         -    for(i=1; zPath[i] && zPwd[i]==zPath[i]; i++){}
          845  +    char *zPwd;
          846  +    char zBuf[2000];
          847  +    zPwd = zBuf;
          848  +    file_getcwd(zBuf, sizeof(zBuf)-20);
          849  +    zPwd = file_without_drive_letter(zBuf);
          850  +    i = 1;
          851  +#ifdef _WIN32
          852  +    while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++;
          853  +#else
          854  +    while( zPath[i] && zPwd[i]==zPath[i] ) i++;
          855  +#endif
   350    856       if( zPath[i]==0 ){
   351    857         blob_reset(pOut);
   352    858         if( zPwd[i]==0 ){
   353    859           blob_append(pOut, ".", 1);
   354    860         }else{
   355    861           blob_append(pOut, "..", 2);
   356    862           for(j=i+1; zPwd[j]; j++){
   357         -          if( zPwd[j]=='/' ) {
          863  +          if( zPwd[j]=='/' ){
   358    864               blob_append(pOut, "/..", 3);
   359    865             }
   360    866           }
   361    867         }
   362    868         return;
   363    869       }
   364    870       if( zPwd[i]==0 && zPath[i]=='/' ){
................................................................................
   367    873         blob_append(pOut, &zPath[i+1], -1);
   368    874         blob_reset(&tmp);
   369    875         return;
   370    876       }
   371    877       while( zPath[i-1]!='/' ){ i--; }
   372    878       blob_set(&tmp, "../");
   373    879       for(j=i; zPwd[j]; j++){
   374         -      if( zPwd[j]=='/' ) {
          880  +      if( zPwd[j]=='/' ){
   375    881           blob_append(&tmp, "../", 3);
   376    882         }
   377    883       }
   378    884       blob_append(&tmp, &zPath[i], -1);
   379    885       blob_reset(pOut);
   380    886       memcpy(pOut, &tmp, sizeof(tmp));
   381    887     }
................................................................................
   387    893   ** Test the operation of the relative name generator.
   388    894   */
   389    895   void cmd_test_relative_name(void){
   390    896     int i;
   391    897     Blob x;
   392    898     blob_zero(&x);
   393    899     for(i=2; i<g.argc; i++){
   394         -    file_relative_name(g.argv[i], &x);
   395         -    printf("%s\n", blob_buffer(&x));
          900  +    file_relative_name(g.argv[i], &x, 0);
          901  +    fossil_print("%s\n", blob_buffer(&x));
   396    902       blob_reset(&x);
   397    903     }
   398    904   }
   399    905   
   400    906   /*
   401    907   ** Compute a pathname for a file relative to the root of the local
   402    908   ** tree.  Return TRUE on success.  On failure, print and error
   403    909   ** message and quit if the errFatal flag is true.  If errFatal is
   404    910   ** false, then simply return 0.
   405    911   **
   406    912   ** The root of the tree is defined by the g.zLocalRoot variable.
   407    913   */
   408    914   int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){
   409         -  int n;
          915  +  Blob localRoot;
          916  +  int nLocalRoot;
          917  +  char *zLocalRoot;
   410    918     Blob full;
          919  +  int nFull;
          920  +  char *zFull;
          921  +
          922  +  blob_zero(pOut);
   411    923     db_must_be_within_tree();
   412         -  file_canonical_name(zOrigName, &full);
   413         -  n = strlen(g.zLocalRoot);
   414         -  if( blob_size(&full)<=n || memcmp(g.zLocalRoot, blob_buffer(&full), n) ){
          924  +  file_canonical_name(g.zLocalRoot, &localRoot, 1);
          925  +  nLocalRoot = blob_size(&localRoot);
          926  +  zLocalRoot = blob_buffer(&localRoot);
          927  +  assert( nLocalRoot>0 && zLocalRoot[nLocalRoot-1]=='/' );
          928  +  file_canonical_name(zOrigName, &full, 0);
          929  +  nFull = blob_size(&full);
          930  +  zFull = blob_buffer(&full);
          931  +
          932  +  /* Special case.  zOrigName refers to g.zLocalRoot directory. */
          933  +  if( nFull==nLocalRoot-1 && memcmp(zLocalRoot, zFull, nFull)==0 ){
          934  +    blob_append(pOut, ".", 1);
          935  +    blob_reset(&localRoot);
          936  +    blob_reset(&full);
          937  +    return 1;
          938  +  }
          939  +
          940  +  if( nFull<=nLocalRoot || memcmp(zLocalRoot, zFull, nLocalRoot) ){
          941  +    blob_reset(&localRoot);
   415    942       blob_reset(&full);
   416    943       if( errFatal ){
   417    944         fossil_fatal("file outside of checkout tree: %s", zOrigName);
   418    945       }
   419    946       return 0;
   420    947     }
   421         -  blob_zero(pOut);
   422         -  blob_append(pOut, blob_buffer(&full)+n, blob_size(&full)-n);
          948  +  blob_append(pOut, &zFull[nLocalRoot], nFull-nLocalRoot);
          949  +  blob_reset(&localRoot);
          950  +  blob_reset(&full);
   423    951     return 1;
   424    952   }
   425    953   
   426    954   /*
   427    955   ** COMMAND:  test-tree-name
   428    956   **
   429    957   ** Test the operation of the tree name generator.
................................................................................
   430    958   */
   431    959   void cmd_test_tree_name(void){
   432    960     int i;
   433    961     Blob x;
   434    962     blob_zero(&x);
   435    963     for(i=2; i<g.argc; i++){
   436    964       if( file_tree_name(g.argv[i], &x, 1) ){
   437         -      printf("%s\n", blob_buffer(&x));
          965  +      fossil_print("%s\n", blob_buffer(&x));
   438    966         blob_reset(&x);
   439    967       }
   440    968     }
   441    969   }
   442    970   
   443    971   /*
   444    972   ** Parse a URI into scheme, host, port, and path.
................................................................................
   482   1010   }
   483   1011   
   484   1012   /*
   485   1013   ** Construct a random temporary filename into zBuf[].
   486   1014   */
   487   1015   void file_tempname(int nBuf, char *zBuf){
   488   1016     static const char *azDirs[] = {
         1017  +#if defined(_WIN32)
         1018  +     0, /* GetTempPath */
         1019  +     0, /* TEMP */
         1020  +     0, /* TMP */
         1021  +#else
   489   1022        "/var/tmp",
   490   1023        "/usr/tmp",
   491   1024        "/tmp",
   492   1025        "/temp",
         1026  +#endif
   493   1027        ".",
   494   1028     };
   495   1029     static const unsigned char zChars[] =
   496   1030       "abcdefghijklmnopqrstuvwxyz"
   497   1031       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   498   1032       "0123456789";
   499   1033     unsigned int i, j;
   500         -  struct stat buf;
   501   1034     const char *zDir = ".";
   502         -  
         1035  +  int cnt = 0;
         1036  +
         1037  +#if defined(_WIN32)
         1038  +  wchar_t zTmpPath[MAX_PATH];
         1039  +
         1040  +  if( GetTempPathW(MAX_PATH, zTmpPath) ){
         1041  +    azDirs[0] = fossil_filename_to_utf8(zTmpPath);
         1042  +  }
         1043  +
         1044  +  azDirs[1] = fossil_getenv("TEMP");
         1045  +  azDirs[2] = fossil_getenv("TMP");
         1046  +#endif
         1047  +
         1048  +
   503   1049     for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
   504         -    if( stat(azDirs[i], &buf) ) continue;
   505         -    if( !S_ISDIR(buf.st_mode) ) continue;
   506         -    if( access(azDirs[i], 07) ) continue;
         1050  +    if( azDirs[i]==0 ) continue;
         1051  +    if( !file_isdir(azDirs[i]) ) continue;
   507   1052       zDir = azDirs[i];
   508   1053       break;
   509   1054     }
   510   1055   
   511         -  /* Check that the output buffer is large enough for the temporary file 
         1056  +  /* Check that the output buffer is large enough for the temporary file
   512   1057     ** name. If it is not, return SQLITE_ERROR.
   513   1058     */
   514   1059     if( (strlen(zDir) + 17) >= (size_t)nBuf ){
   515   1060       fossil_fatal("insufficient space for temporary filename");
   516   1061     }
   517   1062   
   518   1063     do{
         1064  +    if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
   519   1065       sqlite3_snprintf(nBuf-17, zBuf, "%s/", zDir);
   520   1066       j = (int)strlen(zBuf);
   521   1067       sqlite3_randomness(15, &zBuf[j]);
   522   1068       for(i=0; i<15; i++, j++){
   523   1069         zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
   524   1070       }
   525   1071       zBuf[j] = 0;
   526         -  }while( access(zBuf,0)==0 );
         1072  +  }while( file_size(zBuf)>=0 );
         1073  +
         1074  +#if defined(_WIN32)
         1075  +  fossil_unicode_free((char *)azDirs[1]);
         1076  +  fossil_unicode_free((char *)azDirs[2]);
         1077  +#endif
         1078  +}
         1079  +
         1080  +
         1081  +/*
         1082  +** Return true if a file named zName exists and has identical content
         1083  +** to the blob pContent.  If zName does not exist or if the content is
         1084  +** different in any way, then return false.
         1085  +*/
         1086  +int file_is_the_same(Blob *pContent, const char *zName){
         1087  +  i64 iSize;
         1088  +  int rc;
         1089  +  Blob onDisk;
         1090  +
         1091  +  iSize = file_wd_size(zName);
         1092  +  if( iSize<0 ) return 0;
         1093  +  if( iSize!=blob_size(pContent) ) return 0;
         1094  +  if( file_wd_islink(zName) ){
         1095  +    blob_read_link(&onDisk, zName);
         1096  +  }else{
         1097  +    blob_read_from_file(&onDisk, zName);
         1098  +  }
         1099  +  rc = blob_compare(&onDisk, pContent);
         1100  +  blob_reset(&onDisk);
         1101  +  return rc==0;
         1102  +}
         1103  +
         1104  +/*
         1105  +** Portable unicode implementation of opendir()
         1106  +*/
         1107  +#if INTERFACE
         1108  +
         1109  +#include <dirent.h>
         1110  +#if defined(_WIN32)
         1111  +# define DIR _WDIR
         1112  +# define dirent _wdirent
         1113  +# define opendir _wopendir
         1114  +# define readdir _wreaddir
         1115  +# define closedir _wclosedir
         1116  +#endif /* _WIN32 */
         1117  +
         1118  +#endif /* INTERFACE */
         1119  +
         1120  +/*
         1121  +** Return the value of an environment variable as UTF8.
         1122  +** Use fossil_filename_free() to release resources.
         1123  +*/
         1124  +char *fossil_getenv(const char *zName){
         1125  +#ifdef _WIN32
         1126  +  wchar_t *uName = fossil_utf8_to_unicode(zName);
         1127  +  void *zValue = _wgetenv(uName);
         1128  +  fossil_unicode_free(uName);
         1129  +#else
         1130  +  char *zValue = getenv(zName);
         1131  +#endif
         1132  +  if( zValue ) zValue = fossil_filename_to_utf8(zValue);
         1133  +  return zValue;
         1134  +}
         1135  +
         1136  +/*
         1137  +** Like fopen() but always takes a UTF8 argument.
         1138  +*/
         1139  +FILE *fossil_fopen(const char *zName, const char *zMode){
         1140  +#ifdef _WIN32
         1141  +  wchar_t *uMode = fossil_utf8_to_unicode(zMode);
         1142  +  wchar_t *uName = fossil_utf8_to_unicode(zName);
         1143  +  FILE *f = _wfopen(uName, uMode);
         1144  +  fossil_unicode_free(uName);
         1145  +  fossil_unicode_free(uMode);
         1146  +#else
         1147  +  FILE *f = fopen(zName, zMode);
         1148  +#endif
         1149  +  return f;
   527   1150   }

Changes to src/finfo.c.

    19     19   */
    20     20   #include "config.h"
    21     21   #include "finfo.h"
    22     22   
    23     23   /*
    24     24   ** COMMAND: finfo
    25     25   ** 
    26         -** Usage: %fossil finfo FILENAME
           26  +** Usage: %fossil finfo ?OPTIONS? FILENAME
           27  +**
           28  +** Print the complete change history for a single file going backwards
           29  +** in time.  The default mode is -l.
           30  +**
           31  +** For the -l|--log mode: If "-b|--brief" is specified one line per revision
           32  +** is printed, otherwise the full comment is printed.  The "--limit N"
           33  +** and "--offset P" options limits the output to the first N changes
           34  +** after skipping P changes.
           35  +**
           36  +** In the -s mode prints the status as <status> <revision>.  This is
           37  +** a quick status and does not check for up-to-date-ness of the file.
           38  +**
           39  +** In the -p mode, there's an optional flag "-r|--revision REVISION".
           40  +** The specified version (or the latest checked out version) is printed
           41  +** to stdout.  The -p mode is another form of the "cat" command.
    27     42   **
    28         -** Print the change history for a single file.
           43  +** Options:
           44  +**   --brief|-b           display a brief (one line / revision) summary
           45  +**   --limit N            display the first N changes
           46  +**   --log|-l             select log mode (the default)
           47  +**   --offset P           skip P changes
           48  +**   -p                   select print mode
           49  +**   --revision|-r R      print the given revision (or ckout, if none is given)
           50  +**                        to stdout (only in print mode)
           51  +**   -s                   select status mode (print a status indicator for FILE)
           52  +**   --case-sensitive B   Enable or disable case-sensitive filenames.  B is a
           53  +**                        boolean: "yes", "no", "true", "false", etc.
    29     54   **
    30         -** The "--limit N" and "--offset P" options limits the output to the first
    31         -** N changes after skipping P changes.
           55  +** See also: artifact, cat, descendants, info, leaves
    32     56   */
    33     57   void finfo_cmd(void){
    34         -  Stmt q;
    35         -  int vid;
    36         -  Blob dest;
    37         -  const char *zFilename;
    38         -  const char *zLimit;
    39         -  const char *zOffset;
    40         -  int iLimit, iOffset;
    41         -
           58  +  capture_case_sensitive_option();
    42     59     db_must_be_within_tree();
    43         -  vid = db_lget_int("checkout", 0);
    44         -  if( vid==0 ){
    45         -    fossil_panic("no checkout to finfo files in");
    46         -  }
    47         -  zLimit = find_option("limit",0,1);
    48         -  iLimit = zLimit ? atoi(zLimit) : -1;
    49         -  zOffset = find_option("offset",0,1);
    50         -  iOffset = zOffset ? atoi(zOffset) : 0;
    51         -  if (g.argc<3) {
    52         -    usage("FILENAME");
    53         -  }
    54         -  file_tree_name(g.argv[2], &dest, 1);
    55         -  zFilename = blob_str(&dest);
    56         -  db_prepare(&q,
    57         -    "SELECT "
    58         -    "       (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* New file */
    59         -    "       (SELECT uuid FROM blob WHERE rid=mlink.mid),"  /* The check-in */
    60         -    "       date(event.mtime,'localtime'),"
    61         -    "       coalesce(event.ecomment, event.comment),"
    62         -    "       coalesce(event.euser, event.user)"
    63         -    "  FROM mlink, event"
    64         -    " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
    65         -    "   AND event.objid=mlink.mid"
    66         -    " ORDER BY event.mtime DESC LIMIT %d OFFSET %d /*sort*/",
    67         -    zFilename, iLimit, iOffset
    68         -  );
    69         - 
    70         -  printf("History of %s\n", zFilename);
    71         -  while( db_step(&q)==SQLITE_ROW ){
    72         -    const char *zFileUuid = db_column_text(&q, 0);
    73         -    const char *zCiUuid = db_column_text(&q, 1);
    74         -    const char *zDate = db_column_text(&q, 2);
    75         -    const char *zCom = db_column_text(&q, 3);
    76         -    const char *zUser = db_column_text(&q, 4);
    77         -    char *zOut;
    78         -    printf("%s ", zDate);
    79         -    if( zFileUuid==0 ){
    80         -      zOut = sqlite3_mprintf("[%.10s] DELETED %s (user: %s)",
    81         -                              zCiUuid, zCom, zUser);
           60  +  if (find_option("status","s",0)) {
           61  +    Stmt q;
           62  +    Blob line;
           63  +    Blob fname;
           64  +    int vid;
           65  +
           66  +    if( g.argc!=3 ) usage("-s|--status FILENAME");
           67  +    vid = db_lget_int("checkout", 0);
           68  +    if( vid==0 ){
           69  +      fossil_panic("no checkout to finfo files in");
           70  +    }
           71  +    vfile_check_signature(vid, CKSIG_ENOTFILE);
           72  +    file_tree_name(g.argv[2], &fname, 1);
           73  +    db_prepare(&q,
           74  +        "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
           75  +        "  FROM vfile WHERE vfile.pathname=%B %s",
           76  +        &fname, filename_collation());
           77  +    blob_zero(&line);
           78  +    if ( db_step(&q)==SQLITE_ROW ) {
           79  +      Blob uuid;
           80  +      int isDeleted = db_column_int(&q, 1);
           81  +      int isNew = db_column_int(&q,2) == 0;
           82  +      int chnged = db_column_int(&q,3);
           83  +      int renamed = db_column_int(&q,4);
           84  +
           85  +      blob_zero(&uuid);
           86  +      db_blob(&uuid,
           87  +           "SELECT uuid FROM blob, mlink, vfile WHERE "
           88  +           "blob.rid = mlink.mid AND mlink.fid = vfile.rid AND "
           89  +           "vfile.pathname=%B %s",
           90  +           &fname, filename_collation()
           91  +      );
           92  +      if( isNew ){
           93  +        blob_appendf(&line, "new");
           94  +      }else if( isDeleted ){
           95  +        blob_appendf(&line, "deleted");
           96  +      }else if( renamed ){
           97  +        blob_appendf(&line, "renamed");
           98  +      }else if( chnged ){
           99  +        blob_appendf(&line, "edited");
          100  +      }else{
          101  +        blob_appendf(&line, "unchanged");
          102  +      }
          103  +      blob_appendf(&line, " ");
          104  +      blob_appendf(&line, " %10.10s", blob_str(&uuid));
          105  +      blob_reset(&uuid);
          106  +    }else{
          107  +      blob_appendf(&line, "unknown 0000000000");
          108  +    }
          109  +    db_finalize(&q);
          110  +    fossil_print("%s\n", blob_str(&line));
          111  +    blob_reset(&fname);
          112  +    blob_reset(&line);
          113  +  }else if( find_option("print","p",0) ){
          114  +    Blob record;
          115  +    Blob fname;
          116  +    const char *zRevision = find_option("revision", "r", 1);
          117  +
          118  +    file_tree_name(g.argv[2], &fname, 1);
          119  +    if( zRevision ){
          120  +      historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
    82    121       }else{
    83         -      zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])",
    84         -                              zCiUuid, zCom, zUser, zFileUuid);
          122  +      int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
          123  +                       &fname, filename_collation());
          124  +      if( rid==0 ){
          125  +        fossil_fatal("no history for file: %b", &fname);
          126  +      }
          127  +      content_get(rid, &record);
          128  +    }
          129  +    blob_write_to_file(&record, "-");
          130  +    blob_reset(&record);
          131  +    blob_reset(&fname);
          132  +  }else{
          133  +    Blob line;
          134  +    Stmt q;
          135  +    Blob fname;
          136  +    int rid;
          137  +    const char *zFilename;
          138  +    const char *zLimit;
          139  +    const char *zOffset;
          140  +    int iLimit, iOffset, iBrief;
          141  +
          142  +    if( find_option("log","l",0) ){
          143  +      /* this is the default, no-op */
          144  +    }
          145  +    zLimit = find_option("limit",0,1);
          146  +    iLimit = zLimit ? atoi(zLimit) : -1;
          147  +    zOffset = find_option("offset",0,1);
          148  +    iOffset = zOffset ? atoi(zOffset) : 0;
          149  +    iBrief = (find_option("brief","b",0) == 0);
          150  +    if( g.argc!=3 ){
          151  +      usage("?-l|--log? ?-b|--brief? FILENAME");
          152  +    }
          153  +    file_tree_name(g.argv[2], &fname, 1);
          154  +    rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
          155  +                 &fname, filename_collation());
          156  +    if( rid==0 ){
          157  +      fossil_fatal("no history for file: %b", &fname);
          158  +    }
          159  +    zFilename = blob_str(&fname);
          160  +    db_prepare(&q,
          161  +        "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime'),"
          162  +        "       coalesce(event.ecomment, event.comment),"
          163  +        "       coalesce(event.euser, event.user),"
          164  +        "       (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
          165  +                                " AND tagxref.rid=mlink.mid)" /* Tags */
          166  +        "  FROM mlink, blob b, event, blob ci, filename"
          167  +        " WHERE filename.name=%Q %s"
          168  +        "   AND mlink.fnid=filename.fnid"
          169  +        "   AND b.rid=mlink.fid"
          170  +        "   AND event.objid=mlink.mid"
          171  +        "   AND event.objid=ci.rid"
          172  +        " ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
          173  +        TAG_BRANCH, zFilename, filename_collation(), iLimit, iOffset
          174  +    );
          175  +    blob_zero(&line);
          176  +    if( iBrief ){
          177  +      fossil_print("History of %s\n", blob_str(&fname));
          178  +    }
          179  +    while( db_step(&q)==SQLITE_ROW ){
          180  +      const char *zFileUuid = db_column_text(&q, 0);
          181  +      const char *zCiUuid = db_column_text(&q,1);
          182  +      const char *zDate = db_column_text(&q, 2);
          183  +      const char *zCom = db_column_text(&q, 3);
          184  +      const char *zUser = db_column_text(&q, 4);
          185  +      const char *zBr = db_column_text(&q, 5);
          186  +      char *zOut;
          187  +      if( zBr==0 ) zBr = "trunk";
          188  +      if( iBrief ){
          189  +        fossil_print("%s ", zDate);
          190  +        zOut = sqlite3_mprintf(
          191  +           "[%.10s] %s (user: %s, artifact: [%.10s], branch: %s)",
          192  +           zCiUuid, zCom, zUser, zFileUuid, zBr);
          193  +        comment_print(zOut, 11, 79);
          194  +        sqlite3_free(zOut);
          195  +      }else{
          196  +        blob_reset(&line);
          197  +        blob_appendf(&line, "%.10s ", zCiUuid);
          198  +        blob_appendf(&line, "%.10s ", zDate);
          199  +        blob_appendf(&line, "%8.8s ", zUser);
          200  +        blob_appendf(&line, "%8.8s ", zBr);
          201  +        blob_appendf(&line,"%-40.40s\n", zCom );
          202  +        comment_print(blob_str(&line), 0, 79);
          203  +      }
    85    204       }
    86         -    comment_print(zOut, 11, 79);
    87         -    sqlite3_free(zOut);
          205  +    db_finalize(&q);
          206  +    blob_reset(&fname);
    88    207     }
    89         -  db_finalize(&q);
    90         -  blob_reset(&dest);
          208  +}
          209  +
          210  +/*
          211  +** COMMAND: cat
          212  +**
          213  +** Usage: %fossil cat FILENAME ... ?OPTIONS?
          214  +**
          215  +** Print on standard output the content of one or more files as they exist
          216  +** in the repository.  The version currently checked out is shown by default.
          217  +** Other versions may be specified using the -r option.
          218  +**
          219  +** Options:
          220  +**    -R|--repository FILE       Extract artifacts from repository FILE
          221  +**    -r VERSION                 The specific check-in containing the file
          222  +**
          223  +** See also: finfo
          224  +*/
          225  +void cat_cmd(void){
          226  +  int i;
          227  +  int rc;
          228  +  Blob content, fname;
          229  +  const char *zRev;
          230  +  db_find_and_open_repository(0, 0);
          231  +  zRev = find_option("r","r",1);
          232  +  for(i=2; i<g.argc; i++){
          233  +    file_tree_name(g.argv[i], &fname, 1);
          234  +    blob_zero(&content);
          235  +    rc = historical_version_of_file(zRev, blob_str(&fname), &content, 0,0,0,0);
          236  +    if( rc==0 ){
          237  +      fossil_fatal("no such file: %s", g.argv[i]);
          238  +    }
          239  +    blob_write_to_file(&content, "-");
          240  +    blob_reset(&fname);
          241  +    blob_reset(&content);
          242  +  }
    91    243   }
    92    244   
          245  +/* Values for the debug= query parameter to finfo */
          246  +#define FINFO_DEBUG_MLINK  0x01
    93    247   
    94    248   /*
    95    249   ** WEBPAGE: finfo
    96    250   ** URL: /finfo?name=FILENAME
    97    251   **
    98         -** Show the complete change history for a single file. 
          252  +** Show the change history for a single file. 
          253  +**
          254  +** Additional query parameters:
          255  +**
          256  +**    a=DATE     Only show changes after DATE
          257  +**    b=DATE     Only show changes before DATE
          258  +**    n=NUM      Show the first NUM changes only
          259  +**    brbg       Background color by branch name
          260  +**    ubg        Background color by user name
          261  +**    fco=BOOL   Show only first occurrence of each version if true (default)
    99    262   */
   100    263   void finfo_page(void){
   101    264     Stmt q;
   102    265     const char *zFilename;
   103    266     char zPrevDate[20];
          267  +  const char *zA;
          268  +  const char *zB;
          269  +  int n;
          270  +  
   104    271     Blob title;
          272  +  Blob sql;
          273  +  HQuery url;
   105    274     GraphContext *pGraph;
          275  +  int brBg = P("brbg")!=0;
          276  +  int uBg = P("ubg")!=0;
          277  +  int firstChngOnly = atoi(PD("fco","1"))!=0;
          278  +  int fDebug = atoi(PD("debug","0"));
   106    279   
   107    280     login_check_credentials();
   108         -  if( !g.okRead ){ login_needed(); return; }
          281  +  if( !g.perm.Read ){ login_needed(); return; }
   109    282     style_header("File History");
   110    283     login_anonymous_available();
          284  +  url_initialize(&url, "finfo");
          285  +  if( brBg ) url_add_parameter(&url, "brbg", 0);
          286  +  if( uBg ) url_add_parameter(&url, "ubg", 0);
          287  +  if( firstChngOnly ) url_add_parameter(&url, "fco", "0");
   111    288   
   112    289     zPrevDate[0] = 0;
   113    290     zFilename = PD("name","");
   114         -  db_prepare(&q,
          291  +  url_add_parameter(&url, "name", zFilename);
          292  +  blob_zero(&sql);
          293  +  blob_appendf(&sql, 
   115    294       "SELECT"
   116    295       " datetime(event.mtime,'localtime'),"            /* Date of change */
   117    296       " coalesce(event.ecomment, event.comment),"      /* Check-in comment */
   118    297       " coalesce(event.euser, event.user),"            /* User who made chng */
   119         -    " mlink.pid,"                                    /* File rid */
   120         -    " mlink.fid,"                                    /* Parent file rid */
          298  +    " mlink.pid,"                                    /* Parent rid */
          299  +    " mlink.fid,"                                    /* File rid */
   121    300       " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
   122    301       " (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* Current file uuid */
   123    302       " (SELECT uuid FROM blob WHERE rid=mlink.mid),"  /* Check-in uuid */
   124    303       " event.bgcolor,"                                /* Background color */
   125    304       " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
   126         -                                " AND tagxref.rid=mlink.mid)" /* Tags */
          305  +                                " AND tagxref.rid=mlink.mid)," /* Tags */
          306  +    " mlink.mid,"                                    /* check-in ID */
          307  +    " mlink.pfnid",                                  /* Previous filename */
          308  +    TAG_BRANCH
          309  +  );
          310  +  if( firstChngOnly ){
          311  +    blob_appendf(&sql, ", min(event.mtime)");
          312  +  }
          313  +  blob_appendf(&sql,
   127    314       "  FROM mlink, event"
   128         -    " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
   129         -    "   AND event.objid=mlink.mid"
   130         -    " ORDER BY event.mtime DESC /*sort*/",
   131         -    TAG_BRANCH,
   132         -    zFilename
          315  +    " WHERE mlink.fnid IN (SELECT fnid FROM filename WHERE name=%Q %s)"
          316  +    "   AND event.objid=mlink.mid",
          317  +    zFilename, filename_collation()
   133    318     );
          319  +  if( (zA = P("a"))!=0 ){
          320  +    blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zA);
          321  +    url_add_parameter(&url, "a", zA);
          322  +  }
          323  +  if( (zB = P("b"))!=0 ){
          324  +    blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zB);
          325  +    url_add_parameter(&url, "b", zB);
          326  +  }
          327  +  if( firstChngOnly ){
          328  +    blob_appendf(&sql, " GROUP BY mlink.fid");
          329  +  }
          330  +  blob_appendf(&sql," ORDER BY event.mtime DESC /*sort*/");
          331  +  if( (n = atoi(PD("n","0")))>0 ){
          332  +    blob_appendf(&sql, " LIMIT %d", n);
          333  +    url_add_parameter(&url, "n", P("n"));
          334  +  }
          335  +  if( firstChngOnly ){
          336  +    style_submenu_element("Full", "Show all changes","%s",
          337  +                          url_render(&url, "fco", "0", 0, 0));
          338  +  }else{
          339  +    style_submenu_element("Simplified", "Show only first use of a change","%s",
          340  +                          url_render(&url, "fco", "1", 0, 0));
          341  +  }
          342  +  db_prepare(&q, blob_str(&sql));
          343  +  blob_reset(&sql);
   134    344     blob_zero(&title);
   135    345     blob_appendf(&title, "History of ");
   136         -  hyperlinked_path(zFilename, &title);
          346  +  hyperlinked_path(zFilename, &title, 0);
   137    347     @ <h2>%b(&title)</h2>
   138    348     blob_reset(&title);
   139    349     pGraph = graph_init();
   140         -  @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
   141         -  @ <table cellspacing=0 border=0 cellpadding=0>
          350  +  @ <div id="canvas" style="position:relative;width:1px;height:1px;"
          351  +  @  onclick="clickOnGraph(event)"></div>
          352  +  @ <table id="timelineTable" class="timelineTable">
   142    353     while( db_step(&q)==SQLITE_ROW ){
   143    354       const char *zDate = db_column_text(&q, 0);
   144    355       const char *zCom = db_column_text(&q, 1);
   145    356       const char *zUser = db_column_text(&q, 2);
   146    357       int fpid = db_column_int(&q, 3);
   147    358       int frid = db_column_int(&q, 4);
   148    359       const char *zPUuid = db_column_text(&q, 5);
   149    360       const char *zUuid = db_column_text(&q, 6);
   150    361       const char *zCkin = db_column_text(&q,7);
   151    362       const char *zBgClr = db_column_text(&q, 8);
   152    363       const char *zBr = db_column_text(&q, 9);
          364  +    int fmid = db_column_int(&q, 10);
          365  +    int pfnid = db_column_int(&q, 11);
   153    366       int gidx;
   154    367       char zTime[10];
   155    368       char zShort[20];
   156    369       char zShortCkin[20];
   157    370       if( zBr==0 ) zBr = "trunk";
   158         -    gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
          371  +    if( uBg ){
          372  +      zBgClr = hash_color(zUser);
          373  +    }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
          374  +      zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
          375  +    }
          376  +    gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr,
          377  +                         zUuid, 0);
   159    378       if( memcmp(zDate, zPrevDate, 10) ){
   160         -      sprintf(zPrevDate, "%.10s", zDate);
          379  +      sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
   161    380         @ <tr><td>
   162         -      @   <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
   163         -      @ </td></tr>
          381  +      @   <div class="divider">%s(zPrevDate)</div>
          382  +      @ </td><td></td><td></td></tr>
   164    383       }
   165    384       memcpy(zTime, &zDate[11], 5);
   166    385       zTime[5] = 0;
   167         -    @ <tr><td valign="top" align="right">
   168         -    @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
   169         -    @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
          386  +    @ <tr><td class="timelineTime">
          387  +    @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
          388  +    @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
   170    389       if( zBgClr && zBgClr[0] ){
   171         -      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
          390  +      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
   172    391       }else{
   173         -      @ <td valign="top" align="left">
          392  +      @ <td class="timelineTableCell">
   174    393       }
   175    394       sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
   176    395       sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
   177    396       if( zUuid ){
   178         -      if( g.okHistory ){
   179         -        @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%S(zUuid)]</a>
   180         -      }else{
   181         -        @ [%S(zUuid)]
          397  +      if( fpid==0 ){
          398  +        @ <b>Added</b>
          399  +      }else if( pfnid ){
          400  +        char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d",
          401  +                                  pfnid);
          402  +        @ <b>Renamed</b> from
          403  +        @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
   182    404         }
   183         -      @ part of check-in
          405  +      @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in
   184    406       }else{
   185         -      @ <b>Deleted</b> by check-in
          407  +      char *zNewName;
          408  +      zNewName = db_text(0, 
          409  +        "SELECT name FROM filename WHERE fnid = "
          410  +        "   (SELECT fnid FROM mlink"
          411  +        "     WHERE mid=%d"
          412  +        "       AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q %s))",
          413  +        fmid, zFilename, filename_collation());
          414  +      if( zNewName ){
          415  +        @ <b>Renamed</b> to
          416  +        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
          417  +        fossil_free(zNewName);
          418  +      }else{
          419  +        @ <b>Deleted</b> by check-in
          420  +      }
   186    421       }
   187    422       hyperlink_to_uuid(zShortCkin);
   188    423       @ %h(zCom) (user: 
   189    424       hyperlink_to_user(zUser, zDate, "");
   190    425       @ branch: %h(zBr))
   191         -    if( g.okHistory && zUuid ){
          426  +    if( g.perm.Hyperlink && zUuid ){
          427  +      const char *z = zFilename;
   192    428         if( fpid ){
   193         -        @ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&amp;v2=%s(zUuid)">[diff]</a>
          429  +        @ %z(href("%R/fdiff?v1=%S&v2=%S",zPUuid,zUuid))[diff]</a>
   194    430         }
   195         -      @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(zFilename)">
          431  +      @ %z(href("%R/annotate?checkin=%S&filename=%h",zCkin,z))
   196    432         @ [annotate]</a>
          433  +      @ %z(href("%R/timeline?n=200&uf=%S",zUuid))[checkins&nbsp;using]</a>
          434  +    }
          435  +    if( fDebug & FINFO_DEBUG_MLINK ){
          436  +      @ fid=%d(frid), pid=%d(fpid), mid=%d(fmid)
   197    437       }
   198         -    @ </td>
          438  +    @ </td></tr>
   199    439     }
   200    440     db_finalize(&q);
   201    441     if( pGraph ){
   202         -    graph_finish(pGraph, 1);
          442  +    graph_finish(pGraph, 0);
   203    443       if( pGraph->nErr ){
   204    444         graph_free(pGraph);
   205    445         pGraph = 0;
   206    446       }else{
   207         -      @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
          447  +      int w = (pGraph->mxRail+1)*pGraph->iRailPitch + 10;
          448  +      @ <tr><td></td><td>
          449  +      @ <div id="grbtm" style="width:%d(w)px;"></div>
          450  +      @     </td><td></td></tr>
   208    451       }
   209    452     }
   210    453     @ </table>
   211         -  timeline_output_graph_javascript(pGraph);
          454  +  timeline_output_graph_javascript(pGraph, 0, 1);
   212    455     style_footer();
   213    456   }

Added src/glob.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to pattern matching using "glob" syntax.
           19  +*/
           20  +#include "config.h"
           21  +#include "glob.h"
           22  +#include <assert.h>
           23  +
           24  +/*
           25  +** Construct and return a string which is an SQL expression that will
           26  +** be TRUE if value zVal matches any of the GLOB expressions in the list
           27  +** zGlobList.  For example:
           28  +**
           29  +**    zVal:       "x"
           30  +**    zGlobList:  "*.o,*.obj"
           31  +**
           32  +**    Result:     "(x GLOB '*.o' OR x GLOB '*.obj')"
           33  +**
           34  +** Each element of the GLOB list may optionally be enclosed in either '...'
           35  +** or "...".  This allows commas in the expression.  Whitespace at the
           36  +** beginning and end of each GLOB pattern is ignored, except when enclosed
           37  +** within '...' or "...".
           38  +**
           39  +** This routine makes no effort to free the memory space it uses.
           40  +*/
           41  +char *glob_expr(const char *zVal, const char *zGlobList){
           42  +  Blob expr;
           43  +  char *zSep = "(";
           44  +  int nTerm = 0;
           45  +  int i;
           46  +  int cTerm;
           47  +
           48  +  if( zGlobList==0 || zGlobList[0]==0 ) return "0";
           49  +  blob_zero(&expr);
           50  +  while( zGlobList[0] ){
           51  +    while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
           52  +    if( zGlobList[0]==0 ) break;
           53  +    if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
           54  +      cTerm = zGlobList[0];
           55  +      zGlobList++;
           56  +    }else{
           57  +      cTerm = ',';
           58  +    }
           59  +    for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
           60  +    if( cTerm==',' ){
           61  +      while( i>0 && fossil_isspace(zGlobList[i-1]) ){ i--; }
           62  +    }
           63  +    blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList);
           64  +    zSep = " OR ";
           65  +    if( cTerm!=',' && zGlobList[i] ) i++;
           66  +    zGlobList += i;
           67  +    if( zGlobList[0] ) zGlobList++;
           68  +    nTerm++;
           69  +  }
           70  +  if( nTerm ){
           71  +    blob_appendf(&expr, ")");
           72  +    return blob_str(&expr);
           73  +  }else{
           74  +    return "0";
           75  +  }
           76  +}
           77  +
           78  +#if INTERFACE
           79  +/*
           80  +** A Glob object holds a set of patterns read to be matched against
           81  +** a string.
           82  +*/
           83  +struct Glob {
           84  +  int nPattern;        /* Number of patterns */
           85  +  char **azPattern;    /* Array of pointers to patterns */
           86  +};
           87  +#endif /* INTERFACE */
           88  +
           89  +/*
           90  +** zPatternList is a comma-separate list of glob patterns.  Parse up
           91  +** that list and use it to create a new Glob object.
           92  +**
           93  +** Elements of the glob list may be optionally enclosed in single our
           94  +** double-quotes.  This allows a comma to be part of a glob.
           95  +**
           96  +** Leading and trailing spaces on unquoted glob patterns are ignored.
           97  +**
           98  +** An empty or null pattern list results in a null glob, which will
           99  +** match nothing.
          100  +*/
          101  +Glob *glob_create(const char *zPatternList){
          102  +  int nList;         /* Size of zPatternList in bytes */
          103  +  int i, j;          /* Loop counters */
          104  +  Glob *p;           /* The glob being created */
          105  +  char *z;           /* Copy of the pattern list */
          106  +  char delimiter;    /* '\'' or '\"' or 0 */
          107  +
          108  +  if( zPatternList==0 || zPatternList[0]==0 ) return 0;
          109  +  nList = strlen(zPatternList);
          110  +  p = fossil_malloc( sizeof(*p) + nList+1 );
          111  +  memset(p, 0, sizeof(*p));
          112  +  z = (char*)&p[1];
          113  +  memcpy(z, zPatternList, nList+1);
          114  +  while( z[0] ){
          115  +    while( z[0]==',' || z[0]==' ' || z[0]=='\n' || z[0]=='\r' ){
          116  +      z++;  /* Skip leading spaces and newlines */
          117  +    }
          118  +    if( z[0]=='\'' || z[0]=='"' ){
          119  +      delimiter = z[0];
          120  +      z++;
          121  +    }else{
          122  +      delimiter = ',';
          123  +    }
          124  +    if( z[0]==0 ) break;
          125  +    p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) );
          126  +    p->azPattern[p->nPattern++] = z;
          127  +    for(i=0; z[i] && z[i]!=delimiter && z[i]!='\n' && z[i]!='\r'; i++){}
          128  +    if( delimiter==',' ){
          129  +      /* Remove trailing spaces / newlines on a comma-delimited pattern */
          130  +      for(j=i; j>1 && (z[j-1]==' ' || z[j-1]=='\n' || z[j-1]=='\r'); j--){}
          131  +      if( j<i ) z[j] = 0;
          132  +    }
          133  +    if( z[i]==0 ) break;
          134  +    z[i] = 0;
          135  +    z += i+1;
          136  +  }
          137  +  return p;
          138  +}
          139  +
          140  +/*
          141  +** Return non-zero if string z matches glob pattern zGlob and zero if the
          142  +** pattern does not match.
          143  +**
          144  +** Globbing rules:
          145  +**
          146  +**      '*'       Matches any sequence of zero or more characters.
          147  +**
          148  +**      '?'       Matches exactly one character.
          149  +**
          150  +**     [...]      Matches one character from the enclosed list of
          151  +**                characters.
          152  +**
          153  +**     [^...]     Matches one character not in the enclosed list.
          154  +*/
          155  +int strglob(const char *zGlob, const char *z){
          156  +  int c, c2;
          157  +  int invert;
          158  +  int seen;
          159  +
          160  +  while( (c = (*(zGlob++)))!=0 ){
          161  +    if( c=='*' ){
          162  +      while( (c=(*(zGlob++))) == '*' || c=='?' ){
          163  +        if( c=='?' && (*(z++))==0 ) return 0;
          164  +      }
          165  +      if( c==0 ){
          166  +        return 1;
          167  +      }else if( c=='[' ){
          168  +        while( *z && strglob(zGlob-1,z)==0 ){
          169  +          z++;
          170  +        }
          171  +        return (*z)!=0;
          172  +      }
          173  +      while( (c2 = (*(z++)))!=0 ){
          174  +        while( c2!=c ){
          175  +          c2 = *(z++);
          176  +          if( c2==0 ) return 0;
          177  +        }
          178  +        if( strglob(zGlob,z) ) return 1;
          179  +      }
          180  +      return 0;
          181  +    }else if( c=='?' ){
          182  +      if( (*(z++))==0 ) return 0;
          183  +    }else if( c=='[' ){
          184  +      int prior_c = 0;
          185  +      seen = 0;
          186  +      invert = 0;
          187  +      c = *(z++);
          188  +      if( c==0 ) return 0;
          189  +      c2 = *(zGlob++);
          190  +      if( c2=='^' ){
          191  +        invert = 1;
          192  +        c2 = *(zGlob++);
          193  +      }
          194  +      if( c2==']' ){
          195  +        if( c==']' ) seen = 1;
          196  +        c2 = *(zGlob++);
          197  +      }
          198  +      while( c2 && c2!=']' ){
          199  +        if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
          200  +          c2 = *(zGlob++);
          201  +          if( c>=prior_c && c<=c2 ) seen = 1;
          202  +          prior_c = 0;
          203  +        }else{
          204  +          if( c==c2 ){
          205  +            seen = 1;
          206  +          }
          207  +          prior_c = c2;
          208  +        }
          209  +        c2 = *(zGlob++);
          210  +      }
          211  +      if( c2==0 || (seen ^ invert)==0 ) return 0;
          212  +    }else{
          213  +      if( c!=(*(z++)) ) return 0;
          214  +    }
          215  +  }
          216  +  return *z==0;
          217  +}
          218  +
          219  +/*
          220  +** Return true (non-zero) if zString matches any of the patterns in
          221  +** the Glob.  The value returned is actually a 1-based index of the pattern
          222  +** that matched.  Return 0 if none of the patterns match zString.
          223  +**
          224  +** A NULL glob matches nothing.
          225  +*/
          226  +int glob_match(Glob *pGlob, const char *zString){
          227  +  int i;
          228  +  if( pGlob==0 ) return 0;
          229  +  for(i=0; i<pGlob->nPattern; i++){
          230  +    if( strglob(pGlob->azPattern[i], zString) ) return i+1;
          231  +  }
          232  +  return 0;
          233  +}
          234  +
          235  +/*
          236  +** Free all memory associated with the given Glob object
          237  +*/
          238  +void glob_free(Glob *pGlob){
          239  +  if( pGlob ){
          240  +    fossil_free(pGlob->azPattern);
          241  +    fossil_free(pGlob);
          242  +  }
          243  +}
          244  +
          245  +/*
          246  +** COMMAND: test-glob
          247  +**
          248  +** Usage:  %fossil test-glob PATTERN STRING...
          249  +**
          250  +** PATTERN is a comma-separated list of glob patterns.  Show which of
          251  +** the STRINGs that follow match the PATTERN.
          252  +*/
          253  +void glob_test_cmd(void){
          254  +  Glob *pGlob;
          255  +  int i;
          256  +  if( g.argc<4 ) usage("PATTERN STRING ...");
          257  +  fossil_print("SQL expression: %s\n", glob_expr("x", g.argv[2]));
          258  +  pGlob = glob_create(g.argv[2]);
          259  +  for(i=0; i<pGlob->nPattern; i++){
          260  +    fossil_print("pattern[%d] = [%s]\n", i, pGlob->azPattern[i]);
          261  +  }
          262  +  for(i=3; i<g.argc; i++){
          263  +    fossil_print("%d %s\n", glob_match(pGlob, g.argv[i]), g.argv[i]);
          264  +  }
          265  +  glob_free(pGlob);
          266  +}

Changes to src/graph.c.

    19     19   */
    20     20   #include "config.h"
    21     21   #include "graph.h"
    22     22   #include <assert.h>
    23     23   
    24     24   #if INTERFACE
    25     25   
    26         -#define GR_MAX_PARENT 10
    27         -#define GR_MAX_RAIL   32
           26  +#define GR_MAX_RAIL   40      /* Max number of "rails" to display */
    28     27   
    29     28   /* The graph appears vertically beside a timeline.  Each row in the
    30         -** timeline corresponds to a row in the graph.
           29  +** timeline corresponds to a row in the graph.  GraphRow.idx is 0 for
           30  +** the top-most row and increases moving down.  Hence (in the absence of
           31  +** time skew) parents have a larger index than their children.
    31     32   */
    32     33   struct GraphRow {
    33     34     int rid;                    /* The rid for the check-in */
    34         -  int nParent;                /* Number of parents */
    35         -  int aParent[GR_MAX_PARENT]; /* Array of parents.  0 element is primary .*/
           35  +  i8 nParent;                 /* Number of parents */
           36  +  int *aParent;               /* Array of parents.  0 element is primary .*/
    36     37     char *zBranch;              /* Branch name */
    37     38     char *zBgClr;               /* Background Color */
           39  +  char zUuid[17];             /* Check-in for file ID */
    38     40   
    39     41     GraphRow *pNext;            /* Next row down in the list of all rows */
    40     42     GraphRow *pPrev;            /* Previous row */
    41     43     
    42     44     int idx;                    /* Row index.  First is 1.  0 used for "none" */
    43         -  u8 isLeaf;                  /* True if no direct child nodes */
           45  +  int idxTop;                 /* Direct descendent highest up on the graph */
           46  +  GraphRow *pChild;           /* Child immediately above this node */
    44     47     u8 isDup;                   /* True if this is duplicate of a prior entry */
    45         -  int iRail;                  /* Which rail this check-in appears on. 0-based.*/
    46         -  int aiRaiser[GR_MAX_RAIL];  /* Raisers from this node to a higher row. */
    47         -  int bDescender;             /* Raiser from bottom of graph to here. */
    48         -  u32 mergeIn;                /* Merge in from other rails */
    49         -  int mergeOut;               /* Merge out to this rail */
    50         -  int mergeUpto;              /* Draw the merge rail up to this level */
           48  +  u8 isLeaf;                  /* True if this is a leaf node */
           49  +  u8 timeWarp;                /* Child is earlier in time */
           50  +  u8 bDescender;              /* True if riser from bottom of graph to here. */
           51  +  i8 iRail;                   /* Which rail this check-in appears on. 0-based.*/
           52  +  i8 mergeOut;                /* Merge out to this rail.  -1 if no merge-out */
           53  +  u8 mergeIn[GR_MAX_RAIL];    /* Merge in from non-zero rails */
           54  +  int aiRiser[GR_MAX_RAIL];   /* Risers from this node to a higher row. */
           55  +  int mergeUpto;              /* Draw the mergeOut rail up to this level */
           56  +  u64 mergeDown;              /* Draw merge lines up from bottom of graph */
    51     57   
    52         -  u32 railInUse;              /* Mask of occupied rails */
           58  +  u64 railInUse;              /* Mask of occupied rails at this row */
    53     59   };
    54     60   
    55     61   /* Context while building a graph
    56     62   */
    57     63   struct GraphContext {
    58         -  int nErr;             /* Number of errors encountered */
    59         -  int mxRail;           /* Number of rails required to render the graph */
    60         -  GraphRow *pFirst;     /* First row in the list */
    61         -  GraphRow *pLast;      /* Last row in the list */
    62         -  int nBranch;          /* Number of distinct branches */
    63         -  char **azBranch;      /* Names of the branches */
           64  +  int nErr;                  /* Number of errors encountered */
           65  +  int mxRail;                /* Number of rails required to render the graph */
           66  +  int iRailPitch;            /* Pixels between rail centers */
           67  +  GraphRow *pFirst;          /* First row in the list */
           68  +  GraphRow *pLast;           /* Last row in the list */
           69  +  int nBranch;               /* Number of distinct branches */
           70  +  char **azBranch;           /* Names of the branches */
    64     71     int nRow;                  /* Number of rows */
    65         -  int railMap[GR_MAX_RAIL];  /* Rail order mapping */
    66     72     int nHash;                 /* Number of slots in apHash[] */
    67         -  GraphRow **apHash;         /* Hash table of rows */
           73  +  GraphRow **apHash;         /* Hash table of GraphRow objects.  Key: rid */
    68     74   };
    69     75   
    70     76   #endif
    71     77   
           78  +/* The N-th bit */
           79  +#define BIT(N)  (((u64)1)<<(N))
           80  +
    72     81   /*
    73     82   ** Malloc for zeroed space.  Panic if unable to provide the
    74     83   ** requested space.
    75     84   */
    76     85   void *safeMalloc(int nByte){
    77         -  void *p = malloc(nByte);
    78         -  if( p==0 ) fossil_panic("out of memory");
           86  +  void *p = fossil_malloc(nByte);
    79     87     memset(p, 0, nByte);
    80     88     return p;
    81     89   }
    82     90   
    83     91   /*
    84     92   ** Create and initialize a GraphContext
    85     93   */
    86     94   GraphContext *graph_init(void){
    87     95     return (GraphContext*)safeMalloc( sizeof(GraphContext) );
    88     96   }
    89     97   
    90     98   /*
    91         -** Destroy a GraphContext;
           99  +** Clear all content from a graph
    92    100   */
    93         -void graph_free(GraphContext *p){
          101  +static void graph_clear(GraphContext *p){
    94    102     int i;
    95    103     GraphRow *pRow;
    96    104     while( p->pFirst ){
    97    105       pRow = p->pFirst;
    98    106       p->pFirst = pRow->pNext;
    99    107       free(pRow);
   100    108     }
   101    109     for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
   102    110     free(p->azBranch);
   103    111     free(p->apHash);
          112  +  memset(p, 0, sizeof(*p));
          113  +  p->nErr = 1;
          114  +}
          115  +
          116  +/*
          117  +** Destroy a GraphContext;
          118  +*/
          119  +void graph_free(GraphContext *p){
          120  +  graph_clear(p);
   104    121     free(p);
   105    122   }
   106    123   
   107    124   /*
   108         -** Insert a row into the hash table.  If there is already another
   109         -** row with the same rid, overwrite the prior entry if the overwrite
   110         -** flag is set.
          125  +** Insert a row into the hash table.  pRow->rid is the key.  Keys must
          126  +** be unique.  If there is already another row with the same rid,
          127  +** overwrite the prior entry if and only if the overwrite flag is set.
   111    128   */
   112    129   static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){
   113    130     int h;
   114    131     h = pRow->rid % p->nHash;
   115    132     while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
   116    133       h++;
   117    134       if( h>=p->nHash ) h = 0;
................................................................................
   133    150     return p->apHash[h];
   134    151   }
   135    152   
   136    153   /*
   137    154   ** Return the canonical pointer for a given branch name.
   138    155   ** Multiple calls to this routine with equivalent strings
   139    156   ** will return the same pointer.
          157  +**
          158  +** The returned value is a pointer to a (readonly) string that
          159  +** has the useful property that strings can be checked for 
          160  +** equality by comparing pointers.
   140    161   **
   141    162   ** Note: also used for background color names.
   142    163   */
   143    164   static char *persistBranchName(GraphContext *p, const char *zBranch){
   144    165     int i;
   145    166     for(i=0; i<p->nBranch; i++){
   146         -    if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
          167  +    if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
   147    168     }
   148    169     p->nBranch++;
   149         -  p->azBranch = realloc(p->azBranch, sizeof(char*)*p->nBranch);
   150         -  if( p->azBranch==0 ) fossil_panic("out of memory");
          170  +  p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch);
   151    171     p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
   152    172     return p->azBranch[p->nBranch-1];
   153    173   }
   154    174   
   155    175   /*
   156         -** Add a new row t the graph context.  Rows are added from top to bottom.
          176  +** Add a new row to the graph context.  Rows are added from top to bottom.
   157    177   */
   158    178   int graph_add_row(
   159    179     GraphContext *p,     /* The context to which the row is added */
   160    180     int rid,             /* RID for the check-in */
   161    181     int nParent,         /* Number of parents */
   162    182     int *aParent,        /* Array of parents */
   163    183     const char *zBranch, /* Branch for this check-in */
   164         -  const char *zBgClr   /* Background color. NULL or "" for white. */
          184  +  const char *zBgClr,  /* Background color. NULL or "" for white. */
          185  +  const char *zUuid,   /* SHA1 hash of the object being graphed */
          186  +  int isLeaf           /* True if this row is a leaf */
   165    187   ){
   166    188     GraphRow *pRow;
          189  +  int nByte;
   167    190   
   168    191     if( p->nErr ) return 0;
   169         -  if( nParent>GR_MAX_PARENT ){ p->nErr++; return 0; }
   170         -  pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) );
          192  +  nByte = sizeof(GraphRow);
          193  +  nByte += sizeof(pRow->aParent[0])*nParent;
          194  +  pRow = (GraphRow*)safeMalloc( nByte );
          195  +  pRow->aParent = (int*)&pRow[1];
   171    196     pRow->rid = rid;
   172    197     pRow->nParent = nParent;
   173    198     pRow->zBranch = persistBranchName(p, zBranch);
          199  +  if( zUuid==0 ) zUuid = "";
          200  +  sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
          201  +  pRow->isLeaf = isLeaf;
          202  +  memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
   174    203     if( zBgClr==0 || zBgClr[0]==0 ) zBgClr = "white";
   175    204     pRow->zBgClr = persistBranchName(p, zBgClr);
   176    205     memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
   177    206     if( p->pFirst==0 ){
   178    207       p->pFirst = pRow;
   179    208     }else{
   180    209       p->pLast->pNext = pRow;
   181    210     }
   182    211     p->pLast = pRow;
   183    212     p->nRow++;
   184         -  pRow->idx = p->nRow;
          213  +  pRow->idx = pRow->idxTop = p->nRow;
   185    214     return pRow->idx;
   186    215   }
   187    216   
   188    217   /*
   189    218   ** Return the index of a rail currently not in use for any row between
   190    219   ** top and bottom, inclusive.  
   191    220   */
   192    221   static int findFreeRail(
   193    222     GraphContext *p,         /* The graph context */
   194    223     int top, int btm,        /* Span of rows for which the rail is needed */
   195         -  u32 inUseMask,           /* Mask or rails already in use */
          224  +  u64 inUseMask,           /* Mask or rails already in use */
   196    225     int iNearto              /* Find rail nearest to this rail */
   197    226   ){
   198    227     GraphRow *pRow;
   199    228     int i;
   200    229     int iBest = 0;
   201    230     int iBestDist = 9999;
   202    231     for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){}
   203    232     while( pRow && pRow->idx<=btm ){
   204    233       inUseMask |= pRow->railInUse;
   205    234       pRow = pRow->pNext;
   206    235     }
   207    236     for(i=0; i<32; i++){
   208         -    if( (inUseMask & (1<<i))==0 ){
          237  +    if( (inUseMask & BIT(i))==0 ){
   209    238         int dist;
   210    239         if( iNearto<=0 ){
   211    240           return i;
   212    241         }
   213    242         dist = i - iNearto;
   214    243         if( dist<0 ) dist = -dist;
   215    244         if( dist<iBestDist ){
   216    245           iBestDist = dist;
   217    246           iBest = i;
   218    247         }
   219    248       }
   220    249     }
   221    250     if( iBestDist>1000 ) p->nErr++;
          251  +  if( iBest>p->mxRail ) p->mxRail = iBest;
   222    252     return iBest;
   223    253   }
          254  +
          255  +/*
          256  +** Assign all children of node pBottom to the same rail as pBottom.
          257  +*/
          258  +static void assignChildrenToRail(GraphRow *pBottom){
          259  +  int iRail = pBottom->iRail;
          260  +  GraphRow *pCurrent;
          261  +  GraphRow *pPrior;
          262  +  u64 mask = ((u64)1)<<iRail;
          263  +
          264  +  pBottom->iRail = iRail;
          265  +  pBottom->railInUse |= mask;
          266  +  pPrior = pBottom;
          267  +  for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){
          268  +    assert( pPrior->idx > pCurrent->idx );
          269  +    assert( pCurrent->iRail<0 );
          270  +    pCurrent->iRail = iRail;
          271  +    pCurrent->railInUse |= mask;
          272  +    pPrior->aiRiser[iRail] = pCurrent->idx;
          273  +    while( pPrior->idx > pCurrent->idx ){
          274  +      pPrior->railInUse |= mask;
          275  +      pPrior = pPrior->pPrev;
          276  +      assert( pPrior!=0 );
          277  +    }
          278  +  }
          279  +}
          280  +
          281  +/*
          282  +** Create a merge-arrow riser going from pParent up to pChild.
          283  +*/
          284  +static void createMergeRiser(
          285  +  GraphContext *p,
          286  +  GraphRow *pParent,
          287  +  GraphRow *pChild
          288  +){
          289  +  int u;
          290  +  u64 mask;
          291  +  GraphRow *pLoop;
          292  +
          293  +  if( pParent->mergeOut<0 ){
          294  +    u = pParent->aiRiser[pParent->iRail];
          295  +    if( u>=0 && u<pChild->idx ){
          296  +      /* The thick arrow up to the next primary child of pDesc goes
          297  +      ** further up than the thin merge arrow riser, so draw them both
          298  +      ** on the same rail. */
          299  +      pParent->mergeOut = pParent->iRail*4;
          300  +      if( pParent->iRail<pChild->iRail ) pParent->mergeOut += 2;
          301  +      pParent->mergeUpto = pChild->idx;
          302  +    }else{
          303  +      /* The thin merge arrow riser is taller than the thick primary
          304  +      ** child riser, so use separate rails. */
          305  +      int iTarget = pParent->iRail;
          306  +      pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1,
          307  +                                       0, iTarget)*4 + 1;
          308  +      pParent->mergeUpto = pChild->idx;
          309  +      mask = BIT(pParent->mergeOut/4);
          310  +      for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
          311  +           pLoop=pLoop->pNext){
          312  +        pLoop->railInUse |= mask;
          313  +      }
          314  +    }
          315  +  }
          316  +  pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1;
          317  +}
          318  +
          319  +/*
          320  +** Compute the maximum rail number.
          321  +*/
          322  +static void find_max_rail(GraphContext *p){
          323  +  GraphRow *pRow;
          324  +  p->mxRail = 0;
          325  +  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
          326  +    if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
          327  +    if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4;
          328  +    while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
          329  +      p->mxRail++;
          330  +    }
          331  +  }
          332  +}
          333  +
   224    334   
   225    335   /*
   226    336   ** Compute the complete graph
   227    337   */
   228    338   void graph_finish(GraphContext *p, int omitDescenders){
   229         -  GraphRow *pRow, *pDesc, *pDup, *pLoop;
          339  +  GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
   230    340     int i;
   231         -  u32 mask;
   232         -  u32 inUse;
   233         -  int hasDup = 0;    /* True if one or more isDup entries */
          341  +  u64 mask;
          342  +  u64 inUse;
          343  +  int hasDup = 0;      /* True if one or more isDup entries */
   234    344     const char *zTrunk;
   235    345   
   236    346     if( p==0 || p->pFirst==0 || p->nErr ) return;
          347  +  p->nErr = 1;   /* Assume an error until proven otherwise */
   237    348   
   238    349     /* Initialize all rows */
   239    350     p->nHash = p->nRow*2 + 1;
   240    351     p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash );
   241    352     for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
   242    353       if( pRow->pNext ) pRow->pNext->pPrev = pRow;
   243    354       pRow->iRail = -1;
................................................................................
   246    357         hasDup = 1;
   247    358         pDup->isDup = 1;
   248    359       }
   249    360       hashInsert(p, pRow, 1);
   250    361     }
   251    362     p->mxRail = -1;
   252    363   
   253         -  /* Purge merge-parents that are out-of-graph
          364  +  /* Purge merge-parents that are out-of-graph if descenders are not
          365  +  ** drawn.
          366  +  **
          367  +  ** Each node has one primary parent and zero or more "merge" parents.
          368  +  ** A merge parent is a prior checkin from which changes were merged into
          369  +  ** the current check-in.  If a merge parent is not in the visible section
          370  +  ** of this graph, then no arrows will be drawn for it, so remove it from
          371  +  ** the aParent[] array.
   254    372     */
   255         -  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
   256         -    for(i=1; i<pRow->nParent; i++){
   257         -      if( hashFind(p, pRow->aParent[i])==0 ){
   258         -        pRow->aParent[i] = pRow->aParent[--pRow->nParent];
   259         -        i--;
          373  +  if( omitDescenders ){
          374  +    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
          375  +      for(i=1; i<pRow->nParent; i++){
          376  +        if( hashFind(p, pRow->aParent[i])==0 ){
          377  +          pRow->aParent[i] = pRow->aParent[--pRow->nParent];
          378  +          i--;
          379  +        }
   260    380         }
   261    381       }
   262    382     }
   263    383   
   264         -  /* Figure out which nodes have no direct children (children on
   265         -  ** the same rail).  Mark such nodes as isLeaf.
          384  +
          385  +  /* Find the pChild pointer for each node. 
          386  +  **
          387  +  ** The pChild points to the node directly above on the same rail.
          388  +  ** The pChild must be in the same branch.  Leaf nodes have a NULL
          389  +  ** pChild.
          390  +  **
          391  +  ** In the case of a fork, choose the pChild that results in the
          392  +  ** longest rail.
   266    393     */
   267         -  memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
   268         -  for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
   269         -  for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
   270         -    GraphRow *pParent;
   271         -    hashInsert(p, pRow, 0);
   272         -    if( !pRow->isDup
   273         -     && pRow->nParent>0 
   274         -     && (pParent = hashFind(p, pRow->aParent[0]))!=0
   275         -     && pRow->zBranch==pParent->zBranch
   276         -    ){
   277         -      pParent->isLeaf = 0;
          394  +  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
          395  +    if( pRow->isDup ) continue;
          396  +    if( pRow->nParent==0 ) continue;                   /* Root node */
          397  +    pParent = hashFind(p, pRow->aParent[0]);
          398  +    if( pParent==0 ) continue;                         /* Parent off-screen */
          399  +    if( pParent->zBranch!=pRow->zBranch ) continue;    /* Different branch */
          400  +    if( pParent->idx <= pRow->idx ){
          401  +       pParent->timeWarp = 1;
          402  +       continue;                                       /* Time-warp */
          403  +    }
          404  +    if( pRow->idxTop < pParent->idxTop ){
          405  +      pParent->pChild = pRow;
          406  +      pParent->idxTop = pRow->idxTop;
   278    407       }
   279    408     }
   280    409   
   281    410     /* Identify rows where the primary parent is off screen.  Assign
   282    411     ** each to a rail and draw descenders to the bottom of the screen.
          412  +  **
          413  +  ** Strive to put the "trunk" branch on far left.
   283    414     */
   284    415     zTrunk = persistBranchName(p, "trunk");
   285    416     for(i=0; i<2; i++){
   286         -    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
          417  +    for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
          418  +      if( pRow->isDup ) continue;
   287    419         if( i==0 ){
   288    420           if( pRow->zBranch!=zTrunk ) continue;
   289    421         }else {
   290    422           if( pRow->iRail>=0 ) continue;
   291    423         }
   292    424         if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
   293    425           if( omitDescenders ){
   294         -          pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
          426  +          pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0, 0);
   295    427           }else{
   296    428             pRow->iRail = ++p->mxRail;
   297    429           }
   298         -        mask = 1<<(pRow->iRail);
   299         -        if( omitDescenders ){
   300         -          pRow->railInUse |= mask;
   301         -          if( pRow->pNext ) pRow->pNext->railInUse |= mask;
   302         -        }else{
          430  +        if( p->mxRail>=GR_MAX_RAIL ) return;
          431  +        mask = BIT(pRow->iRail);
          432  +        if( !omitDescenders ){
   303    433             pRow->bDescender = pRow->nParent>0;
   304         -          for(pDesc=pRow; pDesc; pDesc=pDesc->pNext){
   305         -            pDesc->railInUse |= mask;
          434  +          for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
          435  +            pLoop->railInUse |= mask;
   306    436             }
   307    437           }
          438  +        assignChildrenToRail(pRow);
   308    439         }
   309    440       }
   310    441     }
   311    442   
   312    443     /* Assign rails to all rows that are still unassigned.
   313         -  ** The first primary child of a row goes on the same rail as
   314         -  ** that row.
   315    444     */
   316         -  inUse = (1<<(p->mxRail+1))-1;
          445  +  inUse = BIT(p->mxRail+1) - 1;
   317    446     for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
   318    447       int parentRid;
   319         -    if( pRow->iRail>=0 ) continue;
          448  +
          449  +    if( pRow->iRail>=0 ){
          450  +      if( pRow->pChild==0 && !pRow->timeWarp ){
          451  +        if( omitDescenders || count_nonbranch_children(pRow->rid)==0 ){
          452  +          inUse &= ~BIT(pRow->iRail);
          453  +        }else{
          454  +          pRow->aiRiser[pRow->iRail] = 0;
          455  +          mask = BIT(pRow->iRail);
          456  +          for(pLoop=pRow; pLoop; pLoop=pLoop->pPrev){
          457  +            pLoop->railInUse |= mask;
          458  +          }
          459  +        }
          460  +      }
          461  +      continue;
          462  +    }
   320    463       if( pRow->isDup ){
   321         -      pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
   322         -      pDesc = pRow;
          464  +      continue;
   323    465       }else{
   324    466         assert( pRow->nParent>0 );
   325    467         parentRid = pRow->aParent[0];
   326         -      pDesc = hashFind(p, parentRid);
   327         -      if( pDesc==0 ){
   328         -        /* Time skew */
          468  +      pParent = hashFind(p, parentRid);
          469  +      if( pParent==0 ){
   329    470           pRow->iRail = ++p->mxRail;
   330         -        pRow->railInUse = 1<<pRow->iRail;
          471  +        if( p->mxRail>=GR_MAX_RAIL ) return;
          472  +        pRow->railInUse = BIT(pRow->iRail);
   331    473           continue;
   332    474         }
   333         -      if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){
   334         -        pRow->iRail = pDesc->iRail;
          475  +      if( pParent->idx>pRow->idx ){
          476  +        /* Common case:  Child occurs after parent and is above the
          477  +        ** parent in the timeline */
          478  +        pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail);
          479  +        if( p->mxRail>=GR_MAX_RAIL ) return;
          480  +        pParent->aiRiser[pRow->iRail] = pRow->idx;
   335    481         }else{
   336         -        pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, pDesc->iRail);
          482  +        /* Timewarp case:  Child occurs earlier in time than parent and
          483  +        ** appears below the parent in the timeline. */
          484  +        int iDownRail = ++p->mxRail;
          485  +        if( iDownRail<1 ) iDownRail = ++p->mxRail;
          486  +        pRow->iRail = ++p->mxRail;
          487  +        if( p->mxRail>=GR_MAX_RAIL ) return;
          488  +        pRow->railInUse = BIT(pRow->iRail);
          489  +        pParent->aiRiser[iDownRail] = pRow->idx;
          490  +        mask = BIT(iDownRail);
          491  +        inUse |= mask;
          492  +        for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
          493  +          pLoop->railInUse |= mask;
          494  +        }
   337    495         }
   338         -      pDesc->aiRaiser[pRow->iRail] = pRow->idx;
   339    496       }
   340         -    mask = 1<<pRow->iRail;
   341         -    if( pRow->isLeaf ){
          497  +    mask = BIT(pRow->iRail);
          498  +    pRow->railInUse |= mask;
          499  +    if( pRow->pChild==0 ){
   342    500         inUse &= ~mask;
   343    501       }else{
   344    502         inUse |= mask;
          503  +      assignChildrenToRail(pRow);
   345    504       }
   346         -    for(pLoop=pRow; pLoop && pLoop!=pDesc; pLoop=pLoop->pNext){
   347         -      pLoop->railInUse |= mask;
          505  +    if( pParent ){
          506  +      for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
          507  +        pLoop->railInUse |= mask;
          508  +      }
   348    509       }
   349         -    pDesc->railInUse |= mask;
   350    510     }
   351    511   
   352    512     /*
   353    513     ** Insert merge rails and merge arrows
   354    514     */
   355    515     for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
   356    516       for(i=1; i<pRow->nParent; i++){
   357    517         int parentRid = pRow->aParent[i];
   358    518         pDesc = hashFind(p, parentRid);
   359         -      if( pDesc==0 ) continue;
   360         -      if( pDesc->mergeOut<0 ){
   361         -        int iTarget = (pRow->iRail + pDesc->iRail)/2;
   362         -        pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
   363         -        pDesc->mergeUpto = pRow->idx;
   364         -        mask = 1<<pDesc->mergeOut;
   365         -        pDesc->railInUse |= mask;
   366         -        for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid;
   367         -             pDesc=pDesc->pNext){
   368         -          pDesc->railInUse |= mask;
          519  +      if( pDesc==0 ){
          520  +        /* Merge from a node that is off-screen */
          521  +        int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0);
          522  +        if( p->mxRail>=GR_MAX_RAIL ) return;
          523  +        mask = BIT(iMrail);
          524  +        pRow->mergeIn[iMrail] = 2;
          525  +        pRow->mergeDown |= mask;
          526  +        for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
          527  +          pLoop->railInUse |= mask;
   369    528           }
          529  +      }else{
          530  +        /* Merge from an on-screen node */
          531  +        createMergeRiser(p, pDesc, pRow);
          532  +        if( p->mxRail>=GR_MAX_RAIL ) return;
   370    533         }
   371         -      pRow->mergeIn |= 1<<pDesc->mergeOut;
   372    534       }
   373    535     }
   374    536   
   375    537     /*
   376    538     ** Insert merge rails from primaries to duplicates. 
   377    539     */
   378    540     if( hasDup ){
          541  +    int dupRail;
          542  +    int mxRail;
          543  +    find_max_rail(p);
          544  +    mxRail = p->mxRail;
          545  +    dupRail = mxRail+1;
          546  +    if( p->mxRail>=GR_MAX_RAIL ) return;
   379    547       for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
   380    548         if( !pRow->isDup ) continue;
          549  +      pRow->iRail = dupRail;
   381    550         pDesc = hashFind(p, pRow->rid);
   382    551         assert( pDesc!=0 && pDesc!=pRow );
   383         -      if( pDesc->mergeOut<0 ){
   384         -        int iTarget = (pRow->iRail + pDesc->iRail)/2;
   385         -        pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
   386         -        pDesc->mergeUpto = pRow->idx;
   387         -        mask = 1<<pDesc->mergeOut;
   388         -        pDesc->railInUse |= mask;
   389         -        for(pLoop=pRow->pNext; pLoop && pLoop!=pDesc; pLoop=pLoop->pNext){
   390         -          pLoop->railInUse |= mask;
   391         -        }
          552  +      createMergeRiser(p, pDesc, pRow);
          553  +      if( pDesc->mergeOut/4>mxRail ) mxRail = pDesc->mergeOut/4;
          554  +    }
          555  +    if( dupRail<=mxRail ){
          556  +      dupRail = mxRail+1;
          557  +      for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
          558  +        if( pRow->isDup ) pRow->iRail = dupRail;
   392    559         }
   393         -      pRow->mergeIn |= 1<<pDesc->mergeOut;
   394    560       }
          561  +    if( mxRail>=GR_MAX_RAIL ) return;
   395    562     }
   396    563   
   397    564     /*
   398    565     ** Find the maximum rail number.
   399    566     */
   400         -  for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
   401         -  p->mxRail = 0;
   402         -  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
   403         -    if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
   404         -    if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
   405         -  }
          567  +  find_max_rail(p);
          568  +  p->iRailPitch = 18 - (p->mxRail/3);
          569  +  if( p->iRailPitch<12 ) p->iRailPitch = 12;
          570  +  p->nErr = 0;
   406    571   }

Added src/gzip.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to incrementally generate a GZIP compressed
           19  +** file.  The GZIP format is described in RFC-1952.
           20  +**
           21  +** State information is stored in static variables, so this implementation
           22  +** can only be building up a single GZIP file at a time.
           23  +*/
           24  +#include <assert.h>
           25  +#include <zlib.h>
           26  +#include "config.h"
           27  +#include "gzip.h"
           28  +
           29  +/*
           30  +** State information for the GZIP file under construction.
           31  +*/
           32  +struct gzip_state {
           33  +  int eState;           /* 0: idle   1: header  2: compressing */
           34  +  int iCRC;             /* The checksum */
           35  +  z_stream stream;      /* The working compressor */
           36  +  Blob out;             /* Results stored here */
           37  +} gzip;
           38  +
           39  +/*
           40  +** Write a 32-bit integer as little-endian into the given buffer.
           41  +*/
           42  +static void put32(char *z, int v){
           43  +  z[0] = v & 0xff;
           44  +  z[1] = (v>>8) & 0xff;
           45  +  z[2] = (v>>16) & 0xff;
           46  +  z[3] = (v>>24) & 0xff;
           47  +}
           48  +
           49  +/*
           50  +** Begin constructing a gzip file.
           51  +*/
           52  +void gzip_begin(sqlite3_int64 now){
           53  +  char aHdr[10];
           54  +  assert( gzip.eState==0 );
           55  +  blob_zero(&gzip.out);
           56  +  aHdr[0] = 0x1f;
           57  +  aHdr[1] = 0x8b;
           58  +  aHdr[2] = 8;
           59  +  aHdr[3] = 0;
           60  +  if( now==0 ){
           61  +    now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0");
           62  +  }
           63  +  put32(&aHdr[4], now&0xffffffff);
           64  +  aHdr[8] = 2;
           65  +  aHdr[9] = 255;
           66  +  blob_append(&gzip.out, aHdr, 10);
           67  +  gzip.iCRC = 0;
           68  +  gzip.eState = 1;
           69  +}
           70  +
           71  +/*
           72  +** Add nIn bytes of content from pIn to the gzip file.
           73  +*/
           74  +#define GZIP_BUFSZ 100000
           75  +void gzip_step(const char *pIn, int nIn){
           76  +  char *zOutBuf;
           77  +  int nOut;
           78  +  
           79  +  nOut = nIn + nIn/10 + 100;
           80  +  if( nOut<100000 ) nOut = 100000;
           81  +  zOutBuf = fossil_malloc(nOut);
           82  +  gzip.stream.avail_in = nIn;
           83  +  gzip.stream.next_in = (unsigned char*)pIn;
           84  +  gzip.stream.avail_out = nOut;
           85  +  gzip.stream.next_out = (unsigned char*)zOutBuf;
           86  +  if( gzip.eState==1 ){
           87  +    gzip.stream.zalloc = (alloc_func)0;
           88  +    gzip.stream.zfree = (free_func)0;
           89  +    gzip.stream.opaque = 0;
           90  +    deflateInit2(&gzip.stream, 9, Z_DEFLATED, -MAX_WBITS,8,Z_DEFAULT_STRATEGY);
           91  +    gzip.eState = 2;
           92  +  }
           93  +  gzip.iCRC = crc32(gzip.iCRC, gzip.stream.next_in, gzip.stream.avail_in);
           94  +  do{
           95  +    deflate(&gzip.stream, nIn==0 ? Z_FINISH : 0);
           96  +    blob_append(&gzip.out, zOutBuf, nOut - gzip.stream.avail_out);
           97  +    gzip.stream.avail_out = nOut;
           98  +    gzip.stream.next_out = (unsigned char*)zOutBuf;
           99  +  }while( gzip.stream.avail_in>0 );
          100  +  fossil_free(zOutBuf);
          101  +}
          102  +
          103  +/*
          104  +** Finish the gzip file and put the content in *pOut
          105  +*/
          106  +void gzip_finish(Blob *pOut){
          107  +  char aTrailer[8];
          108  +  assert( gzip.eState>0 );
          109  +  gzip_step("", 0);
          110  +  deflateEnd(&gzip.stream);
          111  +  put32(aTrailer, gzip.iCRC);
          112  +  put32(&aTrailer[4], gzip.stream.total_in);
          113  +  blob_append(&gzip.out, aTrailer, 8);
          114  +  *pOut = gzip.out;
          115  +  blob_zero(&gzip.out);
          116  +  gzip.eState = 0;
          117  +}
          118  +
          119  +/*
          120  +** COMMAND: test-gzip
          121  +**
          122  +** Usage: %fossil test-gzip FILENAME
          123  +**
          124  +** Compress a file using gzip.
          125  +*/
          126  +void test_gzip_cmd(void){
          127  +  Blob b;
          128  +  char *zOut;
          129  +  if( g.argc!=3 ) usage("FILENAME");
          130  +  sqlite3_open(":memory:", &g.db);
          131  +  gzip_begin(0);
          132  +  blob_read_from_file(&b, g.argv[2]);
          133  +  zOut = mprintf("%s.gz", g.argv[2]);
          134  +  gzip_step(blob_buffer(&b), blob_size(&b));
          135  +  blob_reset(&b);
          136  +  gzip_finish(&b);
          137  +  blob_write_to_file(&b, zOut);
          138  +  blob_reset(&b);
          139  +  fossil_free(zOut);
          140  +}

Changes to src/http.c.

    37     37     Blob nonce;          /* The nonce */
    38     38     const char *zLogin;  /* The user login name */
    39     39     const char *zPw;     /* The user password */
    40     40     Blob pw;             /* The nonce with user password appended */
    41     41     Blob sig;            /* The signature field */
    42     42   
    43     43     blob_zero(pLogin);
    44         -  if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
           44  +  if( g.urlUser==0 || fossil_strcmp(g.urlUser, "anonymous")==0 ){
    45     45        return;  /* If no login card for users "nobody" and "anonymous" */
    46     46     }
           47  +  if( g.urlIsSsh ){
           48  +     return;  /* If no login card for SSH: */
           49  +  }
    47     50     blob_zero(&nonce);
    48     51     blob_zero(&pw);
    49     52     sha1sum_blob(pPayload, &nonce);
    50     53     blob_copy(&pw, &nonce);
    51     54     zLogin = g.urlUser;
    52     55     if( g.urlPasswd ){
    53     56       zPw = g.urlPasswd;
................................................................................
    55     58       /* Password failure while doing a sync from the web interface */
    56     59       cgi_printf("*** incorrect or missing password for user %h\n", zLogin);
    57     60       zPw = 0;
    58     61     }else{
    59     62       /* Password failure while doing a sync from the command-line interface */
    60     63       url_prompt_for_password();
    61     64       zPw = g.urlPasswd;
    62         -    if( !g.dontKeepUrl ) db_set("last-sync-pw", zPw, 0);
           65  +    if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0);
    63     66     }
           67  +
           68  +  /* If the first character of the password is "#", then that character is
           69  +  ** not really part of the password - it is an indicator that we should
           70  +  ** use Basic Authentication.  So skip that character.
           71  +  */
           72  +  if( zPw && zPw[0]=='#' ) zPw++;
    64     73   
    65     74     /* The login card wants the SHA1 hash of the password, so convert the
    66     75     ** password to its SHA1 hash it it isn't already a SHA1 hash.
    67         -  **
    68         -  ** Except, if the password begins with "*" then use the characters
    69         -  ** after the "*" as a cleartext password.  Put an "*" at the beginning
    70         -  ** of the password to trick a newer client to use the cleartext password
    71         -  ** protocol required by legacy servers.
    72     76     */
    73         -  if( zPw && zPw[0] ){
    74         -    if( zPw[0]=='*' ){
    75         -      zPw++;
    76         -    }else{
    77         -      zPw = sha1_shared_secret(zPw, zLogin);
    78         -    }
    79         -  }
           77  +  if( zPw && zPw[0] ) zPw = sha1_shared_secret(zPw, zLogin, 0);
    80     78   
    81     79     blob_append(&pw, zPw, -1);
    82     80     sha1sum_blob(&pw, &sig);
    83     81     blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
    84     82     blob_reset(&pw);
    85     83     blob_reset(&sig);
    86     84     blob_reset(&nonce);
................................................................................
    98     96     blob_zero(pHdr);
    99     97     i = strlen(g.urlPath);
   100     98     if( i>0 && g.urlPath[i-1]=='/' ){
   101     99       zSep = "";
   102    100     }else{
   103    101       zSep = "/";
   104    102     }
   105         -  blob_appendf(pHdr, "POST %s%sxfer HTTP/1.0\r\n", g.urlPath, zSep);
          103  +  blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep);
   106    104     if( g.urlProxyAuth ){
   107         -    blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth);
          105  +    blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.urlProxyAuth);
          106  +  }
          107  +  if( g.urlPasswd && g.urlUser && g.urlPasswd[0]=='#' ){
          108  +    char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]);
          109  +    char *zEncoded = encode64(zCredentials, -1);
          110  +    blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
          111  +    fossil_free(zEncoded);
          112  +    fossil_free(zCredentials);
   108    113     }
   109    114     blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
   110         -  blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
          115  +  blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION 
          116  +                     " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n");
   111    117     if( g.fHttpTrace ){
   112    118       blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
   113    119     }else{
   114    120       blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
   115    121     }
   116    122     blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload));
   117    123   }
................................................................................
   122    128   ** in pRecv.  pRecv is assumed to be uninitialized when
   123    129   ** this routine is called - this routine will initialize it.
   124    130   **
   125    131   ** The server address is contain in the "g" global structure.  The
   126    132   ** url_parse() routine should have been called prior to this routine
   127    133   ** in order to fill this structure appropriately.
   128    134   */
   129         -void http_exchange(Blob *pSend, Blob *pReply, int useLogin){
          135  +int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){
   130    136     Blob login;           /* The login card */
   131    137     Blob payload;         /* The complete payload including login card */
   132    138     Blob hdr;             /* The HTTP request header */
   133    139     int closeConnection;  /* True to close the connection when done */
   134    140     int iLength;          /* Length of the reply payload */
   135         -  int rc;               /* Result code */
          141  +  int rc = 0;           /* Result code */
   136    142     int iHttpVersion;     /* Which version of HTTP protocol server uses */
   137    143     char *zLine;          /* A single line of the reply header */
   138    144     int i;                /* Loop counter */
          145  +  int isError = 0;      /* True if the reply is an error message */
          146  +  int isCompressed = 1; /* True if the reply is compressed */
   139    147   
   140    148     if( transport_open() ){
   141         -    fossil_fatal(transport_errmsg());
          149  +    fossil_warning(transport_errmsg());
          150  +    return 1;
   142    151     }
   143    152   
   144    153     /* Construct the login card and prepare the complete payload */
   145    154     blob_zero(&login);
   146    155     if( useLogin ) http_build_login_card(pSend, &login);
   147    156     if( g.fHttpTrace ){
   148    157       payload = login;
................................................................................
   155    164     /* Construct the HTTP request header */
   156    165     http_build_header(&payload, &hdr);
   157    166   
   158    167     /* When tracing, write the transmitted HTTP message both to standard
   159    168     ** output and into a file.  The file can then be used to drive the
   160    169     ** server-side like this:
   161    170     **
   162         -  **      ./fossil http <http-trace-1.txt
          171  +  **      ./fossil test-http <http-request-1.txt
   163    172     */
   164    173     if( g.fHttpTrace ){
   165    174       static int traceCnt = 0;
   166    175       char *zOutFile;
   167    176       FILE *out;
   168    177       traceCnt++;
   169         -    zOutFile = mprintf("http-trace-%d.txt", traceCnt);
   170         -    printf("HTTP SEND: (%s)\n%s%s=======================\n", 
   171         -        zOutFile, blob_str(&hdr), blob_str(&payload));
   172         -    out = fopen(zOutFile, "w");
          178  +    zOutFile = mprintf("http-request-%d.txt", traceCnt);
          179  +    out = fopen(zOutFile, "wb");
   173    180       if( out ){
   174    181         fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out);
   175    182         fwrite(blob_buffer(&payload), 1, blob_size(&payload), out);
   176    183         fclose(out);
   177    184       }
          185  +    free(zOutFile);
          186  +    zOutFile = mprintf("http-reply-%d.txt", traceCnt);
          187  +    out = fopen(zOutFile, "wb");
          188  +    transport_log(out);
          189  +    free(zOutFile);
   178    190     }
   179    191   
   180    192     /*
   181    193     ** Send the request to the server.
   182    194     */
   183    195     transport_send(&hdr);
   184    196     transport_send(&payload);
................................................................................
   188    200     
   189    201     /*
   190    202     ** Read and interpret the server reply
   191    203     */
   192    204     closeConnection = 1;
   193    205     iLength = -1;
   194    206     while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
   195         -    if( strncasecmp(zLine, "http/1.", 7)==0 ){
          207  +    /* printf("[%s]\n", zLine); fflush(stdout); */
          208  +    if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
   196    209         if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
   197    210         if( rc!=200 && rc!=302 ){
   198    211           int ii;
   199    212           for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
   200    213           while( zLine[ii]==' ' ) ii++;
   201         -        fossil_fatal("server says: %s\n", &zLine[ii]);
          214  +        fossil_warning("server says: %s", &zLine[ii]);
   202    215           goto write_err;
   203    216         }
   204    217         if( iHttpVersion==0 ){
   205    218           closeConnection = 1;
   206    219         }else{
   207    220           closeConnection = 0;
   208    221         }
   209         -    }else if( strncasecmp(zLine, "content-length:", 15)==0 ){
   210         -      for(i=15; isspace(zLine[i]); i++){}
          222  +    }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
          223  +      for(i=15; fossil_isspace(zLine[i]); i++){}
   211    224         iLength = atoi(&zLine[i]);
   212         -    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
          225  +    }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
   213    226         char c;
   214         -      for(i=11; isspace(zLine[i]); i++){}
          227  +      for(i=11; fossil_isspace(zLine[i]); i++){}
   215    228         c = zLine[i];
   216    229         if( c=='c' || c=='C' ){
   217    230           closeConnection = 1;
   218    231         }else if( c=='k' || c=='K' ){
   219    232           closeConnection = 0;
   220    233         }
   221         -    }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){
          234  +    }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){
   222    235         int i, j;
          236  +
          237  +      if ( --maxRedirect == 0){
          238  +        fossil_fatal("redirect limit exceeded");
          239  +      }
   223    240         for(i=9; zLine[i] && zLine[i]==' '; i++){}
   224    241         if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine);
   225    242         j = strlen(zLine) - 1; 
   226         -      if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0;
          243  +      while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
          244  +         j -= 4;
          245  +         zLine[j] = 0;
          246  +      }
   227    247         fossil_print("redirect to %s\n", &zLine[i]);
   228    248         url_parse(&zLine[i]);
   229    249         transport_close();
   230         -      http_exchange(pSend, pReply, useLogin);
   231         -      return;
          250  +      return http_exchange(pSend, pReply, useLogin, maxRedirect);
          251  +    }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
          252  +      if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
          253  +        isCompressed = 0;
          254  +      }else if( fossil_strnicmp(&zLine[14], 
          255  +                          "application/x-fossil-uncompressed", -1)==0 ){
          256  +        isCompressed = 0;
          257  +      }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){
          258  +        isError = 1;
          259  +      }
   232    260       }
   233    261     }
          262  +  if( iLength<0 ){
          263  +    fossil_fatal("server did not reply");
          264  +    goto write_err;
          265  +  }
   234    266     if( rc!=200 ){
   235         -    fossil_fatal("\"location:\" missing from 302 redirect reply");
          267  +    fossil_warning("\"location:\" missing from 302 redirect reply");
   236    268       goto write_err;
   237    269     }
   238    270   
   239    271     /*
   240    272     ** Extract the reply payload that follows the header
   241    273     */
   242         -  if( iLength<0 ){
   243         -    fossil_fatal("server did not reply");
   244         -    goto write_err;
   245         -  }
   246    274     blob_zero(pReply);
   247    275     blob_resize(pReply, iLength);
   248    276     iLength = transport_receive(blob_buffer(pReply), iLength);
   249    277     blob_resize(pReply, iLength);
   250         -  if( g.fHttpTrace ){
   251         -    printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
   252         -  }else{
   253         -    blob_uncompress(pReply, pReply);
          278  +  if( isError ){
          279  +    char *z;
          280  +    int i, j;
          281  +    z = blob_str(pReply);
          282  +    for(i=j=0; z[i]; i++, j++){
          283  +      if( z[i]=='<' ){
          284  +        while( z[i] && z[i]!='>' ) i++;
          285  +        if( z[i]==0 ) break;
          286  +      }
          287  +      z[j] = z[i];
          288  +    }
          289  +    z[j] = 0;
          290  +    fossil_fatal("server sends error: %s", z);
   254    291     }
          292  +  if( isCompressed ) blob_uncompress(pReply, pReply);
   255    293   
   256    294     /*
   257    295     ** Close the connection to the server if appropriate.
   258    296     **
   259    297     ** FIXME:  There is some bug in the lower layers that prevents the
   260    298     ** connection from remaining open.  The easiest fix for now is to
   261    299     ** simply close and restart the connection for each round-trip.
................................................................................
   262    300     */
   263    301     closeConnection = 1; /* FIX ME */
   264    302     if( closeConnection ){
   265    303       transport_close();
   266    304     }else{
   267    305       transport_rewind();
   268    306     }
   269         -  return;
          307  +  return 0;
   270    308   
   271    309     /* 
   272    310     ** Jump to here if an error is seen.
   273    311     */
   274    312   write_err:
   275    313     transport_close();
   276         -  return;  
          314  +  return 1;  
   277    315   }

Changes to src/http_socket.c.

    24     24   **
    25     25   ** Low-level sockets are abstracted out into this module because they 
    26     26   ** are handled different on Unix and windows.
    27     27   */
    28     28   
    29     29   #include "config.h"
    30     30   #include "http_socket.h"
    31         -#ifdef __MINGW32__
    32         -#  include <windows.h>
           31  +#if defined(_WIN32)
    33     32   #  include <winsock2.h>
           33  +#  include <ws2tcpip.h>
    34     34   #else
           35  +#  include <netinet/in.h>
    35     36   #  include <arpa/inet.h>
    36     37   #  include <sys/socket.h>
    37     38   #  include <netdb.h>
    38         -#  include <netinet/in.h>
    39     39   #endif
    40     40   #include <assert.h>
    41     41   #include <sys/types.h>
    42     42   #include <signal.h>
    43     43   
    44     44   /*
    45     45   ** There can only be a single socket connection open at a time.
    46     46   ** State information about that socket is stored in the following
    47     47   ** local variables:
    48     48   */
    49     49   static int socketIsInit = 0;    /* True after global initialization */
    50         -#ifdef __MINGW32__
           50  +#if defined(_WIN32)
    51     51   static WSADATA socketInfo;      /* Windows socket initialize data */
    52     52   #endif
    53     53   static int iSocket = -1;        /* The socket on which we talk to the server */
    54     54   static char *socketErrMsg = 0;  /* Text of most recent socket error */
    55     55   
    56     56   
    57     57   /*
................................................................................
    82     82   
    83     83   /*
    84     84   ** Call this routine once before any other use of the socket interface.
    85     85   ** This routine does initial configuration of the socket module.
    86     86   */
    87     87   void socket_global_init(void){
    88     88     if( socketIsInit==0 ){
    89         -#ifdef __MINGW32__
           89  +#if defined(_WIN32)
    90     90       if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){
    91     91         fossil_panic("can't initialize winsock");
    92     92       }
    93     93   #endif
    94     94       socketIsInit = 1;
    95     95     }
    96     96   }
................................................................................
    97     97   
    98     98   /*
    99     99   ** Call this routine to shutdown the socket module prior to program
   100    100   ** exit.
   101    101   */
   102    102   void socket_global_shutdown(void){
   103    103     if( socketIsInit ){
   104         -#ifdef __MINGW32__
          104  +#if defined(_WIN32)
   105    105       WSACleanup();
   106    106   #endif
   107    107       socket_clear_errmsg();
   108    108       socketIsInit = 0;
   109    109     }
   110    110   }
   111    111   
   112    112   /*
   113    113   ** Close the currently open socket.  If no socket is open, this routine
   114    114   ** is a no-op.
   115    115   */
   116    116   void socket_close(void){
   117    117     if( iSocket>=0 ){
   118         -#ifdef __MINGW32__
          118  +#if defined(_WIN32)
   119    119       closesocket(iSocket);
   120    120   #else
   121    121       close(iSocket);
   122    122   #endif
   123    123       iSocket = -1;
   124    124     }
   125    125   }
   126    126   
   127    127   /*
   128    128   ** Open a socket connection.  The identify of the server is determined
   129         -** by global varibles that are set using url_parse():
          129  +** by global variables that are set using url_parse():
   130    130   **
   131    131   **    g.urlName       Name of the server.  Ex: www.fossil-scm.org
   132    132   **    g.urlPort       TCP/IP port to use.  Ex: 80
   133    133   **
   134    134   ** Return the number of errors.
   135    135   */
   136    136   int socket_open(void){
................................................................................
   147    147         struct hostent *pHost;
   148    148         pHost = gethostbyname(g.urlName);
   149    149         if( pHost!=0 ){
   150    150           memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length);
   151    151         }else
   152    152   #endif
   153    153         {
   154         -        socket_set_errmsg("can't resolve host name: %s\n", g.urlName);
          154  +        socket_set_errmsg("can't resolve host name: %s", g.urlName);
   155    155           return 1;
   156    156         }
   157    157       }
   158    158       addrIsInit = 1;
   159    159   
   160    160       /* Set the Global.zIpAddr variable to the server we are talking to.
   161    161       ** This is used to populate the ipaddr column of the rcvfrom table,
................................................................................
   169    169       return 1;
   170    170     }
   171    171     if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
   172    172       socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
   173    173       socket_close();
   174    174       return 1;
   175    175     }
   176         -#ifndef __MINGW32__
          176  +#if !defined(_WIN32)
   177    177     signal(SIGPIPE, SIG_IGN);
   178    178   #endif
   179    179     return 0;
   180    180   }
   181    181   
   182    182   /*
   183    183   ** Send content out over the open socket connection.
................................................................................
   195    195     return total;
   196    196   }
   197    197   
   198    198   /*
   199    199   ** Receive content back from the open socket connection.
   200    200   */
   201    201   size_t socket_receive(void *NotUsed, void *pContent, size_t N){
   202         -  size_t got;
          202  +  ssize_t got;
   203    203     size_t total = 0;
   204    204     while( N>0 ){
   205    205       got = recv(iSocket, pContent, N, 0);
   206    206       if( got<=0 ) break;
   207         -    total += got;
   208         -    N -= got;
          207  +    total += (size_t)got;
          208  +    N -= (size_t)got;
   209    209       pContent = (void*)&((char*)pContent)[got];
   210    210     }
   211    211     return total;
   212    212   }

Added src/http_ssl.c.

            1  +/*
            2  +** Copyright (c) 2009 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +**
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file manages low-level SSL communications.
           19  +**
           20  +** This file implements a singleton.  A single SSL connection may be active
           21  +** at a time.  State information is stored in static variables.  The identity
           22  +** of the server is held in global variables that are set by url_parse().
           23  +**
           24  +** SSL support is abstracted out into this module because Fossil can
           25  +** be compiled without SSL support (which requires OpenSSL library)
           26  +*/
           27  +
           28  +#include "config.h"
           29  +
           30  +#ifdef FOSSIL_ENABLE_SSL
           31  +
           32  +#include <openssl/bio.h>
           33  +#include <openssl/ssl.h>
           34  +#include <openssl/err.h>
           35  +
           36  +#include "http_ssl.h"
           37  +#include <assert.h>
           38  +#include <sys/types.h>
           39  +
           40  +/*
           41  +** There can only be a single OpenSSL IO connection open at a time.
           42  +** State information about that IO is stored in the following
           43  +** local variables:
           44  +*/
           45  +static int sslIsInit = 0;    /* True after global initialization */
           46  +static BIO *iBio;            /* OpenSSL I/O abstraction */
           47  +static char *sslErrMsg = 0;  /* Text of most recent OpenSSL error */
           48  +static SSL_CTX *sslCtx;      /* SSL context */
           49  +static SSL *ssl;
           50  +
           51  +
           52  +/*
           53  +** Clear the SSL error message
           54  +*/
           55  +static void ssl_clear_errmsg(void){
           56  +  free(sslErrMsg);
           57  +  sslErrMsg = 0;
           58  +}
           59  +
           60  +/*
           61  +** Set the SSL error message.
           62  +*/
           63  +void ssl_set_errmsg(char *zFormat, ...){
           64  +  va_list ap;
           65  +  ssl_clear_errmsg();
           66  +  va_start(ap, zFormat);
           67  +  sslErrMsg = vmprintf(zFormat, ap);
           68  +  va_end(ap);
           69  +}
           70  +
           71  +/*
           72  +** Return the current SSL error message
           73  +*/
           74  +const char *ssl_errmsg(void){
           75  +  return sslErrMsg;
           76  +}
           77  +
           78  +/*
           79  +** When a server requests a client certificate that hasn't been provided,
           80  +** display a warning message explaining what to do next.
           81  +*/
           82  +static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){
           83  +  fossil_warning("The remote server requested a client certificate for "
           84  +    "authentication. Specify the pathname to a file containing the PEM "
           85  +    "encoded certificate and private key with the --ssl-identity option "
           86  +    "or the ssl-identity setting.");
           87  +  return 0; /* no cert available */    
           88  +}
           89  +
           90  +/*
           91  +** Call this routine once before any other use of the SSL interface.
           92  +** This routine does initial configuration of the SSL module.
           93  +*/
           94  +void ssl_global_init(void){
           95  +  const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0;
           96  +  const char *identityFile;
           97  +  
           98  +  if( sslIsInit==0 ){
           99  +    SSL_library_init();
          100  +    SSL_load_error_strings();
          101  +    ERR_load_BIO_strings();
          102  +    OpenSSL_add_all_algorithms();    
          103  +    sslCtx = SSL_CTX_new(SSLv23_client_method());
          104  +    /* Disable SSLv2 */
          105  +    SSL_CTX_set_options(sslCtx, SSL_OP_NO_SSLv2);
          106  +    
          107  +    /* Set up acceptable CA root certificates */
          108  +    zCaSetting = db_get("ssl-ca-location", 0);
          109  +    if( zCaSetting==0 || zCaSetting[0]=='\0' ){
          110  +      /* CA location not specified, use platform's default certificate store */
          111  +      X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx));
          112  +    }else{
          113  +      /* User has specified a CA location, make sure it exists and use it */
          114  +      switch( file_isdir(zCaSetting) ){
          115  +        case 0: { /* doesn't exist */
          116  +          fossil_fatal("ssl-ca-location is set to '%s', "
          117  +              "but is not a file or directory", zCaSetting);
          118  +          break;
          119  +        }
          120  +        case 1: { /* directory */
          121  +          zCaDirectory = zCaSetting;
          122  +          break;
          123  +        }
          124  +        case 2: { /* file */
          125  +          zCaFile = zCaSetting;
          126  +          break;
          127  +        }
          128  +      }
          129  +      if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
          130  +        fossil_fatal("Failed to use CA root certificates from "
          131  +          "ssl-ca-location '%s'", zCaSetting);
          132  +      }
          133  +    }
          134  +    
          135  +    /* Load client SSL identity, preferring the filename specified on the
          136  +    ** command line */
          137  +    if( g.zSSLIdentity!=0 ){
          138  +      identityFile = g.zSSLIdentity;
          139  +    }else{
          140  +      identityFile = db_get("ssl-identity", 0);
          141  +    }
          142  +    if( identityFile!=0 && identityFile[0]!='\0' ){
          143  +      if( SSL_CTX_use_certificate_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
          144  +       || SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
          145  +      ){
          146  +        fossil_fatal("Could not load SSL identity from %s", identityFile);
          147  +      }
          148  +    }
          149  +    /* Register a callback to tell the user what to do when the server asks
          150  +    ** for a cert */
          151  +    SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback);
          152  +
          153  +    sslIsInit = 1;
          154  +  }
          155  +}
          156  +
          157  +/*
          158  +** Call this routine to shutdown the SSL module prior to program exit.
          159  +*/
          160  +void ssl_global_shutdown(void){
          161  +  if( sslIsInit ){
          162  +    SSL_CTX_free(sslCtx);
          163  +    ssl_clear_errmsg();
          164  +    sslIsInit = 0;
          165  +  }
          166  +}
          167  +
          168  +/*
          169  +** Close the currently open SSL connection.  If no connection is open, 
          170  +** this routine is a no-op.
          171  +*/
          172  +void ssl_close(void){
          173  +  if( iBio!=NULL ){
          174  +    (void)BIO_reset(iBio);
          175  +    BIO_free_all(iBio);
          176  +  }
          177  +}
          178  +
          179  +/*
          180  +** Open an SSL connection.  The identify of the server is determined
          181  +** by global variables that are set using url_parse():
          182  +**
          183  +**    g.urlName       Name of the server.  Ex: www.fossil-scm.org
          184  +**    g.urlPort       TCP/IP port to use.  Ex: 80
          185  +**
          186  +** Return the number of errors.
          187  +*/
          188  +int ssl_open(void){
          189  +  X509 *cert;
          190  +  int hasSavedCertificate = 0;
          191  +  int trusted = 0;
          192  +  unsigned long e;
          193  +
          194  +  ssl_global_init();
          195  +
          196  +  /* Get certificate for current server from global config and
          197  +   * (if we have it in config) add it to certificate store.
          198  +   */
          199  +  cert = ssl_get_certificate(&trusted);
          200  +  if ( cert!=NULL ){
          201  +    X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
          202  +    X509_free(cert);
          203  +    hasSavedCertificate = 1;
          204  +  }
          205  +
          206  +  iBio = BIO_new_ssl_connect(sslCtx);
          207  +  BIO_get_ssl(iBio, &ssl);
          208  +
          209  +#if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
          210  +  if( !SSL_set_tlsext_host_name(ssl, g.urlName) ){
          211  +    fossil_warning("WARNING: failed to set server name indication (SNI), "
          212  +                  "continuing without it.\n");
          213  +  }
          214  +#endif
          215  +
          216  +  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
          217  +  if( iBio==NULL ) {
          218  +    ssl_set_errmsg("SSL: cannot open SSL (%s)", 
          219  +                    ERR_reason_error_string(ERR_get_error()));
          220  +    return 1;
          221  +  }
          222  +
          223  +  BIO_set_conn_hostname(iBio, g.urlName);
          224  +  BIO_set_conn_int_port(iBio, &g.urlPort);
          225  +  
          226  +  if( BIO_do_connect(iBio)<=0 ){
          227  +    ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", 
          228  +        g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error()));
          229  +    ssl_close();
          230  +    return 1;
          231  +  }
          232  +  
          233  +  if( BIO_do_handshake(iBio)<=0 ) {
          234  +    ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", 
          235  +        g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error()));
          236  +    ssl_close();
          237  +    return 1;
          238  +  }
          239  +  /* Check if certificate is valid */
          240  +  cert = SSL_get_peer_certificate(ssl);
          241  +
          242  +  if ( cert==NULL ){
          243  +    ssl_set_errmsg("No SSL certificate was presented by the peer");
          244  +    ssl_close();
          245  +    return 1;
          246  +  }
          247  +
          248  +  if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){
          249  +    char *desc, *prompt;
          250  +    char *warning = "";
          251  +    Blob ans;
          252  +    char cReply;
          253  +    BIO *mem;
          254  +    unsigned char md[32];
          255  +    unsigned int mdLength = 31;
          256  +    
          257  +    mem = BIO_new(BIO_s_mem());
          258  +    X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
          259  +    BIO_puts(mem, "\n\nIssued By:\n\n");
          260  +    X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
          261  +    BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n ");
          262  +    if(X509_digest(cert, EVP_sha1(), md, &mdLength)){
          263  +      int j;
          264  +      for( j = 0; j < mdLength; ++j ) {
          265  +        BIO_printf(mem, " %02x", md[j]);
          266  +      }
          267  +    }
          268  +    BIO_write(mem, "", 1); /* nul-terminate mem buffer */
          269  +    BIO_get_mem_data(mem, &desc);
          270  +    
          271  +    if( hasSavedCertificate ){
          272  +      warning = "WARNING: Certificate doesn't match the "
          273  +                "saved certificate for this host!";
          274  +    }
          275  +    prompt = mprintf("\nSSL verification failed: %s\n"
          276  +        "Certificate received: \n\n%s\n\n%s\n"
          277  +        "Either:\n"
          278  +        " * verify the certificate is correct using the "
          279  +        "SHA1 fingerprint above\n"
          280  +        " * use the global ssl-ca-location setting to specify your CA root\n"
          281  +        "   certificates list\n\n"
          282  +        "If you are not expecting this message, answer no and "
          283  +        "contact your server\nadministrator.\n\n"
          284  +        "Accept certificate for host %s (a=always/y/N)? ",
          285  +        X509_verify_cert_error_string(e), desc, warning,
          286  +        g.urlName);
          287  +    BIO_free(mem);
          288  +
          289  +    prompt_user(prompt, &ans);
          290  +    free(prompt);
          291  +    cReply = blob_str(&ans)[0];
          292  +    blob_reset(&ans);
          293  +    if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') {
          294  +      X509_free(cert);
          295  +      ssl_set_errmsg("SSL certificate declined");
          296  +      ssl_close();
          297  +      return 1;
          298  +    }
          299  +    if( cReply=='a' || cReply=='A') {
          300  +      if ( trusted==0 ){
          301  +        prompt_user("\nSave this certificate as fully trusted (a=always/N)? ",
          302  +                    &ans);
          303  +        cReply = blob_str(&ans)[0];
          304  +        trusted = ( cReply=='a' || cReply=='A' );
          305  +        blob_reset(&ans);
          306  +      }
          307  +      ssl_save_certificate(cert, trusted);
          308  +    }
          309  +  }
          310  +
          311  +  /* Set the Global.zIpAddr variable to the server we are talking to.
          312  +  ** This is used to populate the ipaddr column of the rcvfrom table,
          313  +  ** if any files are received from the server.
          314  +  */
          315  +  {
          316  +    /* IPv4 only code */
          317  +    const unsigned char *ip = (const unsigned char *) BIO_get_conn_ip(iBio);
          318  +    g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
          319  +  }
          320  +
          321  +  X509_free(cert);
          322  +  return 0;
          323  +}
          324  +
          325  +/*
          326  +** Save certificate to global config.
          327  +*/
          328  +void ssl_save_certificate(X509 *cert, int trusted){
          329  +  BIO *mem;
          330  +  char *zCert, *zHost;
          331  +
          332  +  mem = BIO_new(BIO_s_mem());
          333  +  PEM_write_bio_X509(mem, cert);
          334  +  BIO_write(mem, "", 1); /* nul-terminate mem buffer */
          335  +  BIO_get_mem_data(mem, &zCert);
          336  +  zHost = mprintf("cert:%s", g.urlName);
          337  +  db_set(zHost, zCert, 1);
          338  +  free(zHost);
          339  +  zHost = mprintf("trusted:%s", g.urlName);
          340  +  db_set_int(zHost, trusted, 1);
          341  +  free(zHost);
          342  +  BIO_free(mem);  
          343  +}
          344  +
          345  +/*
          346  +** Get certificate for g.urlName from global config.
          347  +** Return NULL if no certificate found.
          348  +*/
          349  +X509 *ssl_get_certificate(int *pTrusted){
          350  +  char *zHost, *zCert;
          351  +  BIO *mem;
          352  +  X509 *cert;
          353  +
          354  +  zHost = mprintf("cert:%s", g.urlName);
          355  +  zCert = db_get(zHost, NULL);
          356  +  free(zHost);
          357  +  if ( zCert==NULL )
          358  +    return NULL;
          359  +
          360  +  if ( pTrusted!=0 ){
          361  +    zHost = mprintf("trusted:%s", g.urlName);
          362  +    *pTrusted = db_get_int(zHost, 0);
          363  +    free(zHost);
          364  +  }
          365  +
          366  +  mem = BIO_new(BIO_s_mem());
          367  +  BIO_puts(mem, zCert);
          368  +  cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
          369  +  free(zCert);
          370  +  BIO_free(mem);  
          371  +  return cert;
          372  +}
          373  +
          374  +/*
          375  +** Send content out over the SSL connection.
          376  +*/
          377  +size_t ssl_send(void *NotUsed, void *pContent, size_t N){
          378  +  size_t sent;
          379  +  size_t total = 0;
          380  +  while( N>0 ){
          381  +    sent = BIO_write(iBio, pContent, N);
          382  +    if( sent<=0 ) break;
          383  +    total += sent;
          384  +    N -= sent;
          385  +    pContent = (void*)&((char*)pContent)[sent];
          386  +  }
          387  +  return total;
          388  +}
          389  +
          390  +/*
          391  +** Receive content back from the SSL connection.
          392  +*/
          393  +size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
          394  +  size_t got;
          395  +  size_t total = 0;
          396  +  while( N>0 ){
          397  +    got = BIO_read(iBio, pContent, N);
          398  +    if( got<=0 ) break;
          399  +    total += got;
          400  +    N -= got;
          401  +    pContent = (void*)&((char*)pContent)[got];
          402  +  }
          403  +  return total;
          404  +}
          405  +
          406  +#endif /* FOSSIL_ENABLE_SSL */

Changes to src/http_transport.c.

    28     28   */
    29     29   static struct {
    30     30     int isOpen;             /* True when the transport layer is open */
    31     31     char *pBuf;             /* Buffer used to hold the reply */
    32     32     int nAlloc;             /* Space allocated for transportBuf[] */
    33     33     int nUsed ;             /* Space of transportBuf[] used */
    34     34     int iCursor;            /* Next unread by in transportBuf[] */
    35         -  int nSent;              /* Number of bytes sent */
    36         -  int nRcvd;              /* Number of bytes received */
           35  +  i64 nSent;              /* Number of bytes sent */
           36  +  i64 nRcvd;              /* Number of bytes received */
    37     37     FILE *pFile;            /* File I/O for FILE: */
    38     38     char *zOutFile;         /* Name of outbound file for FILE: */
    39     39     char *zInFile;          /* Name of inbound file for FILE: */
           40  +  FILE *pLog;             /* Log output here */
    40     41   } transport = {
    41     42     0, 0, 0, 0, 0, 0, 0
    42     43   };
           44  +
           45  +/*
           46  +** Information about the connection to the SSH subprocess when
           47  +** using the ssh:// sync method.
           48  +*/
           49  +static int sshPid;             /* Process id of ssh subprocess */
           50  +static int sshIn;              /* From ssh subprocess to this process */
           51  +static FILE *sshOut;           /* From this to ssh subprocess */
           52  +
    43     53   
    44     54   /*
    45     55   ** Return the current transport error message.
    46     56   */
    47     57   const char *transport_errmsg(void){
    48     58     #ifdef FOSSIL_ENABLE_SSL
    49     59     if( g.urlIsHttps ){
................................................................................
    53     63     return socket_errmsg();
    54     64   }
    55     65   
    56     66   /*
    57     67   ** Retrieve send/receive counts from the transport layer.  If "resetFlag"
    58     68   ** is true, then reset the counts.
    59     69   */
    60         -void transport_stats(int *pnSent, int *pnRcvd, int resetFlag){
           70  +void transport_stats(i64 *pnSent, i64 *pnRcvd, int resetFlag){
    61     71     if( pnSent ) *pnSent = transport.nSent;
    62     72     if( pnRcvd ) *pnRcvd = transport.nRcvd;
    63     73     if( resetFlag ){
    64     74       transport.nSent = 0;
    65     75       transport.nRcvd = 0;
    66     76     }
    67     77   }
           78  +
           79  +/*
           80  +** Read text from sshIn.  Zero-terminate and remove trailing
           81  +** whitespace.
           82  +*/
           83  +static void sshin_read(char *zBuf, int szBuf){
           84  +  int got;
           85  +  zBuf[0] = 0;
           86  +  got = read(sshIn, zBuf, szBuf-1);
           87  +  while( got>=0 ){
           88  +    zBuf[got] = 0;
           89  +    if( got==0 || !fossil_isspace(zBuf[got-1]) ) break;
           90  +    got--;
           91  +  }
           92  +}
           93  +
           94  +/*
           95  +** Default SSH command
           96  +*/
           97  +#ifdef __MINGW32__
           98  +static char zDefaultSshCmd[] = "ssh -T";
           99  +#else
          100  +static char zDefaultSshCmd[] = "ssh -e none -T";
          101  +#endif
          102  +
          103  +/*
          104  +** Generate a random SSH link problem keyword
          105  +*/
          106  +static int random_probe(char *zProbe, int nProbe){
          107  +  unsigned r[4];
          108  +  sqlite3_randomness(sizeof(r), r);
          109  +  sqlite3_snprintf(nProbe, zProbe, "probe-%08x%08x%08x%08x",
          110  +                   r[0], r[1], r[2], r[3]);
          111  +  return (int)strlen(zProbe);
          112  +}
          113  +
          114  +/*
          115  +** Bring up an SSH link.  This involves sending some "echo" commands and
          116  +** get back appropriate responses.  The point is to move past the MOTD and
          117  +** verify that the link is working.
          118  +*/
          119  +static void transport_ssh_startup(void){
          120  +  char *zIn;                       /* An input line received back from remote */
          121  +  int nWait;                       /* Number of times waiting for the MOTD */
          122  +  char zProbe[40];                 /* Text of the random probe */
          123  +  int nProbe;                      /* Size of probe message */
          124  +  int nIn;                         /* Size of input */
          125  +  static const int nBuf = 10000;   /* Size of input buffer */
          126  +
          127  +  zIn = fossil_malloc(nBuf);
          128  +  nProbe = random_probe(zProbe, sizeof(zProbe));
          129  +  fprintf(sshOut, "echo %s\n", zProbe);
          130  +  fflush(sshOut);
          131  +  if( g.fSshTrace ){
          132  +    printf("Sent: [echo %s]\n", zProbe);
          133  +    fflush(stdout);
          134  +  }
          135  +  memset(zIn, '*', nProbe);
          136  +  for(nWait=1; nWait<=10; nWait++){
          137  +    sshin_read(zIn+nProbe, nBuf-nProbe);
          138  +    if( g.fSshTrace ){
          139  +      printf("Got back-----------------------------------------------\n"
          140  +             "%s\n"
          141  +             "-------------------------------------------------------\n",
          142  +             zIn+nProbe);
          143  +    }
          144  +    if( strstr(zIn, zProbe) ) break;
          145  +    sqlite3_sleep(100*nWait);
          146  +    nIn = (int)strlen(zIn);
          147  +    memcpy(zIn, zIn+(nIn-nProbe), nProbe);
          148  +    if( g.fSshTrace ){
          149  +      printf("Fetching more text.  Looking for [%s]...\n", zProbe);
          150  +      fflush(stdout);
          151  +    }
          152  +  }
          153  +  nProbe = random_probe(zProbe, sizeof(zProbe));
          154  +  fprintf(sshOut, "echo %s\n", zProbe);
          155  +  fflush(sshOut);
          156  +  if( g.fSshTrace ){
          157  +    printf("Sent: [echo %s]\n", zProbe);
          158  +    fflush(stdout);
          159  +  }
          160  +  sshin_read(zIn, nBuf);
          161  +  if( zIn[0]==0 ){
          162  +    sqlite3_sleep(250);
          163  +    sshin_read(zIn, nBuf);
          164  +  }
          165  +  if( g.fSshTrace ){
          166  +    printf("Got back-----------------------------------------------\n"
          167  +           "%s\n"
          168  +           "-------------------------------------------------------\n", zIn);
          169  +  }
          170  +  if( memcmp(zIn, zProbe, nProbe)!=0 ){
          171  +    pclose2(sshIn, sshOut, sshPid);
          172  +    fossil_fatal("ssh connection failed: [%s]", zIn);
          173  +  }
          174  +  fossil_free(zIn);
          175  +}
          176  +
          177  +/*
          178  +** Global initialization of the transport layer
          179  +*/
          180  +void transport_global_startup(void){
          181  +  if( g.urlIsSsh ){
          182  +    /* Only SSH requires a global initialization.  For SSH we need to create
          183  +    ** and run an SSH command to talk to the remote machine.
          184  +    */
          185  +    const char *zSsh;  /* The base SSH command */
          186  +    Blob zCmd;         /* The SSH command */
          187  +    char *zHost;       /* The host name to contact */
          188  +    int n;             /* Size of prefix string */
          189  +
          190  +    zSsh = db_get("ssh-command", zDefaultSshCmd);
          191  +    blob_init(&zCmd, zSsh, -1);
          192  +    if( g.urlPort!=g.urlDfltPort ){
          193  +#ifdef __MINGW32__
          194  +      blob_appendf(&zCmd, " -P %d", g.urlPort);
          195  +#else
          196  +      blob_appendf(&zCmd, " -p %d", g.urlPort);
          197  +#endif
          198  +    }
          199  +    fossil_force_newline();
          200  +    fossil_print("%s", blob_str(&zCmd));  /* Show the base of the SSH command */
          201  +    if( g.urlUser && g.urlUser[0] ){
          202  +      zHost = mprintf("%s@%s", g.urlUser, g.urlName);
          203  +#ifdef __MINGW32__
          204  +      /* Only win32 (and specifically PLINK.EXE) support the -pw option */
          205  +      if( g.urlPasswd && g.urlPasswd[0] ){
          206  +        Blob pw;
          207  +        blob_zero(&pw);
          208  +        if( g.urlPasswd[0]=='*' ){
          209  +          char *zPrompt;
          210  +          zPrompt = mprintf("Password for [%s]: ", zHost);
          211  +          prompt_for_password(zPrompt, &pw, 0);
          212  +          free(zPrompt);
          213  +        }else{
          214  +          blob_init(&pw, g.urlPasswd, -1);
          215  +        }
          216  +        blob_append(&zCmd, " -pw ", -1);
          217  +        shell_escape(&zCmd, blob_str(&pw));
          218  +        blob_reset(&pw);
          219  +        fossil_print(" -pw ********");  /* Do not show the password text */
          220  +      }
          221  +#endif
          222  +    }else{
          223  +      zHost = mprintf("%s", g.urlName);
          224  +    }
          225  +    n = blob_size(&zCmd);
          226  +    blob_append(&zCmd, " ", 1);
          227  +    shell_escape(&zCmd, zHost);
          228  +    if( g.urlShell ){
          229  +      blob_appendf(&zCmd, " %s", g.urlShell);
          230  +    }else{
          231  +#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
          232  +      /* The following works.  But only if the fossil on the remote side
          233  +      ** is recent enough to support the test-ssh-far-side command.  That
          234  +      ** command was added on 2013-02-06.  We will leave this turned off
          235  +      ** until most fossil servers have upgraded to that version or a later
          236  +      ** version.  The sync will still work as long as the shell on the far
          237  +      ** side is bash and not tcsh.  And if the default far side shell is
          238  +      ** tcsh, then the shell=/bin/bash query parameter can be used as a
          239  +      ** work-around.  Enable this code after about a year...
          240  +      */
          241  +      blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
          242  +#endif
          243  +    }
          244  +    fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
          245  +    free(zHost);
          246  +    popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
          247  +    if( sshPid==0 ){
          248  +      fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
          249  +    }
          250  +    blob_reset(&zCmd);
          251  +    transport_ssh_startup();
          252  +  }
          253  +}
          254  +
          255  +/*
          256  +** COMMAND: test-ssh-far-side
          257  +**
          258  +** Read lines of input text, one by one, and evaluate each line using
          259  +** system().  The ssh: sync protocol uses this on the far side of the
          260  +** SSH link.
          261  +*/
          262  +void test_ssh_far_side_cmd(void){
          263  +  int i = 0;
          264  +  int got;
          265  +  char zLine[5000];
          266  +  while( i<sizeof(zLine) ){
          267  +    got = read(0, zLine+i, 1);
          268  +    if( got==0 ) return;
          269  +    if( zLine[i]=='\n' ){
          270  +      zLine[i] = 0;
          271  +      system(zLine);
          272  +      i = 0;
          273  +    }else{
          274  +      i++;
          275  +    }
          276  +  }
          277  +}
    68    278   
    69    279   /*
    70    280   ** Open a connection to the server.  The server is defined by the following
    71    281   ** global variables:
    72    282   **
    73    283   **   g.urlName        Name of the server.  Ex: www.fossil-scm.org
    74    284   **   g.urlPort        TCP/IP port.  Ex: 80
................................................................................
    75    285   **   g.urlIsHttps     Use TLS for the connection
    76    286   **
    77    287   ** Return the number of errors.
    78    288   */
    79    289   int transport_open(void){
    80    290     int rc = 0;
    81    291     if( transport.isOpen==0 ){
    82         -    if( g.urlIsHttps ){
          292  +    if( g.urlIsSsh ){
          293  +      Blob cmd;
          294  +      blob_zero(&cmd);
          295  +      shell_escape(&cmd, g.urlFossil);
          296  +      blob_append(&cmd, " test-http ", -1);
          297  +      shell_escape(&cmd, g.urlPath);
          298  +      fprintf(sshOut, "%s || true\n", blob_str(&cmd));
          299  +      fflush(sshOut);
          300  +      if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd));
          301  +      blob_reset(&cmd);
          302  +    }else if( g.urlIsHttps ){
    83    303         #ifdef FOSSIL_ENABLE_SSL
    84    304         rc = ssl_open();
    85    305         if( rc==0 ) transport.isOpen = 1;
    86    306         #else
    87    307         socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
    88    308         rc = 1;
    89    309         #endif
................................................................................
    90    310       }else if( g.urlIsFile ){
    91    311         sqlite3_uint64 iRandId;
    92    312         sqlite3_randomness(sizeof(iRandId), &iRandId);
    93    313         transport.zOutFile = mprintf("%s-%llu-out.http", 
    94    314                                          g.zRepositoryName, iRandId);
    95    315         transport.zInFile = mprintf("%s-%llu-in.http", 
    96    316                                          g.zRepositoryName, iRandId);
    97         -      transport.pFile = fopen(transport.zOutFile, "wb");
          317  +      transport.pFile = fossil_fopen(transport.zOutFile, "wb");
    98    318         if( transport.pFile==0 ){
    99    319           fossil_fatal("cannot output temporary file: %s", transport.zOutFile);
   100    320         }
   101    321         transport.isOpen = 1;
   102    322       }else{
   103    323         rc = socket_open();
   104    324         if( rc==0 ) transport.isOpen = 1;
................................................................................
   113    333   void transport_close(void){
   114    334     if( transport.isOpen ){
   115    335       free(transport.pBuf);
   116    336       transport.pBuf = 0;
   117    337       transport.nAlloc = 0;
   118    338       transport.nUsed = 0;
   119    339       transport.iCursor = 0;
   120         -    if( g.urlIsHttps ){
          340  +    if( transport.pLog ){
          341  +      fclose(transport.pLog);
          342  +      transport.pLog = 0;
          343  +    }
          344  +    if( g.urlIsSsh ){
          345  +      /* No-op */
          346  +    }else if( g.urlIsHttps ){
   121    347         #ifdef FOSSIL_ENABLE_SSL
   122    348         ssl_close();
   123    349         #endif
   124    350       }else if( g.urlIsFile ){
   125    351         if( transport.pFile ){ 
   126    352           fclose(transport.pFile);
   127    353           transport.pFile = 0;
   128    354         }
   129         -      unlink(transport.zInFile);
   130         -      unlink(transport.zOutFile);
          355  +      file_delete(transport.zInFile);
          356  +      file_delete(transport.zOutFile);
   131    357         free(transport.zInFile);
   132    358         free(transport.zOutFile);
   133    359       }else{
   134    360         socket_close();
   135    361       }
   136    362       transport.isOpen = 0;
   137    363     }
................................................................................
   140    366   /*
   141    367   ** Send content over the wire.
   142    368   */
   143    369   void transport_send(Blob *toSend){
   144    370     char *z = blob_buffer(toSend);
   145    371     int n = blob_size(toSend);
   146    372     transport.nSent += n;
   147         -  if( g.urlIsHttps ){
          373  +  if( g.urlIsSsh ){
          374  +    fwrite(z, 1, n, sshOut);
          375  +    fflush(sshOut);
          376  +  }else if( g.urlIsHttps ){
   148    377       #ifdef FOSSIL_ENABLE_SSL
   149    378       int sent;
   150    379       while( n>0 ){
   151    380         sent = ssl_send(0, z, n);
   152    381         /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
   153    382         if( sent<=0 ) break;
   154    383         n -= sent;
................................................................................
   165    394         n -= sent;
   166    395       }
   167    396     }
   168    397   }
   169    398   
   170    399   /*
   171    400   ** This routine is called when the outbound message is complete and
   172         -** it is time to being recieving a reply.
          401  +** it is time to being receiving a reply.
   173    402   */
   174    403   void transport_flip(void){
   175         -  if( g.urlIsFile ){
          404  +  if( g.urlIsSsh ){
          405  +    fprintf(sshOut, "\n\n");
          406  +  }else if( g.urlIsFile ){
   176    407       char *zCmd;
   177    408       fclose(transport.pFile);
   178         -    zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
   179         -       g.argv[0], g.urlName, transport.zOutFile, transport.zInFile
          409  +    zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
          410  +       g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile
   180    411       );
   181         -    portable_system(zCmd);
          412  +    fossil_system(zCmd);
   182    413       free(zCmd);
   183         -    transport.pFile = fopen(transport.zInFile, "rb");
          414  +    transport.pFile = fossil_fopen(transport.zInFile, "rb");
   184    415     }
   185    416   }
          417  +
          418  +/*
          419  +** Log all input to a file.  The transport layer will take responsibility
          420  +** for closing the log file when it is done.
          421  +*/
          422  +void transport_log(FILE *pLog){
          423  +  if( transport.pLog ){
          424  +    fclose(transport.pLog);
          425  +    transport.pLog = 0;
          426  +  }
          427  +  transport.pLog = pLog;
          428  +}
   186    429   
   187    430   /*
   188    431   ** This routine is called when the inbound message has been received
   189    432   ** and it is time to start sending again.
   190    433   */
   191    434   void transport_rewind(void){
   192    435     if( g.urlIsFile ){
   193    436       transport_close();
   194    437     }
   195    438   }
          439  +
          440  +/*
          441  +** Read N bytes of content directly from the wire and write into
          442  +** the buffer.
          443  +*/
          444  +static int transport_fetch(char *zBuf, int N){
          445  +  int got;
          446  +  if( sshIn ){
          447  +    int x;
          448  +    int wanted = N;
          449  +    got = 0;
          450  +    while( wanted>0 ){
          451  +      x = read(sshIn, &zBuf[got], wanted);
          452  +      if( x<=0 ) break;
          453  +      got += x;
          454  +      wanted -= x;
          455  +    }
          456  +  }else if( g.urlIsHttps ){
          457  +    #ifdef FOSSIL_ENABLE_SSL
          458  +    got = ssl_receive(0, zBuf, N);
          459  +    #else
          460  +    got = 0;
          461  +    #endif
          462  +  }else if( g.urlIsFile ){
          463  +    got = fread(zBuf, 1, N, transport.pFile);
          464  +  }else{
          465  +    got = socket_receive(0, zBuf, N);
          466  +  }
          467  +  /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
          468  +  if( transport.pLog ){
          469  +    fwrite(zBuf, 1, got, transport.pLog);
          470  +    fflush(transport.pLog);
          471  +  }
          472  +  return got;
          473  +}
   196    474   
   197    475   /*
   198    476   ** Read N bytes of content from the wire and store in the supplied buffer.
   199    477   ** Return the number of bytes actually received.
   200    478   */
   201    479   int transport_receive(char *zBuf, int N){
   202    480     int onHand;       /* Bytes current held in the transport buffer */
   203    481     int nByte = 0;    /* Bytes of content received */
   204    482   
   205    483     onHand = transport.nUsed - transport.iCursor;
          484  +  if( g.fSshTrace){
          485  +    printf("Reading %d bytes with %d on hand...  ", N, onHand);
          486  +    fflush(stdout);
          487  +  }
   206    488     if( onHand>0 ){
   207    489       int toMove = onHand;
   208    490       if( toMove>N ) toMove = N;
   209    491       /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
   210    492       memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
   211    493       transport.iCursor += toMove;
   212    494       if( transport.iCursor>=transport.nUsed ){
................................................................................
   214    496         transport.iCursor = 0;
   215    497       }
   216    498       N -= toMove;
   217    499       zBuf += toMove;
   218    500       nByte += toMove;
   219    501     }
   220    502     if( N>0 ){
   221         -    int got;
   222         -    if( g.urlIsHttps ){
   223         -      #ifdef FOSSIL_ENABLE_SSL
   224         -      got = ssl_receive(0, zBuf, N);
   225         -      /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
   226         -      #else
   227         -      got = 0;
   228         -      #endif
   229         -    }else if( g.urlIsFile ){
   230         -      got = fread(zBuf, 1, N, transport.pFile);
   231         -    }else{
   232         -      got = socket_receive(0, zBuf, N);
   233         -      /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
   234         -    }
          503  +    int got = transport_fetch(zBuf, N);
   235    504       if( got>0 ){
   236    505         nByte += got;
   237    506         transport.nRcvd += got;
   238    507       }
   239    508     }
          509  +  if( g.fSshTrace ) printf("Got %d bytes\n", nByte);
   240    510     return nByte;
   241    511   }
   242    512   
   243    513   /*
   244    514   ** Load up to N new bytes of content into the transport.pBuf buffer.
   245    515   ** The buffer itself might be moved.  And the transport.iCursor value
   246    516   ** might be reset to 0.
   247    517   */
   248    518   static void transport_load_buffer(int N){
   249    519     int i, j;
   250    520     if( transport.nAlloc==0 ){
   251    521       transport.nAlloc = N;
   252         -    transport.pBuf = malloc( N );
   253         -    if( transport.pBuf==0 ) fossil_panic("out of memory");
          522  +    transport.pBuf = fossil_malloc( N );
   254    523       transport.iCursor = 0;
   255    524       transport.nUsed = 0;
   256    525     }
   257    526     if( transport.iCursor>0 ){
   258    527       for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){
   259    528         transport.pBuf[i] = transport.pBuf[j];
   260    529       }
   261    530       transport.nUsed -= transport.iCursor;
   262    531       transport.iCursor = 0;
   263    532     }
   264    533     if( transport.nUsed + N > transport.nAlloc ){
   265    534       char *pNew;
   266    535       transport.nAlloc = transport.nUsed + N;
   267         -    pNew = realloc(transport.pBuf, transport.nAlloc);
   268         -    if( pNew==0 ) fossil_panic("out of memory");
          536  +    pNew = fossil_realloc(transport.pBuf, transport.nAlloc);
   269    537       transport.pBuf = pNew;
   270    538     }
   271    539     if( N>0 ){
   272         -    i = transport_receive(&transport.pBuf[transport.nUsed], N);
          540  +    i = transport_fetch(&transport.pBuf[transport.nUsed], N);
   273    541       if( i>0 ){
          542  +      transport.nRcvd += i;
   274    543         transport.nUsed += i;
   275    544       }
   276    545     }
   277    546   }
   278    547   
   279    548   /*
   280    549   ** Fetch a single line of input where a line is all text up to the next
................................................................................
   287    556   char *transport_receive_line(void){
   288    557     int i;
   289    558     int iStart;
   290    559   
   291    560     i = iStart = transport.iCursor;
   292    561     while(1){
   293    562       if( i >= transport.nUsed ){
   294         -      transport_load_buffer(1000);
          563  +      transport_load_buffer(g.urlIsSsh ? 2 : 1000);
   295    564         i -= iStart;
   296    565         iStart = 0;
   297    566         if( i >= transport.nUsed ){
   298    567           transport.pBuf[i] = 0;
   299    568           transport.iCursor = i;
   300    569           break;
   301    570         }
   302    571       }
   303    572       if( transport.pBuf[i]=='\n' ){
   304    573         transport.iCursor = i+1;
   305         -      while( i>=iStart && isspace(transport.pBuf[i]) ){
          574  +      while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){
   306    575           transport.pBuf[i] = 0;
   307    576           i--;
   308    577         }
   309    578         break;
   310    579       }
   311    580       i++;
   312    581     }
   313         -  /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
          582  +  if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
   314    583     return &transport.pBuf[iStart];
   315    584   }
   316    585   
   317    586   void transport_global_shutdown(void){
          587  +  if( g.urlIsSsh && sshPid ){
          588  +    /*printf("Closing SSH tunnel: ");*/
          589  +    fflush(stdout);
          590  +    pclose2(sshIn, sshOut, sshPid);
          591  +    sshPid = 0;
          592  +  }
   318    593     if( g.urlIsHttps ){
   319    594       #ifdef FOSSIL_ENABLE_SSL
   320    595       ssl_global_shutdown();
   321    596       #endif
   322    597     }else{
   323    598       socket_global_shutdown();
   324    599     }
   325    600   }

Added src/import.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@sqlite.org
           14  +**
           15  +*******************************************************************************
           16  +**
           17  +** This file contains code used to import the content of a Git
           18  +** repository in the git-fast-import format as a new Fossil
           19  +** repository.
           20  +*/
           21  +#include "config.h"
           22  +#include "import.h"
           23  +#include <assert.h>
           24  +
           25  +#if INTERFACE
           26  +/*
           27  +** A single file change record.
           28  +*/
           29  +struct ImportFile {
           30  +  char *zName;           /* Name of a file */
           31  +  char *zUuid;           /* UUID of the file */
           32  +  char *zPrior;          /* Prior name if the name was changed */
           33  +  char isFrom;           /* True if obtained from the parent */
           34  +  char isExe;            /* True if executable */
           35  +  char isLink;           /* True if symlink */
           36  +};
           37  +#endif
           38  +
           39  +
           40  +/*
           41  +** State information about an on-going fast-import parse.
           42  +*/
           43  +static struct {
           44  +  void (*xFinish)(void);      /* Function to finish a prior record */
           45  +  int nData;                  /* Bytes of data */
           46  +  char *zTag;                 /* Name of a tag */
           47  +  char *zBranch;              /* Name of a branch for a commit */
           48  +  char *zPrevBranch;          /* The branch of the previous check-in */
           49  +  char *aData;                /* Data content */
           50  +  char *zMark;                /* The current mark */
           51  +  char *zDate;                /* Date/time stamp */
           52  +  char *zUser;                /* User name */
           53  +  char *zComment;             /* Comment of a commit */
           54  +  char *zFrom;                /* from value as a UUID */
           55  +  char *zPrevCheckin;         /* Name of the previous check-in */
           56  +  char *zFromMark;            /* The mark of the "from" field */
           57  +  int nMerge;                 /* Number of merge values */
           58  +  int nMergeAlloc;            /* Number of slots in azMerge[] */
           59  +  char **azMerge;             /* Merge values */
           60  +  int nFile;                  /* Number of aFile values */
           61  +  int nFileAlloc;             /* Number of slots in aFile[] */
           62  +  ImportFile *aFile;          /* Information about files in a commit */
           63  +  int fromLoaded;             /* True zFrom content loaded into aFile[] */
           64  +  int hasLinks;               /* True if git repository contains symlinks */
           65  +  int tagCommit;              /* True if the commit adds a tag */
           66  +} gg;
           67  +
           68  +/*
           69  +** Duplicate a string.
           70  +*/
           71  +char *fossil_strdup(const char *zOrig){
           72  +  char *z = 0;
           73  +  if( zOrig ){
           74  +    int n = strlen(zOrig);
           75  +    z = fossil_malloc( n+1 );
           76  +    memcpy(z, zOrig, n+1);
           77  +  }
           78  +  return z;
           79  +}
           80  +
           81  +/*
           82  +** A no-op "xFinish" method
           83  +*/
           84  +static void finish_noop(void){}
           85  +
           86  +/*
           87  +** Deallocate the state information.
           88  +**
           89  +** The azMerge[] and aFile[] arrays are zeroed by allocated space is
           90  +** retained unless the freeAll flag is set.
           91  +*/
           92  +static void import_reset(int freeAll){
           93  +  int i;
           94  +  gg.xFinish = 0;
           95  +  fossil_free(gg.zTag); gg.zTag = 0;
           96  +  fossil_free(gg.zBranch); gg.zBranch = 0;
           97  +  fossil_free(gg.aData); gg.aData = 0;
           98  +  fossil_free(gg.zMark); gg.zMark = 0;
           99  +  fossil_free(gg.zDate); gg.zDate = 0;
          100  +  fossil_free(gg.zUser); gg.zUser = 0;
          101  +  fossil_free(gg.zComment); gg.zComment = 0;
          102  +  fossil_free(gg.zFrom); gg.zFrom = 0;
          103  +  fossil_free(gg.zFromMark); gg.zFromMark = 0;
          104  +  for(i=0; i<gg.nMerge; i++){
          105  +    fossil_free(gg.azMerge[i]); gg.azMerge[i] = 0;
          106  +  }
          107  +  gg.nMerge = 0;
          108  +  for(i=0; i<gg.nFile; i++){
          109  +    fossil_free(gg.aFile[i].zName);
          110  +    fossil_free(gg.aFile[i].zUuid);
          111  +    fossil_free(gg.aFile[i].zPrior);
          112  +  }
          113  +  memset(gg.aFile, 0, gg.nFile*sizeof(gg.aFile[0]));
          114  +  gg.nFile = 0;
          115  +  if( freeAll ){
          116  +    fossil_free(gg.zPrevBranch);
          117  +    fossil_free(gg.zPrevCheckin);
          118  +    fossil_free(gg.azMerge);
          119  +    fossil_free(gg.aFile);
          120  +    memset(&gg, 0, sizeof(gg));
          121  +  }
          122  +  gg.xFinish = finish_noop;
          123  +}
          124  +
          125  +/*
          126  +** Insert an artifact into the BLOB table if it isn't there already.
          127  +** If zMark is not zero, create a cross-reference from that mark back
          128  +** to the newly inserted artifact.
          129  +**
          130  +** If saveUuid is true, then pContent is a commit record.  Record its
          131  +** UUID in gg.zPrevCheckin.
          132  +*/
          133  +static int fast_insert_content(Blob *pContent, const char *zMark, int saveUuid){
          134  +  Blob hash;
          135  +  Blob cmpr;
          136  +  int rid;
          137  +
          138  +  sha1sum_blob(pContent, &hash);
          139  +  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash);
          140  +  if( rid==0 ){
          141  +    static Stmt ins;
          142  +    db_static_prepare(&ins,
          143  +        "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)"
          144  +    );
          145  +    db_bind_text(&ins, ":uuid", blob_str(&hash));
          146  +    db_bind_int(&ins, ":size", gg.nData);
          147  +    blob_compress(pContent, &cmpr);
          148  +    db_bind_blob(&ins, ":content", &cmpr);
          149  +    db_step(&ins);
          150  +    db_reset(&ins);
          151  +    blob_reset(&cmpr);
          152  +    rid = db_last_insert_rowid();
          153  +  }
          154  +  if( zMark ){
          155  +    db_multi_exec(
          156  +        "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)"
          157  +        "VALUES(%Q,%d,%B)",
          158  +        zMark, rid, &hash
          159  +    );
          160  +    db_multi_exec(
          161  +        "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)"
          162  +        "VALUES(%B,%d,%B)",
          163  +        &hash, rid, &hash
          164  +    );
          165  +  }
          166  +  if( saveUuid ){
          167  +    fossil_free(gg.zPrevCheckin);
          168  +    gg.zPrevCheckin = fossil_strdup(blob_str(&hash));
          169  +  }
          170  +  blob_reset(&hash);
          171  +  return rid;
          172  +}
          173  +
          174  +/*
          175  +** Use data accumulated in gg from a "blob" record to add a new file
          176  +** to the BLOB table.
          177  +*/
          178  +static void finish_blob(void){
          179  +  Blob content;
          180  +  blob_init(&content, gg.aData, gg.nData);
          181  +  fast_insert_content(&content, gg.zMark, 0);
          182  +  blob_reset(&content);
          183  +  import_reset(0);
          184  +}
          185  +
          186  +/*
          187  +** Use data accumulated in gg from a "tag" record to add a new
          188  +** control artifact to the BLOB table.
          189  +*/
          190  +static void finish_tag(void){
          191  +  Blob record, cksum;
          192  +  if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
          193  +    blob_zero(&record);
          194  +    blob_appendf(&record, "D %s\n", gg.zDate);
          195  +    blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom);
          196  +    blob_appendf(&record, "U %F\n", gg.zUser);
          197  +    md5sum_blob(&record, &cksum);
          198  +    blob_appendf(&record, "Z %b\n", &cksum);
          199  +    fast_insert_content(&record, 0, 0);
          200  +    blob_reset(&record);
          201  +    blob_reset(&cksum);
          202  +  }
          203  +  import_reset(0);
          204  +}
          205  +
          206  +/*
          207  +** Compare two ImportFile objects for sorting
          208  +*/
          209  +static int mfile_cmp(const void *pLeft, const void *pRight){
          210  +  const ImportFile *pA = (const ImportFile*)pLeft;
          211  +  const ImportFile *pB = (const ImportFile*)pRight;
          212  +  return fossil_strcmp(pA->zName, pB->zName);
          213  +}
          214  +
          215  +/*
          216  +** Compare two strings for sorting.
          217  +*/
          218  +static int string_cmp(const void *pLeft, const void *pRight){
          219  +  const char *zLeft = *(char const **)pLeft;
          220  +  const char *zRight = *(char const **)pRight;
          221  +  return fossil_strcmp(zLeft, zRight);
          222  +}
          223  +
          224  +/* Forward reference */
          225  +static void import_prior_files(void);
          226  +
          227  +/*
          228  +** Use data accumulated in gg from a "commit" record to add a new
          229  +** manifest artifact to the BLOB table.
          230  +*/
          231  +static void finish_commit(void){
          232  +  int i;
          233  +  char *zFromBranch;
          234  +  char *aTCard[4];                /* Array of T cards for manifest */
          235  +  int nTCard = 0;                 /* Entries used in aTCard[] */
          236  +  Blob record, cksum;
          237  +
          238  +  import_prior_files();
          239  +  qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp);
          240  +  blob_zero(&record);
          241  +  blob_appendf(&record, "C %F\n", gg.zComment);
          242  +  blob_appendf(&record, "D %s\n", gg.zDate);
          243  +  for(i=0; i<gg.nFile; i++){
          244  +    const char *zUuid = gg.aFile[i].zUuid;
          245  +    if( zUuid==0 ) continue;
          246  +    blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid);
          247  +    if( gg.aFile[i].isExe ){
          248  +      blob_append(&record, " x\n", 3);
          249  +    }else if( gg.aFile[i].isLink ){
          250  +      blob_append(&record, " l\n", 3);
          251  +      gg.hasLinks = 1;
          252  +    }else{
          253  +      blob_append(&record, "\n", 1);
          254  +    }
          255  +  }
          256  +  if( gg.zFrom ){
          257  +    blob_appendf(&record, "P %s", gg.zFrom);
          258  +    for(i=0; i<gg.nMerge; i++){
          259  +      blob_appendf(&record, " %s", gg.azMerge[i]);
          260  +    }
          261  +    blob_append(&record, "\n", 1);
          262  +    zFromBranch = db_text(0, "SELECT brnm FROM xbranch WHERE tname=%Q",
          263  +                              gg.zFromMark);
          264  +  }else{
          265  +    zFromBranch = 0;
          266  +  }
          267  +
          268  +  /* Add the required "T" cards to the manifest. Make sure they are added
          269  +  ** in sorted order and without any duplicates. Otherwise, fossil will not
          270  +  ** recognize the document as a valid manifest. */
          271  +  if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){
          272  +    aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch);
          273  +    aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch);
          274  +    if( zFromBranch ){
          275  +      aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch);
          276  +    }
          277  +  }
          278  +  if( gg.zFrom==0 ){
          279  +    aTCard[nTCard++] = mprintf("T *sym-trunk *\n");
          280  +  }
          281  +  qsort(aTCard, nTCard, sizeof(char *), string_cmp);
          282  +  for(i=0; i<nTCard; i++){
          283  +    if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
          284  +      blob_appendf(&record, "%s", aTCard[i]);
          285  +    }
          286  +  }
          287  +  for(i=0; i<nTCard; i++) free(aTCard[i]);
          288  +
          289  +  free(zFromBranch);
          290  +  db_multi_exec("INSERT INTO xbranch(tname, brnm) VALUES(%Q,%Q)",
          291  +                gg.zMark, gg.zBranch);
          292  +  blob_appendf(&record, "U %F\n", gg.zUser);
          293  +  md5sum_blob(&record, &cksum);
          294  +  blob_appendf(&record, "Z %b\n", &cksum);
          295  +  fast_insert_content(&record, gg.zMark, 1);
          296  +  blob_reset(&record);
          297  +  blob_reset(&cksum);
          298  +
          299  +  /* The "git fast-export" command might output multiple "commit" lines
          300  +  ** that reference a tag using "refs/tags/TAGNAME".  The tag should only
          301  +  ** be applied to the last commit that is output.  The problem is we do not
          302  +  ** know at this time if the current commit is the last one to hold this
          303  +  ** tag or not.  So make an entry in the XTAG table to record this tag
          304  +  ** but overwrite that entry if a later instance of the same tag appears.
          305  +  **
          306  +  ** This behavior seems like a bug in git-fast-export, but it is easier
          307  +  ** to work around the problem than to fix git-fast-export.
          308  +  */
          309  +  if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){
          310  +    blob_appendf(&record, "D %s\n", gg.zDate);
          311  +    blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin);
          312  +    blob_appendf(&record, "U %F\n", gg.zUser);
          313  +    md5sum_blob(&record, &cksum);
          314  +    blob_appendf(&record, "Z %b\n", &cksum);
          315  +    db_multi_exec(
          316  +       "INSERT OR REPLACE INTO xtag(tname, tcontent)"
          317  +       " VALUES(%Q,%Q)", gg.zBranch, blob_str(&record)
          318  +    );
          319  +    blob_reset(&record);
          320  +    blob_reset(&cksum);
          321  +  }
          322  +
          323  +  fossil_free(gg.zPrevBranch);
          324  +  gg.zPrevBranch = gg.zBranch;
          325  +  gg.zBranch = 0;
          326  +  import_reset(0);
          327  +}
          328  +
          329  +/*
          330  +** Turn the first \n in the input string into a \000
          331  +*/
          332  +static void trim_newline(char *z){
          333  +  while( z[0] && z[0]!='\n' ){ z++; }
          334  +  z[0] = 0;
          335  +}
          336  +
          337  +/*
          338  +** Get a token from a line of text.  Return a pointer to the first
          339  +** character of the token and zero-terminate the token.  Make
          340  +** *pzIn point to the first character past the end of the zero
          341  +** terminator, or at the zero-terminator at EOL.
          342  +*/
          343  +static char *next_token(char **pzIn){
          344  +  char *z = *pzIn;
          345  +  int i;
          346  +  if( z[0]==0 ) return z;
          347  +  for(i=0; z[i] && z[i]!=' ' && z[i]!='\n'; i++){}
          348  +  if( z[i] ){
          349  +    z[i] = 0;
          350  +    *pzIn = &z[i+1];
          351  +  }else{
          352  +    *pzIn = &z[i];
          353  +  }
          354  +  return z;
          355  +}
          356  +
          357  +/*
          358  +** Return a token that is all text up to (but omitting) the next \n
          359  +** or \r\n.
          360  +*/
          361  +static char *rest_of_line(char **pzIn){
          362  +  char *z = *pzIn;
          363  +  int i;
          364  +  if( z[0]==0 ) return z;
          365  +  for(i=0; z[i] && z[i]!='\r' && z[i]!='\n'; i++){}
          366  +  if( z[i] ){
          367  +    if( z[i]=='\r' && z[i+1]=='\n' ){
          368  +      z[i] = 0;
          369  +      i++;
          370  +    }else{
          371  +      z[i] = 0;
          372  +    }
          373  +    *pzIn = &z[i+1];
          374  +  }else{
          375  +    *pzIn = &z[i];
          376  +  }
          377  +  return z;
          378  +}
          379  +
          380  +/*
          381  +** Convert a "mark" or "committish" into the UUID.
          382  +*/
          383  +static char *resolve_committish(const char *zCommittish){
          384  +  char *zRes;
          385  +
          386  +  zRes = db_text(0, "SELECT tuuid FROM xmark WHERE tname=%Q", zCommittish);
          387  +  return zRes;
          388  +}
          389  +
          390  +/*
          391  +** Create a new entry in the gg.aFile[] array
          392  +*/
          393  +static ImportFile *import_add_file(void){
          394  +  ImportFile *pFile;
          395  +  if( gg.nFile>=gg.nFileAlloc ){
          396  +    gg.nFileAlloc = gg.nFileAlloc*2 + 100;
          397  +    gg.aFile = fossil_realloc(gg.aFile, gg.nFileAlloc*sizeof(gg.aFile[0]));
          398  +  }
          399  +  pFile = &gg.aFile[gg.nFile++];
          400  +  memset(pFile, 0, sizeof(*pFile));
          401  +  return pFile;
          402  +}
          403  +
          404  +
          405  +/*
          406  +** Load all file information out of the gg.zFrom check-in
          407  +*/
          408  +static void import_prior_files(void){
          409  +  Manifest *p;
          410  +  int rid;
          411  +  ManifestFile *pOld;
          412  +  ImportFile *pNew;
          413  +  if( gg.fromLoaded ) return;
          414  +  gg.fromLoaded = 1;
          415  +  if( gg.zFrom==0 && gg.zPrevCheckin!=0
          416  +   && fossil_strcmp(gg.zBranch, gg.zPrevBranch)==0
          417  +  ){
          418  +     gg.zFrom = gg.zPrevCheckin;
          419  +     gg.zPrevCheckin = 0;
          420  +  }
          421  +  if( gg.zFrom==0 ) return;
          422  +  rid = fast_uuid_to_rid(gg.zFrom);
          423  +  if( rid==0 ) return;
          424  +  p = manifest_get(rid, CFTYPE_MANIFEST);
          425  +  if( p==0 ) return;
          426  +  manifest_file_rewind(p);
          427  +  while( (pOld = manifest_file_next(p, 0))!=0 ){
          428  +    pNew = import_add_file();
          429  +    pNew->zName = fossil_strdup(pOld->zName);
          430  +    pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0;
          431  +    pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0;
          432  +    pNew->zUuid = fossil_strdup(pOld->zUuid);
          433  +    pNew->isFrom = 1;
          434  +  }
          435  +  manifest_destroy(p);
          436  +}
          437  +
          438  +/*
          439  +** Locate a file in the gg.aFile[] array by its name.  Begin the search
          440  +** with the *pI-th file.  Update *pI to be one past the file found.
          441  +** Do not search past the mx-th file.
          442  +*/
          443  +static ImportFile *import_find_file(const char *zName, int *pI, int mx){
          444  +  int i = *pI;
          445  +  int nName = strlen(zName);
          446  +  while( i<mx ){
          447  +    const char *z = gg.aFile[i].zName;
          448  +    if( memcmp(zName, z, nName)==0 && (z[nName]==0 || z[nName]=='/') ){
          449  +      *pI = i+1;
          450  +      return &gg.aFile[i];
          451  +    }
          452  +    i++;
          453  +  }
          454  +  return 0;
          455  +}
          456  +
          457  +/*
          458  +** Dequote a fast-export filename.  Filenames are normally unquoted.  But
          459  +** if the contain some obscure special characters, quotes might be added.
          460  +*/
          461  +static void dequote_git_filename(char *zName){
          462  +  int n, i, j;
          463  +  if( zName==0 || zName[0]!='"' ) return;
          464  +  n = (int)strlen(zName);
          465  +  if( zName[n-1]!='"' ) return;
          466  +  for(i=0, j=1; j<n-1; j++){
          467  +    char c = zName[j];
          468  +    if( c=='\\' ) c = zName[++j];
          469  +    zName[i++] = c;
          470  +  }
          471  +  zName[i] = 0;
          472  +}
          473  +
          474  +
          475  +/*
          476  +** Read the git-fast-import format from pIn and insert the corresponding
          477  +** content into the database.
          478  +*/
          479  +static void git_fast_import(FILE *pIn){
          480  +  ImportFile *pFile, *pNew;
          481  +  int i, mx;
          482  +  char *z;
          483  +  char *zUuid;
          484  +  char *zName;
          485  +  char *zPerm;
          486  +  char *zFrom;
          487  +  char *zTo;
          488  +  char zLine[1000];
          489  +
          490  +  gg.xFinish = finish_noop;
          491  +  while( fgets(zLine, sizeof(zLine), pIn) ){
          492  +    if( zLine[0]=='\n' || zLine[0]=='#' ) continue;
          493  +    if( memcmp(zLine, "blob", 4)==0 ){
          494  +      gg.xFinish();
          495  +      gg.xFinish = finish_blob;
          496  +    }else
          497  +    if( memcmp(zLine, "commit ", 7)==0 ){
          498  +      gg.xFinish();
          499  +      gg.xFinish = finish_commit;
          500  +      trim_newline(&zLine[7]);
          501  +      z = &zLine[7];
          502  +
          503  +      /* The argument to the "commit" line might match either of these
          504  +      ** patterns:
          505  +      **
          506  +      **   (A)  refs/heads/BRANCHNAME
          507  +      **   (B)  refs/tags/TAGNAME
          508  +      **
          509  +      ** If pattern A is used, then the branchname used is as shown.
          510  +      ** Except, the "master" branch which is the default branch name in
          511  +      ** Git is changed to "trunk" which is the default name in Fossil.
          512  +      ** If the pattern is B, then the new commit should be on the same
          513  +      ** branch as its parent.  And, we might need to add the TAGNAME
          514  +      ** tag to the new commit.  However, if there are multiple instances
          515  +      ** of pattern B with the same TAGNAME, then only put the tag on the
          516  +      ** last commit that holds that tag.
          517  +      **
          518  +      ** None of the above is explained in the git-fast-export
          519  +      ** documentation.  We had to figure it out via trial and error.
          520  +      */
          521  +      for(i=strlen(z)-1; i>=0 && z[i]!='/'; i--){}
          522  +      gg.tagCommit = memcmp(&z[i-4], "tags", 4)==0;  /* True for pattern B */
          523  +      if( z[i+1]!=0 ) z += i+1;
          524  +      if( fossil_strcmp(z, "master")==0 ) z = "trunk";
          525  +      gg.zBranch = fossil_strdup(z);
          526  +      gg.fromLoaded = 0;
          527  +    }else
          528  +    if( memcmp(zLine, "tag ", 4)==0 ){
          529  +      gg.xFinish();
          530  +      gg.xFinish = finish_tag;
          531  +      trim_newline(&zLine[4]);
          532  +      gg.zTag = fossil_strdup(&zLine[4]);
          533  +    }else
          534  +    if( memcmp(zLine, "reset ", 4)==0 ){
          535  +      gg.xFinish();
          536  +    }else
          537  +    if( memcmp(zLine, "checkpoint", 10)==0 ){
          538  +      gg.xFinish();
          539  +    }else
          540  +    if( memcmp(zLine, "feature", 7)==0 ){
          541  +      gg.xFinish();
          542  +    }else
          543  +    if( memcmp(zLine, "option", 6)==0 ){
          544  +      gg.xFinish();
          545  +    }else
          546  +    if( memcmp(zLine, "progress ", 9)==0 ){
          547  +      gg.xFinish();
          548  +      trim_newline(&zLine[9]);
          549  +      fossil_print("%s\n", &zLine[9]);
          550  +      fflush(stdout);
          551  +    }else
          552  +    if( memcmp(zLine, "data ", 5)==0 ){
          553  +      fossil_free(gg.aData); gg.aData = 0;
          554  +      gg.nData = atoi(&zLine[5]);
          555  +      if( gg.nData ){
          556  +        int got;
          557  +        gg.aData = fossil_malloc( gg.nData+1 );
          558  +        got = fread(gg.aData, 1, gg.nData, pIn);
          559  +        if( got!=gg.nData ){
          560  +          fossil_fatal("short read: got %d of %d bytes", got, gg.nData);
          561  +        }
          562  +        gg.aData[got] = 0;
          563  +        if( gg.zComment==0 && gg.xFinish==finish_commit ){
          564  +          gg.zComment = gg.aData;
          565  +          gg.aData = 0;
          566  +          gg.nData = 0;
          567  +        }
          568  +      }
          569  +    }else
          570  +    if( memcmp(zLine, "author ", 7)==0 ){
          571  +      /* No-op */
          572  +    }else
          573  +    if( memcmp(zLine, "mark ", 5)==0 ){
          574  +      trim_newline(&zLine[5]);
          575  +      fossil_free(gg.zMark);
          576  +      gg.zMark = fossil_strdup(&zLine[5]);
          577  +    }else
          578  +    if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){
          579  +      sqlite3_int64 secSince1970;
          580  +      for(i=0; zLine[i] && zLine[i]!='<'; i++){}
          581  +      if( zLine[i]==0 ) goto malformed_line;
          582  +      z = &zLine[i+1];
          583  +      for(i=i+1; zLine[i] && zLine[i]!='>'; i++){}
          584  +      if( zLine[i]==0 ) goto malformed_line;
          585  +      zLine[i] = 0;
          586  +      fossil_free(gg.zUser);
          587  +      gg.zUser = fossil_strdup(z);
          588  +      secSince1970 = 0;
          589  +      for(i=i+2; fossil_isdigit(zLine[i]); i++){
          590  +        secSince1970 = secSince1970*10 + zLine[i] - '0';
          591  +      }
          592  +      fossil_free(gg.zDate);
          593  +      gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970);
          594  +      gg.zDate[10] = 'T';
          595  +    }else
          596  +    if( memcmp(zLine, "from ", 5)==0 ){
          597  +      trim_newline(&zLine[5]);
          598  +      fossil_free(gg.zFromMark);
          599  +      gg.zFromMark = fossil_strdup(&zLine[5]);
          600  +      fossil_free(gg.zFrom);
          601  +      gg.zFrom = resolve_committish(&zLine[5]);
          602  +    }else
          603  +    if( memcmp(zLine, "merge ", 6)==0 ){
          604  +      trim_newline(&zLine[6]);
          605  +      if( gg.nMerge>=gg.nMergeAlloc ){
          606  +        gg.nMergeAlloc = gg.nMergeAlloc*2 + 10;
          607  +        gg.azMerge = fossil_realloc(gg.azMerge, gg.nMergeAlloc*sizeof(char*));
          608  +      }
          609  +      gg.azMerge[gg.nMerge] = resolve_committish(&zLine[6]);
          610  +      if( gg.azMerge[gg.nMerge] ) gg.nMerge++;
          611  +    }else
          612  +    if( memcmp(zLine, "M ", 2)==0 ){
          613  +      import_prior_files();
          614  +      z = &zLine[2];
          615  +      zPerm = next_token(&z);
          616  +      zUuid = next_token(&z);
          617  +      zName = rest_of_line(&z);
          618  +      dequote_git_filename(zName);
          619  +      i = 0;
          620  +      pFile = import_find_file(zName, &i, gg.nFile);
          621  +      if( pFile==0 ){
          622  +        pFile = import_add_file();
          623  +        pFile->zName = fossil_strdup(zName);
          624  +      }
          625  +      pFile->isExe = (fossil_strcmp(zPerm, "100755")==0);
          626  +      pFile->isLink = (fossil_strcmp(zPerm, "120000")==0);
          627  +      fossil_free(pFile->zUuid);
          628  +      pFile->zUuid = resolve_committish(zUuid);
          629  +      pFile->isFrom = 0;
          630  +    }else
          631  +    if( memcmp(zLine, "D ", 2)==0 ){
          632  +      import_prior_files();
          633  +      z = &zLine[2];
          634  +      zName = rest_of_line(&z);
          635  +      dequote_git_filename(zName);
          636  +      i = 0;
          637  +      while( (pFile = import_find_file(zName, &i, gg.nFile))!=0 ){
          638  +        if( pFile->isFrom==0 ) continue;
          639  +        fossil_free(pFile->zName);
          640  +        fossil_free(pFile->zPrior);
          641  +        fossil_free(pFile->zUuid);
          642  +        *pFile = gg.aFile[--gg.nFile];
          643  +        i--;
          644  +      }
          645  +    }else
          646  +    if( memcmp(zLine, "C ", 2)==0 ){
          647  +      int nFrom;
          648  +      import_prior_files();
          649  +      z = &zLine[2];
          650  +      zFrom = next_token(&z);
          651  +      zTo = rest_of_line(&z);
          652  +      i = 0;
          653  +      mx = gg.nFile;
          654  +      nFrom = strlen(zFrom);
          655  +      while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){
          656  +        if( pFile->isFrom==0 ) continue;
          657  +        pNew = import_add_file();
          658  +        pFile = &gg.aFile[i-1];
          659  +        if( strlen(pFile->zName)>nFrom ){
          660  +          pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
          661  +        }else{
          662  +          pNew->zName = fossil_strdup(pFile->zName);
          663  +        }
          664  +        pNew->isExe = pFile->isExe;
          665  +        pNew->isLink = pFile->isLink;
          666  +        pNew->zUuid = fossil_strdup(pFile->zUuid);
          667  +        pNew->isFrom = 0;
          668  +      }
          669  +    }else
          670  +    if( memcmp(zLine, "R ", 2)==0 ){
          671  +      int nFrom;
          672  +      import_prior_files();
          673  +      z = &zLine[2];
          674  +      zFrom = next_token(&z);
          675  +      zTo = rest_of_line(&z);
          676  +      i = 0;
          677  +      nFrom = strlen(zFrom);
          678  +      while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){
          679  +        if( pFile->isFrom==0 ) continue;
          680  +        pNew = import_add_file();
          681  +        pFile = &gg.aFile[i-1];
          682  +        if( strlen(pFile->zName)>nFrom ){
          683  +          pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
          684  +        }else{
          685  +          pNew->zName = fossil_strdup(pFile->zName);
          686  +        }
          687  +        pNew->zPrior = pFile->zName;
          688  +        pNew->isExe = pFile->isExe;
          689  +        pNew->isLink = pFile->isLink;
          690  +        pNew->zUuid = pFile->zUuid;
          691  +        pNew->isFrom = 0;
          692  +        gg.nFile--;
          693  +        *pFile = *pNew;
          694  +        memset(pNew, 0, sizeof(*pNew));
          695  +      }
          696  +      fossil_fatal("cannot handle R records, use --full-tree");
          697  +    }else
          698  +    if( memcmp(zLine, "deleteall", 9)==0 ){
          699  +      gg.fromLoaded = 1;
          700  +    }else
          701  +    if( memcmp(zLine, "N ", 2)==0 ){
          702  +      /* No-op */
          703  +    }else
          704  +
          705  +    {
          706  +      goto malformed_line;
          707  +    }
          708  +  }
          709  +  gg.xFinish();
          710  +  if( gg.hasLinks ){
          711  +    db_set_int("allow-symlinks", 1, 0);
          712  +  }
          713  +  import_reset(1);
          714  +  return;
          715  +
          716  +malformed_line:
          717  +  trim_newline(zLine);
          718  +  fossil_fatal("bad fast-import line: [%s]", zLine);
          719  +  return;
          720  +}
          721  +
          722  +/*
          723  +** COMMAND: import
          724  +**
          725  +** Usage: %fossil import --git ?OPTIONS? NEW-REPOSITORY
          726  +**
          727  +** Read text generated by the git-fast-export command and use it to
          728  +** construct a new Fossil repository named by the NEW-REPOSITORY
          729  +** argument.  The git-fast-export text is read from standard input.
          730  +**
          731  +** The git-fast-export file format is currently the only VCS interchange
          732  +** format that is understood, though other interchange formats may be added
          733  +** in the future.
          734  +**
          735  +** The --incremental option allows an existing repository to be extended
          736  +** with new content.
          737  +**
          738  +** Options:
          739  +**   --incremental  allow importing into an existing repository
          740  +**
          741  +** See also: export
          742  +*/
          743  +void git_import_cmd(void){
          744  +  char *zPassword;
          745  +  FILE *pIn;
          746  +  Stmt q;
          747  +  int forceFlag = find_option("force", "f", 0)!=0;
          748  +  int incrFlag = find_option("incremental", "i", 0)!=0;
          749  +
          750  +  find_option("git",0,0);  /* Skip the --git option for now */
          751  +  verify_all_options();
          752  +  if( g.argc!=3  && g.argc!=4 ){
          753  +    usage("REPOSITORY-NAME");
          754  +  }
          755  +  if( g.argc==4 ){
          756  +    pIn = fossil_fopen(g.argv[3], "rb");
          757  +  }else{
          758  +    pIn = stdin;
          759  +    fossil_binary_mode(pIn);
          760  +  }
          761  +  if( !incrFlag ){
          762  +    if( forceFlag ) file_delete(g.argv[2]);
          763  +    db_create_repository(g.argv[2]);
          764  +  }
          765  +  db_open_repository(g.argv[2]);
          766  +  db_open_config(0);
          767  +
          768  +  /* The following temp-tables are used to hold information needed for
          769  +  ** the import.
          770  +  **
          771  +  ** The XMARK table provides a mapping from fast-import "marks" and symbols
          772  +  ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts).
          773  +  ** Given any valid fast-import symbol, the corresponding fossil rid and
          774  +  ** uuid can found by searching against the xmark.tname field.
          775  +  **
          776  +  ** The XBRANCH table maps commit marks and symbols into the branch those
          777  +  ** commits belong to.  If xbranch.tname is a fast-import symbol for a
          778  +  ** checkin then xbranch.brnm is the branch that checkin is part of.
          779  +  **
          780  +  ** The XTAG table records information about tags that need to be applied
          781  +  ** to various branches after the import finishes.  The xtag.tcontent field
          782  +  ** contains the text of an artifact that will add a tag to a check-in.
          783  +  ** The git-fast-export file format might specify the same tag multiple
          784  +  ** times but only the last tag should be used.  And we do not know which
          785  +  ** occurrence of the tag is the last until the import finishes.
          786  +  */
          787  +  db_multi_exec(
          788  +     "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);"
          789  +     "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);"
          790  +     "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);"
          791  +  );
          792  +
          793  +
          794  +  db_begin_transaction();
          795  +  if( !incrFlag ) db_initial_setup(0, 0, 0, 1);
          796  +  git_fast_import(pIn);
          797  +  db_prepare(&q, "SELECT tcontent FROM xtag");
          798  +  while( db_step(&q)==SQLITE_ROW ){
          799  +    Blob record;
          800  +    db_ephemeral_blob(&q, 0, &record);
          801  +    fast_insert_content(&record, 0, 0);
          802  +    import_reset(0);
          803  +  }
          804  +  db_finalize(&q);
          805  +  db_end_transaction(0);
          806  +  db_begin_transaction();
          807  +  fossil_print("Rebuilding repository meta-data...\n");
          808  +  rebuild_db(0, 1, !incrFlag);
          809  +  verify_cancel();
          810  +  db_end_transaction(0);
          811  +  fossil_print("Vacuuming..."); fflush(stdout);
          812  +  db_multi_exec("VACUUM");
          813  +  fossil_print(" ok\n");
          814  +  if( !incrFlag ){
          815  +    fossil_print("project-id: %s\n", db_get("project-code", 0));
          816  +    fossil_print("server-id:  %s\n", db_get("server-code", 0));
          817  +    zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
          818  +    fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
          819  +  }
          820  +}

Changes to src/info.c.

    20     20   ** the current tree, or a particular artifact or check-in.
    21     21   */
    22     22   #include "config.h"
    23     23   #include "info.h"
    24     24   #include <assert.h>
    25     25   
    26     26   /*
    27         -** Return a string (in memory obtained from malloc) holding a 
    28         -** comma-separated list of tags that apply to check-in with 
           27  +** Return a string (in memory obtained from malloc) holding a
           28  +** comma-separated list of tags that apply to check-in with
    29     29   ** record-id rid.  If the "propagatingOnly" flag is true, then only
    30     30   ** show branch tags (tags that propagate to children).
    31     31   **
    32     32   ** Return NULL if there are no such tags.
    33     33   */
    34     34   char *info_tags_of_checkin(int rid, int propagatingOnly){
    35     35     char *zTags;
................................................................................
    47     47   ** Print common information about a particular record.
    48     48   **
    49     49   **     *  The UUID
    50     50   **     *  The record ID
    51     51   **     *  mtime and ctime
    52     52   **     *  who signed it
    53     53   */
    54         -void show_common_info(int rid, const char *zUuidName, int showComment){
           54  +void show_common_info(
           55  +  int rid,                   /* The rid for the check-in to display info for */
           56  +  const char *zUuidName,     /* Name of the UUID */
           57  +  int showComment,           /* True to show the check-in comment */
           58  +  int showFamily             /* True to show parents and children */
           59  +){
    55     60     Stmt q;
    56     61     char *zComment = 0;
    57     62     char *zTags;
    58     63     char *zDate;
    59     64     char *zUuid;
    60     65     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    61     66     if( zUuid ){
    62         -    zDate = db_text(0, 
           67  +    zDate = db_text(0,
    63     68         "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
    64     69         rid
    65     70       );
    66     71            /* 01234567890123 */
    67         -    printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
           72  +    fossil_print("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
    68     73       free(zUuid);
    69     74       free(zDate);
    70     75     }
    71         -  db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
    72         -                 " WHERE cid=%d", rid);
    73         -  while( db_step(&q)==SQLITE_ROW ){
    74         -    const char *zUuid = db_column_text(&q, 0);
    75         -    zDate = db_text("", 
    76         -      "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
    77         -      db_column_int(&q, 1)
           76  +  if( zUuid && showComment ){
           77  +    zComment = db_text(0,
           78  +      "SELECT coalesce(ecomment,comment) || "
           79  +      "       ' (user: ' || coalesce(euser,user,'?') || ')' "
           80  +      "  FROM event WHERE objid=%d",
           81  +      rid
    78     82       );
    79         -    printf("parent:       %s %s\n", zUuid, zDate);
    80         -    free(zDate);
    81         -  }
    82         -  db_finalize(&q);
    83         -  db_prepare(&q, "SELECT uuid, cid FROM plink JOIN blob ON cid=rid "
    84         -                 " WHERE pid=%d", rid);
    85         -  while( db_step(&q)==SQLITE_ROW ){
    86         -    const char *zUuid = db_column_text(&q, 0);
    87         -    zDate = db_text("", 
    88         -      "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
    89         -      db_column_int(&q, 1)
    90         -    );
    91         -    printf("child:        %s %s\n", zUuid, zDate);
    92         -    free(zDate);
    93         -  }
    94         -  db_finalize(&q);
           83  +  }
           84  +  if( showFamily ){
           85  +    db_prepare(&q, "SELECT uuid, pid, isprim FROM plink JOIN blob ON pid=rid "
           86  +                   " WHERE cid=%d"
           87  +                   " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
           88  +    while( db_step(&q)==SQLITE_ROW ){
           89  +      const char *zUuid = db_column_text(&q, 0);
           90  +      const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:";
           91  +      zDate = db_text("",
           92  +        "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
           93  +        db_column_int(&q, 1)
           94  +      );
           95  +      fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
           96  +      free(zDate);
           97  +    }
           98  +    db_finalize(&q);
           99  +    db_prepare(&q, "SELECT uuid, cid, isprim FROM plink JOIN blob ON cid=rid "
          100  +                   " WHERE pid=%d"
          101  +                   " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
          102  +    while( db_step(&q)==SQLITE_ROW ){
          103  +      const char *zUuid = db_column_text(&q, 0);
          104  +      const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:";
          105  +      zDate = db_text("",
          106  +        "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
          107  +        db_column_int(&q, 1)
          108  +      );
          109  +      fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
          110  +      free(zDate);
          111  +    }
          112  +    db_finalize(&q);
          113  +  }
    95    114     zTags = info_tags_of_checkin(rid, 0);
    96    115     if( zTags && zTags[0] ){
    97         -    printf("tags:         %s\n", zTags);
          116  +    fossil_print("tags:         %s\n", zTags);
    98    117     }
    99    118     free(zTags);
   100    119     if( zComment ){
   101         -    printf("comment:\n%s\n", zComment);
          120  +    fossil_print("comment:      ");
          121  +    comment_print(zComment, 14, 79);
   102    122       free(zComment);
   103    123     }
   104    124   }
          125  +
          126  +/*
          127  +** Print information about the URLs used to access a repository and
          128  +** checkouts in a repository.
          129  +*/
          130  +static void extraRepoInfo(void){
          131  +  Stmt s;
          132  +  db_prepare(&s, "SELECT substr(name,7), date(mtime,'unixepoch')"
          133  +                 "  FROM config"
          134  +                 " WHERE name GLOB 'ckout:*' ORDER BY name");
          135  +  while( db_step(&s)==SQLITE_ROW ){
          136  +    const char *zName;
          137  +    const char *zCkout = db_column_text(&s, 0);
          138  +    if( g.localOpen ){
          139  +      if( fossil_strcmp(zCkout, g.zLocalRoot)==0 ) continue;
          140  +      zName = "alt-root:";
          141  +    }else{
          142  +      zName = "check-out:";
          143  +    }
          144  +    fossil_print("%-11s   %-54s %s\n", zName, zCkout,
          145  +                 db_column_text(&s, 1));
          146  +  }
          147  +  db_finalize(&s);
          148  +  db_prepare(&s, "SELECT substr(name,9), date(mtime,'unixepoch')"
          149  +                 "  FROM config"
          150  +                 " WHERE name GLOB 'baseurl:*' ORDER BY name");
          151  +  while( db_step(&s)==SQLITE_ROW ){
          152  +    fossil_print("access-url:   %-54s %s\n", db_column_text(&s, 0),
          153  +                 db_column_text(&s, 1));
          154  +  }
          155  +  db_finalize(&s);
          156  +}
   105    157   
   106    158   
   107    159   /*
   108    160   ** COMMAND: info
   109    161   **
   110         -** Usage: %fossil info ?ARTIFACT-ID|FILENAME?
          162  +** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
   111    163   **
   112    164   ** With no arguments, provide information about the current tree.
   113    165   ** If an argument is specified, provide information about the object
   114         -** in the respository of the current tree that the argument refers
          166  +** in the repository of the current tree that the argument refers
   115    167   ** to.  Or if the argument is the name of a repository, show
   116    168   ** information about that repository.
          169  +**
          170  +** Use the "finfo" command to get information about a specific
          171  +** file in a checkout.
          172  +**
          173  +** Options:
          174  +**
          175  +**    -R|--repository FILE       Extract info from repository FILE
          176  +**    -l|--detail                Show extra information
          177  +**
          178  +** See also: annotate, artifact, finfo, timeline
   117    179   */
   118    180   void info_cmd(void){
   119    181     i64 fsize;
   120         -  if( g.argc!=2 && g.argc!=3 ){
   121         -    usage("?FILENAME|ARTIFACT-ID?");
   122         -  }
          182  +  int bDetail = find_option("detail","l",0)!=0;
   123    183     if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
   124    184       db_open_config(0);
   125    185       db_record_repository_filename(g.argv[2]);
   126    186       db_open_repository(g.argv[2]);
   127         -    printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
   128         -    printf("project-code: %s\n", db_get("project-code", "<none>"));
   129         -    printf("server-code:  %s\n", db_get("server-code", "<none>"));
          187  +    fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
          188  +    fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
          189  +    extraRepoInfo();
   130    190       return;
   131    191     }
   132         -  db_must_be_within_tree();
          192  +  db_find_and_open_repository(0,0);
   133    193     if( g.argc==2 ){
   134    194       int vid;
   135    195            /* 012345678901234 */
   136    196       db_record_repository_filename(0);
   137         -    printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
   138         -    printf("repository:   %s\n", db_lget("repository", ""));
   139         -    printf("local-root:   %s\n", g.zLocalRoot);
   140         -#ifdef __MINGW32__
          197  +    fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
          198  +    if( g.localOpen ){
          199  +      fossil_print("repository:   %s\n", db_repository_filename());
          200  +      fossil_print("local-root:   %s\n", g.zLocalRoot);
          201  +    }
          202  +    if( bDetail ) extraRepoInfo();
          203  +#if defined(_WIN32)
   141    204       if( g.zHome ){
   142         -      printf("user-home:    %s\n", g.zHome);
          205  +      fossil_print("user-home:    %s\n", g.zHome);
   143    206       }
   144    207   #endif
   145         -    printf("project-code: %s\n", db_get("project-code", ""));
   146         -    printf("server-code:  %s\n", db_get("server-code", ""));
   147         -    vid = db_lget_int("checkout", 0);
   148         -    if( vid==0 ){
   149         -      printf("checkout:     nil\n");
   150         -    }else{
   151         -      show_common_info(vid, "checkout:", 1);
          208  +    fossil_print("project-code: %s\n", db_get("project-code", ""));
          209  +    vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
          210  +    if( vid ){
          211  +      show_common_info(vid, "checkout:", 1, 1);
   152    212       }
          213  +    fossil_print("checkins:     %d\n",
          214  +                 db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/"));
   153    215     }else{
   154    216       int rid;
   155    217       rid = name_to_rid(g.argv[2]);
   156    218       if( rid==0 ){
   157    219         fossil_panic("no such object: %s\n", g.argv[2]);
   158    220       }
   159         -    show_common_info(rid, "uuid:", 1);
          221  +    show_common_info(rid, "uuid:", 1, 1);
   160    222     }
   161    223   }
   162    224   
   163    225   /*
   164    226   ** Show information about all tags on a given node.
   165    227   */
   166    228   static void showTags(int rid, const char *zNotGlob){
................................................................................
   168    230     int cnt = 0;
   169    231     db_prepare(&q,
   170    232       "SELECT tag.tagid, tagname, "
   171    233       "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
   172    234       "       value, datetime(tagxref.mtime,'localtime'), tagtype,"
   173    235       "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
   174    236       "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
   175         -    " WHERE tagxref.rid=%d AND tagname NOT GLOB '%s'"
   176         -    " ORDER BY tagname", rid, rid, rid, zNotGlob
          237  +    " WHERE tagxref.rid=%d AND tagname NOT GLOB '%q'"
          238  +    " ORDER BY tagname /*sort*/", rid, rid, rid, zNotGlob
   177    239     );
   178    240     while( db_step(&q)==SQLITE_ROW ){
   179    241       const char *zTagname = db_column_text(&q, 1);
   180    242       const char *zSrcUuid = db_column_text(&q, 2);
   181    243       const char *zValue = db_column_text(&q, 3);
   182    244       const char *zDate = db_column_text(&q, 4);
   183    245       int tagtype = db_column_int(&q, 5);
................................................................................
   185    247       cnt++;
   186    248       if( cnt==1 ){
   187    249         @ <div class="section">Tags And Properties</div>
   188    250         @ <ul>
   189    251       }
   190    252       @ <li>
   191    253       if( tagtype==0 ){
   192         -      @ <b><s>%h(zTagname)</s></b> cancelled
          254  +      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
   193    255       }else if( zValue ){
   194         -      @ <b>%h(zTagname)=%h(zValue)</b>
          256  +      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
   195    257       }else {
   196         -      @ <b>%h(zTagname)</b>
          258  +      @ <span class="infoTag">%h(zTagname)</span>
   197    259       }
   198    260       if( tagtype==2 ){
   199    261         if( zOrigUuid && zOrigUuid[0] ){
   200    262           @ inherited from
   201    263           hyperlink_to_uuid(zOrigUuid);
   202    264         }else{
   203    265           @ propagates to descendants
   204    266         }
   205         -      if( zValue && strcmp(zTagname,"branch")==0 ){
          267  +#if 0
          268  +      if( zValue && fossil_strcmp(zTagname,"branch")==0 ){
   206    269           @ &nbsp;&nbsp;
   207         -        @ <a href="%s(g.zBaseURL)/timeline?t=%T(zValue)">branch timeline</a>
          270  +        @ %z(href("%R/timeline?r=%T",zValue))branch timeline</a>
   208    271         }
          272  +#endif
   209    273       }
   210    274       if( zSrcUuid && zSrcUuid[0] ){
   211    275         if( tagtype==0 ){
   212    276           @ by
   213    277         }else{
   214    278           @ added by
   215    279         }
   216    280         hyperlink_to_uuid(zSrcUuid);
   217    281         @ on
   218    282         hyperlink_to_date(zDate,0);
   219    283       }
          284  +    @ </li>
   220    285     }
   221    286     db_finalize(&q);
   222    287     if( cnt ){
   223    288       @ </ul>
   224    289     }
   225    290   }
   226    291   
   227    292   
   228    293   /*
   229         -** Append the difference between two RIDs to the output
          294  +** Append the difference between artifacts to the output
   230    295   */
   231         -static void append_diff(int fromid, int toid){
          296  +static void append_diff(
          297  +  const char *zFrom,    /* Diff from this artifact */
          298  +  const char *zTo,      /*  ... to this artifact */
          299  +  u64 diffFlags,        /* Diff formatting flags */
          300  +  ReCompiled *pRe       /* Only show change matching this regex */
          301  +){
          302  +  int fromid;
          303  +  int toid;
   232    304     Blob from, to, out;
   233         -  content_get(fromid, &from);
   234         -  content_get(toid, &to);
          305  +  if( zFrom ){
          306  +    fromid = uuid_to_rid(zFrom, 0);
          307  +    content_get(fromid, &from);
          308  +  }else{
          309  +    blob_zero(&from);
          310  +  }
          311  +  if( zTo ){
          312  +    toid = uuid_to_rid(zTo, 0);
          313  +    content_get(toid, &to);
          314  +  }else{
          315  +    blob_zero(&to);
          316  +  }
   235    317     blob_zero(&out);
   236         -  text_diff(&from, &to, &out, 5);
   237         -  @ %h(blob_str(&out))
          318  +  if( diffFlags & DIFF_SIDEBYSIDE ){
          319  +    text_diff(&from, &to, &out, pRe, diffFlags | DIFF_HTML);
          320  +    @ <div class="sbsdiff">
          321  +    @ %s(blob_str(&out))
          322  +    @ </div>
          323  +  }else{
          324  +    text_diff(&from, &to, &out, pRe, diffFlags | DIFF_LINENO | DIFF_HTML);
          325  +    @ <div class="udiff">
          326  +    @ %s(blob_str(&out))
          327  +    @ </div>
          328  +  }
   238    329     blob_reset(&from);
   239    330     blob_reset(&to);
   240         -  blob_reset(&out);  
          331  +  blob_reset(&out);
          332  +}
          333  +
          334  +
          335  +/*
          336  +** Write a line of web-page output that shows changes that have occurred
          337  +** to a file between two check-ins.
          338  +*/
          339  +static void append_file_change_line(
          340  +  const char *zName,    /* Name of the file that has changed */
          341  +  const char *zOld,     /* blob.uuid before change.  NULL for added files */
          342  +  const char *zNew,     /* blob.uuid after change.  NULL for deletes */
          343  +  const char *zOldName, /* Prior name.  NULL if no name change. */
          344  +  u64 diffFlags,        /* Flags for text_diff().  Zero to omit diffs */
          345  +  ReCompiled *pRe,      /* Only show diffs that match this regex, if not NULL */
          346  +  int mperm             /* executable or symlink permission for zNew */
          347  +){
          348  +  if( !g.perm.Hyperlink ){
          349  +    if( zNew==0 ){
          350  +      @ <p>Deleted %h(zName)</p>
          351  +    }else if( zOld==0 ){
          352  +      @ <p>Added %h(zName)</p>
          353  +    }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
          354  +      @ <p>Name change from %h(zOldName) to %h(zName)
          355  +    }else if( fossil_strcmp(zNew, zOld)==0 ){
          356  +      @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
          357  +      @  for %h(zName)</p>
          358  +    }else{
          359  +      @ <p>Changes to %h(zName)</p>
          360  +    }
          361  +    if( diffFlags ){
          362  +      @ <pre style="white-space:pre;">
          363  +      append_diff(zOld, zNew, diffFlags, pRe);
          364  +      @ </pre>
          365  +    }
          366  +  }else{
          367  +    if( zOld && zNew ){
          368  +      if( fossil_strcmp(zOld, zNew)!=0 ){
          369  +        @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          370  +        @ from %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a>
          371  +        @ to %z(href("%R/artifact/%s",zNew))[%S(zNew)].</a>
          372  +      }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
          373  +        @ <p>Name change
          374  +        @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
          375  +        @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
          376  +      }else{
          377  +        @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
          378  +        @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          379  +      }
          380  +    }else if( zOld ){
          381  +      @ <p>Deleted %z(href("%s/finfo?name=%T",g.zTop,zName))%h(zName)</a>
          382  +      @ version %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a>
          383  +    }else{
          384  +      @ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          385  +      @ version %z(href("%R/artifact/%s",zNew))[%S(zNew)]</a>
          386  +    }
          387  +    if( diffFlags ){
          388  +      @ <pre style="white-space:pre;">
          389  +      append_diff(zOld, zNew, diffFlags, pRe);
          390  +      @ </pre>
          391  +    }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
          392  +      @ &nbsp;&nbsp;
          393  +      @ %z(href("%R/fdiff?v1=%S&v2=%S",zOld,zNew))[diff]</a>
          394  +    }
          395  +    @ </p>
          396  +  }
          397  +}
          398  +
          399  +/*
          400  +** Construct an appropriate diffFlag for text_diff() based on query
          401  +** parameters and the to boolean arguments.
          402  +*/
          403  +u64 construct_diff_flags(int showDiff, int sideBySide){
          404  +  u64 diffFlags;
          405  +  if( showDiff==0 ){
          406  +    diffFlags = 0;  /* Zero means do not show any diff */
          407  +  }else{
          408  +    int x;
          409  +    if( sideBySide ){
          410  +      diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
          411  +
          412  +      /* "dw" query parameter determines width of each column */
          413  +      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
          414  +      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
          415  +      diffFlags += x;
          416  +    }else{
          417  +      diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
          418  +    }
          419  +
          420  +    /* "dc" query parameter determines lines of context */
          421  +    x = atoi(PD("dc","7"));
          422  +    if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
          423  +    diffFlags += x;
          424  +
          425  +    /* The "noopt" parameter disables diff optimization */
          426  +    if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
          427  +  }
          428  +  return diffFlags;
   241    429   }
   242    430   
   243    431   
   244    432   /*
   245    433   ** WEBPAGE: vinfo
   246    434   ** WEBPAGE: ci
   247    435   ** URL:  /ci?name=RID|ARTIFACTID
   248    436   **
   249         -** Display information about a particular check-in. 
          437  +** Display information about a particular check-in.
   250    438   **
   251    439   ** We also jump here from /info if the name is a version.
   252    440   **
   253    441   ** If the /ci page is used (instead of /vinfo or /info) then the
   254    442   ** default behavior is to show unified diffs of all file changes.
   255    443   ** With /vinfo and /info, only a list of the changed files are
   256    444   ** shown, without diffs.  This behavior is inverted if the
   257    445   ** "show-version-diffs" setting is turned on.
   258    446   */
   259    447   void ci_page(void){
   260    448     Stmt q;
   261    449     int rid;
   262    450     int isLeaf;
   263         -  int showDiff;
   264         -  const char *zName;
          451  +  int showDiff;        /* True to show diffs */
          452  +  int sideBySide;      /* True for side-by-side diffs */
          453  +  u64 diffFlags;       /* Flag parameter for text_diff() */
          454  +  const char *zName;   /* Name of the checkin to be displayed */
          455  +  const char *zUuid;   /* UUID of zName */
          456  +  const char *zParent; /* UUID of the parent checkin (if any) */
          457  +  const char *zRe;     /* regex parameter */
          458  +  ReCompiled *pRe = 0; /* regex */
   265    459   
   266    460     login_check_credentials();
   267         -  if( !g.okRead ){ login_needed(); return; }
          461  +  if( !g.perm.Read ){ login_needed(); return; }
   268    462     zName = P("name");
   269    463     rid = name_to_rid_www("name");
   270    464     if( rid==0 ){
   271    465       style_header("Check-in Information Error");
   272    466       @ No such object: %h(g.argv[2])
   273    467       style_footer();
   274    468       return;
   275    469     }
   276         -  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
   277         -  db_prepare(&q, 
   278         -     "SELECT uuid, datetime(mtime, 'localtime'), user, comment"
          470  +  zRe = P("regex");
          471  +  if( zRe ) re_compile(&pRe, zRe, 0);
          472  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          473  +  zParent = db_text(0,
          474  +    "SELECT uuid FROM plink, blob"
          475  +    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
          476  +    rid
          477  +  );
          478  +  isLeaf = is_a_leaf(rid);
          479  +  db_prepare(&q,
          480  +     "SELECT uuid, datetime(mtime, 'localtime'), user, comment,"
          481  +     "       datetime(omtime, 'localtime'), mtime"
   279    482        "  FROM blob, event"
   280    483        " WHERE blob.rid=%d"
   281    484        "   AND event.objid=%d",
   282    485        rid, rid
   283    486     );
          487  +  sideBySide = atoi(PD("sbs","1"));
   284    488     if( db_step(&q)==SQLITE_ROW ){
   285    489       const char *zUuid = db_column_text(&q, 0);
   286    490       char *zTitle = mprintf("Check-in [%.10s]", zUuid);
   287    491       char *zEUser, *zEComment;
   288    492       const char *zUser;
   289    493       const char *zComment;
   290    494       const char *zDate;
          495  +    const char *zOrigDate;
          496  +#if 0
          497  +    char *zThisBranch;
          498  +    double thisMtime;
          499  +    int seenDiffTitle = 0;
          500  +#endif
          501  +
   291    502       style_header(zTitle);
   292    503       login_anonymous_available();
   293    504       free(zTitle);
   294    505       zEUser = db_text(0,
   295    506                      "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
   296    507                       TAG_USER, rid);
   297         -    zEComment = db_text(0, 
          508  +    zEComment = db_text(0,
   298    509                      "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
   299    510                      TAG_COMMENT, rid);
   300    511       zUser = db_column_text(&q, 2);
   301    512       zComment = db_column_text(&q, 3);
   302    513       zDate = db_column_text(&q,1);
          514  +    zOrigDate = db_column_text(&q, 4);
          515  +#if 0
          516  +    thisMtime = db_column_double(&q, 5);
          517  +#endif
   303    518       @ <div class="section">Overview</div>
   304         -    @ <p><table class="label-value">
          519  +    @ <table class="label-value">
   305    520       @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
   306         -    if( g.okSetup ){
          521  +    if( g.perm.Setup ){
   307    522         @ (Record ID: %d(rid))
   308    523       }
   309    524       @ </td></tr>
   310    525       @ <tr><th>Date:</th><td>
   311    526       hyperlink_to_date(zDate, "</td></tr>");
          527  +    if( zOrigDate && fossil_strcmp(zDate, zOrigDate)!=0 ){
          528  +      @ <tr><th>Original&nbsp;Date:</th><td>
          529  +      hyperlink_to_date(zOrigDate, "</td></tr>");
          530  +    }
   312    531       if( zEUser ){
   313    532         @ <tr><th>Edited&nbsp;User:</th><td>
   314    533         hyperlink_to_user(zEUser,zDate,"</td></tr>");
   315    534         @ <tr><th>Original&nbsp;User:</th><td>
   316    535         hyperlink_to_user(zUser,zDate,"</td></tr>");
   317    536       }else{
   318    537         @ <tr><th>User:</th><td>
................................................................................
   320    539       }
   321    540       if( zEComment ){
   322    541         @ <tr><th>Edited&nbsp;Comment:</th><td>%w(zEComment)</td></tr>
   323    542         @ <tr><th>Original&nbsp;Comment:</th><td>%w(zComment)</td></tr>
   324    543       }else{
   325    544         @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
   326    545       }
   327         -    if( g.okAdmin ){
   328         -      db_prepare(&q, 
          546  +    if( g.perm.Admin ){
          547  +      db_prepare(&q,
   329    548            "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
   330    549            "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
   331    550            " WHERE blob.rid=%d",
   332    551            rid
   333    552         );
   334    553         if( db_step(&q)==SQLITE_ROW ){
   335    554           const char *zIpAddr = db_column_text(&q, 0);
................................................................................
   337    556           const char *zDate = db_column_text(&q, 2);
   338    557           if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
   339    558           @ <tr><th>Received&nbsp;From:</th>
   340    559           @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
   341    560         }
   342    561         db_finalize(&q);
   343    562       }
   344         -    if( g.okHistory ){
          563  +    if( g.perm.Hyperlink ){
   345    564         const char *zProjName = db_get("project-name", "unnamed");
   346    565         @ <tr><th>Timelines:</th><td>
   347         -      @    <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
   348         -      @    | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
   349         -      @    | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a>
          566  +      @   %z(href("%R/timeline?f=%S",zUuid))family</a>
          567  +      if( zParent ){
          568  +        @ | %z(href("%R/timeline?p=%S",zUuid))ancestors</a>
          569  +      }
          570  +      if( !isLeaf ){
          571  +        @ | %z(href("%R/timeline?d=%S",zUuid))descendants</a>
          572  +      }
          573  +      if( zParent && !isLeaf ){
          574  +        @ | %z(href("%R/timeline?dp=%S",zUuid))both</a>
          575  +      }
   350    576         db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
   351    577                        " WHERE rid=%d AND tagtype>0 "
   352    578                        "   AND tag.tagid=tagxref.tagid "
   353    579                        "   AND +tag.tagname GLOB 'sym-*'", rid);
   354    580         while( db_step(&q)==SQLITE_ROW ){
   355    581           const char *zTagName = db_column_text(&q, 0);
   356         -        @  | <a href="%s(g.zTop)/timeline?t=%T(zTagName)">%h(zTagName)</a>
          582  +        @  | %z(href("%R/timeline?r=%T",zTagName))%h(zTagName)</a>
          583  +      }
          584  +      db_finalize(&q);
          585  +
          586  +#if 0
          587  +      /* Select a few other branches to diff against */
          588  +      zThisBranch = db_text("trunk", "SELECT value FROM tagxref"
          589  +                                     " WHERE tagid=%d AND tagtype>0"
          590  +                                     "   AND rid=%d",
          591  +                                     TAG_BRANCH, rid);
          592  +
          593  +      /* Find nearby leaves to offer to diff against */
          594  +      db_prepare(&q,
          595  +         "SELECT tagxref.value, blob.uuid, min(%.17g-event.mtime)"
          596  +         "  FROM leaf, event, tagxref, blob"
          597  +         " WHERE event.mtime BETWEEN %.17g AND %.17g"
          598  +         "   AND event.type='ci'"
          599  +         "   AND event.objid=leaf.rid"
          600  +         "   AND NOT %z"
          601  +         "   AND tagxref.rid=event.objid"
          602  +         "   AND tagxref.tagid=%d AND tagxref.tagtype>0"
          603  +         "   AND tagxref.value!=%Q"
          604  +         "   AND blob.rid=tagxref.rid"
          605  +         " GROUP BY 1 ORDER BY 3",
          606  +         thisMtime, thisMtime-7, thisMtime+7,
          607  +         leaf_is_closed_sql("leaf.rid"),
          608  +         TAG_BRANCH, zThisBranch
          609  +      );
          610  +      while( db_step(&q)==SQLITE_ROW ){
          611  +        const char *zBr = db_column_text(&q, 0);
          612  +        const char *zId = db_column_text(&q, 1);
          613  +        if( !seenDiffTitle ){
          614  +          @ <tr><th valign="top">Diffs:</th><td valign="top">
          615  +          seenDiffTitle = 1;
          616  +        }else{
          617  +          @ |
          618  +        }
          619  +        @ %z(href("%R/vdiff?from=%S&to=%S",zId, zUuid))%h(zBr)</a>
   357    620         }
   358    621         db_finalize(&q);
          622  +
          623  +      if( fossil_strcmp(zThisBranch,"trunk")!=0 ){
          624  +        if( !seenDiffTitle ){
          625  +          @ <tr><th valign="top">Diffs:</th><td valign="top">
          626  +          seenDiffTitle = 1;
          627  +        }else{
          628  +          @ |
          629  +        }
          630  +        @ %z(href("%R/vdiff?from=root:%S&to=%S",zUuid,zUuid))root of
          631  +        @ this branch</a>
          632  +      }
          633  +      if( seenDiffTitle ){
          634  +        @ </td></tr>
          635  +      }
          636  +#endif
          637  +
          638  +      /* The Download: line */
          639  +      if( g.perm.Zip ){
          640  +        char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s",
          641  +                             zProjName, zUuid, zUuid);
          642  +        @ </td></tr>
          643  +        @ <tr><th>Downloads:</th><td>
          644  +        @ %z(href("%s",zUrl))Tarball</a>
          645  +        @ | %z(href("%R/zip/%t-%S.zip?uuid=%s",zProjName,zUuid,zUuid))
          646  +        @         ZIP archive</a>
          647  +        fossil_free(zUrl);
          648  +      }
   359    649         @ </td></tr>
   360    650         @ <tr><th>Other&nbsp;Links:</th>
   361    651         @   <td>
   362         -      @     <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a>
   363         -      @   | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
   364         -      @         ZIP archive</a>
   365         -      @   | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
   366         -      if( g.okWrite ){
   367         -        @   | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
          652  +      @     %z(href("%R/dir?ci=%S",zUuid))files</a>
          653  +      @   | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
          654  +      @   | %z(href("%R/artifact/%S",zUuid))manifest</a>
          655  +      if( g.perm.Write ){
          656  +        @   | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
   368    657         }
   369    658         @   </td>
   370    659         @ </tr>
   371    660       }
   372         -    @ </table></p>
          661  +    @ </table>
   373    662     }else{
   374    663       style_header("Check-in Information");
   375    664       login_anonymous_available();
   376    665     }
   377    666     db_finalize(&q);
   378    667     showTags(rid, "");
   379         -  @ <div class="section">Changes</div>
   380         -  showDiff = g.zPath[0]!='c';
   381         -  if( db_get_boolean("show-version-diffs", 0)==0 ){
   382         -    showDiff = !showDiff;
   383         -    if( showDiff ){
   384         -      @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[hide&nbsp;diffs]</a><br/>
   385         -    }else{
   386         -      @ <a href="%s(g.zBaseURL)/ci/%T(zName)">[show&nbsp;diffs]</a><br/>
   387         -    }
   388         -  }else{
   389         -    if( showDiff ){
   390         -      @ <a href="%s(g.zBaseURL)/ci/%T(zName)">[hide&nbsp;diffs]</a><br/>
   391         -    }else{
   392         -      @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[show&nbsp;diffs]</a><br/>
   393         -    }
   394         -  }
   395         -  db_prepare(&q,
   396         -     "SELECT pid, fid, name,"
   397         -     "       (SELECT uuid FROM blob WHERE rid=mlink.pid),"
   398         -     "       (SELECT uuid FROM blob WHERE rid=mlink.fid)"
   399         -     "  FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
   400         -     " WHERE mlink.mid=%d"
   401         -     " ORDER BY name",
   402         -     rid
   403         -  );
   404         -  while( db_step(&q)==SQLITE_ROW ){
   405         -    int pid = db_column_int(&q,0);
   406         -    int fid = db_column_int(&q,1);
   407         -    const char *zName = db_column_text(&q,2);
   408         -    const char *zOld = db_column_text(&q,3);
   409         -    const char *zNew = db_column_text(&q,4);
   410         -    if( !g.okHistory ){
   411         -      if( zNew==0 ){
   412         -        @ <p>Deleted %h(zName)</p>
   413         -        continue;
          668  +  if( zParent ){
          669  +    @ <div class="section">Changes</div>
          670  +    @ <div class="sectionmenu">
          671  +    showDiff = g.zPath[0]!='c';
          672  +    if( db_get_boolean("show-version-diffs", 0)==0 ){
          673  +      showDiff = !showDiff;
          674  +      if( showDiff ){
          675  +        @ %z(xhref("class='button'","%R/vinfo/%T",zName))
          676  +        @ hide&nbsp;diffs</a>
          677  +        if( sideBySide ){
          678  +          @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
          679  +          @ unified&nbsp;diffs</a>
          680  +        }else{
          681  +          @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
          682  +          @ side-by-side&nbsp;diffs</a>
          683  +        }
          684  +      }else{
          685  +        @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
          686  +        @ show&nbsp;unified&nbsp;diffs</a>
          687  +        @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
          688  +        @ show&nbsp;side-by-side&nbsp;diffs</a>
          689  +      }
          690  +    }else{
          691  +      if( showDiff ){
          692  +        @ %z(xhref("class='button'","%R/ci/%T",zName))hide&nbsp;diffs</a>
          693  +        if( sideBySide ){
          694  +          @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName))
          695  +          @ unified&nbsp;diffs</a>
          696  +        }else{
          697  +          @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName))
          698  +          @ side-by-side&nbsp;diffs</a>
          699  +        }
   414    700         }else{
   415         -        @ <p>Changes to %h(zName)</p>
   416         -      }
   417         -    }else if( zOld && zNew ){
   418         -      @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   419         -      @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
   420         -      @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
   421         -      if( !showDiff ){
   422         -        @ &nbsp;&nbsp;
   423         -        @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a>
   424         -      }
   425         -    }else if( zOld ){
   426         -      @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   427         -      @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p>
   428         -      continue;
   429         -    }else{
   430         -      @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   431         -      @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p>
   432         -    }
   433         -    if( showDiff ){
   434         -      @ <blockquote><pre>
   435         -      append_diff(pid, fid);
   436         -      @ </pre></blockquote>
   437         -    }
   438         -  }
   439         -  db_finalize(&q);
          701  +        @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName))
          702  +        @ show&nbsp;unified&nbsp;diffs</a>
          703  +        @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName))
          704  +        @ show&nbsp;side-by-side&nbsp;diffs</a>
          705  +      }
          706  +    }
          707  +    @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
          708  +    @ patch</a></div>
          709  +    if( pRe ){
          710  +      @ <p><b>Only differences that match regular expression "%h(zRe)"
          711  +      @ are shown.</b></p>
          712  +    }
          713  +    db_prepare(&q,
          714  +       "SELECT name,"
          715  +       "       mperm,"
          716  +       "       (SELECT uuid FROM blob WHERE rid=mlink.pid),"
          717  +       "       (SELECT uuid FROM blob WHERE rid=mlink.fid),"
          718  +       "       (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
          719  +       "  FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
          720  +       " WHERE mlink.mid=%d"
          721  +       "   AND (mlink.fid>0"
          722  +              " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
          723  +       " ORDER BY name /*sort*/",
          724  +       rid, rid
          725  +    );
          726  +    diffFlags = construct_diff_flags(showDiff, sideBySide);
          727  +    while( db_step(&q)==SQLITE_ROW ){
          728  +      const char *zName = db_column_text(&q,0);
          729  +      int mperm = db_column_int(&q, 1);
          730  +      const char *zOld = db_column_text(&q,2);
          731  +      const char *zNew = db_column_text(&q,3);
          732  +      const char *zOldName = db_column_text(&q, 4);
          733  +      append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
          734  +    }
          735  +    db_finalize(&q);
          736  +  }
   440    737     style_footer();
   441    738   }
   442    739   
   443    740   /*
   444    741   ** WEBPAGE: winfo
   445         -** URL:  /winfo?name=RID
          742  +** URL:  /winfo?name=UUID
   446    743   **
   447    744   ** Return information about a wiki page.
   448    745   */
   449    746   void winfo_page(void){
   450         -  Stmt q;
   451    747     int rid;
          748  +  Manifest *pWiki;
          749  +  char *zUuid;
          750  +  char *zDate;
          751  +  Blob wiki;
          752  +  int modPending;
          753  +  const char *zModAction;
   452    754   
   453    755     login_check_credentials();
   454         -  if( !g.okRdWiki ){ login_needed(); return; }
          756  +  if( !g.perm.RdWiki ){ login_needed(); return; }
   455    757     rid = name_to_rid_www("name");
   456         -  if( rid==0 ){
          758  +  if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI))==0 ){
   457    759       style_header("Wiki Page Information Error");
   458         -    @ No such object: %h(g.argv[2])
          760  +    @ No such object: %h(P("name"))
   459    761       style_footer();
   460    762       return;
   461    763     }
   462         -  db_prepare(&q, 
   463         -     "SELECT substr(tagname, 6, 1000), uuid,"
   464         -     "       datetime(event.mtime, 'localtime'), user"
   465         -     "  FROM tagxref, tag, blob, event"
   466         -     " WHERE tagxref.rid=%d"
   467         -     "   AND tag.tagid=tagxref.tagid"
   468         -     "   AND tag.tagname LIKE 'wiki-%%'"
   469         -     "   AND blob.rid=%d"
   470         -     "   AND event.objid=%d",
   471         -     rid, rid, rid
   472         -  );
   473         -  if( db_step(&q)==SQLITE_ROW ){
   474         -    const char *zName = db_column_text(&q, 0);
   475         -    const char *zUuid = db_column_text(&q, 1);
   476         -    char *zTitle = mprintf("Wiki Page %s", zName);
   477         -    const char *zDate = db_column_text(&q,2);
   478         -    const char *zUser = db_column_text(&q,3);
   479         -    style_header(zTitle);
   480         -    free(zTitle);
   481         -    login_anonymous_available();
   482         -    @ <div class="section">Overview</div>
   483         -    @ <p><table class="label-value">
   484         -    @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
   485         -    @ <tr><th>Date:</th><td>
   486         -    hyperlink_to_date(zDate, "</td></tr>");
   487         -    if( g.okSetup ){
   488         -      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
   489         -    }
   490         -    @ <tr><th>Original&nbsp;User:</th><td>
   491         -    hyperlink_to_user(zUser, zDate, "</td></tr>");
   492         -    if( g.okHistory ){
   493         -      @ <tr><th>Commands:</th>
   494         -      @   <td>
   495         -      @     <a href="%s(g.zBaseURL)/whistory?name=%t(zName)">history</a>
   496         -      @     | <a href="%s(g.zBaseURL)/artifact/%S(zUuid)">raw-text</a>
   497         -      @   </td>
   498         -      @ </tr>
   499         -    }
   500         -    @ </table></p>
   501         -  }else{
   502         -    style_header("Wiki Information");
   503         -    rid = 0;
   504         -  }
   505         -  db_finalize(&q);
   506         -  showTags(rid, "wiki-*");
   507         -  if( rid ){
   508         -    Blob content;
   509         -    Manifest m;
   510         -    memset(&m, 0, sizeof(m));
   511         -    blob_zero(&m.content);
   512         -    content_get(rid, &content);
   513         -    manifest_parse(&m, &content);
   514         -    if( m.type==CFTYPE_WIKI ){
   515         -      Blob wiki;
   516         -      blob_init(&wiki, m.zWiki, -1);
   517         -      @ <div class="section">Content</div>
   518         -      wiki_convert(&wiki, 0, 0);
   519         -      blob_reset(&wiki);
   520         -    }
   521         -    manifest_clear(&m);
   522         -  }
          764  +  if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
          765  +    if( strcmp(zModAction,"delete")==0 ){
          766  +      moderation_disapprove(rid);
          767  +      cgi_redirectf("%R/wiki?name=%T", pWiki->zWikiTitle);
          768  +      /*NOTREACHED*/
          769  +    }
          770  +    if( strcmp(zModAction,"approve")==0 ){
          771  +      moderation_approve(rid);
          772  +    }
          773  +  }
          774  +  style_header("Update of \"%h\"", pWiki->zWikiTitle);
          775  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          776  +  zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate);
          777  +  style_submenu_element("Raw", "Raw", "artifact/%S", zUuid);
          778  +  style_submenu_element("History", "History", "whistory?name=%t",
          779  +                        pWiki->zWikiTitle);
          780  +  style_submenu_element("Page", "Page", "wiki?name=%t",
          781  +                        pWiki->zWikiTitle);
          782  +  login_anonymous_available();
          783  +  @ <div class="section">Overview</div>
          784  +  @ <p><table class="label-value">
          785  +  @ <tr><th>Artifact&nbsp;ID:</th>
          786  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
          787  +  if( g.perm.Setup ){
          788  +    @ (%d(rid))
          789  +  }
          790  +  modPending = moderation_pending(rid);
          791  +  if( modPending ){
          792  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          793  +  }
          794  +  @ </td></tr>
          795  +  @ <tr><th>Page&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
          796  +  @ <tr><th>Date:</th><td>
          797  +  hyperlink_to_date(zDate, "</td></tr>");
          798  +  @ <tr><th>Original&nbsp;User:</th><td>
          799  +  hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
          800  +  if( pWiki->nParent>0 ){
          801  +    int i;
          802  +    @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
          803  +    for(i=0; i<pWiki->nParent; i++){
          804  +      char *zParent = pWiki->azParent[i];
          805  +      @ %z(href("info/%S",zParent))%s(zParent)</a>
          806  +    }
          807  +    @ </td></tr>
          808  +  }
          809  +  @ </table>
          810  +
          811  +  if( g.perm.ModWiki && modPending ){
          812  +    @ <div class="section">Moderation</div>
          813  +    @ <blockquote>
          814  +    @ <form method="POST" action="%R/winfo/%s(zUuid)">
          815  +    @ <label><input type="radio" name="modaction" value="delete">
          816  +    @ Delete this change</label><br />
          817  +    @ <label><input type="radio" name="modaction" value="approve">
          818  +    @ Approve this change</label><br />
          819  +    @ <input type="submit" value="Submit">
          820  +    @ </form>
          821  +    @ </blockquote>
          822  +  }
          823  +
          824  +
          825  +  @ <div class="section">Content</div>
          826  +  blob_init(&wiki, pWiki->zWiki, -1);
          827  +  wiki_convert(&wiki, 0, 0);
          828  +  blob_reset(&wiki);
          829  +  manifest_destroy(pWiki);
          830  +  style_footer();
          831  +}
          832  +
          833  +/*
          834  +** Show a webpage error message
          835  +*/
          836  +void webpage_error(const char *zFormat, ...){
          837  +  va_list ap;
          838  +  const char *z;
          839  +  va_start(ap, zFormat);
          840  +  z = vmprintf(zFormat, ap);
          841  +  va_end(ap);
          842  +  style_header("URL Error");
          843  +  @ <h1>Error</h1>
          844  +  @ <p>%h(z)</p>
   523    845     style_footer();
   524    846   }
   525    847   
   526    848   /*
   527         -** WEBPAGE: vdiff
   528         -** URL: /vdiff?name=RID
   529         -**
   530         -** Show all differences for a particular check-in.
          849  +** Find an checkin based on query parameter zParam and parse its
          850  +** manifest.  Return the number of errors.
   531    851   */
   532         -void vdiff_page(void){
          852  +static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
   533    853     int rid;
   534         -  Stmt q;
   535         -  char *zUuid;
   536    854   
   537         -  login_check_credentials();
   538         -  if( !g.okRead ){ login_needed(); return; }
   539         -  login_anonymous_available();
   540         -
   541         -  rid = name_to_rid_www("name");
          855  +  *pRid = rid = name_to_rid_www(zParam);
   542    856     if( rid==0 ){
   543         -    fossil_redirect_home();
          857  +    const char *z = P(zParam);
          858  +    if( z==0 || z[0]==0 ){
          859  +      webpage_error("Missing \"%s\" query parameter.", zParam);
          860  +    }else{
          861  +      webpage_error("No such artifact: \"%s\"", z);
          862  +    }
          863  +    return 0;
   544    864     }
   545         -  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   546         -  style_header("Check-in [%.10s]", zUuid);
          865  +  if( !is_a_version(rid) ){
          866  +    webpage_error("Artifact %s is not a checkin.", P(zParam));
          867  +    return 0;
          868  +  }
          869  +  return manifest_get(rid, CFTYPE_MANIFEST);
          870  +}
          871  +
          872  +/*
          873  +** Output a description of a check-in
          874  +*/
          875  +static void checkin_description(int rid){
          876  +  Stmt q;
   547    877     db_prepare(&q,
   548         -    "SELECT datetime(mtime), "
   549         -    "       coalesce(event.ecomment,event.comment),"
   550         -    "       coalesce(event.euser,event.user)"
   551         -    "  FROM event WHERE type='ci' AND objid=%d",
   552         -    rid
          878  +    "SELECT datetime(mtime), coalesce(euser,user),"
          879  +    "       coalesce(ecomment,comment), uuid,"
          880  +    "      (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
          881  +    "        WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
          882  +    "          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
          883  +    "  FROM event, blob"
          884  +    " WHERE event.objid=%d AND type='ci'"
          885  +    "   AND blob.rid=%d",
          886  +    rid, rid
   553    887     );
   554    888     while( db_step(&q)==SQLITE_ROW ){
   555    889       const char *zDate = db_column_text(&q, 0);
   556         -    const char *zUser = db_column_text(&q, 2);
   557         -    const char *zComment = db_column_text(&q, 1);
   558         -    @ <h2>Check-in %s(zUuid)</h2>
   559         -    @ <p>Made by
   560         -    hyperlink_to_user(zUser,zDate," on");
   561         -    hyperlink_to_date(zDate, ":");
   562         -    @ %w(zComment). 
   563         -    if( g.okHistory ){
   564         -      @ <a href="%s(g.zBaseURL)/ci/%s(zUuid)">[details]</a>
   565         -    }
   566         -    @ </p><hr>
   567         -  }
   568         -  db_finalize(&q);
   569         -  db_prepare(&q,
   570         -     "SELECT pid, fid, name"
   571         -     "  FROM mlink, filename"
   572         -     " WHERE mlink.mid=%d"
   573         -     "   AND filename.fnid=mlink.fnid"
   574         -     " ORDER BY name",
   575         -     rid
   576         -  );
   577         -  while( db_step(&q)==SQLITE_ROW ){
   578         -    int pid = db_column_int(&q,0);
   579         -    int fid = db_column_int(&q,1);
   580         -    const char *zName = db_column_text(&q,2);
   581         -    if( g.okHistory ){
   582         -      @ <p><a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></p>
   583         -    }else{
   584         -      @ <p>%h(zName)</p>
   585         -    }
   586         -    @ <blockquote><pre>
   587         -    append_diff(pid, fid);
   588         -    @ </pre></blockquote>
          890  +    const char *zUser = db_column_text(&q, 1);
          891  +    const char *zUuid = db_column_text(&q, 3);
          892  +    const char *zTagList = db_column_text(&q, 4);
          893  +    Blob comment;
          894  +    int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
          895  +    if( db_get_boolean("timeline-block-markup", 0)==0 ){
          896  +      wikiFlags |= WIKI_NOBLOCK;
          897  +    }
          898  +    hyperlink_to_uuid(zUuid);
          899  +    blob_zero(&comment);
          900  +    db_column_blob(&q, 2, &comment);
          901  +    wiki_convert(&comment, 0, wikiFlags);
          902  +    blob_reset(&comment);
          903  +    @ (user:
          904  +    hyperlink_to_user(zUser,zDate,",");
          905  +    if( zTagList && zTagList[0] && g.perm.Hyperlink ){
          906  +      int i;
          907  +      const char *z = zTagList;
          908  +      Blob links;
          909  +      blob_zero(&links);
          910  +      while( z && z[0] ){
          911  +        for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
          912  +        blob_appendf(&links,
          913  +              "%z%#h</a>%.2s",
          914  +              href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
          915  +        );
          916  +        if( z[i]==0 ) break;
          917  +        z += i+2;
          918  +      }
          919  +      @ tags: %s(blob_str(&links)),
          920  +      blob_reset(&links);
          921  +    }else{
          922  +      @ tags: %h(zTagList),
          923  +    }
          924  +    @ date:
          925  +    hyperlink_to_date(zDate, ")");
   589    926     }
   590    927     db_finalize(&q);
          928  +}
          929  +
          930  +
          931  +/*
          932  +** WEBPAGE: vdiff
          933  +** URL: /vdiff
          934  +**
          935  +** Query parameters:
          936  +**
          937  +**   from=TAG
          938  +**   to=TAG
          939  +**   branch=TAG
          940  +**   detail=BOOLEAN
          941  +**   sbs=BOOLEAN
          942  +**
          943  +**
          944  +** Show all differences between two checkins.
          945  +*/
          946  +void vdiff_page(void){
          947  +  int ridFrom, ridTo;
          948  +  int showDetail = 0;
          949  +  int sideBySide = 0;
          950  +  u64 diffFlags = 0;
          951  +  Manifest *pFrom, *pTo;
          952  +  ManifestFile *pFileFrom, *pFileTo;
          953  +  const char *zBranch;
          954  +  const char *zFrom;
          955  +  const char *zTo;
          956  +  const char *zRe;
          957  +  ReCompiled *pRe = 0;
          958  +
          959  +  login_check_credentials();
          960  +  if( !g.perm.Read ){ login_needed(); return; }
          961  +  login_anonymous_available();
          962  +
          963  +  zRe = P("regex");
          964  +  if( zRe ) re_compile(&pRe, zRe, 0);
          965  +  zBranch = P("branch");
          966  +  if( zBranch && zBranch[0] ){
          967  +    cgi_replace_parameter("from", mprintf("root:%s", zBranch));
          968  +    cgi_replace_parameter("to", zBranch);
          969  +  }
          970  +  pTo = vdiff_parse_manifest("to", &ridTo);
          971  +  if( pTo==0 ) return;
          972  +  pFrom = vdiff_parse_manifest("from", &ridFrom);
          973  +  if( pFrom==0 ) return;
          974  +  sideBySide = atoi(PD("sbs","1"));
          975  +  showDetail = atoi(PD("detail","0"));
          976  +  if( !showDetail && sideBySide ) showDetail = 1;
          977  +  zFrom = P("from");
          978  +  zTo = P("to");
          979  +  if( !sideBySide ){
          980  +    style_submenu_element("Side-by-side Diff", "sbsdiff",
          981  +                          "%R/vdiff?from=%T&to=%T&detail=%d&sbs=1",
          982  +                          zFrom, zTo, showDetail);
          983  +  }else{
          984  +    style_submenu_element("Unified Diff", "udiff",
          985  +                          "%R/vdiff?from=%T&to=%T&detail=%d&sbs=0",
          986  +                          zFrom, zTo, showDetail);
          987  +  }
          988  +  style_submenu_element("Invert", "invert",
          989  +                        "%R/vdiff?from=%T&to=%T&detail=%d&sbs=%d",
          990  +                        zTo, zFrom, showDetail, sideBySide);
          991  +  style_header("Check-in Differences");
          992  +  @ <h2>Difference From:</h2><blockquote>
          993  +  checkin_description(ridFrom);
          994  +  @ </blockquote><h2>To:</h2><blockquote>
          995  +  checkin_description(ridTo);
          996  +  @ </blockquote>
          997  +  if( pRe ){
          998  +    @ <p><b>Only differences that match regular expression "%h(zRe)"
          999  +    @ are shown.</b></p>
         1000  +  }
         1001  +  @<hr /><p>
         1002  +
         1003  +  manifest_file_rewind(pFrom);
         1004  +  pFileFrom = manifest_file_next(pFrom, 0);
         1005  +  manifest_file_rewind(pTo);
         1006  +  pFileTo = manifest_file_next(pTo, 0);
         1007  +  diffFlags = construct_diff_flags(showDetail, sideBySide);
         1008  +  while( pFileFrom || pFileTo ){
         1009  +    int cmp;
         1010  +    if( pFileFrom==0 ){
         1011  +      cmp = +1;
         1012  +    }else if( pFileTo==0 ){
         1013  +      cmp = -1;
         1014  +    }else{
         1015  +      cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
         1016  +    }
         1017  +    if( cmp<0 ){
         1018  +      append_file_change_line(pFileFrom->zName,
         1019  +                              pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
         1020  +      pFileFrom = manifest_file_next(pFrom, 0);
         1021  +    }else if( cmp>0 ){
         1022  +      append_file_change_line(pFileTo->zName,
         1023  +                              0, pFileTo->zUuid, 0, diffFlags, pRe,
         1024  +                              manifest_file_mperm(pFileTo));
         1025  +      pFileTo = manifest_file_next(pTo, 0);
         1026  +    }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
         1027  +      /* No changes */
         1028  +      pFileFrom = manifest_file_next(pFrom, 0);
         1029  +      pFileTo = manifest_file_next(pTo, 0);
         1030  +    }else{
         1031  +      append_file_change_line(pFileFrom->zName,
         1032  +                              pFileFrom->zUuid,
         1033  +                              pFileTo->zUuid, 0, diffFlags, pRe,
         1034  +                              manifest_file_mperm(pFileTo));
         1035  +      pFileFrom = manifest_file_next(pFrom, 0);
         1036  +      pFileTo = manifest_file_next(pTo, 0);
         1037  +    }
         1038  +  }
         1039  +  manifest_destroy(pFrom);
         1040  +  manifest_destroy(pTo);
         1041  +
   591   1042     style_footer();
   592   1043   }
         1044  +
         1045  +#if INTERFACE
         1046  +/*
         1047  +** Possible return values from object_description()
         1048  +*/
         1049  +#define OBJTYPE_CHECKIN    0x0001
         1050  +#define OBJTYPE_CONTENT    0x0002
         1051  +#define OBJTYPE_WIKI       0x0004
         1052  +#define OBJTYPE_TICKET     0x0008
         1053  +#define OBJTYPE_ATTACHMENT 0x0010
         1054  +#define OBJTYPE_EVENT      0x0020
         1055  +#define OBJTYPE_TAG        0x0040
         1056  +#define OBJTYPE_SYMLINK    0x0080
         1057  +#define OBJTYPE_EXE        0x0100
         1058  +#endif
   593   1059   
   594   1060   /*
   595   1061   ** Write a description of an object to the www reply.
   596   1062   **
   597   1063   ** If the object is a file then mention:
   598   1064   **
   599   1065   **     * It's artifact ID
................................................................................
   602   1068   **
   603   1069   ** If the object is a manifest, then mention:
   604   1070   **
   605   1071   **     * It's artifact ID
   606   1072   **     * date of check-in
   607   1073   **     * Comment & user
   608   1074   */
   609         -void object_description(
         1075  +int object_description(
   610   1076     int rid,                 /* The artifact ID */
   611   1077     int linkToView,          /* Add viewer link if true */
   612   1078     Blob *pDownloadName      /* Fill with an appropriate download name */
   613   1079   ){
   614   1080     Stmt q;
   615   1081     int cnt = 0;
   616   1082     int nWiki = 0;
         1083  +  int objType = 0;
   617   1084     char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
         1085  +
         1086  +  char *prevName = 0;
   618   1087   
   619   1088     db_prepare(&q,
   620   1089       "SELECT filename.name, datetime(event.mtime),"
   621   1090       "       coalesce(event.ecomment,event.comment),"
   622   1091       "       coalesce(event.euser,event.user),"
   623         -    "       b.uuid"
         1092  +    "       b.uuid, mlink.mperm,"
         1093  +    "       coalesce((SELECT value FROM tagxref"
         1094  +                    "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')"
   624   1095       "  FROM mlink, filename, event, blob a, blob b"
   625   1096       " WHERE filename.fnid=mlink.fnid"
   626   1097       "   AND event.objid=mlink.mid"
   627   1098       "   AND a.rid=mlink.fid"
   628   1099       "   AND b.rid=mlink.mid"
   629         -    "   AND mlink.fid=%d",
   630         -    rid
         1100  +    "   AND mlink.fid=%d"
         1101  +    "   ORDER BY filename.name, event.mtime /*sort*/",
         1102  +    TAG_BRANCH, rid
   631   1103     );
         1104  +  @ <ul>
   632   1105     while( db_step(&q)==SQLITE_ROW ){
   633   1106       const char *zName = db_column_text(&q, 0);
   634   1107       const char *zDate = db_column_text(&q, 1);
   635   1108       const char *zCom = db_column_text(&q, 2);
   636   1109       const char *zUser = db_column_text(&q, 3);
   637   1110       const char *zVers = db_column_text(&q, 4);
   638         -    if( cnt>0 ){
   639         -      @ Also file
   640         -    }else{
   641         -      @ File
         1111  +    int mPerm = db_column_int(&q, 5);
         1112  +    const char *zBr = db_column_text(&q, 6);
         1113  +    if( !prevName || fossil_strcmp(zName, prevName) ) {
         1114  +      if( prevName ) {
         1115  +        @ </ul>
         1116  +      }
         1117  +      if( mPerm==PERM_LNK ){
         1118  +        @ <li>Symbolic link
         1119  +        objType |= OBJTYPE_SYMLINK;
         1120  +      }else if( mPerm==PERM_EXE ){
         1121  +        @ <li>Executable file
         1122  +        objType |= OBJTYPE_EXE;
         1123  +      }else{
         1124  +        @ <li>File
         1125  +      }
         1126  +      objType |= OBJTYPE_CONTENT;
         1127  +      @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
         1128  +      @ <ul>
         1129  +      prevName = fossil_strdup(zName);
   642   1130       }
   643         -    if( g.okHistory ){
   644         -      @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a>
   645         -    }else{
   646         -      @ %h(zName)
   647         -    }
   648         -    @ part of check-in
         1131  +    @ <li>
         1132  +    hyperlink_to_date(zDate,"");
         1133  +    @ - part of checkin
   649   1134       hyperlink_to_uuid(zVers);
   650         -    @ - %w(zCom) by 
   651         -    hyperlink_to_user(zUser,zDate," on");
   652         -    hyperlink_to_date(zDate,".");
         1135  +    if( zBr && zBr[0] ){
         1136  +      @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a>
         1137  +    }
         1138  +    @ - %w(zCom) (user:
         1139  +    hyperlink_to_user(zUser,zDate,")");
         1140  +    if( g.perm.Hyperlink ){
         1141  +      @ %z(href("%R/annotate?checkin=%S&filename=%T",zVers,zName))
         1142  +      @ [annotate]</a>
         1143  +    }
   653   1144       cnt++;
   654   1145       if( pDownloadName && blob_size(pDownloadName)==0 ){
   655   1146         blob_append(pDownloadName, zName, -1);
   656   1147       }
   657   1148     }
         1149  +  if( prevName ){
         1150  +    @ </ul>
         1151  +  }
         1152  +  @ </ul>
         1153  +  free(prevName);
   658   1154     db_finalize(&q);
   659         -  db_prepare(&q, 
         1155  +  db_prepare(&q,
   660   1156       "SELECT substr(tagname, 6, 10000), datetime(event.mtime),"
   661   1157       "       coalesce(event.euser, event.user)"
   662   1158       "  FROM tagxref, tag, event"
   663   1159       " WHERE tagxref.rid=%d"
   664         -    "   AND tag.tagid=tagxref.tagid" 
         1160  +    "   AND tag.tagid=tagxref.tagid"
   665   1161       "   AND tag.tagname LIKE 'wiki-%%'"
   666   1162       "   AND event.objid=tagxref.rid",
   667   1163       rid
   668   1164     );
   669   1165     while( db_step(&q)==SQLITE_ROW ){
   670   1166       const char *zPagename = db_column_text(&q, 0);
   671   1167       const char *zDate = db_column_text(&q, 1);
   672   1168       const char *zUser = db_column_text(&q, 2);
   673   1169       if( cnt>0 ){
   674   1170         @ Also wiki page
   675   1171       }else{
   676   1172         @ Wiki page
   677   1173       }
   678         -    if( g.okHistory ){
   679         -      @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>]
   680         -    }else{
   681         -      @ [%h(zPagename)]
   682         -    }
   683         -    @ by
         1174  +    objType |= OBJTYPE_WIKI;
         1175  +    @ [%z(href("%R/wiki?name=%t",zPagename))%h(zPagename)</a>] by
   684   1176       hyperlink_to_user(zUser,zDate," on");
   685   1177       hyperlink_to_date(zDate,".");
   686   1178       nWiki++;
   687   1179       cnt++;
   688   1180       if( pDownloadName && blob_size(pDownloadName)==0 ){
   689         -      blob_appendf(pDownloadName, "%s.wiki", zPagename);
         1181  +      blob_appendf(pDownloadName, "%s.txt", zPagename);
   690   1182       }
   691   1183     }
   692   1184     db_finalize(&q);
   693   1185     if( nWiki==0 ){
   694   1186       db_prepare(&q,
   695         -      "SELECT datetime(mtime), user, comment, type, uuid"
         1187  +      "SELECT datetime(mtime), user, comment, type, uuid, tagid"
   696   1188         "  FROM event, blob"
   697   1189         " WHERE event.objid=%d"
   698   1190         "   AND blob.rid=%d",
   699   1191         rid, rid
   700   1192       );
   701   1193       while( db_step(&q)==SQLITE_ROW ){
   702   1194         const char *zDate = db_column_text(&q, 0);
................................................................................
   705   1197         const char *zType = db_column_text(&q, 3);
   706   1198         const char *zUuid = db_column_text(&q, 4);
   707   1199         if( cnt>0 ){
   708   1200           @ Also
   709   1201         }
   710   1202         if( zType[0]=='w' ){
   711   1203           @ Wiki edit
         1204  +        objType |= OBJTYPE_WIKI;
   712   1205         }else if( zType[0]=='t' ){
   713   1206           @ Ticket change
         1207  +        objType |= OBJTYPE_TICKET;
   714   1208         }else if( zType[0]=='c' ){
   715   1209           @ Manifest of check-in
         1210  +        objType |= OBJTYPE_CHECKIN;
         1211  +      }else if( zType[0]=='e' ){
         1212  +        @ Instance of event
         1213  +        objType |= OBJTYPE_EVENT;
         1214  +        hyperlink_to_event_tagid(db_column_int(&q, 5));
   716   1215         }else{
   717   1216           @ Control file referencing
   718   1217         }
   719         -      hyperlink_to_uuid(zUuid);
         1218  +      if( zType[0]!='e' ){
         1219  +        hyperlink_to_uuid(zUuid);
         1220  +      }
   720   1221         @ - %w(zCom) by
   721   1222         hyperlink_to_user(zUser,zDate," on");
   722   1223         hyperlink_to_date(zDate, ".");
   723   1224         if( pDownloadName && blob_size(pDownloadName)==0 ){
   724   1225           blob_appendf(pDownloadName, "%.10s.txt", zUuid);
   725   1226         }
   726   1227         cnt++;
   727   1228       }
   728   1229       db_finalize(&q);
   729   1230     }
   730         -  db_prepare(&q, 
         1231  +  db_prepare(&q,
   731   1232       "SELECT target, filename, datetime(mtime), user, src"
   732   1233       "  FROM attachment"
   733   1234       " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
   734   1235       " ORDER BY mtime DESC /*sort*/",
   735   1236       rid
   736   1237     );
   737   1238     while( db_step(&q)==SQLITE_ROW ){
................................................................................
   741   1242       const char *zUser = db_column_text(&q, 3);
   742   1243       /* const char *zSrc = db_column_text(&q, 4); */
   743   1244       if( cnt>0 ){
   744   1245         @ Also attachment "%h(zFilename)" to
   745   1246       }else{
   746   1247         @ Attachment "%h(zFilename)" to
   747   1248       }
         1249  +    objType |= OBJTYPE_ATTACHMENT;
   748   1250       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
   749         -      char zShort[20];
   750         -      memcpy(zShort, zTarget, 10);
   751         -      if( g.okHistory && g.okRdTkt ){
   752         -        @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
         1251  +      if( g.perm.Hyperlink && g.perm.RdTkt ){
         1252  +        @ ticket [%z(href("%R/tktview?name=%S",zTarget))%S(zTarget)</a>]
   753   1253         }else{
   754         -        @ ticket [%s(zShort)]
         1254  +        @ ticket [%S(zTarget)]
   755   1255         }
   756   1256       }else{
   757         -      if( g.okHistory && g.okRdWiki ){
   758         -        @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
         1257  +      if( g.perm.Hyperlink && g.perm.RdWiki ){
         1258  +        @ wiki page [%z(href("%R/wiki?name=%t",zTarget))%h(zTarget)</a>]
   759   1259         }else{
   760   1260           @ wiki page [%h(zTarget)]
   761   1261         }
   762   1262       }
   763   1263       @ added by
   764   1264       hyperlink_to_user(zUser,zDate," on");
   765   1265       hyperlink_to_date(zDate,".");
................................................................................
   770   1270     }
   771   1271     db_finalize(&q);
   772   1272     if( cnt==0 ){
   773   1273       @ Control artifact.
   774   1274       if( pDownloadName && blob_size(pDownloadName)==0 ){
   775   1275         blob_appendf(pDownloadName, "%.10s.txt", zUuid);
   776   1276       }
   777         -  }else if( linkToView && g.okHistory ){
   778         -    @ <a href="%s(g.zBaseURL)/artifact/%S(zUuid)">[view]</a>
         1277  +  }else if( linkToView && g.perm.Hyperlink ){
         1278  +    @ %z(href("%R/artifact/%S",zUuid))[view]</a>
   779   1279     }
         1280  +  return objType;
   780   1281   }
   781   1282   
   782   1283   
   783   1284   /*
   784   1285   ** WEBPAGE: fdiff
         1286  +** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN&regex=REGEX
   785   1287   **
   786         -** Two arguments, v1 and v2, are integers.  Show the difference between
   787         -** the two records.
         1288  +** Two arguments, v1 and v2, identify the files to be diffed.  Show the
         1289  +** difference between the two artifacts.  Show diff side by side unless sbs
         1290  +** is 0.  Generate plaintext if "patch" is present.
   788   1291   */
   789   1292   void diff_page(void){
   790         -  int v1 = name_to_rid(P("v1"));
   791         -  int v2 = name_to_rid(P("v2"));
   792         -  Blob c1, c2, diff;
         1293  +  int v1, v2;
         1294  +  int isPatch;
         1295  +  int sideBySide;
         1296  +  Blob c1, c2, diff, *pOut;
         1297  +  char *zV1;
         1298  +  char *zV2;
         1299  +  const char *zRe;
         1300  +  ReCompiled *pRe = 0;
         1301  +  u64 diffFlags;
         1302  +  const char *zStyle = "sbsdiff";
   793   1303   
   794   1304     login_check_credentials();
   795         -  if( !g.okRead ){ login_needed(); return; }
         1305  +  if( !g.perm.Read ){ login_needed(); return; }
         1306  +  v1 = name_to_rid_www("v1");
         1307  +  v2 = name_to_rid_www("v2");
   796   1308     if( v1==0 || v2==0 ) fossil_redirect_home();
   797         -  style_header("Diff");
   798         -  @ <h2>Differences From:</h2>
   799         -  @ <blockquote>
   800         -  object_description(v1, 1, 0);
   801         -  @ </blockquote>
   802         -  @ <h2>To:</h2>
   803         -  @ <blockquote>
   804         -  object_description(v2, 1, 0);
   805         -  @ </blockquote>
   806         -  @ <hr>
   807         -  @ <blockquote><pre>
         1309  +  sideBySide = atoi(PD("sbs","1"));
         1310  +  zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
         1311  +  zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
         1312  +  isPatch = P("patch")!=0;
         1313  +  if( isPatch ){
         1314  +    pOut = cgi_output_blob();
         1315  +    cgi_set_content_type("text/plain");
         1316  +    diffFlags = 4;
         1317  +  }else{
         1318  +    blob_zero(&diff);
         1319  +    pOut = &diff;
         1320  +    diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
         1321  +    if( sideBySide ){
         1322  +      zStyle = "sbsdiff";
         1323  +    }else{
         1324  +      diffFlags |= DIFF_LINENO;
         1325  +      zStyle = "udiff";
         1326  +    }
         1327  +  }
         1328  +  zRe = P("regex");
         1329  +  if( zRe ) re_compile(&pRe, zRe, 0);
   808   1330     content_get(v1, &c1);
   809   1331     content_get(v2, &c2);
   810         -  blob_zero(&diff);
   811         -  text_diff(&c1, &c2, &diff, 4);
         1332  +  text_diff(&c1, &c2, pOut, pRe, diffFlags);
   812   1333     blob_reset(&c1);
   813   1334     blob_reset(&c2);
   814         -  @ %h(blob_str(&diff))
   815         -  @ </pre></blockquote>
   816         -  blob_reset(&diff);
   817         -  style_footer();
         1335  +  if( !isPatch ){
         1336  +    style_header("Diff");
         1337  +    style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
         1338  +                          g.zTop, P("v1"), P("v2"));
         1339  +    if( !sideBySide ){
         1340  +      style_submenu_element("Side-by-side Diff", "sbsdiff",
         1341  +                            "%s/fdiff?v1=%T&v2=%T&sbs=1",
         1342  +                            g.zTop, P("v1"), P("v2"));
         1343  +    }else{
         1344  +      style_submenu_element("Unified Diff", "udiff",
         1345  +                            "%s/fdiff?v1=%T&v2=%T&sbs=0",
         1346  +                            g.zTop, P("v1"), P("v2"));
         1347  +    }
         1348  +
         1349  +    if( P("smhdr")!=0 ){
         1350  +      @ <h2>Differences From Artifact
         1351  +      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
         1352  +      @ %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>.</h2>
         1353  +    }else{
         1354  +      @ <h2>Differences From
         1355  +      @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
         1356  +      object_description(v1, 0, 0);
         1357  +      @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
         1358  +      object_description(v2, 0, 0);
         1359  +    }
         1360  +    if( pRe ){
         1361  +      @ <b>Only differences that match regular expression "%h(zRe)"
         1362  +      @ are shown.</b>
         1363  +    }
         1364  +    @ <hr />
         1365  +    @ <div class="%s(zStyle)">
         1366  +    @ %s(blob_str(&diff))
         1367  +    @ </div>
         1368  +    blob_reset(&diff);
         1369  +    style_footer();
         1370  +  }
   818   1371   }
   819   1372   
   820   1373   /*
   821   1374   ** WEBPAGE: raw
   822   1375   ** URL: /raw?name=ARTIFACTID&m=TYPE
   823         -** 
         1376  +**
   824   1377   ** Return the uninterpreted content of an artifact.  Used primarily
   825   1378   ** to view artifacts that are images.
   826   1379   */
   827   1380   void rawartifact_page(void){
   828   1381     int rid;
   829   1382     const char *zMime;
   830   1383     Blob content;
   831   1384   
   832   1385     rid = name_to_rid_www("name");
   833         -  zMime = PD("m","application/x-fossil-artifact");
   834   1386     login_check_credentials();
   835         -  if( !g.okRead ){ login_needed(); return; }
         1387  +  if( !g.perm.Read ){ login_needed(); return; }
   836   1388     if( rid==0 ) fossil_redirect_home();
         1389  +  zMime = P("m");
         1390  +  if( zMime==0 ){
         1391  +    char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
         1392  +                              " WHERE mlink.fid=%d"
         1393  +                              "   AND filename.fnid=mlink.fnid", rid);
         1394  +    if( !zFName ){
         1395  +      /* Look also at the attachment table */
         1396  +      zFName = db_text(0, "SELECT attachment.filename FROM attachment, blob"
         1397  +                          " WHERE blob.rid=%d"
         1398  +                          "   AND attachment.src=blob.uuid", rid);
         1399  +    }
         1400  +    if( zFName ) zMime = mimetype_from_name(zFName);
         1401  +    if( zMime==0 ) zMime = "application/x-fossil-artifact";
         1402  +  }
   837   1403     content_get(rid, &content);
   838   1404     cgi_set_content_type(zMime);
   839   1405     cgi_set_content(&content);
   840   1406   }
   841   1407   
   842   1408   /*
   843   1409   ** Render a hex dump of a file.
................................................................................
   853   1419     for(i=0; i<n; i+=16){
   854   1420       j = 0;
   855   1421       zLine[0] = zHex[(i>>24)&0xf];
   856   1422       zLine[1] = zHex[(i>>16)&0xf];
   857   1423       zLine[2] = zHex[(i>>8)&0xf];
   858   1424       zLine[3] = zHex[i&0xf];
   859   1425       zLine[4] = ':';
   860         -    sprintf(zLine, "%04x: ", i);
         1426  +    sqlite3_snprintf(sizeof(zLine), zLine, "%04x: ", i);
   861   1427       for(j=0; j<16; j++){
   862   1428         k = 5+j*3;
   863   1429         zLine[k] = ' ';
   864   1430         if( i+j<n ){
   865   1431           unsigned char c = x[i+j];
   866   1432           zLine[k+1] = zHex[c>>4];
   867   1433           zLine[k+2] = zHex[c&0xf];
................................................................................
   889   1455       @ %h(zLine)
   890   1456     }
   891   1457   }
   892   1458   
   893   1459   /*
   894   1460   ** WEBPAGE: hexdump
   895   1461   ** URL: /hexdump?name=ARTIFACTID
   896         -** 
         1462  +**
   897   1463   ** Show the complete content of a file identified by ARTIFACTID
   898   1464   ** as preformatted text.
   899   1465   */
   900   1466   void hexdump_page(void){
   901   1467     int rid;
   902   1468     Blob content;
   903   1469     Blob downloadName;
   904   1470     char *zUuid;
   905   1471   
   906   1472     rid = name_to_rid_www("name");
   907   1473     login_check_credentials();
   908         -  if( !g.okRead ){ login_needed(); return; }
         1474  +  if( !g.perm.Read ){ login_needed(); return; }
   909   1475     if( rid==0 ) fossil_redirect_home();
   910         -  if( g.okAdmin ){
         1476  +  if( g.perm.Admin ){
   911   1477       const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
   912   1478       if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
   913   1479         style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
   914   1480               g.zTop, zUuid);
   915   1481       }else{
   916   1482         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
   917   1483               g.zTop, zUuid);
   918   1484       }
   919   1485     }
   920   1486     style_header("Hex Artifact Content");
   921   1487     zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
   922         -  @ <h2>Artifact %s(zUuid):</h2>
   923         -  @ <blockquote>
         1488  +  if( g.perm.Setup ){
         1489  +    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
         1490  +  }else{
         1491  +    @ <h2>Artifact %s(zUuid):</h2>
         1492  +  }
   924   1493     blob_zero(&downloadName);
   925   1494     object_description(rid, 0, &downloadName);
   926         -  style_submenu_element("Download", "Download", 
         1495  +  style_submenu_element("Download", "Download",
   927   1496           "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
   928         -  @ </blockquote>
   929         -  @ <hr>
         1497  +  @ <hr />
   930   1498     content_get(rid, &content);
   931   1499     @ <blockquote><pre>
   932   1500     hexdump(&content);
   933   1501     @ </pre></blockquote>
   934   1502     style_footer();
   935   1503   }
   936   1504   
................................................................................
   938   1506   ** Look for "ci" and "filename" query parameters.  If found, try to
   939   1507   ** use them to extract the record ID of an artifact for the file.
   940   1508   */
   941   1509   int artifact_from_ci_and_filename(void){
   942   1510     const char *zFilename;
   943   1511     const char *zCI;
   944   1512     int cirid;
   945         -  Blob content;
   946         -  Manifest m;
   947         -  int i;
         1513  +  Manifest *pManifest;
         1514  +  ManifestFile *pFile;
   948   1515   
   949   1516     zCI = P("ci");
   950   1517     if( zCI==0 ) return 0;
   951   1518     zFilename = P("filename");
   952   1519     if( zFilename==0 ) return 0;
   953   1520     cirid = name_to_rid_www("ci");
   954         -  if( !content_get(cirid, &content) ) return 0;
   955         -  if( !manifest_parse(&m, &content) ) return 0;
   956         -  if( m.type!=CFTYPE_MANIFEST ) return 0;
   957         -  for(i=0; i<m.nFile; i++){
   958         -    if( strcmp(zFilename, m.aFile[i].zName)==0 ){
   959         -      return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
         1521  +  pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
         1522  +  if( pManifest==0 ) return 0;
         1523  +  manifest_file_rewind(pManifest);
         1524  +  while( (pFile = manifest_file_next(pManifest,0))!=0 ){
         1525  +    if( fossil_strcmp(zFilename, pFile->zName)==0 ){
         1526  +      int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
         1527  +      manifest_destroy(pManifest);
         1528  +      return rid;
   960   1529       }
   961   1530     }
   962   1531     return 0;
   963   1532   }
   964   1533   
         1534  +/*
         1535  +** The "z" argument is a string that contains the text of a source code
         1536  +** file.  This routine appends that text to the HTTP reply with line numbering.
         1537  +**
         1538  +** zLn is the ?ln= parameter for the HTTP query.  If there is an argument,
         1539  +** then highlight that line number and scroll to it once the page loads.
         1540  +** If there are two line numbers, highlight the range of lines.
         1541  +*/
         1542  +void output_text_with_line_numbers(
         1543  +  const char *z,
         1544  +  const char *zLn
         1545  +){
         1546  +  int iStart, iEnd;    /* Start and end of region to highlight */
         1547  +  int n = 0;           /* Current line number */
         1548  +  int i;               /* Loop index */
         1549  +  int iTop = 0;        /* Scroll so that this line is on top of screen. */
         1550  +
         1551  +  iStart = iEnd = atoi(zLn);
         1552  +  if( iStart>0 ){
         1553  +    for(i=0; fossil_isdigit(zLn[i]); i++){}
         1554  +    if( zLn[i]==',' || zLn[i]=='-' || zLn[i]=='.' ){
         1555  +      i++;
         1556  +      while( zLn[i]=='.' ){ i++; }
         1557  +      iEnd = atoi(&zLn[i]);
         1558  +    }
         1559  +    if( iEnd<iStart ) iEnd = iStart;
         1560  +    iTop = iStart - 15 + (iEnd-iStart)/4;
         1561  +    if( iTop>iStart - 2 ) iTop = iStart-2;
         1562  +  }
         1563  +  @ <pre>
         1564  +  while( z[0] ){
         1565  +    n++;
         1566  +    for(i=0; z[i] && z[i]!='\n'; i++){}
         1567  +    if( n==iTop ) cgi_append_content("<span id=\"topln\">", -1);
         1568  +    if( n==iStart ){
         1569  +      cgi_append_content("<div class=\"selectedText\">",-1);
         1570  +    }
         1571  +    cgi_printf("%6d  ", n);
         1572  +    if( i>0 ){
         1573  +      char *zHtml = htmlize(z, i);
         1574  +      cgi_append_content(zHtml, -1);
         1575  +      fossil_free(zHtml);
         1576  +    }
         1577  +    if( n==iStart-15 ) cgi_append_content("</span>", -1);
         1578  +    if( n==iEnd ) cgi_append_content("</div>", -1);
         1579  +    else cgi_append_content("\n", 1);
         1580  +    z += i;
         1581  +    if( z[0]=='\n' ) z++;
         1582  +  }
         1583  +  if( n<iEnd ) cgi_printf("</div>");
         1584  +  @ </pre>
         1585  +  if( iStart ){
         1586  +    @ <script>gebi('topln').scrollIntoView(true);</script>
         1587  +  }
         1588  +}
         1589  +
   965   1590   
   966   1591   /*
   967   1592   ** WEBPAGE: artifact
   968         -** URL: /artifact?name=ARTIFACTID
         1593  +** URL: /artifact/ARTIFACTID
   969   1594   ** URL: /artifact?ci=CHECKIN&filename=PATH
   970         -** 
         1595  +**
         1596  +** Additional query parameters:
         1597  +**
         1598  +**   ln              - show line numbers
         1599  +**   ln=N            - highlight line number N
         1600  +**   ln=M-N          - highlight lines M through N inclusive
         1601  +**
   971   1602   ** Show the complete content of a file identified by ARTIFACTID
   972   1603   ** as preformatted text.
   973   1604   */
   974   1605   void artifact_page(void){
   975   1606     int rid = 0;
   976   1607     Blob content;
   977   1608     const char *zMime;
   978   1609     Blob downloadName;
   979   1610     int renderAsWiki = 0;
   980   1611     int renderAsHtml = 0;
         1612  +  int objType;
         1613  +  int asText;
   981   1614     const char *zUuid;
         1615  +
   982   1616     if( P("ci") && P("filename") ){
   983   1617       rid = artifact_from_ci_and_filename();
   984   1618     }
   985   1619     if( rid==0 ){
   986   1620       rid = name_to_rid_www("name");
   987   1621     }
   988   1622   
   989   1623     login_check_credentials();
   990         -  if( !g.okRead ){ login_needed(); return; }
         1624  +  if( !g.perm.Read ){ login_needed(); return; }
   991   1625     if( rid==0 ) fossil_redirect_home();
   992         -  if( g.okAdmin ){
         1626  +  if( g.perm.Admin ){
   993   1627       const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
   994   1628       if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
   995   1629         style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
   996   1630               g.zTop, zUuid);
   997   1631       }else{
   998   1632         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
   999   1633               g.zTop, zUuid);
  1000   1634       }
  1001   1635     }
  1002   1636     style_header("Artifact Content");
  1003   1637     zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1004         -  @ <h2>Artifact %s(zUuid)</h2>
  1005         -  @ <blockquote>
         1638  +  if( g.perm.Setup ){
         1639  +    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
         1640  +  }else{
         1641  +    @ <h2>Artifact %s(zUuid):</h2>
         1642  +  }
  1006   1643     blob_zero(&downloadName);
  1007         -  object_description(rid, 0, &downloadName);
  1008         -  style_submenu_element("Download", "Download", 
  1009         -          "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
         1644  +  objType = object_description(rid, 0, &downloadName);
         1645  +  style_submenu_element("Download", "Download",
         1646  +          "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid);
         1647  +  if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
         1648  +    style_submenu_element("Checkins Using", "Checkins Using",
         1649  +          "%R/timeline?uf=%s&n=200",zUuid);
         1650  +  }
         1651  +  asText = P("txt")!=0;
  1010   1652     zMime = mimetype_from_name(blob_str(&downloadName));
  1011   1653     if( zMime ){
  1012         -    if( strcmp(zMime, "text/html")==0 ){
  1013         -      if( P("txt") ){
         1654  +    if( fossil_strcmp(zMime, "text/html")==0 ){
         1655  +      if( asText ){
  1014   1656           style_submenu_element("Html", "Html",
  1015         -                              "%s/artifact?name=%s", g.zTop, zUuid);
         1657  +                              "%s/artifact/%s", g.zTop, zUuid);
  1016   1658         }else{
  1017   1659           renderAsHtml = 1;
  1018   1660           style_submenu_element("Text", "Text",
  1019         -                              "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
         1661  +                              "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1020   1662         }
  1021         -    }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
  1022         -      if( P("txt") ){
         1663  +    }else if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
         1664  +      if( asText ){
  1023   1665           style_submenu_element("Wiki", "Wiki",
  1024         -                              "%s/artifact?name=%s", g.zTop, zUuid);
         1666  +                              "%s/artifact/%s", g.zTop, zUuid);
  1025   1667         }else{
  1026   1668           renderAsWiki = 1;
  1027   1669           style_submenu_element("Text", "Text",
  1028         -                              "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
         1670  +                              "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1029   1671         }
  1030   1672       }
  1031   1673     }
  1032         -  @ </blockquote>
  1033         -  @ <hr>
         1674  +  if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
         1675  +    style_submenu_element("Parsed", "Parsed", "%R/info/%s", zUuid);
         1676  +  }
         1677  +  @ <hr />
  1034   1678     content_get(rid, &content);
  1035   1679     if( renderAsWiki ){
  1036   1680       wiki_convert(&content, 0, 0);
  1037   1681     }else if( renderAsHtml ){
  1038   1682       @ <div>
         1683  +    blob_to_utf8_no_bom(&content, 0);
  1039   1684       cgi_append_content(blob_buffer(&content), blob_size(&content));
  1040   1685       @ </div>
  1041   1686     }else{
         1687  +    style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
  1042   1688       zMime = mimetype_from_content(&content);
  1043   1689       @ <blockquote>
  1044   1690       if( zMime==0 ){
  1045         -      @ <pre>
  1046         -      @ %h(blob_str(&content))
  1047         -      @ </pre>
  1048         -      style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
         1691  +      const char *zLn = P("ln");
         1692  +      const char *z;
         1693  +      blob_to_utf8_no_bom(&content, 0);
         1694  +      z = blob_str(&content);
         1695  +      if( zLn ){
         1696  +        output_text_with_line_numbers(z, zLn);
         1697  +      }else{
         1698  +        @ <pre>
         1699  +        @ %h(z)
         1700  +        @ </pre>
         1701  +      }
  1049   1702       }else if( strncmp(zMime, "image/", 6)==0 ){
  1050         -      @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img>
  1051         -      style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
         1703  +      @ <img src="%R/raw/%S(zUuid)?m=%s(zMime)" />
         1704  +      style_submenu_element("Image", "Image",
         1705  +                            "%R/raw/%S?m=%s", zUuid, zMime);
  1052   1706       }else{
  1053         -      @ <pre>
  1054         -      hexdump(&content);
  1055         -      @ </pre>
         1707  +      @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
  1056   1708       }
  1057   1709       @ </blockquote>
  1058   1710     }
  1059   1711     style_footer();
  1060         -}  
         1712  +}
  1061   1713   
  1062   1714   /*
  1063   1715   ** WEBPAGE: tinfo
  1064   1716   ** URL: /tinfo?name=ARTIFACTID
  1065   1717   **
  1066   1718   ** Show the details of a ticket change control artifact.
  1067   1719   */
  1068   1720   void tinfo_page(void){
  1069   1721     int rid;
  1070         -  Blob content;
  1071   1722     char *zDate;
  1072   1723     const char *zUuid;
  1073         -  char zTktName[20];
  1074         -  Manifest m;
         1724  +  char zTktName[UUID_SIZE+1];
         1725  +  Manifest *pTktChng;
         1726  +  int modPending;
         1727  +  const char *zModAction;
  1075   1728   
  1076   1729     login_check_credentials();
  1077         -  if( !g.okRdTkt ){ login_needed(); return; }
         1730  +  if( !g.perm.RdTkt ){ login_needed(); return; }
  1078   1731     rid = name_to_rid_www("name");
  1079   1732     if( rid==0 ){ fossil_redirect_home(); }
  1080   1733     zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1081         -  if( g.okAdmin ){
         1734  +  if( g.perm.Admin ){
  1082   1735       if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
  1083   1736         style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
  1084   1737               g.zTop, zUuid);
  1085   1738       }else{
  1086   1739         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
  1087   1740               g.zTop, zUuid);
  1088   1741       }
  1089   1742     }
  1090         -  content_get(rid, &content);
  1091         -  if( manifest_parse(&m, &content)==0 ){
  1092         -    fossil_redirect_home();
  1093         -  }
  1094         -  if( m.type!=CFTYPE_TICKET ){
  1095         -    fossil_redirect_home();
         1743  +  pTktChng = manifest_get(rid, CFTYPE_TICKET);
         1744  +  if( pTktChng==0 ) fossil_redirect_home();
         1745  +  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
         1746  +  memcpy(zTktName, pTktChng->zTicketUuid, UUID_SIZE+1);
         1747  +  if( g.perm.ModTkt && (zModAction = P("modaction"))!=0 ){
         1748  +    if( strcmp(zModAction,"delete")==0 ){
         1749  +      moderation_disapprove(rid);
         1750  +      cgi_redirectf("%R/tktview/%s", zTktName);
         1751  +      /*NOTREACHED*/
         1752  +    }
         1753  +    if( strcmp(zModAction,"approve")==0 ){
         1754  +      moderation_approve(rid);
         1755  +    }
  1096   1756     }
  1097   1757     style_header("Ticket Change Details");
  1098         -  zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
  1099         -  memcpy(zTktName, m.zTicketUuid, 10);
  1100         -  zTktName[10] = 0;
  1101         -  if( g.okHistory ){
  1102         -    @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
  1103         -    @
  1104         -    @ <p>By %h(m.zUser) on %s(zDate).  See also:
  1105         -    @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
  1106         -    @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
  1107         -    @ </p>
         1758  +  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
         1759  +  style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName);
         1760  +  style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName);
         1761  +  style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName);
         1762  +  if( P("plaintext") ){
         1763  +    style_submenu_element("Formatted", "Formatted", "%R/info/%S", zUuid);
  1108   1764     }else{
  1109         -    @ <h2>Changes to ticket %s(zTktName)</h2>
  1110         -    @
  1111         -    @ <p>By %h(m.zUser) on %s(zDate).
  1112         -    @ </p>
         1765  +    style_submenu_element("Plaintext", "Plaintext",
         1766  +                          "%R/info/%S?plaintext", zUuid);
  1113   1767     }
  1114         -  @
  1115         -  @ <ol>
         1768  +
         1769  +  @ <div class="section">Overview</div>
         1770  +  @ <p><table class="label-value">
         1771  +  @ <tr><th>Artifact&nbsp;ID:</th>
         1772  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
         1773  +  if( g.perm.Setup ){
         1774  +    @ (%d(rid))
         1775  +  }
         1776  +  modPending = moderation_pending(rid);
         1777  +  if( modPending ){
         1778  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
         1779  +  }
         1780  +  @ <tr><th>Ticket:</th>
         1781  +  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr>
         1782  +  @ <tr><th>Date:</th><td>
         1783  +  hyperlink_to_date(zDate, "</td></tr>");
         1784  +  @ <tr><th>User:</th><td>
         1785  +  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
         1786  +  @ </table>
  1116   1787     free(zDate);
  1117         -  ticket_output_change_artifact(&m);
  1118         -  manifest_clear(&m);
         1788  +  
         1789  +  if( g.perm.ModTkt && modPending ){
         1790  +    @ <div class="section">Moderation</div>
         1791  +    @ <blockquote>
         1792  +    @ <form method="POST" action="%R/tinfo/%s(zUuid)">
         1793  +    @ <label><input type="radio" name="modaction" value="delete">
         1794  +    @ Delete this change</label><br />
         1795  +    @ <label><input type="radio" name="modaction" value="approve">
         1796  +    @ Approve this change</label><br />
         1797  +    @ <input type="submit" value="Submit">
         1798  +    @ </form>
         1799  +    @ </blockquote>
         1800  +  }
         1801  +
         1802  +  @ <div class="section">Changes</div>
         1803  +  @ <p>
         1804  +  ticket_output_change_artifact(pTktChng, 0);
         1805  +  manifest_destroy(pTktChng);
  1119   1806     style_footer();
  1120   1807   }
  1121   1808   
  1122   1809   
  1123   1810   /*
  1124   1811   ** WEBPAGE: info
  1125   1812   ** URL: info/ARTIFACTID
  1126   1813   **
  1127   1814   ** The argument is a artifact ID which might be a baseline or a file or
  1128         -** a ticket changes or a wiki edit or something else. 
         1815  +** a ticket changes or a wiki edit or something else.
  1129   1816   **
  1130   1817   ** Figure out what the artifact ID is and jump to it.
  1131   1818   */
  1132   1819   void info_page(void){
  1133   1820     const char *zName;
  1134   1821     Blob uuid;
  1135   1822     int rid;
  1136         -  
         1823  +  int rc;
         1824  +
  1137   1825     zName = P("name");
  1138   1826     if( zName==0 ) fossil_redirect_home();
  1139         -  if( validate16(zName, strlen(zName))
  1140         -   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
  1141         -    tktview_page();
  1142         -    return;
         1827  +  if( validate16(zName, strlen(zName)) ){
         1828  +    if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
         1829  +      tktview_page();
         1830  +      return;
         1831  +    }
         1832  +    if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){
         1833  +      event_page();
         1834  +      return;
         1835  +    }
  1143   1836     }
  1144   1837     blob_set(&uuid, zName);
  1145         -  if( name_to_uuid(&uuid, 1) ){
  1146         -    fossil_redirect_home();
         1838  +  rc = name_to_uuid(&uuid, -1, "*");
         1839  +  if( rc==1 ){
         1840  +    style_header("No Such Object");
         1841  +    @ <p>No such object: %h(zName)</p>
         1842  +    style_footer();
         1843  +    return;
         1844  +  }else if( rc==2 ){
         1845  +    cgi_set_parameter("src","info");
         1846  +    ambiguous_page();
         1847  +    return;
  1147   1848     }
  1148   1849     zName = blob_str(&uuid);
  1149   1850     rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%s'", zName);
  1150   1851     if( rid==0 ){
  1151   1852       style_header("Broken Link");
  1152   1853       @ <p>No such object: %h(zName)</p>
  1153   1854       style_footer();
................................................................................
  1166   1867     }else
  1167   1868     if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){
  1168   1869       ci_page();
  1169   1870     }else
  1170   1871     if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
  1171   1872       ci_page();
  1172   1873     }else
         1874  +  if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
         1875  +    ainfo_page();
         1876  +  }else
  1173   1877     {
  1174   1878       artifact_page();
  1175   1879     }
  1176   1880   }
         1881  +
         1882  +/*
         1883  +** Generate HTML that will present the user with a selection of
         1884  +** potential background colors for timeline entries.
         1885  +*/
         1886  +void render_color_chooser(
         1887  +  int fPropagate,             /* Default value for propagation */
         1888  +  const char *zDefaultColor,  /* The current default color */
         1889  +  const char *zIdPropagate,   /* ID of form element checkbox.  NULL for none */
         1890  +  const char *zId,            /* The ID of the form element */
         1891  +  const char *zIdCustom       /* ID of text box for custom color */
         1892  +){
         1893  +  static const struct SampleColors {
         1894  +     const char *zCName;
         1895  +     const char *zColor;
         1896  +  } aColor[] = {
         1897  +     { "(none)",  "" },
         1898  +     { "#f2dcdc", 0 },
         1899  +     { "#bde5d6", 0 },
         1900  +     { "#a0a0a0", 0 },
         1901  +     { "#b0b0b0", 0 },
         1902  +     { "#c0c0c0", 0 },
         1903  +     { "#d0d0d0", 0 },
         1904  +     { "#e0e0e0", 0 },
         1905  +
         1906  +     { "#c0fff0", 0 },
         1907  +     { "#c0f0ff", 0 },
         1908  +     { "#d0c0ff", 0 },
         1909  +     { "#ffc0ff", 0 },
         1910  +     { "#ffc0d0", 0 },
         1911  +     { "#fff0c0", 0 },
         1912  +     { "#f0ffc0", 0 },
         1913  +     { "#c0ffc0", 0 },
         1914  +
         1915  +     { "#a8d3c0", 0 },
         1916  +     { "#a8c7d3", 0 },
         1917  +     { "#aaa8d3", 0 },
         1918  +     { "#cba8d3", 0 },
         1919  +     { "#d3a8bc", 0 },
         1920  +     { "#d3b5a8", 0 },
         1921  +     { "#d1d3a8", 0 },
         1922  +     { "#b1d3a8", 0 },
         1923  +
         1924  +     { "#8eb2a1", 0 },
         1925  +     { "#8ea7b2", 0 },
         1926  +     { "#8f8eb2", 0 },
         1927  +     { "#ab8eb2", 0 },
         1928  +     { "#b28e9e", 0 },
         1929  +     { "#b2988e", 0 },
         1930  +     { "#b0b28e", 0 },
         1931  +     { "#95b28e", 0 },
         1932  +
         1933  +     { "#80d6b0", 0 },
         1934  +     { "#80bbd6", 0 },
         1935  +     { "#8680d6", 0 },
         1936  +     { "#c680d6", 0 },
         1937  +     { "#d680a6", 0 },
         1938  +     { "#d69b80", 0 },
         1939  +     { "#d1d680", 0 },
         1940  +     { "#91d680", 0 },
         1941  +
         1942  +
         1943  +     { "custom",  "##" },
         1944  +  };
         1945  +  int nColor = sizeof(aColor)/sizeof(aColor[0])-1;
         1946  +  int stdClrFound = 0;
         1947  +  int i;
         1948  +
         1949  +  @ <table border="0" cellpadding="0" cellspacing="1">
         1950  +  if( zIdPropagate ){
         1951  +    @ <tr><td colspan="6" align="left"><label>
         1952  +    if( fPropagate ){
         1953  +      @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" />
         1954  +    }else{
         1955  +      @ <input type="checkbox" name="%s(zIdPropagate)" />
         1956  +    }
         1957  +    @ Propagate color to descendants</label></td></tr>
         1958  +  }
         1959  +  @ <tr>
         1960  +  for(i=0; i<nColor; i++){
         1961  +    const char *zClr = aColor[i].zColor;
         1962  +    if( zClr==0 ) zClr = aColor[i].zCName;
         1963  +    if( zClr[0] ){
         1964  +      @ <td style="background-color: %h(zClr);">
         1965  +    }else{
         1966  +      @ <td>
         1967  +    }
         1968  +    if( fossil_strcmp(zDefaultColor, zClr)==0 ){
         1969  +      @ <input type="radio" name="%s(zId)" value="%h(zClr)"
         1970  +      @  checked="checked" />
         1971  +      stdClrFound=1;
         1972  +    }else{
         1973  +      @ <input type="radio" name="%s(zId)" value="%h(zClr)" />
         1974  +    }
         1975  +    @ %h(aColor[i].zCName)</td>
         1976  +    if( (i%8)==7 && i+1<nColor ){
         1977  +      @ </tr><tr>
         1978  +    }
         1979  +  }
         1980  +  @ </tr><tr>
         1981  +  if (stdClrFound){
         1982  +    @ <td colspan="6">
         1983  +    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" />
         1984  +  }else{
         1985  +    @ <td style="background-color: %h(zDefaultColor);" colspan="6">
         1986  +    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
         1987  +    @  checked="checked" />
         1988  +  }
         1989  +  @ %h(aColor[i].zCName)&nbsp;
         1990  +  @ <input type="text" name="%s(zIdCustom)"
         1991  +  @  id="%s(zIdCustom)" class="checkinUserColor"
         1992  +  @  value="%h(stdClrFound?"":zDefaultColor)" />
         1993  +  @ </td>
         1994  +  @ </tr>
         1995  +  @ </table>
         1996  +}
         1997  +
         1998  +/*
         1999  +** Do a comment comparison.
         2000  +**
         2001  +** +  Leading and trailing whitespace are ignored.
         2002  +** +  \r\n characters compare equal to \n
         2003  +**
         2004  +** Return true if equal and false if not equal.
         2005  +*/
         2006  +static int comment_compare(const char *zA, const char *zB){
         2007  +  if( zA==0 ) zA = "";
         2008  +  if( zB==0 ) zB = "";
         2009  +  while( fossil_isspace(zA[0]) ) zA++;
         2010  +  while( fossil_isspace(zB[0]) ) zB++;
         2011  +  while( zA[0] && zB[0] ){
         2012  +    if( zA[0]==zB[0] ){ zA++; zB++; continue; }
         2013  +    if( zA[0]=='\r' && zA[1]=='\n' && zB[0]=='\n' ){
         2014  +      zA += 2;
         2015  +      zB++;
         2016  +      continue;
         2017  +    }
         2018  +    if( zB[0]=='\r' && zB[1]=='\n' && zA[0]=='\n' ){
         2019  +      zB += 2;
         2020  +      zA++;
         2021  +      continue;
         2022  +    }
         2023  +    return 0;
         2024  +  }
         2025  +  while( fossil_isspace(zB[0]) ) zB++;
         2026  +  while( fossil_isspace(zA[0]) ) zA++;
         2027  +  return zA[0]==0 && zB[0]==0;
         2028  +}
  1177   2029   
  1178   2030   /*
  1179   2031   ** WEBPAGE: ci_edit
  1180   2032   ** URL:  ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
  1181   2033   **
  1182   2034   ** Present a dialog for updating properties of a baseline:
  1183   2035   **
................................................................................
  1189   2041     int rid;
  1190   2042     const char *zComment;         /* Current comment on the check-in */
  1191   2043     const char *zNewComment;      /* Revised check-in comment */
  1192   2044     const char *zUser;            /* Current user for the check-in */
  1193   2045     const char *zNewUser;         /* Revised user */
  1194   2046     const char *zDate;            /* Current date of the check-in */
  1195   2047     const char *zNewDate;         /* Revised check-in date */
  1196         -  const char *zColor;       
         2048  +  const char *zColor;
  1197   2049     const char *zNewColor;
  1198   2050     const char *zNewTagFlag;
  1199   2051     const char *zNewTag;
  1200   2052     const char *zNewBrFlag;
  1201   2053     const char *zNewBranch;
  1202   2054     const char *zCloseFlag;
  1203         -  int fPropagateColor;
         2055  +  int fPropagateColor;          /* True if color propagates before edit */
         2056  +  int fNewPropagateColor;       /* True if color propagates after edit */
         2057  +  const char *zChngTime = 0;     /* Value of chngtime= query param, if any */
  1204   2058     char *zUuid;
  1205   2059     Blob comment;
  1206   2060     Stmt q;
  1207         -  static const struct SampleColors {
  1208         -     const char *zCName;
  1209         -     const char *zColor;
  1210         -  } aColor[] = {
  1211         -     { "(none)",  "" },
  1212         -     { "#f2dcdc", "#f2dcdc" },
  1213         -     { "#f0ffc0", "#f0ffc0" },
  1214         -     { "#bde5d6", "#bde5d6" },
  1215         -     { "#c0ffc0", "#c0ffc0" },
  1216         -     { "#c0fff0", "#c0fff0" },
  1217         -     { "#c0f0ff", "#c0f0ff" },
  1218         -     { "#d0c0ff", "#d0c0ff" },
  1219         -     { "#ffc0ff", "#ffc0ff" },
  1220         -     { "#ffc0d0", "#ffc0d0" },
  1221         -     { "#fff0c0", "#fff0c0" },
  1222         -     { "#c0c0c0", "#c0c0c0" },
  1223         -  };
  1224         -  int nColor = sizeof(aColor)/sizeof(aColor[0]);
  1225         -  int i;
  1226         -  
         2061  +
  1227   2062     login_check_credentials();
  1228         -  if( !g.okWrite ){ login_needed(); return; }
  1229         -  rid = name_to_rid(P("r"));
         2063  +  if( !g.perm.Write ){ login_needed(); return; }
         2064  +  rid = name_to_typed_rid(P("r"), "ci");
  1230   2065     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  1231   2066     zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
  1232   2067                           "  FROM event WHERE objid=%d", rid);
  1233   2068     if( zComment==0 ) fossil_redirect_home();
  1234   2069     if( P("cancel") ){
  1235   2070       cgi_redirectf("ci?name=%s", zUuid);
  1236   2071     }
         2072  +  if( g.perm.Setup ) zChngTime = P("chngtime");
  1237   2073     zNewComment = PD("c",zComment);
  1238   2074     zUser = db_text(0, "SELECT coalesce(euser,user)"
  1239   2075                        "  FROM event WHERE objid=%d", rid);
  1240   2076     if( zUser==0 ) fossil_redirect_home();
  1241         -  zNewUser = PD("u",zUser);
         2077  +  zNewUser = PDT("u",zUser);
  1242   2078     zDate = db_text(0, "SELECT datetime(mtime)"
  1243   2079                        "  FROM event WHERE objid=%d", rid);
  1244   2080     if( zDate==0 ) fossil_redirect_home();
  1245         -  zNewDate = PD("dt",zDate);
         2081  +  zNewDate = PDT("dt",zDate);
  1246   2082     zColor = db_text("", "SELECT bgcolor"
  1247   2083                           "  FROM event WHERE objid=%d", rid);
  1248         -  zNewColor = PD("clr",zColor);
  1249         -  fPropagateColor = P("pclr")!=0;
         2084  +  zNewColor = PDT("clr",zColor);
         2085  +  if( fossil_strcmp(zNewColor,"##")==0 ){
         2086  +    zNewColor = PT("clrcust");
         2087  +  }
         2088  +  fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
         2089  +                              " WHERE rid=%d AND tagid=%d",
         2090  +                              rid, TAG_BGCOLOR)==2;
         2091  +  fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
  1250   2092     zNewTagFlag = P("newtag") ? " checked" : "";
  1251         -  zNewTag = PD("tagname","");
         2093  +  zNewTag = PDT("tagname","");
  1252   2094     zNewBrFlag = P("newbr") ? " checked" : "";
  1253         -  zNewBranch = PD("brname","");
         2095  +  zNewBranch = PDT("brname","");
  1254   2096     zCloseFlag = P("close") ? " checked" : "";
  1255   2097     if( P("apply") ){
  1256   2098       Blob ctrl;
  1257         -    char *zDate;
         2099  +    char *zNow;
  1258   2100       int nChng = 0;
  1259   2101   
  1260   2102       login_verify_csrf_secret();
  1261   2103       blob_zero(&ctrl);
  1262         -    zDate = db_text(0, "SELECT datetime('now')");
  1263         -    zDate[10] = 'T';
  1264         -    blob_appendf(&ctrl, "D %s\n", zDate);
         2104  +    zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
         2105  +    blob_appendf(&ctrl, "D %s\n", zNow);
  1265   2106       db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
  1266         -    if( zNewColor[0] && strcmp(zColor,zNewColor)!=0 ){
         2107  +    if( zNewColor[0]
         2108  +     && (fPropagateColor!=fNewPropagateColor
         2109  +             || fossil_strcmp(zColor,zNewColor)!=0)
         2110  +    ){
  1267   2111         char *zPrefix = "+";
  1268         -      if( fPropagateColor ){
         2112  +      if( fNewPropagateColor ){
  1269   2113           zPrefix = "*";
  1270   2114         }
  1271   2115         db_multi_exec("REPLACE INTO newtags VALUES('bgcolor',%Q,%Q)",
  1272   2116                       zPrefix, zNewColor);
  1273   2117       }
  1274   2118       if( zNewColor[0]==0 && zColor[0]!=0 ){
  1275   2119         db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
  1276   2120       }
  1277         -    if( strcmp(zComment,zNewComment)!=0 ){
         2121  +    if( comment_compare(zComment,zNewComment)==0 ){
  1278   2122         db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
  1279   2123                       zNewComment);
  1280   2124       }
  1281         -    if( strcmp(zDate,zNewDate)!=0 ){
         2125  +    if( fossil_strcmp(zDate,zNewDate)!=0 ){
  1282   2126         db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
  1283   2127                       zNewDate);
  1284   2128       }
  1285         -    if( strcmp(zUser,zNewUser)!=0 ){
         2129  +    if( fossil_strcmp(zUser,zNewUser)!=0 ){
  1286   2130         db_multi_exec("REPLACE INTO newtags VALUES('user','+',%Q)", zNewUser);
  1287   2131       }
  1288   2132       db_prepare(&q,
  1289   2133          "SELECT tag.tagid, tagname FROM tagxref, tag"
  1290   2134          " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
  1291   2135          rid
  1292   2136       );
  1293   2137       while( db_step(&q)==SQLITE_ROW ){
  1294   2138         int tagid = db_column_int(&q, 0);
  1295   2139         const char *zTag = db_column_text(&q, 1);
  1296   2140         char zLabel[30];
  1297         -      sprintf(zLabel, "c%d", tagid);
         2141  +      sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
  1298   2142         if( P(zLabel) ){
  1299   2143           db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag);
  1300   2144         }
  1301   2145       }
  1302   2146       db_finalize(&q);
  1303   2147       if( zCloseFlag[0] ){
  1304   2148         db_multi_exec("REPLACE INTO newtags VALUES('closed','+',NULL)");
  1305   2149       }
  1306         -    if( zNewTagFlag[0] ){
         2150  +    if( zNewTagFlag[0] && zNewTag[0] ){
  1307   2151         db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','+',NULL)", zNewTag);
  1308   2152       }
  1309         -    if( zNewBrFlag[0] ){
         2153  +    if( zNewBrFlag[0] && zNewBranch[0] ){
  1310   2154         db_multi_exec(
  1311   2155           "REPLACE INTO newtags "
  1312   2156           " SELECT tagname, '-', NULL FROM tagxref, tag"
  1313   2157           "  WHERE tagxref.rid=%d AND tagtype==2"
  1314   2158           "    AND tagname GLOB 'sym-*'"
  1315   2159           "    AND tag.tagid=tagxref.tagid",
  1316   2160           rid
................................................................................
  1337   2181         int nrid;
  1338   2182         Blob cksum;
  1339   2183         blob_appendf(&ctrl, "U %F\n", g.zLogin);
  1340   2184         md5sum_blob(&ctrl, &cksum);
  1341   2185         blob_appendf(&ctrl, "Z %b\n", &cksum);
  1342   2186         db_begin_transaction();
  1343   2187         g.markPrivate = content_is_private(rid);
  1344         -      nrid = content_put(&ctrl, 0, 0);
         2188  +      nrid = content_put(&ctrl);
  1345   2189         manifest_crosslink(nrid, &ctrl);
         2190  +      assert( blob_is_reset(&ctrl) );
  1346   2191         db_end_transaction(0);
  1347   2192       }
  1348   2193       cgi_redirectf("ci?name=%s", zUuid);
  1349   2194     }
  1350   2195     blob_zero(&comment);
  1351   2196     blob_append(&comment, zNewComment, -1);
  1352   2197     zUuid[10] = 0;
................................................................................
  1354   2199     if( P("preview") ){
  1355   2200       Blob suffix;
  1356   2201       int nTag = 0;
  1357   2202       @ <b>Preview:</b>
  1358   2203       @ <blockquote>
  1359   2204       @ <table border=0>
  1360   2205       if( zNewColor && zNewColor[0] ){
  1361         -      @ <tr><td bgcolor="%h(zNewColor)">
         2206  +      @ <tr><td style="background-color: %h(zNewColor);">
  1362   2207       }else{
  1363   2208         @ <tr><td>
  1364   2209       }
  1365         -    wiki_convert(&comment, 0, WIKI_INLINE);
         2210  +    @ %w(blob_str(&comment))
  1366   2211       blob_zero(&suffix);
  1367   2212       blob_appendf(&suffix, "(user: %h", zNewUser);
  1368   2213       db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
  1369   2214                      " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
  1370   2215                      "   AND tagtype>1 AND tag.tagid=tagxref.tagid",
  1371   2216                      rid);
  1372   2217       while( db_step(&q)==SQLITE_ROW ){
................................................................................
  1378   2223         }
  1379   2224         nTag++;
  1380   2225       }
  1381   2226       db_finalize(&q);
  1382   2227       blob_appendf(&suffix, ")");
  1383   2228       @ %s(blob_str(&suffix))
  1384   2229       @ </td></tr></table>
         2230  +    if( zChngTime ){
         2231  +      @ <p>The timestamp on the tag used to make the changes above
         2232  +      @ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
         2233  +    }
  1385   2234       @ </blockquote>
  1386         -    @ <hr>
         2235  +    @ <hr />
  1387   2236       blob_reset(&suffix);
  1388   2237     }
  1389   2238     @ <p>Make changes to attributes of check-in
  1390         -  @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
  1391         -  @ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
         2239  +  @ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
         2240  +  form_begin(0, "%R/ci_edit");
  1392   2241     login_insert_csrf_secret();
  1393         -  @ <input type="hidden" name="r" value="%d(rid)">
         2242  +  @ <div><input type="hidden" name="r" value="%S(zUuid)" />
  1394   2243     @ <table border="0" cellspacing="10">
  1395   2244   
  1396   2245     @ <tr><td align="right" valign="top"><b>User:</b></td>
  1397   2246     @ <td valign="top">
  1398         -  @   <input type="text" name="u" size="20" value="%h(zNewUser)">
         2247  +  @   <input type="text" name="u" size="20" value="%h(zNewUser)" />
  1399   2248     @ </td></tr>
  1400   2249   
  1401   2250     @ <tr><td align="right" valign="top"><b>Comment:</b></td>
  1402   2251     @ <td valign="top">
  1403   2252     @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea>
  1404   2253     @ </td></tr>
  1405   2254   
  1406   2255     @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td>
  1407   2256     @ <td valign="top">
  1408         -  @   <input type="text" name="dt" size="20" value="%h(zNewDate)">
         2257  +  @   <input type="text" name="dt" size="20" value="%h(zNewDate)" />
  1409   2258     @ </td></tr>
         2259  +
         2260  +  if( zChngTime ){
         2261  +    @ <tr><td align="right" valign="top"><b>Timestamp of this change:</b></td>
         2262  +    @ <td valign="top">
         2263  +    @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)" />
         2264  +    @ </td></tr>
         2265  +  }
  1410   2266   
  1411   2267     @ <tr><td align="right" valign="top"><b>Background Color:</b></td>
  1412   2268     @ <td valign="top">
  1413         -  @ <table border=0 cellpadding=0 cellspacing=1>
  1414         -  @ <tr><td colspan="6" align="left">
  1415         -  if( fPropagateColor ){
  1416         -    @ <input type="checkbox" name="pclr" checked>
  1417         -  }else{
  1418         -    @ <input type="checkbox" name="pclr">
  1419         -  }
  1420         -  @ Propagate color to descendants</input></td></tr>
  1421         -  @ <tr>
  1422         -  for(i=0; i<nColor; i++){
  1423         -    if( aColor[i].zColor[0] ){
  1424         -      @ <td bgcolor="%h(aColor[i].zColor)">
  1425         -    }else{
  1426         -      @ <td>
  1427         -    }
  1428         -    if( strcmp(zNewColor, aColor[i].zColor)==0 ){
  1429         -      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
  1430         -    }else{
  1431         -      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)">
  1432         -    }
  1433         -    @ %h(aColor[i].zCName)</input></td>
  1434         -    if( (i%6)==5 && i+1<nColor ){
  1435         -      @ </tr><tr>
  1436         -    }
  1437         -  }
  1438         -  @ </tr>
  1439         -  @ </table>
         2269  +  render_color_chooser(fNewPropagateColor, zNewColor, "pclr", "clr", "clrcust");
  1440   2270     @ </td></tr>
  1441   2271   
  1442   2272     @ <tr><td align="right" valign="top"><b>Tags:</b></td>
  1443   2273     @ <td valign="top">
  1444         -  @ <input type="checkbox" name="newtag"%s(zNewTagFlag)>
  1445         -  @ Add the following new tag name to this check-in:
  1446         -  @ <input type="text" width="15" name="tagname" value="%h(zNewTag)">
         2274  +  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
         2275  +  @ Add the following new tag name to this check-in:</label>
         2276  +  @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)"
         2277  +  @ onkeyup="gebi('newtag').checked=!!this.value" />
  1447   2278     db_prepare(&q,
  1448   2279        "SELECT tag.tagid, tagname FROM tagxref, tag"
  1449   2280        " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
  1450   2281        " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
  1451         -     "               ELSE tagname END",
         2282  +     "               ELSE tagname END /*sort*/",
  1452   2283        rid
  1453   2284     );
  1454   2285     while( db_step(&q)==SQLITE_ROW ){
  1455   2286       int tagid = db_column_int(&q, 0);
  1456   2287       const char *zTagName = db_column_text(&q, 1);
  1457   2288       char zLabel[30];
  1458         -    sprintf(zLabel, "c%d", tagid);
         2289  +    sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
         2290  +    @ <br /><label>
  1459   2291       if( P(zLabel) ){
  1460         -      @ <br><input type="checkbox" name="c%d(tagid)" checked>
         2292  +      @ <input type="checkbox" name="c%d(tagid)" checked="checked" />
  1461   2293       }else{
  1462         -      @ <br><input type="checkbox" name="c%d(tagid)">
         2294  +      @ <input type="checkbox" name="c%d(tagid)" />
  1463   2295       }
  1464   2296       if( strncmp(zTagName, "sym-", 4)==0 ){
  1465         -      @ Cancel tag <b>%h(&zTagName[4])</b>
         2297  +      @ Cancel tag <b>%h(&zTagName[4])</b></label>
  1466   2298       }else{
  1467         -      @ Cancel special tag <b>%h(zTagName)</b>
         2299  +      @ Cancel special tag <b>%h(zTagName)</b></label>
  1468   2300       }
  1469   2301     }
  1470   2302     db_finalize(&q);
  1471   2303     @ </td></tr>
  1472   2304   
  1473   2305     @ <tr><td align="right" valign="top"><b>Branching:</b></td>
  1474   2306     @ <td valign="top">
  1475         -  @ <input type="checkbox" name="newbr"%s(zNewBrFlag)>
  1476         -  @ Make this check-in the start of a new branch named:
  1477         -  @ <input type="text" width="15" name="brname" value="%h(zNewBranch)">
         2307  +  @ <label><input id="newbr" type="checkbox" name="newbr"%s(zNewBrFlag) />
         2308  +  @ Make this check-in the start of a new branch named:</label>
         2309  +  @ <input type="text" style="width:15;" name="brname" value="%h(zNewBranch)"
         2310  +  @ onkeyup="gebi('newbr').checked=!!this.value" />
  1478   2311     @ </td></tr>
  1479   2312   
  1480   2313     if( is_a_leaf(rid)
  1481   2314      && !db_exists("SELECT 1 FROM tagxref "
  1482   2315                    " WHERE tagid=%d AND rid=%d AND tagtype>0",
  1483   2316                    TAG_CLOSED, rid)
  1484   2317     ){
  1485   2318       @ <tr><td align="right" valign="top"><b>Leaf Closure:</b></td>
  1486   2319       @ <td valign="top">
  1487         -    @ <input type="checkbox" name="close"%s(zCloseFlag)>
         2320  +    @ <label><input type="checkbox" name="close"%s(zCloseFlag) />
  1488   2321       @ Mark this leaf as "closed" so that it no longer appears on the
  1489         -    @ "leaves" page and is no longer labeled as a "<b>Leaf</b>".
         2322  +    @ "leaves" page and is no longer labeled as a "<b>Leaf</b>".</label>
  1490   2323       @ </td></tr>
  1491   2324     }
  1492   2325   
  1493   2326   
  1494   2327     @ <tr><td colspan="2">
  1495         -  @ <input type="submit" name="preview" value="Preview">
  1496         -  @ <input type="submit" name="apply" value="Apply Changes">
  1497         -  @ <input type="submit" name="cancel" value="Cancel">
         2328  +  @ <input type="submit" name="preview" value="Preview" />
         2329  +  @ <input type="submit" name="apply" value="Apply Changes" />
         2330  +  @ <input type="submit" name="cancel" value="Cancel" />
  1498   2331     @ </td></tr>
  1499   2332     @ </table>
  1500         -  @ </form>
         2333  +  @ </div></form>
  1501   2334     style_footer();
  1502   2335   }

Added src/json.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*******************************************************************************
           18  +**
           19  +** Code for the JSON API.
           20  +**
           21  +** For notes regarding the public JSON interface, please see:
           22  +**
           23  +** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit
           24  +**
           25  +**
           26  +** Notes for hackers...
           27  +**
           28  +** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
           29  +** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then
           30  +** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f().
           31  +** See the API docs for that typedef (below) for the semantics of the callbacks.
           32  +**
           33  +**
           34  +*/
           35  +#include "config.h"
           36  +#include "VERSION.h"
           37  +#include "json.h"
           38  +#include <assert.h>
           39  +#include <time.h>
           40  +
           41  +#if INTERFACE
           42  +#include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */
           43  +#endif
           44  +
           45  +const FossilJsonKeys_ FossilJsonKeys = {
           46  +  "anonymousSeed" /*anonymousSeed*/,
           47  +  "authToken"  /*authToken*/,
           48  +  "COMMAND_PATH" /*commandPath*/,
           49  +  "mtime" /*mtime*/,
           50  +  "payload" /* payload */,
           51  +  "requestId" /*requestId*/,
           52  +  "resultCode" /*resultCode*/,
           53  +  "resultText" /*resultText*/,
           54  +  "timestamp" /*timestamp*/
           55  +};
           56  +
           57  +
           58  +/* Timer code taken from sqlite3's shell.c, modified slightly.
           59  +   FIXME: move the timer into the fossil core API so that we can
           60  +   start the timer early on in the app init phase. Right now we're
           61  +   just timing the json ops themselves.
           62  +*/
           63  +#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
           64  +#include <sys/time.h>
           65  +#include <sys/resource.h>
           66  +
           67  +/* Saved resource information for the beginning of an operation */
           68  +static struct rusage sBegin;
           69  +
           70  +/*
           71  +** Begin timing an operation
           72  +*/
           73  +static void beginTimer(void){
           74  +  getrusage(RUSAGE_SELF, &sBegin);
           75  +}
           76  +
           77  +/* Return the difference of two time_structs in milliseconds */
           78  +static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
           79  +  return ((pEnd->tv_usec - pStart->tv_usec)*0.001 +
           80  +          (double)((pEnd->tv_sec - pStart->tv_sec)*1000.0));
           81  +}
           82  +
           83  +/*
           84  +** Print the timing results.
           85  +*/
           86  +static double endTimer(void){
           87  +  struct rusage sEnd;
           88  +  getrusage(RUSAGE_SELF, &sEnd);
           89  +#if 0
           90  +  printf("CPU Time: user %f sys %f\n",
           91  +         timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
           92  +         timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
           93  +#endif
           94  +  return timeDiff(&sBegin.ru_utime, &sEnd.ru_utime)
           95  +    + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime);
           96  +}
           97  +
           98  +#define BEGIN_TIMER beginTimer()
           99  +#define END_TIMER endTimer()
          100  +#define HAS_TIMER 1
          101  +
          102  +#elif (defined(_WIN32) || defined(WIN32))
          103  +
          104  +#include <windows.h>
          105  +
          106  +/* Saved resource information for the beginning of an operation */
          107  +static HANDLE hProcess;
          108  +static FILETIME ftKernelBegin;
          109  +static FILETIME ftUserBegin;
          110  +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
          111  +static GETPROCTIMES getProcessTimesAddr = NULL;
          112  +
          113  +/*
          114  +** Check to see if we have timer support.  Return 1 if necessary
          115  +** support found (or found previously).
          116  +*/
          117  +static int hasTimer(void){
          118  +  if( getProcessTimesAddr ){
          119  +    return 1;
          120  +  } else {
          121  +    /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions.
          122  +    ** See if the version we are running on has it, and if it does, save off
          123  +    ** a pointer to it and the current process handle.
          124  +    */
          125  +    hProcess = GetCurrentProcess();
          126  +    if( hProcess ){
          127  +      HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
          128  +      if( NULL != hinstLib ){
          129  +        getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
          130  +        if( NULL != getProcessTimesAddr ){
          131  +          return 1;
          132  +        }
          133  +        FreeLibrary(hinstLib);
          134  +      }
          135  +    }
          136  +  }
          137  +  return 0;
          138  +}
          139  +
          140  +/*
          141  +** Begin timing an operation
          142  +*/
          143  +static void beginTimer(void){
          144  +  if( getProcessTimesAddr ){
          145  +    FILETIME ftCreation, ftExit;
          146  +    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
          147  +  }
          148  +}
          149  +
          150  +/* Return the difference of two FILETIME structs in milliseconds */
          151  +static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
          152  +  sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
          153  +  sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
          154  +  return (double) ((i64End - i64Start) / 10000.0);
          155  +}
          156  +
          157  +/*
          158  +** Print the timing results.
          159  +*/
          160  +static double endTimer(void){
          161  +  if(getProcessTimesAddr){
          162  +    FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
          163  +    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
          164  +    return timeDiff(&ftUserBegin, &ftUserEnd) +
          165  +      timeDiff(&ftKernelBegin, &ftKernelEnd);
          166  +  }
          167  +  return 0.0;
          168  +}
          169  +
          170  +#define BEGIN_TIMER beginTimer()
          171  +#define END_TIMER endTimer()
          172  +#define HAS_TIMER hasTimer()
          173  +
          174  +#else
          175  +#define BEGIN_TIMER
          176  +#define END_TIMER 0.0
          177  +#define HAS_TIMER 0
          178  +#endif
          179  +
          180  +/*
          181  +** Returns true (non-0) if fossil appears to be running in JSON mode.
          182  +*/
          183  +char fossil_has_json(){
          184  +  return g.json.isJsonMode && (g.isHTTP || g.json.post.o);
          185  +}
          186  +
          187  +/*
          188  +** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
          189  +** (but planned) pages/commands.
          190  +*/
          191  +cson_value * json_page_nyi(){
          192  +  g.json.resultCode = FSL_JSON_E_NYI;
          193  +  return NULL;
          194  +}
          195  +
          196  +/*
          197  +** Given a FossilJsonCodes value, it returns a string suitable for use
          198  +** as a resultCode string. Returns some unspecified non-empty string
          199  +** if errCode is not one of the FossilJsonCodes values.
          200  +*/
          201  +static char const * json_err_cstr( int errCode ){
          202  +  switch( errCode ){
          203  +    case 0: return "Success";
          204  +#define C(X,V) case FSL_JSON_E_ ## X: return V
          205  +
          206  +    C(GENERIC,"Generic error");
          207  +    C(INVALID_REQUEST,"Invalid request");
          208  +    C(UNKNOWN_COMMAND,"Unknown command or subcommand");
          209  +    C(UNKNOWN,"Unknown error");
          210  +    C(TIMEOUT,"Timeout reached");
          211  +    C(ASSERT,"Assertion failed");
          212  +    C(ALLOC,"Resource allocation failed");
          213  +    C(NYI,"Not yet implemented");
          214  +    C(PANIC,"x");
          215  +    C(MANIFEST_READ_FAILED,"Reading artifact manifest failed");
          216  +    C(FILE_OPEN_FAILED,"Opening file failed");
          217  +
          218  +    C(AUTH,"Authentication error");
          219  +    C(MISSING_AUTH,"Authentication info missing from request");
          220  +    C(DENIED,"Access denied");
          221  +    C(WRONG_MODE,"Request not allowed (wrong operation mode)");
          222  +    C(LOGIN_FAILED,"Login failed");
          223  +    C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed");
          224  +    C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
          225  +    C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
          226  +    C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
          227  +
          228  +    C(USAGE,"Usage error");
          229  +    C(INVALID_ARGS,"Invalid argument(s)");
          230  +    C(MISSING_ARGS,"Missing argument(s)");
          231  +    C(AMBIGUOUS_UUID,"Resource identifier is ambiguous");
          232  +    C(UNRESOLVED_UUID,"Provided uuid/tag/branch could not be resolved");
          233  +    C(RESOURCE_ALREADY_EXISTS,"Resource already exists");
          234  +    C(RESOURCE_NOT_FOUND,"Resource not found");
          235  +
          236  +    C(DB,"Database error");
          237  +    C(STMT_PREP,"Statement preparation failed");
          238  +    C(STMT_BIND,"Statement parameter binding failed");
          239  +    C(STMT_EXEC,"Statement execution/stepping failed");
          240  +    C(DB_LOCKED,"Database is locked");
          241  +    C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt");
          242  +    C(DB_NOT_FOUND,"Fossil repository db file could not be found.");
          243  +    C(DB_NOT_VALID, "Fossil repository db file is not valid.");
          244  +#undef C
          245  +    default:
          246  +      return "Unknown Error";
          247  +  }
          248  +}
          249  +
          250  +/*
          251  +** Implements the cson_data_dest_f() interface and outputs the data to
          252  +** a fossil Blob object.  pState must be-a initialized (Blob*), to
          253  +** which n bytes of src will be appended.
          254  +**/
          255  +int cson_data_dest_Blob(void * pState, void const * src, unsigned int n){
          256  +  Blob * b = (Blob*)pState;
          257  +  blob_append( b, (char const *)src, (int)n ) /* will die on OOM */;
          258  +  return 0;
          259  +}
          260  +
          261  +/*
          262  +** Implements the cson_data_source_f() interface and reads input from
          263  +** a fossil Blob object. pState must be-a (Blob*) populated with JSON
          264  +** data.
          265  +*/
          266  +int cson_data_src_Blob(void * pState, void * dest, unsigned int * n){
          267  +  Blob * b = (Blob*)pState;
          268  +  *n = blob_read( b, dest, *n );
          269  +  return 0;
          270  +}
          271  +
          272  +/*
          273  +** Convenience wrapper around cson_output() which appends the output
          274  +** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
          275  +*/
          276  +int cson_output_Blob( cson_value const * pVal, Blob * pDest, cson_output_opt const * pOpt ){
          277  +  return cson_output( pVal, cson_data_dest_Blob,
          278  +                      pDest, pOpt ? pOpt : &g.json.outOpt );
          279  +}
          280  +
          281  +/*
          282  +** Convenience wrapper around cson_parse() which reads its input
          283  +** from pSrc. pSrc is rewound before parsing.
          284  +**
          285  +** pInfo may be NULL. If it is not NULL then it will contain details
          286  +** about the parse state when this function returns.
          287  +**
          288  +** On success a new JSON Object or Array is returned (owned by the
          289  +** caller). On error NULL is returned.
          290  +*/
          291  +cson_value * cson_parse_Blob( Blob * pSrc, cson_parse_info * pInfo ){
          292  +  cson_value * root = NULL;
          293  +  blob_rewind( pSrc );
          294  +  cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
          295  +  return root;
          296  +}
          297  +
          298  +/*
          299  +** Implements the cson_data_dest_f() interface and outputs the data to
          300  +** cgi_append_content(). pState is ignored.
          301  +**/
          302  +int cson_data_dest_cgi(void * pState, void const * src, unsigned int n){
          303  +  cgi_append_content( (char const *)src, (int)n );
          304  +  return 0;
          305  +}
          306  +
          307  +/*
          308  +** Returns a string in the form FOSSIL-XXXX, where XXXX is a
          309  +** left-zero-padded value of code. The returned buffer is static, and
          310  +** must be copied if needed for later.  The returned value will always
          311  +** be 11 bytes long (not including the trailing NUL byte).
          312  +**
          313  +** In practice we will only ever call this one time per app execution
          314  +** when constructing the JSON response envelope, so the static buffer
          315  +** "shouldn't" be a problem.
          316  +**
          317  +*/
          318  +char const * json_rc_cstr( int code ){
          319  +  enum { BufSize = 12 };
          320  +  static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
          321  +  assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
          322  +  sprintf(buf+7,"%04d", code);
          323  +  return buf;
          324  +}
          325  +
          326  +/*
          327  +** Adds v to the API-internal cleanup mechanism. key is ignored
          328  +** (legacy) but might be re-introduced and "should" be a unique
          329  +** (app-wide) value.  Failure to insert an item may be caused by any
          330  +** of the following:
          331  +**
          332  +** - Allocation error.
          333  +** - g.json.gc.a is NULL
          334  +** - key is NULL or empty.
          335  +**
          336  +** Returns 0 on success.
          337  +**
          338  +** Ownership of v is transfered to (or shared with) g.json.gc, and v
          339  +** will be valid until that object is cleaned up or some internal code
          340  +** incorrectly removes it from the gc (which we never do). If this
          341  +** function fails, it is fatal to the app (as it indicates an
          342  +** allocation error (more likely than not) or a serious internal error
          343  +** such as numeric overflow).
          344  +*/
          345  +void json_gc_add( char const * key, cson_value * v ){
          346  +  int const rc = cson_array_append( g.json.gc.a, v );
          347  +  assert( NULL != g.json.gc.a );
          348  +  if( 0 != rc ){
          349  +    cson_value_free( v );
          350  +  }
          351  +  assert( (0==rc) && "Adding item to GC failed." );
          352  +  if(0!=rc){
          353  +    fprintf(stderr,"%s: FATAL: alloc error.\n", g.argv[0])
          354  +        /* reminder: allocation error is the only reasonable cause of
          355  +           error here, provided g.json.gc.a and v are not NULL.
          356  +        */
          357  +        ;
          358  +    fossil_exit(1)/*not fossil_panic() b/c it might land us somewhere
          359  +                    where this function is called again.
          360  +                  */;
          361  +  }
          362  +}
          363  +
          364  +
          365  +/*
          366  +** Returns the value of json_rc_cstr(code) as a new JSON
          367  +** string, which is owned by the caller and must eventually
          368  +** be cson_value_free()d or transfered to a JSON container.
          369  +*/
          370  +cson_value * json_rc_string( int code ){
          371  +  return cson_value_new_string( json_rc_cstr(code), 11 );
          372  +}
          373  +
          374  +cson_value * json_new_string( char const * str ){
          375  +  return str
          376  +    ? cson_value_new_string(str,strlen(str))
          377  +    : NULL;
          378  +}
          379  +
          380  +cson_value * json_new_string_f( char const * fmt, ... ){
          381  +  cson_value * v;
          382  +  char * zStr;
          383  +  va_list vargs;
          384  +  va_start(vargs,fmt);
          385  +  zStr = vmprintf(fmt,vargs);
          386  +  va_end(vargs);
          387  +  v = cson_value_new_string(zStr, strlen(zStr));
          388  +  free(zStr);
          389  +  return v;
          390  +}
          391  +
          392  +cson_value * json_new_int( int v ){
          393  +  return cson_value_new_integer((cson_int_t)v);
          394  +}
          395  +
          396  +/*
          397  +** Gets a POST/POST.payload/GET/COOKIE/ENV value. The returned memory
          398  +** is owned by the g.json object (one of its sub-objects). Returns
          399  +** NULL if no match is found.
          400  +**
          401  +** ENV means the system environment (getenv()).
          402  +**
          403  +** Precedence: POST.payload, GET/COOKIE/non-JSON POST, JSON POST, ENV.
          404  +**
          405  +** FIXME: the precedence SHOULD be: GET, POST.payload, POST, COOKIE,
          406  +** ENV, but the amalgamation of the GET/POST vars makes it difficult
          407  +** for me to do that. Since fossil only uses one cookie, cookie
          408  +** precedence isn't a real/high-priority problem.
          409  +*/
          410  +cson_value * json_getenv( char const * zKey ){
          411  +  cson_value * rc;
          412  +  rc = g.json.reqPayload.o
          413  +    ? cson_object_get( g.json.reqPayload.o, zKey )
          414  +    : NULL;
          415  +  if(rc){
          416  +    return rc;
          417  +  }
          418  +  rc = cson_object_get( g.json.param.o, zKey );
          419  +  if( rc ){
          420  +    return rc;
          421  +  }
          422  +  rc = cson_object_get( g.json.post.o, zKey );
          423  +  if(rc){
          424  +    return rc;
          425  +  }else{
          426  +    char const * cv = PD(zKey,NULL);
          427  +    if(!cv && !g.isHTTP){
          428  +      /* reminder to self: in CLI mode i'd like to try
          429  +         find_option(zKey,NULL,XYZ) here, but we don't have a sane
          430  +         default for the XYZ param here.
          431  +      */
          432  +      cv = fossil_getenv(zKey);
          433  +    }
          434  +    if(cv){/*transform it to JSON for later use.*/
          435  +      /* use sscanf() to figure out if it's an int,
          436  +         and transform it to JSON int if it is.
          437  +
          438  +         FIXME: use strtol(), since it has more accurate
          439  +         error handling.
          440  +      */
          441  +      int intVal = -1;
          442  +      char endOfIntCheck;
          443  +      int const scanRc = sscanf(cv,"%d%c",&intVal, &endOfIntCheck)
          444  +        /* The %c bit there is to make sure that we don't accept 123x
          445  +          as a number. sscanf() returns the number of tokens
          446  +          successfully parsed, so an RC of 1 will be correct for "123"
          447  +          but "123x" will have RC==2.
          448  +
          449  +          But it appears to not be working that way :/
          450  +        */
          451  +        ;
          452  +      if(1==scanRc){
          453  +        json_setenv( zKey, cson_value_new_integer(intVal) );
          454  +      }else{
          455  +        rc = cson_value_new_string(cv,strlen(cv));
          456  +        json_setenv( zKey, rc );
          457  +      }
          458  +      return rc;
          459  +    }else{
          460  +      return NULL;
          461  +    }
          462  +  }
          463  +}
          464  +
          465  +/*
          466  +** Wrapper around json_getenv() which...
          467  +**
          468  +** If it finds a value and that value is-a JSON number or is a string
          469  +** which looks like an integer or is-a JSON bool/null then it is
          470  +** converted to an int. If none of those apply then dflt is returned.
          471  +*/
          472  +int json_getenv_int(char const * pKey, int dflt ){
          473  +  cson_value const * v = json_getenv(pKey);
          474  +  const cson_type_id type = v ? cson_value_type_id(v) : CSON_TYPE_UNDEF;
          475  +  switch(type){
          476  +    case CSON_TYPE_INTEGER:
          477  +    case CSON_TYPE_DOUBLE:
          478  +      return (int)cson_value_get_integer(v);
          479  +    case CSON_TYPE_STRING: {
          480  +      char const * sv = cson_string_cstr(cson_value_get_string(v));
          481  +      assert( (NULL!=sv) && "This is quite unexpected." );
          482  +      return sv ? atoi(sv) : dflt;
          483  +    }
          484  +    case CSON_TYPE_BOOL:
          485  +      return cson_value_get_bool(v) ? 1 : 0;
          486  +    case CSON_TYPE_NULL:
          487  +      return 0;
          488  +    default:
          489  +      return dflt;
          490  +  }
          491  +}
          492  +
          493  +
          494  +/*
          495  +** Wrapper around json_getenv() which tries to evaluate a payload/env
          496  +** value as a boolean. Uses mostly the same logic as
          497  +** json_getenv_int(), with the addition that string values which
          498  +** either start with a digit 1..9 or the letters [tTyY] are considered
          499  +** to be true. If this function cannot find a matching key/value then
          500  +** dflt is returned. e.g. if it finds the key but the value is-a
          501  +** Object then dftl is returned.
          502  +**
          503  +** If an entry is found, this function guarantees that it will return
          504  +** either 0 or 1, as opposed to "0 or non-zero", so that clients can
          505  +** pass a different value as dflt. Thus they can use, e.g. -1 to know
          506  +** whether or not this function found a match (it will return -1 in
          507  +** that case).
          508  +*/
          509  +char json_getenv_bool(char const * pKey, char dflt ){
          510  +  cson_value const * v = json_getenv(pKey);
          511  +  const cson_type_id type = v ? cson_value_type_id(v) : CSON_TYPE_UNDEF;
          512  +  switch(type){
          513  +    case CSON_TYPE_INTEGER:
          514  +    case CSON_TYPE_DOUBLE:
          515  +      return cson_value_get_integer(v) ? 1 : 0;
          516  +    case CSON_TYPE_STRING: {
          517  +      char const * sv = cson_string_cstr(cson_value_get_string(v));
          518  +      assert( (NULL!=sv) && "This is quite unexpected." );
          519  +      if(!*sv || ('0'==*sv)){
          520  +        return 0;
          521  +      }else{
          522  +        return ((('1'<=*sv) && ('9'>=*sv))
          523  +                || ('t'==*sv) || ('T'==*sv)
          524  +                || ('y'==*sv) || ('Y'==*sv)
          525  +                )
          526  +          ? 1 : 0;
          527  +      }
          528  +    }
          529  +    case CSON_TYPE_BOOL:
          530  +      return cson_value_get_bool(v) ? 1 : 0;
          531  +    case CSON_TYPE_NULL:
          532  +      return 0;
          533  +    default:
          534  +      return dflt;
          535  +  }
          536  +}
          537  +
          538  +/*
          539  +** Returns the string form of a json_getenv() value, but ONLY If that
          540  +** value is-a String. Non-strings are not converted to strings for
          541  +** this purpose. Returned memory is owned by g.json or fossil and is
          542  +** valid until end-of-app or the given key is replaced in fossil's
          543  +** internals via cgi_replace_parameter() and friends or json_setenv().
          544  +*/
          545  +char const * json_getenv_cstr( char const * zKey ){
          546  +  return cson_value_get_cstr( json_getenv(zKey) );
          547  +}
          548  +
          549  +/*
          550  +** An extended form of find_option() which tries to look up a combo
          551  +** GET/POST/CLI argument.
          552  +**
          553  +** zKey must be the GET/POST parameter key. zCLILong must be the "long
          554  +s** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
          555  +** the "short form" CLI flag (if NULL, no short form is used).
          556  +**
          557  +** If argPos is >=0 and no other match is found,
          558  +** json_command_arg(argPos) is also checked.
          559  +**
          560  +** On error (no match found) NULL is returned.
          561  +**
          562  +** This ONLY works for String JSON/GET/CLI values, not JSON
          563  +** booleans and whatnot.
          564  +*/
          565  +char const * json_find_option_cstr2(char const * zKey,
          566  +                                    char const * zCLILong,
          567  +                                    char const * zCLIShort,
          568  +                                    int argPos){
          569  +  char const * rc = NULL;
          570  +  assert(NULL != zKey);
          571  +  if(!g.isHTTP){
          572  +    rc = find_option(zCLILong ? zCLILong : zKey,
          573  +                     zCLIShort, 1);
          574  +  }
          575  +  if(!rc && fossil_has_json()){
          576  +    rc = json_getenv_cstr(zKey);
          577  +    if(!rc && zCLIShort){
          578  +      rc = cson_value_get_cstr( cson_object_get( g.json.param.o, zCLIShort) );
          579  +    }
          580  +  }
          581  +  if(!rc && (argPos>=0)){
          582  +    rc = json_command_arg((unsigned char)argPos);
          583  +  }
          584  +  return rc;
          585  +}
          586  +
          587  +/*
          588  +** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1).
          589  +*/
          590  +char const * json_find_option_cstr(char const * zKey,
          591  +                                   char const * zCLILong,
          592  +                                   char const * zCLIShort){
          593  +  return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1);
          594  +}
          595  +
          596  +/*
          597  +** The boolean equivalent of json_find_option_cstr().
          598  +** If the option is not found, dftl is returned.
          599  +*/
          600  +char json_find_option_bool(char const * zKey,
          601  +                           char const * zCLILong,
          602  +                           char const * zCLIShort,
          603  +                           char dflt ){
          604  +  char rc = -1;
          605  +  if(!g.isHTTP){
          606  +    if(NULL != find_option(zCLILong ? zCLILong : zKey,
          607  +                           zCLIShort, 0)){
          608  +      rc = 1;
          609  +    }
          610  +  }
          611  +  if((-1==rc) && fossil_has_json()){
          612  +    rc = json_getenv_bool(zKey,-1);
          613  +  }
          614  +  return (-1==rc) ? dflt : rc;
          615  +}
          616  +
          617  +/*
          618  +** The integer equivalent of json_find_option_cstr2().
          619  +** If the option is not found, dftl is returned.
          620  +*/
          621  +int json_find_option_int(char const * zKey,
          622  +                         char const * zCLILong,
          623  +                         char const * zCLIShort,
          624  +                         int dflt ){
          625  +  enum { Magic = -1947854832 };
          626  +  int rc = Magic;
          627  +  if(!g.isHTTP){
          628  +    /* FIXME: use strtol() for better error/dflt handling. */
          629  +    char const * opt = find_option(zCLILong ? zCLILong : zKey,
          630  +                                   zCLIShort, 1);
          631  +    if(NULL!=opt){
          632  +      rc = atoi(opt);
          633  +    }
          634  +  }
          635  +  if(Magic==rc){
          636  +    rc = json_getenv_int(zKey,Magic);
          637  +  }
          638  +  return (Magic==rc) ? dflt : rc;
          639  +}
          640  +
          641  +
          642  +/*
          643  +** Adds v to g.json.param.o using the given key. May cause any prior
          644  +** item with that key to be destroyed (depends on current reference
          645  +** count for that value). On success, transfers (or shares) ownership
          646  +** of v to (or with) g.json.param.o. On error ownership of v is not
          647  +** modified.
          648  +*/
          649  +int json_setenv( char const * zKey, cson_value * v ){
          650  +  return cson_object_set( g.json.param.o, zKey, v );
          651  +}
          652  +
          653  +/*
          654  +** Guesses a RESPONSE Content-Type value based (primarily) on the
          655  +** HTTP_ACCEPT header.
          656  +**
          657  +** It will try to figure out if the client can support
          658  +** application/json or application/javascript, and will fall back to
          659  +** text/plain if it cannot figure out anything more specific.
          660  +**
          661  +** Returned memory is static and immutable, but if the environment
          662  +** changes after calling this then subsequent calls to this function
          663  +** might return different (also static/immutable) values.
          664  +*/
          665  +char const * json_guess_content_type(){
          666  +  char const * cset;
          667  +  char doUtf8;
          668  +  cset = PD("HTTP_ACCEPT_CHARSET",NULL);
          669  +  doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
          670  +    ? 1 : 0;
          671  +  if( g.json.jsonp ){
          672  +    return doUtf8
          673  +      ? "application/javascript; charset=utf-8"
          674  +      : "application/javascript";
          675  +  }else{
          676  +    /*
          677  +      Content-type
          678  +
          679  +      If the browser does not sent an ACCEPT for application/json
          680  +      then we fall back to text/plain.
          681  +    */
          682  +    char const * cstr;
          683  +    cstr = PD("HTTP_ACCEPT",NULL);
          684  +    if( NULL == cstr ){
          685  +      return doUtf8
          686  +        ? "application/json; charset=utf-8"
          687  +        : "application/json";
          688  +    }else{
          689  +      if( strstr( cstr, "application/json" )
          690  +          || strstr( cstr, "*/*" ) ){
          691  +        return doUtf8
          692  +          ? "application/json; charset=utf-8"
          693  +          : "application/json";
          694  +      }else{
          695  +        return "text/plain";
          696  +      }
          697  +    }
          698  +  }
          699  +}
          700  +
          701  +/*
          702  +** Sends pResponse to the output stream as the response object.  This
          703  +** function does no validation of pResponse except to assert() that it
          704  +** is not NULL. The caller is responsible for ensuring that it meets
          705  +** API response envelope conventions.
          706  +**
          707  +** In CLI mode pResponse is sent to stdout immediately. In HTTP
          708  +** mode pResponse replaces any current CGI content but cgi_reply()
          709  +** is not called to flush the output.
          710  +**
          711  +** If g.json.jsonp is not NULL then the content type is set to
          712  +** application/javascript and the output is wrapped in a jsonp
          713  +** wrapper.
          714  +*/
          715  +void json_send_response( cson_value const * pResponse ){
          716  +  assert( NULL != pResponse );
          717  +  if( g.isHTTP ){
          718  +    cgi_reset_content();
          719  +    if( g.json.jsonp ){
          720  +      cgi_printf("%s(",g.json.jsonp);
          721  +    }
          722  +    cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
          723  +    if( g.json.jsonp ){
          724  +      cgi_append_content(")",1);
          725  +    }
          726  +  }else{/*CLI mode*/
          727  +    if( g.json.jsonp ){
          728  +      fprintf(stdout,"%s(",g.json.jsonp);
          729  +    }
          730  +    cson_output_FILE( pResponse, stdout, &g.json.outOpt );
          731  +    if( g.json.jsonp ){
          732  +      fwrite(")\n", 2, 1, stdout);
          733  +    }
          734  +  }
          735  +}
          736  +
          737  +/*
          738  +** Returns the current request's JSON authentication token, or NULL if
          739  +** none is found. The token's memory is owned by (or shared with)
          740  +** g.json.
          741  +**
          742  +** If an auth token is found in the GET/POST request data then fossil
          743  +** is given that data for use in authentication for this
          744  +** session. i.e. the GET/POST data overrides fossil's authentication
          745  +** cookie value (if any) and also works with clients which do not
          746  +** support cookies.
          747  +**
          748  +** Must be called once before login_check_credentials() is called or
          749  +** we will not be able to replace fossil's internal idea of the auth
          750  +** info in time (and future changes to that state may cause unexpected
          751  +** results).
          752  +**
          753  +** The result of this call are cached for future calls.
          754  +*/
          755  +cson_value * json_auth_token(){
          756  +  if( !g.json.authToken ){
          757  +    /* Try to get an authorization token from GET parameter, POSTed
          758  +       JSON, or fossil cookie (in that order). */
          759  +    g.json.authToken = json_getenv(FossilJsonKeys.authToken);
          760  +    if(g.json.authToken
          761  +       && cson_value_is_string(g.json.authToken)
          762  +       && !PD(login_cookie_name(),NULL)){
          763  +      /* tell fossil to use this login info.
          764  +
          765  +      FIXME?: because the JSON bits don't carry around
          766  +      login_cookie_name(), there is(?) a potential(?) login hijacking
          767  +      window here. We may need to change the JSON auth token to be in
          768  +      the form: login_cookie_name()=...
          769  +
          770  +      Then again, the hardened cookie value helps ensure that
          771  +      only a proper key/value match is valid.
          772  +      */
          773  +      cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) );
          774  +    }else if( g.isHTTP ){
          775  +      /* try fossil's conventional cookie. */
          776  +      /* Reminder: chicken/egg scenario regarding db access in CLI
          777  +         mode because login_cookie_name() needs the db. CLI
          778  +         mode does not use any authentication, so we don't need
          779  +         to support it here.
          780  +      */
          781  +      char const * zCookie = P(login_cookie_name());
          782  +      if( zCookie && *zCookie ){
          783  +        /* Transfer fossil's cookie to JSON for downstream convenience... */
          784  +        cson_value * v = cson_value_new_string(zCookie, strlen(zCookie));
          785  +        json_gc_add( FossilJsonKeys.authToken, v );
          786  +        g.json.authToken = v;
          787  +      }
          788  +    }
          789  +  }
          790  +  return g.json.authToken;
          791  +}
          792  +
          793  +/*
          794  +** If g.json.reqPayload.o is NULL then NULL is returned, else the
          795  +** given property is searched for in the request payload.  If found it
          796  +** is returned. The returned value is owned by (or shares ownership
          797  +** with) g.json, and must NOT be cson_value_free()'d by the
          798  +** caller.
          799  +*/
          800  +cson_value * json_req_payload_get(char const *pKey){
          801  +  return g.json.reqPayload.o
          802  +    ? cson_object_get(g.json.reqPayload.o,pKey)
          803  +    : NULL;
          804  +}
          805  +
          806  +/*
          807  +** Initializes some JSON bits which need to be initialized relatively
          808  +** early on. It should only be called from cgi_init() or
          809  +** json_cmd_top() (early on in those functions).
          810  +**
          811  +** Initializes g.json.gc and g.json.param. This code does not (and
          812  +** must not) rely on any of the fossil environment having been set
          813  +** up. e.g. it must not use cgi_parameter() and friends because this
          814  +** must be called before those data are initialized.
          815  +*/
          816  +void json_main_bootstrap(){
          817  +  cson_value * v;
          818  +  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );
          819  +
          820  +  /* g.json.gc is our "garbage collector" - where we put JSON values
          821  +     which need a long lifetime but don't have a logical parent to put
          822  +     them in.
          823  +  */
          824  +  v = cson_value_new_array();
          825  +  g.json.gc.v = v;
          826  +  g.json.gc.a = cson_value_get_array(v);
          827  +  cson_value_add_reference(v)
          828  +    /* Needed to allow us to include this value in other JSON
          829  +       containers without transferring ownership to those containers.
          830  +       All other persistent g.json.XXX.v values get appended to
          831  +       g.json.gc.a, and therefore already have a live reference
          832  +       for this purpose.
          833  +    */
          834  +    ;
          835  +
          836  +  /*
          837  +    g.json.param holds the JSONized counterpart of fossil's
          838  +    cgi_parameter_xxx() family of data. We store them as JSON, as
          839  +    opposed to using fossil's data directly, because we can retain
          840  +    full type information for data this way (as opposed to it always
          841  +    being of type string).
          842  +  */
          843  +  v = cson_value_new_object();
          844  +  g.json.param.v = v;
          845  +  g.json.param.o = cson_value_get_object(v);
          846  +  json_gc_add("$PARAMS", v);
          847  +}
          848  +
          849  +/*
          850  +** Appends a warning object to the (pending) JSON response.
          851  +**
          852  +** Code must be a FSL_JSON_W_xxx value from the FossilJsonCodes enum.
          853  +**
          854  +** A Warning object has this JSON structure:
          855  +**
          856  +** { "code":integer, "text":"string" }
          857  +**
          858  +** But the text part is optional.
          859  +**
          860  +** If msg is non-NULL and not empty then it is used as the "text"
          861  +** property's value. It is copied, and need not refer to static
          862  +** memory.
          863  +**
          864  +** CURRENTLY this code only allows a given warning code to be
          865  +** added one time, and elides subsequent warnings. The intention
          866  +** is to remove that burden from loops which produce warnings.
          867  +**
          868  +** FIXME: if msg is NULL then use a standard string for
          869  +** the given code. If !*msg then elide the "text" property,
          870  +** for consistency with how json_err() works.
          871  +*/
          872  +void json_warn( int code, char const * fmt, ... ){
          873  +  cson_object * obj = NULL;
          874  +  assert( (code>FSL_JSON_W_START)
          875  +          && (code<FSL_JSON_W_END)
          876  +          && "Invalid warning code.");
          877  +  if(!g.json.warnings){
          878  +    g.json.warnings = cson_new_array();
          879  +    assert((NULL != g.json.warnings) && "Alloc error.");
          880  +    json_gc_add("$WARNINGS",cson_array_value(g.json.warnings));
          881  +  }
          882  +  obj = cson_new_object();
          883  +  cson_array_append(g.json.warnings, cson_object_value(obj));
          884  +  cson_object_set(obj,"code",cson_value_new_integer(code));
          885  +  if(fmt && *fmt){
          886  +    /* FIXME: treat NULL fmt as standard warning message for
          887  +       the code, but we don't have those yet.
          888  +    */
          889  +    va_list vargs;
          890  +    char * msg;
          891  +    va_start(vargs,fmt);
          892  +    msg = vmprintf(fmt,vargs);
          893  +    va_end(vargs);
          894  +    cson_object_set(obj,"text", cson_value_new_string(msg,strlen(msg)));
          895  +    free(msg);
          896  +  }
          897  +}
          898  +
          899  +/*
          900  +** Splits zStr (which must not be NULL) into tokens separated by the
          901  +** given separator character. If doDeHttp is true then each element
          902  +** will be passed through dehttpize(), otherwise they are used
          903  +** as-is. Note that tokenization happens before dehttpize(),
          904  +** which is significant if the ENcoded tokens might contain the
          905  +** separator character.
          906  +**
          907  +** Each new element is appended to the given target array object,
          908  +** which must not be NULL and ownership of it is not changed by this
          909  +** call.
          910  +**
          911  +** On success, returns the number of tokens _encountered_. On error a
          912  +** NEGATIVE number is returned - its absolute value is the number of
          913  +** tokens encountered (i.e. it reveals which token in zStr was
          914  +** problematic).
          915  +**
          916  +** Achtung: leading and trailing whitespace of elements are elided.
          917  +**
          918  +** Achtung: empty elements will be skipped, meaning consecutive empty
          919  +** elements are collapsed.
          920  +*/
          921  +int json_string_split( char const * zStr,
          922  +                       char separator,
          923  +                       char doDeHttp,
          924  +                       cson_array * target ){
          925  +  char const * p = zStr /* current byte */;
          926  +  char const * head  /* current start-of-token */;
          927  +  unsigned int len = 0   /* current token's length */;
          928  +  int rc = 0   /* return code (number of added elements)*/;
          929  +  assert( zStr && target );
          930  +  while( fossil_isspace(*p) ){
          931  +    ++p;
          932  +  }
          933  +  head = p;
          934  +  for( ; ; ++p){
          935  +    if( !*p || (separator == *p) ){
          936  +      if( len ){/* append head..(head+len) as next array
          937  +                   element. */
          938  +        cson_value * part = NULL;
          939  +        char * zPart = NULL;
          940  +        ++rc;
          941  +        assert( head != p );
          942  +        zPart = (char*)fossil_malloc(len+1);
          943  +        memcpy(zPart, head, len);
          944  +        zPart[len] = 0;
          945  +        if(doDeHttp){
          946  +          dehttpize(zPart);
          947  +        }
          948  +        if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
          949  +          part = cson_value_new_string(zPart, strlen(zPart));
          950  +          if( 0 != cson_array_append( target, part ) ){
          951  +            cson_value_free(part);
          952  +            rc = -rc;
          953  +            break;
          954  +          }
          955  +        }else{
          956  +          assert(0 && "i didn't think this was possible!");
          957  +          fprintf(stderr,"%s:%d: My God! It's full of stars!\n",
          958  +                  __FILE__, __LINE__);
          959  +          fossil_exit(1)
          960  +            /* Not fossil_panic() b/c this code needs to be able to
          961  +              run before some of the fossil/json bits are initialized,
          962  +              and fossil_panic() calls into the JSON API.
          963  +            */
          964  +            ;
          965  +        }
          966  +        fossil_free(zPart);
          967  +        len = 0;
          968  +      }
          969  +      if( !*p ){
          970  +        break;
          971  +      }
          972  +      head = p+1;
          973  +      while( *head && fossil_isspace(*head) ){
          974  +        ++head;
          975  +        ++p;
          976  +      }
          977  +      if(!*head){
          978  +        break;
          979  +      }
          980  +      continue;
          981  +    }
          982  +    ++len;
          983  +  }
          984  +  return rc;
          985  +}
          986  +
          987  +/*
          988  +** Wrapper around json_string_split(), taking the same first 3
          989  +** parameters as this function, but returns the results as a JSON
          990  +** Array (if splitting produced tokens) or NULL (if splitting failed
          991  +** in any way or produced no tokens).
          992  +**
          993  +** The returned value is owned by the caller. If not NULL then it
          994  +** _will_ have a JSON type of Array.
          995  +*/
          996  +cson_value * json_string_split2( char const * zStr,
          997  +                                 char separator,
          998  +                                 char doDeHttp ){
          999  +  cson_array * a = cson_new_array();
         1000  +  int rc = json_string_split( zStr, separator, doDeHttp, a );
         1001  +  if( 0>=rc ){
         1002  +    cson_free_array(a);
         1003  +    a = NULL;
         1004  +  }
         1005  +  return a ? cson_array_value(a) : NULL;
         1006  +}
         1007  +
         1008  +
         1009  +/*
         1010  +** Performs some common initialization of JSON-related state.  Must be
         1011  +** called by the json_page_top() and json_cmd_top() dispatching
         1012  +** functions to set up the JSON stat used by the dispatched functions.
         1013  +**
         1014  +** Implicitly sets up the login information state in CGI mode, but
         1015  +** does not perform any permissions checking. It _might_ (haven't
         1016  +** tested this) die with an error if an auth cookie is malformed.
         1017  +**
         1018  +** This must be called by the top-level JSON command dispatching code
         1019  +** before they do any work.
         1020  +**
         1021  +** This must only be called once, or an assertion may be triggered.
         1022  +*/
         1023  +static void json_mode_bootstrap(){
         1024  +  static char once = 0  /* guard against multiple runs */;
         1025  +  char const * zPath = P("PATH_INFO");
         1026  +  assert( (0==once) && "json_mode_bootstrap() called too many times!");
         1027  +  if( once ){
         1028  +    return;
         1029  +  }else{
         1030  +    once = 1;
         1031  +  }
         1032  +  g.json.isJsonMode = 1;
         1033  +  g.json.resultCode = 0;
         1034  +  g.json.cmd.offset = -1;
         1035  +  g.json.jsonp = PD("jsonp",NULL)
         1036  +    /* FIXME: do some sanity checking on g.json.jsonp and ignore it
         1037  +       if it is not halfway reasonable.
         1038  +    */
         1039  +    ;
         1040  +  if( !g.isHTTP && g.fullHttpReply ){
         1041  +    /* workaround for server mode, so we see it as CGI mode. */
         1042  +    g.isHTTP = 1;
         1043  +  }
         1044  +
         1045  +  if(g.isHTTP){
         1046  +    cgi_set_content_type(json_guess_content_type())
         1047  +      /* reminder: must be done after g.json.jsonp is initialized */
         1048  +      ;
         1049  +#if 0
         1050  +    /* Calling this seems to trigger an SQLITE_MISUSE warning???
         1051  +       Maybe it's not legal to set the logger more than once?
         1052  +    */
         1053  +    sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0)
         1054  +        /* avoids debug messages on stderr in JSON mode */
         1055  +        ;
         1056  +#endif
         1057  +  }
         1058  +
         1059  +  g.json.cmd.v = cson_value_new_array();
         1060  +  g.json.cmd.a = cson_value_get_array(g.json.cmd.v);
         1061  +  json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v );
         1062  +  /*
         1063  +    The following if/else block translates the PATH_INFO path (in
         1064  +    CLI/server modes) or g.argv (CLI mode) into an internal list so
         1065  +    that we can simplify command dispatching later on.
         1066  +
         1067  +    Note that translating g.argv this way is overkill but allows us to
         1068  +    avoid CLI-only special-case handling in other code, e.g.
         1069  +    json_command_arg().
         1070  +  */
         1071  +  if( zPath ){/* Either CGI or server mode... */
         1072  +    /* Translate PATH_INFO into JSON array for later convenience. */
         1073  +    json_string_split(zPath, '/', 1, g.json.cmd.a);
         1074  +  }else{/* assume CLI mode */
         1075  +    int i;
         1076  +    char const * arg;
         1077  +    cson_value * part;
         1078  +    for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
         1079  +      arg = g.argv[i];
         1080  +      if( !arg || !*arg ){
         1081  +        continue;
         1082  +      }
         1083  +      if('-' == *arg){
         1084  +        /* workaround to skip CLI args so that
         1085  +           json_command_arg() does not see them.
         1086  +           This assumes that all arguments come LAST
         1087  +           on the command line.
         1088  +        */
         1089  +        break;
         1090  +      }
         1091  +      part = cson_value_new_string(arg,strlen(arg));
         1092  +      cson_array_append(g.json.cmd.a, part);
         1093  +    }
         1094  +  }
         1095  +
         1096  +  while(!g.isHTTP){
         1097  +    /* Simulate JSON POST data via input file.  Pedantic reminder:
         1098  +       error handling does not honor user-supplied g.json.outOpt
         1099  +       because outOpt cannot (generically) be configured until after
         1100  +       POST-reading is finished.
         1101  +    */
         1102  +    FILE * inFile = NULL;
         1103  +    char const * jfile = find_option("json-input",NULL,1);
         1104  +    if(!jfile || !*jfile){
         1105  +      break;
         1106  +    }
         1107  +    inFile = (0==strcmp("-",jfile))
         1108  +      ? stdin
         1109  +      : fopen(jfile,"rb");
         1110  +    if(!inFile){
         1111  +      g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED;
         1112  +      fossil_fatal("Could not open JSON file [%s].",jfile)
         1113  +        /* Does not return. */
         1114  +        ;
         1115  +    }
         1116  +    cgi_parse_POST_JSON(inFile, 0);
         1117  +    if( stdin != inFile ){
         1118  +      fclose(inFile);
         1119  +    }
         1120  +    break;
         1121  +  }
         1122  +
         1123  +  /* g.json.reqPayload exists only to simplify some of our access to
         1124  +     the request payload. We currently only use this in the context of
         1125  +     Object payloads, not Arrays, strings, etc.
         1126  +  */
         1127  +  g.json.reqPayload.v = cson_object_get( g.json.post.o, FossilJsonKeys.payload );
         1128  +  if( g.json.reqPayload.v ){
         1129  +    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
         1130  +        /* g.json.reqPayload.o may legally be NULL, which means only that
         1131  +           g.json.reqPayload.v is-not-a Object.
         1132  +        */;
         1133  +  }
         1134  +
         1135  +  /* Anything which needs json_getenv() and friends should go after
         1136  +     this point.
         1137  +  */
         1138  +
         1139  +  if(1 == cson_array_length_get(g.json.cmd.a)){
         1140  +    /* special case: if we're at the top path, look for
         1141  +       a "command" request arg which specifies which command
         1142  +       to run.
         1143  +    */
         1144  +    char const * cmd = json_getenv_cstr("command");
         1145  +    if(cmd){
         1146  +      json_string_split(cmd, '/', 0, g.json.cmd.a);
         1147  +      g.json.cmd.commandStr = cmd;
         1148  +    }
         1149  +  }
         1150  +
         1151  +
         1152  +  if(!g.json.jsonp){
         1153  +    g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
         1154  +  }
         1155  +  if(!g.isHTTP){
         1156  +    g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
         1157  +  }
         1158  +
         1159  +  {/* set up JSON output formatting options. */
         1160  +    int indent = -1;
         1161  +    indent = json_find_option_int("indent",NULL,"I",-1);
         1162  +    g.json.outOpt.indentation = (0>indent)
         1163  +      ? (g.isHTTP ? 0 : 1)
         1164  +      : (unsigned char)indent;
         1165  +    g.json.outOpt.addNewline = g.isHTTP
         1166  +      ? 0
         1167  +      : (g.json.jsonp ? 0 : 1);
         1168  +  }
         1169  +
         1170  +  if( g.isHTTP ){
         1171  +    json_auth_token()/* will copy our auth token, if any, to fossil's
         1172  +                        core, which we need before we call
         1173  +                        login_check_credentials(). */;
         1174  +    login_check_credentials()/* populates g.perm */;
         1175  +  }
         1176  +  else{
         1177  +    /* FIXME: we need an option which allows us to skip this. At least
         1178  +       one known command (/json/version) does not need an opened
         1179  +       repo. The problem here is we cannot know which functions need
         1180  +       it from here (because command dispatching hasn't yet happened)
         1181  +       and all other commands rely on the repo being opened before
         1182  +       they are called. A textbook example of lack of foresight :/.
         1183  +     */
         1184  +    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
         1185  +  }
         1186  +}
         1187  +
         1188  +/*
         1189  +** Returns the ndx'th item in the "command path", where index 0 is the
         1190  +** position of the "json" part of the path. Returns NULL if ndx is out
         1191  +** of bounds or there is no "json" path element.
         1192  +**
         1193  +** In CLI mode the "path" is the list of arguments (skipping argv[0]).
         1194  +** In server/CGI modes the path is taken from PATH_INFO.
         1195  +**
         1196  +** The returned bytes are owned by g.json.cmd.v and _may_ be
         1197  +** invalidated if that object is modified (depending on how it is
         1198  +** modified).
         1199  +**
         1200  +** Note that CLI options are not included in the command path. Use
         1201  +** find_option() to get those.
         1202  +**
         1203  +*/
         1204  +char const * json_command_arg(unsigned char ndx){
         1205  +  cson_array * ar = g.json.cmd.a;
         1206  +  assert((NULL!=ar) && "Internal error. Was json_mode_bootstrap() called?");
         1207  +  assert((g.argc>1) && "Internal error - we never should have gotten this far.");
         1208  +  if( g.json.cmd.offset < 0 ){
         1209  +    /* first-time setup. */
         1210  +    short i = 0;
         1211  +#define NEXT cson_string_cstr(          \
         1212  +                 cson_value_get_string( \
         1213  +                   cson_array_get(ar,i) \
         1214  +                   ))
         1215  +    char const * tok = NEXT;
         1216  +    while( tok ){
         1217  +      if( !g.isHTTP/*workaround for "abbreviated name" in CLI mode*/
         1218  +          ? (0==strcmp(g.argv[1],tok))
         1219  +          : (0==strncmp("json",tok,4))
         1220  +          ){
         1221  +        g.json.cmd.offset = i;
         1222  +        break;
         1223  +      }
         1224  +      ++i;
         1225  +      tok = NEXT;
         1226  +    }
         1227  +  }
         1228  +#undef NEXT
         1229  +  if(g.json.cmd.offset < 0){
         1230  +    return NULL;
         1231  +  }else{
         1232  +    ndx = g.json.cmd.offset + ndx;
         1233  +    return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd.offset + ndx )));
         1234  +  }
         1235  +}
         1236  +
         1237  +/* Returns the C-string form of json_auth_token(), or NULL
         1238  +** if json_auth_token() returns NULL.
         1239  +*/
         1240  +char const * json_auth_token_cstr(){
         1241  +  return cson_value_get_cstr( json_auth_token() );
         1242  +}
         1243  +
         1244  +/*
         1245  +** Returns the JsonPageDef with the given name, or NULL if no match is
         1246  +** found.
         1247  +**
         1248  +** head must be a pointer to an array of JsonPageDefs in which the
         1249  +** last entry has a NULL name.
         1250  +*/
         1251  +JsonPageDef const * json_handler_for_name( char const * name, JsonPageDef const * head ){
         1252  +  JsonPageDef const * pageDef = head;
         1253  +  assert( head != NULL );
         1254  +  if(name && *name) for( ; pageDef->name; ++pageDef ){
         1255  +    if( 0 == strcmp(name, pageDef->name) ){
         1256  +      return pageDef;
         1257  +    }
         1258  +  }
         1259  +  return NULL;
         1260  +}
         1261  +
         1262  +/*
         1263  +** Given a Fossil/JSON result code, this function "dumbs it down"
         1264  +** according to the current value of g.json.errorDetailParanoia. The
         1265  +** dumbed-down value is returned.
         1266  +**
         1267  +** This function assert()s that code is in the inclusive range 0 to
         1268  +** 9999.
         1269  +**
         1270  +** Note that WARNING codes (1..999) are never dumbed down.
         1271  +**
         1272  +*/
         1273  +static int json_dumbdown_rc( int code ){
         1274  +  if(!g.json.errorDetailParanoia
         1275  +     || !code
         1276  +     || ((code>=FSL_JSON_W_START) && (code<FSL_JSON_W_END))){
         1277  +    return code;
         1278  +  }else{
         1279  +    int modulo = 0;
         1280  +    assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
         1281  +    switch( g.json.errorDetailParanoia ){
         1282  +      case 1: modulo = 10; break;
         1283  +      case 2: modulo = 100; break;
         1284  +      case 3: modulo = 1000; break;
         1285  +      default: break;
         1286  +    }
         1287  +    if( modulo ) code = code - (code % modulo);
         1288  +    return code;
         1289  +  }
         1290  +}
         1291  +
         1292  +/*
         1293  +** Convenience routine which converts a Julian time value into a Unix
         1294  +** Epoch timestamp. Requires the db, so this cannot be used before the
         1295  +** repo is opened (will trigger a fatal error in db_xxx()). The returned
         1296  +** value is owned by the caller.
         1297  +*/
         1298  +cson_value * json_julian_to_timestamp(double j){
         1299  +  return cson_value_new_integer((cson_int_t)
         1300  +           db_int64(0,"SELECT cast(strftime('%%s',%lf) as int)",j)
         1301  +                                );
         1302  +}
         1303  +
         1304  +/*
         1305  +** Returns a timestamp value.
         1306  +*/
         1307  +cson_int_t json_timestamp(){
         1308  +  return (cson_int_t)time(0);
         1309  +}
         1310  +
         1311  +/*
         1312  +** Returns a new JSON value (owned by the caller) representing
         1313  +** a timestamp. If timeVal is < 0 then time(0) is used to fetch
         1314  +** the time, else timeVal is used as-is. The returned value is
         1315  +** owned by the caller.
         1316  +*/
         1317  +cson_value * json_new_timestamp(cson_int_t timeVal){
         1318  +  return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal);
         1319  +}
         1320  +
         1321  +/*
         1322  +** Internal helper for json_create_response(). Appends the first
         1323  +** g.json.dispatchDepth elements of g.json.cmd.a, skipping the first
         1324  +** one (the "json" part), to a string and returns that string value
         1325  +** (which is owned by the caller).
         1326  +*/
         1327  +static cson_value * json_response_command_path(){
         1328  +  if(!g.json.cmd.a){
         1329  +    return NULL;
         1330  +  }else{
         1331  +    cson_value * rc = NULL;
         1332  +    Blob path = empty_blob;
         1333  +    unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/
         1334  +    unsigned int i = 1;
         1335  +    for( ; i < aLen; ++i ){
         1336  +      char const * part = cson_string_cstr(cson_value_get_string(cson_array_get(g.json.cmd.a, i)));
         1337  +      if(!part){
         1338  +#if 1
         1339  +          fossil_warning("Iterating further than expected in %s.",
         1340  +                       __FILE__);
         1341  +#endif
         1342  +          break;
         1343  +      }
         1344  +      blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part);
         1345  +    }
         1346  +    rc = json_new_string((blob_size(&path)>0)
         1347  +                         ? blob_buffer(&path)
         1348  +                         : "")
         1349  +      /* reminder; we need an empty string instead of NULL
         1350  +         in this case, to avoid what outwardly looks like
         1351  +         (but is not) an allocation error in
         1352  +         json_create_response().
         1353  +      */
         1354  +      ;
         1355  +    blob_reset(&path);
         1356  +    return rc;
         1357  +  }
         1358  +}
         1359  +
         1360  +/*
         1361  +** Returns a JSON Object representation of the global g object.
         1362  +** Returned value is owned by the caller.
         1363  +*/
         1364  +cson_value * json_g_to_json(){
         1365  +  cson_object * o = NULL;
         1366  +  cson_object * pay = NULL;
         1367  +  pay = o = cson_new_object();
         1368  +
         1369  +#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
         1370  +#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) : cson_value_null())
         1371  +#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
         1372  +  VAL(capabilities, json_cap_value());
         1373  +  INT(g, argc);
         1374  +  INT(g, isConst);
         1375  +  INT(g, useAttach);
         1376  +  INT(g, configOpen);
         1377  +  INT(g, repositoryOpen);
         1378  +  INT(g, localOpen);
         1379  +  INT(g, minPrefix);
         1380  +  INT(g, fSqlTrace);
         1381  +  INT(g, fSqlStats);
         1382  +  INT(g, fSqlPrint);
         1383  +  INT(g, fQuiet);
         1384  +  INT(g, fHttpTrace);
         1385  +  INT(g, fSystemTrace);
         1386  +  INT(g, fNoSync);
         1387  +  INT(g, iErrPriority);
         1388  +  INT(g, sslNotAvailable);
         1389  +  INT(g, cgiOutput);
         1390  +  INT(g, xferPanic);
         1391  +  INT(g, fullHttpReply);
         1392  +  INT(g, xlinkClusterOnly);
         1393  +  INT(g, fTimeFormat);
         1394  +  INT(g, markPrivate);
         1395  +  INT(g, clockSkewSeen);
         1396  +  INT(g, isHTTP);
         1397  +  INT(g, urlIsFile);
         1398  +  INT(g, urlIsHttps);
         1399  +  INT(g, urlIsSsh);
         1400  +  INT(g, urlPort);
         1401  +  INT(g, urlDfltPort);
         1402  +  INT(g, dontKeepUrl);
         1403  +  INT(g, useLocalauth);
         1404  +  INT(g, noPswd);
         1405  +  INT(g, userUid);
         1406  +  INT(g, rcvid);
         1407  +  INT(g, okCsrf);
         1408  +  INT(g, thTrace);
         1409  +  INT(g, isHome);
         1410  +  INT(g, nAux);
         1411  +  INT(g, allowSymlinks);
         1412  +
         1413  +  CSTR(g, zMainDbType);
         1414  +  CSTR(g, zConfigDbType);
         1415  +  CSTR(g, zHome);
         1416  +  CSTR(g, zLocalRoot);
         1417  +  CSTR(g, zPath);
         1418  +  CSTR(g, zExtra);
         1419  +  CSTR(g, zBaseURL);
         1420  +  CSTR(g, zTop);
         1421  +  CSTR(g, zContentType);
         1422  +  CSTR(g, zErrMsg);
         1423  +  CSTR(g, urlName);
         1424  +  CSTR(g, urlHostname);
         1425  +  CSTR(g, urlProtocol);
         1426  +  CSTR(g, urlPath);
         1427  +  CSTR(g, urlUser);
         1428  +  CSTR(g, urlPasswd);
         1429  +  CSTR(g, urlCanonical);
         1430  +  CSTR(g, urlProxyAuth);
         1431  +  CSTR(g, urlFossil);
         1432  +  CSTR(g, zLogin);
         1433  +  CSTR(g, zSSLIdentity);
         1434  +  CSTR(g, zIpAddr);
         1435  +  CSTR(g, zNonce);
         1436  +  CSTR(g, zCsrfToken);
         1437  +
         1438  +  o = cson_new_object();
         1439  +  cson_object_set(pay, "json", cson_object_value(o) );
         1440  +  INT(g.json, isJsonMode);
         1441  +  INT(g.json, resultCode);
         1442  +  INT(g.json, errorDetailParanoia);
         1443  +  INT(g.json, dispatchDepth);
         1444  +  VAL(authToken, g.json.authToken);
         1445  +  CSTR(g.json, jsonp);
         1446  +  VAL(gc, g.json.gc.v);
         1447  +  VAL(cmd, g.json.cmd.v);
         1448  +  VAL(param, g.json.param.v);
         1449  +  VAL(POST, g.json.post.v);
         1450  +  VAL(warnings, cson_array_value(g.json.warnings));
         1451  +  /*cson_output_opt outOpt;*/
         1452  +
         1453  +
         1454  +#undef INT
         1455  +#undef CSTR
         1456  +#undef VAL
         1457  +  return cson_object_value(pay);
         1458  +}
         1459  +
         1460  +
         1461  +/*
         1462  +** Creates a new Fossil/JSON response envelope skeleton.  It is owned
         1463  +** by the caller, who must eventually free it using cson_value_free(),
         1464  +** or add it to a cson container to transfer ownership. Returns NULL
         1465  +** on error.
         1466  +**
         1467  +** If payload is not NULL and resultCode is 0 then it is set as the
         1468  +** "payload" property of the returned object.  If resultCode is 0 then
         1469  +** it defaults to g.json.resultCode. If resultCode is (or defaults to)
         1470  +** non-zero and payload is not NULL then this function calls
         1471  +** cson_value_free(payload) and does not insert the payload into the
         1472  +** response. In either case, ownership of payload is transfered to (or
         1473  +** shared with, if the caller holds a reference) this function.
         1474  +**
         1475  +** pMsg is an optional message string property (resultText) of the
         1476  +** response. If resultCode is non-0 and pMsg is NULL then
         1477  +** json_err_cstr() is used to get the error string. The caller may
         1478  +** provide his own or may use an empty string to suppress the
         1479  +** resultText property.
         1480  +**
         1481  +*/
         1482  +static cson_value * json_create_response( int resultCode,
         1483  +                                          char const * pMsg,
         1484  +                                          cson_value * payload){
         1485  +  cson_value * v = NULL;
         1486  +  cson_value * tmp = NULL;
         1487  +  cson_object * o = NULL;
         1488  +  int rc;
         1489  +  resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode);
         1490  +  o = cson_new_object();
         1491  +  v = cson_object_value(o);
         1492  +  if( ! o ) return NULL;
         1493  +#define SET(K) if(!tmp) goto cleanup; \
         1494  +  rc = cson_object_set( o, K, tmp ); \
         1495  +  if(rc) do{\
         1496  +    cson_value_free(tmp); \
         1497  +    tmp = NULL; \
         1498  +    goto cleanup; \
         1499  +  }while(0)
         1500  +
         1501  +
         1502  +  tmp = json_new_string(MANIFEST_UUID);
         1503  +  SET("fossil");
         1504  +
         1505  +  tmp = json_new_timestamp(-1);
         1506  +  SET(FossilJsonKeys.timestamp);
         1507  +
         1508  +  if( 0 != resultCode ){
         1509  +    if( ! pMsg ){
         1510  +      pMsg = g.zErrMsg;
         1511  +      if(!pMsg){
         1512  +        pMsg = json_err_cstr(resultCode);
         1513  +      }
         1514  +    }
         1515  +    tmp = json_new_string(json_rc_cstr(resultCode));
         1516  +    SET(FossilJsonKeys.resultCode);
         1517  +  }
         1518  +
         1519  +  if( pMsg && *pMsg ){
         1520  +    tmp = json_new_string(pMsg);
         1521  +    SET(FossilJsonKeys.resultText);
         1522  +  }
         1523  +
         1524  +  if(g.json.cmd.commandStr){
         1525  +    tmp = json_new_string(g.json.cmd.commandStr);
         1526  +  }else{
         1527  +    tmp = json_response_command_path();
         1528  +  }
         1529  +  SET("command");
         1530  +
         1531  +  tmp = json_getenv(FossilJsonKeys.requestId);
         1532  +  if( tmp ) cson_object_set( o, FossilJsonKeys.requestId, tmp );
         1533  +
         1534  +  if(0){/* these are only intended for my own testing...*/
         1535  +    if(g.json.cmd.v){
         1536  +      tmp = g.json.cmd.v;
         1537  +      SET("$commandPath");
         1538  +    }
         1539  +    if(g.json.param.v){
         1540  +      tmp = g.json.param.v;
         1541  +      SET("$params");
         1542  +    }
         1543  +    if(0){/*Only for debugging, add some info to the response.*/
         1544  +      tmp = cson_value_new_integer( g.json.cmd.offset );
         1545  +      cson_object_set( o, "cmd.offset", tmp );
         1546  +      cson_object_set( o, "isCGI", cson_value_new_bool( g.isHTTP ) );
         1547  +    }
         1548  +  }
         1549  +
         1550  +  if(HAS_TIMER){
         1551  +    /* This is, philosophically speaking, not quite the right place
         1552  +       for ending the timer, but this is the one function which all of
         1553  +       the JSON exit paths use (and they call it after processing,
         1554  +       just before they end).
         1555  +    */
         1556  +    double span;
         1557  +    span = END_TIMER;
         1558  +    /* I'm actually seeing sub-ms runtimes in some tests, but a time of
         1559  +       0 is "just wrong", so we'll bump that up to 1ms.
         1560  +    */
         1561  +    cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)((span>1.0)?span:1)));
         1562  +  }
         1563  +  if(g.json.warnings){
         1564  +    tmp = cson_array_value(g.json.warnings);
         1565  +    SET("warnings");
         1566  +  }
         1567  +
         1568  +  /* Only add the payload to SUCCESS responses. Else delete it. */
         1569  +  if( NULL != payload ){
         1570  +    if( resultCode ){
         1571  +      cson_value_free(payload);
         1572  +      payload = NULL;
         1573  +    }else{
         1574  +      tmp = payload;
         1575  +      SET(FossilJsonKeys.payload);
         1576  +    }
         1577  +  }
         1578  +
         1579  +  if(json_find_option_bool("debugFossilG","json-debug-g",NULL,0)
         1580  +     &&(g.perm.Admin||g.perm.Setup)){
         1581  +    tmp = json_g_to_json();
         1582  +    SET("g");
         1583  +  }
         1584  +
         1585  +#undef SET
         1586  +  goto ok;
         1587  +  cleanup:
         1588  +  cson_value_free(v);
         1589  +  v = NULL;
         1590  +  ok:
         1591  +  return v;
         1592  +}
         1593  +
         1594  +/*
         1595  +** Outputs a JSON error response to either the cgi_xxx() family of
         1596  +** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0
         1597  +** then g.json.resultCode is used. If that is also 0 then the "Unknown
         1598  +** Error" code is used.
         1599  +**
         1600  +** If g.isHTTP then the generated JSON error response object replaces
         1601  +** any currently buffered page output. Because the output goes via
         1602  +** the cgi_xxx() family of functions, this function inherits any
         1603  +** compression which fossil does for its output.
         1604  +**
         1605  +** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to
         1606  +** flush the output (and headers). Generally only do this if you are
         1607  +** about to call exit().
         1608  +**
         1609  +** If !g.isHTTP then alsoOutput is ignored and all output is sent to
         1610  +** stdout immediately.
         1611  +**
         1612  +** For generating the resultText property: if msg is not NULL then it
         1613  +** is used as-is. If it is NULL then g.zErrMsg is checked, and if that
         1614  +** is NULL then json_err_cstr(code) is used.
         1615  +*/
         1616  +void json_err( int code, char const * msg, char alsoOutput ){
         1617  +  int rc = code ? code : (g.json.resultCode
         1618  +                          ? g.json.resultCode
         1619  +                          : FSL_JSON_E_UNKNOWN);
         1620  +  cson_value * resp = NULL;
         1621  +  rc = json_dumbdown_rc(rc);
         1622  +  if( rc && !msg ){
         1623  +    msg = g.zErrMsg;
         1624  +    if(!msg){
         1625  +      msg = json_err_cstr(rc);
         1626  +    }
         1627  +  }
         1628  +  resp = json_create_response(rc, msg, NULL);
         1629  +  if(!resp){
         1630  +    /* about the only error case here is out-of-memory. DO NOT
         1631  +       call fossil_panic() here because that calls this function.
         1632  +    */
         1633  +    fprintf(stderr, "%s: Fatal error: could not allocate "
         1634  +            "response object.\n", g.argv[0]);
         1635  +    fossil_exit(1);
         1636  +  }
         1637  +  if( g.isHTTP ){
         1638  +    if(alsoOutput){
         1639  +      json_send_response(resp);
         1640  +    }else{
         1641  +      /* almost a duplicate of json_send_response() :( */
         1642  +      cgi_reset_content();
         1643  +      if( g.json.jsonp ){
         1644  +        cgi_printf("%s(",g.json.jsonp);
         1645  +      }
         1646  +      cson_output( resp, cson_data_dest_cgi, NULL, &g.json.outOpt );
         1647  +      if( g.json.jsonp ){
         1648  +        cgi_append_content(")",1);
         1649  +      }
         1650  +    }
         1651  +  }else{
         1652  +    json_send_response(resp);
         1653  +  }
         1654  +  cson_value_free(resp);
         1655  +}
         1656  +
         1657  +/*
         1658  +** Sets g.json.resultCode and g.zErrMsg, but does not report the error
         1659  +** via json_err(). Returns the code passed to it.
         1660  +**
         1661  +** code must be in the inclusive range 1000..9999.
         1662  +*/
         1663  +int json_set_err( int code, char const * fmt, ... ){
         1664  +  assert( (code>=1000) && (code<=9999) );
         1665  +  free(g.zErrMsg);
         1666  +  g.json.resultCode = code;
         1667  +  if(!fmt || !*fmt){
         1668  +    g.zErrMsg = mprintf("%s", json_err_cstr(code));
         1669  +  }else{
         1670  +    va_list vargs;
         1671  +    char * msg;
         1672  +    va_start(vargs,fmt);
         1673  +    msg = vmprintf(fmt, vargs);
         1674  +    va_end(vargs);
         1675  +    g.zErrMsg = msg;
         1676  +  }
         1677  +  return code;
         1678  +}
         1679  +
         1680  +/*
         1681  +** Iterates through a prepared SELECT statement and converts each row
         1682  +** to a JSON object. If pTgt is not NULL then this function will
         1683  +** append the results to pTgt and return cson_array_value(pTgt). If
         1684  +** pTgt is NULL then a new Array object is created and returned (owned
         1685  +** by the caller). Each row of pStmt is converted to an Object and
         1686  +** appended to the array. If the result set has no rows AND pTgt is
         1687  +** NULL then NULL (not an empty array) is returned.
         1688  +*/
         1689  +cson_value * json_stmt_to_array_of_obj(Stmt *pStmt,
         1690  +                                       cson_array * pTgt){
         1691  +  cson_array * a = pTgt;
         1692  +  char const * warnMsg = NULL;
         1693  +  cson_value * colNamesV = NULL;
         1694  +  cson_array * colNames = NULL;
         1695  +  while( (SQLITE_ROW==db_step(pStmt)) ){
         1696  +    cson_value * row = NULL;
         1697  +    if(!a){
         1698  +      a = cson_new_array();
         1699  +      assert(NULL!=a);
         1700  +    }
         1701  +    if(!colNames){
         1702  +      colNamesV = cson_sqlite3_column_names(pStmt->pStmt);
         1703  +      assert(NULL != colNamesV);
         1704  +      /*Why? cson_value_add_reference(colNamesV) avoids an ownership problem*/;
         1705  +      colNames = cson_value_get_array(colNamesV);
         1706  +      assert(NULL != colNames);
         1707  +    }
         1708  +    row = cson_sqlite3_row_to_object2(pStmt->pStmt, colNames);
         1709  +    if(!row && !warnMsg){
         1710  +      warnMsg = "Could not convert at least one result row to JSON.";
         1711  +      continue;
         1712  +    }
         1713  +    if( 0 != cson_array_append(a, row) ){
         1714  +      cson_value_free(row);
         1715  +      if(pTgt != a) {
         1716  +        cson_free_array(a);
         1717  +      }
         1718  +      assert( 0 && "Alloc error.");
         1719  +      return NULL;
         1720  +    }
         1721  +  }
         1722  +  cson_value_free(colNamesV);
         1723  +  if(warnMsg){
         1724  +    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
         1725  +  }
         1726  +  return cson_array_value(a);
         1727  +}
         1728  +
         1729  +/*
         1730  +** Works just like json_stmt_to_array_of_obj(), but each row in the
         1731  +** result set is represented as an Array of values instead of an
         1732  +** Object (key/value pairs). If pTgt is NULL and the statement
         1733  +** has no results then NULL is returned, not an empty array.
         1734  +*/
         1735  +cson_value * json_stmt_to_array_of_array(Stmt *pStmt,
         1736  +                                         cson_array * pTgt){
         1737  +  cson_array * a = pTgt;
         1738  +  while( (SQLITE_ROW==db_step(pStmt)) ){
         1739  +    cson_value * row = NULL;
         1740  +    if(!a){
         1741  +      a = cson_new_array();
         1742  +      assert(NULL!=a);
         1743  +    }
         1744  +    row = cson_sqlite3_row_to_array(pStmt->pStmt);
         1745  +    cson_array_append(a, row);
         1746  +  }
         1747  +  return cson_array_value(a);
         1748  +}
         1749  +
         1750  +cson_value * json_stmt_to_array_of_values(Stmt *pStmt,
         1751  +                                          int resultColumn,
         1752  +                                          cson_array * pTgt){
         1753  +  cson_array * a = pTgt;
         1754  +  while( (SQLITE_ROW==db_step(pStmt)) ){
         1755  +    cson_value * row = cson_sqlite3_column_to_value(pStmt->pStmt,
         1756  +                                                    resultColumn);
         1757  +    if(row){
         1758  +      if(!a){
         1759  +        a = cson_new_array();
         1760  +        assert(NULL!=a);
         1761  +      }
         1762  +      cson_array_append(a, row);
         1763  +    }
         1764  +  }
         1765  +  return cson_array_value(a);
         1766  +}
         1767  +
         1768  +/*
         1769  +** Executes the given SQL and runs it through
         1770  +** json_stmt_to_array_of_obj(), returning the result of that
         1771  +** function. If resetBlob is true then blob_reset(pSql) is called
         1772  +** after preparing the query.
         1773  +**
         1774  +** pTgt has the same semantics as described for
         1775  +** json_stmt_to_array_of_obj().
         1776  +**
         1777  +** FIXME: change this to take a (char const *) instead of a blob,
         1778  +** to simplify the trivial use-cases (which don't need a Blob).
         1779  +*/
         1780  +cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
         1781  +                                      char resetBlob){
         1782  +  Stmt q = empty_Stmt;
         1783  +  cson_value * pay = NULL;
         1784  +  assert( blob_size(pSql) > 0 );
         1785  +  db_prepare(&q, "%s", blob_str(pSql));
         1786  +  if(resetBlob){
         1787  +    blob_reset(pSql);
         1788  +  }
         1789  +  pay = json_stmt_to_array_of_obj(&q, pTgt);
         1790  +  db_finalize(&q);
         1791  +  return pay;
         1792  +
         1793  +}
         1794  +
         1795  +/*
         1796  +** If the given COMMIT rid has any tags associated with it, this
         1797  +** function returns a JSON Array containing the tag names (owned by
         1798  +** the caller), else it returns NULL.
         1799  +**
         1800  +** See info_tags_of_checkin() for more details (this is simply a JSON
         1801  +** wrapper for that function).
         1802  +**
         1803  +** If there are no tags then this function returns NULL, not an empty
         1804  +** Array.
         1805  +*/
         1806  +cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){
         1807  +  cson_value * v = NULL;
         1808  +  char * tags = info_tags_of_checkin(rid, propagatingOnly);
         1809  +  if(tags){
         1810  +    if(*tags){
         1811  +      v = json_string_split2(tags,',',0);
         1812  +    }
         1813  +    free(tags);
         1814  +  }
         1815  +  return v;
         1816  +}
         1817  +
         1818  +/*
         1819  + ** Returns a "new" value representing the boolean value of zVal
         1820  + ** (false if zVal is NULL). Note that cson does not really allocate
         1821  + ** any memory for boolean values, but they "should" (for reasons of
         1822  + ** style and philosophy) be cleaned up like any other values (but
         1823  + ** it's a no-op for bools).
         1824  + */
         1825  +cson_value * json_value_to_bool(cson_value const * zVal){
         1826  +  return cson_value_get_bool(zVal)
         1827  +    ? cson_value_true()
         1828  +    : cson_value_false();
         1829  +}
         1830  +
         1831  +/*
         1832  +** Impl of /json/resultCodes
         1833  +**
         1834  +*/
         1835  +cson_value * json_page_resultCodes(){
         1836  +    cson_array * list = cson_new_array();
         1837  +    cson_object * obj = NULL;
         1838  +    cson_string * kRC;
         1839  +    cson_string * kSymbol;
         1840  +    cson_string * kNumber;
         1841  +    cson_string * kDesc;
         1842  +    cson_array_reserve( list, 35 );
         1843  +    kRC = cson_new_string("resultCode",10);
         1844  +    kSymbol = cson_new_string("cSymbol",7);
         1845  +    kNumber = cson_new_string("number",6);
         1846  +    kDesc = cson_new_string("description",11);
         1847  +#define C(K) obj = cson_new_object(); \
         1848  +    cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); \
         1849  +    cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) );             \
         1850  +    cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) );        \
         1851  +    cson_object_set_s(obj, kDesc, json_new_string(json_err_cstr(FSL_JSON_E_##K))); \
         1852  +    cson_array_append( list, cson_object_value(obj) ); obj = NULL;
         1853  +
         1854  +    C(GENERIC);
         1855  +    C(INVALID_REQUEST);
         1856  +    C(UNKNOWN_COMMAND);
         1857  +    C(UNKNOWN);
         1858  +    C(TIMEOUT);
         1859  +    C(ASSERT);
         1860  +    C(ALLOC);
         1861  +    C(NYI);
         1862  +    C(PANIC);
         1863  +    C(MANIFEST_READ_FAILED);
         1864  +    C(FILE_OPEN_FAILED);
         1865  +
         1866  +    C(AUTH);
         1867  +    C(MISSING_AUTH);
         1868  +    C(DENIED);
         1869  +    C(WRONG_MODE);
         1870  +    C(LOGIN_FAILED);
         1871  +    C(LOGIN_FAILED_NOSEED);
         1872  +    C(LOGIN_FAILED_NONAME);
         1873  +    C(LOGIN_FAILED_NOPW);
         1874  +    C(LOGIN_FAILED_NOTFOUND);
         1875  +
         1876  +    C(USAGE);
         1877  +    C(INVALID_ARGS);
         1878  +    C(MISSING_ARGS);
         1879  +    C(AMBIGUOUS_UUID);
         1880  +    C(UNRESOLVED_UUID);
         1881  +    C(RESOURCE_ALREADY_EXISTS);
         1882  +    C(RESOURCE_NOT_FOUND);
         1883  +
         1884  +    C(DB);
         1885  +    C(STMT_PREP);
         1886  +    C(STMT_BIND);
         1887  +    C(STMT_EXEC);
         1888  +    C(DB_LOCKED);
         1889  +    C(DB_NEEDS_REBUILD);
         1890  +    C(DB_NOT_FOUND);
         1891  +    C(DB_NOT_VALID);
         1892  +#undef C
         1893  +    return cson_array_value(list);
         1894  +}
         1895  +
         1896  +
         1897  +/*
         1898  +** /json/version implementation.
         1899  +**
         1900  +** Returns the payload object (owned by the caller).
         1901  +*/
         1902  +cson_value * json_page_version(){
         1903  +  cson_value * jval = NULL;
         1904  +  cson_object * jobj = NULL;
         1905  +  jval = cson_value_new_object();
         1906  +  jobj = cson_value_get_object(jval);
         1907  +#define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X)))
         1908  +  FSET(MANIFEST_UUID,"manifestUuid");
         1909  +  FSET(MANIFEST_VERSION,"manifestVersion");
         1910  +  FSET(MANIFEST_DATE,"manifestDate");
         1911  +  FSET(MANIFEST_YEAR,"manifestYear");
         1912  +  FSET(RELEASE_VERSION,"releaseVersion");
         1913  +  cson_object_set( jobj, "releaseVersionNumber",
         1914  +                   cson_value_new_integer(RELEASE_VERSION_NUMBER) );
         1915  +  cson_object_set( jobj, "resultCodeParanoiaLevel",
         1916  +                   cson_value_new_integer(g.json.errorDetailParanoia) );
         1917  +  FSET(FOSSIL_JSON_API_VERSION, "jsonApiVersion" );
         1918  +#undef FSET
         1919  +  return jval;
         1920  +}
         1921  +
         1922  +
         1923  +/*
         1924  +** Returns the current user's capabilities string as a String value.
         1925  +** Returned value is owned by the caller, and will only be NULL if
         1926  +** g.userUid is invalid or an out of memory error. Or, it turns out,
         1927  +** in CLI mode (where there is no logged-in user).
         1928  +*/
         1929  +cson_value * json_cap_value(){
         1930  +  if(g.userUid<=0){
         1931  +    return NULL;
         1932  +  }else{
         1933  +    Stmt q = empty_Stmt;
         1934  +    cson_value * val = NULL;
         1935  +    db_prepare(&q, "SELECT cap FROM user WHERE uid=%d", g.userUid);
         1936  +    if( db_step(&q)==SQLITE_ROW ){
         1937  +      char const * str = (char const *)sqlite3_column_text(q.pStmt,0);
         1938  +      if( str ){
         1939  +        val = json_new_string(str);
         1940  +      }
         1941  +    }
         1942  +    db_finalize(&q);
         1943  +    return val;
         1944  +  }
         1945  +}
         1946  +
         1947  +/*
         1948  +** Implementation for /json/cap
         1949  +**
         1950  +** Returned object contains details about the "capabilities" of the
         1951  +** current user (what he may/may not do).
         1952  +**
         1953  +** This is primarily intended for debuggering, but may have
         1954  +** a use in client code. (?)
         1955  +*/
         1956  +cson_value * json_page_cap(){
         1957  +  cson_value * payload = cson_value_new_object();
         1958  +  cson_value * sub = cson_value_new_object();
         1959  +  Stmt q;
         1960  +  cson_object * obj = cson_value_get_object(payload);
         1961  +  db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
         1962  +  if( db_step(&q)==SQLITE_ROW ){
         1963  +    /* reminder: we don't use g.zLogin because it's 0 for the guest
         1964  +       user and the HTML UI appears to currently allow the name to be
         1965  +       changed (but doing so would break other code). */
         1966  +    char const * str = (char const *)sqlite3_column_text(q.pStmt,0);
         1967  +    if( str ){
         1968  +      cson_object_set( obj, "name",
         1969  +                       cson_value_new_string(str,strlen(str)) );
         1970  +    }
         1971  +    str = (char const *)sqlite3_column_text(q.pStmt,1);
         1972  +    if( str ){
         1973  +      cson_object_set( obj, "capabilities",
         1974  +                       cson_value_new_string(str,strlen(str)) );
         1975  +    }
         1976  +  }
         1977  +  db_finalize(&q);
         1978  +  cson_object_set( obj, "permissionFlags", sub );
         1979  +  obj = cson_value_get_object(sub);
         1980  +
         1981  +#define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X))
         1982  +  ADD(Setup,"setup");
         1983  +  ADD(Admin,"admin");
         1984  +  ADD(Delete,"delete");
         1985  +  ADD(Password,"password");
         1986  +  ADD(Query,"query"); /* don't think this one is actually used */
         1987  +  ADD(Write,"checkin");
         1988  +  ADD(Read,"checkout");
         1989  +  ADD(Hyperlink,"history");
         1990  +  ADD(Clone,"clone");
         1991  +  ADD(RdWiki,"readWiki");
         1992  +  ADD(NewWiki,"createWiki");
         1993  +  ADD(ApndWiki,"appendWiki");
         1994  +  ADD(WrWiki,"editWiki");
         1995  +  ADD(ModWiki,"moderateWiki");
         1996  +  ADD(RdTkt,"readTicket");
         1997  +  ADD(NewTkt,"createTicket");
         1998  +  ADD(ApndTkt,"appendTicket");
         1999  +  ADD(WrTkt,"editTicket");
         2000  +  ADD(ModTkt,"moderateTicket");
         2001  +  ADD(Attach,"attachFile");
         2002  +  ADD(TktFmt,"createTicketReport");
         2003  +  ADD(RdAddr,"readPrivate");
         2004  +  ADD(Zip,"zip");
         2005  +  ADD(Private,"xferPrivate");
         2006  +#undef ADD
         2007  +  return payload;
         2008  +}
         2009  +
         2010  +/*
         2011  +** Implementation of the /json/stat page/command.
         2012  +**
         2013  +*/
         2014  +cson_value * json_page_stat(){
         2015  +  i64 t, fsize;
         2016  +  int n, m;
         2017  +  int full;
         2018  +  const char *zDb;
         2019  +  enum { BufLen = 1000 };
         2020  +  char zBuf[BufLen];
         2021  +  cson_value * jv = NULL;
         2022  +  cson_object * jo = NULL;
         2023  +  cson_value * jv2 = NULL;
         2024  +  cson_object * jo2 = NULL;
         2025  +  char * zTmp = NULL;
         2026  +  if( !g.perm.Read ){
         2027  +    json_set_err(FSL_JSON_E_DENIED,
         2028  +                 "Requires 'o' permissions.");
         2029  +    return NULL;
         2030  +  }
         2031  +  full = json_find_option_bool("full",NULL,"f",0);
         2032  +#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
         2033  +
         2034  +  jv = cson_value_new_object();
         2035  +  jo = cson_value_get_object(jv);
         2036  +
         2037  +  zTmp = db_get("project-name",NULL);
         2038  +  cson_object_set(jo, "projectName", json_new_string(zTmp));
         2039  +  free(zTmp);
         2040  +  zTmp = db_get("project-description",NULL);
         2041  +  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
         2042  +  free(zTmp);
         2043  +  zTmp = NULL;
         2044  +  fsize = file_size(g.zRepositoryName);
         2045  +  cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize));
         2046  +
         2047  +  if(full){
         2048  +    n = db_int(0, "SELECT count(*) FROM blob");
         2049  +    m = db_int(0, "SELECT count(*) FROM delta");
         2050  +    cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
         2051  +    cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
         2052  +    if( n>0 ){
         2053  +      int a, b;
         2054  +      Stmt q;
         2055  +      db_prepare(&q, "SELECT total(size), avg(size), max(size)"
         2056  +                 " FROM blob WHERE size>0");
         2057  +      db_step(&q);
         2058  +      t = db_column_int64(&q, 0);
         2059  +      cson_object_set(jo, "uncompressedArtifactSize",
         2060  +                      cson_value_new_integer((cson_int_t)t));
         2061  +      cson_object_set(jo, "averageArtifactSize",
         2062  +                      cson_value_new_integer((cson_int_t)db_column_int(&q, 1)));
         2063  +      cson_object_set(jo, "maxArtifactSize",
         2064  +                      cson_value_new_integer((cson_int_t)db_column_int(&q, 2)));
         2065  +      db_finalize(&q);
         2066  +      if( t/fsize < 5 ){
         2067  +        b = 10;
         2068  +        fsize /= 10;
         2069  +      }else{
         2070  +        b = 1;
         2071  +      }
         2072  +      a = t/fsize;
         2073  +      sqlite3_snprintf(BufLen,zBuf, "%d:%d", a, b);
         2074  +      SETBUF(jo, "compressionRatio");
         2075  +    }
         2076  +    n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/");
         2077  +    cson_object_set(jo, "checkinCount", cson_value_new_integer((cson_int_t)n));
         2078  +    n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
         2079  +    cson_object_set(jo, "fileCount", cson_value_new_integer((cson_int_t)n));
         2080  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
         2081  +               " WHERE +tagname GLOB 'wiki-*'");
         2082  +    cson_object_set(jo, "wikiPageCount", cson_value_new_integer((cson_int_t)n));
         2083  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
         2084  +               " WHERE +tagname GLOB 'tkt-*'");
         2085  +    cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
         2086  +  }/*full*/
         2087  +  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
         2088  +                " + 0.99");
         2089  +  cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
         2090  +  cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
         2091  +  sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
         2092  +  SETBUF(jo, "projectCode");
         2093  +  cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
         2094  +
         2095  +  jv2 = cson_value_new_object();
         2096  +  jo2 = cson_value_get_object(jv2);
         2097  +  cson_object_set(jo, "sqlite", jv2);
         2098  +  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
         2099  +                   SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
         2100  +  SETBUF(jo2, "version");
         2101  +  zDb = db_name("repository");
         2102  +  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.page_count", zDb)));
         2103  +  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.page_size", zDb)));
         2104  +  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.freelist_count", zDb)));
         2105  +  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.encoding", zDb));
         2106  +  SETBUF(jo2, "encoding");
         2107  +  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
         2108  +  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
         2109  +  return jv;
         2110  +#undef SETBUF
         2111  +}
         2112  +
         2113  +
         2114  +
         2115  +
         2116  +/*
         2117  +** Creates a comma-separated list of command names
         2118  +** taken from zPages. zPages must be an array of objects
         2119  +** whose final entry MUST have a NULL name value or results
         2120  +** are undefined.
         2121  +**
         2122  +** The list is appended to pOut. The number of items (not bytes)
         2123  +** appended are returned.
         2124  +*/
         2125  +static int json_pagedefs_to_string(JsonPageDef const * zPages,
         2126  +                                   Blob * pOut){
         2127  +  int i = 0;
         2128  +  for( ; zPages->name; ++zPages, ++i ){
         2129  +    if(g.isHTTP && zPages->runMode < 0) continue;
         2130  +    else if(zPages->runMode > 0) continue;
         2131  +    blob_appendf(pOut, zPages->name, -1);
         2132  +    if((zPages+1)->name){
         2133  +      blob_append(pOut, ", ",2);
         2134  +    }
         2135  +  }
         2136  +  return i;
         2137  +}
         2138  +
         2139  +/*
         2140  +** Creates an error message using zErrPrefix and the given array of
         2141  +** JSON command definitions, and sets the g.json error state to
         2142  +** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then
         2143  +** some default is used (e.g. "Try one of: "). If it is "" then
         2144  +** no prefix is used.
         2145  +**
         2146  +** The intention is to provide the user (via the response.resultText)
         2147  +** a list of available commands/subcommands.
         2148  +**
         2149  +*/
         2150  +void json_dispatch_missing_args_err( JsonPageDef const * pCommands,
         2151  +                                     char const * zErrPrefix ){
         2152  +  Blob cmdNames = empty_blob;
         2153  +  blob_init(&cmdNames,NULL,0);
         2154  +  if( !zErrPrefix ) {
         2155  +    zErrPrefix = "Try one of: ";
         2156  +  }
         2157  +  blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) );
         2158  +  json_pagedefs_to_string(pCommands, &cmdNames);
         2159  +  json_set_err(FSL_JSON_E_MISSING_ARGS, "%s",
         2160  +               blob_str(&cmdNames));
         2161  +  blob_reset(&cmdNames);
         2162  +}
         2163  +
         2164  +cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
         2165  +  JsonPageDef const * def;
         2166  +  char const * cmd = json_command_arg(1+g.json.dispatchDepth);
         2167  +  assert( NULL != pages );
         2168  +  if( ! cmd ){
         2169  +    json_dispatch_missing_args_err(pages,
         2170  +                                   "No subcommand specified. "
         2171  +                                   "Try one of: ");
         2172  +    return NULL;
         2173  +  }
         2174  +  def = json_handler_for_name( cmd, pages );
         2175  +  if(!def){
         2176  +    json_set_err(FSL_JSON_E_UNKNOWN_COMMAND,
         2177  +                 "Unknown subcommand: %s", cmd);
         2178  +    return NULL;
         2179  +  }
         2180  +  else{
         2181  +    ++g.json.dispatchDepth;
         2182  +    return (*def->func)();
         2183  +  }
         2184  +}
         2185  +
         2186  +
         2187  +/*
         2188  +** Impl of /json/rebuild. Requires admin privileges.
         2189  +*/
         2190  +static cson_value * json_page_rebuild(){
         2191  +  if( !g.perm.Admin ){
         2192  +    json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges.");
         2193  +    return NULL;
         2194  +  }else{
         2195  +  /* Reminder: the db_xxx() ops "should" fail via the fossil core
         2196  +     error handlers, which will cause a JSON error and exit(). i.e. we
         2197  +     don't handle the errors here. TODO: confirm that all these db
         2198  +     routine fail gracefully in JSON mode.
         2199  +
         2200  +     On large repos (e.g. fossil's) this operation is likely to take
         2201  +     longer than the client timeout, which will cause it to fail (but
         2202  +     it's sqlite3, so it'll fail gracefully).
         2203  +  */
         2204  +    db_close(1);
         2205  +    db_open_repository(g.zRepositoryName);
         2206  +    db_begin_transaction();
         2207  +    rebuild_db(0, 0, 0);
         2208  +    db_end_transaction(0);
         2209  +    return NULL;
         2210  +  }
         2211  +}
         2212  +
         2213  +/*
         2214  +** Impl of /json/g. Requires admin/setup rights.
         2215  +*/
         2216  +static cson_value * json_page_g(){
         2217  +  if(!g.perm.Admin || !g.perm.Setup){
         2218  +    json_set_err(FSL_JSON_E_DENIED,
         2219  +                 "Requires 'a' or 's' privileges.");
         2220  +    return NULL;
         2221  +  }
         2222  +  return json_g_to_json();
         2223  +}
         2224  +
         2225  +/* Impl in json_login.c. */
         2226  +cson_value * json_page_anon_password();
         2227  +/* Impl in json_artifact.c. */
         2228  +cson_value * json_page_artifact();
         2229  +/* Impl in json_branch.c. */
         2230  +cson_value * json_page_branch();
         2231  +/* Impl in json_diff.c. */
         2232  +cson_value * json_page_diff();
         2233  +/* Impl in json_dir.c. */
         2234  +cson_value * json_page_dir();
         2235  +/* Impl in json_login.c. */
         2236  +cson_value * json_page_login();
         2237  +/* Impl in json_login.c. */
         2238  +cson_value * json_page_logout();
         2239  +/* Impl in json_query.c. */
         2240  +cson_value * json_page_query();
         2241  +/* Impl in json_report.c. */
         2242  +cson_value * json_page_report();
         2243  +/* Impl in json_tag.c. */
         2244  +cson_value * json_page_tag();
         2245  +/* Impl in json_user.c. */
         2246  +cson_value * json_page_user();
         2247  +/* Impl in json_config.c. */
         2248  +cson_value * json_page_config();
         2249  +/* Impl in json_finfo.c. */
         2250  +cson_value * json_page_finfo();
         2251  +
         2252  +/*
         2253  +** Mapping of names to JSON pages/commands.  Each name is a subpath of
         2254  +** /json (in CGI mode) or a subcommand of the json command in CLI mode
         2255  +*/
         2256  +static const JsonPageDef JsonPageDefs[] = {
         2257  +/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
         2258  +{"anonymousPassword", json_page_anon_password, 0},
         2259  +{"artifact", json_page_artifact, 0},
         2260  +{"branch", json_page_branch,0},
         2261  +{"cap", json_page_cap, 0},
         2262  +{"config", json_page_config, 0 },
         2263  +{"diff", json_page_diff, 0},
         2264  +{"dir", json_page_dir, 0},
         2265  +{"finfo", json_page_finfo, 0},
         2266  +{"g", json_page_g, 0},
         2267  +{"HAI",json_page_version,0},
         2268  +{"login",json_page_login,0},
         2269  +{"logout",json_page_logout,0},
         2270  +{"query",json_page_query,0},
         2271  +{"rebuild",json_page_rebuild,0},
         2272  +{"report", json_page_report, 0},
         2273  +{"resultCodes", json_page_resultCodes,0},
         2274  +{"stat",json_page_stat,0},
         2275  +{"tag", json_page_tag,0},
         2276  +/*{"ticket", json_page_nyi,0},*/
         2277  +{"timeline", json_page_timeline,0},
         2278  +{"user",json_page_user,0},
         2279  +{"version",json_page_version,0},
         2280  +{"whoami",json_page_whoami,0},
         2281  +{"wiki",json_page_wiki,0},
         2282  +/* Last entry MUST have a NULL name. */
         2283  +{NULL,NULL,0}
         2284  +};
         2285  +
         2286  +/*
         2287  +** Internal helper for json_cmd_top() and json_page_top().
         2288  +**
         2289  +** Searches JsonPageDefs for a command with the given name. If found,
         2290  +** it is used to generate and output a JSON response. If not found, it
         2291  +** generates a JSON-style error response. Returns 0 on success, non-0
         2292  +** on error. On error it will set g.json's error state.
         2293  +*/
         2294  +static int json_dispatch_root_command( char const * zCommand ){
         2295  +  int rc = 0;
         2296  +  cson_value * payload = NULL;
         2297  +  JsonPageDef const * pageDef = NULL;
         2298  +  pageDef = json_handler_for_name(zCommand,&JsonPageDefs[0]);
         2299  +  if( ! pageDef ){
         2300  +    rc = FSL_JSON_E_UNKNOWN_COMMAND;
         2301  +    json_set_err( rc, "Unknown command: %s", zCommand );
         2302  +  }else if( pageDef->runMode < 0 /*CLI only*/){
         2303  +    rc = FSL_JSON_E_WRONG_MODE;
         2304  +  }else if( (g.isHTTP && (pageDef->runMode < 0 /*CLI only*/))
         2305  +            ||
         2306  +            (!g.isHTTP && (pageDef->runMode > 0 /*HTTP only*/))
         2307  +            ){
         2308  +    rc = FSL_JSON_E_WRONG_MODE;
         2309  +  }
         2310  +  else{
         2311  +    rc = 0;
         2312  +    g.json.dispatchDepth = 1;
         2313  +    payload = (*pageDef->func)();
         2314  +  }
         2315  +  payload = json_create_response(rc, NULL, payload);
         2316  +  json_send_response(payload);
         2317  +  cson_value_free(payload);
         2318  +  return rc;
         2319  +}
         2320  +
         2321  +#ifdef FOSSIL_ENABLE_JSON
         2322  +/* dupe ifdef needed for mkindex */
         2323  +/*
         2324  +** WEBPAGE: json
         2325  +**
         2326  +** Pages under /json/... must be entered into JsonPageDefs.
         2327  +** This function dispatches them, and is the HTTP equivalent of
         2328  +** json_cmd_top().
         2329  +*/
         2330  +void json_page_top(void){
         2331  +  char const * zCommand;
         2332  +  BEGIN_TIMER;
         2333  +  json_mode_bootstrap();
         2334  +  zCommand = json_command_arg(1);
         2335  +  if(!zCommand || !*zCommand){
         2336  +    json_dispatch_missing_args_err( JsonPageDefs,
         2337  +                                    "No command (sub-path) specified."
         2338  +                                    " Try one of: ");
         2339  +    return;
         2340  +  }
         2341  +  json_dispatch_root_command( zCommand );
         2342  +}
         2343  +#endif /* FOSSIL_ENABLE_JSON for mkindex */
         2344  +
         2345  +#ifdef FOSSIL_ENABLE_JSON
         2346  +/* dupe ifdef needed for mkindex */
         2347  +/*
         2348  +** This function dispatches json commands and is the CLI equivalent of
         2349  +** json_page_top().
         2350  +**
         2351  +** COMMAND: json
         2352  +**
         2353  +** Usage: %fossil json SUBCOMMAND ?OPTIONS?
         2354  +**
         2355  +** In CLI mode, the -R REPO common option is supported. Due to limitations
         2356  +** in the argument dispatching code, any -FLAGS must come after the final
         2357  +** sub- (or subsub-) command.
         2358  +**
         2359  +** The commands include:
         2360  +**
         2361  +**   anonymousPassword
         2362  +**   artifact
         2363  +**   branch
         2364  +**   cap
         2365  +**   config
         2366  +**   diff
         2367  +**   dir
         2368  +**   g
         2369  +**   login
         2370  +**   logout
         2371  +**   query
         2372  +**   rebuild
         2373  +**   report
         2374  +**   resultCodes
         2375  +**   stat
         2376  +**   tag
         2377  +**   timeline
         2378  +**   user
         2379  +**   version (alias: HAI)
         2380  +**   whoami
         2381  +**   wiki
         2382  +**
         2383  +** Run '%fossil json' without any subcommand to see the full list (but be
         2384  +** aware that some listed might not yet be fully implemented).
         2385  +**
         2386  +*/
         2387  +void json_cmd_top(void){
         2388  +  char const * cmd = NULL;
         2389  +  int rc = 0;
         2390  +  BEGIN_TIMER;
         2391  +  memset( &g.perm, 0xff, sizeof(g.perm) )
         2392  +    /* In CLI mode fossil does not use permissions
         2393  +       and they all default to false. We enable them
         2394  +       here because (A) fossil doesn't use them in local
         2395  +       mode but (B) having them set gives us one less
         2396  +       difference in the CLI/CGI/Server-mode JSON
         2397  +       handling.
         2398  +    */
         2399  +    ;
         2400  +  json_main_bootstrap();
         2401  +  json_mode_bootstrap();
         2402  +  if( 2 > cson_array_length_get(g.json.cmd.a) ){
         2403  +    goto usage;
         2404  +  }
         2405  +#if 0
         2406  +  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
         2407  +  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
         2408  +#endif
         2409  +  cmd = json_command_arg(1);
         2410  +  if( !cmd || !*cmd ){
         2411  +    goto usage;
         2412  +  }
         2413  +  rc = json_dispatch_root_command( cmd );
         2414  +  if(0 != rc){
         2415  +    /* FIXME: we need a way of passing this error back
         2416  +       up to the routine which called this callback.
         2417  +       e.g. add g.errCode.
         2418  +    */
         2419  +    fossil_exit(1);
         2420  +  }
         2421  +  return;
         2422  +  usage:
         2423  +  {
         2424  +    cson_value * payload;
         2425  +    json_dispatch_missing_args_err( JsonPageDefs,
         2426  +                                    "No subcommand specified."
         2427  +                                    " Try one of: ");
         2428  +    payload = json_create_response(0, NULL, NULL);
         2429  +    json_send_response(payload);
         2430  +    cson_value_free(payload);
         2431  +    fossil_exit(1);
         2432  +  }
         2433  +}
         2434  +#endif /* FOSSIL_ENABLE_JSON for mkindex */
         2435  +
         2436  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_artifact.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_artifact.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +/*
           27  +** Internal callback for /json/artifact handlers. rid refers to
           28  +** the rid of a given type of artifact, and each callback is
           29  +** specialized to return a JSON form of one type of artifact.
           30  +**
           31  +** Implementations may assert() that rid refers to requested artifact
           32  +** type, since mismatches in the artifact types come from
           33  +** json_page_artifact() as opposed to client data.
           34  +**
           35  +** The pParent parameter points to the response payload object.  It
           36  +** _may_ be used to populate "top-level" information in the response
           37  +** payload, but normally this is neither necessary nor desired.
           38  +*/
           39  +typedef cson_value * (*artifact_f)( cson_object * pParent, int rid );
           40  +
           41  +/*
           42  +** Internal per-artifact-type dispatching helper.
           43  +*/
           44  +typedef struct ArtifactDispatchEntry {
           45  +  /**
           46  +     Artifact type name, e.g. "checkin", "ticket", "wiki".
           47  +   */
           48  +  char const * name;
           49  +
           50  +  /**
           51  +     JSON construction callback. Creates the contents for the
           52  +     payload.artifact property of /json/artifact responses.
           53  +  */
           54  +  artifact_f func;
           55  +} ArtifactDispatchEntry;
           56  +
           57  +
           58  +/*
           59  +** Generates a JSON Array reference holding the parent UUIDs (as strings).
           60  +** If it finds no matches then it returns NULL (OOM is a fatal error).
           61  +**
           62  +** Returned value is NULL or an Array owned by the caller.
           63  +*/
           64  +cson_value * json_parent_uuids_for_ci( int rid ){
           65  +  Stmt q = empty_Stmt;
           66  +  cson_array * pParents = NULL;
           67  +  db_prepare( &q,
           68  +              "SELECT uuid FROM plink, blob"
           69  +              " WHERE plink.cid=%d AND blob.rid=plink.pid"
           70  +              " ORDER BY plink.isprim DESC",
           71  +              rid );
           72  +  while( SQLITE_ROW==db_step(&q) ){
           73  +    if(!pParents) {
           74  +      pParents = cson_new_array();
           75  +    }
           76  +    cson_array_append( pParents, cson_sqlite3_column_to_value( q.pStmt, 0 ) );
           77  +  }
           78  +  db_finalize(&q);
           79  +  return cson_array_value(pParents);
           80  +}
           81  +
           82  +/*
           83  +** Generates an artifact Object for the given rid,
           84  +** which must refer to a Checkin.
           85  +**
           86  +** Returned value is NULL or an Object owned by the caller.
           87  +*/
           88  +cson_value * json_artifact_for_ci( int rid, char showFiles ){
           89  +  cson_value * v = NULL;
           90  +  Stmt q = empty_Stmt;
           91  +  static cson_value * eventTypeLabel = NULL;
           92  +  if(!eventTypeLabel){
           93  +    eventTypeLabel = json_new_string("checkin");
           94  +    json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel);
           95  +  }
           96  +  
           97  +  db_prepare(&q,
           98  +             "SELECT b.uuid, "
           99  +             " cast(strftime('%%s',e.mtime) as int), "
          100  +             " strftime('%%s',e.omtime),"
          101  +             " e.user, "
          102  +             " e.comment"
          103  +             " FROM blob b, event e"
          104  +             " WHERE b.rid=%d"
          105  +             "   AND e.objid=%d",
          106  +             rid, rid
          107  +             );
          108  +  if( db_step(&q)==SQLITE_ROW ){
          109  +    cson_object * o;
          110  +    cson_value * tmpV = NULL;
          111  +    const char *zUuid = db_column_text(&q, 0);
          112  +    const char *zUser;
          113  +    const char *zComment;
          114  +    char * zEUser, * zEComment;
          115  +    int mtime, omtime;
          116  +    v = cson_value_new_object();
          117  +    o = cson_value_get_object(v);
          118  +#define SET(K,V) cson_object_set(o,(K), (V))
          119  +    SET("type", eventTypeLabel );
          120  +    SET("uuid",json_new_string(zUuid));
          121  +    SET("isLeaf", cson_value_new_bool(is_a_leaf(rid)));
          122  +
          123  +    mtime = db_column_int(&q,1);
          124  +    SET("timestamp",json_new_int(mtime));
          125  +    omtime = db_column_int(&q,2);
          126  +    if(omtime && (omtime!=mtime)){
          127  +      SET("originTime",json_new_int(omtime));
          128  +    }
          129  +
          130  +    zUser = db_column_text(&q,3);
          131  +    zEUser = db_text(0,
          132  +                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
          133  +                   TAG_USER, rid);
          134  +    if(zEUser){
          135  +      SET("user", json_new_string(zEUser));
          136  +      if(0!=strcmp(zEUser,zUser)){
          137  +        SET("originUser",json_new_string(zUser));
          138  +      }
          139  +      free(zEUser);
          140  +    }else{
          141  +      SET("user",json_new_string(zUser));
          142  +    }
          143  +
          144  +    zComment = db_column_text(&q,4);
          145  +    zEComment = db_text(0, 
          146  +                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
          147  +                   TAG_COMMENT, rid);
          148  +    if(zEComment){
          149  +      SET("comment",json_new_string(zEComment));
          150  +      if(0 != strcmp(zEComment,zComment)){
          151  +        SET("originComment", json_new_string(zComment));
          152  +      }
          153  +      free(zEComment);
          154  +    }else{
          155  +      SET("comment",json_new_string(zComment));
          156  +    }
          157  +
          158  +    tmpV = json_parent_uuids_for_ci(rid);
          159  +    if(tmpV){
          160  +      SET("parents", tmpV);
          161  +    }
          162  +
          163  +    tmpV = json_tags_for_checkin_rid(rid,0);
          164  +    if(tmpV){
          165  +      SET("tags",tmpV);
          166  +    }
          167  +
          168  +    if( showFiles ){
          169  +      tmpV = json_get_changed_files(rid, 1);
          170  +      if(tmpV){
          171  +        SET("files",tmpV);
          172  +      }
          173  +    }
          174  +
          175  +#undef SET
          176  +  }
          177  +  db_finalize(&q);
          178  +  return v;
          179  +}
          180  +
          181  +/*
          182  +** Very incomplete/incorrect impl of /json/artifact/TICKET_ID.
          183  +*/
          184  +cson_value * json_artifact_ticket( cson_object * zParent, int rid ){
          185  +  cson_object * pay = NULL;
          186  +  Manifest *pTktChng = NULL;
          187  +  static cson_value * eventTypeLabel = NULL;
          188  +  if(! g.perm.RdTkt ){
          189  +    g.json.resultCode = FSL_JSON_E_DENIED;
          190  +    return NULL;
          191  +  }
          192  +  if(!eventTypeLabel){
          193  +    eventTypeLabel = json_new_string("ticket");
          194  +    json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel);
          195  +  }
          196  +
          197  +  pTktChng = manifest_get(rid, CFTYPE_TICKET);
          198  +  if( pTktChng==0 ){
          199  +    g.json.resultCode = FSL_JSON_E_MANIFEST_READ_FAILED;
          200  +    return NULL;
          201  +  }
          202  +  pay = cson_new_object();
          203  +  cson_object_set(pay, "eventType", eventTypeLabel );
          204  +  cson_object_set(pay, "uuid", json_new_string(pTktChng->zTicketUuid));
          205  +  cson_object_set(pay, "user", json_new_string(pTktChng->zUser));
          206  +  cson_object_set(pay, "timestamp", json_julian_to_timestamp(pTktChng->rDate));
          207  +  manifest_destroy(pTktChng);
          208  +  return cson_object_value(pay);
          209  +}
          210  +
          211  +/*
          212  +** Sub-impl of /json/artifact for checkins.
          213  +*/
          214  +static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
          215  +  if(!g.perm.Read){
          216  +    json_set_err( FSL_JSON_E_DENIED, "Viewing checkins requires 'o' privileges." );
          217  +    return NULL;
          218  +  }else{
          219  +    cson_value * artV = json_artifact_for_ci(rid, 1);
          220  +    cson_object * art = cson_value_get_object(artV);
          221  +    if(art){
          222  +      cson_object_merge( zParent, art, CSON_MERGE_REPLACE );
          223  +      cson_free_object(art);
          224  +    }
          225  +    return cson_object_value(zParent);
          226  +  }
          227  +}
          228  +
          229  +/*
          230  +** Internal mapping of /json/artifact/FOO commands/callbacks.
          231  +*/
          232  +static ArtifactDispatchEntry ArtifactDispatchList[] = {
          233  +{"checkin", json_artifact_ci},
          234  +{"file", json_artifact_file},
          235  +{"tag", NULL},
          236  +{"ticket", json_artifact_ticket},
          237  +{"wiki", json_artifact_wiki},
          238  +/* Final entry MUST have a NULL name. */
          239  +{NULL,NULL}
          240  +};
          241  +
          242  +/*
          243  +** Internal helper which returns:
          244  +**
          245  +** If the "format" (CLI: -f) flag is set function returns the same as
          246  +** json_wiki_get_content_format_flag(), else it returns true (non-0)
          247  +** if either the includeContent (HTTP) or -content|-c boolean flags
          248  +** (CLI) are set.
          249  +*/ 
          250  +static char json_artifact_get_content_format_flag(){
          251  +  enum { MagicValue = -9 };
          252  +  char contentFormat = json_wiki_get_content_format_flag(MagicValue);
          253  +  if(MagicValue == contentFormat){
          254  +    contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0;
          255  +  }
          256  +  return contentFormat;
          257  +}
          258  +
          259  +extern char json_wiki_get_content_format_flag( char defaultValue ) /* json_wiki.c */;
          260  +
          261  +cson_value * json_artifact_wiki(cson_object * zParent, int rid){
          262  +  if( ! g.perm.RdWiki ){
          263  +    json_set_err(FSL_JSON_E_DENIED,
          264  +                 "Requires 'j' privileges.");
          265  +    return NULL;
          266  +  }else{
          267  +    enum { MagicValue = -9 };
          268  +    char const contentFormat = json_artifact_get_content_format_flag();
          269  +    return json_get_wiki_page_by_rid(rid, contentFormat);
          270  +  }
          271  +}
          272  +
          273  +/*
          274  +** Internal helper for routines which add a "status" flag to file
          275  +** artifact data. isNew and isDel should be the "is this object new?" 
          276  +** and "is this object removed?" flags of the underlying query.  This
          277  +** function returns a static string from the set (added, removed,
          278  +** modified), depending on the combination of the two args.
          279  +**
          280  +** Reminder to self: (mlink.pid==0) AS isNew, (mlink.fid==0) AS isDel
          281  +*/
          282  +char const * json_artifact_status_to_string( char isNew, char isDel ){
          283  +  return isNew
          284  +    ? "added"
          285  +    : (isDel
          286  +       ? "removed"
          287  +       : "modified");
          288  +}
          289  +
          290  +cson_value * json_artifact_file(cson_object * zParent, int rid){
          291  +  cson_object * pay = NULL;
          292  +  Stmt q = empty_Stmt;
          293  +  cson_array * checkin_arr = NULL;
          294  +  char contentFormat;
          295  +  i64 contentSize = -1;
          296  +  char * parentUuid;
          297  +  if( ! g.perm.Read ){
          298  +    json_set_err(FSL_JSON_E_DENIED,
          299  +                 "Requires 'o' privileges.");
          300  +    return NULL;
          301  +  }
          302  +  
          303  +  pay = zParent;
          304  +
          305  +  contentFormat = json_artifact_get_content_format_flag();
          306  +  if( 0 != contentFormat ){
          307  +    Blob content = empty_blob;
          308  +    const char *zMime;
          309  +    char const * zFormat = (contentFormat<1) ? "raw" : "html";
          310  +    content_get(rid, &content);
          311  +    zMime = mimetype_from_content(&content);
          312  +    cson_object_set(zParent, "contentType",
          313  +                    json_new_string(zMime ? zMime : "text/plain"));
          314  +    if(!zMime){/* text/plain */
          315  +      if(0 < blob_size(&content)){
          316  +        if( 0 < contentFormat ){/*HTML-size it*/
          317  +          Blob html = empty_blob;
          318  +          wiki_convert(&content, &html, 0);
          319  +          assert( blob_size(&content) < blob_size(&html) );
          320  +          blob_swap( &html, &content );
          321  +          assert( blob_size(&content) > blob_size(&html) );
          322  +          blob_reset( &html );
          323  +        }/*else as-is*/
          324  +      }
          325  +      cson_object_set(zParent, "content",
          326  +                      cson_value_new_string(blob_str(&content),
          327  +                                            (unsigned int)blob_size(&content)));
          328  +    }/*else binary: ignore*/
          329  +    contentSize = blob_size(&content);
          330  +    cson_object_set(zParent, "contentSize", json_new_int(contentSize) );
          331  +    cson_object_set(zParent, "contentFormat", json_new_string(zFormat) );
          332  +    blob_reset(&content);
          333  +  }
          334  +  contentSize = db_int64(-1, "SELECT size FROM blob WHERE rid=%d", rid);
          335  +  assert( -1 < contentSize );
          336  +  cson_object_set(zParent, "size", json_new_int(contentSize) );
          337  +
          338  +  parentUuid = db_text(NULL,
          339  +                       "SELECT DISTINCT p.uuid "
          340  +                       "FROM blob p, blob f, mlink m "
          341  +                       "WHERE m.pid=p.rid "
          342  +                       "AND m.fid=f.rid "
          343  +                       "AND f.rid=%d",
          344  +                       rid
          345  +                       );
          346  +  if(parentUuid){
          347  +    cson_object_set( zParent, "parent", json_new_string(parentUuid) );
          348  +    fossil_free(parentUuid);
          349  +  }
          350  +  
          351  +  /* Find checkins associated with this file... */
          352  +  db_prepare(&q,
          353  +      "SELECT filename.name AS name, "
          354  +      "  (mlink.pid==0) AS isNew,"
          355  +      "  (mlink.fid==0) AS isDel,"
          356  +      "  cast(strftime('%%s',event.mtime) as int) AS timestamp,"
          357  +      "  coalesce(event.ecomment,event.comment) as comment,"
          358  +      "  coalesce(event.euser,event.user) as user,"
          359  +#if 0
          360  +      "  a.size AS size," /* same for all checkins. */
          361  +#endif
          362  +      "  b.uuid as checkin, "
          363  +#if 0
          364  +      "  mlink.mperm as mperm,"
          365  +#endif
          366  +      "  coalesce((SELECT value FROM tagxref"
          367  +                      "  WHERE tagid=%d AND tagtype>0 AND "
          368  +                      " rid=mlink.mid),'trunk') as branch"
          369  +      "  FROM mlink, filename, event, blob a, blob b"
          370  +      " WHERE filename.fnid=mlink.fnid"
          371  +      "   AND event.objid=mlink.mid"
          372  +      "   AND a.rid=mlink.fid"
          373  +      "   AND b.rid=mlink.mid"
          374  +      "   AND mlink.fid=%d"
          375  +      "   ORDER BY filename.name, event.mtime",
          376  +      TAG_BRANCH, rid
          377  +    );
          378  +  /* TODO: add a "state" flag for the file in each checkin,
          379  +     e.g. "modified", "new", "deleted".
          380  +   */
          381  +  checkin_arr = cson_new_array(); 
          382  +  cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
          383  +  while( (SQLITE_ROW==db_step(&q) ) ){
          384  +    cson_object * row = cson_value_get_object(cson_sqlite3_row_to_object(q.pStmt));
          385  +    /* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */
          386  +    char const isNew = cson_value_get_bool(cson_object_get(row,"isNew"));
          387  +    char const isDel = cson_value_get_bool(cson_object_get(row,"isDel"));
          388  +    cson_object_set(row, "isNew", NULL);
          389  +    cson_object_set(row, "isDel", NULL);
          390  +    cson_object_set(row, "state",
          391  +                    json_new_string(json_artifact_status_to_string(isNew, isDel)));
          392  +    cson_array_append( checkin_arr, cson_object_value(row) );
          393  +  }
          394  +  db_finalize(&q);
          395  +  return cson_object_value(pay);
          396  +}
          397  +
          398  +/*
          399  +** Impl of /json/artifact. This basically just determines the type of
          400  +** an artifact and forwards the real work to another function.
          401  +*/
          402  +cson_value * json_page_artifact(){
          403  +  cson_object * pay = NULL;
          404  +  char const * zName = NULL;
          405  +  char const * zType = NULL;
          406  +  char const * zUuid = NULL;
          407  +  cson_value * entry = NULL;
          408  +  Blob uuid = empty_blob;
          409  +  int rc;
          410  +  int rid = 0;
          411  +  ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0];
          412  +  zName = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1);
          413  +  if(!zName || !*zName) {
          414  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          415  +                 "Missing 'name' argument.");
          416  +    return NULL;
          417  +  }
          418  +
          419  +  if( validate16(zName, strlen(zName)) ){
          420  +    if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
          421  +      zType = "ticket";
          422  +      goto handle_entry;
          423  +    }
          424  +    if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){
          425  +      zType = "tag";
          426  +      goto handle_entry;
          427  +    }
          428  +  }
          429  +  blob_set(&uuid,zName);
          430  +  rc = name_to_uuid(&uuid,-1,"*");
          431  +  /* FIXME: check for a filename if all else fails. */
          432  +  if(1==rc){
          433  +    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
          434  +    goto error;
          435  +  }else if(2==rc){
          436  +    g.json.resultCode = FSL_JSON_E_AMBIGUOUS_UUID;
          437  +    goto error;
          438  +  }
          439  +  zUuid = blob_str(&uuid);
          440  +  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid);
          441  +  if(0==rid){
          442  +    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
          443  +    goto error;
          444  +  }
          445  +
          446  +  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid)
          447  +      || db_exists("SELECT 1 FROM plink WHERE cid=%d", rid)
          448  +      || db_exists("SELECT 1 FROM plink WHERE pid=%d", rid)){
          449  +    zType = "checkin";
          450  +    goto handle_entry;
          451  +  }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
          452  +                      " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
          453  +    zType = "wiki";
          454  +    goto handle_entry;
          455  +  }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
          456  +                      " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){
          457  +    zType = "ticket";
          458  +    goto handle_entry;
          459  +  }else if ( db_exists("SELECT 1 FROM mlink WHERE fid = %d", rid) ){
          460  +    zType = "file";
          461  +    goto handle_entry;
          462  +  }else{
          463  +    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
          464  +    goto error;
          465  +  }
          466  +
          467  +  error:
          468  +  assert( 0 != g.json.resultCode );
          469  +  goto veryend;
          470  +
          471  +  handle_entry:
          472  +  pay = cson_new_object();
          473  +  assert( (NULL != zType) && "Internal dispatching error." );
          474  +  for( ; dispatcher->name; ++dispatcher ){
          475  +    if(0!=strcmp(dispatcher->name, zType)){
          476  +      continue;
          477  +    }else{
          478  +      entry = (*dispatcher->func)(pay, rid);
          479  +      break;
          480  +    }
          481  +  }
          482  +  if(!g.json.resultCode){
          483  +    assert( NULL != entry );
          484  +    assert( NULL != zType );
          485  +    cson_object_set( pay, "type", json_new_string(zType) );
          486  +    cson_object_set( pay, "uuid", json_new_string(zUuid) );
          487  +    /*cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) );*/
          488  +    /*cson_object_set( pay, "rid", cson_value_new_integer(rid) );*/
          489  +    if(cson_value_is_object(entry) && (cson_value_get_object(entry) != pay)){
          490  +      cson_object_set(pay, "artifact", entry);
          491  +    }
          492  +  }
          493  +  veryend:
          494  +  blob_reset(&uuid);
          495  +  if(g.json.resultCode && pay){
          496  +    cson_free_object(pay);
          497  +    pay = NULL;
          498  +  }
          499  +  return cson_object_value(pay);
          500  +}
          501  +
          502  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_branch.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_branch.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +static cson_value * json_branch_list();
           28  +static cson_value * json_branch_create();
           29  +/*
           30  +** Mapping of /json/branch/XXX commands/paths to callbacks.
           31  +*/
           32  +static const JsonPageDef JsonPageDefs_Branch[] = {
           33  +{"create", json_branch_create, 0},
           34  +{"list", json_branch_list, 0},
           35  +{"new", json_branch_create, -1/* for compat with non-JSON branch command.*/},
           36  +/* Last entry MUST have a NULL name. */
           37  +{NULL,NULL,0}
           38  +};
           39  +
           40  +/*
           41  +** Implements the /json/branch family of pages/commands. Far from
           42  +** complete.
           43  +**
           44  +*/
           45  +cson_value * json_page_branch(){
           46  +  return json_page_dispatch_helper(&JsonPageDefs_Branch[0]);
           47  +}
           48  +
           49  +/*
           50  +** Impl for /json/branch/list
           51  +**
           52  +**
           53  +** CLI mode options:
           54  +**
           55  +**  --range X | -r X, where X is one of (open,closed,all)
           56  +**    (only the first letter is significant, default=open).
           57  +**  -a (same as --range a)
           58  +**  -c (same as --range c)
           59  +**
           60  +** HTTP mode options:
           61  +**
           62  +** "range" GET/POST.payload parameter. FIXME: currently we also use
           63  +** POST, but really want to restrict this to POST.payload.
           64  +*/
           65  +static cson_value * json_branch_list(){
           66  +  cson_value * payV;
           67  +  cson_object * pay;
           68  +  cson_value * listV;
           69  +  cson_array * list;
           70  +  char const * range = NULL;
           71  +  int which = 0;
           72  +  char * sawConversionError = NULL;
           73  +  Stmt q;
           74  +  if( !g.perm.Read ){
           75  +    json_set_err(FSL_JSON_E_DENIED,
           76  +                 "Requires 'o' permissions.");
           77  +    return NULL;
           78  +  }
           79  +  payV = cson_value_new_object();
           80  +  pay = cson_value_get_object(payV);
           81  +  listV = cson_value_new_array();
           82  +  list = cson_value_get_array(listV);
           83  +  if(fossil_has_json()){
           84  +      range = json_getenv_cstr("range");
           85  +  }
           86  +
           87  +  range = json_find_option_cstr("range",NULL,"r");
           88  +  if((!range||!*range) && !g.isHTTP){
           89  +    range = find_option("all","a",0);
           90  +    if(range && *range){
           91  +      range = "a";
           92  +    }else{
           93  +      range = find_option("closed","c",0);
           94  +      if(range&&*range){
           95  +        range = "c";
           96  +      }
           97  +    }
           98  +  }
           99  +
          100  +  if(!range || !*range){
          101  +    range = "o";
          102  +  }
          103  +  /* Normalize range values... */
          104  +  switch(*range){
          105  +    case 'c':
          106  +      range = "closed";
          107  +      which = -1;
          108  +      break;
          109  +    case 'a':
          110  +      range = "all";
          111  +      which = 1;
          112  +      break;
          113  +    default:
          114  +      range = "open";
          115  +      which = 0;
          116  +      break;
          117  +  };
          118  +  cson_object_set(pay,"range",json_new_string(range));
          119  +
          120  +  if( g.localOpen ){ /* add "current" property (branch name). */
          121  +    int vid = db_lget_int("checkout", 0);
          122  +    char const * zCurrent = vid
          123  +      ? db_text(0, "SELECT value FROM tagxref"
          124  +                " WHERE rid=%d AND tagid=%d",
          125  +                vid, TAG_BRANCH)
          126  +      : 0;
          127  +    if(zCurrent){
          128  +      cson_object_set(pay,"current",json_new_string(zCurrent));
          129  +    }
          130  +  }
          131  +
          132  +  
          133  +  branch_prepare_list_query(&q, which);
          134  +  cson_object_set(pay,"branches",listV);
          135  +  while((SQLITE_ROW==db_step(&q))){
          136  +    cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
          137  +    if(v){
          138  +      cson_array_append(list,v);
          139  +    }else if(!sawConversionError){
          140  +      sawConversionError = mprintf("Column-to-json failed @ %s:%d",
          141  +                                   __FILE__,__LINE__);
          142  +    }
          143  +  }
          144  +  if( sawConversionError ){
          145  +    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
          146  +    free(sawConversionError);
          147  +  }
          148  +  return payV;
          149  +}
          150  +
          151  +/*
          152  +** Parameters for the create-branch operation.
          153  +*/
          154  +typedef struct BranchCreateOptions{
          155  +  char const * zName;
          156  +  char const * zBasis;
          157  +  char const * zColor;
          158  +  char isPrivate;
          159  +  /**
          160  +     Might be set to an error string by
          161  +     json_branch_new().
          162  +   */
          163  +  char const * rcErrMsg;
          164  +} BranchCreateOptions;
          165  +
          166  +/*
          167  +** Tries to create a new branch based on the options set in zOpt. If
          168  +** an error is encountered, zOpt->rcErrMsg _might_ be set to a
          169  +** descriptive string and one of the FossilJsonCodes values will be
          170  +** returned. Or fossil_fatal() (or similar) might be called, exiting
          171  +** the app.
          172  +**
          173  +** On success 0 is returned and if zNewRid is not NULL then the rid of
          174  +** the new branch is assigned to it.
          175  +**
          176  +** If zOpt->isPrivate is 0 but the parent branch is private,
          177  +** zOpt->isPrivate will be set to a non-zero value and the new branch
          178  +** will be private.
          179  +*/
          180  +static int json_branch_new(BranchCreateOptions * zOpt,
          181  +                           int *zNewRid){
          182  +  /* Mostly copied from branch.c:branch_new(), but refactored a small
          183  +     bit to not produce output or interact with the user. The
          184  +     down-side to that is that we dropped the gpg-signing. It was
          185  +     either that or abort the creation if we couldn't sign. We can't
          186  +     sign over HTTP mode, anyway.
          187  +  */
          188  +  char const * zBranch = zOpt->zName;
          189  +  char const * zBasis = zOpt->zBasis;
          190  +  char const * zColor = zOpt->zColor;
          191  +  int rootid;            /* RID of the root check-in - what we branch off of */
          192  +  int brid;              /* RID of the branch check-in */
          193  +  int i;                 /* Loop counter */
          194  +  char *zUuid;           /* Artifact ID of origin */
          195  +  Stmt q;                /* Generic query */
          196  +  char *zDate;           /* Date that branch was created */
          197  +  char *zComment;        /* Check-in comment for the new branch */
          198  +  Blob branch;           /* manifest for the new branch */
          199  +  Manifest *pParent;     /* Parsed parent manifest */
          200  +  Blob mcksum;           /* Self-checksum on the manifest */
          201  +
          202  +  /* fossil branch new name */
          203  +  if( zBranch==0 || zBranch[0]==0 ){
          204  +    zOpt->rcErrMsg = "Branch name may not be null/empty.";
          205  +    return FSL_JSON_E_INVALID_ARGS;
          206  +  }
          207  +  if( db_exists(
          208  +        "SELECT 1 FROM tagxref"
          209  +        " WHERE tagtype>0"
          210  +        "   AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%q')",
          211  +        zBranch)!=0 ){
          212  +    zOpt->rcErrMsg = "Branch already exists.";
          213  +    return FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
          214  +  }
          215  +
          216  +  db_begin_transaction();
          217  +  rootid = name_to_typed_rid(zBasis, "ci");
          218  +  if( rootid==0 ){
          219  +    zOpt->rcErrMsg = "Basis branch not found.";
          220  +    return FSL_JSON_E_RESOURCE_NOT_FOUND;
          221  +  }
          222  +
          223  +  pParent = manifest_get(rootid, CFTYPE_MANIFEST);
          224  +  if( pParent==0 ){
          225  +    zOpt->rcErrMsg = "Could not read parent manifest.";
          226  +    return FSL_JSON_E_UNKNOWN;
          227  +  }
          228  +
          229  +  /* Create a manifest for the new branch */
          230  +  blob_zero(&branch);
          231  +  if( pParent->zBaseline ){
          232  +    blob_appendf(&branch, "B %s\n", pParent->zBaseline);
          233  +  }
          234  +  zComment = mprintf("Create new branch named \"%s\" "
          235  +                     "from \"%s\".", zBranch, zBasis);
          236  +  blob_appendf(&branch, "C %F\n", zComment);
          237  +  free(zComment);
          238  +  zDate = date_in_standard_format("now");
          239  +  blob_appendf(&branch, "D %s\n", zDate);
          240  +  free(zDate);
          241  +
          242  +  /* Copy all of the content from the parent into the branch */
          243  +  for(i=0; i<pParent->nFile; ++i){
          244  +    blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
          245  +    if( pParent->aFile[i].zUuid ){
          246  +      blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
          247  +      if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){
          248  +        blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
          249  +      }
          250  +    }
          251  +    blob_append(&branch, "\n", 1);
          252  +  }
          253  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
          254  +  blob_appendf(&branch, "P %s\n", zUuid);
          255  +  free(zUuid);
          256  +  if( pParent->zRepoCksum ){
          257  +    blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
          258  +  }
          259  +  manifest_destroy(pParent);
          260  +
          261  +  /* Add the symbolic branch name and the "branch" tag to identify
          262  +  ** this as a new branch */
          263  +  if( content_is_private(rootid) ) zOpt->isPrivate = 1;
          264  +  if( zOpt->isPrivate && zColor==0 ) zColor = "#fec084";
          265  +  if( zColor!=0 ){
          266  +    blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
          267  +  }
          268  +  blob_appendf(&branch, "T *branch * %F\n", zBranch);
          269  +  blob_appendf(&branch, "T *sym-%F *\n", zBranch);
          270  +  if( zOpt->isPrivate ){
          271  +    blob_appendf(&branch, "T +private *\n");
          272  +  }
          273  +
          274  +  /* Cancel all other symbolic tags */
          275  +  db_prepare(&q,
          276  +      "SELECT tagname FROM tagxref, tag"
          277  +      " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
          278  +      "   AND tagtype>0 AND tagname GLOB 'sym-*'"
          279  +      " ORDER BY tagname",
          280  +      rootid);
          281  +  while( db_step(&q)==SQLITE_ROW ){
          282  +    const char *zTag = db_column_text(&q, 0);
          283  +    blob_appendf(&branch, "T -%F *\n", zTag);
          284  +  }
          285  +  db_finalize(&q);
          286  +  
          287  +  blob_appendf(&branch, "U %F\n", g.zLogin);
          288  +  md5sum_blob(&branch, &mcksum);
          289  +  blob_appendf(&branch, "Z %b\n", &mcksum);
          290  +
          291  +  brid = content_put(&branch);
          292  +  if( brid==0 ){
          293  +    fossil_panic("Problem committing manifest: %s", g.zErrMsg);
          294  +  }
          295  +  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
          296  +  if( manifest_crosslink(brid, &branch)==0 ){
          297  +    fossil_panic("unable to install new manifest");
          298  +  }
          299  +  assert( blob_is_reset(&branch) );
          300  +  content_deltify(rootid, brid, 0);
          301  +  if( zNewRid ){
          302  +    *zNewRid = brid;
          303  +  }
          304  +
          305  +  /* Commit */
          306  +  db_end_transaction(0);
          307  +  
          308  +#if 0 /* Do an autosync push, if requested */
          309  +  /* arugable for JSON mode? */
          310  +  if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
          311  +#endif
          312  +  return 0;
          313  +}
          314  +
          315  +
          316  +/*
          317  +** Impl of /json/branch/create.
          318  +*/
          319  +static cson_value * json_branch_create(){
          320  +  cson_value * payV = NULL;
          321  +  cson_object * pay = NULL;
          322  +  int rc = 0;
          323  +  BranchCreateOptions opt;
          324  +  char * zUuid = NULL;
          325  +  int rid = 0;
          326  +  if( !g.perm.Write ){
          327  +    json_set_err(FSL_JSON_E_DENIED,
          328  +                 "Requires 'i' permissions.");
          329  +    return NULL;
          330  +  }
          331  +  memset(&opt,0,sizeof(BranchCreateOptions));
          332  +  if(fossil_has_json()){
          333  +    opt.zName = json_getenv_cstr("name");
          334  +  }
          335  +
          336  +  if(!opt.zName){
          337  +    opt.zName = json_command_arg(g.json.dispatchDepth+1);
          338  +  }
          339  +
          340  +  if(!opt.zName){
          341  +    json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );
          342  +    return NULL;
          343  +  }
          344  +
          345  +  opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
          346  +  opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
          347  +  if(!opt.zBasis && !g.isHTTP){
          348  +    opt.zBasis = json_command_arg(g.json.dispatchDepth+2);
          349  +  }
          350  +  if(!opt.zBasis){
          351  +    opt.zBasis = "trunk";
          352  +  }
          353  +  opt.isPrivate = json_find_option_bool("private",NULL,NULL,-1);
          354  +  if(-1==opt.isPrivate){
          355  +    if(!g.isHTTP){
          356  +      opt.isPrivate = (NULL != find_option("private","",0));
          357  +    }else{
          358  +      opt.isPrivate = 0;
          359  +    }
          360  +  }
          361  +  
          362  +  rc = json_branch_new( &opt, &rid );
          363  +  if(rc){
          364  +    json_set_err(rc, opt.rcErrMsg);
          365  +    goto error;
          366  +  }
          367  +  assert(0 != rid);
          368  +  payV = cson_value_new_object();
          369  +  pay = cson_value_get_object(payV);
          370  +
          371  +  cson_object_set(pay,"name",json_new_string(opt.zName));
          372  +  cson_object_set(pay,"basis",json_new_string(opt.zBasis));
          373  +  cson_object_set(pay,"rid",json_new_int(rid));
          374  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          375  +  cson_object_set(pay,"uuid", json_new_string(zUuid));
          376  +  cson_object_set(pay, "isPrivate", cson_value_new_bool(opt.isPrivate));
          377  +  free(zUuid);
          378  +  if(opt.zColor){
          379  +    cson_object_set(pay,"bgColor",json_new_string(opt.zColor));
          380  +  }
          381  +
          382  +  goto ok;
          383  +  error:
          384  +  assert( 0 != g.json.resultCode );
          385  +  cson_value_free(payV);
          386  +  payV = NULL;
          387  +  ok:
          388  +  return payV;
          389  +}
          390  +
          391  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_config.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_config.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +static cson_value * json_config_get();
           27  +static cson_value * json_config_save();
           28  +
           29  +/*
           30  +** Mapping of /json/config/XXX commands/paths to callbacks.
           31  +*/
           32  +static const JsonPageDef JsonPageDefs_Config[] = {
           33  +{"get", json_config_get, 0},
           34  +{"save", json_config_save, 0},
           35  +/* Last entry MUST have a NULL name. */
           36  +{NULL,NULL,0}
           37  +};
           38  +
           39  +
           40  +/*
           41  +** Implements the /json/config family of pages/commands.
           42  +**
           43  +*/
           44  +cson_value * json_page_config(){
           45  +  return json_page_dispatch_helper(&JsonPageDefs_Config[0]);
           46  +}
           47  +
           48  +
           49  +/*
           50  +** JSON-internal mapping of config options to config groups.  This is
           51  +** mostly a copy of the config options in configure.c, but that data
           52  +** is private and cannot be re-used directly here.
           53  +*/
           54  +static const struct JsonConfigProperty {
           55  +  char const * name;
           56  +  int groupMask;
           57  +} JsonConfigProperties[] = {
           58  +{ "css",                    CONFIGSET_SKIN },
           59  +{ "header",                 CONFIGSET_SKIN },
           60  +{ "footer",                 CONFIGSET_SKIN },
           61  +{ "index-page",             CONFIGSET_SKIN },
           62  +{ "timeline-block-markup",  CONFIGSET_SKIN },
           63  +{ "timeline-max-comment",   CONFIGSET_SKIN },
           64  +
           65  +{ "project-name",           CONFIGSET_PROJ },
           66  +{ "project-description",    CONFIGSET_PROJ },
           67  +{ "manifest",               CONFIGSET_PROJ },
           68  +{ "ignore-glob",            CONFIGSET_PROJ },
           69  +{ "crnl-glob",              CONFIGSET_PROJ },
           70  +{ "empty-dirs",             CONFIGSET_PROJ },
           71  +{ "allow-symlinks",         CONFIGSET_PROJ },
           72  +
           73  +{ "ticket-table",           CONFIGSET_TKT  },
           74  +{ "ticket-common",          CONFIGSET_TKT  },
           75  +{ "ticket-change",          CONFIGSET_TKT  },
           76  +{ "ticket-newpage",         CONFIGSET_TKT  },
           77  +{ "ticket-viewpage",        CONFIGSET_TKT  },
           78  +{ "ticket-editpage",        CONFIGSET_TKT  },
           79  +{ "ticket-reportlist",      CONFIGSET_TKT  },
           80  +{ "ticket-report-template", CONFIGSET_TKT  },
           81  +{ "ticket-key-template",    CONFIGSET_TKT  },
           82  +{ "ticket-title-expr",      CONFIGSET_TKT  },
           83  +{ "ticket-closed-expr",     CONFIGSET_TKT  },
           84  +
           85  +{NULL, 0}
           86  +};
           87  +
           88  +
           89  +/*
           90  +** Impl of /json/config/get. Requires setup rights.
           91  +**
           92  +*/
           93  +static cson_value * json_config_get(){
           94  +  cson_object * pay = NULL;
           95  +  Stmt q = empty_Stmt;
           96  +  Blob sql = empty_blob;
           97  +  char const * zName = NULL;
           98  +  int confMask = 0;
           99  +  char optSkinBackups = 0;
          100  +  unsigned int i;
          101  +  if(!g.perm.Setup){
          102  +    json_set_err(FSL_JSON_E_DENIED, "Requires 's' permissions.");
          103  +    return NULL;
          104  +  }
          105  +
          106  +  i = g.json.dispatchDepth + 1;
          107  +  zName = json_command_arg(i);
          108  +  for( ; zName; zName = json_command_arg(++i) ){
          109  +    if(0==(strcmp("all", zName))){
          110  +      confMask = CONFIGSET_ALL;
          111  +    }else if(0==(strcmp("project", zName))){
          112  +      confMask |= CONFIGSET_PROJ;
          113  +    }else if(0==(strcmp("skin", zName))){
          114  +      confMask |= CONFIGSET_SKIN;
          115  +    }else if(0==(strcmp("ticket", zName))){
          116  +      confMask |= CONFIGSET_TKT;
          117  +    }else if(0==(strcmp("skin-backup", zName))){
          118  +      optSkinBackups = 1;
          119  +    }else{
          120  +      json_set_err( FSL_JSON_E_INVALID_ARGS,
          121  +                    "Unknown config area: %s", zName);
          122  +      return NULL;
          123  +    }
          124  +  }
          125  +
          126  +  if(!confMask && !optSkinBackups){
          127  +    json_set_err(FSL_JSON_E_MISSING_ARGS, "No configuration area(s) selected.");
          128  +  }
          129  +  blob_append(&sql,
          130  +              "SELECT name, value"
          131  +              " FROM config "
          132  +              " WHERE 0 ", -1);
          133  +  {
          134  +    const struct JsonConfigProperty * prop = &JsonConfigProperties[0];
          135  +    blob_append(&sql," OR name IN (",-1);
          136  +    for( i = 0; prop->name; ++prop ){
          137  +      if(prop->groupMask & confMask){
          138  +        if( i++ ){
          139  +          blob_append(&sql,",",1);
          140  +        }
          141  +        blob_appendf(&sql, "%Q", prop->name);
          142  +      }
          143  +    }
          144  +    blob_append(&sql,") ", -1);
          145  +  }
          146  +
          147  +
          148  +  if( optSkinBackups ){
          149  +    blob_append(&sql, " OR name GLOB 'skin:*'", -1);
          150  +  }
          151  +  blob_append(&sql," ORDER BY name", -1);
          152  +  db_prepare(&q, blob_str(&sql));
          153  +  blob_reset(&sql);
          154  +  pay = cson_new_object();
          155  +  while( (SQLITE_ROW==db_step(&q)) ){
          156  +    cson_object_set(pay,
          157  +                    db_column_text(&q,0),
          158  +                    json_new_string(db_column_text(&q,1)));
          159  +  }
          160  +  db_finalize(&q);
          161  +  return cson_object_value(pay);
          162  +}
          163  +
          164  +/*
          165  +** Impl of /json/config/save.
          166  +**
          167  +** TODOs:
          168  +*/
          169  +static cson_value * json_config_save(){
          170  +  json_set_err(FSL_JSON_E_NYI, NULL);
          171  +  return NULL;
          172  +}
          173  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_detail.h.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +#if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
            3  +#define FOSSIL_JSON_DETAIL_H_INCLUDED
            4  +/*
            5  +** Copyright (c) 2011 D. Richard Hipp
            6  +**
            7  +** This program is free software; you can redistribute it and/or
            8  +** modify it under the terms of the Simplified BSD License (also
            9  +** known as the "2-Clause License" or "FreeBSD License".)
           10  +**
           11  +** This program is distributed in the hope that it will be useful,
           12  +** but without any warranty; without even the implied warranty of
           13  +** merchantability or fitness for a particular purpose.
           14  +**
           15  +** Author contact information:
           16  +**   drh@hwaci.com
           17  +**   http://www.hwaci.com/drh/
           18  +**
           19  +*/
           20  +
           21  +#include "cson_amalgamation.h"
           22  +
           23  +/**
           24  +   FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
           25  +   "significant" change to the JSON API (a change in an interface or
           26  +   new functionality). It is sent as part of the /json/version
           27  +   request. We could arguably add it to each response or even add a
           28  +   version number to each response type, allowing very fine (too
           29  +   fine?) granularity in compatibility change notification. The
           30  +   version number could be included in part of the command dispatching
           31  +   framework, allowing the top-level dispatching code to deal with it
           32  +   (for the most part).
           33  +*/
           34  +#define FOSSIL_JSON_API_VERSION "20120713"
           35  +
           36  +/*
           37  +** Impl details for the JSON API which need to be shared
           38  +** across multiple C files.
           39  +*/
           40  +
           41  +/*
           42  +** The "official" list of Fossil/JSON error codes.  Their values might
           43  +** very well change during initial development but after their first
           44  +** public release they must stay stable.
           45  +**
           46  +** Values must be in the range 1000..9999 for error codes and 1..999
           47  +** for warning codes.
           48  +**
           49  +** Numbers evenly dividable by 100 are "categories", and error codes
           50  +** for a given category have their high bits set to the category
           51  +** value.
           52  +**
           53  +** Maintenance reminder: when entries are added to this list, update
           54  +** the code in json_page_resultCodes() and json_err_cstr() (both in
           55  +** json.c)!
           56  +**
           57  +*/
           58  +enum FossilJsonCodes {
           59  +FSL_JSON_W_START = 0,
           60  +FSL_JSON_W_UNKNOWN /*+1*/,
           61  +FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/,
           62  +FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/,
           63  +FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/,
           64  +FSL_JSON_W_TAG_NOT_FOUND /*+5*/,
           65  +
           66  +FSL_JSON_W_END = 1000,
           67  +FSL_JSON_E_GENERIC = 1000,
           68  +FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100,
           69  +FSL_JSON_E_INVALID_REQUEST /*+1*/,
           70  +FSL_JSON_E_UNKNOWN_COMMAND /*+2*/,
           71  +FSL_JSON_E_UNKNOWN /*+3*/,
           72  +/*REUSE: +4*/
           73  +FSL_JSON_E_TIMEOUT /*+5*/,
           74  +FSL_JSON_E_ASSERT /*+6*/,
           75  +FSL_JSON_E_ALLOC /*+7*/,
           76  +FSL_JSON_E_NYI /*+8*/,
           77  +FSL_JSON_E_PANIC /*+9*/,
           78  +FSL_JSON_E_MANIFEST_READ_FAILED /*+10*/,
           79  +FSL_JSON_E_FILE_OPEN_FAILED /*+11*/,
           80  +
           81  +FSL_JSON_E_AUTH = 2000,
           82  +FSL_JSON_E_MISSING_AUTH /*+1*/,
           83  +FSL_JSON_E_DENIED /*+2*/,
           84  +FSL_JSON_E_WRONG_MODE /*+3*/,
           85  +
           86  +FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH +100,
           87  +FSL_JSON_E_LOGIN_FAILED_NOSEED /*+1*/,
           88  +FSL_JSON_E_LOGIN_FAILED_NONAME /*+2*/,
           89  +FSL_JSON_E_LOGIN_FAILED_NOPW /*+3*/,
           90  +FSL_JSON_E_LOGIN_FAILED_NOTFOUND /*+4*/,
           91  +
           92  +FSL_JSON_E_USAGE = 3000,
           93  +FSL_JSON_E_INVALID_ARGS /*+1*/,
           94  +FSL_JSON_E_MISSING_ARGS /*+2*/,
           95  +FSL_JSON_E_AMBIGUOUS_UUID /*+3*/,
           96  +FSL_JSON_E_UNRESOLVED_UUID /*+4*/,
           97  +FSL_JSON_E_RESOURCE_ALREADY_EXISTS /*+5*/,
           98  +FSL_JSON_E_RESOURCE_NOT_FOUND /*+6*/,
           99  +
          100  +FSL_JSON_E_DB = 4000,
          101  +FSL_JSON_E_STMT_PREP /*+1*/,
          102  +FSL_JSON_E_STMT_BIND /*+2*/,
          103  +FSL_JSON_E_STMT_EXEC /*+3*/,
          104  +FSL_JSON_E_DB_LOCKED /*+4*/,
          105  +
          106  +FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101,
          107  +FSL_JSON_E_DB_NOT_FOUND = FSL_JSON_E_DB + 102,
          108  +FSL_JSON_E_DB_NOT_VALID = FSL_JSON_E_DB + 103
          109  +
          110  +};
          111  +
          112  +
          113  +/*
          114  +** Signature for JSON page/command callbacks. Each callback is
          115  +** responsible for handling one JSON request/command and/or
          116  +** dispatching to sub-commands.
          117  +**
          118  +** By the time the callback is called, json_page_top() (HTTP mode) or
          119  +** json_cmd_top() (CLI mode) will have set up the JSON-related
          120  +** environment. Implementations may generate a "result payload" of any
          121  +** JSON type by returning its value from this function (ownership is
          122  +** transferred to the caller). On error they should set
          123  +** g.json.resultCode to one of the FossilJsonCodes values and return
          124  +** either their payload object or NULL. Note that NULL is a legal
          125  +** success value - it simply means the response will contain no
          126  +** payload. If g.json.resultCode is non-zero when this function
          127  +** returns then the top-level dispatcher will destroy any payload
          128  +** returned by this function and will output a JSON error response
          129  +** instead.
          130  +**
          131  +** All of the setup/response code is handled by the top dispatcher
          132  +** functions and the callbacks concern themselves only with:
          133  +**
          134  +** a) Permissions checking (inspecting g.perm).
          135  +** b) generating a response payload (if applicable)
          136  +** c) Setting g.json's error state (if applicable). See json_set_err().
          137  +**
          138  +** It is imperative that NO callback functions EVER output ANYTHING to
          139  +** stdout, as that will effectively corrupt any JSON output, and
          140  +** almost certainly will corrupt any HTTP response headers. Output
          141  +** sent to stderr ends up in my apache log, so that might be useful
          142  +** for debugging in some cases, but no such code should be left
          143  +** enabled for non-debugging builds.
          144  +*/
          145  +typedef cson_value * (*fossil_json_f)();
          146  +
          147  +/*
          148  +** Holds name-to-function mappings for JSON page/command dispatching.
          149  +**
          150  +** Internally we model page dispatching lists as arrays of these
          151  +** objects, where the final entry in the array has a NULL name value
          152  +** to act as the end-of-list sentinel.
          153  +**
          154  +*/
          155  +typedef struct JsonPageDef{
          156  +  /*
          157  +  ** The commmand/page's name (path, not including leading /json/).
          158  +  **
          159  +  ** Reminder to self: we cannot use sub-paths with commands this way
          160  +  ** without additional string-splitting downstream. e.g. foo/bar.
          161  +  ** Alternately, we can create different JsonPageDef arrays for each
          162  +  ** subset.
          163  +  */
          164  +  char const * name;
          165  +  /*
          166  +  ** Returns a payload object for the response.  If it returns a
          167  +  ** non-NULL value, the caller owns it.  To trigger an error this
          168  +  ** function should set g.json.resultCode to a value from the
          169  +  ** FossilJsonCodes enum. If it sets an error value and returns
          170  +  ** a payload, the payload will be destroyed (not sent with the
          171  +  ** response).
          172  +  */
          173  +  fossil_json_f func;
          174  +  /*
          175  +  ** Which mode(s) of execution does func() support:
          176  +  **
          177  +  ** <0 = CLI only, >0 = HTTP only, 0==both
          178  +  **
          179  +  ** Now that we can simulate POST in CLI mode, the distinction
          180  +  ** between them has disappeared in most (or all) cases, so 0 is
          181  +  ** the standard value.
          182  +  **
          183  +  ** 201207: this is not needed any more. We can get rid of it. Or
          184  +  ** keep it around in case it becomes useful again at some point.
          185  +  */
          186  +  char runMode;
          187  +} JsonPageDef;
          188  +
          189  +/*
          190  +** Holds common keys used for various JSON API properties.
          191  +*/
          192  +typedef struct FossilJsonKeys_{
          193  +  /** maintainers: please keep alpha sorted (case-insensitive) */
          194  +  char const * anonymousSeed;
          195  +  char const * authToken;
          196  +  char const * commandPath;
          197  +  char const * mtime;
          198  +  char const * payload;
          199  +  char const * requestId;
          200  +  char const * resultCode;
          201  +  char const * resultText;
          202  +  char const * timestamp;
          203  +} FossilJsonKeys_;
          204  +extern const FossilJsonKeys_ FossilJsonKeys;
          205  +
          206  +/*
          207  +** A page/command dispatch helper for fossil_json_f() implementations.
          208  +** pages must be an array of JsonPageDef commands which we can
          209  +** dispatch. The final item in the array MUST have a NULL name
          210  +** element.
          211  +**
          212  +** This function takes the command specified in
          213  +** json_command_arg(1+g.json.dispatchDepth) and searches pages for a
          214  +** matching name. If found then that page's func() is called to fetch
          215  +** the payload, which is returned to the caller.
          216  +**
          217  +** On error, g.json.resultCode is set to one of the FossilJsonCodes
          218  +** values and NULL is returned. If non-NULL is returned, ownership is
          219  +** transfered to the caller (but the g.json error state might still be
          220  +** set in that case, so the caller must check that or pass it on up
          221  +** the dispatch chain).
          222  +*/
          223  +cson_value * json_page_dispatch_helper(JsonPageDef const * pages);
          224  +
          225  +/*
          226  +** Convenience wrapper around cson_value_new_string().
          227  +** Returns NULL if str is NULL or on allocation error.
          228  +*/
          229  +cson_value * json_new_string( char const * str );
          230  +
          231  +/*
          232  +** Similar to json_new_string(), but takes a printf()-style format
          233  +** specifiers. Supports the printf extensions supported by fossil's
          234  +** mprintf().  Returns NULL if str is NULL or on allocation error.
          235  +**
          236  +** Maintenance note: json_new_string() is NOT variadic because by the
          237  +** time the variadic form was introduced we already had use cases
          238  +** which segfaulted via json_new_string() because they contain printf
          239  +** markup (e.g. wiki content). Been there, debugged that.
          240  +*/
          241  +cson_value * json_new_string_f( char const * fmt, ... );
          242  +
          243  +/*
          244  +** Returns true if fossil is running in JSON mode and we are either
          245  +** running in HTTP mode OR g.json.post.o is not NULL (meaning POST
          246  +** data was fed in from CLI mode).
          247  +**
          248  +** Specifically, it will return false when any of these apply:
          249  +**
          250  +** a) Not running in JSON mode (via json command or /json path).
          251  +**
          252  +** b) We are running in JSON CLI mode, but no POST data has been fed
          253  +** in.
          254  +**
          255  +** Whether or not we need to take args from CLI or POST data makes a
          256  +** difference in argument/parameter handling in many JSON routines,
          257  +** and thus this distinction.
          258  +*/
          259  +char fossil_has_json();
          260  +
          261  +enum json_get_changed_files_flags {
          262  +    json_get_changed_files_ELIDE_PARENT = 1 << 0
          263  +};
          264  +
          265  +#endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/
          266  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_diff.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +
           19  +#include "config.h"
           20  +#include "json_diff.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +
           28  +/*
           29  +** Generates a diff between two versions (zFrom and zTo), using nContext
           30  +** content lines in the output. On success, returns a new JSON String
           31  +** object. On error it sets g.json's error state and returns NULL.
           32  +**
           33  +** If fSbs is true (non-0) them side-by-side diffs are used.
           34  +**
           35  +** If fHtml is true then HTML markup is added to the diff.
           36  +*/
           37  +cson_value * json_generate_diff(const char *zFrom, const char *zTo,
           38  +                                int nContext, char fSbs,
           39  +                                char fHtml){
           40  +  int fromid;
           41  +  int toid;
           42  +  int outLen;
           43  +  Blob from = empty_blob, to = empty_blob, out = empty_blob;
           44  +  cson_value * rc = NULL;
           45  +  int flags = (DIFF_CONTEXT_MASK & nContext)
           46  +    | (fSbs ? DIFF_SIDEBYSIDE : 0)
           47  +    | (fHtml ? DIFF_HTML : 0);
           48  +  fromid = name_to_typed_rid(zFrom, "*");
           49  +  if(fromid<=0){
           50  +      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
           51  +                   "Could not resolve 'from' ID.");
           52  +      return NULL;
           53  +  }
           54  +  toid = name_to_typed_rid(zTo, "*");
           55  +  if(toid<=0){
           56  +      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
           57  +                   "Could not resolve 'to' ID.");
           58  +      return NULL;
           59  +  }
           60  +  content_get(fromid, &from);
           61  +  content_get(toid, &to);
           62  +  blob_zero(&out);
           63  +  text_diff(&from, &to, &out, 0, flags);
           64  +  blob_reset(&from);
           65  +  blob_reset(&to);
           66  +  outLen = blob_size(&out);
           67  +  if(outLen>=0){
           68  +    rc = cson_value_new_string(blob_buffer(&out),
           69  +                               (unsigned int)blob_size(&out));
           70  +  }
           71  +  blob_reset(&out);
           72  +  return rc;
           73  +}
           74  +
           75  +/*
           76  +** Implementation of the /json/diff page.
           77  +**
           78  +** Arguments:
           79  +**
           80  +** v1=1st version to diff
           81  +** v2=2nd version to diff
           82  +**
           83  +** Can come from GET, POST.payload, CLI -v1/-v2 or as positional
           84  +** parameters following the command name (in HTTP and CLI modes).
           85  +**
           86  +*/
           87  +cson_value * json_page_diff(){
           88  +  cson_object * pay = NULL;
           89  +  cson_value * v = NULL;
           90  +  char const * zFrom;
           91  +  char const * zTo;
           92  +  int nContext = 0;
           93  +  char doSBS;
           94  +  char doHtml;
           95  +  if(!g.perm.Read){
           96  +    json_set_err(FSL_JSON_E_DENIED,
           97  +                 "Requires 'o' permissions.");
           98  +    return NULL;
           99  +  }
          100  +  zFrom = json_find_option_cstr("v1",NULL,NULL);
          101  +  if(!zFrom){
          102  +    zFrom = json_command_arg(2);
          103  +  }
          104  +  if(!zFrom){
          105  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          106  +                 "Required 'v1' parameter is missing.");
          107  +    return NULL;
          108  +  }
          109  +  zTo = json_find_option_cstr("v2",NULL,NULL);
          110  +  if(!zTo){
          111  +    zTo = json_command_arg(3);
          112  +  }
          113  +  if(!zTo){
          114  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          115  +                 "Required 'v2' parameter is missing.");
          116  +    return NULL;
          117  +  }
          118  +  nContext = json_find_option_int("context",NULL,"c",5);
          119  +  doSBS = json_find_option_bool("sbs",NULL,"y",0);
          120  +  doHtml = json_find_option_bool("html",NULL,"h",0);
          121  +  v = json_generate_diff(zFrom, zTo, nContext, doSBS, doHtml);
          122  +  if(!v){
          123  +    if(!g.json.resultCode){
          124  +      json_set_err(FSL_JSON_E_UNKNOWN,
          125  +                   "Generating diff failed for unknown reason.");
          126  +    }
          127  +    return NULL;
          128  +  }
          129  +  pay = cson_new_object();
          130  +  cson_object_set(pay, "from", json_new_string(zFrom));
          131  +  cson_object_set(pay, "to", json_new_string(zTo));
          132  +  cson_object_set(pay, "diff", v);
          133  +  v = 0;
          134  +  
          135  +  return pay ? cson_object_value(pay) : NULL;
          136  +}
          137  +
          138  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_dir.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_dir.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +static cson_value * json_page_dir_list();
           27  +/*
           28  +** Mapping of /json/wiki/XXX commands/paths to callbacks.
           29  +*/
           30  +static const JsonPageDef JsonPageDefs_Dir[] = {
           31  +/* Last entry MUST have a NULL name. */
           32  +{NULL,NULL,0}
           33  +};
           34  +
           35  +#if 0 /* TODO: Not used? */
           36  +static char const * json_dir_path_extra(){
           37  +  static char const * zP = NULL;
           38  +  if( !zP ){
           39  +    zP = g.zExtra;
           40  +    while(zP && *zP && ('/'==*zP)){
           41  +      ++zP;
           42  +    }
           43  +  }
           44  +  return zP;
           45  +}
           46  +#endif
           47  +
           48  +/*
           49  +** Impl of /json/dir. 98% of it was taken directly
           50  +** from browse.c::page_dir()
           51  +*/
           52  +static cson_value * json_page_dir_list(){
           53  +  cson_object * zPayload = NULL; /* return value */
           54  +  cson_array * zEntries = NULL; /* accumulated list of entries. */
           55  +  cson_object * zEntry = NULL;  /* a single dir/file entry. */
           56  +  cson_array * keyStore = NULL; /* garbage collector for shared strings. */
           57  +  cson_string * zKeyName = NULL;
           58  +  cson_string * zKeySize = NULL;
           59  +  cson_string * zKeyIsDir = NULL;
           60  +  cson_string * zKeyUuid = NULL;
           61  +  cson_string * zKeyTime = NULL;
           62  +  cson_string * zKeyRaw = NULL;
           63  +  char * zD = NULL;
           64  +  char const * zDX = NULL;
           65  +  int nD;
           66  +  char * zUuid = NULL;
           67  +  char const * zCI = NULL;
           68  +  Manifest * pM = NULL;
           69  +  Stmt q = empty_Stmt;
           70  +  int rid = 0;
           71  +  if( !g.perm.Read ){
           72  +    json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions.");
           73  +    return NULL;
           74  +  }
           75  +  zCI = json_find_option_cstr("checkin",NULL,"ci" );
           76  +
           77  +  /* If a specific check-in is requested, fetch and parse it.  If the
           78  +  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
           79  +  ** files from all check-ins to be displayed.
           80  +  */
           81  +  if( zCI && *zCI ){
           82  +    pM = manifest_get_by_name(zCI, &rid);
           83  +    if( pM ){
           84  +      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
           85  +    }else{
           86  +      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
           87  +                   "Checkin name [%s] is unresolved.",
           88  +                   zCI);
           89  +      return NULL;
           90  +    }
           91  +  }
           92  +
           93  +  /* Jump through some hoops to find the directory name... */
           94  +  zDX = json_find_option_cstr("name",NULL,NULL);
           95  +  if(!zDX && !g.isHTTP){
           96  +    zDX = json_command_arg(g.json.dispatchDepth+1);
           97  +  }
           98  +  if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){
           99  +    zDX = NULL;
          100  +  }
          101  +  zD = zDX ? fossil_strdup(zDX) : NULL;
          102  +  nD = zD ? strlen(zD)+1 : 0;
          103  +  while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
          104  +
          105  +  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
          106  +                          pathelementFunc, 0, 0);
          107  +
          108  +  /* Compute the temporary table "localfiles" containing the names
          109  +  ** of all files and subdirectories in the zD[] directory.  
          110  +  **
          111  +  ** Subdirectory names begin with "/".  This causes them to sort
          112  +  ** first and it also gives us an easy way to distinguish files
          113  +  ** from directories in the loop that follows.
          114  +  */
          115  +
          116  +  if( zCI ){
          117  +    Stmt ins;
          118  +    ManifestFile *pFile;
          119  +    ManifestFile *pPrev = 0;
          120  +    int nPrev = 0;
          121  +    int c;
          122  +
          123  +    db_multi_exec(
          124  +                  "CREATE TEMP TABLE json_dir_files("
          125  +                  "  n UNIQUE NOT NULL %s," /* file name */
          126  +                  "  fn UNIQUE NOT NULL %s," /* full file name */
          127  +                  "  u DEFAULT NULL," /* file uuid */
          128  +                  "  sz DEFAULT -1," /* file size */
          129  +                  "  mtime DEFAULT NULL" /* file mtime in unix epoch format */
          130  +                  ");",
          131  +                  filename_collation(), filename_collation()
          132  +                  );
          133  +    
          134  +    db_prepare(&ins,
          135  +               "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) "
          136  +               "SELECT"
          137  +               "  pathelement(:path,0),"
          138  +               "  CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath,"
          139  +               "  a.uuid,"
          140  +               "  a.size,"
          141  +               "  CAST(strftime('%%s',e.mtime) AS INTEGER) "
          142  +               "FROM"
          143  +               "  mlink m, "
          144  +               "  event e,"
          145  +               "  blob a,"
          146  +               "  blob b "
          147  +               "WHERE"
          148  +               " e.objid=m.mid"
          149  +               " AND a.rid=m.fid"/*FILE artifact*/
          150  +               " AND b.rid=m.mid"/*CHECKIN artifact*/
          151  +               " AND a.uuid=:uuid",
          152  +               zD, zD
          153  +               );
          154  +    manifest_file_rewind(pM);
          155  +    while( (pFile = manifest_file_next(pM,0))!=0 ){
          156  +      if( nD>0 
          157  +        && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1)))
          158  +      ){
          159  +        continue;
          160  +      }
          161  +      /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/
          162  +      if( pPrev
          163  +       && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0
          164  +       && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/')
          165  +      ){
          166  +        continue;
          167  +      }
          168  +      db_bind_text( &ins, ":path", &pFile->zName[nD] );
          169  +      db_bind_text( &ins, ":abspath", &pFile->zName[nD] );
          170  +      db_bind_text( &ins, ":uuid", pFile->zUuid );
          171  +      db_step(&ins);
          172  +      db_reset(&ins);
          173  +      pPrev = pFile;
          174  +      for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
          175  +      if( c=='/' ) nPrev++;
          176  +    }
          177  +    db_finalize(&ins);
          178  +  }else if( zD && *zD ){
          179  +    if( filenames_are_case_sensitive() ){
          180  +      db_multi_exec(
          181  +        "CREATE TEMP VIEW json_dir_files AS"
          182  +        " SELECT DISTINCT(pathelement(name,%d)) AS n,"
          183  +        " %Q||'/'||name AS fn,"
          184  +        " NULL AS u, NULL AS sz, NULL AS mtime"
          185  +        " FROM filename"
          186  +        "  WHERE name GLOB '%q/*'"
          187  +        " GROUP BY n",
          188  +        nD, zD, zD
          189  +      );
          190  +    }else{
          191  +      db_multi_exec(
          192  +        "CREATE TEMP VIEW json_dir_files AS"
          193  +        " SELECT DISTINCT(pathelement(name,%d)) AS n, "
          194  +        " %Q||'/'||name AS fn,"
          195  +        " NULL AS u, NULL AS sz, NULL AS mtime"
          196  +        " FROM filename"
          197  +        "  WHERE name LIKE '%q/%%'"
          198  +        " GROUP BY n",
          199  +        nD, zD, zD
          200  +      );
          201  +    }
          202  +  }else{
          203  +    db_multi_exec(
          204  +      "CREATE TEMP VIEW json_dir_files"
          205  +      " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn"
          206  +      " FROM filename"
          207  +    );
          208  +  }
          209  +
          210  +  if(zCI){
          211  +    db_prepare( &q, "SELECT"
          212  +                "  n as name,"
          213  +                "  fn as fullname,"
          214  +                "  u as uuid,"
          215  +                "  sz as size,"
          216  +                "  mtime as mtime "
          217  +                "FROM json_dir_files ORDER BY n");
          218  +  }else{/* UUIDs are all NULL. */
          219  +    db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n");
          220  +  }
          221  +
          222  +  zKeyName = cson_new_string("name",4);
          223  +  zKeyUuid = cson_new_string("uuid",4);
          224  +  zKeyIsDir = cson_new_string("isDir",5);
          225  +  keyStore = cson_new_array();
          226  +  cson_array_append( keyStore, cson_string_value(zKeyName) );
          227  +  cson_array_append( keyStore, cson_string_value(zKeyUuid) );
          228  +  cson_array_append( keyStore, cson_string_value(zKeyIsDir) );
          229  +
          230  +  if( zCI ){
          231  +    zKeySize = cson_new_string("size",4);
          232  +    cson_array_append( keyStore, cson_string_value(zKeySize) );
          233  +    zKeyTime = cson_new_string("timestamp",9);
          234  +    cson_array_append( keyStore, cson_string_value(zKeyTime) );
          235  +    zKeyRaw = cson_new_string("downloadPath",12);
          236  +    cson_array_append( keyStore, cson_string_value(zKeyRaw) );
          237  +  }
          238  +  zPayload = cson_new_object();
          239  +  cson_object_set_s( zPayload, zKeyName,
          240  +                     json_new_string((zD&&*zD) ? zD : "/") );
          241  +  if( zUuid ){
          242  +    cson_object_set( zPayload, "checkin", json_new_string(zUuid) );
          243  +  }
          244  +
          245  +  while( (SQLITE_ROW==db_step(&q)) ){
          246  +    cson_value * name = NULL;
          247  +    char const * n = db_column_text(&q,0);
          248  +    char const isDir = ('/'==*n);
          249  +    zEntry = cson_new_object();
          250  +    if(!zEntries){
          251  +      zEntries = cson_new_array();
          252  +      cson_object_set( zPayload, "entries", cson_array_value(zEntries) );
          253  +    }
          254  +    cson_array_append(zEntries, cson_object_value(zEntry) );
          255  +    if(isDir){
          256  +      name = json_new_string( n+1 );
          257  +      cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() );
          258  +    } else{
          259  +      name = json_new_string( n );
          260  +    }
          261  +    cson_object_set_s(zEntry, zKeyName, name );
          262  +    if( zCI && !isDir){
          263  +      /* Don't add the uuid/size for dir entries - that data refers to
          264  +         one of the files in that directory :/. Entries with no
          265  +         --checkin may refer to N versions, and therefore we cannot
          266  +         associate a single size and uuid with them (and fetching all
          267  +         would be overkill for most use cases).
          268  +      */
          269  +      char const * fullName = db_column_text(&q,1);
          270  +      char const * u = db_column_text(&q,2);
          271  +      sqlite_int64 const sz = db_column_int64(&q,3);
          272  +      sqlite_int64 const ts = db_column_int64(&q,4);
          273  +      cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) );
          274  +      cson_object_set_s(zEntry, zKeySize,
          275  +                        cson_value_new_integer( (cson_int_t)sz ));
          276  +      cson_object_set_s(zEntry, zKeyTime,
          277  +          cson_value_new_integer( (cson_int_t)ts ));
          278  +      cson_object_set_s(zEntry, zKeyRaw,
          279  +                        json_new_string_f("/raw/%T?name=%t",
          280  +                                          fullName, u));
          281  +    }
          282  +  }
          283  +  db_finalize(&q);
          284  +  if(pM){
          285  +    manifest_destroy(pM);
          286  +  }
          287  +  cson_free_array( keyStore );
          288  +  
          289  +  free( zUuid );
          290  +  free( zD );
          291  +  return cson_object_value(zPayload);
          292  +}
          293  +
          294  +/*
          295  +** Implements the /json/dir family of pages/commands.
          296  +**
          297  +*/
          298  +cson_value * json_page_dir(){
          299  +#if 1
          300  +  return json_page_dir_list();
          301  +#else
          302  +  return json_page_dispatch_helper(&JsonPageDefs_Dir[0]);
          303  +#endif
          304  +}
          305  +
          306  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_finfo.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_finfo.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +/*
           27  +** Implements the /json/finfo page/command.
           28  +**
           29  +*/
           30  +cson_value * json_page_finfo(){
           31  +  cson_object * pay = NULL;
           32  +  cson_array * checkins = NULL;
           33  +  char const * zFilename = NULL;
           34  +  Blob sql = empty_blob;
           35  +  Stmt q = empty_Stmt;
           36  +  char const * zAfter = NULL;
           37  +  char const * zBefore = NULL;
           38  +  int limit = -1;
           39  +  int currentRow = 0;
           40  +  char const * zCheckin = NULL;
           41  +  char sort = -1;
           42  +  if(!g.perm.Read){
           43  +    json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
           44  +    return NULL;
           45  +  }
           46  +  json_warn( FSL_JSON_W_UNKNOWN, "Achtung: the output of the finfo command is up for change.");
           47  +
           48  +  /* For the "name" argument we have to jump through some hoops to make sure that we don't
           49  +     get the fossil-internally-assigned "name" option.
           50  +  */
           51  +  zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1);
           52  +  if(!zFilename || !*zFilename){
           53  +    json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter.");
           54  +    return NULL;
           55  +  }
           56  +
           57  +  if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){
           58  +    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found.");
           59  +    return NULL;
           60  +  }
           61  +  
           62  +  zBefore = json_find_option_cstr("before",NULL,"b");
           63  +  zAfter = json_find_option_cstr("after",NULL,"a");
           64  +  limit = json_find_option_int("limit",NULL,"n", -1);
           65  +  zCheckin = json_find_option_cstr("checkin",NULL,"ci");
           66  +
           67  +  blob_appendf(&sql, 
           68  +/*0*/   "SELECT b.uuid,"
           69  +/*1*/   "   ci.uuid,"
           70  +/*2*/   "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* Current file uuid */
           71  +/*3*/   "   cast(strftime('%%s',event.mtime) AS INTEGER),"
           72  +/*4*/   "   coalesce(event.euser, event.user),"
           73  +/*5*/   "   coalesce(event.ecomment, event.comment),"
           74  +/*6*/   " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
           75  +/*7*/   "   event.bgcolor,"
           76  +/*8*/   " b.size,"
           77  +/*9*/   " (mlink.pid==0) AS isNew,"
           78  +/*10*/  " (mlink.fid==0) AS isDel"
           79  +	"  FROM mlink, blob b, event, blob ci, filename"
           80  +        " WHERE filename.name=%Q %s"
           81  +        "   AND mlink.fnid=filename.fnid"
           82  +        "   AND b.rid=mlink.fid"
           83  +        "   AND event.objid=mlink.mid"
           84  +        "   AND event.objid=ci.rid",
           85  +        zFilename, filename_collation()
           86  +               );
           87  +
           88  +  if( zCheckin && *zCheckin ){
           89  +    char * zU = NULL;
           90  +    int rc = name_to_uuid2( zCheckin, "ci", &zU );
           91  +    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
           92  +    if(rc<=0){
           93  +      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND,
           94  +                   "Checkin UUID %s.", (rc<0) ? "is ambiguous" : "not found");
           95  +      blob_reset(&sql);
           96  +      return NULL;
           97  +    }
           98  +    blob_appendf(&sql, " AND ci.uuid='%q'", zU);
           99  +    free(zU);
          100  +  }else{
          101  +    if( zAfter && *zAfter ){
          102  +      blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zAfter);
          103  +      sort = 1;
          104  +    }else if( zBefore && *zBefore ){
          105  +      blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zBefore);
          106  +    }
          107  +  }
          108  +
          109  +  blob_appendf(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC"));
          110  +  /*printf("SQL=\n%s\n",blob_str(&sql));*/
          111  +  db_prepare(&q, "%s", blob_str(&sql)/*extra %s to avoid double-expanding
          112  +                                       SQL escapes*/);
          113  +  blob_reset(&sql);
          114  +
          115  +  pay = cson_new_object();
          116  +  cson_object_set(pay, "name", json_new_string(zFilename));
          117  +  if( limit > 0 ){
          118  +    cson_object_set(pay, "limit", json_new_int(limit));
          119  +  }
          120  +  checkins = cson_new_array();
          121  +  cson_object_set(pay, "checkins", cson_array_value(checkins));
          122  +  while( db_step(&q)==SQLITE_ROW ){
          123  +    cson_object * row = cson_new_object();
          124  +    int const isNew = db_column_int(&q,9);
          125  +    int const isDel = db_column_int(&q,10);
          126  +    cson_array_append( checkins, cson_object_value(row) );
          127  +    cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) ));
          128  +    cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) ));
          129  +    /*cson_object_set(row, "parentArtifact", json_new_string( db_column_text(&q,6) ));*/
          130  +    cson_object_set(row, "timestamp", json_new_int( db_column_int(&q,3) ));
          131  +    cson_object_set(row, "user", json_new_string( db_column_text(&q,4) ));
          132  +    cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) ));
          133  +    /*cson_object_set(row, "bgColor", json_new_string( db_column_text(&q,7) ));*/
          134  +    cson_object_set(row, "size", cson_value_new_integer( (cson_int_t)db_column_int64(&q,8) ));
          135  +    cson_object_set(row, "state",
          136  +                    json_new_string(json_artifact_status_to_string(isNew,isDel)));
          137  +    if( (0 < limit) && (++currentRow >= limit) ){
          138  +      break;
          139  +    }
          140  +  }
          141  +  db_finalize(&q);
          142  +
          143  +  return pay ? cson_object_value(pay) : NULL;
          144  +}
          145  +
          146  +
          147  +
          148  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_login.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +
           19  +#include "config.h"
           20  +#include "json_login.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +/*
           28  +** Implementation of the /json/login page.
           29  +**
           30  +*/
           31  +cson_value * json_page_login(){
           32  +  char preciseErrors = /* if true, "complete" JSON error codes are used,
           33  +                          else they are "dumbed down" to a generic login
           34  +                          error code.
           35  +                       */
           36  +#if 1
           37  +    g.json.errorDetailParanoia ? 0 : 1
           38  +#else
           39  +    0
           40  +#endif
           41  +    ;
           42  +  /*
           43  +    FIXME: we want to check the GET/POST args in this order:
           44  +
           45  +    - GET: name, n, password, p
           46  +    - POST: name, password
           47  +
           48  +    but a bug in cgi_parameter() is breaking that, causing PD() to
           49  +    return the last element of the PATH_INFO instead.
           50  +
           51  +    Summary: If we check for P("name") first, then P("n"),
           52  +    then ONLY a GET param of "name" will match ("n"
           53  +    is not recognized). If we reverse the order of the
           54  +    checks then both forms work. Strangely enough, the
           55  +    "p"/"password" check is not affected by this.
           56  +   */
           57  +  char const * name = cson_value_get_cstr(json_req_payload_get("name"));
           58  +  char const * pw = NULL;
           59  +  char const * anonSeed = NULL;
           60  +  cson_value * payload = NULL;
           61  +  int uid = 0;
           62  +  /* reminder to self: Fossil internally (for the sake of /wiki)
           63  +     interprets paths in the form /foo/bar/baz such that P("name") ==
           64  +     "bar/baz". This collides with our name/password checking, and
           65  +     thus we do some rather elaborate name=... checking.
           66  +  */
           67  +  pw = cson_value_get_cstr(json_req_payload_get("password"));
           68  +  if( !pw ){
           69  +    pw = PD("p",NULL);
           70  +    if( !pw ){
           71  +      pw = PD("password",NULL);
           72  +    }
           73  +  }
           74  +  if(!pw){
           75  +    g.json.resultCode = preciseErrors
           76  +      ? FSL_JSON_E_LOGIN_FAILED_NOPW
           77  +      : FSL_JSON_E_LOGIN_FAILED;
           78  +    return NULL;
           79  +  }
           80  +
           81  +  if( !name ){
           82  +    name = PD("n",NULL);
           83  +    if( !name ){
           84  +      name = PD("name",NULL);
           85  +      if( !name ){
           86  +        g.json.resultCode = preciseErrors
           87  +          ? FSL_JSON_E_LOGIN_FAILED_NONAME
           88  +          : FSL_JSON_E_LOGIN_FAILED;
           89  +        return NULL;
           90  +      }
           91  +    }
           92  +  }
           93  +
           94  +  if(0 == strcmp("anonymous",name)){
           95  +    /* check captcha/seed values... */
           96  +    enum { SeedBufLen = 100 /* in some JSON tests i once actually got an
           97  +                           80-digit number.
           98  +                        */
           99  +    };
          100  +    static char seedBuffer[SeedBufLen];
          101  +    cson_value const * jseed = json_getenv(FossilJsonKeys.anonymousSeed);
          102  +    seedBuffer[0] = 0;
          103  +    if( !jseed ){
          104  +      jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed);
          105  +      if( !jseed ){
          106  +        jseed = json_getenv("cs") /* name used by HTML interface */;
          107  +      }
          108  +    }
          109  +    if(jseed){
          110  +      if( cson_value_is_number(jseed) ){
          111  +        sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed));
          112  +        anonSeed = seedBuffer;
          113  +      }else if( cson_value_is_string(jseed) ){
          114  +        anonSeed = cson_string_cstr(cson_value_get_string(jseed));
          115  +      }
          116  +    }
          117  +    if(!anonSeed){
          118  +      g.json.resultCode = preciseErrors
          119  +        ? FSL_JSON_E_LOGIN_FAILED_NOSEED
          120  +        : FSL_JSON_E_LOGIN_FAILED;
          121  +      return NULL;
          122  +    }
          123  +  }
          124  +
          125  +#if 0
          126  +  {
          127  +    /* only for debugging the PD()-incorrect-result problem */
          128  +    cson_object * o = NULL;
          129  +    uid = login_search_uid( name, pw );
          130  +    payload = cson_value_new_object();
          131  +    o = cson_value_get_object(payload);
          132  +    cson_object_set( o, "n", cson_value_new_string(name,strlen(name)));
          133  +    cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw)));
          134  +    return payload;
          135  +  }
          136  +#endif
          137  +  uid = anonSeed
          138  +    ? login_is_valid_anonymous(name, pw, anonSeed)
          139  +    : login_search_uid(name, pw)
          140  +    ;
          141  +  if( !uid ){
          142  +    g.json.resultCode = preciseErrors
          143  +      ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
          144  +      : FSL_JSON_E_LOGIN_FAILED;
          145  +    return NULL;
          146  +  }else{
          147  +    char * cookie = NULL;
          148  +    cson_object * po;
          149  +    char * cap = NULL;
          150  +    if(anonSeed){
          151  +      login_set_anon_cookie(NULL, &cookie);
          152  +    }else{
          153  +      login_set_user_cookie(name, uid, &cookie);
          154  +    }
          155  +    payload = cson_value_new_object();
          156  +    po = cson_value_get_object(payload);
          157  +    cson_object_set(po, "authToken", json_new_string(cookie));
          158  +    free(cookie);
          159  +    cson_object_set(po, "name", json_new_string(name));
          160  +    cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
          161  +    cson_object_set(po, "capabilities", cap ? json_new_string(cap) : cson_value_null() );
          162  +    free(cap);        
          163  +    cson_object_set(po, "loginCookieName", json_new_string( login_cookie_name() ) );
          164  +    /* TODO: add loginExpiryTime to the payload. To do this properly
          165  +       we "should" add an ([unsigned] int *) to
          166  +       login_set_user_cookie() and login_set_anon_cookie(), to which
          167  +       the expiry time is assigned. (Remember that JSON doesn't do
          168  +       unsigned int.)
          169  +
          170  +       For non-anonymous users we could also simply query the
          171  +       user.cexpire db field after calling login_set_user_cookie(),
          172  +       but for anonymous we need to get the time when the cookie is
          173  +       set because anon does not get a db entry like normal users
          174  +       do. Anonymous cookies currently have a hard-coded lifetime in
          175  +       login_set_anon_cookie() (currently 6 hours), which we "should
          176  +       arguably" change to use the time configured for non-anonymous
          177  +       users (see login_set_user_cookie() for details).
          178  +    */
          179  +    return payload;
          180  +  }
          181  +}
          182  +
          183  +/*
          184  +** Impl of /json/logout.
          185  +**
          186  +*/
          187  +cson_value * json_page_logout(){
          188  +  cson_value const *token = g.json.authToken;
          189  +    /* Remember that json_mode_bootstrap() replaces the login cookie
          190  +       with the JSON auth token if the request contains it. If the
          191  +       request is missing the auth token then this will fetch fossil's
          192  +       original cookie. Either way, it's what we want :).
          193  +
          194  +       We require the auth token to avoid someone maliciously
          195  +       trying to log someone else out (not 100% sure if that
          196  +       would be possible, given fossil's hardened cookie, but
          197  +       I'll assume it would be for the time being).
          198  +    */
          199  +    ;
          200  +  if(!token){
          201  +    g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
          202  +  }else{
          203  +    login_clear_login_data();
          204  +    g.json.authToken = NULL /* memory is owned elsewhere.*/;
          205  +    json_setenv(FossilJsonKeys.authToken, NULL);
          206  +  }
          207  +  return json_page_whoami();
          208  +}
          209  +
          210  +/*
          211  +** Implementation of the /json/anonymousPassword page.
          212  +*/
          213  +cson_value * json_page_anon_password(){
          214  +  cson_value * v = cson_value_new_object();
          215  +  cson_object * o = cson_value_get_object(v);
          216  +  unsigned const int seed = captcha_seed();
          217  +  char const * zCaptcha = captcha_decode(seed);
          218  +  cson_object_set(o, "seed",
          219  +                  cson_value_new_integer( (cson_int_t)seed )
          220  +                  );
          221  +  cson_object_set(o, "password",
          222  +                  cson_value_new_string( zCaptcha, strlen(zCaptcha) )
          223  +                  );
          224  +  return v;
          225  +}
          226  +
          227  +
          228  +
          229  +/*
          230  +** Implements the /json/whoami page/command.
          231  +*/
          232  +cson_value * json_page_whoami(){
          233  +  cson_value * payload = NULL;
          234  +  cson_object * obj = NULL;
          235  +  Stmt q;
          236  +  if(!g.json.authToken){
          237  +      /* assume we just logged out. */
          238  +      db_prepare(&q, "SELECT login, cap FROM user WHERE login='nobody'");
          239  +  }
          240  +  else{
          241  +      db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d",
          242  +                 g.userUid);
          243  +  }
          244  +  if( db_step(&q)==SQLITE_ROW ){
          245  +
          246  +    /* reminder: we don't use g.zLogin because it's 0 for the guest
          247  +       user and the HTML UI appears to currently allow the name to be
          248  +       changed (but doing so would break other code). */
          249  +    char const * str;
          250  +    payload = cson_value_new_object();
          251  +    obj = cson_value_get_object(payload);
          252  +    str = (char const *)sqlite3_column_text(q.pStmt,0);
          253  +    if( str ){
          254  +      cson_object_set( obj, "name",
          255  +                       cson_value_new_string(str,strlen(str)) );
          256  +    }
          257  +    str = (char const *)sqlite3_column_text(q.pStmt,1);
          258  +    if( str ){
          259  +      cson_object_set( obj, "capabilities",
          260  +                       cson_value_new_string(str,strlen(str)) );
          261  +    }
          262  +    if( g.json.authToken ){
          263  +      cson_object_set( obj, "authToken", g.json.authToken );
          264  +    }
          265  +  }else{
          266  +    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
          267  +  }
          268  +  db_finalize(&q);
          269  +  return payload;
          270  +}
          271  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_query.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +
           19  +#include "config.h"
           20  +#include "json_query.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +/*
           28  +** Implementation of the /json/query page.
           29  +**
           30  +** Requires admin privileges. Intended primarily to assist me in
           31  +** coming up with JSON output structures for pending features.
           32  +**
           33  +** Options/parameters:
           34  +**
           35  +** sql=string - a SELECT statement
           36  +**
           37  +** format=string 'a' means each row is an Array of values, 'o'
           38  +** (default) creates each row as an Object.
           39  +**
           40  +** TODO: in CLI mode (only) use -S FILENAME to read the sql
           41  +** from a file.
           42  +*/
           43  +cson_value * json_page_query(){
           44  +  char const * zSql = NULL;
           45  +  cson_value * payV;
           46  +  char const * zFmt;
           47  +  Stmt q = empty_Stmt;
           48  +  int check;
           49  +  if(!g.perm.Admin && !g.perm.Setup){
           50  +    json_set_err(FSL_JSON_E_DENIED,
           51  +                 "Requires 'a' or 's' privileges.");
           52  +    return NULL;
           53  +  }
           54  +
           55  +  if( cson_value_is_string(g.json.reqPayload.v) ){
           56  +    zSql = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
           57  +  }else{
           58  +    zSql = json_find_option_cstr2("sql",NULL,"s",2);
           59  +  }
           60  +
           61  +  if(!zSql || !*zSql){
           62  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
           63  +                 "'sql' (-s) argument is missing.");
           64  +    return NULL;
           65  +  }
           66  +
           67  +  zFmt = json_find_option_cstr2("format",NULL,"f",3);
           68  +  if(!zFmt) zFmt = "o";
           69  +  db_prepare(&q,"%s", zSql);
           70  +  if( 0 == sqlite3_column_count( q.pStmt ) ){
           71  +      json_set_err(FSL_JSON_E_USAGE,
           72  +                   "Input query has no result columns. "
           73  +                   "Only SELECT-like queries are supported.");
           74  +      db_finalize(&q);
           75  +      return NULL;
           76  +  }
           77  +  switch(*zFmt){
           78  +    case 'a':
           79  +      check = cson_sqlite3_stmt_to_json(q.pStmt, &payV, 0);
           80  +      break;
           81  +    case 'o':
           82  +    default:
           83  +      check = cson_sqlite3_stmt_to_json(q.pStmt, &payV, 1);
           84  +  };
           85  +  db_finalize(&q);
           86  +  if(0 != check){
           87  +    json_set_err(FSL_JSON_E_UNKNOWN,
           88  +                 "Conversion to JSON failed with cson code #%d (%s).",
           89  +                 check, cson_rc_string(check));
           90  +    assert(NULL==payV);
           91  +  }
           92  +  return payV;
           93  +
           94  +}
           95  +
           96  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_report.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +
           19  +#include "config.h"
           20  +#include "json_report.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +static cson_value * json_report_create();
           28  +static cson_value * json_report_get();
           29  +static cson_value * json_report_list();
           30  +static cson_value * json_report_run();
           31  +static cson_value * json_report_save();
           32  +
           33  +/*
           34  +** Mapping of /json/report/XXX commands/paths to callbacks.
           35  +*/
           36  +static const JsonPageDef JsonPageDefs_Report[] = {
           37  +{"create", json_report_create, 0},
           38  +{"get", json_report_get, 0},
           39  +{"list", json_report_list, 0},
           40  +{"run", json_report_run, 0},
           41  +{"save", json_report_save, 0},
           42  +/* Last entry MUST have a NULL name. */
           43  +{NULL,NULL,0}
           44  +};
           45  +/*
           46  +** Implementation of the /json/report page.
           47  +**
           48  +**
           49  +*/
           50  +cson_value * json_page_report(){
           51  +  if(!g.perm.RdTkt && !g.perm.NewTkt ){
           52  +    json_set_err(FSL_JSON_E_DENIED,
           53  +                 "Requires 'r' or 'n' permissions.");
           54  +    return NULL;
           55  +  }
           56  +  return json_page_dispatch_helper(JsonPageDefs_Report);
           57  +}
           58  +
           59  +/*
           60  +** Searches the environment for a "report" parameter
           61  +** (CLI: -report/-r #).
           62  +**
           63  +** If one is not found and argPos is >0 then json_command_arg()
           64  +** is checked.
           65  +** 
           66  +** Returns >0 (the report number) on success .
           67  +*/
           68  +static int json_report_get_number(int argPos){
           69  +  int nReport = json_find_option_int("report",NULL,"r",-1);
           70  +  if( (nReport<=0) && cson_value_is_integer(g.json.reqPayload.v)){
           71  +    nReport = cson_value_get_integer(g.json.reqPayload.v);
           72  +  }
           73  +  if( (nReport <= 0) && (argPos>0) ){
           74  +    char const * arg = json_command_arg(argPos);
           75  +    if(arg && fossil_isdigit(*arg)) {
           76  +      nReport = atoi(arg);
           77  +    }
           78  +  }
           79  +  return nReport;
           80  +}
           81  +
           82  +static cson_value * json_report_create(){
           83  +  json_set_err(FSL_JSON_E_NYI, NULL);
           84  +  return NULL;
           85  +}
           86  +
           87  +static cson_value * json_report_get(){
           88  +  int nReport;
           89  +  Stmt q = empty_Stmt;
           90  +  cson_value * pay = NULL;
           91  +
           92  +  if(!g.perm.TktFmt){
           93  +    json_set_err(FSL_JSON_E_DENIED,
           94  +                 "Requires 't' privileges.");
           95  +    return NULL;
           96  +  }
           97  +  nReport = json_report_get_number(3);
           98  +  if(nReport <=0){
           99  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          100  +                 "Missing or invalid 'report' (-r) parameter.");
          101  +    return NULL;
          102  +  }
          103  +
          104  +  db_prepare(&q,"SELECT rn AS report,"
          105  +             " owner AS owner,"
          106  +             " title AS title,"
          107  +             " cast(strftime('%%s',mtime) as int) as timestamp,"
          108  +             " cols as columns,"
          109  +             " sqlcode as sqlCode"
          110  +             " FROM reportfmt"
          111  +             " WHERE rn=%d",
          112  +             nReport);
          113  +  if( SQLITE_ROW != db_step(&q) ){
          114  +    db_finalize(&q);
          115  +    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          116  +                 "Report #%d not found.", nReport);
          117  +    return NULL;
          118  +  }
          119  +  pay = cson_sqlite3_row_to_object(q.pStmt);
          120  +  db_finalize(&q);
          121  +  return pay;
          122  +}
          123  +
          124  +/*
          125  +** Impl of /json/report/list.
          126  +*/
          127  +static cson_value * json_report_list(){
          128  +  Blob sql = empty_blob;
          129  +  cson_value * pay = NULL;
          130  +  if(!g.perm.RdTkt){
          131  +    json_set_err(FSL_JSON_E_DENIED,
          132  +                 "Requires 'r' privileges.");
          133  +    return NULL;
          134  +  }
          135  +  blob_append(&sql, "SELECT"
          136  +              " rn AS report,"
          137  +              " title as title,"
          138  +              " owner as owner"
          139  +              " FROM reportfmt"
          140  +              " WHERE 1"
          141  +              " ORDER BY title",
          142  +              -1);
          143  +  pay = json_sql_to_array_of_obj(&sql, NULL, 1);
          144  +  if(!pay){
          145  +    json_set_err(FSL_JSON_E_UNKNOWN,
          146  +                 "Quite unexpected: no ticket reports found.");
          147  +  }
          148  +  return pay;
          149  +}
          150  +
          151  +/*
          152  +** Impl for /json/report/run
          153  +**
          154  +** Options/arguments:
          155  +**
          156  +** report=int (CLI: -report # or -r #) is the report number to run.
          157  +**
          158  +** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands.
          159  +**
          160  +** format=a|o Specifies result format: a=each row is an arry, o=each
          161  +** row is an object.  Default=o.
          162  +*/
          163  +static cson_value * json_report_run(){
          164  +  int nReport;
          165  +  Stmt q = empty_Stmt;
          166  +  cson_object * pay = NULL;
          167  +  cson_array * tktList = NULL;
          168  +  char const * zFmt;
          169  +  char * zTitle = NULL;
          170  +  Blob sql = empty_blob;
          171  +  int limit = 0;
          172  +  cson_value * colNames = NULL;
          173  +  int i;
          174  +
          175  +  if(!g.perm.RdTkt){
          176  +    json_set_err(FSL_JSON_E_DENIED,
          177  +                 "Requires 'r' privileges.");
          178  +    return NULL;
          179  +  }
          180  +  nReport = json_report_get_number(3);
          181  +  if(nReport <=0){
          182  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          183  +                 "Missing or invalid 'number' (-n) parameter.");
          184  +    goto error;
          185  +  }
          186  +  zFmt = json_find_option_cstr2("format",NULL,"f",3);
          187  +  if(!zFmt) zFmt = "o";
          188  +  db_prepare(&q,
          189  +             "SELECT sqlcode, "
          190  +             " title"
          191  +             " FROM reportfmt"
          192  +             " WHERE rn=%d",
          193  +             nReport);
          194  +  if(SQLITE_ROW != db_step(&q)){
          195  +    json_set_err(FSL_JSON_E_INVALID_ARGS,
          196  +                 "Report number %d not found.",
          197  +                 nReport);
          198  +    db_finalize(&q);
          199  +    goto error;
          200  +  }
          201  +
          202  +  limit = json_find_option_int("limit",NULL,"n",-1);
          203  +
          204  +  
          205  +  /* Copy over report's SQL...*/
          206  +  blob_append(&sql, db_column_text(&q,0), -1);
          207  +  zTitle = mprintf("%s", db_column_text(&q,1));
          208  +  db_finalize(&q);
          209  +  db_prepare(&q, "%s", blob_str(&sql));
          210  +
          211  +  /** Build the response... */
          212  +  pay = cson_new_object();
          213  +
          214  +  cson_object_set(pay, "report", json_new_int(nReport));
          215  +  cson_object_set(pay, "title", json_new_string(zTitle));
          216  +  if(limit>0){
          217  +    cson_object_set(pay, "limit", json_new_int((limit<0) ? 0 : limit));
          218  +  }
          219  +  free(zTitle);
          220  +  zTitle = NULL;
          221  +
          222  +  if(g.perm.TktFmt){
          223  +    cson_object_set(pay, "sqlcode",
          224  +                    cson_value_new_string(blob_str(&sql),
          225  +                                          (unsigned int)blob_size(&sql)));
          226  +  }
          227  +  blob_reset(&sql);
          228  +
          229  +  colNames = cson_sqlite3_column_names(q.pStmt);
          230  +  cson_object_set( pay, "columnNames", colNames);
          231  +  for( i = 0 ; ((limit>0) ?(i < limit) : 1)
          232  +         && (SQLITE_ROW == db_step(&q));
          233  +       ++i){
          234  +    cson_value * row = ('a'==*zFmt)
          235  +      ? cson_sqlite3_row_to_array(q.pStmt)
          236  +      : cson_sqlite3_row_to_object2(q.pStmt,
          237  +                                    cson_value_get_array(colNames));
          238  +    ;
          239  +    if(row && !tktList){
          240  +      tktList = cson_new_array();
          241  +    }
          242  +    cson_array_append(tktList, row);
          243  +  }
          244  +  db_finalize(&q);
          245  +  cson_object_set(pay, "tickets",
          246  +                  tktList ? cson_array_value(tktList) : cson_value_null());
          247  +
          248  +  goto end;
          249  +
          250  +  error:
          251  +  assert(0 != g.json.resultCode);
          252  +  cson_value_free( cson_object_value(pay) );
          253  +  pay = NULL;
          254  +  end:
          255  +
          256  +  return pay ? cson_object_value(pay) : NULL;
          257  +
          258  +}
          259  +
          260  +static cson_value * json_report_save(){
          261  +  return NULL;
          262  +}
          263  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_tag.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_tag.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +
           27  +static cson_value * json_tag_add();
           28  +static cson_value * json_tag_cancel();
           29  +static cson_value * json_tag_find();
           30  +static cson_value * json_tag_list();
           31  +/*
           32  +** Mapping of /json/tag/XXX commands/paths to callbacks.
           33  +*/
           34  +static const JsonPageDef JsonPageDefs_Tag[] = {
           35  +{"add", json_tag_add, 0},
           36  +{"cancel", json_tag_cancel, 0},
           37  +{"find", json_tag_find, 0},
           38  +{"list", json_tag_list, 0},
           39  +/* Last entry MUST have a NULL name. */
           40  +{NULL,NULL,0}
           41  +};
           42  +
           43  +/*
           44  +** Implements the /json/tag family of pages/commands.
           45  +**
           46  +*/
           47  +cson_value * json_page_tag(){
           48  +  return json_page_dispatch_helper(&JsonPageDefs_Tag[0]);
           49  +}
           50  +
           51  +
           52  +/*
           53  +** Impl of /json/tag/add.
           54  +*/
           55  +static cson_value * json_tag_add(){
           56  +  cson_value * payV = NULL;
           57  +  cson_object * pay = NULL;
           58  +  char const * zName = NULL;
           59  +  char const * zCheckin = NULL;
           60  +  char fRaw = 0;
           61  +  char fPropagate = 0;
           62  +  char const * zValue = NULL;
           63  +  const char *zPrefix = NULL;
           64  +
           65  +  if( !g.perm.Write ){
           66  +    json_set_err(FSL_JSON_E_DENIED,
           67  +                 "Requires 'i' permissions.");
           68  +    return NULL;
           69  +  }
           70  +  fRaw = json_find_option_bool("raw",NULL,NULL,0);
           71  +  fPropagate = json_find_option_bool("propagate",NULL,NULL,0);
           72  +  zName = json_find_option_cstr("name",NULL,NULL);
           73  +  zPrefix = fRaw ? "" : "sym-";
           74  +  if(!zName || !*zName){
           75  +    if(!fossil_has_json()){
           76  +      zName = json_command_arg(3);
           77  +    }
           78  +    if(!zName || !*zName){
           79  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
           80  +                   "'name' parameter is missing.");
           81  +      return NULL;
           82  +    }
           83  +  }
           84  +  
           85  +  zCheckin = json_find_option_cstr("checkin",NULL,NULL);
           86  +  if( !zCheckin ){
           87  +    if(!fossil_has_json()){
           88  +      zCheckin = json_command_arg(4);
           89  +    }
           90  +    if(!zCheckin || !*zCheckin){
           91  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
           92  +                   "'checkin' parameter is missing.");
           93  +      return NULL;
           94  +    }
           95  +  }
           96  +
           97  +
           98  +  zValue = json_find_option_cstr("value",NULL,NULL);
           99  +  if(!zValue && !fossil_has_json()){
          100  +    zValue = json_command_arg(5);
          101  +  }
          102  +
          103  +  db_begin_transaction();
          104  +  tag_add_artifact(zPrefix, zName, zCheckin, zValue,
          105  +                   1+fPropagate,NULL/*DateOvrd*/,NULL/*UserOvrd*/);
          106  +  db_end_transaction(0);
          107  +
          108  +  payV = cson_value_new_object();
          109  +  pay = cson_value_get_object(payV);
          110  +  cson_object_set(pay, "name", json_new_string(zName) );
          111  +  cson_object_set(pay, "value", (zValue&&*zValue)
          112  +                  ? json_new_string(zValue)
          113  +                  : cson_value_null());
          114  +  cson_object_set(pay, "propagate", cson_value_new_bool(fPropagate));
          115  +  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
          116  +  {
          117  +    Blob uu = empty_blob;
          118  +    int rc;
          119  +    blob_append(&uu, zName, -1);
          120  +    rc = name_to_uuid(&uu, 9, "*");
          121  +    if(0!=rc){
          122  +      json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert name back to UUID!");
          123  +      blob_reset(&uu);
          124  +      goto error;
          125  +    }
          126  +    cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
          127  +    blob_reset(&uu);
          128  +  }
          129  +
          130  +  goto ok;
          131  +  error:
          132  +  assert( 0 != g.json.resultCode );
          133  +  cson_value_free(payV);
          134  +  payV = NULL;
          135  +  ok:
          136  +  return payV;
          137  +}
          138  +
          139  +
          140  +/*
          141  +** Impl of /json/tag/cancel.
          142  +*/
          143  +static cson_value * json_tag_cancel(){
          144  +  char const * zName = NULL;
          145  +  char const * zCheckin = NULL;
          146  +  char fRaw = 0;
          147  +  const char *zPrefix = NULL;
          148  +
          149  +  if( !g.perm.Write ){
          150  +    json_set_err(FSL_JSON_E_DENIED,
          151  +                 "Requires 'i' permissions.");
          152  +    return NULL;
          153  +  }
          154  +
          155  +  fRaw = json_find_option_bool("raw",NULL,NULL,0);
          156  +  zPrefix = fRaw ? "" : "sym-";
          157  +  zName = json_find_option_cstr("name",NULL,NULL);
          158  +  if(!zName || !*zName){
          159  +    if(!fossil_has_json()){
          160  +      zName = json_command_arg(3);
          161  +    }
          162  +    if(!zName || !*zName){
          163  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
          164  +                   "'name' parameter is missing.");
          165  +      return NULL;
          166  +    }
          167  +  }
          168  +  
          169  +  zCheckin = json_find_option_cstr("checkin",NULL,NULL);
          170  +  if( !zCheckin ){
          171  +    if(!fossil_has_json()){
          172  +      zCheckin = json_command_arg(4);
          173  +    }
          174  +    if(!zCheckin || !*zCheckin){
          175  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
          176  +                   "'checkin' parameter is missing.");
          177  +      return NULL;
          178  +    }
          179  +  }
          180  +  /* FIXME?: verify that the tag is currently active. We have no real
          181  +     error case unless we do that.
          182  +  */
          183  +  db_begin_transaction();
          184  +  tag_add_artifact(zPrefix, zName, zCheckin, NULL, 0, 0, 0);
          185  +  db_end_transaction(0);
          186  +  return NULL;
          187  +}
          188  +
          189  +
          190  +/*
          191  +** Impl of /json/tag/find.
          192  +*/
          193  +static cson_value * json_tag_find(){
          194  +  cson_value * payV = NULL;
          195  +  cson_object * pay = NULL;
          196  +  cson_value * listV = NULL;
          197  +  cson_array * list = NULL;
          198  +  char const * zName = NULL;
          199  +  char const * zType = NULL;
          200  +  char const * zType2 = NULL;
          201  +  char fRaw = 0;
          202  +  Stmt q = empty_Stmt;
          203  +  int limit = 0;
          204  +  int tagid = 0;
          205  +
          206  +  if( !g.perm.Read ){
          207  +    json_set_err(FSL_JSON_E_DENIED,
          208  +                 "Requires 'o' permissions.");
          209  +    return NULL;
          210  +  }
          211  +  zName = json_find_option_cstr("name",NULL,NULL);
          212  +  if(!zName || !*zName){
          213  +    if(!fossil_has_json()){
          214  +      zName = json_command_arg(3);
          215  +    }
          216  +    if(!zName || !*zName){
          217  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
          218  +                   "'name' parameter is missing.");
          219  +      return NULL;
          220  +    }
          221  +  }
          222  +  zType = json_find_option_cstr("type",NULL,"t");
          223  +  if(!zType || !*zType){
          224  +    zType = "*";
          225  +    zType2 = zType;
          226  +  }else{
          227  +    switch(*zType){
          228  +      case 'c': zType = "ci"; zType2 = "checkin"; break;
          229  +      case 'e': zType = "e"; zType2 = "event"; break;
          230  +      case 'w': zType = "w"; zType2 = "wiki"; break;
          231  +      case 't': zType = "t"; zType2 = "ticket"; break;
          232  +    }
          233  +  }
          234  +
          235  +  limit = json_find_option_int("limit",NULL,"n",0);
          236  +  fRaw = json_find_option_bool("raw",NULL,NULL,0);
          237  +  
          238  +  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='%s' || %Q",
          239  +                 fRaw ? "" : "sym-",
          240  +                 zName);
          241  +  
          242  +  payV = cson_value_new_object();
          243  +  pay = cson_value_get_object(payV);
          244  +  cson_object_set(pay, "name", json_new_string(zName));
          245  +  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
          246  +  cson_object_set(pay, "type", json_new_string(zType2));
          247  +  cson_object_set(pay, "limit", json_new_int(limit));
          248  +
          249  +#if 1
          250  +  if( tagid<=0 ){
          251  +    cson_object_set(pay,"artifacts", cson_value_null());
          252  +    json_warn(FSL_JSON_W_TAG_NOT_FOUND, "Tag not found.");
          253  +    return payV;
          254  +  }
          255  +#endif
          256  +
          257  +  if( fRaw ){
          258  +    db_prepare(&q,
          259  +               "SELECT blob.uuid FROM tagxref, blob"
          260  +               " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
          261  +               "   AND tagxref.tagtype>0"
          262  +               "   AND blob.rid=tagxref.rid"
          263  +               "%s LIMIT %d",
          264  +               zName,
          265  +               (limit>0)?"":"--", limit
          266  +               );
          267  +    while( db_step(&q)==SQLITE_ROW ){
          268  +      if(!listV){
          269  +        listV = cson_value_new_array();
          270  +        list = cson_value_get_array(listV);
          271  +      }
          272  +      cson_array_append(list, cson_sqlite3_column_to_value(q.pStmt,0));
          273  +    }
          274  +    db_finalize(&q);
          275  +  }else{
          276  +    char const * zSqlBase = /*modified from timeline_query_for_tty()*/
          277  +      " SELECT"
          278  +#if 0
          279  +      "   blob.rid AS rid,"
          280  +#endif
          281  +      "   uuid AS uuid,"
          282  +      "   cast(strftime('%s',event.mtime) as int) AS timestamp,"
          283  +      "   coalesce(ecomment,comment) AS comment,"
          284  +      "   coalesce(euser,user) AS user,"
          285  +      "   CASE event.type"
          286  +      "     WHEN 'ci' THEN 'checkin'"
          287  +      "     WHEN 'w' THEN 'wiki'"
          288  +      "     WHEN 'e' THEN 'event'"
          289  +      "     WHEN 't' THEN 'ticket'"
          290  +      "     ELSE 'unknown'"
          291  +      "   END"
          292  +      "   AS eventType"
          293  +      " FROM event, blob"
          294  +      " WHERE blob.rid=event.objid"
          295  +      ;
          296  +    /* FIXME: re-add tags. */
          297  +    db_prepare(&q,
          298  +               "%s"
          299  +               "  AND event.type GLOB '%q'"
          300  +               "  AND blob.rid IN ("
          301  +               "    SELECT rid FROM tagxref"
          302  +               "      WHERE tagtype>0 AND tagid=%d"
          303  +               "  )"
          304  +               " ORDER BY event.mtime DESC"
          305  +               "%s LIMIT %d",
          306  +               zSqlBase, zType, tagid,
          307  +               (limit>0)?"":"--", limit
          308  +               );
          309  +    listV = json_stmt_to_array_of_obj(&q, NULL);
          310  +    db_finalize(&q);
          311  +  }
          312  +
          313  +  if(!listV) {
          314  +    listV = cson_value_null();
          315  +  }
          316  +  cson_object_set(pay, "artifacts", listV);
          317  +  return payV;
          318  +}
          319  +
          320  +
          321  +/*
          322  +** Impl for /json/tag/list
          323  +**
          324  +** TODOs:
          325  +**
          326  +** Add -type TYPE (ci, w, e, t)
          327  +*/
          328  +static cson_value * json_tag_list(){
          329  +  cson_value * payV = NULL;
          330  +  cson_object * pay = NULL;
          331  +  cson_value const * tagsVal = NULL;
          332  +  char const * zCheckin = NULL;
          333  +  char fRaw = 0;
          334  +  char fTicket = 0;
          335  +  Stmt q = empty_Stmt;
          336  +
          337  +  if( !g.perm.Read ){
          338  +    json_set_err(FSL_JSON_E_DENIED,
          339  +                 "Requires 'o' permissions.");
          340  +    return NULL;
          341  +  }
          342  +
          343  +  fRaw = json_find_option_bool("raw",NULL,NULL,0);
          344  +  fTicket = json_find_option_bool("includeTickets","tkt","t",0);
          345  +  zCheckin = json_find_option_cstr("checkin",NULL,NULL);
          346  +  if( !zCheckin ){
          347  +    zCheckin = json_command_arg( g.json.dispatchDepth + 1);
          348  +    if( !zCheckin && cson_value_is_string(g.json.reqPayload.v) ){
          349  +      zCheckin = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
          350  +      assert(zCheckin);
          351  +    }
          352  +  }
          353  +  payV = cson_value_new_object();
          354  +  pay = cson_value_get_object(payV);
          355  +  cson_object_set(pay, "raw", cson_value_new_bool(fRaw) );
          356  +  if( zCheckin ){
          357  +    /**
          358  +       Tags for a specific checkin. Output format:
          359  +
          360  +       RAW mode:
          361  +    
          362  +       {
          363  +           "sym-tagname": (value || null),
          364  +           ...other tags...
          365  +       }
          366  +
          367  +       Non-raw:
          368  +
          369  +       {
          370  +          "tagname": (value || null),
          371  +          ...other tags...
          372  +       }
          373  +    */
          374  +    cson_value * objV = NULL;
          375  +    cson_object * obj = NULL;
          376  +    int const rid = name_to_rid(zCheckin);
          377  +    if(0==rid){
          378  +      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
          379  +                   "Could not find artifact for checkin [%s].",
          380  +                   zCheckin);
          381  +      goto error;
          382  +    }
          383  +    cson_object_set(pay, "checkin", json_new_string(zCheckin));
          384  +    db_prepare(&q,
          385  +               "SELECT tagname, value FROM tagxref, tag"
          386  +               " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
          387  +               "   AND tagtype>%d"
          388  +               " ORDER BY tagname",
          389  +               rid,
          390  +               fRaw ? -1 : 0
          391  +               );
          392  +    while( SQLITE_ROW == db_step(&q) ){
          393  +      const char *zName = db_column_text(&q, 0);
          394  +      const char *zValue = db_column_text(&q, 1);
          395  +      if( fRaw==0 ){
          396  +        if( 0!=strncmp(zName, "sym-", 4) ) continue;
          397  +        zName += 4;
          398  +        assert( *zName );
          399  +      }
          400  +      if(NULL==objV){
          401  +        objV = cson_value_new_object();
          402  +        obj = cson_value_get_object(objV);
          403  +        tagsVal = objV;
          404  +        cson_object_set( pay, "tags", objV );
          405  +      }
          406  +      if( zValue && zValue[0] ){
          407  +        cson_object_set( obj, zName, json_new_string(zValue) );
          408  +      }else{
          409  +        cson_object_set( obj, zName, cson_value_null() );
          410  +      }
          411  +    }
          412  +    db_finalize(&q);
          413  +  }else{/* all tags */
          414  +    /* Output format:
          415  +
          416  +    RAW mode:
          417  +    
          418  +    ["tagname", "sym-tagname2",...]
          419  +
          420  +    Non-raw:
          421  +
          422  +    ["tagname", "tagname2",...]
          423  +
          424  +    i don't really like the discrepancy in the format but this list
          425  +    can get really long and (A) most tags don't have values, (B) i
          426  +    don't want to bloat it more, and (C) cson_object_set() is O(N)
          427  +    (N=current number of properties) because it uses an unsorted list
          428  +    internally (for memory reasons), so this can slow down appreciably
          429  +    on a long list. The culprit is really tkt- tags, as there is one
          430  +    for each ticket (941 in the main fossil repo as of this writing).
          431  +    */
          432  +    Blob sql = empty_blob;
          433  +    cson_value * arV = NULL;
          434  +    cson_array * ar = NULL;
          435  +    blob_append(&sql,
          436  +                "SELECT tagname FROM tag"
          437  +                " WHERE EXISTS(SELECT 1 FROM tagxref"
          438  +                "               WHERE tagid=tag.tagid"
          439  +                "                 AND tagtype>0)",
          440  +                -1
          441  +                );
          442  +    if(!fTicket){
          443  +      blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1);
          444  +    }
          445  +    blob_append(&sql,
          446  +                " ORDER BY tagname", -1);
          447  +    db_prepare(&q, blob_buffer(&sql));
          448  +    blob_reset(&sql);
          449  +    cson_object_set(pay, "includeTickets", cson_value_new_bool(fTicket) );
          450  +    while( SQLITE_ROW == db_step(&q) ){
          451  +      const char *zName = db_column_text(&q, 0);
          452  +      if(NULL==arV){
          453  +        arV = cson_value_new_array();
          454  +        ar = cson_value_get_array(arV);
          455  +        cson_object_set(pay, "tags", arV);
          456  +        tagsVal = arV;
          457  +      }
          458  +      else if( !fRaw && (0==strncmp(zName, "sym-", 4))){
          459  +        zName += 4;
          460  +        assert( *zName );
          461  +      }
          462  +      cson_array_append(ar, json_new_string(zName));
          463  +    }
          464  +    db_finalize(&q);
          465  +  }
          466  +
          467  +  goto end;
          468  +  error:
          469  +  assert(0 != g.json.resultCode);
          470  +  cson_value_free(payV);
          471  +  payV = NULL;
          472  +  end:
          473  +  if( payV && !tagsVal ){
          474  +    cson_object_set( pay, "tags", cson_value_null() );
          475  +  }
          476  +  return payV;
          477  +}
          478  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_timeline.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +
           19  +#include "VERSION.h"
           20  +#include "config.h"
           21  +#include "json_timeline.h"
           22  +
           23  +#if INTERFACE
           24  +#include "json_detail.h"
           25  +#endif
           26  +
           27  +static cson_value * json_timeline_branch();
           28  +static cson_value * json_timeline_ci();
           29  +static cson_value * json_timeline_ticket();
           30  +/*
           31  +** Mapping of /json/timeline/XXX commands/paths to callbacks.
           32  +*/
           33  +static const JsonPageDef JsonPageDefs_Timeline[] = {
           34  +/* the short forms are only enabled in CLI mode, to avoid
           35  +   that we end up with HTTP clients using 3 different names
           36  +   for the same requests.
           37  +*/
           38  +{"branch", json_timeline_branch, 0},
           39  +{"checkin", json_timeline_ci, 0},
           40  +{"ticket", json_timeline_ticket, 0},
           41  +{"wiki", json_timeline_wiki, 0},
           42  +/* Last entry MUST have a NULL name. */
           43  +{NULL,NULL,0}
           44  +};
           45  +
           46  +
           47  +/*
           48  +** Implements the /json/timeline family of pages/commands. Far from
           49  +** complete.
           50  +**
           51  +*/
           52  +cson_value * json_page_timeline(){
           53  +#if 0
           54  +  /* The original timeline code does not require 'h' access,
           55  +     but it arguably should. For JSON mode i think one could argue
           56  +     that History permissions are required.
           57  +  */
           58  +  if(! g.perm.Hyperlink && !g.perm.Read ){
           59  +    json_set_err(FSL_JSON_E_DENIED, "Timeline requires 'h' or 'o' access.");
           60  +    return NULL;
           61  +  }
           62  +#endif
           63  +  return json_page_dispatch_helper(&JsonPageDefs_Timeline[0]);
           64  +}
           65  +
           66  +/*
           67  +** Create a temporary table suitable for storing timeline data.
           68  +*/
           69  +static void json_timeline_temp_table(void){
           70  +  /* Field order MUST match that from json_timeline_query()!!! */
           71  +  static const char zSql[] = 
           72  +    @ CREATE TEMP TABLE IF NOT EXISTS json_timeline(
           73  +    @   sortId INTEGER PRIMARY KEY,
           74  +    @   rid INTEGER,
           75  +    @   uuid TEXT,
           76  +    @   mtime INTEGER,
           77  +    @   timestampString TEXT,
           78  +    @   comment TEXT,
           79  +    @   user TEXT,
           80  +    @   isLeaf BOOLEAN,
           81  +    @   bgColor TEXT,
           82  +    @   eventType TEXT,
           83  +    @   tags TEXT,
           84  +    @   tagId INTEGER,
           85  +    @   brief TEXT
           86  +    @ )
           87  +  ;
           88  +  db_multi_exec(zSql);
           89  +}
           90  +
           91  +/*
           92  +** Return a pointer to a constant string that forms the basis
           93  +** for a timeline query for the JSON interface.
           94  +*/
           95  +char const * json_timeline_query(void){
           96  +  /* Field order MUST match that from json_timeline_temp_table()!!! */
           97  +  static const char zBaseSql[] =
           98  +    @ SELECT
           99  +    @   NULL,
          100  +    @   blob.rid,
          101  +    @   uuid,
          102  +    @   CAST(strftime('%%s',event.mtime) AS INTEGER),
          103  +    @   datetime(event.mtime,'utc'),
          104  +    @   coalesce(ecomment, comment),
          105  +    @   coalesce(euser, user),
          106  +    @   blob.rid IN leaf,
          107  +    @   bgcolor,
          108  +    @   event.type,
          109  +    @   (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref
          110  +    @     WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
          111  +    @       AND tagxref.rid=blob.rid AND tagxref.tagtype>0) as tags,
          112  +    @   tagid as tagId,
          113  +    @   brief as brief
          114  +    @  FROM event JOIN blob 
          115  +    @ WHERE blob.rid=event.objid
          116  +  ;
          117  +  return zBaseSql;
          118  +}
          119  +
          120  +/*
          121  +** Internal helper to append query information if the
          122  +** "tag" or "branch" request properties (CLI: --tag/--branch)
          123  +** are set. Limits the query to a particular branch/tag.
          124  +**
          125  +** tag works like HTML mode's "t" option and branch works like HTML
          126  +** mode's "r" option. They are very similar, but subtly different -
          127  +** tag mode shows only entries with a given tag but branch mode can
          128  +** also reveal some with "related" tags (meaning they were merged into
          129  +** the requested branch).
          130  +**
          131  +** pSql is the target blob to append the query [subset]
          132  +** to.
          133  +**
          134  +** Returns a positive value if it modifies pSql, 0 if it
          135  +** does not. It returns a negative value if the tag
          136  +** provided to the request was not found (pSql is not modified
          137  +** in that case).
          138  +**
          139  +** If payload is not NULL then on success its "tag" or "branch"
          140  +** property is set to the tag/branch name found in the request.
          141  +**
          142  +** Only one of "tag" or "branch" modes will work at a time, and if
          143  +** both are specified, which one takes precedence is unspecified.
          144  +*/
          145  +static char json_timeline_add_tag_branch_clause(Blob *pSql,
          146  +                                                cson_object * pPayload){
          147  +  char const * zTag = NULL;
          148  +  char const * zBranch = NULL;
          149  +  int tagid = 0;
          150  +  if(! g.perm.Read ){
          151  +    return 0;
          152  +  }
          153  +  zTag = json_find_option_cstr("tag",NULL,NULL);
          154  +  if(!zTag || !*zTag){
          155  +    zBranch = json_find_option_cstr("branch",NULL,NULL);
          156  +    if(!zBranch || !*zBranch){
          157  +      return 0;
          158  +    }
          159  +    zTag = zBranch;
          160  +  }
          161  +  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
          162  +                 zTag);
          163  +  if(tagid<=0){
          164  +    return -1;
          165  +  }
          166  +  if(pPayload){
          167  +    cson_object_set( pPayload, zBranch ? "branch" : "tag", json_new_string(zTag) );
          168  +  }
          169  +  blob_appendf(pSql,
          170  +               " AND ("
          171  +               " EXISTS(SELECT 1 FROM tagxref"
          172  +               "        WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
          173  +               tagid);
          174  +  if(zBranch){
          175  +    /* from "r" flag code in page_timeline().*/
          176  +    blob_appendf(pSql,
          177  +                 " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
          178  +                 "    WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
          179  +                 tagid);
          180  +#if 0 /* from the undocumented "mionly" flag in page_timeline() */
          181  +    blob_appendf(pSql,
          182  +                 " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
          183  +                 "    WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
          184  +                 tagid);
          185  +#endif
          186  +  }
          187  +  blob_append(pSql," ) ",3);
          188  +  return 1;
          189  +}
          190  +/*
          191  +** Helper for the timeline family of functions.  Possibly appends 1
          192  +** AND clause and an ORDER BY clause to pSql, depending on the state
          193  +** of the "after" ("a") or "before" ("b") environment parameters.
          194  +** This function gives "after" precedence over "before", and only
          195  +** applies one of them.
          196  +**
          197  +** Returns -1 if it adds a "before" clause, 1 if it adds
          198  +** an "after" clause, and 0 if adds only an order-by clause.
          199  +*/
          200  +static char json_timeline_add_time_clause(Blob *pSql){
          201  +  char const * zAfter = NULL;
          202  +  char const * zBefore = NULL;
          203  +  int rc = 0;
          204  +  zAfter = json_find_option_cstr("after",NULL,"a");
          205  +  zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b");
          206  +
          207  +  if(zAfter&&*zAfter){
          208  +    while( fossil_isspace(*zAfter) ) ++zAfter;
          209  +    blob_appendf(pSql,
          210  +                 " AND event.mtime>=(SELECT julianday(%Q,'utc')) "
          211  +                 " ORDER BY event.mtime ASC ",
          212  +                 zAfter);
          213  +    rc = 1;
          214  +  }else if(zBefore && *zBefore){
          215  +    while( fossil_isspace(*zBefore) ) ++zBefore;
          216  +    blob_appendf(pSql,
          217  +                 " AND event.mtime<=(SELECT julianday(%Q,'utc')) "
          218  +                 " ORDER BY event.mtime DESC ",
          219  +                 zBefore);
          220  +    rc = -1;
          221  +  }else{
          222  +    blob_append(pSql, " ORDER BY event.mtime DESC ", -1);
          223  +    rc = 0;
          224  +  }
          225  +  return rc;
          226  +}
          227  +
          228  +/*
          229  +** Tries to figure out a timeline query length limit base on
          230  +** environment parameters. If it can it returns that value,
          231  +** else it returns some statically defined default value.
          232  +**
          233  +** Never returns a negative value. 0 means no limit.
          234  +*/
          235  +static int json_timeline_limit(int defaultLimit){
          236  +  int limit = -1;
          237  +  if(!g.isHTTP){/* CLI mode */
          238  +    char const * arg = find_option("limit","n",1);
          239  +    if(arg && *arg){
          240  +      limit = atoi(arg);
          241  +    }
          242  +  }
          243  +  if( (limit<0) && fossil_has_json() ){
          244  +    limit = json_getenv_int("limit",-1);
          245  +  }
          246  +  return (limit<0) ? defaultLimit : limit;
          247  +}
          248  +
          249  +/*
          250  +** Internal helper for the json_timeline_EVENTTYPE() family of
          251  +** functions. zEventType must be one of (ci, w, t). pSql must be a
          252  +** cleanly-initialized, empty Blob to store the sql in. If pPayload is
          253  +** not NULL it is assumed to be the pending response payload. If
          254  +** json_timeline_limit() returns non-0, this function adds a LIMIT
          255  +** clause to the generated SQL.
          256  +**
          257  +** If pPayload is not NULL then this might add properties to pPayload,
          258  +** reflecting options set in the request environment.
          259  +**
          260  +** Returns 0 on success. On error processing should not continue and
          261  +** the returned value should be used as g.json.resultCode.
          262  +*/
          263  +static int json_timeline_setup_sql( char const * zEventType,
          264  +                                    Blob * pSql,
          265  +                                    cson_object * pPayload ){
          266  +  int limit;
          267  +  assert( zEventType && *zEventType && pSql );
          268  +  json_timeline_temp_table();
          269  +  blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1);
          270  +  blob_append(pSql, json_timeline_query(), -1 );
          271  +  blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType);
          272  +  if( json_timeline_add_tag_branch_clause(pSql, pPayload) < 0 ){
          273  +    return FSL_JSON_E_INVALID_ARGS;
          274  +  }
          275  +  json_timeline_add_time_clause(pSql);
          276  +  limit = json_timeline_limit(20);
          277  +  if(limit>0){
          278  +    blob_appendf(pSql,"LIMIT %d ",limit);
          279  +  }
          280  +  if(pPayload){
          281  +    cson_object_set(pPayload, "limit", json_new_int(limit));
          282  +  }
          283  +  return 0;
          284  +}
          285  +
          286  +
          287  +/*
          288  +** If any files are associated with the given rid, a JSON array
          289  +** containing information about them is returned (and is owned by the
          290  +** caller). If no files are associated with it then NULL is returned.
          291  +**
          292  +** flags may optionally be a bitmask of json_get_changed_files flags,
          293  +** or 0 for defaults.
          294  +*/
          295  +cson_value * json_get_changed_files(int rid, int flags){
          296  +  cson_value * rowsV = NULL;
          297  +  cson_array * rows = NULL;
          298  +  Stmt q = empty_Stmt;
          299  +  db_prepare(&q, 
          300  +           "SELECT (pid==0) AS isnew,"
          301  +           "       (fid==0) AS isdel,"
          302  +           "       (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name,"
          303  +           "       blob.uuid as uuid,"
          304  +           "       (SELECT uuid FROM blob WHERE rid=pid) as parent,"
          305  +           "       blob.size as size"
          306  +           "  FROM mlink, blob"
          307  +           " WHERE mid=%d AND pid!=fid"
          308  +           " AND blob.rid=fid "
          309  +           " ORDER BY name /*sort*/",
          310  +             rid
          311  +             );
          312  +  while( (SQLITE_ROW == db_step(&q)) ){
          313  +    cson_value * rowV = cson_value_new_object();
          314  +    cson_object * row = cson_value_get_object(rowV);
          315  +    int const isNew = db_column_int(&q,0);
          316  +    int const isDel = db_column_int(&q,1);
          317  +    char * zDownload = NULL;
          318  +    if(!rowsV){
          319  +      rowsV = cson_value_new_array();
          320  +      rows = cson_value_get_array(rowsV);
          321  +    }
          322  +    cson_array_append( rows, rowV );
          323  +    cson_object_set(row, "name", json_new_string(db_column_text(&q,2)));
          324  +    cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
          325  +    if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
          326  +      cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
          327  +    }
          328  +    cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));
          329  +
          330  +    cson_object_set(row, "state",
          331  +                    json_new_string(json_artifact_status_to_string(isNew,isDel)));
          332  +    zDownload = mprintf("/raw/%s?name=%s",
          333  +                        /* reminder: g.zBaseURL is of course not set for CLI mode. */
          334  +                        db_column_text(&q,2),
          335  +                        db_column_text(&q,3));
          336  +    cson_object_set(row, "downloadPath", json_new_string(zDownload));
          337  +    free(zDownload);
          338  +  }
          339  +  db_finalize(&q);
          340  +  return rowsV;
          341  +}
          342  +
          343  +static cson_value * json_timeline_branch(){
          344  +  cson_value * pay = NULL;
          345  +  Blob sql = empty_blob;
          346  +  Stmt q = empty_Stmt;
          347  +  int limit = 0;
          348  +  if(!g.perm.Read){
          349  +    json_set_err(FSL_JSON_E_DENIED,
          350  +                 "Requires 'o' permissions.");
          351  +    return NULL;
          352  +  }
          353  +  json_timeline_temp_table();
          354  +  blob_append(&sql,
          355  +              "SELECT"
          356  +              "  blob.rid AS rid,"
          357  +              "  uuid AS uuid,"
          358  +              "  CAST(strftime('%s',event.mtime) AS INTEGER) as timestamp,"
          359  +              "  coalesce(ecomment, comment) as comment,"
          360  +              "  coalesce(euser, user) as user,"
          361  +              "  blob.rid IN leaf as isLeaf,"
          362  +              "  bgcolor as bgColor"
          363  +              " FROM event JOIN blob"
          364  +              " WHERE blob.rid=event.objid",
          365  +              -1);
          366  +
          367  +  blob_appendf(&sql,
          368  +               " AND event.type='ci'"
          369  +               " AND blob.rid IN (SELECT rid FROM tagxref"
          370  +               "  WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
          371  +               " ORDER BY event.mtime DESC",
          372  +               TAG_BRANCH);
          373  +  limit = json_timeline_limit(20);
          374  +  if(limit>0){
          375  +    blob_appendf(&sql," LIMIT %d ",limit);
          376  +  }
          377  +  db_prepare(&q,"%s", blob_str(&sql));
          378  +  blob_reset(&sql);
          379  +  pay = json_stmt_to_array_of_obj(&q, NULL);
          380  +  db_finalize(&q);
          381  +  assert(NULL != pay);
          382  +  if(pay){
          383  +    /* get the array-form tags of each record. */
          384  +    cson_string * tags = cson_new_string("tags",4);
          385  +    cson_string * isLeaf = cson_new_string("isLeaf",6);
          386  +    cson_array * ar = cson_value_get_array(pay);
          387  +    cson_object * outer = NULL;
          388  +    unsigned int i = 0;
          389  +    unsigned int len = cson_array_length_get(ar);
          390  +    cson_value_add_reference( cson_string_value(tags) );
          391  +    cson_value_add_reference( cson_string_value(isLeaf) );
          392  +    for( ; i < len; ++i ){
          393  +      cson_object * row = cson_value_get_object(cson_array_get(ar,i));
          394  +      int rid = cson_value_get_integer(cson_object_get(row,"rid"));
          395  +      assert( rid > 0 );
          396  +      cson_object_set_s(row, tags, json_tags_for_checkin_rid(rid,0));
          397  +      cson_object_set_s(row, isLeaf,
          398  +                        json_value_to_bool(cson_object_get(row,"isLeaf")));
          399  +      cson_object_set(row, "rid", NULL)
          400  +        /* remove rid - we don't really want it to be public */;
          401  +    }
          402  +    cson_value_free( cson_string_value(tags) );
          403  +    cson_value_free( cson_string_value(isLeaf) );
          404  +
          405  +    /* now we wrap the payload in an outer shell, for consistency with
          406  +       other /json/timeline/xyz APIs...
          407  +    */
          408  +    outer = cson_new_object();
          409  +    if(limit>0){
          410  +      cson_object_set( outer, "limit", json_new_int(limit) );
          411  +    }
          412  +    cson_object_set( outer, "timeline", pay );
          413  +    pay = cson_object_value(outer);
          414  +  }
          415  +  return pay;
          416  +}
          417  +
          418  +/*
          419  +** Implementation of /json/timeline/ci.
          420  +**
          421  +** Still a few TODOs (like figuring out how to structure
          422  +** inheritance info).
          423  +*/
          424  +static cson_value * json_timeline_ci(){
          425  +  cson_value * payV = NULL;
          426  +  cson_object * pay = NULL;
          427  +  cson_value * tmp = NULL;
          428  +  cson_value * listV = NULL;
          429  +  cson_array * list = NULL;
          430  +  int check = 0;
          431  +  char showFiles = -1/*magic number*/;
          432  +  Stmt q = empty_Stmt;
          433  +  char warnRowToJsonFailed = 0;
          434  +  Blob sql = empty_blob;
          435  +  if( !g.perm.Hyperlink ){
          436  +    /* Reminder to self: HTML impl requires 'o' (Read)
          437  +       rights.
          438  +    */
          439  +    json_set_err( FSL_JSON_E_DENIED, "Checkin timeline requires 'h' access." );
          440  +    return NULL;
          441  +  }
          442  +  showFiles = json_find_option_bool("files",NULL,"f",0);
          443  +  payV = cson_value_new_object();
          444  +  pay = cson_value_get_object(payV);
          445  +  check = json_timeline_setup_sql( "ci", &sql, pay );
          446  +  if(check){
          447  +    json_set_err(check, "Query initialization failed.");
          448  +    goto error;
          449  +  }
          450  +#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
          451  +    json_set_err((cson_rc.AllocError==check)        \
          452  +                 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,\
          453  +                 "Object property insertion failed");     \
          454  +    goto error;\
          455  +  } (void)0
          456  +
          457  +#if 0
          458  +  /* only for testing! */
          459  +  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
          460  +  SET("timelineSql");
          461  +#endif
          462  +  db_multi_exec(blob_buffer(&sql));
          463  +  blob_reset(&sql);
          464  +  db_prepare(&q, "SELECT "
          465  +             " rid AS rid"
          466  +             " FROM json_timeline"
          467  +             " ORDER BY rowid");
          468  +  listV = cson_value_new_array();
          469  +  list = cson_value_get_array(listV);
          470  +  tmp = listV;
          471  +  SET("timeline");
          472  +  while( (SQLITE_ROW == db_step(&q) )){
          473  +    /* convert each row into a JSON object...*/
          474  +    int const rid = db_column_int(&q,0);
          475  +    cson_value * rowV = json_artifact_for_ci(rid, showFiles);
          476  +    cson_object * row = cson_value_get_object(rowV);
          477  +    if(!row){
          478  +      if( !warnRowToJsonFailed ){
          479  +        warnRowToJsonFailed = 1;
          480  +        json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
          481  +                   "Could not convert at least one timeline result row to JSON." );
          482  +      }
          483  +      continue;
          484  +    }
          485  +    cson_array_append(list, rowV);
          486  +  }
          487  +#undef SET
          488  +  goto ok;
          489  +  error:
          490  +  assert( 0 != g.json.resultCode );
          491  +  cson_value_free(payV);
          492  +  payV = NULL;
          493  +  ok:
          494  +  db_finalize(&q);
          495  +  return payV;
          496  +}
          497  +
          498  +/*
          499  +** Implementation of /json/timeline/wiki.
          500  +**
          501  +*/
          502  +cson_value * json_timeline_wiki(){
          503  +  /* This code is 95% the same as json_timeline_ci(), by the way. */
          504  +  cson_value * payV = NULL;
          505  +  cson_object * pay = NULL;
          506  +  cson_array * list = NULL;
          507  +  int check = 0;
          508  +  Stmt q = empty_Stmt;
          509  +  Blob sql = empty_blob;
          510  +  if( !g.perm.RdWiki && !g.perm.Read ){
          511  +    json_set_err( FSL_JSON_E_DENIED, "Wiki timeline requires 'o' or 'j' access.");
          512  +    return NULL;
          513  +  }
          514  +  payV = cson_value_new_object();
          515  +  pay = cson_value_get_object(payV);
          516  +  check = json_timeline_setup_sql( "w", &sql, pay );
          517  +  if(check){
          518  +    json_set_err(check, "Query initialization failed.");
          519  +    goto error;
          520  +  }
          521  +
          522  +#if 0
          523  +  /* only for testing! */
          524  +  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
          525  +  SET("timelineSql");
          526  +#endif
          527  +  db_multi_exec(blob_buffer(&sql));
          528  +  blob_reset(&sql);
          529  +  db_prepare(&q, "SELECT"
          530  +             " uuid AS uuid,"
          531  +             " mtime AS timestamp,"
          532  +#if 0
          533  +             " timestampString AS timestampString,"
          534  +#endif
          535  +             " comment AS comment, "
          536  +             " user AS user,"
          537  +             " eventType AS eventType"
          538  +#if 0
          539  +             /* can wiki pages have tags? */
          540  +             " tags AS tags," /*FIXME: split this into
          541  +                                a JSON array*/
          542  +             " tagId AS tagId,"
          543  +#endif
          544  +             " FROM json_timeline"
          545  +             " ORDER BY rowid",
          546  +             -1);
          547  +  list = cson_new_array();
          548  +  json_stmt_to_array_of_obj(&q, list);
          549  +  cson_object_set(pay, "timeline", cson_array_value(list));
          550  +  goto ok;
          551  +  error:
          552  +  assert( 0 != g.json.resultCode );
          553  +  cson_value_free(payV);
          554  +  payV = NULL;
          555  +  ok:
          556  +  db_finalize(&q);
          557  +  blob_reset(&sql);
          558  +  return payV;
          559  +}
          560  +
          561  +/*
          562  +** Implementation of /json/timeline/ticket.
          563  +**
          564  +*/
          565  +static cson_value * json_timeline_ticket(){
          566  +  /* This code is 95% the same as json_timeline_ci(), by the way. */
          567  +  cson_value * payV = NULL;
          568  +  cson_object * pay = NULL;
          569  +  cson_value * tmp = NULL;
          570  +  cson_value * listV = NULL;
          571  +  cson_array * list = NULL;
          572  +  int check = 0;
          573  +  Stmt q = empty_Stmt;
          574  +  Blob sql = empty_blob;
          575  +  if( !g.perm.RdTkt && !g.perm.Read ){
          576  +    json_set_err(FSL_JSON_E_DENIED, "Ticket timeline requires 'o' or 'r' access.");
          577  +    return NULL;
          578  +  }
          579  +  payV = cson_value_new_object();
          580  +  pay = cson_value_get_object(payV);
          581  +  check = json_timeline_setup_sql( "t", &sql, pay );
          582  +  if(check){
          583  +    json_set_err(check, "Query initialization failed.");
          584  +    goto error;
          585  +  }
          586  +
          587  +  db_multi_exec(blob_buffer(&sql));
          588  +#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
          589  +    json_set_err((cson_rc.AllocError==check)        \
          590  +                 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,      \
          591  +                 "Object property insertion failed."); \
          592  +    goto error;\
          593  +  } (void)0
          594  +
          595  +#if 0
          596  +  /* only for testing! */
          597  +  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
          598  +  SET("timelineSql");
          599  +#endif
          600  +
          601  +  blob_reset(&sql);
          602  +  /*
          603  +    REMINDER/FIXME(?): we have both uuid (the change uuid?)  and
          604  +    ticketUuid (the actual ticket). This is different from the wiki
          605  +    timeline, where we only have the wiki page uuid.
          606  +   */
          607  +  db_prepare(&q, "SELECT rid AS rid,"
          608  +             " uuid AS uuid,"
          609  +             " mtime AS timestamp,"
          610  +#if 0
          611  +             " timestampString AS timestampString,"
          612  +#endif
          613  +             " user AS user,"
          614  +             " eventType AS eventType,"
          615  +             " comment AS comment,"
          616  +             " brief AS briefComment"
          617  +             " FROM json_timeline"
          618  +             " ORDER BY rowid",
          619  +             -1);
          620  +  listV = cson_value_new_array();
          621  +  list = cson_value_get_array(listV);
          622  +  tmp = listV;
          623  +  SET("timeline");
          624  +  while( (SQLITE_ROW == db_step(&q) )){
          625  +    /* convert each row into a JSON object...*/
          626  +    int rc;
          627  +    int const rid = db_column_int(&q,0);
          628  +    Manifest * pMan = NULL;
          629  +    cson_value * rowV;
          630  +    cson_object * row;
          631  +    /*printf("rid=%d\n",rid);*/
          632  +    pMan = manifest_get(rid, CFTYPE_TICKET);
          633  +    if(!pMan){
          634  +      /* this might be an attachment? I'm seeing this with
          635  +         rid 15380, uuid [1292fef05f2472108].
          636  +
          637  +         /json/artifact/1292fef05f2472108 returns not-found,
          638  +         probably because we haven't added artifact/ticket
          639  +         yet(?).
          640  +      */
          641  +      continue;
          642  +    }
          643  +
          644  +    rowV = cson_sqlite3_row_to_object(q.pStmt);
          645  +    row = cson_value_get_object(rowV);
          646  +    if(!row){
          647  +      manifest_destroy(pMan);
          648  +      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
          649  +                 "Could not convert at least one timeline result row to JSON." );
          650  +      continue;
          651  +    }
          652  +    /* FIXME: certainly there's a more efficient way for use to get
          653  +       the ticket UUIDs?
          654  +    */
          655  +    cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid));
          656  +    manifest_destroy(pMan);
          657  +    rc = cson_array_append( list, rowV );
          658  +    if( 0 != rc ){
          659  +      cson_value_free(rowV);
          660  +      g.json.resultCode = (cson_rc.AllocError==rc)
          661  +        ? FSL_JSON_E_ALLOC
          662  +        : FSL_JSON_E_UNKNOWN;
          663  +      goto error;
          664  +    }
          665  +  }
          666  +#undef SET
          667  +  goto ok;
          668  +  error:
          669  +  assert( 0 != g.json.resultCode );
          670  +  cson_value_free(payV);
          671  +  payV = NULL;
          672  +  ok:
          673  +  blob_reset(&sql);
          674  +  db_finalize(&q);
          675  +  return payV;
          676  +}
          677  +
          678  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_user.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_user.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +static cson_value * json_user_get();
           27  +static cson_value * json_user_list();
           28  +static cson_value * json_user_save();
           29  +
           30  +/*
           31  +** Mapping of /json/user/XXX commands/paths to callbacks.
           32  +*/
           33  +static const JsonPageDef JsonPageDefs_User[] = {
           34  +{"save", json_user_save, 0},
           35  +{"get", json_user_get, 0},
           36  +{"list", json_user_list, 0},
           37  +/* Last entry MUST have a NULL name. */
           38  +{NULL,NULL,0}
           39  +};
           40  +
           41  +
           42  +/*
           43  +** Implements the /json/user family of pages/commands.
           44  +**
           45  +*/
           46  +cson_value * json_page_user(){
           47  +  return json_page_dispatch_helper(&JsonPageDefs_User[0]);
           48  +}
           49  +
           50  +
           51  +/*
           52  +** Impl of /json/user/list. Requires admin/setup rights.
           53  +*/
           54  +static cson_value * json_user_list(){
           55  +  cson_value * payV = NULL;
           56  +  Stmt q;
           57  +  if(!g.perm.Admin && !g.perm.Setup){
           58  +    json_set_err(FSL_JSON_E_DENIED,
           59  +                 "Requires 'a' or 's' privileges.");
           60  +    return NULL;
           61  +  }
           62  +  db_prepare(&q,"SELECT uid AS uid,"
           63  +             " login AS name,"
           64  +             " cap AS capabilities,"
           65  +             " info AS info,"
           66  +             " mtime AS timestamp"
           67  +             " FROM user ORDER BY login");
           68  +  payV = json_stmt_to_array_of_obj(&q, NULL);
           69  +  db_finalize(&q);
           70  +  if(NULL == payV){
           71  +    json_set_err(FSL_JSON_E_UNKNOWN,
           72  +                 "Could not convert user list to JSON.");
           73  +  }
           74  +  return payV;  
           75  +}
           76  +
           77  +/*
           78  +** Creates a new JSON Object based on the db state of
           79  +** the given user name. On error (no record found)
           80  +** it returns NULL, else the caller owns the returned
           81  +** object.
           82  +*/
           83  +static cson_value * json_load_user_by_name(char const * zName){
           84  +  cson_value * u = NULL;
           85  +  Stmt q;
           86  +  db_prepare(&q,"SELECT uid AS uid,"
           87  +             " login AS name,"
           88  +             " cap AS capabilities,"
           89  +             " info AS info,"
           90  +             " mtime AS timestamp"
           91  +             " FROM user"
           92  +             " WHERE login=%Q",
           93  +             zName);
           94  +  if( (SQLITE_ROW == db_step(&q)) ){
           95  +    u = cson_sqlite3_row_to_object(q.pStmt);
           96  +  }
           97  +  db_finalize(&q);
           98  +  return u;  
           99  +}
          100  +
          101  +/*
          102  +** Identical to json_load_user_by_name(), but expects a user ID.  Returns
          103  +** NULL if no user found with that ID.
          104  +*/
          105  +static cson_value * json_load_user_by_id(int uid){
          106  +  cson_value * u = NULL;
          107  +  Stmt q;
          108  +  db_prepare(&q,"SELECT uid AS uid,"
          109  +             " login AS name,"
          110  +             " cap AS capabilities,"
          111  +             " info AS info,"
          112  +             " mtime AS timestamp"
          113  +             " FROM user"
          114  +             " WHERE uid=%d",
          115  +             uid);
          116  +  if( (SQLITE_ROW == db_step(&q)) ){
          117  +    u = cson_sqlite3_row_to_object(q.pStmt);
          118  +  }
          119  +  db_finalize(&q);
          120  +  return u;  
          121  +}
          122  +
          123  +
          124  +/*
          125  +** Impl of /json/user/get. Requires admin or setup rights.
          126  +*/
          127  +static cson_value * json_user_get(){
          128  +  cson_value * payV = NULL;
          129  +  char const * pUser = NULL;
          130  +  if(!g.perm.Admin && !g.perm.Setup){
          131  +    json_set_err(FSL_JSON_E_DENIED,
          132  +                 "Requires 'a' or 's' privileges.");
          133  +    return NULL;
          134  +  }
          135  +  pUser = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1);
          136  +  if(!pUser || !*pUser){
          137  +    json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property.");
          138  +    return NULL;
          139  +  }
          140  +  payV = json_load_user_by_name(pUser);
          141  +  if(!payV){
          142  +    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found.");
          143  +  }
          144  +  return payV;  
          145  +}
          146  +
          147  +/*
          148  +** Expects pUser to contain fossil user fields in JSON form: name,
          149  +** uid, info, capabilities, password.
          150  +**
          151  +** At least one of (name, uid) must be included. All others are
          152  +** optional and their db fields will not be updated if those fields
          153  +** are not included in pUser.
          154  +**
          155  +** If uid is specified then name may refer to a _new_ name
          156  +** for a user, otherwise the name must refer to an existing user.
          157  +** If uid=-1 then the name must be specified and a new user is
          158  +** created (fails if one already exists).
          159  +**
          160  +** If uid is not set, this function might modify pUser to contain the
          161  +** db-found (or inserted) user ID.
          162  +**
          163  +** On error g.json's error state is set and one of the FSL_JSON_E_xxx
          164  +** values from FossilJsonCodes is returned.
          165  +**
          166  +** On success the db record for the given user is updated.
          167  +**
          168  +** Requires either Admin, Setup, or Password access. Non-admin/setup
          169  +** users can only change their own information. Non-setup users may
          170  +** not modify the 's' permission. Admin users without setup
          171  +** permissions may not edit any other user who has the 's' permission.
          172  +**
          173  +*/
          174  +int json_user_update_from_json( cson_object * pUser ){
          175  +#define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) ))
          176  +  char const * zName = CSTR("name");
          177  +  char const * zNameNew = zName;
          178  +  char * zNameFree = NULL;
          179  +  char const * zInfo = CSTR("info");
          180  +  char const * zCap = CSTR("capabilities");
          181  +  char const * zPW = CSTR("password");
          182  +  cson_value const * forceLogout = cson_object_get(pUser, "forceLogout");
          183  +  int gotFields = 0;
          184  +#undef CSTR
          185  +  cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") );
          186  +  char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's'));
          187  +  char tgtHadSetup = 0;
          188  +  Blob sql = empty_blob;
          189  +  Stmt q = empty_Stmt;
          190  +
          191  +#if 0
          192  +  if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){
          193  +    return json_set_err( FSL_JSON_E_DENIED,
          194  +                         "Password change requires 'a', 's', "
          195  +                         "or 'p' permissions.");
          196  +  }
          197  +#endif
          198  +  if(uid<=0 && (!zName||!*zName)){
          199  +    return json_set_err(FSL_JSON_E_MISSING_ARGS,
          200  +                        "One of 'uid' or 'name' is required.");
          201  +  }else if(uid>0){
          202  +    zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid);
          203  +    if(!zNameFree){
          204  +      return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          205  +                          "No login found for uid %d.", uid);
          206  +    }
          207  +    zName = zNameFree;
          208  +  }else if(-1==uid){
          209  +    /* try to create a new user */
          210  +    if(!g.perm.Admin && !g.perm.Setup){
          211  +      json_set_err(FSL_JSON_E_DENIED,
          212  +                   "Requires 'a' or 's' privileges.");
          213  +      goto error;
          214  +    }else if(!zName || !*zName){
          215  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
          216  +                   "No name specified for new user.");
          217  +      goto error;
          218  +    }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){
          219  +      json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS,
          220  +                   "User %s already exists.", zName);
          221  +      goto error;
          222  +    }else{
          223  +      Stmt ins = empty_Stmt;
          224  +      db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName);
          225  +      db_step( &ins );
          226  +      db_finalize(&ins);
          227  +      uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName);
          228  +      assert(uid>0);
          229  +      zNameNew = zName;
          230  +      cson_object_set( pUser, "uid", cson_value_new_integer(uid) );
          231  +    }
          232  +  }else{
          233  +    uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName);
          234  +    if(uid<=0){
          235  +      json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          236  +                   "No login found for user [%s].", zName);
          237  +      goto error;
          238  +    }
          239  +    cson_object_set( pUser, "uid", cson_value_new_integer(uid) );
          240  +  }
          241  +
          242  +  /* Maintenance note: all error-returns from here on out should go
          243  +     via 'goto error' in order to clean up.
          244  +  */
          245  +  
          246  +  if(uid != g.userUid){
          247  +    if(!g.perm.Admin && !g.perm.Setup){
          248  +      json_set_err(FSL_JSON_E_DENIED,
          249  +                   "Changing another user's data requires "
          250  +                   "'a' or 's' privileges.");
          251  +      goto error;
          252  +    }
          253  +  }
          254  +  /* check if the target uid currently has setup rights. */
          255  +  tgtHadSetup = db_int(0,"SELECT 1 FROM user where uid=%d"
          256  +                       " AND cap GLOB '*s*'", uid);
          257  +
          258  +  if((tgtHasSetup || tgtHadSetup) && !g.perm.Setup){
          259  +    /*
          260  +      Do not allow a non-setup user to set or remove setup
          261  +      privileges. setup.c uses similar logic.
          262  +    */
          263  +    json_set_err(FSL_JSON_E_DENIED,
          264  +                 "Modifying 's' users/privileges requires "
          265  +                 "'s' privileges.");
          266  +    goto error;
          267  +  }
          268  +  /*
          269  +    Potential todo: do not allow a setup user to remove 's' from
          270  +    himself, to avoid locking himself out?
          271  +  */
          272  +
          273  +  blob_append(&sql, "UPDATE user SET",-1 );
          274  +  blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1);
          275  +
          276  +  if((uid>0) && zNameNew){
          277  +    /* Check for name change... */
          278  +    if(0!=strcmp(zName,zNameNew)){
          279  +      if( (!g.perm.Admin && !g.perm.Setup)
          280  +          && (zName != zNameNew)){
          281  +        json_set_err( FSL_JSON_E_DENIED,
          282  +                      "Modifying user names requires 'a' or 's' privileges.");
          283  +        goto error;
          284  +      }
          285  +      forceLogout = cson_value_true()
          286  +        /* reminders: 1) does not allocate.
          287  +           2) we do this because changing a name
          288  +           invalidates any login token because the old name
          289  +           is part of the token hash.
          290  +        */;
          291  +      blob_appendf(&sql, ", login=%Q", zNameNew);
          292  +      ++gotFields;
          293  +    }
          294  +  }
          295  +
          296  +  if( zCap && *zCap ){
          297  +    if(!g.perm.Admin || !g.perm.Setup){
          298  +      /* we "could" arguably silently ignore cap in this case. */
          299  +      json_set_err(FSL_JSON_E_DENIED,
          300  +                   "Changing capabilities requires 'a' or 's' privileges.");
          301  +      goto error;
          302  +    }
          303  +    blob_appendf(&sql, ", cap=%Q", zCap);
          304  +    ++gotFields;
          305  +  }
          306  +
          307  +  if( zPW && *zPW ){
          308  +    if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){
          309  +      json_set_err( FSL_JSON_E_DENIED,
          310  +                    "Password change requires 'a', 's', "
          311  +                    "or 'p' permissions.");
          312  +      goto error;
          313  +    }else{
          314  +#define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */
          315  +#if !TRY_LOGIN_GROUP
          316  +      char * zPWHash = NULL;
          317  +      ++gotFields;
          318  +      zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL);
          319  +      blob_appendf(&sql, ", pw=%Q", zPWHash);
          320  +      free(zPWHash);
          321  +#else
          322  +      ++gotFields;
          323  +      blob_appendf(&sql, ", pw=coalesce(shared_secret(%Q,%Q,"
          324  +                   "(SELECT value FROM config WHERE name='project-code')))",
          325  +                   zPW, zNameNew ? zNameNew : zName);
          326  +      /* shared_secret() func is undefined? */
          327  +#endif
          328  +    }
          329  +  }
          330  +
          331  +  if( zInfo ){
          332  +    blob_appendf(&sql, ", info=%Q", zInfo);
          333  +    ++gotFields;
          334  +  }
          335  +
          336  +  if((g.perm.Admin || g.perm.Setup)
          337  +     && forceLogout && cson_value_get_bool(forceLogout)){
          338  +    blob_append(&sql, ", cookie=NULL, cexpire=NULL", -1);
          339  +    ++gotFields;
          340  +  }
          341  +  
          342  +  if(!gotFields){
          343  +    json_set_err( FSL_JSON_E_MISSING_ARGS,
          344  +                  "Required user data are missing.");
          345  +    goto error;
          346  +  }
          347  +  assert(uid>0);
          348  +#if !TRY_LOGIN_GROUP
          349  +  blob_appendf(&sql, " WHERE uid=%d", uid);
          350  +#else /* need name for login group support :/ */
          351  +  blob_appendf(&sql, " WHERE login=%Q", zName);
          352  +#endif
          353  +#if 0
          354  +  puts(blob_str(&sql));
          355  +  cson_output_FILE( cson_object_value(pUser), stdout, NULL );
          356  +#endif
          357  +  db_prepare(&q, "%s", blob_str(&sql));
          358  +  db_exec(&q);
          359  +  db_finalize(&q);
          360  +#if TRY_LOGIN_GROUP
          361  +  if( zPW || cson_value_get_bool(forceLogout) ){
          362  +    Blob groupSql = empty_blob;
          363  +    char * zErr = NULL;
          364  +    blob_appendf(&groupSql,
          365  +      "INSERT INTO user(login)"
          366  +      "  SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);",
          367  +      zName, zName
          368  +    );
          369  +    blob_append(&groupSql, blob_str(&sql), blob_size(&sql));
          370  +    login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr);
          371  +    blob_reset(&groupSql);
          372  +    if( zErr ){
          373  +      json_set_err( FSL_JSON_E_UNKNOWN,
          374  +                    "Repo-group update at least partially failed: %s",
          375  +                    zErr);
          376  +      free(zErr);
          377  +      goto error;
          378  +    }
          379  +  }
          380  +#endif /* TRY_LOGIN_GROUP */
          381  +
          382  +#undef TRY_LOGIN_GROUP
          383  +
          384  +  free( zNameFree );
          385  +  blob_reset(&sql);
          386  +  return 0;
          387  +
          388  +  error:
          389  +  assert(0 != g.json.resultCode);
          390  +  free(zNameFree);
          391  +  blob_reset(&sql);
          392  +  return g.json.resultCode;
          393  +}
          394  +
          395  +
          396  +/*
          397  +** Impl of /json/user/save.
          398  +*/
          399  +static cson_value * json_user_save(){
          400  +  /* try to get user info from GET/CLI args and construct
          401  +     a JSON form of it... */
          402  +  cson_object * u = cson_new_object();
          403  +  char const * str = NULL;
          404  +  char b = -1;
          405  +  int i = -1;
          406  +  int uid = -1;
          407  +  cson_value * payload = NULL;
          408  +  /* String properties... */
          409  +#define PROP(LK,SK) str = json_find_option_cstr(LK,NULL,SK);     \
          410  +  if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0
          411  +  PROP("name","n");
          412  +  PROP("password","p");
          413  +  PROP("info","i");
          414  +  PROP("capabilities","c");
          415  +#undef PROP
          416  +  /* Boolean properties... */
          417  +#define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT);     \
          418  +  if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0
          419  +  PROP("forceLogout",-1);
          420  +#undef PROP
          421  +
          422  +#define PROP(LK,DFLT) i = json_find_option_int(LK,NULL,NULL,DFLT);   \
          423  +  if(DFLT != i){ cson_object_set(u, LK, cson_value_new_integer(i)); } (void)0
          424  +  PROP("uid",-99);
          425  +#undef PROP
          426  +  if( g.json.reqPayload.o ){
          427  +    cson_object_merge( u, g.json.reqPayload.o, CSON_MERGE_NO_RECURSE );
          428  +  }
          429  +  json_user_update_from_json( u );
          430  +  if(!g.json.resultCode){
          431  +    uid = cson_value_get_integer( cson_object_get(u, "uid") );
          432  +    assert((uid>0) && "Something went wrong in json_user_update_from_json()");
          433  +    payload = json_load_user_by_id(uid);
          434  +  }
          435  +  cson_free_object(u);
          436  +  return payload;
          437  +}
          438  +#endif /* FOSSIL_ENABLE_JSON */

Added src/json_wiki.c.

            1  +#ifdef FOSSIL_ENABLE_JSON
            2  +/*
            3  +** Copyright (c) 2011-12 D. Richard Hipp
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +**
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*/
           18  +#include "VERSION.h"
           19  +#include "config.h"
           20  +#include "json_wiki.h"
           21  +
           22  +#if INTERFACE
           23  +#include "json_detail.h"
           24  +#endif
           25  +
           26  +static cson_value * json_wiki_create();
           27  +static cson_value * json_wiki_get();
           28  +static cson_value * json_wiki_list();
           29  +static cson_value * json_wiki_preview();
           30  +static cson_value * json_wiki_save();
           31  +static cson_value * json_wiki_diff();
           32  +/*
           33  +** Mapping of /json/wiki/XXX commands/paths to callbacks.
           34  +*/
           35  +static const JsonPageDef JsonPageDefs_Wiki[] = {
           36  +{"create", json_wiki_create, 0},
           37  +{"diff", json_wiki_diff, 0},
           38  +{"get", json_wiki_get, 0},
           39  +{"list", json_wiki_list, 0},
           40  +{"preview", json_wiki_preview, 0},
           41  +{"save", json_wiki_save, 0},
           42  +{"timeline", json_timeline_wiki,0},
           43  +/* Last entry MUST have a NULL name. */
           44  +{NULL,NULL,0}
           45  +};
           46  +
           47  +
           48  +/*
           49  +** Implements the /json/wiki family of pages/commands.
           50  +**
           51  +*/
           52  +cson_value * json_page_wiki(){
           53  +  return json_page_dispatch_helper(JsonPageDefs_Wiki);
           54  +}
           55  +
           56  +/*
           57  +** Returns the UUID for the given wiki blob RID, or NULL if not
           58  +** found. The returned string is allocated via db_text() and must be
           59  +** free()d by the caller.
           60  +*/
           61  +char * json_wiki_get_uuid_for_rid( int rid )
           62  +{
           63  +  return db_text(NULL,
           64  +             "SELECT b.uuid FROM tag t, tagxref x, blob b"
           65  +             " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' "
           66  +             " AND b.rid=x.rid AND b.rid=%d"
           67  +             " ORDER BY x.mtime DESC LIMIT 1",
           68  +             rid
           69  +             );
           70  +}
           71  +
           72  +/*
           73  +** Tries to load a wiki page from the given rid creates a JSON object
           74  +** representation of it. If the page is not found then NULL is
           75  +** returned. If contentFormat is positive then the page content
           76  +** is HTML-ized using fossil's conventional wiki format, if it is
           77  +** negative then no parsing is performed, if it is 0 then the content
           78  +** is not returned in the response. If contentFormat is 0 then the
           79  +** contentSize reflects the number of bytes, not characters, stored in
           80  +** the page.
           81  +**
           82  +** The returned value, if not NULL, is-a JSON Object owned by the
           83  +** caller. If it returns NULL then it may set g.json's error state.
           84  +*/
           85  +cson_value * json_get_wiki_page_by_rid(int rid, char contentFormat){
           86  +  Manifest * pWiki = NULL;
           87  +  if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI)) ){
           88  +    json_set_err( FSL_JSON_E_UNKNOWN,
           89  +                  "Error reading wiki page from manifest (rid=%d).",
           90  +                  rid );
           91  +    return NULL;
           92  +  }else{
           93  +    unsigned int len = 0;
           94  +    cson_object * pay = cson_new_object();
           95  +    char const * zBody = pWiki->zWiki;
           96  +    char const * zFormat = NULL;
           97  +    char * zUuid = json_wiki_get_uuid_for_rid(rid);
           98  +    cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle));
           99  +    cson_object_set(pay,"uuid",json_new_string(zUuid));
          100  +    free(zUuid);
          101  +    zUuid = NULL;
          102  +    if( pWiki->nParent > 0 ){
          103  +      cson_object_set( pay, "parent", json_new_string(pWiki->azParent[0]) )
          104  +        /* Reminder: wiki pages do not branch and have only one parent
          105  +           (except for the initial version, which has no parents). */;
          106  +    }
          107  +    /*cson_object_set(pay,"rid",json_new_int((cson_int_t)rid));*/
          108  +    cson_object_set(pay,"user",json_new_string(pWiki->zUser));
          109  +    cson_object_set(pay,FossilJsonKeys.timestamp,
          110  +                    json_julian_to_timestamp(pWiki->rDate));
          111  +    if(0 == contentFormat){
          112  +      cson_object_set(pay,"size",
          113  +                      json_new_int((cson_int_t)(zBody?strlen(zBody):0)));
          114  +    }else{
          115  +      if( contentFormat>0 ){/*HTML-ize it*/
          116  +        Blob content = empty_blob;
          117  +        Blob raw = empty_blob;
          118  +        zFormat = "html";
          119  +        if(zBody && *zBody){
          120  +          blob_append(&raw,zBody,-1);
          121  +          wiki_convert(&raw,&content,0);
          122  +          len = (unsigned int)blob_size(&content);
          123  +        }
          124  +        cson_object_set(pay,"size",json_new_int((cson_int_t)len));
          125  +        cson_object_set(pay,"content",
          126  +                        cson_value_new_string(blob_buffer(&content),len));
          127  +        blob_reset(&content);
          128  +        blob_reset(&raw);
          129  +      }else{/*raw format*/
          130  +        zFormat = "raw";
          131  +        len = zBody ? strlen(zBody) : 0;
          132  +        cson_object_set(pay,"size",json_new_int((cson_int_t)len));
          133  +        cson_object_set(pay,"content",cson_value_new_string(zBody,len));
          134  +      }
          135  +      cson_object_set(pay,"contentFormat",json_new_string(zFormat));
          136  +
          137  +    }
          138  +    /*TODO: add 'T' (tag) fields*/
          139  +    /*TODO: add the 'A' card (file attachment) entries?*/
          140  +    manifest_destroy(pWiki);
          141  +    return cson_object_value(pay);
          142  +  }  
          143  +}
          144  +
          145  +/*
          146  +** Searches for the latest version of a wiki page with the given
          147  +** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
          148  +** contentFormat), else it returns NULL.
          149  +*/
          150  +cson_value * json_get_wiki_page_by_name(char const * zPageName, char contentFormat){
          151  +  int rid;
          152  +  rid = db_int(0,
          153  +               "SELECT x.rid FROM tag t, tagxref x, blob b"
          154  +               " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
          155  +               " AND b.rid=x.rid"
          156  +               " ORDER BY x.mtime DESC LIMIT 1",
          157  +               zPageName 
          158  +             );
          159  +  if( 0==rid ){
          160  +    json_set_err( FSL_JSON_E_RESOURCE_NOT_FOUND, "Wiki page not found: %s",
          161  +                  zPageName );
          162  +    return NULL;
          163  +  }
          164  +  return json_get_wiki_page_by_rid(rid, contentFormat);
          165  +}
          166  +
          167  +
          168  +/*
          169  +** Searches json_find_option_ctr("format",NULL,"f") for a flag.
          170  +** If not found it returns defaultValue else it returns a value
          171  +** depending on the first character of the format option:
          172  +**
          173  +** [h]tml = 1
          174  +** [n]one = 0
          175  +** [r]aw = -1
          176  +**
          177  +** The return value is intended for use with
          178  +** json_get_wiki_page_by_rid() and friends.
          179  +*/
          180  +char json_wiki_get_content_format_flag( char defaultValue ){
          181  +  char contentFormat = defaultValue;
          182  +  char const * zFormat = json_find_option_cstr("format",NULL,"f");
          183  +  if( !zFormat || !*zFormat ){
          184  +    return contentFormat;
          185  +  }
          186  +  else if('r'==*zFormat){
          187  +    contentFormat = -1;
          188  +  }
          189  +  else if('h'==*zFormat){
          190  +    contentFormat = 1;
          191  +  }
          192  +  else if('n'==*zFormat){
          193  +    contentFormat = 0;
          194  +  }
          195  +  return contentFormat;
          196  +}
          197  +
          198  +/*
          199  +** Helper for /json/wiki/get and /json/wiki/preview. At least one of
          200  +** zPageName (wiki page name) or zSymname must be set to a
          201  +** non-empty/non-NULL value. zSymname takes precedence.  On success
          202  +** the result of one of json_get_wiki_page_by_rid() or
          203  +** json_get_wiki_page_by_name() will be returned (owned by the
          204  +** caller). On error g.json's error state is set and NULL is returned.
          205  +*/
          206  +static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName,
          207  +                                                     char const * zSymname,
          208  +                                                     char contentFormat ){
          209  +  if(!zSymname || !*zSymname){
          210  +    return json_get_wiki_page_by_name(zPageName, contentFormat);
          211  +  }else{
          212  +    int rid = symbolic_name_to_rid( zSymname ? zSymname : zPageName, "w" );
          213  +    if(rid<0){
          214  +      json_set_err(FSL_JSON_E_AMBIGUOUS_UUID,
          215  +                   "UUID [%s] is ambiguous.", zSymname);
          216  +      return NULL;
          217  +    }else if(rid==0){
          218  +      json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          219  +                   "UUID [%s] does not resolve to a wiki page.", zSymname);
          220  +      return NULL;
          221  +    }else{
          222  +      return json_get_wiki_page_by_rid(rid, contentFormat);
          223  +    }
          224  +  }
          225  +}
          226  +
          227  +/*
          228  +** Implementation of /json/wiki/get.
          229  +**
          230  +*/
          231  +static cson_value * json_wiki_get(){
          232  +  char const * zPageName;
          233  +  char const * zSymName = NULL;
          234  +  char contentFormat = -1;
          235  +  if( !g.perm.RdWiki && !g.perm.Read ){
          236  +    json_set_err(FSL_JSON_E_DENIED,
          237  +                 "Requires 'o' or 'j' access.");
          238  +    return NULL;
          239  +  }
          240  +  zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);
          241  +
          242  +  zSymName = json_find_option_cstr("uuid",NULL,"u");
          243  +  
          244  +  if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
          245  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          246  +                 "At least one of the 'name' or 'uuid' arguments must be provided.");
          247  +    return NULL;
          248  +  }
          249  +
          250  +  /* TODO: see if we have a page named zPageName. If not, try to resolve
          251  +     zPageName as a UUID.
          252  +  */
          253  +  
          254  +  contentFormat = json_wiki_get_content_format_flag(contentFormat);
          255  +  return json_wiki_get_by_name_or_symname( zPageName, zSymName, contentFormat );
          256  +}
          257  +
          258  +/*
          259  +** Implementation of /json/wiki/preview.
          260  +**
          261  +*/
          262  +static cson_value * json_wiki_preview(){
          263  +  char const * zContent = NULL;
          264  +  cson_value * pay = NULL;
          265  +  Blob contentOrig = empty_blob;
          266  +  Blob contentHtml = empty_blob;
          267  +  if( !g.perm.WrWiki ){
          268  +    json_set_err(FSL_JSON_E_DENIED,
          269  +                 "Requires 'k' access.");
          270  +    return NULL;
          271  +  }
          272  +  zContent = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
          273  +  if(!zContent) {
          274  +    json_set_err(FSL_JSON_E_MISSING_ARGS,
          275  +                 "The 'payload' property must be a string containing the wiki code to preview.");
          276  +    return NULL;
          277  +  }
          278  +  blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(cson_value_get_string(g.json.reqPayload.v)) );
          279  +  wiki_convert( &contentOrig, &contentHtml, 0 );
          280  +  blob_reset( &contentOrig );
          281  +  pay = cson_value_new_string( blob_str(&contentHtml), (unsigned int)blob_size(&contentHtml));
          282  +  blob_reset( &contentHtml );
          283  +  return pay;
          284  +}
          285  +
          286  +
          287  +/*
          288  +** Internal impl of /wiki/save and /wiki/create. If createMode is 0
          289  +** and the page already exists then a
          290  +** FSL_JSON_E_RESOURCE_ALREADY_EXISTS error is triggered.  If
          291  +** createMode is false then the FSL_JSON_E_RESOURCE_NOT_FOUND is
          292  +** triggered if the page does not already exists.
          293  +**
          294  +** Note that the error triggered when createMode==0 and no such page
          295  +** exists is rather arbitrary - we could just as well create the entry
          296  +** here if it doesn't already exist. With that, save/create would
          297  +** become one operation. That said, i expect there are people who
          298  +** would categorize such behaviour as "being too clever" or "doing too
          299  +** much automatically" (and i would likely agree with them).
          300  +**
          301  +** If allowCreateIfNotExists is true then this function will allow a new
          302  +** page to be created even if createMode is false.
          303  +*/
          304  +static cson_value * json_wiki_create_or_save(char createMode,
          305  +                                             char allowCreateIfNotExists){
          306  +  Blob content = empty_blob;  /* wiki  page content */
          307  +  cson_value * nameV;         /* wiki page name */
          308  +  char const * zPageName;     /* cstr form of page name */
          309  +  cson_value * contentV;      /* passed-in content */
          310  +  cson_value * emptyContent = NULL;  /* placeholder for empty content. */
          311  +  cson_value * payV = NULL;   /* payload/return value */
          312  +  cson_string const * jstr = NULL;  /* temp for cson_value-to-cson_string conversions. */
          313  +  unsigned int contentLen = 0;
          314  +  int rid;
          315  +  if( (createMode && !g.perm.NewWiki)
          316  +      || (!createMode && !g.perm.WrWiki)){
          317  +    json_set_err(FSL_JSON_E_DENIED,
          318  +                 "Requires '%c' permissions.",
          319  +                 (createMode ? 'f' : 'k'));
          320  +    return NULL;
          321  +  }
          322  +  nameV = json_req_payload_get("name");
          323  +  if(!nameV){
          324  +    json_set_err( FSL_JSON_E_MISSING_ARGS,
          325  +                  "'name' parameter is missing.");
          326  +    return NULL;
          327  +  }
          328  +  zPageName = cson_string_cstr(cson_value_get_string(nameV));
          329  +  if(!zPageName || !*zPageName){
          330  +    json_set_err(FSL_JSON_E_INVALID_ARGS,
          331  +                 "'name' parameter must be a non-empty string.");
          332  +    return NULL;
          333  +  }
          334  +  rid = db_int(0,
          335  +     "SELECT x.rid FROM tag t, tagxref x"
          336  +     " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
          337  +     " ORDER BY x.mtime DESC LIMIT 1",
          338  +     zPageName
          339  +  );
          340  +
          341  +  if(rid){
          342  +    if(createMode){
          343  +      json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS,
          344  +                   "Wiki page '%s' already exists.",
          345  +                   zPageName);
          346  +      goto error;
          347  +    }
          348  +  }else if(!createMode && !allowCreateIfNotExists){
          349  +    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          350  +                 "Wiki page '%s' not found.",
          351  +                 zPageName);
          352  +    goto error;
          353  +  }
          354  +
          355  +  contentV = json_req_payload_get("content");
          356  +  if( !contentV ){
          357  +    if( createMode || (!rid && allowCreateIfNotExists) ){
          358  +      contentV = emptyContent = cson_value_new_string("",0);
          359  +    }else{
          360  +      json_set_err(FSL_JSON_E_MISSING_ARGS,
          361  +                   "'content' parameter is missing.");
          362  +      goto error;
          363  +    }
          364  +  }
          365  +  if( !cson_value_is_string(nameV)
          366  +      || !cson_value_is_string(contentV)){
          367  +    json_set_err(FSL_JSON_E_INVALID_ARGS,
          368  +                 "'content' parameter must be a string.");
          369  +    goto error;
          370  +  }
          371  +  jstr = cson_value_get_string(contentV);
          372  +  contentLen = (int)cson_string_length_bytes(jstr);
          373  +  if(contentLen){
          374  +    blob_append(&content, cson_string_cstr(jstr),contentLen);
          375  +  }
          376  +  wiki_cmd_commit(zPageName, 0==rid, &content);
          377  +  blob_reset(&content);
          378  +  /*
          379  +    Our return value here has a race condition: if this operation
          380  +    is called concurrently for the same wiki page via two requests,
          381  +    payV could reflect the results of the other save operation.
          382  +  */
          383  +  payV = json_get_wiki_page_by_name(
          384  +           cson_string_cstr(
          385  +             cson_value_get_string(nameV)),
          386  +             0);
          387  +  goto ok;
          388  +  error:
          389  +  assert( 0 != g.json.resultCode );
          390  +  assert( NULL == payV );
          391  +  ok:
          392  +  if( emptyContent ){
          393  +    /* We have some potentially tricky memory ownership
          394  +       here, which is why we handle emptyContent separately.
          395  +
          396  +       This is, in fact, overkill because cson_value_new_string("",0)
          397  +       actually returns a shared singleton instance (i.e. doesn't
          398  +       allocate), but that is a cson implementation detail which i
          399  +       don't want leaking into this code...
          400  +    */
          401  +    cson_value_free(emptyContent);
          402  +  }
          403  +  return payV;
          404  +
          405  +}
          406  +
          407  +/*
          408  +** Implementation of /json/wiki/create.
          409  +*/
          410  +static cson_value * json_wiki_create(){
          411  +  return json_wiki_create_or_save(1,0);
          412  +}
          413  +
          414  +/*
          415  +** Implementation of /json/wiki/save.
          416  +*/
          417  +static cson_value * json_wiki_save(){
          418  +  char const createIfNotExists = json_getenv_bool("createIfNotExists",0);
          419  +  return json_wiki_create_or_save(0,createIfNotExists);
          420  +}
          421  +
          422  +/*
          423  +** Implementation of /json/wiki/list.
          424  +*/
          425  +static cson_value * json_wiki_list(){
          426  +  cson_value * listV = NULL;
          427  +  cson_array * list = NULL;
          428  +  char const * zGlob = NULL;
          429  +  Stmt q = empty_Stmt;
          430  +  Blob sql = empty_blob;
          431  +  char const verbose = json_find_option_bool("verbose",NULL,"v",0);
          432  +  char fInvert = json_find_option_bool("invert",NULL,"i",0);;
          433  +
          434  +  if( !g.perm.RdWiki && !g.perm.Read ){
          435  +    json_set_err(FSL_JSON_E_DENIED,
          436  +                 "Requires 'j' or 'o' permissions.");
          437  +    return NULL;
          438  +  }
          439  +  blob_append(&sql,"SELECT"
          440  +              " substr(tagname,6) as name"
          441  +              " FROM tag WHERE tagname GLOB 'wiki-*'",
          442  +              -1);
          443  +  zGlob = json_find_option_cstr("glob",NULL,"g");
          444  +  if(zGlob && *zGlob){
          445  +    blob_appendf(&sql," AND name %s GLOB %Q",
          446  +                 fInvert ? "NOT" : "", zGlob);
          447  +  }else{
          448  +    zGlob = json_find_option_cstr("like",NULL,"l");
          449  +    if(zGlob && *zGlob){
          450  +      blob_appendf(&sql," AND name %s LIKE %Q",
          451  +                   fInvert ? "NOT" : "",
          452  +                   zGlob);
          453  +    }
          454  +  }
          455  +  blob_append(&sql," ORDER BY lower(name)", -1);
          456  +  db_prepare(&q,"%s", blob_str(&sql));
          457  +  blob_reset(&sql);
          458  +  listV = cson_value_new_array();
          459  +  list = cson_value_get_array(listV);
          460  +  while( SQLITE_ROW == db_step(&q) ){
          461  +    cson_value * v;
          462  +    if( verbose ){
          463  +      char const * name = db_column_text(&q,0);
          464  +      v = json_get_wiki_page_by_name(name,0);
          465  +    }else{
          466  +      v = cson_sqlite3_column_to_value(q.pStmt,0);
          467  +    }
          468  +    if(!v){
          469  +      json_set_err(FSL_JSON_E_UNKNOWN,
          470  +                   "Could not convert wiki name column to JSON.");
          471  +      goto error;
          472  +    }else if( 0 != cson_array_append( list, v ) ){
          473  +      cson_value_free(v);
          474  +      json_set_err(FSL_JSON_E_ALLOC,"Could not append wiki page name to array.")
          475  +        /* OOM (or maybe numeric overflow) are the only realistic
          476  +           error codes for that particular failure.*/;
          477  +      goto error;
          478  +    }
          479  +  }
          480  +  goto end;
          481  +  error:
          482  +  assert(0 != g.json.resultCode);
          483  +  cson_value_free(listV);
          484  +  listV = NULL;
          485  +  end:
          486  +  db_finalize(&q);
          487  +  return listV;
          488  +}
          489  +
          490  +static cson_value * json_wiki_diff(){
          491  +  char const * zV1 = NULL;
          492  +  char const * zV2 = NULL;
          493  +  cson_object * pay = NULL;
          494  +  int argPos = g.json.dispatchDepth;
          495  +  int r1 = 0, r2 = 0;
          496  +  Manifest * pW1 = NULL, *pW2 = NULL;
          497  +  Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob;
          498  +  char const * zErrTag = NULL;
          499  +  u64 diffFlags;
          500  +  char * zUuid = NULL;
          501  +  if( !g.perm.Hyperlink ){
          502  +    json_set_err(FSL_JSON_E_DENIED,
          503  +                 "Requires 'h' permissions.");
          504  +    return NULL;
          505  +  }
          506  +
          507  +  
          508  +  zV1 = json_find_option_cstr2( "v1",NULL, NULL, ++argPos );
          509  +  zV2 = json_find_option_cstr2( "v2",NULL, NULL, ++argPos );
          510  +  if(!zV1 || !*zV1 || !zV2 || !*zV2) {
          511  +    json_set_err(FSL_JSON_E_INVALID_ARGS,
          512  +                 "Requires both 'v1' and 'v2' arguments.");
          513  +    return NULL;
          514  +  }
          515  +  
          516  +  r1 = symbolic_name_to_rid( zV1, "w" );
          517  +  zErrTag = zV1;
          518  +  if(r1<0){
          519  +    goto ambiguous;
          520  +  }else if(0==r1){
          521  +    goto invalid;
          522  +  }
          523  +
          524  +  r2 = symbolic_name_to_rid( zV2, "w" );
          525  +  zErrTag = zV2;
          526  +  if(r2<0){
          527  +    goto ambiguous;
          528  +  }else if(0==r2){
          529  +    goto invalid;
          530  +  }
          531  +
          532  +  zErrTag = zV1;
          533  +  pW1 = manifest_get(r1, CFTYPE_WIKI);
          534  +  if( pW1==0 ) {
          535  +    goto manifest;
          536  +  }
          537  +  zErrTag = zV2;
          538  +  pW2 = manifest_get(r2, CFTYPE_WIKI);
          539  +  if( pW2==0 ) {
          540  +    goto manifest;
          541  +  }
          542  +
          543  +  blob_init(&w1, pW1->zWiki, -1);
          544  +  blob_zero(&w2);
          545  +  blob_init(&w2, pW2->zWiki, -1);
          546  +  blob_zero(&d);
          547  +  diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE;
          548  +  text_diff(&w2, &w1, &d, 0, diffFlags);
          549  +  blob_reset(&w1);
          550  +  blob_reset(&w2);
          551  +
          552  +  pay = cson_new_object();
          553  +  
          554  +  zUuid = json_wiki_get_uuid_for_rid( pW1->rid );
          555  +  cson_object_set(pay, "v1", json_new_string(zUuid) );
          556  +  free(zUuid);
          557  +  zUuid = json_wiki_get_uuid_for_rid( pW2->rid );
          558  +  cson_object_set(pay, "v2", json_new_string(zUuid) );
          559  +  free(zUuid);
          560  +  zUuid = NULL;
          561  +
          562  +  manifest_destroy(pW1);
          563  +  manifest_destroy(pW2);
          564  +
          565  +  cson_object_set(pay, "diff",
          566  +                  cson_value_new_string( blob_str(&d),
          567  +                                         (unsigned int)blob_size(&d)));
          568  +  
          569  +  return cson_object_value(pay);
          570  +
          571  +  manifest:
          572  +  json_set_err(FSL_JSON_E_UNKNOWN,
          573  +               "Could not load wiki manifest for UUID [%s].",
          574  +               zErrTag);
          575  +  goto end;
          576  +
          577  +  ambiguous:
          578  +  json_set_err(FSL_JSON_E_AMBIGUOUS_UUID,
          579  +               "UUID [%s] is ambiguous.", zErrTag);
          580  +  goto end;
          581  +
          582  +  invalid:
          583  +  json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
          584  +               "UUID [%s] not found.", zErrTag);
          585  +  goto end;
          586  +
          587  +  end:
          588  +  cson_free_object(pay);
          589  +  return NULL;
          590  +}
          591  +
          592  +#endif /* FOSSIL_ENABLE_JSON */

Added src/leaf.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to manage the "leaf" table of the
           19  +** repository.
           20  +**
           21  +** The LEAF table contains the rids for all leaves in the checkin DAG.
           22  +** A leaf is a checkin that has no children in the same branch.
           23  +*/
           24  +#include "config.h"
           25  +#include "leaf.h"
           26  +#include <assert.h>
           27  +
           28  +
           29  +/*
           30  +** Return true if the check-in with RID=rid is a leaf.
           31  +**
           32  +** A leaf has no children in the same branch. 
           33  +*/
           34  +int is_a_leaf(int rid){
           35  +  int rc;
           36  +  static const char zSql[] = 
           37  +    @ SELECT 1 FROM plink
           38  +    @  WHERE pid=%d
           39  +    @    AND coalesce((SELECT value FROM tagxref
           40  +    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
           41  +    @       =coalesce((SELECT value FROM tagxref
           42  +    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
           43  +  ;
           44  +  rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH);
           45  +  return rc==0;
           46  +}
           47  +
           48  +/*
           49  +** Count the number of primary non-branch children for the given check-in.
           50  +**
           51  +** A primary child is one where the parent is the primary parent, not
           52  +** a merge parent.  A "leaf" is a node that has zero children of any
           53  +** kind.  This routine counts only primary children.
           54  +**
           55  +** A non-branch child is one which is on the same branch as the parent.
           56  +*/
           57  +int count_nonbranch_children(int pid){
           58  +  int nNonBranch = 0;
           59  +  static Stmt q;
           60  +  static const char zSql[] = 
           61  +    @ SELECT count(*) FROM plink
           62  +    @  WHERE pid=:pid AND isprim
           63  +    @    AND coalesce((SELECT value FROM tagxref
           64  +    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
           65  +    @       =coalesce((SELECT value FROM tagxref
           66  +    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
           67  +  ;
           68  +  db_static_prepare(&q, zSql, TAG_BRANCH, TAG_BRANCH);
           69  +  db_bind_int(&q, ":pid", pid);
           70  +  if( db_step(&q)==SQLITE_ROW ){
           71  +    nNonBranch = db_column_int(&q, 0);
           72  +  }
           73  +  db_reset(&q);
           74  +  return nNonBranch;
           75  +}
           76  +
           77  +
           78  +/*
           79  +** Recompute the entire LEAF table.  
           80  +**
           81  +** This can be expensive (5 seconds or so) for a really large repository.
           82  +** So it is only done for things like a rebuild.
           83  +*/
           84  +void leaf_rebuild(void){
           85  +  db_multi_exec(
           86  +    "DELETE FROM leaf;"
           87  +    "INSERT OR IGNORE INTO leaf"
           88  +    "  SELECT cid FROM plink"
           89  +    "  EXCEPT"
           90  +    "  SELECT pid FROM plink"
           91  +    "   WHERE coalesce((SELECT value FROM tagxref"
           92  +                       " WHERE tagid=%d AND rid=plink.pid),'trunk')"
           93  +         " == coalesce((SELECT value FROM tagxref"
           94  +                       " WHERE tagid=%d AND rid=plink.cid),'trunk')",
           95  +    TAG_BRANCH, TAG_BRANCH
           96  +  );
           97  +}
           98  +
           99  +/*
          100  +** A bag of checkins whose leaf status needs to be checked.
          101  +*/
          102  +static Bag needToCheck;
          103  +
          104  +/*
          105  +** Check to see if checkin "rid" is a leaf and either add it to the LEAF
          106  +** table if it is, or remove it if it is not.
          107  +*/
          108  +void leaf_check(int rid){
          109  +  static Stmt checkIfLeaf;
          110  +  static Stmt addLeaf;
          111  +  static Stmt removeLeaf;
          112  +  int rc;
          113  +
          114  +  db_static_prepare(&checkIfLeaf,
          115  +    "SELECT 1 FROM plink"
          116  +    " WHERE pid=:rid"
          117  +    "   AND coalesce((SELECT value FROM tagxref"
          118  +                    " WHERE tagid=%d AND rid=:rid),'trunk')"
          119  +       " == coalesce((SELECT value FROM tagxref"
          120  +                    " WHERE tagid=%d AND rid=plink.cid),'trunk');",
          121  +    TAG_BRANCH, TAG_BRANCH
          122  +  );
          123  +  db_bind_int(&checkIfLeaf, ":rid", rid);
          124  +  rc = db_step(&checkIfLeaf);
          125  +  db_reset(&checkIfLeaf);
          126  +  if( rc==SQLITE_ROW ){
          127  +    db_static_prepare(&removeLeaf, "DELETE FROM leaf WHERE rid=:rid");
          128  +    db_bind_int(&removeLeaf, ":rid", rid);
          129  +    db_step(&removeLeaf);
          130  +    db_reset(&removeLeaf);
          131  +  }else{
          132  +    db_static_prepare(&addLeaf, "INSERT OR IGNORE INTO leaf VALUES(:rid)");
          133  +    db_bind_int(&addLeaf, ":rid", rid);
          134  +    db_step(&addLeaf);
          135  +    db_reset(&addLeaf);
          136  +  }
          137  +}
          138  +
          139  +/*
          140  +** Return an SQL expression (stored in memory obtained from fossil_malloc())
          141  +** that is true if the SQL variable named "zVar" contains the rid with
          142  +** a CLOSED tag.  In other words, return true if the leaf is closed.
          143  +**
          144  +** The result can be prefaced with a NOT operator to get all leaves that
          145  +** are open.
          146  +*/
          147  +char *leaf_is_closed_sql(const char *zVar){
          148  +  return mprintf(
          149  +    "EXISTS(SELECT 1 FROM tagxref AS tx"
          150  +           " WHERE tx.rid=%s"
          151  +             " AND tx.tagid=%d"
          152  +             " AND tx.tagtype>0)",
          153  +    zVar, TAG_CLOSED
          154  +  );
          155  +}
          156  +
          157  +/*
          158  +** Schedule a leaf check for "rid" and its parents.
          159  +*/
          160  +void leaf_eventually_check(int rid){
          161  +  static Stmt parentsOf;
          162  +
          163  +  db_static_prepare(&parentsOf, 
          164  +     "SELECT pid FROM plink WHERE cid=:rid AND pid>0"
          165  +  );
          166  +  db_bind_int(&parentsOf, ":rid", rid);
          167  +  bag_insert(&needToCheck, rid);
          168  +  while( db_step(&parentsOf)==SQLITE_ROW ){
          169  +    bag_insert(&needToCheck, db_column_int(&parentsOf, 0));
          170  +  }
          171  +  db_reset(&parentsOf);
          172  +}
          173  +
          174  +/*
          175  +** Do all pending leaf checks.
          176  +*/
          177  +void leaf_do_pending_checks(void){
          178  +  int rid;
          179  +  for(rid=bag_first(&needToCheck); rid; rid=bag_next(&needToCheck,rid)){
          180  +    leaf_check(rid);
          181  +  }
          182  +  bag_clear(&needToCheck);
          183  +}

Changes to src/login.c.

    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code for generating the login and logout screens.
    19     19   **
    20     20   ** Notes:
    21     21   **
    22         -** There are two special-case user-ids: "anonymous" and "nobody".
           22  +** There are four special-case user-ids:  "anonymous", "nobody",
           23  +** "developer" and "reader".
           24  +**
    23     25   ** The capabilities of the nobody user are available to anyone,
    24     26   ** regardless of whether or not they are logged in.  The capabilities
    25     27   ** of anonymous are only available after logging in, but the login
    26     28   ** screen displays the password for the anonymous login, so this
    27         -** should not prevent a human user from doing so.
           29  +** should not prevent a human user from doing so.  The capabilities
           30  +** of developer and reader are inherited by any user that has the
           31  +** "v" and "u" capabilities, respectively.
    28     32   **
    29     33   ** The nobody user has capabilities that you want spiders to have.
    30     34   ** The anonymous user has capabilities that you want people without
    31     35   ** logins to have.
    32     36   **
    33     37   ** Of course, a sophisticated spider could easily circumvent the
    34     38   ** anonymous login requirement and walk the website.  But that is
................................................................................
    35     39   ** not really the point.  The anonymous login keeps search-engine
    36     40   ** crawlers and site download tools like wget from walking change
    37     41   ** logs and downloading diffs of very version of the archive that
    38     42   ** has ever existed, and things like that.
    39     43   */
    40     44   #include "config.h"
    41     45   #include "login.h"
    42         -#ifdef __MINGW32__
           46  +#if defined(_WIN32)  
    43     47   #  include <windows.h>           /* for Sleep */
    44         -#  define sleep Sleep            /* windows does not have sleep, but Sleep */
           48  +#  if defined(__MINGW32__) || defined(_MSC_VER)
           49  +#    define sleep Sleep            /* windows does not have sleep, but Sleep */
           50  +#  endif
    45     51   #endif
    46     52   #include <time.h>
    47     53   
    48     54   /*
    49         -** Return the name of the login cookie
           55  +** Return the login-group name.  Or return 0 if this repository is
           56  +** not a member of a login-group.
    50     57   */
    51         -static char *login_cookie_name(void){
           58  +const char *login_group_name(void){
           59  +  static const char *zGroup = 0;
           60  +  static int once = 1;
           61  +  if( once ){
           62  +    zGroup = db_get("login-group-name", 0);
           63  +    once = 0;
           64  +  }
           65  +  return zGroup;
           66  +}
           67  +
           68  +/*
           69  +** Return a path appropriate for setting a cookie.
           70  +**
           71  +** The path is g.zTop for single-repo cookies.  It is "/" for
           72  +** cookies of a login-group.
           73  +*/
           74  +static const char *login_cookie_path(void){
           75  +  if( login_group_name()==0 ){
           76  +    return g.zTop;
           77  +  }else{
           78  +    return "/";
           79  +  }
           80  +}
           81  +
           82  +/*
           83  +** Return the name of the login cookie.
           84  +**
           85  +** The login cookie name is always of the form:  fossil-XXXXXXXXXXXXXXXX
           86  +** where the Xs are the first 16 characters of the login-group-code or
           87  +** of the project-code if we are not a member of any login-group.
           88  +*/
           89  +char *login_cookie_name(void){
    52     90     static char *zCookieName = 0;
    53     91     if( zCookieName==0 ){
    54         -    int n = strlen(g.zTop);
    55         -    zCookieName = malloc( n*2+16 );
    56         -                      /* 0123456789 12345 */
    57         -    strcpy(zCookieName, "fossil_login_");
    58         -    encode16((unsigned char*)g.zTop, (unsigned char*)&zCookieName[13], n);
           92  +    zCookieName = db_text(0,
           93  +       "SELECT 'fossil-' || substr(value,1,16)"
           94  +       "  FROM config"
           95  +       " WHERE name IN ('project-code','login-group-code')"
           96  +       " ORDER BY name /*sort*/"
           97  +    );
    59     98     }
    60     99     return zCookieName;
    61    100   }
    62    101   
    63    102   /*
    64    103   ** Redirect to the page specified by the "g" query parameter.
    65    104   ** Or if there is no "g" query parameter, redirect to the homepage.
................................................................................
    70    109       cgi_redirect(zGoto);
    71    110     }else{
    72    111       fossil_redirect_home();
    73    112     }
    74    113   }
    75    114   
    76    115   /*
    77         -** The IP address of the client is stored as part of the anonymous
    78         -** login cookie for additional security.  But some clients are behind
    79         -** firewalls that shift the IP address with each HTTP request.  To
    80         -** allow such (broken) clients to log in, extract just a prefix of the
    81         -** IP address.  
          116  +** The IP address of the client is stored as part of login cookies.
          117  +** But some clients are behind firewalls that shift the IP address 
          118  +** with each HTTP request.  To allow such (broken) clients to log in, 
          119  +** extract just a prefix of the IP address.  
    82    120   */
    83    121   static char *ipPrefix(const char *zIP){
    84         -  int i, j; 
          122  +  int i, j;
          123  +  static int ip_prefix_terms = -1;
          124  +  if( ip_prefix_terms<0 ){
          125  +    ip_prefix_terms = db_get_int("ip-prefix-terms",2);
          126  +  }
          127  +  if( ip_prefix_terms==0 ) return mprintf("0");
    85    128     for(i=j=0; zIP[i]; i++){
    86    129       if( zIP[i]=='.' ){
    87    130         j++;
    88         -      if( j==2 ) break;
          131  +      if( j==ip_prefix_terms ) break;
    89    132       }
    90    133     }
    91    134     return mprintf("%.*s", i, zIP);
    92    135   }
    93         -        
          136  +
          137  +/*
          138  +** Return an abbreviated project code.  The abbreviation is the first
          139  +** 16 characters of the project code.
          140  +**
          141  +** Memory is obtained from malloc.
          142  +*/
          143  +static char *abbreviated_project_code(const char *zFullCode){
          144  +  return mprintf("%.16s", zFullCode);
          145  +}
          146  +
    94    147   
    95    148   /*
    96    149   ** Check to see if the anonymous login is valid.  If it is valid, return
    97    150   ** the userid of the anonymous user.
          151  +**
          152  +** The zCS parameter is the "captcha seed" used for a specific
          153  +** anonymous login request.
    98    154   */
    99         -static int isValidAnonymousLogin(
          155  +int login_is_valid_anonymous(
   100    156     const char *zUsername,  /* The username.  Must be "anonymous" */
   101         -  const char *zPassword   /* The supplied password */
          157  +  const char *zPassword,  /* The supplied password */
          158  +  const char *zCS         /* The captcha seed value */
   102    159   ){
   103         -  const char *zCS;        /* The captcha seed value */
   104    160     const char *zPw;        /* The correct password shown in the captcha */
   105    161     int uid;                /* The user ID of anonymous */
   106    162   
   107    163     if( zUsername==0 ) return 0;
   108         -  if( zPassword==0 ) return 0;
   109         -  if( strcmp(zUsername,"anonymous")!=0 ) return 0;
   110         -  zCS = P("cs");   /* The "cs" parameter is the "captcha seed" */
   111         -  if( zCS==0 ) return 0;
          164  +  else if( zPassword==0 ) return 0;
          165  +  else if( zCS==0 ) return 0;
          166  +  else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
   112    167     zPw = captcha_decode((unsigned int)atoi(zCS));
   113         -  if( strcasecmp(zPw, zPassword)!=0 ) return 0;
          168  +  if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
   114    169     uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
   115    170                     " AND length(pw)>0 AND length(cap)>0");
   116    171     return uid;
   117    172   }
          173  +
          174  +/*
          175  +** Make sure the accesslog table exists.  Create it if it does not
          176  +*/
          177  +void create_accesslog_table(void){
          178  +  db_multi_exec(
          179  +    "CREATE TABLE IF NOT EXISTS %s.accesslog("
          180  +    "  uname TEXT,"
          181  +    "  ipaddr TEXT,"
          182  +    "  success BOOLEAN,"
          183  +    "  mtime TIMESTAMP"
          184  +    ");", db_name("repository")
          185  +  );
          186  +}
          187  +
          188  +/*
          189  +** Make a record of a login attempt, if login record keeping is enabled.
          190  +*/
          191  +static void record_login_attempt(
          192  +  const char *zUsername,     /* Name of user logging in */
          193  +  const char *zIpAddr,       /* IP address from which they logged in */
          194  +  int bSuccess               /* True if the attempt was a success */
          195  +){
          196  +  if( !db_get_boolean("access-log", 0) ) return;
          197  +  create_accesslog_table();
          198  +  db_multi_exec(
          199  +    "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
          200  +    "VALUES(%Q,%Q,%d,julianday('now'));",
          201  +    zUsername, zIpAddr, bSuccess
          202  +  );
          203  +}
          204  +
          205  +/*
          206  +** Searches for the user ID matching the given name and password.
          207  +** On success it returns a positive value. On error it returns 0.
          208  +** On serious (DB-level) error it will probably exit.
          209  +**
          210  +** zPassword may be either the plain-text form or the encrypted
          211  +** form of the user's password.
          212  +*/
          213  +int login_search_uid(char const *zUsername, char const *zPasswd){
          214  +  char * zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
          215  +  int const uid =
          216  +      db_int(0,
          217  +             "SELECT uid FROM user"
          218  +             " WHERE login=%Q"
          219  +             "   AND length(cap)>0 AND length(pw)>0"
          220  +             "   AND login NOT IN ('anonymous','nobody','developer','reader')"
          221  +             "   AND (pw=%Q OR pw=%Q)",
          222  +             zUsername, zPasswd, zSha1Pw
          223  +             );
          224  +  free(zSha1Pw);
          225  +  return uid;
          226  +}
          227  +
          228  +/*
          229  +** Generates a login cookie value for a non-anonymous user.
          230  +**
          231  +** The zHash parameter must be a random value which must be
          232  +** subsequently stored in user.cookie for later validation.
          233  +**
          234  +** The returned memory should be free()d after use.
          235  +*/
          236  +char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){
          237  +  char * zProjCode = db_get("project-code",NULL);
          238  +  char *zCode = abbreviated_project_code(zProjCode);
          239  +  free(zProjCode);
          240  +  assert((zUsername && *zUsername) && "Invalid user data.");
          241  +  return mprintf("%s/%z/%s", zHash, zCode, zUsername);
          242  +}
          243  +
          244  +/*
          245  +** Generates a login cookie for NON-ANONYMOUS users.  Note that this
          246  +** function "could" figure out the uid by itself but it currently
          247  +** doesn't because the code which calls this already has the uid.
          248  +**
          249  +** This function also updates the user.cookie, user.ipaddr,
          250  +** and user.cexpire fields for the given user.
          251  +**
          252  +** If zDest is not NULL then the generated cookie is copied to
          253  +** *zDdest and ownership is transfered to the caller (who should
          254  +** eventually pass it to free()).
          255  +*/
          256  +void login_set_user_cookie(
          257  +  char const * zUsername, /* User's name */
          258  +  int uid,                /* User's ID */
          259  +  char ** zDest           /* Optional: store generated cookie value. */
          260  +){
          261  +  const char *zCookieName = login_cookie_name();
          262  +  const char *zExpire = db_get("cookie-expire","8766");
          263  +  int expires = atoi(zExpire)*3600;
          264  +  char *zHash;
          265  +  char *zCookie;
          266  +  char const *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */
          267  +  char *zRemoteAddr = ipPrefix(zIpAddr);         /* Abbreviated IP address */
          268  +
          269  +  assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
          270  +  zHash = db_text(0,
          271  +      "SELECT cookie FROM user"
          272  +      " WHERE uid=%d"
          273  +      "   AND ipaddr=%Q"
          274  +      "   AND cexpire>julianday('now')"
          275  +      "   AND length(cookie)>30",
          276  +      uid, zRemoteAddr);
          277  +  if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))");
          278  +  zCookie = login_gen_user_cookie_value(zUsername, zHash);
          279  +  cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
          280  +  record_login_attempt(zUsername, zIpAddr, 1);
          281  +  db_multi_exec(
          282  +                "UPDATE user SET cookie=%Q, ipaddr=%Q, "
          283  +                "  cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
          284  +                zHash, zRemoteAddr, expires, uid
          285  +                );
          286  +  free(zRemoteAddr);
          287  +  free(zHash);
          288  +  if( zDest ){
          289  +    *zDest = zCookie;
          290  +  }else{
          291  +    free(zCookie);
          292  +  }
          293  +}
          294  +
          295  +/* Sets a cookie for an anonymous user login, which looks like this:
          296  +**
          297  +**    HASH/TIME/anonymous
          298  +**
          299  +** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
          300  +** is the abbreviated IP address and SECRET is captcha-secret.
          301  +**
          302  +** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
          303  +** is used.
          304  +**
          305  +** If zCookieDest is not NULL then the generated cookie is assigned to
          306  +** *zCookieDest and the caller must eventually free() it.
          307  +*/
          308  +void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
          309  +  char const *zNow;            /* Current time (julian day number) */
          310  +  char *zCookie;               /* The login cookie */
          311  +  char const *zCookieName;     /* Name of the login cookie */
          312  +  Blob b;                      /* Blob used during cookie construction */
          313  +  char * zRemoteAddr;     /* Abbreviated IP address */
          314  +  if(!zIpAddr){
          315  +    zIpAddr = PD("REMOTE_ADDR","nil");
          316  +  }
          317  +  zRemoteAddr = ipPrefix(zIpAddr);
          318  +  zCookieName = login_cookie_name();
          319  +  zNow = db_text("0", "SELECT julianday('now')");
          320  +  assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
          321  +  blob_init(&b, zNow, -1);
          322  +  blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
          323  +  sha1sum_blob(&b, &b);
          324  +  zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
          325  +  blob_reset(&b);
          326  +  cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
          327  +  if( zCookieDest ){
          328  +    *zCookieDest = zCookie;
          329  +  }else{
          330  +    free(zCookie);
          331  +  }
          332  +
          333  +}
          334  +
          335  +/*
          336  +** "Unsets" the login cookie (insofar as cookies can be unset) and
          337  +** clears the current user's (g.userUid) login information from the
          338  +** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
          339  +**
          340  +** We could/should arguably clear out g.userUid and g.perm here, but
          341  +** we don't currently do not.
          342  +**
          343  +** This is a no-op if g.userUid is 0.
          344  +*/
          345  +void login_clear_login_data(){
          346  +  if(!g.userUid){
          347  +    return;
          348  +  }else{
          349  +    char const * cookie = login_cookie_name(); 
          350  +    /* To logout, change the cookie value to an empty string */
          351  +    cgi_set_cookie(cookie, "",
          352  +                   login_cookie_path(), -86400);
          353  +    db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
          354  +                  "  cexpire=0 WHERE uid=%d"
          355  +                  "  AND login NOT IN ('anonymous','nobody',"
          356  +                  "  'developer','reader')", g.userUid);
          357  +    cgi_replace_parameter(cookie, NULL)
          358  +      /* At the time of this writing, cgi_replace_parameter() was
          359  +      ** "NULL-value-safe", and I'm hoping the NULL doesn't cause any
          360  +      ** downstream problems here. We could alternately use "" here.
          361  +      */
          362  +      ;
          363  +  }
          364  +}
          365  +
          366  +/*
          367  +** Return true if the prefix of zStr matches zPattern.  Return false if
          368  +** they are different.
          369  +**
          370  +** A lowercase character in zPattern will match either upper or lower
          371  +** case in zStr.  But an uppercase in zPattern will only match an
          372  +** uppercase in zStr.
          373  +*/
          374  +static int prefix_match(const char *zPattern, const char *zStr){
          375  +  int i;
          376  +  char c;
          377  +  for(i=0; (c = zPattern[i])!=0; i++){
          378  +    if( zStr[i]!=c && fossil_tolower(zStr[i])!=c ) return 0;
          379  +  }
          380  +  return 1;
          381  +}
          382  +
          383  +/*
          384  +** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
          385  +** is a manually operated browser or a bot.  When in doubt, assume a bot.
          386  +** Return true if we believe the agent is a real person.
          387  +*/
          388  +static int isHuman(const char *zAgent){
          389  +  int i;
          390  +  if( zAgent==0 ) return 0;  /* If no UserAgent, then probably a bot */
          391  +  for(i=0; zAgent[i]; i++){
          392  +    if( prefix_match("bot", zAgent+i) ) return 0;
          393  +    if( prefix_match("spider", zAgent+i) ) return 0;
          394  +    if( prefix_match("crawl", zAgent+i) ) return 0;
          395  +    /* If a URI appears in the User-Agent, it is probably a bot */
          396  +    if( memcmp("http", zAgent+i,4)==0 ) return 0;
          397  +  }
          398  +  if( memcmp(zAgent, "Mozilla/", 8)==0 ){
          399  +    if( atoi(&zAgent[8])<4 ) return 0;  /* Many bots advertise as Mozilla/3 */
          400  +    if( strglob("*Firefox/[1-9]*", zAgent) ) return 1;
          401  +    if( strglob("*Chrome/[1-9]*", zAgent) ) return 1;
          402  +    if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1;
          403  +    if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1;
          404  +    return 0;
          405  +  }
          406  +  if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
          407  +  if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
          408  +  if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
          409  +  if( memcmp(zAgent, "NetSurf/", 8)==0 ) return 1;
          410  +  return 0;
          411  +}
          412  +
          413  +/*
          414  +** COMMAND: test-ishuman
          415  +**
          416  +** Read lines of text from standard input.  Interpret each line of text
          417  +** as a User-Agent string from an HTTP header.  Label each line as HUMAN
          418  +** or ROBOT.
          419  +*/
          420  +void test_ishuman(void){
          421  +  char zLine[3000];
          422  +  while( fgets(zLine, sizeof(zLine), stdin) ){
          423  +    fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine);
          424  +  }
          425  +}
          426  +
          427  +/*
          428  +** SQL function for constant time comparison of two values.
          429  +** Sets result to 0 if two values are equal.
          430  +*/
          431  +static void constant_time_cmp_function(
          432  + sqlite3_context *context,
          433  + int argc,
          434  + sqlite3_value **argv
          435  +){
          436  +  const unsigned char *buf1, *buf2;
          437  +  int len, i;
          438  +  unsigned char rc = 0;
          439  +
          440  +  assert( argc==2 );
          441  +  len = sqlite3_value_bytes(argv[0]);
          442  +  if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
          443  +    rc = 1;
          444  +  }else{
          445  +    buf1 = sqlite3_value_text(argv[0]);
          446  +    buf2 = sqlite3_value_text(argv[1]);
          447  +    for( i=0; i<len; i++ ){
          448  +      rc = rc | (buf1[i] ^ buf2[i]);
          449  +    }
          450  +  }
          451  +  sqlite3_result_int(context, rc);
          452  +}
   118    453   
   119    454   /*
   120    455   ** WEBPAGE: login
   121    456   ** WEBPAGE: logout
   122    457   ** WEBPAGE: my
   123    458   **
   124    459   ** Generate the login page.
................................................................................
   128    463   ** line on the title bar.  The "my" page was never completed so it is now
   129    464   ** removed.  Use this page as a placeholder in older installations.
   130    465   */
   131    466   void login_page(void){
   132    467     const char *zUsername, *zPasswd;
   133    468     const char *zNew1, *zNew2;
   134    469     const char *zAnonPw = 0;
          470  +  const char *zGoto = P("g");
   135    471     int anonFlag;
   136    472     char *zErrMsg = "";
   137         -  int uid;                     /* User id loged in user */
          473  +  int uid;                     /* User id logged in user */
   138    474     char *zSha1Pw;
          475  +  const char *zIpAddr;         /* IP address of requestor */
   139    476   
   140    477     login_check_credentials();
          478  +  sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
          479  +		  constant_time_cmp_function, 0, 0);
   141    480     zUsername = P("u");
   142    481     zPasswd = P("p");
   143    482     anonFlag = P("anon")!=0;
   144    483     if( P("out")!=0 ){
   145         -    const char *zCookieName = login_cookie_name();
   146         -    cgi_set_cookie(zCookieName, "", 0, -86400);
          484  +    login_clear_login_data();
   147    485       redirect_to_g();
   148    486     }
   149         -  if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
   150         -    zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
          487  +  if( g.perm.Password && zPasswd
          488  +   && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
          489  +  ){
          490  +    /* The user requests a password change */
          491  +    zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
   151    492       if( db_int(1, "SELECT 0 FROM user"
   152         -                  " WHERE uid=%d AND (pw=%Q OR pw=%Q)", 
   153         -                  g.userUid, zPasswd, zSha1Pw) ){
          493  +                  " WHERE uid=%d"
          494  +                  " AND (constant_time_cmp(pw,%Q)=0"
          495  +                  "      OR constant_time_cmp(pw,%Q)=0)", 
          496  +                  g.userUid, zSha1Pw, zPasswd) ){
   154    497         sleep(1);
   155    498         zErrMsg = 
   156         -         @ <p><font color="red">
          499  +         @ <p><span class="loginError">
   157    500            @ You entered an incorrect old password while attempting to change
   158    501            @ your password.  Your password is unchanged.
   159         -         @ </font></p>
          502  +         @ </span></p>
   160    503         ;
   161         -    }else if( strcmp(zNew1,zNew2)!=0 ){
          504  +    }else if( fossil_strcmp(zNew1,zNew2)!=0 ){
   162    505         zErrMsg = 
   163         -         @ <p><font color="red">
          506  +         @ <p><span class="loginError">
   164    507            @ The two copies of your new passwords do not match.
   165    508            @ Your password is unchanged.
   166         -         @ </font></p>
          509  +         @ </span></p>
   167    510         ;
   168    511       }else{
   169         -      char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
          512  +      char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
          513  +      char *zChngPw;
          514  +      char *zErr;
   170    515         db_multi_exec(
   171    516            "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
   172    517         );
   173         -      redirect_to_g();
   174         -      return;
          518  +      fossil_free(zNewPw);
          519  +      zChngPw = mprintf(
          520  +         "UPDATE user"
          521  +         "   SET pw=shared_secret(%Q,%Q,"
          522  +         "        (SELECT value FROM config WHERE name='project-code'))"
          523  +         " WHERE login=%Q",
          524  +         zNew1, g.zLogin, g.zLogin
          525  +      );
          526  +      if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){
          527  +        zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
          528  +        fossil_free(zErr);
          529  +      }else{
          530  +        redirect_to_g();
          531  +        return;
          532  +      }
   175    533       }
   176    534     }
   177         -  uid = isValidAnonymousLogin(zUsername, zPasswd);
          535  +  zIpAddr = PD("REMOTE_ADDR","nil");   /* Complete IP address for logging */
          536  +  uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
   178    537     if( uid>0 ){
   179         -    char *zNow;                  /* Current time (julian day number) */
   180         -    const char *zIpAddr;         /* IP address of requestor */
   181         -    char *zCookie;               /* The login cookie */
   182         -    const char *zCookieName;     /* Name of the login cookie */
   183         -    Blob b;                      /* Blob used during cookie construction */
   184         -
   185         -    zIpAddr = PD("REMOTE_ADDR","nil");
   186         -    zCookieName = login_cookie_name();
   187         -    zNow = db_text("0", "SELECT julianday('now')");
   188         -    blob_init(&b, zNow, -1);
   189         -    blob_appendf(&b, "/%z/%s", ipPrefix(zIpAddr), db_get("captcha-secret",""));
   190         -    sha1sum_blob(&b, &b);
   191         -    zCookie = sqlite3_mprintf("anon/%s/%s", zNow, blob_buffer(&b));
   192         -    blob_reset(&b);
   193         -    free(zNow);
   194         -    cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
          538  +    login_set_anon_cookie(zIpAddr, NULL);
          539  +    record_login_attempt("anonymous", zIpAddr, 1);
   195    540       redirect_to_g();
   196    541     }
   197    542     if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
   198         -    zSha1Pw = sha1_shared_secret(zPasswd, zUsername);
   199         -    uid = db_int(0,
   200         -        "SELECT uid FROM user"
   201         -        " WHERE login=%Q"
   202         -        "   AND login NOT IN ('anonymous','nobody','developer','reader')"
   203         -        "   AND (pw=%Q OR pw=%Q)",
   204         -        zUsername, zPasswd, zSha1Pw
   205         -    );
          543  +    /* Attempting to log in as a user other than anonymous.
          544  +    */
          545  +    uid = login_search_uid(zUsername, zPasswd);
   206    546       if( uid<=0 ){
   207    547         sleep(1);
   208    548         zErrMsg = 
   209         -         @ <p><font color="red">
          549  +         @ <p><span class="loginError">
   210    550            @ You entered an unknown user or an incorrect password.
   211         -         @ </font></p>
          551  +         @ </span></p>
   212    552         ;
          553  +      record_login_attempt(zUsername, zIpAddr, 0);
   213    554       }else{
   214         -      char *zCookie;
   215         -      const char *zCookieName = login_cookie_name();
   216         -      const char *zExpire = db_get("cookie-expire","8766");
   217         -      int expires = atoi(zExpire)*3600;
   218         -      const char *zIpAddr = PD("REMOTE_ADDR","nil");
   219         - 
   220         -      zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
   221         -      cgi_set_cookie(zCookieName, zCookie, 0, expires);
   222         -      db_multi_exec(
   223         -        "UPDATE user SET cookie=%Q, ipaddr=%Q, "
   224         -        "  cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
   225         -        zCookie, zIpAddr, expires, uid
   226         -      );
          555  +      /* Non-anonymous login is successful.  Set a cookie of the form:
          556  +      **
          557  +      **    HASH/PROJECT/LOGIN
          558  +      **
          559  +      ** where HASH is a random hex number, PROJECT is either project
          560  +      ** code prefix, and LOGIN is the user name.
          561  +      */
          562  +      login_set_user_cookie(zUsername, uid, NULL);
   227    563         redirect_to_g();
   228    564       }
   229    565     }
   230    566     style_header("Login/Logout");
   231    567     @ %s(zErrMsg)
   232         -  @ <form action="login" method="POST">
   233         -  if( P("g") ){
   234         -    @ <input type="hidden" name="g" value="%h(P("g"))">
          568  +  if( zGoto && P("anon")==0 ){
          569  +    @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
   235    570     }
   236         -  @ <table align="left" hspace="10">
          571  +  form_begin(0, "%R/login");
          572  +  if( zGoto ){
          573  +    @ <input type="hidden" name="g" value="%h(zGoto)" />
          574  +  }
          575  +  @ <table class="login_out">
   237    576     @ <tr>
   238         -  @   <td align="right">User ID:</td>
          577  +  @   <td class="login_out_label">User ID:</td>
   239    578     if( anonFlag ){
   240         -    @   <td><input type="text" id="u" name="u" value="anonymous" size=30></td>
          579  +    @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
   241    580     }else{
   242         -    @   <td><input type="text" id="u" name="u" value="" size=30></td>
          581  +    @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
   243    582     }
   244    583     @ </tr>
   245    584     @ <tr>
   246         -  @  <td align="right">Password:</td>
   247         -  @   <td><input type="password" id="p" name="p" value="" size=30></td>
          585  +  @  <td class="login_out_label">Password:</td>
          586  +  @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
   248    587     @ </tr>
   249    588     if( g.zLogin==0 ){
   250    589       zAnonPw = db_text(0, "SELECT pw FROM user"
   251    590                            " WHERE login='anonymous'"
   252    591                            "   AND cap!=''");
   253    592     }
   254    593     @ <tr>
   255    594     @   <td></td>
   256         -  @   <td><input type="submit" name="in" value="Login"></td>
          595  +  @   <td><input type="submit" name="in" value="Login"
          596  +  @        onClick="chngAction(this.form)" /></td>
   257    597     @ </tr>
   258    598     @ </table>
   259         -  @ <script>document.getElementById('u').focus()</script>
          599  +  @ <script type="text/JavaScript">
          600  +  @   gebi('u').focus()
          601  +  @   function chngAction(form){
          602  +  if( g.sslNotAvailable==0
          603  +   && memcmp(g.zBaseURL,"https:",6)!=0
          604  +   && db_get_boolean("https-login",0)
          605  +  ){
          606  +     char *zSSL = mprintf("https:%s", &g.zBaseURL[5]);
          607  +     @  if( form.u.value!="anonymous" ){
          608  +     @     form.action = "%h(zSSL)/login";
          609  +     @  }
          610  +  }
          611  +  @ }
          612  +  @ </script>
   260    613     if( g.zLogin==0 ){
   261    614       @ <p>Enter
   262    615     }else{
   263    616       @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
   264    617       @ <p>To change your login to a different user, enter
   265    618     }
   266    619     @ your user-id and password at the left and press the
   267    620     @ "Login" button.  Your user name will be stored in a browser cookie.
   268    621     @ You must configure your web browser to accept cookies in order for
   269    622     @ the login to take.</p>
          623  +  if( db_get_boolean("self-register", 0) ){
          624  +    @ <p>If you do not have an account, you can 
          625  +    @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
          626  +  }
   270    627     if( zAnonPw ){
   271    628       unsigned int uSeed = captcha_seed();
   272    629       char const *zDecoded = captcha_decode(uSeed);
   273    630       int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
   274    631       char *zCaptcha = captcha_render(zDecoded);
   275    632   
   276         -    @ <input type="hidden" name="cs" value="%u(uSeed)"/>
   277         -    @ <p>Visitors may enter <b>anonymous</b> as the user-ID with
          633  +    @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
          634  +    @ Visitors may enter <b>anonymous</b> as the user-ID with
   278    635       @ the 8-character hexadecimal password shown below:</p>
   279         -    @ <center><table border="1" cellpadding="10"><tr><td><pre>
   280         -    @ %s(zCaptcha)
          636  +    @ <div class="captcha"><table class="captcha"><tr><td><pre>
          637  +    @ %h(zCaptcha)
   281    638       @ </pre></td></tr></table>
   282    639       if( bAutoCaptcha ) {
   283    640           @ <input type="button" value="Fill out captcha"
   284         -        @  onclick="document.getElementById('u').value='anonymous';
   285         -        @           document.getElementById('p').value='%s(zDecoded)';"/>
          641  +        @  onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
   286    642       }
   287         -    @ </center>
          643  +    @ </div>
   288    644       free(zCaptcha);
   289    645     }
   290    646     if( g.zLogin ){
   291         -    @ <br clear="both"><hr>
          647  +    @ <hr />
   292    648       @ <p>To log off the system (and delete your login cookie)
   293         -    @  press the following button:<br>
   294         -    @ <input type="submit" name="out" value="Logout"></p>
          649  +    @  press the following button:<br />
          650  +    @ <input type="submit" name="out" value="Logout" /></p>
   295    651     }
   296    652     @ </form>
   297         -  if( g.okPassword ){
   298         -    @ <br clear="both"><hr>
          653  +  if( g.perm.Password ){
          654  +    @ <hr />
   299    655       @ <p>To change your password, enter your old password and your
   300    656       @ new password twice below then press the "Change Password"
   301    657       @ button.</p>
   302         -    @ <form action="login" method="POST">
          658  +    form_begin(0, "%R/login");
   303    659       @ <table>
   304         -    @ <tr><td align="right">Old Password:</td>
   305         -    @ <td><input type="password" name="p" size=30></td></tr>
   306         -    @ <tr><td align="right">New Password:</td>
   307         -    @ <td><input type="password" name="n1" size=30></td></tr>
   308         -    @ <tr><td align="right">Repeat New Password:</td>
   309         -    @ <td><input type="password" name="n2" size=30></td></tr>
          660  +    @ <tr><td class="login_out_label">Old Password:</td>
          661  +    @ <td><input type="password" name="p" size="30" /></td></tr>
          662  +    @ <tr><td class="login_out_label">New Password:</td>
          663  +    @ <td><input type="password" name="n1" size="30" /></td></tr>
          664  +    @ <tr><td class="login_out_label">Repeat New Password:</td>
          665  +    @ <td><input type="password" name="n2" size="30" /></td></tr>
   310    666       @ <tr><td></td>
   311         -    @ <td><input type="submit" value="Change Password"></td></tr>
          667  +    @ <td><input type="submit" value="Change Password" /></td></tr>
   312    668       @ </table>
   313    669       @ </form>
   314    670     }
   315    671     style_footer();
   316    672   }
   317    673   
          674  +/*
          675  +** Attempt to find login credentials for user zLogin on a peer repository
          676  +** with project code zCode.  Transfer those credentials to the local 
          677  +** repository.
          678  +**
          679  +** Return true if a transfer was made and false if not.
          680  +*/
          681  +static int login_transfer_credentials(
          682  +  const char *zLogin,          /* Login we are looking for */
          683  +  const char *zCode,           /* Project code of peer repository */
          684  +  const char *zHash,           /* HASH from login cookie HASH/CODE/LOGIN */
          685  +  const char *zRemoteAddr      /* Request comes from here */
          686  +){
          687  +  sqlite3 *pOther = 0;         /* The other repository */
          688  +  sqlite3_stmt *pStmt;         /* Query against the other repository */
          689  +  char *zSQL;                  /* SQL of the query against other repo */
          690  +  char *zOtherRepo;            /* Filename of the other repository */
          691  +  int rc;                      /* Result code from SQLite library functions */
          692  +  int nXfer = 0;               /* Number of credentials transferred */
   318    693   
          694  +  zOtherRepo = db_text(0, 
          695  +       "SELECT value FROM config WHERE name='peer-repo-%q'",
          696  +       zCode
          697  +  );
          698  +  if( zOtherRepo==0 ) return 0;  /* No such peer repository */
          699  +
          700  +  rc = sqlite3_open(zOtherRepo, &pOther);
          701  +  if( rc==SQLITE_OK ){
          702  +    sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0);
          703  +    sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0,
          704  +		  constant_time_cmp_function, 0, 0);
          705  +    sqlite3_busy_timeout(pOther, 5000);
          706  +    zSQL = mprintf(
          707  +      "SELECT cexpire FROM user"
          708  +      " WHERE login=%Q"
          709  +      "   AND ipaddr=%Q"
          710  +      "   AND length(cap)>0"
          711  +      "   AND length(pw)>0"
          712  +      "   AND cexpire>julianday('now')"
          713  +      "   AND constant_time_cmp(cookie,%Q)=0",
          714  +      zLogin, zRemoteAddr, zHash
          715  +    );
          716  +    pStmt = 0;
          717  +    rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
          718  +    if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
          719  +      db_multi_exec(
          720  +        "UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g"
          721  +        " WHERE login=%Q",
          722  +        zHash, zRemoteAddr,
          723  +        sqlite3_column_double(pStmt, 0), zLogin
          724  +      );
          725  +      nXfer++;
          726  +    }
          727  +    sqlite3_finalize(pStmt);
          728  +  }
          729  +  sqlite3_close(pOther);
          730  +  fossil_free(zOtherRepo);
          731  +  return nXfer;
          732  +}
          733  +
          734  +/*
          735  +** Lookup the uid for a non-built-in user with zLogin and zCookie and
          736  +** zRemoteAddr.  Return 0 if not found.
          737  +**
          738  +** Note that this only searches for logged-in entries with matching
          739  +** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
          740  +** entries.
          741  +*/
          742  +static int login_find_user(
          743  +  const char *zLogin,            /* User name */
          744  +  const char *zCookie,           /* Login cookie value */
          745  +  const char *zRemoteAddr        /* Abbreviated IP address for valid login */
          746  +){
          747  +  int uid;
          748  +  if( fossil_strcmp(zLogin, "anonymous")==0 ) return 0;
          749  +  if( fossil_strcmp(zLogin, "nobody")==0 ) return 0;
          750  +  if( fossil_strcmp(zLogin, "developer")==0 ) return 0;
          751  +  if( fossil_strcmp(zLogin, "reader")==0 ) return 0;
          752  +  uid = db_int(0, 
          753  +    "SELECT uid FROM user"
          754  +    " WHERE login=%Q"
          755  +    "   AND ipaddr=%Q"
          756  +    "   AND cexpire>julianday('now')"
          757  +    "   AND length(cap)>0"
          758  +    "   AND length(pw)>0"
          759  +    "   AND constant_time_cmp(cookie,%Q)=0",
          760  +    zLogin, zRemoteAddr, zCookie
          761  +  );
          762  +  return uid;
          763  +}
   319    764   
   320    765   /*
   321    766   ** This routine examines the login cookie to see if it exists and
   322         -** and is valid.  If the login cookie checks out, it then sets 
   323         -** g.zUserUuid appropriately.
          767  +** is valid.  If the login cookie checks out, it then sets global
          768  +** variables appropriately.  Global variables set include g.userUid
          769  +** and g.zLogin and the g.perm family of permission booleans.
   324    770   **
          771  +** If the 
   325    772   */
   326    773   void login_check_credentials(void){
   327    774     int uid = 0;                  /* User id */
   328    775     const char *zCookie;          /* Text of the login cookie */
   329         -  const char *zRemoteAddr;      /* IP address of the requestor */
          776  +  const char *zIpAddr;          /* Raw IP address of the requestor */
          777  +  char *zRemoteAddr;            /* Abbreviated IP address of the requestor */
   330    778     const char *zCap = 0;         /* Capability string */
          779  +  const char *zPublicPages = 0; /* GLOB patterns of public pages */
   331    780   
   332    781     /* Only run this check once.  */
   333    782     if( g.userUid!=0 ) return;
   334    783   
          784  +  sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
          785  +		  constant_time_cmp_function, 0, 0);
   335    786   
   336    787     /* If the HTTP connection is coming over 127.0.0.1 and if
   337    788     ** local login is disabled and if we are using HTTP and not HTTPS, 
   338    789     ** then there is no need to check user credentials.
   339    790     **
          791  +  ** This feature allows the "fossil ui" command to give the user
          792  +  ** full access rights without having to log in.
   340    793     */
   341         -  zRemoteAddr = PD("REMOTE_ADDR","nil");
   342         -  if( strcmp(zRemoteAddr, "127.0.0.1")==0
          794  +  zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
          795  +  if( fossil_strcmp(zIpAddr, "127.0.0.1")==0
          796  +   && g.useLocalauth
   343    797      && db_get_int("localauth",0)==0
   344    798      && P("HTTPS")==0
   345    799     ){
   346    800       uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
   347    801       g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
   348         -    zCap = "s";
          802  +    zCap = "sx";
   349    803       g.noPswd = 1;
   350         -    strcpy(g.zCsrfToken, "localhost");
          804  +    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
   351    805     }
   352    806   
   353    807     /* Check the login cookie to see if it matches a known valid user.
   354    808     */
   355    809     if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
   356         -    if( isdigit(zCookie[0]) ){
   357         -      /* Cookies of the form "uid/randomness".  There must be a
   358         -      ** corresponding entry in the user table. */
   359         -      uid = db_int(0, 
   360         -            "SELECT uid FROM user"
   361         -            " WHERE uid=%d"
   362         -            "   AND cookie=%Q"
   363         -            "   AND ipaddr=%Q"
   364         -            "   AND cexpire>julianday('now')",
   365         -            atoi(zCookie), zCookie, zRemoteAddr
   366         -         );
   367         -    }else if( memcmp(zCookie,"anon/",5)==0 ){
   368         -      /* Cookies of the form "anon/TIME/HASH".  The TIME must not be
   369         -      ** too old and the sha1 hash of TIME+IPADDR+SECRET must match HASH.
          810  +    /* Parse the cookie value up into HASH/ARG/USER */
          811  +    char *zHash = fossil_strdup(zCookie);
          812  +    char *zArg = 0;
          813  +    char *zUser = 0;
          814  +    int i, c;
          815  +    for(i=0; (c = zHash[i])!=0; i++){
          816  +      if( c=='/' ){
          817  +        zHash[i++] = 0;
          818  +        if( zArg==0 ){
          819  +          zArg = &zHash[i];
          820  +        }else{
          821  +          zUser = &zHash[i];
          822  +          break;
          823  +        }
          824  +      }
          825  +    }
          826  +    if( zUser==0 ){
          827  +      /* Invalid cookie */
          828  +    }else if( fossil_strcmp(zUser, "anonymous")==0 ){
          829  +      /* Cookies of the form "HASH/TIME/anonymous".  The TIME must not be
          830  +      ** too old and the sha1 hash of TIME/IPADDR/SECRET must match HASH.
   370    831         ** SECRET is the "captcha-secret" value in the repository.
   371    832         */
   372         -      double rTime;
   373         -      int i;
          833  +      double rTime = atof(zArg);
   374    834         Blob b;
   375         -      rTime = atof(&zCookie[5]);
   376         -      for(i=5; zCookie[i] && zCookie[i]!='/'; i++){}
   377         -      blob_init(&b, &zCookie[5], i-5);
   378         -      if( zCookie[i]=='/' ){ i++; }
   379         -      blob_append(&b, "/", 1);
   380         -      blob_appendf(&b, "%z/%s", ipPrefix(zRemoteAddr),
   381         -                   db_get("captcha-secret",""));
          835  +      blob_zero(&b);
          836  +      blob_appendf(&b, "%s/%s/%s", 
          837  +                   zArg, zRemoteAddr, db_get("captcha-secret",""));
   382    838         sha1sum_blob(&b, &b);
   383         -      uid = db_int(0, 
   384         -          "SELECT uid FROM user WHERE login='anonymous'"
   385         -          " AND length(cap)>0"
   386         -          " AND length(pw)>0"
   387         -          " AND %f+0.25>julianday('now')"
   388         -          " AND %Q=%Q",
   389         -          rTime, &zCookie[i], blob_buffer(&b)
   390         -      );
          839  +      if( fossil_strcmp(zHash, blob_str(&b))==0 ){
          840  +        uid = db_int(0, 
          841  +            "SELECT uid FROM user WHERE login='anonymous'"
          842  +            " AND length(cap)>0"
          843  +            " AND length(pw)>0"
          844  +            " AND %.17g+0.25>julianday('now')",
          845  +            rTime
          846  +        );
          847  +      }
   391    848         blob_reset(&b);
          849  +    }else{
          850  +      /* Cookies of the form "HASH/CODE/USER".  Search first in the
          851  +      ** local user table, then the user table for project CODE if we
          852  +      ** are part of a login-group.
          853  +      */
          854  +      uid = login_find_user(zUser, zHash, zRemoteAddr);
          855  +      if( uid==0 && login_transfer_credentials(zUser,zArg,zHash,zRemoteAddr) ){
          856  +        uid = login_find_user(zUser, zHash, zRemoteAddr);
          857  +        if( uid ) record_login_attempt(zUser, zIpAddr, 1);
          858  +      }
   392    859       }
   393         -    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zCookie);
          860  +    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
   394    861     }
   395    862   
   396    863     /* If no user found and the REMOTE_USER environment variable is set,
   397         -  ** the accept the value of REMOTE_USER as the user.
          864  +  ** then accept the value of REMOTE_USER as the user.
   398    865     */
   399    866     if( uid==0 ){
   400    867       const char *zRemoteUser = P("REMOTE_USER");
   401    868       if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
   402    869         uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
   403    870                         " AND length(cap)>0 AND length(pw)>0", zRemoteUser);
   404    871       }
................................................................................
   408    875     if( uid==0 ){
   409    876       uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
   410    877       if( uid==0 ){
   411    878         /* If there is no user "nobody", then make one up - with no privileges */
   412    879         uid = -1;
   413    880         zCap = "";
   414    881       }
   415         -    strcpy(g.zCsrfToken, "none");
          882  +    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
   416    883     }
   417    884   
   418    885     /* At this point, we know that uid!=0.  Find the privileges associated
   419    886     ** with user uid.
   420    887     */
   421    888     assert( uid!=0 );
   422    889     if( zCap==0 ){
................................................................................
   435    902       fprintf(stderr, "# login: [%s] with capabilities [%s]\n", g.zLogin, zCap);
   436    903     }
   437    904   
   438    905     /* Set the global variables recording the userid and login.  The
   439    906     ** "nobody" user is a special case in that g.zLogin==0.
   440    907     */
   441    908     g.userUid = uid;
   442         -  if( g.zLogin && strcmp(g.zLogin,"nobody")==0 ){
          909  +  if( fossil_strcmp(g.zLogin,"nobody")==0 ){
   443    910       g.zLogin = 0;
   444    911     }
   445    912   
   446    913     /* Set the capabilities */
   447         -  login_set_capabilities(zCap);
          914  +  login_replace_capabilities(zCap, 0);
   448    915     login_set_anon_nobody_capabilities();
          916  +  if( zCap[0] && !g.perm.Hyperlink
          917  +   && db_get_boolean("auto-hyperlink",1)
          918  +      && isHuman(P("HTTP_USER_AGENT")) ){
          919  +    g.perm.Hyperlink = 1;
          920  +    g.javascriptHyperlink = 1;
          921  +  }
          922  +
          923  +  /* If the public-pages glob pattern is defined and REQUEST_URI matches
          924  +  ** one of the globs in public-pages, then also add in all default-perms
          925  +  ** permissions.
          926  +  */
          927  +  zPublicPages = db_get("public-pages",0);
          928  +  if( zPublicPages!=0 ){
          929  +    Glob *pGlob = glob_create(zPublicPages);
          930  +    if( glob_match(pGlob, PD("REQUEST_URI","no-match")) ){
          931  +      login_set_capabilities(db_get("default-perms","u"), 0);
          932  +    }
          933  +    glob_free(pGlob);
          934  +  }
   449    935   }
   450    936   
          937  +/*
          938  +** Memory of settings
          939  +*/
          940  +static int login_anon_once = 1;
          941  +
   451    942   /*
   452    943   ** Add the default privileges of users "nobody" and "anonymous" as appropriate
   453    944   ** for the user g.zLogin.
   454    945   */
   455    946   void login_set_anon_nobody_capabilities(void){
   456         -  static int once = 1;
   457         -  if( g.zLogin && once ){
          947  +  if( g.zLogin && login_anon_once ){
   458    948       const char *zCap;
   459    949       /* All logged-in users inherit privileges from "nobody" */
   460    950       zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'");
   461         -    login_set_capabilities(zCap);
   462         -    if( strcmp(g.zLogin, "anonymous")!=0 ){
          951  +    login_set_capabilities(zCap, 0);
          952  +    if( fossil_strcmp(g.zLogin, "nobody")!=0 ){
   463    953         /* All logged-in users inherit privileges from "anonymous" */
   464    954         zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'");
   465         -      login_set_capabilities(zCap);
          955  +      login_set_capabilities(zCap, 0);
   466    956       }
   467         -    once = 0;
          957  +    login_anon_once = 0;
   468    958     }
   469    959   }
   470    960   
   471    961   /*
   472         -** Set the global capability flags based on a capability string.
          962  +** Flags passed into the 2nd argument of login_set/replace_capabilities().
          963  +*/
          964  +#if INTERFACE
          965  +#define LOGIN_IGNORE_UV  0x01         /* Ignore "u" and "v" */
          966  +#endif
          967  +
          968  +/*
          969  +** Adds all capability flags in zCap to g.perm.
   473    970   */
   474         -void login_set_capabilities(const char *zCap){
   475         -  static char *zDev = 0;
   476         -  static char *zUser = 0;
          971  +void login_set_capabilities(const char *zCap, unsigned flags){
   477    972     int i;
          973  +  if(NULL==zCap){
          974  +    return;
          975  +  }
   478    976     for(i=0; zCap[i]; i++){
   479    977       switch( zCap[i] ){
   480         -      case 's':   g.okSetup = 1;  /* Fall thru into Admin */
   481         -      case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
   482         -                              g.okRdWiki = g.okWrWiki = g.okNewWiki =
   483         -                              g.okApndWiki = g.okHistory = g.okClone = 
   484         -                              g.okNewTkt = g.okPassword = g.okRdAddr =
   485         -                              g.okTktFmt = g.okAttach = g.okApndTkt = 1;
   486         -                              /* Fall thru into Read/Write */
   487         -      case 'i':   g.okRead = g.okWrite = 1;                     break;
   488         -      case 'o':   g.okRead = 1;                                 break;
   489         -      case 'z':   g.okZip = 1;                                  break;
   490         -
   491         -      case 'd':   g.okDelete = 1;                               break;
   492         -      case 'h':   g.okHistory = 1;                              break;
   493         -      case 'g':   g.okClone = 1;                                break;
   494         -      case 'p':   g.okPassword = 1;                             break;
   495         -
   496         -      case 'j':   g.okRdWiki = 1;                               break;
   497         -      case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
   498         -      case 'm':   g.okApndWiki = 1;                             break;
   499         -      case 'f':   g.okNewWiki = 1;                              break;
   500         -
   501         -      case 'e':   g.okRdAddr = 1;                               break;
   502         -      case 'r':   g.okRdTkt = 1;                                break;
   503         -      case 'n':   g.okNewTkt = 1;                               break;
   504         -      case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt = 
   505         -                  g.okApndTkt = 1;                              break;
   506         -      case 'c':   g.okApndTkt = 1;                              break;
   507         -      case 't':   g.okTktFmt = 1;                               break;
   508         -      case 'b':   g.okAttach = 1;                               break;
          978  +      case 's':   g.perm.Setup = 1;  /* Fall thru into Admin */
          979  +      case 'a':   g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
          980  +                           g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
          981  +                           g.perm.ApndWiki = g.perm.Hyperlink = g.perm.Clone = 
          982  +                           g.perm.NewTkt = g.perm.Password = g.perm.RdAddr =
          983  +                           g.perm.TktFmt = g.perm.Attach = g.perm.ApndTkt =
          984  +                           g.perm.ModWiki = g.perm.ModTkt = 1;
          985  +                           /* Fall thru into Read/Write */
          986  +      case 'i':   g.perm.Read = g.perm.Write = 1;                     break;
          987  +      case 'o':   g.perm.Read = 1;                                 break;
          988  +      case 'z':   g.perm.Zip = 1;                                  break;
          989  +
          990  +      case 'd':   g.perm.Delete = 1;                               break;
          991  +      case 'h':   g.perm.Hyperlink = 1;                            break;
          992  +      case 'g':   g.perm.Clone = 1;                                break;
          993  +      case 'p':   g.perm.Password = 1;                             break;
          994  +
          995  +      case 'j':   g.perm.RdWiki = 1;                               break;
          996  +      case 'k':   g.perm.WrWiki = g.perm.RdWiki = g.perm.ApndWiki =1;    break;
          997  +      case 'm':   g.perm.ApndWiki = 1;                             break;
          998  +      case 'f':   g.perm.NewWiki = 1;                              break;
          999  +      case 'l':   g.perm.ModWiki = 1;                              break;
         1000  +
         1001  +      case 'e':   g.perm.RdAddr = 1;                               break;
         1002  +      case 'r':   g.perm.RdTkt = 1;                                break;
         1003  +      case 'n':   g.perm.NewTkt = 1;                               break;
         1004  +      case 'w':   g.perm.WrTkt = g.perm.RdTkt = g.perm.NewTkt = 
         1005  +                  g.perm.ApndTkt = 1;                              break;
         1006  +      case 'c':   g.perm.ApndTkt = 1;                              break;
         1007  +      case 'q':   g.perm.ModTkt = 1;                               break;
         1008  +      case 't':   g.perm.TktFmt = 1;                               break;
         1009  +      case 'b':   g.perm.Attach = 1;                               break;
         1010  +      case 'x':   g.perm.Private = 1;                              break;
   509   1011   
   510   1012         /* The "u" privileges is a little different.  It recursively 
   511   1013         ** inherits all privileges of the user named "reader" */
   512   1014         case 'u': {
   513         -        if( zUser==0 ){
         1015  +        if( (flags & LOGIN_IGNORE_UV)==0 ){
         1016  +          const char *zUser;
   514   1017             zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
   515         -          login_set_capabilities(zUser);
         1018  +          login_set_capabilities(zUser, flags | LOGIN_IGNORE_UV);
   516   1019           }
   517   1020           break;
   518   1021         }
   519   1022   
   520   1023         /* The "v" privileges is a little different.  It recursively 
   521   1024         ** inherits all privileges of the user named "developer" */
   522   1025         case 'v': {
   523         -        if( zDev==0 ){
         1026  +        if( (flags & LOGIN_IGNORE_UV)==0 ){
         1027  +          const char *zDev;
   524   1028             zDev = db_text("", "SELECT cap FROM user WHERE login='developer'");
   525         -          login_set_capabilities(zDev);
         1029  +          login_set_capabilities(zDev, flags | LOGIN_IGNORE_UV);
   526   1030           }
   527   1031           break;
   528   1032         }
   529   1033       }
   530   1034     }
   531   1035   }
         1036  +
         1037  +/*
         1038  +** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
         1039  +*/
         1040  +void login_replace_capabilities(const char *zCap, unsigned flags){
         1041  +  memset(&g.perm, 0, sizeof(g.perm));
         1042  +  login_set_capabilities(zCap, flags);
         1043  +}
   532   1044   
   533   1045   /*
   534   1046   ** If the current login lacks any of the capabilities listed in
   535   1047   ** the input, then return 0.  If all capabilities are present, then
   536   1048   ** return 1.
   537   1049   */
   538   1050   int login_has_capability(const char *zCap, int nCap){
   539   1051     int i;
   540   1052     int rc = 1;
   541   1053     if( nCap<0 ) nCap = strlen(zCap);
   542   1054     for(i=0; i<nCap && rc && zCap[i]; i++){
   543   1055       switch( zCap[i] ){
   544         -      case 'a':  rc = g.okAdmin;     break;
   545         -      case 'b':  rc = g.okAttach;    break;
   546         -      case 'c':  rc = g.okApndTkt;   break;
   547         -      case 'd':  rc = g.okDelete;    break;
   548         -      case 'e':  rc = g.okRdAddr;    break;
   549         -      case 'f':  rc = g.okNewWiki;   break;
   550         -      case 'g':  rc = g.okClone;     break;
   551         -      case 'h':  rc = g.okHistory;   break;
   552         -      case 'i':  rc = g.okWrite;     break;
   553         -      case 'j':  rc = g.okRdWiki;    break;
   554         -      case 'k':  rc = g.okWrWiki;    break;
   555         -      /* case 'l': */
   556         -      case 'm':  rc = g.okApndWiki;  break;
   557         -      case 'n':  rc = g.okNewTkt;    break;
   558         -      case 'o':  rc = g.okRead;      break;
   559         -      case 'p':  rc = g.okPassword;  break;
   560         -      /* case 'q': */
   561         -      case 'r':  rc = g.okRdTkt;     break;
   562         -      case 's':  rc = g.okSetup;     break;
   563         -      case 't':  rc = g.okTktFmt;    break;
         1056  +      case 'a':  rc = g.perm.Admin;     break;
         1057  +      case 'b':  rc = g.perm.Attach;    break;
         1058  +      case 'c':  rc = g.perm.ApndTkt;   break;
         1059  +      case 'd':  rc = g.perm.Delete;    break;
         1060  +      case 'e':  rc = g.perm.RdAddr;    break;
         1061  +      case 'f':  rc = g.perm.NewWiki;   break;
         1062  +      case 'g':  rc = g.perm.Clone;     break;
         1063  +      case 'h':  rc = g.perm.Hyperlink; break;
         1064  +      case 'i':  rc = g.perm.Write;     break;
         1065  +      case 'j':  rc = g.perm.RdWiki;    break;
         1066  +      case 'k':  rc = g.perm.WrWiki;    break;
         1067  +      case 'l':  rc = g.perm.ModWiki;   break;
         1068  +      case 'm':  rc = g.perm.ApndWiki;  break;
         1069  +      case 'n':  rc = g.perm.NewTkt;    break;
         1070  +      case 'o':  rc = g.perm.Read;      break;
         1071  +      case 'p':  rc = g.perm.Password;  break;
         1072  +      case 'q':  rc = g.perm.ModTkt;    break;
         1073  +      case 'r':  rc = g.perm.RdTkt;     break;
         1074  +      case 's':  rc = g.perm.Setup;     break;
         1075  +      case 't':  rc = g.perm.TktFmt;    break;
   564   1076         /* case 'u': READER    */
   565   1077         /* case 'v': DEVELOPER */
   566         -      case 'w':  rc = g.okWrTkt;     break;
   567         -      /* case 'x': */
         1078  +      case 'w':  rc = g.perm.WrTkt;     break;
         1079  +      case 'x':  rc = g.perm.Private;   break;
   568   1080         /* case 'y': */
   569         -      case 'z':  rc = g.okZip;       break;
         1081  +      case 'z':  rc = g.perm.Zip;       break;
   570   1082         default:   rc = 0;             break;
   571   1083       }
   572   1084     }
   573   1085     return rc;
   574   1086   }
         1087  +
         1088  +/*
         1089  +** Change the login to zUser.
         1090  +*/
         1091  +void login_as_user(const char *zUser){
         1092  +  char *zCap = "";   /* New capabilities */
         1093  +
         1094  +  /* Turn off all capabilities from prior logins */
         1095  +  memset( &g.perm, 0, sizeof(g.perm) );
         1096  +
         1097  +  /* Set the global variables recording the userid and login.  The
         1098  +  ** "nobody" user is a special case in that g.zLogin==0.
         1099  +  */
         1100  +  g.userUid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUser);
         1101  +  if( g.userUid==0 ){
         1102  +    zUser = 0;
         1103  +    g.userUid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
         1104  +  }
         1105  +  if( g.userUid ){
         1106  +    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", g.userUid);
         1107  +  }
         1108  +  if( fossil_strcmp(zUser,"nobody")==0 ) zUser = 0;
         1109  +  g.zLogin = fossil_strdup(zUser);
         1110  +
         1111  +  /* Set the capabilities */
         1112  +  login_set_capabilities(zCap, 0);
         1113  +  login_anon_once = 1;
         1114  +  login_set_anon_nobody_capabilities();
         1115  +}
   575   1116   
   576   1117   /*
   577   1118   ** Call this routine when the credential check fails.  It causes
   578   1119   ** a redirect to the "login" page.
   579   1120   */
   580   1121   void login_needed(void){
   581         -  const char *zUrl = PD("REQUEST_URI", "index");
   582         -  cgi_redirect(mprintf("login?g=%T", zUrl));
   583         -  /* NOTREACHED */
   584         -  assert(0);
         1122  +#ifdef FOSSIL_ENABLE_JSON
         1123  +  if(g.json.isJsonMode){
         1124  +    json_err( FSL_JSON_E_DENIED, NULL, 1 );
         1125  +    fossil_exit(0);
         1126  +    /* NOTREACHED */
         1127  +    assert(0);
         1128  +  }else
         1129  +#endif /* FOSSIL_ENABLE_JSON */
         1130  +  {
         1131  +    const char *zUrl = PD("REQUEST_URI", "index");
         1132  +    cgi_redirect(mprintf("login?g=%T", zUrl));
         1133  +    /* NOTREACHED */
         1134  +    assert(0);
         1135  +  }
   585   1136   }
   586   1137   
   587   1138   /*
   588         -** Call this routine if the user lacks okHistory permission.  If
   589         -** the anonymous user has okHistory permission, then paint a mesage
         1139  +** Call this routine if the user lacks g.perm.Hyperlink permission.  If
         1140  +** the anonymous user has Hyperlink permission, then paint a mesage
   590   1141   ** to inform the user that much more information is available by
   591   1142   ** logging in as anonymous.
   592   1143   */
   593   1144   void login_anonymous_available(void){
   594         -  if( !g.okHistory &&
         1145  +  if( !g.perm.Hyperlink &&
   595   1146         db_exists("SELECT 1 FROM user"
   596   1147                   " WHERE login='anonymous'"
   597   1148                   "   AND cap LIKE '%%h%%'") ){
   598   1149       const char *zUrl = PD("REQUEST_URI", "index");
   599         -    @ <p>Many <font color="red">hyperlinks are disabled.</font><br />
   600         -    @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
         1150  +    @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
         1151  +    @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
   601   1152       @ to enable hyperlinks.</p>
   602   1153     }
   603   1154   }
   604   1155   
   605   1156   /*
   606   1157   ** While rendering a form, call this routine to add the Anti-CSRF token
   607   1158   ** as a hidden element of the form.
   608   1159   */
   609   1160   void login_insert_csrf_secret(void){
   610         -  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
         1161  +  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" />
   611   1162   }
   612   1163   
   613   1164   /*
   614   1165   ** Before using the results of a form, first call this routine to verify
   615         -** that ths Anti-CSRF token is present and is valid.  If the Anti-CSRF token
         1166  +** that this Anti-CSRF token is present and is valid.  If the Anti-CSRF token
   616   1167   ** is missing or is incorrect, that indicates a cross-site scripting attach
   617   1168   ** so emits an error message and abort.
   618   1169   */
   619   1170   void login_verify_csrf_secret(void){
   620         -  const char *zCsrf;            /* The CSRF secret */
   621   1171     if( g.okCsrf ) return;
   622         -  if( (zCsrf = P("csrf"))!=0 && strcmp(zCsrf, g.zCsrfToken)==0 ){
         1172  +  if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
   623   1173       g.okCsrf = 1;
   624   1174       return;
   625   1175     }
   626   1176     fossil_fatal("Cross-site request forgery attempt");
   627   1177   }
         1178  +
         1179  +/*
         1180  +** WEBPAGE: register
         1181  +**
         1182  +** Generate the register page.
         1183  +**
         1184  +*/
         1185  +void register_page(void){
         1186  +  const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;
         1187  +  unsigned int uSeed;
         1188  +  char const *zDecoded;
         1189  +  char *zCaptcha;
         1190  +  if( !db_get_boolean("self-register", 0) ){
         1191  +    style_header("Registration not possible");
         1192  +    @ <p>This project does not allow user self-registration. Please contact the
         1193  +    @ project administrator to obtain an account.</p>
         1194  +    style_footer();
         1195  +    return;
         1196  +  }
         1197  +
         1198  +  style_header("Register");
         1199  +  zUsername = P("u");
         1200  +  zPasswd = P("p");
         1201  +  zConfirm = P("cp");
         1202  +  zContact = P("c");
         1203  +  zCap = P("cap");
         1204  +  zCS = P("cs"); /* Captcha Secret */
         1205  +
         1206  +  /* Try to make any sense from user input. */
         1207  +  if( P("new") ){
         1208  +    if( zCS==0 ) fossil_redirect_home();  /* Forged request */
         1209  +    zPw = captcha_decode((unsigned int)atoi(zCS));
         1210  +    if( !(zUsername && zPasswd && zConfirm && zContact) ){
         1211  +      @ <p><span class="loginError">
         1212  +      @ All fields are obligatory.
         1213  +      @ </span></p>
         1214  +    }else if( strlen(zPasswd) < 6){
         1215  +      @ <p><span class="loginError">
         1216  +      @ Password too weak.
         1217  +      @ </span></p>
         1218  +    }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){
         1219  +      @ <p><span class="loginError">
         1220  +      @ The two copies of your new passwords do not match.
         1221  +      @ </span></p>
         1222  +    }else if( fossil_stricmp(zPw, zCap)!=0 ){
         1223  +      @ <p><span class="loginError">
         1224  +      @ Captcha text invalid.
         1225  +      @ </span></p>
         1226  +    }else{
         1227  +      /* This almost is stupid copy-paste of code from user.c:user_cmd(). */
         1228  +      Blob passwd, login, caps, contact;
         1229  +
         1230  +      blob_init(&login, zUsername, -1);
         1231  +      blob_init(&contact, zContact, -1);
         1232  +      blob_init(&caps, db_get("default-perms", "u"), -1);
         1233  +      blob_init(&passwd, zPasswd, -1);
         1234  +
         1235  +      if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
         1236  +        /* Here lies the reason I don't use zErrMsg - it would not substitute
         1237  +         * this %s(zUsername), or at least I don't know how to force it to.*/
         1238  +        @ <p><span class="loginError">
         1239  +        @ %s(zUsername) already exists.
         1240  +        @ </span></p>
         1241  +      }else{
         1242  +        char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
         1243  +        int uid;
         1244  +        db_multi_exec(
         1245  +            "INSERT INTO user(login,pw,cap,info)"
         1246  +            "VALUES(%B,%Q,%B,%B)",
         1247  +            &login, zPw, &caps, &contact
         1248  +            );
         1249  +        free(zPw);
         1250  +
         1251  +        /* The user is registered, now just log him in. */
         1252  +        uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
         1253  +        login_set_user_cookie( zUsername, uid, NULL );
         1254  +        redirect_to_g();
         1255  +
         1256  +      }
         1257  +    }
         1258  +  }
         1259  +
         1260  +  /* Prepare the captcha. */
         1261  +  uSeed = captcha_seed();
         1262  +  zDecoded = captcha_decode(uSeed);
         1263  +  zCaptcha = captcha_render(zDecoded);
         1264  +
         1265  +  /* Print out the registration form. */
         1266  +  form_begin(0, "%R/register");
         1267  +  if( P("g") ){
         1268  +    @ <input type="hidden" name="g" value="%h(P("g"))" />
         1269  +  }
         1270  +  @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
         1271  +  @ <table class="login_out">
         1272  +  @ <tr>
         1273  +  @   <td class="login_out_label" align="right">User ID:</td>
         1274  +  @   <td><input type="text" id="u" name="u" value="" size="30" /></td>
         1275  +  @ </tr>
         1276  +  @ <tr>
         1277  +  @   <td class="login_out_label" align="right">Password:</td>
         1278  +  @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
         1279  +  @ </tr>
         1280  +  @ <tr>
         1281  +  @   <td class="login_out_label" align="right">Confirm password:</td>
         1282  +  @   <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
         1283  +  @ </tr>
         1284  +  @ <tr>
         1285  +  @   <td class="login_out_label" align="right">Contact info:</td>
         1286  +  @   <td><input type="text" id="c" name="c" value="" size="30" /></td>
         1287  +  @ </tr>
         1288  +  @ <tr>
         1289  +  @   <td class="login_out_label" align="right">Captcha text (below):</td>
         1290  +  @   <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
         1291  +  @ </tr>
         1292  +  @ <tr><td></td>
         1293  +  @ <td><input type="submit" name="new" value="Register" /></td></tr>
         1294  +  @ </table>
         1295  +  @ <div class="captcha"><table class="captcha"><tr><td><pre>
         1296  +  @ %h(zCaptcha)
         1297  +  @ </pre></td></tr></table></div>
         1298  +  @ </form>
         1299  +  style_footer();
         1300  +
         1301  +  free(zCaptcha);
         1302  +}
         1303  +
         1304  +/*
         1305  +** Run SQL on the repository database for every repository in our
         1306  +** login group.  The SQL is run in a separate database connection.
         1307  +**
         1308  +** Any members of the login group whose repository database file
         1309  +** cannot be found is silently removed from the group.
         1310  +**
         1311  +** Error messages accumulate and are returned in *pzErrorMsg.  The
         1312  +** memory used to hold these messages should be freed using
         1313  +** fossil_free() if one desired to avoid a memory leak.  The
         1314  +** zPrefix and zSuffix strings surround each error message.
         1315  +**
         1316  +** Return the number of errors.
         1317  +*/
         1318  +int login_group_sql(
         1319  +  const char *zSql,        /* The SQL to run */
         1320  +  const char *zPrefix,     /* Prefix to each error message */
         1321  +  const char *zSuffix,     /* Suffix to each error message */
         1322  +  char **pzErrorMsg        /* Write error message here, if not NULL */
         1323  +){
         1324  +  sqlite3 *pPeer;          /* Connection to another database */
         1325  +  int nErr = 0;            /* Number of errors seen so far */
         1326  +  int rc;                  /* Result code from subroutine calls */
         1327  +  char *zErr;              /* SQLite error text */
         1328  +  char *zSelfCode;         /* Project code for ourself */
         1329  +  Blob err;                /* Accumulate errors here */
         1330  +  Stmt q;                  /* Query of all peer-* entries in CONFIG */
         1331  +
         1332  +  if( zPrefix==0 ) zPrefix = "";
         1333  +  if( zSuffix==0 ) zSuffix = "";
         1334  +  if( pzErrorMsg ) *pzErrorMsg = 0;
         1335  +  zSelfCode = abbreviated_project_code(db_get("project-code", "x"));
         1336  +  blob_zero(&err);
         1337  +  db_prepare(&q, 
         1338  +    "SELECT name, value FROM config"
         1339  +    " WHERE name GLOB 'peer-repo-*'"
         1340  +    "   AND name <> 'peer-repo-%q'"
         1341  +    " ORDER BY +value",
         1342  +    zSelfCode
         1343  +  );
         1344  +  while( db_step(&q)==SQLITE_ROW ){
         1345  +    const char *zRepoName = db_column_text(&q, 1);
         1346  +    if( file_size(zRepoName)<0 ){
         1347  +      /* Silently remove non-existent repositories from the login group. */
         1348  +      const char *zLabel = db_column_text(&q, 0);
         1349  +      db_multi_exec(
         1350  +         "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
         1351  +         &zLabel[10]
         1352  +      );
         1353  +      continue;
         1354  +    }
         1355  +    rc = sqlite3_open_v2(zRepoName, &pPeer, SQLITE_OPEN_READWRITE, 0);
         1356  +    if( rc!=SQLITE_OK ){
         1357  +      blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
         1358  +                   sqlite3_errmsg(pPeer), zSuffix);
         1359  +      nErr++;
         1360  +      sqlite3_close(pPeer);
         1361  +      continue;
         1362  +    }
         1363  +    sqlite3_create_function(pPeer, "shared_secret", 3, SQLITE_UTF8,
         1364  +                            0, sha1_shared_secret_sql_function, 0, 0);
         1365  +    sqlite3_create_function(pPeer, "now", 0,SQLITE_ANY,0,db_now_function,0,0);
         1366  +    sqlite3_busy_timeout(pPeer, 5000);
         1367  +    zErr = 0;
         1368  +    rc = sqlite3_exec(pPeer, zSql, 0, 0, &zErr);
         1369  +    if( zErr ){
         1370  +      blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, zErr, zSuffix);
         1371  +      sqlite3_free(zErr);
         1372  +      nErr++;
         1373  +    }else if( rc!=SQLITE_OK ){
         1374  +      blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
         1375  +                   sqlite3_errmsg(pPeer), zSuffix);
         1376  +      nErr++;
         1377  +    }
         1378  +    sqlite3_close(pPeer);
         1379  +  }
         1380  +  db_finalize(&q);
         1381  +  if( pzErrorMsg && blob_size(&err)>0 ){
         1382  +    *pzErrorMsg = fossil_strdup(blob_str(&err));
         1383  +  }
         1384  +  blob_reset(&err);
         1385  +  fossil_free(zSelfCode);
         1386  +  return nErr;
         1387  +}
         1388  +
         1389  +/*
         1390  +** Attempt to join a login-group.
         1391  +**
         1392  +** If problems arise, leave an error message in *pzErrMsg.
         1393  +*/
         1394  +void login_group_join(
         1395  +  const char *zRepo,         /* Repository file in the login group */
         1396  +  const char *zLogin,        /* Login name for the other repo */
         1397  +  const char *zPassword,     /* Password to prove we are authorized to join */
         1398  +  const char *zNewName,      /* Name of new login group if making a new one */
         1399  +  char **pzErrMsg            /* Leave an error message here */
         1400  +){
         1401  +  Blob fullName;             /* Blob for finding full pathnames */
         1402  +  sqlite3 *pOther;           /* The other repository */
         1403  +  int rc;                    /* Return code from sqlite3 functions */
         1404  +  char *zOtherProjCode;      /* Project code for pOther */
         1405  +  char *zPwHash;             /* Password hash on pOther */
         1406  +  char *zSelfRepo;           /* Name of our repository */
         1407  +  char *zSelfLabel;          /* Project-name for our repository */
         1408  +  char *zSelfProjCode;       /* Our project-code */
         1409  +  char *zSql;                /* SQL to run on all peers */
         1410  +  const char *zSelf;         /* The ATTACH name of our repository */
         1411  +
         1412  +  *pzErrMsg = 0;   /* Default to no errors */
         1413  +  zSelf = db_name("repository");
         1414  +
         1415  +  /* Get the full pathname of the other repository */  
         1416  +  file_canonical_name(zRepo, &fullName, 0);
         1417  +  zRepo = mprintf(blob_str(&fullName));
         1418  +  blob_reset(&fullName);
         1419  +
         1420  +  /* Get the full pathname for our repository.  Also the project code
         1421  +  ** and project name for ourself. */
         1422  +  file_canonical_name(g.zRepositoryName, &fullName, 0);
         1423  +  zSelfRepo = mprintf(blob_str(&fullName));
         1424  +  blob_reset(&fullName);
         1425  +  zSelfProjCode = db_get("project-code", "unknown");
         1426  +  zSelfLabel = db_get("project-name", 0);
         1427  +  if( zSelfLabel==0 ){
         1428  +    zSelfLabel = zSelfProjCode;
         1429  +  }
         1430  +
         1431  +  /* Make sure we are not trying to join ourselves */
         1432  +  if( fossil_strcmp(zRepo, zSelfRepo)==0 ){
         1433  +    *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
         1434  +    return;
         1435  +  }
         1436  +
         1437  +  /* Make sure the other repository is a valid Fossil database */
         1438  +  if( file_size(zRepo)<0 ){
         1439  +    *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
         1440  +    return;
         1441  +  }
         1442  +  rc = sqlite3_open(zRepo, &pOther);
         1443  +  if( rc!=SQLITE_OK ){
         1444  +    *pzErrMsg = mprintf(sqlite3_errmsg(pOther));
         1445  +  }else{
         1446  +    rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg);
         1447  +  }
         1448  +  sqlite3_close(pOther);
         1449  +  if( rc ) return;
         1450  +
         1451  +  /* Attach the other repository.  Make sure the username/password is
         1452  +  ** valid and has Setup permission.
         1453  +  */
         1454  +  db_attach(zRepo, "other");
         1455  +  zOtherProjCode = db_text("x", "SELECT value FROM other.config"
         1456  +                                " WHERE name='project-code'");
         1457  +  zPwHash = sha1_shared_secret(zPassword, zLogin, zOtherProjCode);
         1458  +  if( !db_exists(
         1459  +    "SELECT 1 FROM other.user"
         1460  +    " WHERE login=%Q AND cap GLOB '*s*'"
         1461  +    "   AND (pw=%Q OR pw=%Q)",
         1462  +    zLogin, zPassword, zPwHash)
         1463  +  ){
         1464  +    db_detach("other");
         1465  +    *pzErrMsg = "The supplied username/password does not correspond to a"
         1466  +                " user Setup permission on the other repository.";
         1467  +    return;
         1468  +  }
         1469  +
         1470  +  /* Create all the necessary CONFIG table entries on both the
         1471  +  ** other repository and on our own repository.
         1472  +  */
         1473  +  zSelfProjCode = abbreviated_project_code(zSelfProjCode);
         1474  +  zOtherProjCode = abbreviated_project_code(zOtherProjCode);
         1475  +  db_begin_transaction();
         1476  +  db_multi_exec(
         1477  +    "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
         1478  +    "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
         1479  +    "INSERT INTO %s.config(name,value) "
         1480  +    "  SELECT 'peer-name-%q', value FROM other.config"
         1481  +    "   WHERE name='project-name';",
         1482  +    zSelf,
         1483  +    zSelf, zOtherProjCode, zRepo,
         1484  +    zSelf, zOtherProjCode
         1485  +  );
         1486  +  db_multi_exec(
         1487  +    "INSERT OR IGNORE INTO other.config(name,value)"
         1488  +    " VALUES('login-group-name',%Q);"
         1489  +    "INSERT OR IGNORE INTO other.config(name,value)"
         1490  +    " VALUES('login-group-code',lower(hex(randomblob(8))));",
         1491  +    zNewName
         1492  +  );
         1493  +  db_multi_exec(
         1494  +    "REPLACE INTO %s.config(name,value)"
         1495  +    "  SELECT name, value FROM other.config"
         1496  +    "   WHERE name GLOB 'peer-*' OR name GLOB 'login-group-*'",
         1497  +    zSelf
         1498  +  );
         1499  +  db_end_transaction(0);
         1500  +  db_multi_exec("DETACH other");
         1501  +
         1502  +  /* Propagate the changes to all other members of the login-group */
         1503  +  zSql = mprintf(
         1504  +    "BEGIN;"
         1505  +    "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());"
         1506  +    "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());"
         1507  +    "COMMIT;",
         1508  +    zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
         1509  +  );
         1510  +  login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
         1511  +  fossil_free(zSql);
         1512  +}
         1513  +
         1514  +/*
         1515  +** Leave the login group that we are currently part of.
         1516  +*/
         1517  +void login_group_leave(char **pzErrMsg){
         1518  +  char *zProjCode;
         1519  +  char *zSql;
         1520  +
         1521  +  *pzErrMsg = 0;
         1522  +  zProjCode = abbreviated_project_code(db_get("project-code","x"));
         1523  +  zSql = mprintf(
         1524  +    "DELETE FROM config WHERE name GLOB 'peer-*-%q';"
         1525  +    "DELETE FROM config"
         1526  +    " WHERE name='login-group-name'"
         1527  +    "   AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;",
         1528  +    zProjCode
         1529  +  );
         1530  +  fossil_free(zProjCode);
         1531  +  login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
         1532  +  fossil_free(zSql);
         1533  +  db_multi_exec(
         1534  +    "DELETE FROM config "
         1535  +    " WHERE name GLOB 'peer-*'"
         1536  +    "    OR name GLOB 'login-group-*';"
         1537  +  );
         1538  +}

Changes to src/main.c.

     1      1   /*
     2      2   ** Copyright (c) 2006 D. Richard Hipp
     3      3   **
     4      4   ** This program is free software; you can redistribute it and/or
     5      5   ** modify it under the terms of the Simplified BSD License (also
     6      6   ** known as the "2-Clause License" or "FreeBSD License".)
     7         -
            7  +**
     8      8   ** This program is distributed in the hope that it will be useful,
     9      9   ** but without any warranty; without even the implied warranty of
    10     10   ** merchantability or fitness for a particular purpose.
    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
................................................................................
    21     21   #include "config.h"
    22     22   #include "main.h"
    23     23   #include <string.h>
    24     24   #include <time.h>
    25     25   #include <fcntl.h>
    26     26   #include <sys/types.h>
    27     27   #include <sys/stat.h>
    28         -
    29         -
           28  +#include <stdlib.h> /* atexit() */
           29  +#if defined(_WIN32)
           30  +#  include <windows.h>
           31  +#else
           32  +#  include <errno.h> /* errno global */
           33  +#endif
    30     34   #if INTERFACE
           35  +#ifdef FOSSIL_ENABLE_JSON
           36  +#  include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
           37  +#  include "json_detail.h"
           38  +#endif
           39  +#ifdef FOSSIL_ENABLE_TCL
           40  +#include "tcl.h"
           41  +#endif
    31     42   
    32     43   /*
    33     44   ** Number of elements in an array
    34     45   */
    35     46   #define count(X)  (sizeof(X)/sizeof(X[0]))
    36     47   
    37     48   /*
................................................................................
    40     51   #define UUID_SIZE 40
    41     52   
    42     53   /*
    43     54   ** Maximum number of auxiliary parameters on reports
    44     55   */
    45     56   #define MX_AUX  5
    46     57   
           58  +/*
           59  +** Holds flags for fossil user permissions.
           60  +*/
           61  +struct FossilUserPerms {
           62  +  char Setup;            /* s: use Setup screens on web interface */
           63  +  char Admin;            /* a: administrative permission */
           64  +  char Delete;           /* d: delete wiki or tickets */
           65  +  char Password;         /* p: change password */
           66  +  char Query;            /* q: create new reports */
           67  +  char Write;            /* i: xfer inbound. checkin */
           68  +  char Read;             /* o: xfer outbound. checkout */
           69  +  char Hyperlink;        /* h: enable the display of hyperlinks */
           70  +  char Clone;            /* g: clone */
           71  +  char RdWiki;           /* j: view wiki via web */
           72  +  char NewWiki;          /* f: create new wiki via web */
           73  +  char ApndWiki;         /* m: append to wiki via web */
           74  +  char WrWiki;           /* k: edit wiki via web */
           75  +  char ModWiki;          /* l: approve and publish wiki content (Moderator) */
           76  +  char RdTkt;            /* r: view tickets via web */
           77  +  char NewTkt;           /* n: create new tickets */
           78  +  char ApndTkt;          /* c: append to tickets via the web */
           79  +  char WrTkt;            /* w: make changes to tickets via web */
           80  +  char ModTkt;           /* q: approve and publish ticket changes (Moderator) */
           81  +  char Attach;           /* b: add attachments */
           82  +  char TktFmt;           /* t: create new ticket report formats */
           83  +  char RdAddr;           /* e: read email addresses or other private data */
           84  +  char Zip;              /* z: download zipped artifact via /zip URL */
           85  +  char Private;          /* x: can send and receive private content */
           86  +};
           87  +
           88  +#ifdef FOSSIL_ENABLE_TCL
           89  +/*
           90  +** All Tcl related context information is in this structure.  This structure
           91  +** definition has been copied from and should be kept in sync with the one in
           92  +** "th_tcl.c".
           93  +*/
           94  +struct TclContext {
           95  +  int argc;              /* Number of original (expanded) arguments. */
           96  +  char **argv;           /* Full copy of the original (expanded) arguments. */
           97  +  void *library;         /* The Tcl library module handle. */
           98  +  void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
           99  +  void *xCreateInterp;   /* See tcl_CreateInterpProc in th_tcl.c. */
          100  +  Tcl_Interp *interp;    /* The on-demand created Tcl interpreter. */
          101  +  char *setup;           /* The optional Tcl setup script. */
          102  +  void *xPreEval;        /* Optional, called before Tcl_Eval*(). */
          103  +  void *pPreContext;     /* Optional, provided to xPreEval(). */
          104  +  void *xPostEval;       /* Optional, called after Tcl_Eval*(). */
          105  +  void *pPostContext;    /* Optional, provided to xPostEval(). */
          106  +};
          107  +#endif
          108  +
    47    109   /*
    48    110   ** All global variables are in this structure.
    49    111   */
    50    112   struct Global {
    51    113     int argc; char **argv;  /* Command-line arguments to the program */
          114  +  char *nameOfExe;        /* Full path of executable. */
    52    115     int isConst;            /* True if the output is unchanging */
    53    116     sqlite3 *db;            /* The connection to the databases */
    54    117     sqlite3 *dbConfig;      /* Separate connection for global_config table */
    55    118     int useAttach;          /* True if global_config is attached to repository */
    56    119     int configOpen;         /* True if the config database is open */
    57         -  long long int now;      /* Seconds since 1970 */
          120  +  sqlite3_int64 now;      /* Seconds since 1970 */
    58    121     int repositoryOpen;     /* True if the main repository database is open */
    59    122     char *zRepositoryName;  /* Name of the repository database */
          123  +  const char *zMainDbType;/* "configdb", "localdb", or "repository" */
          124  +  const char *zConfigDbType;  /* "configdb", "localdb", or "repository" */
    60    125     const char *zHome;      /* Name of user home directory */
    61    126     int localOpen;          /* True if the local database is open */
    62    127     char *zLocalRoot;       /* The directory holding the  local database */
    63    128     int minPrefix;          /* Number of digits needed for a distinct UUID */
    64         -  int fSqlTrace;          /* True if -sqltrace flag is present */
          129  +  int fSqlTrace;          /* True if --sqltrace flag is present */
          130  +  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
    65    131     int fSqlPrint;          /* True if -sqlprint flag is present */
    66    132     int fQuiet;             /* True if -quiet flag is present */
    67    133     int fHttpTrace;         /* Trace outbound HTTP requests */
    68         -  int fNoSync;            /* Do not do an autosync even.  --nosync */
          134  +  int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
          135  +  int fSshTrace;          /* Trace the SSH setup traffic */
          136  +  int fNoSync;            /* Do not do an autosync ever.  --nosync */
    69    137     char *zPath;            /* Name of webpage being served */
    70    138     char *zExtra;           /* Extra path information past the webpage name */
    71    139     char *zBaseURL;         /* Full text of the URL being served */
    72    140     char *zTop;             /* Parent directory of zPath */
    73    141     const char *zContentType;  /* The content type of the input HTTP request */
    74    142     int iErrPriority;       /* Priority of current error message */
    75    143     char *zErrMsg;          /* Text of an error message */
          144  +  int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
    76    145     Blob cgiIn;             /* Input to an xfer www method */
    77    146     int cgiOutput;          /* Write error and status messages to CGI */
    78    147     int xferPanic;          /* Write error messages in XFER protocol */
    79    148     int fullHttpReply;      /* True for full HTTP reply.  False for CGI reply */
    80    149     Th_Interp *interp;      /* The TH1 interpreter */
          150  +  char *th1Setup;         /* The TH1 post-creation setup script, if any */
    81    151     FILE *httpIn;           /* Accept HTTP input from here */
    82    152     FILE *httpOut;          /* Send HTTP output here */
    83    153     int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
    84    154     int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
    85    155     int *aCommitFile;       /* Array of files to be committed */
    86    156     int markPrivate;        /* All new artifacts are private if true */
          157  +  int clockSkewSeen;      /* True if clocks on client and server out of sync */
          158  +  int wikiFlags;          /* Wiki conversion flags applied to %w and %W */
          159  +  char isHTTP;            /* True if server/CGI modes, else assume CLI. */
          160  +  char javascriptHyperlink; /* If true, set href= using script, not HTML */
    87    161   
    88    162     int urlIsFile;          /* True if a "file:" url */
    89    163     int urlIsHttps;         /* True if a "https:" url */
          164  +  int urlIsSsh;           /* True if an "ssh:" url */
    90    165     char *urlName;          /* Hostname for http: or filename for file: */
    91    166     char *urlHostname;      /* The HOST: parameter on http headers */
    92    167     char *urlProtocol;      /* "http" or "https" */
    93    168     int urlPort;            /* TCP port number for http: or https: */
    94    169     int urlDfltPort;        /* The default port for the given protocol */
    95    170     char *urlPath;          /* Pathname for http: */
    96    171     char *urlUser;          /* User id for http: */
    97    172     char *urlPasswd;        /* Password for http: */
    98    173     char *urlCanonical;     /* Canonical representation of the URL */
    99    174     char *urlProxyAuth;     /* Proxy-Authorizer: string */
          175  +  char *urlFossil;        /* The fossil query parameter on ssh: */
          176  +  char *urlShell;         /* The shell query parameter on ssh: */
   100    177     int dontKeepUrl;        /* Do not persist the URL */
   101    178   
   102    179     const char *zLogin;     /* Login name.  "" if not logged in. */
          180  +  const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of SSL client identity */
          181  +  int useLocalauth;       /* No login required if from 127.0.0.1 */
   103    182     int noPswd;             /* Logged in without password (on 127.0.0.1) */
   104    183     int userUid;            /* Integer user id */
   105    184   
   106    185     /* Information used to populate the RCVFROM table */
   107    186     int rcvid;              /* The rcvid.  0 if not yet defined. */
   108    187     char *zIpAddr;          /* The remote IP address */
   109    188     char *zNonce;           /* The nonce used for login */
   110         -  
          189  +
   111    190     /* permissions used by the server */
   112         -  int okSetup;            /* s: use Setup screens on web interface */
   113         -  int okAdmin;            /* a: administrative permission */
   114         -  int okDelete;           /* d: delete wiki or tickets */
   115         -  int okPassword;         /* p: change password */
   116         -  int okQuery;            /* q: create new reports */
   117         -  int okWrite;            /* i: xfer inbound. checkin */
   118         -  int okRead;             /* o: xfer outbound. checkout */
   119         -  int okHistory;          /* h: access historical information. */
   120         -  int okClone;            /* g: clone */
   121         -  int okRdWiki;           /* j: view wiki via web */
   122         -  int okNewWiki;          /* f: create new wiki via web */
   123         -  int okApndWiki;         /* m: append to wiki via web */
   124         -  int okWrWiki;           /* k: edit wiki via web */
   125         -  int okRdTkt;            /* r: view tickets via web */
   126         -  int okNewTkt;           /* n: create new tickets */
   127         -  int okApndTkt;          /* c: append to tickets via the web */
   128         -  int okWrTkt;            /* w: make changes to tickets via web */
   129         -  int okAttach;           /* b: add attachments */
   130         -  int okTktFmt;           /* t: create new ticket report formats */
   131         -  int okRdAddr;           /* e: read email addresses or other private data */
   132         -  int okZip;              /* z: download zipped artifact via /zip URL */
          191  +  struct FossilUserPerms perm;
          192  +
          193  +#ifdef FOSSIL_ENABLE_TCL
          194  +  /* all Tcl related context necessary for integration */
          195  +  struct TclContext tcl;
          196  +#endif
   133    197   
   134    198     /* For defense against Cross-site Request Forgery attacks */
   135    199     char zCsrfToken[12];    /* Value of the anti-CSRF token */
   136    200     int okCsrf;             /* Anti-CSRF token is present and valid */
   137    201   
          202  +  int parseCnt[10];       /* Counts of artifacts parsed */
   138    203     FILE *fDebug;           /* Write debug information here, if the file exists */
   139    204     int thTrace;            /* True to enable TH1 debugging output */
   140    205     Blob thLog;             /* Text of the TH1 debugging output */
   141    206   
   142    207     int isHome;             /* True if rendering the "home" page */
   143    208   
   144    209     /* Storage for the aux() and/or option() SQL function arguments */
   145    210     int nAux;                    /* Number of distinct aux() or option() values */
   146    211     const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
   147    212     char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
   148    213     const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
   149    214     const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
   150    215     int anAuxCols[MX_AUX];         /* Number of columns for option() values */
          216  +
          217  +  int allowSymlinks;             /* Cached "allow-symlinks" option */
          218  +
          219  +#ifdef FOSSIL_ENABLE_JSON
          220  +  struct FossilJsonBits {
          221  +    int isJsonMode;            /* True if running in JSON mode, else
          222  +                                  false. This changes how errors are
          223  +                                  reported. In JSON mode we try to
          224  +                                  always output JSON-form error
          225  +                                  responses and always exit() with
          226  +                                  code 0 to avoid an HTTP 500 error.
          227  +                               */
          228  +    int resultCode;            /* used for passing back specific codes from /json callbacks. */
          229  +    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
          230  +    cson_output_opt outOpt;    /* formatting options for JSON mode. */
          231  +    cson_value * authToken;    /* authentication token */
          232  +    char const * jsonp;        /* Name of JSONP function wrapper. */
          233  +    unsigned char dispatchDepth /* Tells JSON command dispatching
          234  +                                   which argument we are currently
          235  +                                   working on. For this purpose, arg#0
          236  +                                   is the "json" path/CLI arg.
          237  +                                */;
          238  +    struct {                   /* "garbage collector" */
          239  +      cson_value * v;
          240  +      cson_array * a;
          241  +    } gc;
          242  +    struct {                   /* JSON POST data. */
          243  +      cson_value * v;
          244  +      cson_array * a;
          245  +      int offset;              /* Tells us which PATH_INFO/CLI args
          246  +                                  part holds the "json" command, so
          247  +                                  that we can account for sub-repos
          248  +                                  and path prefixes.  This is handled
          249  +                                  differently for CLI and CGI modes.
          250  +                               */
          251  +      char const * commandStr  /*"command" request param.*/;
          252  +    } cmd;
          253  +    struct {                   /* JSON POST data. */
          254  +      cson_value * v;
          255  +      cson_object * o;
          256  +    } post;
          257  +    struct {                   /* GET/COOKIE params in JSON mode. */
          258  +      cson_value * v;
          259  +      cson_object * o;
          260  +    } param;
          261  +    struct {
          262  +      cson_value * v;
          263  +      cson_object * o;
          264  +    } reqPayload;              /* request payload object (if any) */
          265  +    cson_array * warnings;     /* response warnings */
          266  +  } json;
          267  +#endif /* FOSSIL_ENABLE_JSON */
   151    268   };
   152    269   
   153    270   /*
   154    271   ** Macro for debugging:
   155    272   */
   156    273   #define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X
   157    274   
   158    275   #endif
   159    276   
   160    277   Global g;
   161    278   
   162    279   /*
   163         -** The table of web pages supported by this application is generated 
          280  +** The table of web pages supported by this application is generated
   164    281   ** automatically by the "mkindex" program and written into a file
   165    282   ** named "page_index.h".  We include that file here to get access
   166    283   ** to the table.
   167    284   */
   168    285   #include "page_index.h"
   169    286   
   170    287   /*
................................................................................
   185    302     int upr, lwr, cnt, m, i;
   186    303     int n = strlen(zName);
   187    304     lwr = 0;
   188    305     upr = nMap-1;
   189    306     while( lwr<=upr ){
   190    307       int mid, c;
   191    308       mid = (upr+lwr)/2;
   192         -    c = strcmp(zName, aMap[mid].zName);
          309  +    c = fossil_strcmp(zName, aMap[mid].zName);
   193    310       if( c==0 ){
   194    311         *pIndex = mid;
   195    312         return 0;
   196    313       }else if( c<0 ){
   197    314         upr = mid - 1;
   198    315       }else{
   199    316         lwr = mid + 1;
   200    317       }
   201    318     }
   202         -  for(m=cnt=0, i=upr-2; i<=upr+3 && i<nMap; i++){
          319  +  for(m=cnt=0, i=upr-2; cnt<2 && i<=upr+3 && i<nMap; i++){
   203    320       if( i<0 ) continue;
   204    321       if( strncmp(zName, aMap[i].zName, n)==0 ){
   205    322         m = i;
   206    323         cnt++;
   207    324       }
   208    325     }
   209    326     if( cnt==1 ){
   210    327       *pIndex = m;
   211    328       return 0;
   212    329     }
   213    330     return 1+(cnt>1);
   214    331   }
   215    332   
          333  +/*
          334  +** atexit() handler which frees up "some" of the resources
          335  +** used by fossil.
          336  +*/
          337  +void fossil_atexit(void) {
          338  +#ifdef FOSSIL_ENABLE_JSON
          339  +  cson_value_free(g.json.gc.v);
          340  +  memset(&g.json, 0, sizeof(g.json));
          341  +#endif
          342  +  free(g.zErrMsg);
          343  +  if(g.db){
          344  +    db_close(0);
          345  +  }
          346  +}
          347  +
          348  +/*
          349  +** Convert all arguments from mbcs (or unicode) to UTF-8. Then
          350  +** search g.argv for arguments "--args FILENAME". If found, then
          351  +** (1) remove the two arguments from g.argv
          352  +** (2) Read the file FILENAME
          353  +** (3) Use the contents of FILE to replace the two removed arguments:
          354  +**     (a) Ignore blank lines in the file
          355  +**     (b) Each non-empty line of the file is an argument, except
          356  +**     (c) If the line begins with "-" and contains a space, it is broken
          357  +**         into two arguments at the space.
          358  +*/
          359  +static void expand_args_option(int argc, void *argv){
          360  +  Blob file = empty_blob;   /* Content of the file */
          361  +  Blob line = empty_blob;   /* One line of the file */
          362  +  unsigned int nLine;       /* Number of lines in the file*/
          363  +  unsigned int i, j, k;     /* Loop counters */
          364  +  int n;                    /* Number of bytes in one line */
          365  +  char *z;                  /* General use string pointer */
          366  +  char **newArgv;           /* New expanded g.argv under construction */
          367  +  char const * zFileName;   /* input file name */
          368  +  FILE * zInFile;           /* input FILE */
          369  +#if defined(_WIN32)
          370  +  WCHAR buf[MAX_PATH];
          371  +#endif
          372  +
          373  +  g.argc = argc;
          374  +  g.argv = argv;
          375  +  sqlite3_initialize();
          376  +#if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE)
          377  +  for(i=0; i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]);
          378  +#else
          379  +  for(i=0; i<g.argc; i++) g.argv[i] = fossil_filename_to_utf8(g.argv[i]);
          380  +#endif
          381  +#if defined(_WIN32)
          382  +  GetModuleFileNameW(NULL, buf, MAX_PATH);
          383  +  g.nameOfExe = fossil_filename_to_utf8(buf);
          384  +#else
          385  +  g.nameOfExe = g.argv[0];
          386  +#endif
          387  +  for(i=1; i<g.argc-1; i++){
          388  +    z = g.argv[i];
          389  +    if( z[0]!='-' ) continue;
          390  +    z++;
          391  +    if( z[0]=='-' ) z++;
          392  +    if( z[0]==0 ) return;   /* Stop searching at "--" */
          393  +    if( fossil_strcmp(z, "args")==0 ) break;
          394  +  }
          395  +  if( i>=g.argc-1 ) return;
          396  +
          397  +  zFileName = g.argv[i+1];
          398  +  zInFile = (0==strcmp("-",zFileName))
          399  +    ? stdin
          400  +    : fossil_fopen(zFileName,"rb");
          401  +  if(!zInFile){
          402  +    fossil_panic("Cannot open -args file [%s]", zFileName);
          403  +  }else{
          404  +    blob_read_from_channel(&file, zInFile, -1);
          405  +    if(stdin != zInFile){
          406  +      fclose(zInFile);
          407  +    }
          408  +    zInFile = NULL;
          409  +  }
          410  +  blob_to_utf8_no_bom(&file, 1);
          411  +  z = blob_str(&file);
          412  +  for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
          413  +  newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) );
          414  +  for(j=0; j<i; j++) newArgv[j] = g.argv[j];
          415  +
          416  +  blob_rewind(&file);
          417  +  while( (n = blob_line(&file, &line))>0 ){
          418  +    if( n<=1 ) continue;
          419  +    z = blob_buffer(&line);
          420  +    z[n-1] = 0;
          421  +    if((n>1) && ('\r'==z[n-2])){
          422  +      if(n==2) continue /*empty line*/;
          423  +      z[n-2] = 0;
          424  +    }
          425  +    newArgv[j++] = z;
          426  +    if( z[0]=='-' ){
          427  +      for(k=1; z[k] && !fossil_isspace(z[k]); k++){}
          428  +      if( z[k] ){
          429  +        z[k] = 0;
          430  +        k++;
          431  +        if( z[k] ) newArgv[j++] = &z[k];
          432  +      }
          433  +    }
          434  +  }
          435  +  i += 2;
          436  +  while( i<g.argc ) newArgv[j++] = g.argv[i++];
          437  +  newArgv[j] = 0;
          438  +  g.argc = j;
          439  +  g.argv = newArgv;
          440  +}
          441  +
          442  +#ifdef FOSSIL_ENABLE_TCL
          443  +/*
          444  +** Make a deep copy of the provided argument array and return it.
          445  +*/
          446  +static char **copy_args(int argc, char **argv){
          447  +  char **zNewArgv;
          448  +  int i;
          449  +  zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) );
          450  +  memset(zNewArgv, 0, sizeof(char*)*(argc+1));
          451  +  for(i=0; i<argc; i++){
          452  +    zNewArgv[i] = fossil_strdup(argv[i]);
          453  +  }
          454  +  return zNewArgv;
          455  +}
          456  +#endif
          457  +
   216    458   
   217    459   /*
   218    460   ** This procedure runs first.
   219    461   */
   220         -int main(int argc, char **argv){
   221         -  const char *zCmdName;
          462  +#if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE)
          463  +int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */
          464  +int wmain(int argc, wchar_t **argv)
          465  +#else
          466  +int main(int argc, char **argv)
          467  +#endif
          468  +{
          469  +  const char *zCmdName = "unknown";
   222    470     int idx;
   223    471     int rc;
   224    472   
   225    473     sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
          474  +  memset(&g, 0, sizeof(g));
   226    475     g.now = time(0);
   227         -  g.argc = argc;
   228         -  g.argv = argv;
   229         -  if( getenv("GATEWAY_INTERFACE")!=0 ){
          476  +#ifdef FOSSIL_ENABLE_JSON
          477  +#if defined(NDEBUG)
          478  +  g.json.errorDetailParanoia = 2 /* FIXME: make configurable
          479  +                                    One problem we have here is that this
          480  +                                    code is needed before the db is opened,
          481  +                                    so we can't sql for it.*/;
          482  +#else
          483  +  g.json.errorDetailParanoia = 0;
          484  +#endif
          485  +  g.json.outOpt = cson_output_opt_empty;
          486  +  g.json.outOpt.addNewline = 1;
          487  +  g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
          488  +#endif /* FOSSIL_ENABLE_JSON */
          489  +  expand_args_option(argc, argv);
          490  +#ifdef FOSSIL_ENABLE_TCL
          491  +  memset(&g.tcl, 0, sizeof(TclContext));
          492  +  g.tcl.argc = g.argc;
          493  +  g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
          494  +#endif
          495  +  if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
   230    496       zCmdName = "cgi";
   231         -  }else if( argc<2 ){
   232         -    fprintf(stderr, "Usage: %s COMMAND ...\n", argv[0]);
   233         -    exit(1);
          497  +    g.isHTTP = 1;
          498  +  }else if( g.argc<2 ){
          499  +    fossil_print(
          500  +       "Usage: %s COMMAND ...\n"
          501  +       "   or: %s help           -- for a list of common commands\n"
          502  +       "   or: %s help COMMMAND  -- for help with the named command\n",
          503  +       g.argv[0], g.argv[0], g.argv[0]);
          504  +    fossil_exit(1);
   234    505     }else{
          506  +    const char *zChdir = find_option("chdir",0,1);
          507  +    g.isHTTP = 0;
   235    508       g.fQuiet = find_option("quiet", 0, 0)!=0;
   236    509       g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
          510  +    g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
          511  +    g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
          512  +    g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
          513  +    if( g.fSqlTrace ) g.fSqlStats = 1;
   237    514       g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
   238    515       g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
   239    516       g.zLogin = find_option("user", "U", 1);
   240         -    zCmdName = argv[1];
          517  +    g.zSSLIdentity = find_option("ssl-identity", 0, 1);
          518  +    if( find_option("utc",0,0) ) g.fTimeFormat = 1;
          519  +    if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
          520  +    if( zChdir && chdir(zChdir) ){
          521  +      fossil_fatal("unable to change directories to %s", zChdir);
          522  +    }
          523  +    if( find_option("help",0,0)!=0 ){
          524  +      /* --help anywhere on the command line is translated into
          525  +      ** "fossil help argv[1] argv[2]..." */
          526  +      int i;
          527  +      char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) );
          528  +      for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i];
          529  +      zNewArgv[i+1] = 0;
          530  +      zNewArgv[0] = g.argv[0];
          531  +      zNewArgv[1] = "help";
          532  +      g.argc++;
          533  +      g.argv = zNewArgv;
          534  +    }
          535  +    zCmdName = g.argv[1];
   241    536     }
   242    537     rc = name_search(zCmdName, aCommand, count(aCommand), &idx);
   243    538     if( rc==1 ){
   244         -    fprintf(stderr,"%s: unknown command: %s\n"
   245         -                   "%s: use \"help\" for more information\n",
   246         -                   argv[0], zCmdName, argv[0]);
   247         -    return 1;
          539  +    fossil_fatal("%s: unknown command: %s\n"
          540  +                 "%s: use \"help\" for more information\n",
          541  +                   g.argv[0], zCmdName, g.argv[0]);
   248    542     }else if( rc==2 ){
   249         -    fprintf(stderr,"%s: ambiguous command prefix: %s\n"
   250         -                   "%s: use \"help\" for more information\n",
   251         -                   argv[0], zCmdName, argv[0]);
   252         -    return 1;
          543  +    int i, n;
          544  +    Blob couldbe;
          545  +    blob_zero(&couldbe);
          546  +    n = strlen(zCmdName);
          547  +    for(i=0; i<count(aCommand); i++){
          548  +      if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
          549  +        blob_appendf(&couldbe, " %s", aCommand[i].zName);
          550  +      }
          551  +    }
          552  +    fossil_print("%s: ambiguous command prefix: %s\n"
          553  +                 "%s: could be any of:%s\n"
          554  +                 "%s: use \"help\" for more information\n",
          555  +                 g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]);
          556  +    fossil_exit(1);
   253    557     }
          558  +  atexit( fossil_atexit );
   254    559     aCommand[idx].xFunc();
          560  +  fossil_exit(0);
          561  +  /*NOT_REACHED*/
   255    562     return 0;
   256    563   }
   257    564   
   258    565   /*
   259    566   ** The following variable becomes true while processing a fatal error
   260    567   ** or a panic.  If additional "recursive-fatal" errors occur while
   261    568   ** shutting down, the recursive errors are silently ignored.
   262    569   */
   263    570   static int mainInFatalError = 0;
          571  +
          572  +/*
          573  +** Exit.  Take care to close the database first.
          574  +*/
          575  +NORETURN void fossil_exit(int rc){
          576  +  db_close(1);
          577  +  exit(rc);
          578  +}
   264    579   
   265    580   /*
   266    581   ** Print an error message, rollback all databases, and quit.  These
   267    582   ** routines never return.
   268    583   */
   269         -void fossil_panic(const char *zFormat, ...){
          584  +NORETURN void fossil_panic(const char *zFormat, ...){
   270    585     char *z;
   271    586     va_list ap;
          587  +  int rc = 1;
   272    588     static int once = 1;
   273    589     mainInFatalError = 1;
   274    590     va_start(ap, zFormat);
   275    591     z = vmprintf(zFormat, ap);
   276    592     va_end(ap);
   277         -  if( g.cgiOutput && once ){
   278         -    once = 0;
   279         -    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
   280         -    cgi_reply();
   281         -  }else{
   282         -    fprintf(stderr, "%s: %s\n", g.argv[0], z);
          593  +#ifdef FOSSIL_ENABLE_JSON
          594  +  if( g.json.isJsonMode ){
          595  +    json_err( 0, z, 1 );
          596  +    if( g.isHTTP ){
          597  +      rc = 0 /* avoid HTTP 500 */;
          598  +    }
   283    599     }
          600  +  else
          601  +#endif
          602  +  {
          603  +    if( g.cgiOutput && once ){
          604  +      once = 0;
          605  +      cgi_printf("<p class=\"generalError\">%h</p>", z);
          606  +      cgi_reply();
          607  +    }else if( !g.fQuiet ){
          608  +      fossil_trace("%s: %s\n", g.argv[0], z);
          609  +    }
          610  +  }
          611  +  free(z);
   284    612     db_force_rollback();
   285         -  exit(1);
          613  +  fossil_exit(rc);
   286    614   }
   287         -void fossil_fatal(const char *zFormat, ...){
          615  +
          616  +NORETURN void fossil_fatal(const char *zFormat, ...){
   288    617     char *z;
          618  +  int rc = 1;
   289    619     va_list ap;
   290    620     mainInFatalError = 1;
   291    621     va_start(ap, zFormat);
   292    622     z = vmprintf(zFormat, ap);
   293    623     va_end(ap);
   294         -  if( g.cgiOutput ){
   295         -    g.cgiOutput = 0;
   296         -    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
   297         -    cgi_reply();
   298         -  }else{
   299         -    fprintf(stderr, "%s: %s\n", g.argv[0], z);
          624  +#ifdef FOSSIL_ENABLE_JSON
          625  +  if( g.json.isJsonMode ){
          626  +    json_err( g.json.resultCode, z, 1 );
          627  +    if( g.isHTTP ){
          628  +      rc = 0 /* avoid HTTP 500 */;
          629  +    }
   300    630     }
          631  +  else
          632  +#endif
          633  +  {
          634  +    if( g.cgiOutput ){
          635  +      g.cgiOutput = 0;
          636  +      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
          637  +      cgi_reply();
          638  +    }else if( !g.fQuiet ){
          639  +      fossil_trace("%s: %s\n", g.argv[0], z);
          640  +    }
          641  +  }
          642  +  free(z);
   301    643     db_force_rollback();
   302         -  exit(1);
          644  +  fossil_exit(rc);
   303    645   }
   304    646   
   305    647   /* This routine works like fossil_fatal() except that if called
   306    648   ** recursively, the recursive call is a no-op.
   307    649   **
   308    650   ** Use this in places where an error might occur while doing
   309    651   ** fatal error shutdown processing.  Unlike fossil_panic() and
................................................................................
   310    652   ** fossil_fatal() which never return, this routine might return if
   311    653   ** the fatal error handing is already in process.  The caller must
   312    654   ** be prepared for this routine to return.
   313    655   */
   314    656   void fossil_fatal_recursive(const char *zFormat, ...){
   315    657     char *z;
   316    658     va_list ap;
          659  +  int rc = 1;
   317    660     if( mainInFatalError ) return;
   318    661     mainInFatalError = 1;
   319    662     va_start(ap, zFormat);
   320    663     z = vmprintf(zFormat, ap);
   321    664     va_end(ap);
   322         -  if( g.cgiOutput ){
   323         -    g.cgiOutput = 0;
   324         -    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
   325         -    cgi_reply();
   326         -  }else{
   327         -    fprintf(stderr, "%s: %s\n", g.argv[0], z);
          665  +#ifdef FOSSIL_ENABLE_JSON
          666  +  if( g.json.isJsonMode ){
          667  +    json_err( g.json.resultCode, z, 1 );
          668  +    if( g.isHTTP ){
          669  +      rc = 0 /* avoid HTTP 500 */;
          670  +    }
          671  +  } else
          672  +#endif
          673  +  {
          674  +    if( g.cgiOutput ){
          675  +      g.cgiOutput = 0;
          676  +      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
          677  +      cgi_reply();
          678  +    }else{
          679  +      fossil_trace("%s: %s\n", g.argv[0], z);
          680  +    }
   328    681     }
   329    682     db_force_rollback();
   330         -  exit(1);
          683  +  fossil_exit(rc);
   331    684   }
   332    685   
   333    686   
   334    687   /* Print a warning message */
   335    688   void fossil_warning(const char *zFormat, ...){
   336    689     char *z;
   337    690     va_list ap;
   338    691     va_start(ap, zFormat);
   339    692     z = vmprintf(zFormat, ap);
   340    693     va_end(ap);
   341         -  if( g.cgiOutput ){
   342         -    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
   343         -  }else{
   344         -    fprintf(stderr, "%s: %s\n", g.argv[0], z);
   345         -  }
   346         -}
          694  +#ifdef FOSSIL_ENABLE_JSON
          695  +  if(g.json.isJsonMode){
          696  +    json_warn( FSL_JSON_W_UNKNOWN, z );
          697  +  }else
          698  +#endif
          699  +  {
          700  +    if( g.cgiOutput ){
          701  +      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
          702  +    }else{
          703  +      fossil_trace("%s: %s\n", g.argv[0], z);
          704  +    }
          705  +  }
          706  +  free(z);
          707  +}
          708  +
          709  +/*
          710  +** Malloc and free routines that cannot fail
          711  +*/
          712  +void *fossil_malloc(size_t n){
          713  +  void *p = malloc(n==0 ? 1 : n);
          714  +  if( p==0 ) fossil_panic("out of memory");
          715  +  return p;
          716  +}
          717  +void fossil_free(void *p){
          718  +  free(p);
          719  +}
          720  +void *fossil_realloc(void *p, size_t n){
          721  +  p = realloc(p, n);
          722  +  if( p==0 ) fossil_panic("out of memory");
          723  +  return p;
          724  +}
          725  +
          726  +/*
          727  +** This function implements a cross-platform "system()" interface.
          728  +*/
          729  +int fossil_system(const char *zOrigCmd){
          730  +  int rc;
          731  +#if defined(_WIN32)
          732  +  /* On windows, we have to put double-quotes around the entire command.
          733  +  ** Who knows why - this is just the way windows works.
          734  +  */
          735  +  char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
          736  +  WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd);
          737  +  if( g.fSystemTrace ) {
          738  +    fossil_trace("SYSTEM: %s\n", zNewCmd);
          739  +  }
          740  +  rc = _wsystem(zUnicode);
          741  +  fossil_unicode_free(zUnicode);
          742  +  free(zNewCmd);
          743  +#else
          744  +  /* On unix, evaluate the command directly.
          745  +  */
          746  +  if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd);
          747  +  rc = system(zOrigCmd);
          748  +#endif
          749  +  return rc;
          750  +}
          751  +
          752  +/*
          753  +** Turn off any NL to CRNL translation on the stream given as an
          754  +** argument.  This is a no-op on unix but is necessary on windows.
          755  +*/
          756  +void fossil_binary_mode(FILE *p){
          757  +#if defined(_WIN32)
          758  +  _setmode(_fileno(p), _O_BINARY);
          759  +#endif
          760  +#ifdef __EMX__     /* OS/2 */
          761  +  setmode(fileno(p), O_BINARY);
          762  +#endif
          763  +}
          764  +
          765  +
   347    766   
   348    767   /*
   349    768   ** Return a name for an SQLite error code
   350    769   */
   351    770   static const char *sqlite_error_code_name(int iCode){
   352    771     static char zCode[30];
   353    772     switch( iCode & 0xff ){
................................................................................
   385    804     fossil_warning("%s: %s", sqlite_error_code_name(iCode), zErrmsg);
   386    805   }
   387    806   
   388    807   /*
   389    808   ** Print a usage comment and quit
   390    809   */
   391    810   void usage(const char *zFormat){
   392         -  fprintf(stderr, "Usage: %s %s %s\n", g.argv[0], g.argv[1], zFormat);
   393         -  exit(1);
          811  +  fossil_fatal("Usage: %s %s %s\n", g.argv[0], g.argv[1], zFormat);
   394    812   }
   395    813   
   396    814   /*
   397    815   ** Remove n elements from g.argv beginning with the i-th element.
   398    816   */
   399    817   void remove_from_argv(int i, int n){
   400    818     int j;
................................................................................
   415    833   */
   416    834   const char *find_option(const char *zLong, const char *zShort, int hasArg){
   417    835     int i;
   418    836     int nLong;
   419    837     const char *zReturn = 0;
   420    838     assert( hasArg==0 || hasArg==1 );
   421    839     nLong = strlen(zLong);
   422         -  for(i=2; i<g.argc; i++){
          840  +  for(i=1; i<g.argc; i++){
   423    841       char *z;
   424    842       if (i+hasArg >= g.argc) break;
   425    843       z = g.argv[i];
   426    844       if( z[0]!='-' ) continue;
   427    845       z++;
   428    846       if( z[0]=='-' ){
   429    847         if( z[1]==0 ){
................................................................................
   438    856           remove_from_argv(i, 1);
   439    857           break;
   440    858         }else if( z[nLong]==0 ){
   441    859           zReturn = g.argv[i+hasArg];
   442    860           remove_from_argv(i, 1+hasArg);
   443    861           break;
   444    862         }
   445         -    }else if( zShort!=0 && strcmp(z,zShort)==0 ){
          863  +    }else if( fossil_strcmp(z,zShort)==0 ){
   446    864         zReturn = g.argv[i+hasArg];
   447    865         remove_from_argv(i, 1+hasArg);
   448    866         break;
   449    867       }
   450    868     }
   451    869     return zReturn;
   452    870   }
................................................................................
   456    874   ** Any remaining command-line argument begins with "-" print
   457    875   ** an error message and quit.
   458    876   */
   459    877   void verify_all_options(void){
   460    878     int i;
   461    879     for(i=1; i<g.argc; i++){
   462    880       if( g.argv[i][0]=='-' ){
   463         -      fossil_fatal("unrecognized command-line option, or missing argument: %s", g.argv[i]);
          881  +      fossil_fatal(
          882  +        "unrecognized command-line option, or missing argument: %s",
          883  +        g.argv[i]);
   464    884       }
   465    885     }
   466    886   }
   467    887   
   468    888   /*
   469    889   ** Print a list of words in multiple columns.
   470    890   */
................................................................................
   479    899     }
   480    900     nCol = 80/(mxLen+2);
   481    901     if( nCol==0 ) nCol = 1;
   482    902     nRow = (nWord + nCol - 1)/nCol;
   483    903     for(i=0; i<nRow; i++){
   484    904       const char *zSpacer = "";
   485    905       for(j=i; j<nWord; j+=nRow){
   486         -      printf("%s%-*s", zSpacer, mxLen, azWord[j]);
          906  +      fossil_print("%s%-*s", zSpacer, mxLen, azWord[j]);
   487    907         zSpacer = "  ";
   488    908       }
   489         -    printf("\n");
          909  +    fossil_print("\n");
   490    910     }
   491    911   }
   492    912   
   493    913   /*
   494         -** COM -off- MAND: commands
   495         -**
   496         -** Usage: %fossil commands
   497         -** List all supported commands.
          914  +** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
   498    915   */
   499         -void cmd_cmd_list(void){
          916  +static void command_list(const char *zPrefix, int cmdMask){
   500    917     int i, nCmd;
          918  +  int nPrefix = zPrefix ? strlen(zPrefix) : 0;
   501    919     const char *aCmd[count(aCommand)];
   502    920     for(i=nCmd=0; i<count(aCommand); i++){
   503         -    if( strncmp(aCommand[i].zName,"test",4)==0 ) continue;
   504         -    /* if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; */
          921  +    const char *z = aCommand[i].zName;
          922  +    if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
          923  +    if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
   505    924       aCmd[nCmd++] = aCommand[i].zName;
   506    925     }
   507    926     multi_column_list(aCmd, nCmd);
   508    927   }
   509    928   
   510    929   /*
   511         -** COMMAND: test-commands
          930  +** COMMAND: test-list-webpage
   512    931   **
   513         -** Usage: %fossil test-commands
   514         -**
   515         -** List all commands used for testing and debugging.
          932  +** List all web pages
   516    933   */
   517         -void cmd_test_cmd_list(void){
          934  +void cmd_test_webpage_list(void){
   518    935     int i, nCmd;
   519         -  const char *aCmd[count(aCommand)];
   520         -  for(i=nCmd=0; i<count(aCommand); i++){
   521         -    if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
   522         -    /* if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; */
   523         -    aCmd[nCmd++] = aCommand[i].zName;
          936  +  const char *aCmd[count(aWebpage)];
          937  +  for(i=nCmd=0; i<count(aWebpage); i++){
          938  +    aCmd[nCmd++] = aWebpage[i].zName;
   524    939     }
   525    940     multi_column_list(aCmd, nCmd);
   526    941   }
   527         -
   528    942   
   529    943   /*
   530    944   ** COMMAND: version
   531    945   **
   532    946   ** Usage: %fossil version
   533    947   **
   534    948   ** Print the source code version number for the fossil executable.
   535    949   */
   536    950   void version_cmd(void){
   537         -  printf("This is fossil version " MANIFEST_VERSION " " MANIFEST_DATE " UTC\n");
          951  +  fossil_print("This is fossil version " RELEASE_VERSION " "
          952  +                MANIFEST_VERSION " " MANIFEST_DATE " UTC\n");
   538    953   }
   539    954   
   540    955   
   541    956   /*
   542    957   ** COMMAND: help
   543    958   **
   544    959   ** Usage: %fossil help COMMAND
          960  +**    or: %fossil COMMAND -help
   545    961   **
   546         -** Display information on how to use COMMAND
          962  +** Display information on how to use COMMAND.  To display a list of
          963  +** available commands one of:
          964  +**
          965  +**    %fossil help              Show common commands
          966  +**    %fossil help --all        Show both common and auxiliary commands
          967  +**    %fossil help --test       Show test commands only
          968  +**    %fossil help --aux        Show auxiliary commands only
   547    969   */
   548    970   void help_cmd(void){
   549    971     int rc, idx;
   550    972     const char *z;
   551         -  if( g.argc!=3 ){
   552         -    printf("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", g.argv[0]);
   553         -    cmd_cmd_list();
          973  +  if( g.argc<3 ){
          974  +    z = g.argv[0];
          975  +    fossil_print(
          976  +      "Usage: %s help COMMAND\n"
          977  +      "Common COMMANDs:  (use \"%s help --all\" for a complete list)\n",
          978  +      z, z);
          979  +    command_list(0, CMDFLAG_1ST_TIER);
   554    980       version_cmd();
   555    981       return;
          982  +  }
          983  +  if( find_option("all",0,0) ){
          984  +    command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
          985  +    return;
          986  +  }
          987  +  if( find_option("aux",0,0) ){
          988  +    command_list(0, CMDFLAG_2ND_TIER);
          989  +    return;
          990  +  }
          991  +  if( find_option("test",0,0) ){
          992  +    command_list(0, CMDFLAG_TEST);
          993  +    return;
   556    994     }
   557    995     rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
   558    996     if( rc==1 ){
   559         -    fossil_fatal("unknown command: %s", g.argv[2]);
          997  +    fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
          998  +    command_list(0, 0xff);
          999  +    fossil_exit(1);
   560   1000     }else if( rc==2 ){
   561         -    fossil_fatal("ambiguous command prefix: %s", g.argv[2]);
         1001  +    fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
         1002  +                 g.argv[2]);
         1003  +    command_list(g.argv[2], 0xff);
         1004  +    fossil_exit(1);
   562   1005     }
   563   1006     z = aCmdHelp[idx];
   564   1007     if( z==0 ){
   565   1008       fossil_fatal("no help available for the %s command",
   566   1009          aCommand[idx].zName);
   567   1010     }
   568   1011     while( *z ){
   569   1012       if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){
   570         -      printf("%s", g.argv[0]);
         1013  +      fossil_print("%s", g.argv[0]);
   571   1014         z += 7;
   572   1015       }else{
   573   1016         putchar(*z);
   574   1017         z++;
   575   1018       }
   576   1019     }
   577   1020     putchar('\n');
   578   1021   }
         1022  +
         1023  +/*
         1024  +** WEBPAGE: help
         1025  +** URL: /help/CMD
         1026  +*/
         1027  +void help_page(void){
         1028  +  const char * zCmd = P("cmd");
         1029  +
         1030  +  if( zCmd==0 ) zCmd = P("name");
         1031  +  style_header("Command-line Help");
         1032  +  if( zCmd ){
         1033  +    int rc, idx;
         1034  +    char *z, *s, *d;
         1035  +
         1036  +    style_submenu_element("Command-List", "Command-List", "%s/help", g.zTop);
         1037  +    @ <h1>The "%s(zCmd)" command:</h1>
         1038  +    rc = name_search(zCmd, aCommand, count(aCommand), &idx);
         1039  +    if( rc==1 ){
         1040  +      @ unknown command: %s(zCmd)
         1041  +    }else if( rc==2 ){
         1042  +      @ ambiguous command prefix: %s(zCmd)
         1043  +    }else{
         1044  +      z = (char*)aCmdHelp[idx];
         1045  +      if( z==0 ){
         1046  +        @ no help available for the %s(aCommand[idx].zName) command
         1047  +      }else{
         1048  +        z=s=d=mprintf("%s",z);
         1049  +        while( *s ){
         1050  +          if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){
         1051  +            s++;
         1052  +          }else{
         1053  +            *d++ = *s++;
         1054  +          }
         1055  +        }
         1056  +        *d = 0;
         1057  +        @ <blockquote><pre>
         1058  +        @ %h(z)
         1059  +        @ </pre></blockquote>
         1060  +        fossil_free(z);
         1061  +      }
         1062  +    }
         1063  +  }else{
         1064  +    int i, j, n;
         1065  +
         1066  +    @ <h1>Available commands:</h1>
         1067  +    @ <table border="0"><tr>
         1068  +    for(i=j=0; i<count(aCommand); i++){
         1069  +      const char *z = aCommand[i].zName;
         1070  +      if( strncmp(z,"test",4)==0 ) continue;
         1071  +      j++;
         1072  +    }
         1073  +    n = (j+6)/7;
         1074  +    for(i=j=0; i<count(aCommand); i++){
         1075  +      const char *z = aCommand[i].zName;
         1076  +      if( strncmp(z,"test",4)==0 ) continue;
         1077  +      if( j==0 ){
         1078  +        @ <td valign="top"><ul>
         1079  +      }
         1080  +      @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z)</a>
         1081  +      j++;
         1082  +      if( j>=n ){
         1083  +        @ </ul></td>
         1084  +        j = 0;
         1085  +      }
         1086  +    }
         1087  +    if( j>0 ){
         1088  +      @ </ul></td>
         1089  +    }
         1090  +    @ </tr></table>
         1091  +  }
         1092  +  style_footer();
         1093  +}
         1094  +
         1095  +/*
         1096  +** WEBPAGE: test-all-help
         1097  +**
         1098  +** Show all help text on a single page.  Useful for proof-reading.
         1099  +*/
         1100  +void test_all_help_page(void){
         1101  +  int i;
         1102  +  style_header("Testpage: All Help Text");
         1103  +  for(i=0; i<count(aCommand); i++){
         1104  +    if( memcmp(aCommand[i].zName, "test", 4)==0 ) continue;
         1105  +    @ <h2>%s(aCommand[i].zName):</h2>
         1106  +    @ <blockquote><pre>
         1107  +    @ %h(aCmdHelp[i])
         1108  +    @ </pre></blockquote>
         1109  +  }
         1110  +  style_footer();
         1111  +}
   579   1112   
   580   1113   /*
   581   1114   ** Set the g.zBaseURL value to the full URL for the toplevel of
   582   1115   ** the fossil tree.  Set g.zTop to g.zBaseURL without the
   583   1116   ** leading "http://" and the host and port.
         1117  +**
         1118  +** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
         1119  +** environment variables.  However, if zAltBase is not NULL then it
         1120  +** is the argument to the --baseurl option command-line option and
         1121  +** g.zBaseURL and g.zTop is set from that instead.
   584   1122   */
   585         -void set_base_url(void){
         1123  +static void set_base_url(const char *zAltBase){
   586   1124     int i;
   587         -  const char *zHost = PD("HTTP_HOST","");
   588         -  const char *zMode = PD("HTTPS","off");
   589         -  const char *zCur = PD("SCRIPT_NAME","/");
         1125  +  const char *zHost;
         1126  +  const char *zMode;
         1127  +  const char *zCur;
   590   1128   
   591         -  i = strlen(zCur);
   592         -  while( i>0 && zCur[i-1]=='/' ) i--;
   593         -  if( strcmp(zMode,"on")==0 ){
   594         -    g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
   595         -    g.zTop = &g.zBaseURL[8+strlen(zHost)];
         1129  +  if( g.zBaseURL!=0 ) return;
         1130  +  if( zAltBase ){
         1131  +    int i, n, c;
         1132  +    g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
         1133  +    if( memcmp(g.zTop, "http://", 7)!=0 && memcmp(g.zTop,"https://",8)!=0 ){
         1134  +      fossil_fatal("argument to --baseurl should be 'http://host/path'"
         1135  +                   " or 'https://host/path'");
         1136  +    }
         1137  +    for(i=n=0; (c = g.zTop[i])!=0; i++){
         1138  +      if( c=='/' ){
         1139  +        n++;
         1140  +        if( n==3 ){
         1141  +          g.zTop += i;
         1142  +          break;
         1143  +        }
         1144  +      }
         1145  +    }
         1146  +    if( g.zTop==g.zBaseURL ){
         1147  +      fossil_fatal("argument to --baseurl should be 'http://host/path'"
         1148  +                   " or 'https://host/path'");
         1149  +    }
         1150  +    if( g.zTop[1]==0 ) g.zTop++;
   596   1151     }else{
   597         -    g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
   598         -    g.zTop = &g.zBaseURL[7+strlen(zHost)];
         1152  +    zHost = PD("HTTP_HOST","");
         1153  +    zMode = PD("HTTPS","off");
         1154  +    zCur = PD("SCRIPT_NAME","/");
         1155  +    i = strlen(zCur);
         1156  +    while( i>0 && zCur[i-1]=='/' ) i--;
         1157  +    if( fossil_stricmp(zMode,"on")==0 ){
         1158  +      g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
         1159  +      g.zTop = &g.zBaseURL[8+strlen(zHost)];
         1160  +    }else{
         1161  +      g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
         1162  +      g.zTop = &g.zBaseURL[7+strlen(zHost)];
         1163  +    }
         1164  +  }
         1165  +  if( db_is_writeable("repository") ){
         1166  +    if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
         1167  +      db_multi_exec("INSERT INTO config(name,value,mtime)"
         1168  +                    "VALUES('baseurl:%q',1,now())", g.zBaseURL);
         1169  +    }else{
         1170  +      db_optional_sql("repository",
         1171  +           "REPLACE INTO config(name,value,mtime)"
         1172  +           "VALUES('baseurl:%q',1,now())", g.zBaseURL
         1173  +      );
         1174  +    }
   599   1175     }
   600   1176   }
   601   1177   
   602   1178   /*
   603   1179   ** Send an HTTP redirect back to the designated Index Page.
   604   1180   */
   605         -void fossil_redirect_home(void){
   606         -  cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index"));
         1181  +NORETURN void fossil_redirect_home(void){
         1182  +  cgi_redirectf("%s%s", g.zTop, db_get("index-page", "/index"));
   607   1183   }
   608   1184   
   609   1185   /*
   610   1186   ** If running as root, chroot to the directory containing the
   611   1187   ** repository zRepo and then drop root privileges.  Return the
   612   1188   ** new repository name.
   613   1189   **
................................................................................
   614   1190   ** zRepo might be a directory itself.  In that case chroot into
   615   1191   ** the directory zRepo.
   616   1192   **
   617   1193   ** Assume the user-id and group-id of the repository, or if zRepo
   618   1194   ** is a directory, of that directory.
   619   1195   */
   620   1196   static char *enter_chroot_jail(char *zRepo){
   621         -#if !defined(__MINGW32__)
         1197  +#if !defined(_WIN32)
   622   1198     if( getuid()==0 ){
   623   1199       int i;
   624   1200       struct stat sStat;
   625   1201       Blob dir;
   626   1202       char *zDir;
   627   1203   
   628         -    file_canonical_name(zRepo, &dir);
         1204  +    file_canonical_name(zRepo, &dir, 0);
   629   1205       zDir = blob_str(&dir);
   630   1206       if( file_isdir(zDir)==1 ){
   631         -      chdir(zDir);
   632         -      chroot(zDir);
         1207  +      if( chdir(zDir) || chroot(zDir) || chdir("/") ){
         1208  +        fossil_fatal("unable to chroot into %s", zDir);
         1209  +      }
   633   1210         zRepo = "/";
   634   1211       }else{
   635   1212         for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
   636   1213         if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
   637         -      zDir[i] = 0;
   638         -      chdir(zDir);
   639         -      chroot(zDir);
   640         -      zDir[i] = '/';
         1214  +      if( i>0 ){
         1215  +        zDir[i] = 0;
         1216  +        if( chdir(zDir) || chroot(zDir) || chdir("/") ){
         1217  +          fossil_fatal("unable to chroot into %s", zDir);
         1218  +        }
         1219  +        zDir[i] = '/';
         1220  +      }
   641   1221         zRepo = &zDir[i];
   642   1222       }
   643   1223       if( stat(zRepo, &sStat)!=0 ){
   644   1224         fossil_fatal("cannot stat() repository: %s", zRepo);
   645   1225       }
   646         -    setgid(sStat.st_gid);
   647         -    setuid(sStat.st_uid);
         1226  +    i = setgid(sStat.st_gid);
         1227  +    i = i || setuid(sStat.st_uid);
         1228  +    if(i){
         1229  +      fossil_fatal("setgid/uid() failed with errno %d", errno);
         1230  +    }
   648   1231       if( g.db!=0 ){
   649         -      db_close();
         1232  +      db_close(1);
   650   1233         db_open_repository(zRepo);
   651   1234       }
   652   1235     }
   653   1236   #endif
   654   1237     return zRepo;
   655   1238   }
   656   1239   
................................................................................
   658   1241   ** Preconditions:
   659   1242   **
   660   1243   **  * Environment variables are set up according to the CGI standard.
   661   1244   **
   662   1245   ** If the repository is known, it has already been opened.  If unknown,
   663   1246   ** then g.zRepositoryName holds the directory that contains the repository
   664   1247   ** and the actual repository is taken from the first element of PATH_INFO.
   665         -** 
         1248  +**
   666   1249   ** Process the webpage specified by the PATH_INFO or REQUEST_URI
   667   1250   ** environment variable.
         1251  +**
         1252  +** If the repository is not known, the a search is done through the
         1253  +** file hierarchy rooted at g.zRepositoryName for a suitable repository
         1254  +** with a name of $prefix.fossil, where $prefix is any prefix of PATH_INFO.
         1255  +** Or, if an ordinary file named $prefix is found, and $prefix matches
         1256  +** pFileGlob and $prefix does not match "*.fossil*" and the mimetype of
         1257  +** $prefix can be determined from its suffix, then the file $prefix is
         1258  +** returned as static text.
         1259  +**
         1260  +** If no suitable webpage is found, try to redirect to zNotFound.
   668   1261   */
   669         -static void process_one_web_page(const char *zNotFound){
         1262  +static void process_one_web_page(const char *zNotFound, Glob *pFileGlob){
   670   1263     const char *zPathInfo;
   671   1264     char *zPath = NULL;
   672   1265     int idx;
   673   1266     int i;
   674   1267   
   675   1268     /* If the repository has not been opened already, then find the
   676   1269     ** repository based on the first element of PATH_INFO and open it.
   677   1270     */
   678         -  zPathInfo = P("PATH_INFO");
         1271  +  zPathInfo = PD("PATH_INFO","");
   679   1272     if( !g.repositoryOpen ){
   680         -    char *zRepo;
         1273  +    char *zRepo, *zToFree;
   681   1274       const char *zOldScript = PD("SCRIPT_NAME", "");
   682   1275       char *zNewScript;
   683   1276       int j, k;
   684         -
   685         -    i = 1;
   686         -    while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
   687         -    zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
   688         -
   689         -    /* To avoid mischief, make sure the repository basename contains no
   690         -    ** characters other than alphanumerics, "-", and "_".
   691         -    */
   692         -    for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){
   693         -      if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_';
   694         -    }
   695         -    if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++;
   696         -
   697         -    if( file_size(zRepo)<1024 ){
   698         -      if( zNotFound ){
   699         -        cgi_redirect(zNotFound);
   700         -      }else{
   701         -        @ <h1>Not Found</h1>
   702         -        cgi_set_status(404, "not found");
   703         -        cgi_reply();
         1277  +    i64 szFile;
         1278  +
         1279  +    i = zPathInfo[0]!=0;
         1280  +    while( 1 ){
         1281  +      while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
         1282  +      zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
         1283  +
         1284  +      /* To avoid mischief, make sure the repository basename contains no
         1285  +      ** characters other than alphanumerics, "/", "_", "-", and ".", and
         1286  +      ** that "-" never occurs immediately after a "/" and that "." is always
         1287  +      ** surrounded by two alphanumerics.  Any character that does not 
         1288  +      ** satisfy these constraints is converted into "_".
         1289  +      */
         1290  +      szFile = 0;
         1291  +      for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){
         1292  +        char c = zRepo[j];
         1293  +        if( fossil_isalnum(c) ) continue;
         1294  +        if( c=='/' ) continue;
         1295  +        if( c=='_' ) continue;
         1296  +        if( c=='-' && zRepo[j-1]!='/' ) continue;
         1297  +        if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
         1298  +          continue;
         1299  +        }
         1300  +        szFile = 1;
         1301  +        break;
         1302  +      }
         1303  +      if( szFile==0 ){
         1304  +        if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; }
         1305  +        szFile = file_size(zRepo);
         1306  +      }
         1307  +      if( szFile<0 ){
         1308  +        const char *zMimetype;
         1309  +        assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
         1310  +        zRepo[j] = 0;
         1311  +        if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){
         1312  +          fossil_free(zToFree);
         1313  +          i++;
         1314  +          continue;
         1315  +        }
         1316  +        if( pFileGlob!=0
         1317  +         && file_isfile(zRepo)
         1318  +         && glob_match(pFileGlob, zRepo)
         1319  +         && strglob("*.fossil*",zRepo)==0
         1320  +         && (zMimetype = mimetype_from_name(zRepo))!=0
         1321  +         && strcmp(zMimetype, "application/x-fossil-artifact")!=0
         1322  +        ){
         1323  +          Blob content;
         1324  +          blob_read_from_file(&content, zRepo);
         1325  +          cgi_set_content_type(zMimetype);
         1326  +          cgi_set_content(&content);
         1327  +          cgi_reply();
         1328  +          return;
         1329  +        }
         1330  +        zRepo[j] = '.';
         1331  +      }
         1332  +
         1333  +      if( szFile<1024 ){
         1334  +        set_base_url(0);
         1335  +        if( zNotFound ){
         1336  +          cgi_redirect(zNotFound);
         1337  +        }else{
         1338  +#ifdef FOSSIL_ENABLE_JSON
         1339  +          if(g.json.isJsonMode){
         1340  +            json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
         1341  +            return;
         1342  +          }
         1343  +#endif
         1344  +          @ <h1>Not Found</h1>
         1345  +          cgi_set_status(404, "not found");
         1346  +          cgi_reply();
         1347  +        }
         1348  +        return;
   704   1349         }
   705         -      return;
         1350  +      break;
   706   1351       }
   707   1352       zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
   708   1353       cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
   709   1354       zPathInfo += i;
   710   1355       cgi_replace_parameter("SCRIPT_NAME", zNewScript);
   711   1356       db_open_repository(zRepo);
   712   1357       if( g.fHttpTrace ){
   713         -      fprintf(stderr, 
         1358  +      fprintf(stderr,
   714   1359             "# repository: [%s]\n"
   715   1360             "# new PATH_INFO = [%s]\n"
   716   1361             "# new SCRIPT_NAME = [%s]\n",
   717   1362             zRepo, zPathInfo, zNewScript);
   718   1363       }
   719   1364     }
   720   1365   
   721   1366     /* Find the page that the user has requested, construct and deliver that
   722   1367     ** page.
   723   1368     */
   724   1369     if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
   725   1370       zPathInfo = "/xfer";
   726   1371     }
   727         -  set_base_url();
   728         -  if( zPathInfo==0 || zPathInfo[0]==0 
         1372  +  set_base_url(0);
         1373  +  if( zPathInfo==0 || zPathInfo[0]==0
   729   1374         || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
   730         -    fossil_redirect_home();
         1375  +#ifdef FOSSIL_ENABLE_JSON
         1376  +    if(g.json.isJsonMode){
         1377  +      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
         1378  +      fossil_exit(0);
         1379  +    }
         1380  +#endif
         1381  +    fossil_redirect_home() /*does not return*/;
   731   1382     }else{
   732   1383       zPath = mprintf("%s", zPathInfo);
   733   1384     }
   734   1385   
   735         -  /* Remove the leading "/" at the beginning of the path.
         1386  +  /* Make g.zPath point to the first element of the path.  Make
         1387  +  ** g.zExtra point to everything past that point.
         1388  +  */
         1389  +  while(1){
         1390  +    char *zAltRepo = 0;
         1391  +    g.zPath = &zPath[1];
         1392  +    for(i=1; zPath[i] && zPath[i]!='/'; i++){}
         1393  +    if( zPath[i]=='/' ){
         1394  +      zPath[i] = 0;
         1395  +      g.zExtra = &zPath[i+1];
         1396  +
         1397  +      /* Look for sub-repositories.  A sub-repository is another repository
         1398  +      ** that accepts the login credentials of the current repository.  A
         1399  +      ** subrepository is identified by a CONFIG table entry "subrepo:NAME"
         1400  +      ** where NAME is the first component of the path.  The value of the
         1401  +      ** the CONFIG entries is the string "USER:FILENAME" where USER is the
         1402  +      ** USER name to log in as in the subrepository and FILENAME is the
         1403  +      ** repository filename.
         1404  +      */
         1405  +      zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'",
         1406  +                         g.zPath);
         1407  +      if( zAltRepo ){
         1408  +        int nHost;
         1409  +        int jj;
         1410  +        char *zUser = zAltRepo;
         1411  +        login_check_credentials();
         1412  +        for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){}
         1413  +        if( zAltRepo[jj]==':' ){
         1414  +          zAltRepo[jj] = 0;
         1415  +          zAltRepo += jj+1;
         1416  +        }else{
         1417  +          zUser = "nobody";
         1418  +        }
         1419  +        if( g.zLogin==0 ) zUser = "nobody";
         1420  +        if( zAltRepo[0]!='/' ){
         1421  +          zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo);
         1422  +          file_simplify_name(zAltRepo, -1, 0);
         1423  +        }
         1424  +        db_close(1);
         1425  +        db_open_repository(zAltRepo);
         1426  +        login_as_user(zUser);
         1427  +        g.perm.Password = 0;
         1428  +        zPath += i;
         1429  +        nHost = g.zTop - g.zBaseURL;
         1430  +        g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath);
         1431  +        g.zTop = g.zBaseURL + nHost;
         1432  +        continue;
         1433  +      }
         1434  +    }else{
         1435  +      g.zExtra = 0;
         1436  +    }
         1437  +    break;
         1438  +  }
         1439  +#ifdef FOSSIL_ENABLE_JSON
         1440  +  /*
         1441  +  ** Workaround to allow us to customize some following behaviour for
         1442  +  ** JSON mode.  The problem is, we don't always know if we're in JSON
         1443  +  ** mode at this point (namely, for GET mode we don't know but POST
         1444  +  ** we do), so we snoop g.zPath and cheat a bit.
   736   1445     */
   737         -  g.zPath = &zPath[1];
   738         -  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
   739         -  if( zPath[i]=='/' ){
   740         -    zPath[i] = 0;
   741         -    g.zExtra = &zPath[i+1];
   742         -  }else{
   743         -    g.zExtra = 0;
         1446  +  if( !g.json.isJsonMode && g.zPath && (0==strncmp("json",g.zPath,4)) ){
         1447  +    g.json.isJsonMode = 1;
   744   1448     }
         1449  +#endif
   745   1450     if( g.zExtra ){
   746   1451       /* CGI parameters get this treatment elsewhere, but places like getfile
   747   1452       ** will use g.zExtra directly.
         1453  +    ** Reminder: the login mechanism uses 'name' differently, and may
         1454  +    ** eventually have a problem/collision with this.
         1455  +    **
         1456  +    ** Disabled by stephan when running in JSON mode because this
         1457  +    ** particular parameter name is very common and i have had no end
         1458  +    ** of grief with this handling. The JSON API never relies on the
         1459  +    ** handling below, and by disabling it in JSON mode I can remove
         1460  +    ** lots of special-case handling in several JSON handlers.
   748   1461       */
   749         -    dehttpize(g.zExtra);
   750         -    cgi_set_parameter_nocopy("name", g.zExtra);
         1462  +#ifdef FOSSIL_ENABLE_JSON
         1463  +    if(!g.json.isJsonMode){
         1464  +#endif
         1465  +      dehttpize(g.zExtra);
         1466  +      cgi_set_parameter_nocopy("name", g.zExtra);
         1467  +#ifdef FOSSIL_ENABLE_JSON
         1468  +    }
         1469  +#endif
   751   1470     }
   752         -  
         1471  +
   753   1472     /* Locate the method specified by the path and execute the function
   754   1473     ** that implements that method.
   755   1474     */
   756   1475     if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
   757   1476         name_search("not_found", aWebpage, count(aWebpage), &idx) ){
   758         -    cgi_set_status(404,"Not Found");
   759         -    @ <h1>Not Found</h1>
   760         -    @ <p>Page not found: %h(g.zPath)</p>
         1477  +#ifdef FOSSIL_ENABLE_JSON
         1478  +    if(g.json.isJsonMode){
         1479  +      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
         1480  +    }else
         1481  +#endif
         1482  +    {
         1483  +      cgi_set_status(404,"Not Found");
         1484  +      @ <h1>Not Found</h1>
         1485  +      @ <p>Page not found: %h(g.zPath)</p>
         1486  +    }
         1487  +  }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
         1488  +#ifdef FOSSIL_ENABLE_JSON
         1489  +    if(g.json.isJsonMode){
         1490  +      json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
         1491  +    }else
         1492  +#endif
         1493  +    {
         1494  +      @ <h1>Server Configuration Error</h1>
         1495  +      @ <p>The database schema on the server is out-of-date.  Please ask
         1496  +      @ the administrator to run <b>fossil rebuild</b>.</p>
         1497  +    }
   761   1498     }else{
   762   1499       aWebpage[idx].xFunc();
   763   1500     }
   764   1501   
   765   1502     /* Return the result.
   766   1503     */
   767   1504     cgi_reply();
   768   1505   }
   769   1506   
   770   1507   /*
   771         -** COMMAND: cgi
         1508  +** COMMAND: cgi*
   772   1509   **
   773   1510   ** Usage: %fossil ?cgi? SCRIPT
   774   1511   **
   775   1512   ** The SCRIPT argument is the name of a file that is the CGI script
   776   1513   ** that is being run.  The command name, "cgi", may be omitted if
   777   1514   ** the GATEWAY_INTERFACE environment variable is set to "CGI" (which
   778   1515   ** should always be the case for CGI scripts run by a webserver.)  The
................................................................................
   780   1517   **
   781   1518   **      #!/usr/bin/fossil
   782   1519   **      repository: /home/somebody/project.db
   783   1520   **
   784   1521   ** The second line defines the name of the repository.  After locating
   785   1522   ** the repository, fossil will generate a webpage on stdout based on
   786   1523   ** the values of standard CGI environment variables.
         1524  +**
         1525  +** See also: http, server, winsrv
   787   1526   */
   788   1527   void cmd_cgi(void){
   789   1528     const char *zFile;
   790   1529     const char *zNotFound = 0;
   791         -  Blob config, line, key, value;
   792         -  if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){
         1530  +  char **azRedirect = 0;             /* List of repositories to redirect to */
         1531  +  int nRedirect = 0;                 /* Number of entries in azRedirect */
         1532  +  Glob *pFileGlob = 0;               /* Pattern for files */
         1533  +  Blob config, line, key, value, value2;
         1534  +  if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
   793   1535       zFile = g.argv[2];
   794   1536     }else{
   795   1537       zFile = g.argv[1];
   796   1538     }
   797   1539     g.httpOut = stdout;
   798   1540     g.httpIn = stdin;
   799         -#ifdef __MINGW32__
   800         -  /* Set binary mode on windows to avoid undesired translations
   801         -  ** between \n and \r\n. */
   802         -  setmode(_fileno(g.httpOut), _O_BINARY);
   803         -  setmode(_fileno(g.httpIn), _O_BINARY);
   804         -#endif
   805         -#ifdef __EMX__
   806         -  /* Similar hack for OS/2 */
   807         -  setmode(fileno(g.httpOut), O_BINARY);
   808         -  setmode(fileno(g.httpIn), O_BINARY);
   809         -#endif
         1541  +  fossil_binary_mode(g.httpOut);
         1542  +  fossil_binary_mode(g.httpIn);
   810   1543     g.cgiOutput = 1;
   811   1544     blob_read_from_file(&config, zFile);
   812   1545     while( blob_line(&config, &line) ){
   813   1546       if( !blob_token(&line, &key) ) continue;
   814   1547       if( blob_buffer(&key)[0]=='#' ) continue;
   815   1548       if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){
   816         -      g.fDebug = fopen(blob_str(&value), "a");
         1549  +      g.fDebug = fossil_fopen(blob_str(&value), "ab");
   817   1550         blob_reset(&value);
   818   1551         continue;
   819   1552       }
   820   1553       if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){
   821   1554         cgi_setenv("HOME", blob_str(&value));
   822   1555         blob_reset(&value);
   823   1556         continue;
   824   1557       }
   825         -    if( blob_eq(&key, "repository:") && blob_token(&line, &value) ){
         1558  +    if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
         1559  +      blob_trim(&value);
   826   1560         db_open_repository(blob_str(&value));
   827   1561         blob_reset(&value);
   828   1562         continue;
   829   1563       }
   830   1564       if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){
   831         -      db_close();
         1565  +      db_close(1);
   832   1566         g.zRepositoryName = mprintf("%s", blob_str(&value));
   833   1567         blob_reset(&value);
   834   1568         continue;
   835   1569       }
   836   1570       if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){
   837   1571         zNotFound = mprintf("%s", blob_str(&value));
   838   1572         blob_reset(&value);
   839   1573         continue;
   840   1574       }
         1575  +    if( blob_eq(&key, "localauth") ){
         1576  +      g.useLocalauth = 1;
         1577  +      continue;
         1578  +    }
         1579  +    if( blob_eq(&key, "redirect:") && blob_token(&line, &value)
         1580  +            && blob_token(&line, &value2) ){
         1581  +      nRedirect++;
         1582  +      azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*));
         1583  +      azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value));
         1584  +      azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2));
         1585  +      blob_reset(&value);
         1586  +      blob_reset(&value2);
         1587  +      continue;
         1588  +    }
         1589  +    if( blob_eq(&key, "files:") && blob_token(&line, &value) ){
         1590  +      pFileGlob = glob_create(blob_str(&value));
         1591  +      continue;
         1592  +    }
   841   1593     }
   842   1594     blob_reset(&config);
   843         -  if( g.db==0 && g.zRepositoryName==0 ){
         1595  +  if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){
   844   1596       cgi_panic("Unable to find or open the project repository");
   845   1597     }
   846   1598     cgi_init();
   847         -  process_one_web_page(zNotFound);
         1599  +  if( nRedirect ){
         1600  +    redirect_web_page(nRedirect, azRedirect);
         1601  +  }else{
         1602  +    process_one_web_page(zNotFound, pFileGlob);
         1603  +  }
         1604  +}
         1605  +
         1606  +/* If the CGI program contains one or more lines of the form
         1607  +**
         1608  +**    redirect:  repository-filename  http://hostname/path/%s
         1609  +**
         1610  +** then control jumps here.  Search each repository for an artifact ID
         1611  +** that matches the "name" CGI parameter and for the first match,
         1612  +** redirect to the corresponding URL with the "name" CGI parameter
         1613  +** inserted.  Paint an error page if no match is found.
         1614  +**
         1615  +** If there is a line of the form:
         1616  +**
         1617  +**    redirect: * URL
         1618  +**
         1619  +** Then a redirect is made to URL if no match is found.  Otherwise a
         1620  +** very primitive error message is returned.
         1621  +*/
         1622  +void redirect_web_page(int nRedirect, char **azRedirect){
         1623  +  int i;                             /* Loop counter */
         1624  +  const char *zNotFound = 0;         /* Not found URL */
         1625  +  const char *zName = P("name");
         1626  +  set_base_url(0);
         1627  +  if( zName==0 ){
         1628  +    zName = P("SCRIPT_NAME");
         1629  +    if( zName && zName[0]=='/' ) zName++;
         1630  +  }
         1631  +  if( zName && validate16(zName, strlen(zName)) ){
         1632  +    for(i=0; i<nRedirect; i++){
         1633  +      if( fossil_strcmp(azRedirect[i*2],"*")==0 ){
         1634  +        zNotFound = azRedirect[i*2+1];
         1635  +        continue;
         1636  +      }
         1637  +      db_open_repository(azRedirect[i*2]);
         1638  +      if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%s*'", zName) ){
         1639  +        cgi_redirectf(azRedirect[i*2+1], zName);
         1640  +        return;
         1641  +      }
         1642  +      db_close(1);
         1643  +    }
         1644  +  }
         1645  +  if( zNotFound ){
         1646  +    cgi_redirectf(zNotFound, zName);
         1647  +  }else{
         1648  +    @ <html>
         1649  +    @ <head><title>No Such Object</title></head>
         1650  +    @ <body>
         1651  +    @ <p>No such object: <b>%h(zName)</b></p>
         1652  +    @ </body>
         1653  +    cgi_reply();
         1654  +  }
   848   1655   }
   849   1656   
   850   1657   /*
   851   1658   ** If g.argv[2] exists then it is either the name of a repository
   852   1659   ** that will be used by a server, or else it is a directory that
   853   1660   ** contains multiple repositories that can be served.  If g.argv[2]
   854   1661   ** is a directory, the repositories it contains must be named
   855   1662   ** "*.fossil".  If g.argv[2] does not exists, then we must be within
   856   1663   ** a check-out and the repository to be served is the repository of
   857   1664   ** that check-out.
   858   1665   **
   859         -** Open the respository to be served if it is known.  If g.argv[2] is
         1666  +** Open the repository to be served if it is known.  If g.argv[2] is
   860   1667   ** a directory full of repositories, then set g.zRepositoryName to
   861   1668   ** the name of that directory and the specific repository will be
   862   1669   ** opened later by process_one_web_page() based on the content of
   863   1670   ** the PATH_INFO variable.
   864   1671   **
   865   1672   ** If disallowDir is set, then the directory full of repositories method
   866   1673   ** is disallowed.
   867   1674   */
   868   1675   static void find_server_repository(int disallowDir){
   869   1676     if( g.argc<3 ){
   870   1677       db_must_be_within_tree();
   871         -  }else if( !disallowDir && file_isdir(g.argv[2])==1 ){
   872         -    g.zRepositoryName = mprintf("%s", g.argv[2]);
   873         -    file_simplify_name(g.zRepositoryName, -1);
         1678  +  }else if( file_isdir(g.argv[2])==1 ){
         1679  +    if( disallowDir ){
         1680  +      fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[2]);
         1681  +    }else{
         1682  +      g.zRepositoryName = mprintf("%s", g.argv[2]);
         1683  +      file_simplify_name(g.zRepositoryName, -1, 0);
         1684  +    }
   874   1685     }else{
   875   1686       db_open_repository(g.argv[2]);
   876   1687     }
   877   1688   }
   878   1689   
   879   1690   /*
   880   1691   ** undocumented format:
   881   1692   **
   882   1693   **        fossil http REPOSITORY INFILE OUTFILE IPADDR
   883   1694   **
   884   1695   ** The argv==6 form is used by the win32 server only.
   885   1696   **
   886         -** COMMAND: http
         1697  +** COMMAND: http*
   887   1698   **
   888         -** Usage: %fossil http REPOSITORY [--notfound URL]
         1699  +** Usage: %fossil http REPOSITORY ?OPTIONS?
   889   1700   **
   890   1701   ** Handle a single HTTP request appearing on stdin.  The resulting webpage
   891   1702   ** is delivered on stdout.  This method is used to launch an HTTP request
   892         -** handler from inetd, for example.  The argument is the name of the 
         1703  +** handler from inetd, for example.  The argument is the name of the
   893   1704   ** repository.
   894   1705   **
   895         -** If REPOSITORY is a directory that contains one or more respositories
   896         -** with names of the form "*.fossil" then the first element of the URL
   897         -** pathname selects among the various repositories.  If the pathname does
         1706  +** If REPOSITORY is a directory that contains one or more repositories,
         1707  +** either directly in REPOSITORY itself, or in subdirectories, and
         1708  +** with names of the form "*.fossil" then the a prefix of the URL pathname
         1709  +** selects from among the various repositories.  If the pathname does
   898   1710   ** not select a valid repository and the --notfound option is available,
   899   1711   ** then the server redirects (HTTP code 302) to the URL of --notfound.
         1712  +** When REPOSITORY is a directory, the pathname must contain only
         1713  +** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/"
         1714  +** and every "." must be surrounded on both sides by alphanumerics or else
         1715  +** a 404 error is returned.  Static content files in the directory are
         1716  +** returned if they match comma-separate GLOB pattern specified by --files
         1717  +** and do not match "*.fossil*" and have a well-known suffix.
         1718  +**
         1719  +** The --host option can be used to specify the hostname for the server.
         1720  +** The --https option indicates that the request came from HTTPS rather
         1721  +** than HTTP. If --nossl is given, then SSL connections will not be available,
         1722  +** thus also no redirecting from http: to https: will take place.
         1723  +**
         1724  +** If the --localauth option is given, then automatic login is performed
         1725  +** for requests coming from localhost, if the "localauth" setting is not
         1726  +** enabled.
         1727  +**
         1728  +** Options:
         1729  +**   --localauth      enable automatic login for local connections
         1730  +**   --host NAME      specify hostname of the server
         1731  +**   --https          signal a request coming in via https
         1732  +**   --nossl          signal that no SSL connections are available
         1733  +**   --notfound URL   use URL as "HTTP 404, object not found" page.
         1734  +**   --files GLOB     comma-separate glob patterns for static file to serve
         1735  +**   --baseurl URL    base URL (useful with reverse proxies)
         1736  +**
         1737  +** See also: cgi, server, winsrv
   900   1738   */
   901   1739   void cmd_http(void){
   902   1740     const char *zIpAddr;
   903   1741     const char *zNotFound;
         1742  +  const char *zHost;
         1743  +  const char *zAltBase;
         1744  +  const char *zFileGlob;
         1745  +
         1746  +  /* The winhttp module passes the --files option as --files-urlenc with
         1747  +  ** the argument being URL encoded, to avoid wildcard expansion in the 
         1748  +  ** shell.  This option is for internal use and is undocumented.
         1749  +  */
         1750  +  zFileGlob = find_option("files-urlenc",0,1);
         1751  +  if( zFileGlob ){
         1752  +    char *z = mprintf("%s", zFileGlob);
         1753  +    dehttpize(z);
         1754  +    zFileGlob = z;
         1755  +  }else{
         1756  +    zFileGlob = find_option("files",0,1);
         1757  +  }
   904   1758     zNotFound = find_option("notfound", 0, 1);
   905         -  if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
   906         -    cgi_panic("no repository specified");
   907         -  }
         1759  +  g.useLocalauth = find_option("localauth", 0, 0)!=0;
         1760  +  g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
         1761  +  zAltBase = find_option("baseurl", 0, 1);
         1762  +  if( zAltBase ) set_base_url(zAltBase);
         1763  +  if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on");
         1764  +  zHost = find_option("host", 0, 1);
         1765  +  if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
   908   1766     g.cgiOutput = 1;
         1767  +  if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
         1768  +    fossil_fatal("no repository specified");
         1769  +  }
   909   1770     g.fullHttpReply = 1;
   910   1771     if( g.argc==6 ){
   911         -    g.httpIn = fopen(g.argv[3], "rb");
   912         -    g.httpOut = fopen(g.argv[4], "wb");
         1772  +    g.httpIn = fossil_fopen(g.argv[3], "rb");
         1773  +    g.httpOut = fossil_fopen(g.argv[4], "wb");
   913   1774       zIpAddr = g.argv[5];
   914   1775     }else{
   915   1776       g.httpIn = stdin;
   916   1777       g.httpOut = stdout;
   917   1778       zIpAddr = 0;
   918   1779     }
   919   1780     find_server_repository(0);
   920   1781     g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
   921   1782     cgi_handle_http_request(zIpAddr);
   922         -  process_one_web_page(zNotFound);
         1783  +  process_one_web_page(zNotFound, glob_create(zFileGlob));
   923   1784   }
   924   1785   
   925   1786   /*
         1787  +** Note that the following command is used by ssh:// processing.
         1788  +**
   926   1789   ** COMMAND: test-http
   927   1790   ** Works like the http command but gives setup permission to all users.
   928   1791   */
   929   1792   void cmd_test_http(void){
   930         -  login_set_capabilities("s");
   931         -  cmd_http();
         1793  +  g.thTrace = find_option("th-trace", 0, 0)!=0;
         1794  +  if( g.thTrace ){
         1795  +    blob_zero(&g.thLog);
         1796  +  }
         1797  +  login_set_capabilities("sx", 0);
         1798  +  g.useLocalauth = 1;
         1799  +  cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
         1800  +  g.httpIn = stdin;
         1801  +  g.httpOut = stdout;
         1802  +  find_server_repository(0);
         1803  +  g.cgiOutput = 1;
         1804  +  g.fullHttpReply = 1;
         1805  +  cgi_handle_http_request(0);
         1806  +  process_one_web_page(0, 0);
   932   1807   }
   933   1808   
   934         -#ifndef __MINGW32__
   935         -#if !defined(__DARWIN__) && !defined(__APPLE__)
         1809  +#if !defined(_WIN32)
         1810  +#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
   936   1811   /*
   937   1812   ** Search for an executable on the PATH environment variable.
   938   1813   ** Return true (1) if found and false (0) if not found.
   939   1814   */
   940   1815   static int binaryOnPath(const char *zBinary){
   941         -  const char *zPath = getenv("PATH");
         1816  +  const char *zPath = fossil_getenv("PATH");
   942   1817     char *zFull;
   943   1818     int i;
   944   1819     int bExists;
   945   1820     while( zPath && zPath[0] ){
   946   1821       while( zPath[0]==':' ) zPath++;
   947   1822       for(i=0; zPath[i] && zPath[i]!=':'; i++){}
   948   1823       zFull = mprintf("%.*s/%s", i, zPath, zBinary);
   949         -    bExists = access(zFull, X_OK);
   950         -    free(zFull);
         1824  +    bExists = file_access(zFull, X_OK);
         1825  +    fossil_free(zFull);
   951   1826       if( bExists==0 ) return 1;
   952   1827       zPath += i;
   953   1828     }
   954   1829     return 0;
   955   1830   }
   956   1831   #endif
   957   1832   #endif
   958   1833   
   959   1834   /*
   960         -** COMMAND: server
         1835  +** COMMAND: server*
   961   1836   ** COMMAND: ui
   962   1837   **
   963         -** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY?
   964         -**    Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY?
         1838  +** Usage: %fossil server ?OPTIONS? ?REPOSITORY?
         1839  +**    Or: %fossil ui ?OPTIONS? ?REPOSITORY?
   965   1840   **
   966   1841   ** Open a socket and begin listening and responding to HTTP requests on
   967   1842   ** TCP port 8080, or on any other TCP port defined by the -P or
   968   1843   ** --port option.  The optional argument is the name of the repository.
   969   1844   ** The repository argument may be omitted if the working directory is
   970   1845   ** within an open checkout.
   971   1846   **
   972   1847   ** The "ui" command automatically starts a web browser after initializing
   973         -** the web server.
         1848  +** the web server.  The "ui" command also binds to 127.0.0.1 and so will
         1849  +** only process HTTP traffic from the local machine.
         1850  +**
         1851  +** The REPOSITORY can be a directory (aka folder) that contains one or 
         1852  +** more repositories with names ending in ".fossil".  In this case, the 
         1853  +** a prefix of the URL pathname is used to search the directory for an
         1854  +** appropriate repository.  To thwart mischief, the pathname in the URL must
         1855  +** contain only alphanumerics, "_", "/", "-", and ".", and no "-" may
         1856  +** occur after "/", and every "." must be surrounded on both sides by
         1857  +** alphanumerics.  Any pathname that does not satisfy these constraints
         1858  +** results in a 404 error.  Files in REPOSITORY that match the comma-separated
         1859  +** list of glob patterns given by --files and that have known suffixes
         1860  +** such as ".txt" or ".html" or ".jpeg" and do not match the pattern
         1861  +** "*.fossil*" will be served as static content.  With the "ui" command,
         1862  +** the REPOSITORY can only be a directory if the --notfound option is
         1863  +** also present.
         1864  +**
         1865  +** By default, the "ui" command provides full administrative access without
         1866  +** having to log in.  This can be disabled by setting turning off the
         1867  +** "localauth" setting.  Automatic login for the "server" command is available
         1868  +** if the --localauth option is present and the "localauth" setting is off
         1869  +** and the connection is from localhost.  The optional REPOSITORY argument
         1870  +** to "ui" may be a directory and will function as "server" if and only if
         1871  +** the --notfound option is used.
         1872  +**
         1873  +** Options:
         1874  +**   --localauth         enable automatic login for requests from localhost
         1875  +**   -P|--port TCPPORT   listen to request on port TCPPORT
         1876  +**   --th-trace          trace TH1 execution (for debugging purposes)
         1877  +**   --baseurl URL       Use URL as the base (useful for reverse proxies)
         1878  +**   --notfound URL      Redirect
         1879  +**   --files GLOBLIST    Comma-separated list of glob patterns for static files
   974   1880   **
   975         -** In the "server" command, the REPOSITORY can be a directory (aka folder)
   976         -** that contains one or more respositories with names ending in ".fossil".
   977         -** In that case, the first element of the URL is used to select among the
   978         -** various repositories.
         1881  +** See also: cgi, http, winsrv
   979   1882   */
   980   1883   void cmd_webserver(void){
   981   1884     int iPort, mxPort;        /* Range of TCP ports allowed */
   982   1885     const char *zPort;        /* Value of the --port option */
   983         -  char *zBrowser;           /* Name of web browser program */
         1886  +  const char *zBrowser;     /* Name of web browser program */
   984   1887     char *zBrowserCmd = 0;    /* Command to launch the web browser */
   985   1888     int isUiCmd;              /* True if command is "ui", not "server' */
   986   1889     const char *zNotFound;    /* The --notfound option or NULL */
         1890  +  int flags = 0;            /* Server flags */
         1891  +  const char *zAltBase;     /* Argument to the --baseurl option */
         1892  +  const char *zFileGlob;    /* Static content must match this */
   987   1893   
   988         -#ifdef __MINGW32__
         1894  +#if defined(_WIN32)
   989   1895     const char *zStopperFile;    /* Name of file used to terminate server */
   990   1896     zStopperFile = find_option("stopper", 0, 1);
   991   1897   #endif
   992   1898   
         1899  +  zFileGlob = find_option("files", 0, 1);
   993   1900     g.thTrace = find_option("th-trace", 0, 0)!=0;
         1901  +  g.useLocalauth = find_option("localauth", 0, 0)!=0;
   994   1902     if( g.thTrace ){
   995   1903       blob_zero(&g.thLog);
   996   1904     }
   997   1905     zPort = find_option("port", "P", 1);
   998   1906     zNotFound = find_option("notfound", 0, 1);
         1907  +  zAltBase = find_option("baseurl", 0, 1);
         1908  +  if( zAltBase ){
         1909  +    set_base_url(zAltBase);
         1910  +  }
   999   1911     if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  1000   1912     isUiCmd = g.argv[1][0]=='u';
  1001         -  find_server_repository(isUiCmd);
         1913  +  if( isUiCmd ){
         1914  +    flags |= HTTP_SERVER_LOCALHOST;
         1915  +    g.useLocalauth = 1;
         1916  +  }
         1917  +  find_server_repository(isUiCmd && zNotFound==0);
  1002   1918     if( zPort ){
  1003   1919       iPort = mxPort = atoi(zPort);
  1004   1920     }else{
  1005   1921       iPort = db_get_int("http-port", 8080);
  1006   1922       mxPort = iPort+100;
  1007   1923     }
  1008         -#ifndef __MINGW32__
         1924  +#if !defined(_WIN32)
  1009   1925     /* Unix implementation */
  1010   1926     if( isUiCmd ){
  1011         -#if !defined(__DARWIN__) && !defined(__APPLE__)
         1927  +#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
  1012   1928       zBrowser = db_get("web-browser", 0);
  1013   1929       if( zBrowser==0 ){
  1014         -      static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" };
         1930  +      static const char *const azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" };
  1015   1931         int i;
  1016   1932         zBrowser = "echo";
  1017   1933         for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){
  1018   1934           if( binaryOnPath(azBrowserProg[i]) ){
  1019   1935             zBrowser = azBrowserProg[i];
  1020   1936             break;
  1021   1937           }
................................................................................
  1022   1938         }
  1023   1939       }
  1024   1940   #else
  1025   1941       zBrowser = db_get("web-browser", "open");
  1026   1942   #endif
  1027   1943       zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
  1028   1944     }
  1029         -  db_close();
  1030         -  if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
         1945  +  db_close(1);
         1946  +  if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
  1031   1947       fossil_fatal("unable to listen on TCP socket %d", iPort);
  1032   1948     }
         1949  +  g.sslNotAvailable = 1;
  1033   1950     g.httpIn = stdin;
  1034   1951     g.httpOut = stdout;
  1035   1952     if( g.fHttpTrace || g.fSqlTrace ){
  1036   1953       fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
  1037   1954     }
  1038   1955     g.cgiOutput = 1;
  1039         -  find_server_repository(isUiCmd);
         1956  +  find_server_repository(isUiCmd && zNotFound==0);
  1040   1957     g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
  1041   1958     cgi_handle_http_request(0);
  1042         -  process_one_web_page(zNotFound);
         1959  +  process_one_web_page(zNotFound, glob_create(zFileGlob));
  1043   1960   #else
  1044   1961     /* Win32 implementation */
  1045   1962     if( isUiCmd ){
  1046   1963       zBrowser = db_get("web-browser", "start");
  1047   1964       zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
  1048   1965     }
  1049         -  db_close();
  1050         -  win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound);
         1966  +  db_close(1);
         1967  +  if( win32_http_service(iPort, zNotFound, zFileGlob, flags) ){
         1968  +    win32_http_server(iPort, mxPort, zBrowserCmd,
         1969  +                      zStopperFile, zNotFound, zFileGlob, flags);
         1970  +  }
  1051   1971   #endif
  1052   1972   }
         1973  +
         1974  +/*
         1975  +** COMMAND:  test-echo
         1976  +**
         1977  +** Usage:  %fossil test-echo [--hex] ARGS...
         1978  +**
         1979  +** Echo all command-line arguments (enclosed in [...]) to the screen so that
         1980  +** wildcard expansion behavior of the host shell can be investigated.
         1981  +**
         1982  +** With the --hex option, show the output as hexadecimal.  This can be used
         1983  +** to verify the fossil_filename_to_utf8() routine on Windows and Mac.
         1984  +*/
         1985  +void test_echo_cmd(void){
         1986  +  int i, j;
         1987  +  if( find_option("hex",0,0)==0 ){
         1988  +    fossil_print("g.nameOfExe = [%s]\n", g.nameOfExe);
         1989  +    for(i=0; i<g.argc; i++){
         1990  +      fossil_print("argv[%d] = [%s]\n", i, g.argv[i]);
         1991  +    }
         1992  +  }else{
         1993  +    unsigned char *z, c;
         1994  +    for(i=0; i<g.argc; i++){
         1995  +      fossil_print("argv[%d] = [", i);
         1996  +      z = (unsigned char*)g.argv[i];
         1997  +      for(j=0; (c = z[j])!=0; j++){
         1998  +        fossil_print("%02x", c);
         1999  +      }
         2000  +      fossil_print("]\n");
         2001  +    }
         2002  +  }
         2003  +}

Changes to src/main.mk.

     1         -# DO NOT EDIT
            1  +#
            2  +##############################################################################
            3  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            4  +##############################################################################
     2      5   #
     3      6   # This file is automatically generated.  Instead of editing this
     4         -# file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
            7  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
     5      8   # to regenerate this file.
     6      9   #
     7         -# This file is included by linux-gcc.mk or linux-mingw.mk or possible
     8         -# some other makefiles.  This file contains the rules that are common
     9         -# to building regardless of the target.
           10  +# This file is included by primary Makefile.
    10     11   #
    11     12   
    12         -XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
           13  +XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) -I$(OBJDIR)
    13     14   
    14     15   
    15     16   SRC = \
    16     17     $(SRCDIR)/add.c \
    17     18     $(SRCDIR)/allrepo.c \
    18     19     $(SRCDIR)/attach.c \
    19     20     $(SRCDIR)/bag.c \
           21  +  $(SRCDIR)/bisect.c \
    20     22     $(SRCDIR)/blob.c \
    21     23     $(SRCDIR)/branch.c \
    22     24     $(SRCDIR)/browse.c \
    23     25     $(SRCDIR)/captcha.c \
    24     26     $(SRCDIR)/cgi.c \
    25     27     $(SRCDIR)/checkin.c \
    26     28     $(SRCDIR)/checkout.c \
................................................................................
    33     35     $(SRCDIR)/delta.c \
    34     36     $(SRCDIR)/deltacmd.c \
    35     37     $(SRCDIR)/descendants.c \
    36     38     $(SRCDIR)/diff.c \
    37     39     $(SRCDIR)/diffcmd.c \
    38     40     $(SRCDIR)/doc.c \
    39     41     $(SRCDIR)/encode.c \
           42  +  $(SRCDIR)/event.c \
           43  +  $(SRCDIR)/export.c \
    40     44     $(SRCDIR)/file.c \
    41     45     $(SRCDIR)/finfo.c \
           46  +  $(SRCDIR)/glob.c \
    42     47     $(SRCDIR)/graph.c \
           48  +  $(SRCDIR)/gzip.c \
    43     49     $(SRCDIR)/http.c \
    44     50     $(SRCDIR)/http_socket.c \
           51  +  $(SRCDIR)/http_ssl.c \
    45     52     $(SRCDIR)/http_transport.c \
           53  +  $(SRCDIR)/import.c \
    46     54     $(SRCDIR)/info.c \
           55  +  $(SRCDIR)/json.c \
           56  +  $(SRCDIR)/json_artifact.c \
           57  +  $(SRCDIR)/json_branch.c \
           58  +  $(SRCDIR)/json_config.c \
           59  +  $(SRCDIR)/json_diff.c \
           60  +  $(SRCDIR)/json_dir.c \
           61  +  $(SRCDIR)/json_finfo.c \
           62  +  $(SRCDIR)/json_login.c \
           63  +  $(SRCDIR)/json_query.c \
           64  +  $(SRCDIR)/json_report.c \
           65  +  $(SRCDIR)/json_tag.c \
           66  +  $(SRCDIR)/json_timeline.c \
           67  +  $(SRCDIR)/json_user.c \
           68  +  $(SRCDIR)/json_wiki.c \
           69  +  $(SRCDIR)/leaf.c \
    47     70     $(SRCDIR)/login.c \
    48     71     $(SRCDIR)/main.c \
    49     72     $(SRCDIR)/manifest.c \
           73  +  $(SRCDIR)/markdown.c \
           74  +  $(SRCDIR)/markdown_html.c \
    50     75     $(SRCDIR)/md5.c \
    51     76     $(SRCDIR)/merge.c \
    52     77     $(SRCDIR)/merge3.c \
           78  +  $(SRCDIR)/moderate.c \
    53     79     $(SRCDIR)/name.c \
           80  +  $(SRCDIR)/path.c \
    54     81     $(SRCDIR)/pivot.c \
           82  +  $(SRCDIR)/popen.c \
    55     83     $(SRCDIR)/pqueue.c \
    56     84     $(SRCDIR)/printf.c \
    57     85     $(SRCDIR)/rebuild.c \
           86  +  $(SRCDIR)/regexp.c \
    58     87     $(SRCDIR)/report.c \
    59     88     $(SRCDIR)/rss.c \
    60     89     $(SRCDIR)/schema.c \
    61     90     $(SRCDIR)/search.c \
    62     91     $(SRCDIR)/setup.c \
    63     92     $(SRCDIR)/sha1.c \
    64     93     $(SRCDIR)/shun.c \
    65     94     $(SRCDIR)/skins.c \
           95  +  $(SRCDIR)/sqlcmd.c \
           96  +  $(SRCDIR)/stash.c \
    66     97     $(SRCDIR)/stat.c \
    67     98     $(SRCDIR)/style.c \
    68     99     $(SRCDIR)/sync.c \
    69    100     $(SRCDIR)/tag.c \
          101  +  $(SRCDIR)/tar.c \
    70    102     $(SRCDIR)/th_main.c \
    71    103     $(SRCDIR)/timeline.c \
    72    104     $(SRCDIR)/tkt.c \
    73    105     $(SRCDIR)/tktsetup.c \
    74    106     $(SRCDIR)/undo.c \
          107  +  $(SRCDIR)/unicode.c \
    75    108     $(SRCDIR)/update.c \
    76    109     $(SRCDIR)/url.c \
    77    110     $(SRCDIR)/user.c \
          111  +  $(SRCDIR)/utf8.c \
    78    112     $(SRCDIR)/verify.c \
    79    113     $(SRCDIR)/vfile.c \
    80    114     $(SRCDIR)/wiki.c \
    81    115     $(SRCDIR)/wikiformat.c \
    82    116     $(SRCDIR)/winhttp.c \
          117  +  $(SRCDIR)/wysiwyg.c \
    83    118     $(SRCDIR)/xfer.c \
          119  +  $(SRCDIR)/xfersetup.c \
    84    120     $(SRCDIR)/zip.c
    85    121   
    86    122   TRANS_SRC = \
    87         -  add_.c \
    88         -  allrepo_.c \
    89         -  attach_.c \
    90         -  bag_.c \
    91         -  blob_.c \
    92         -  branch_.c \
    93         -  browse_.c \
    94         -  captcha_.c \
    95         -  cgi_.c \
    96         -  checkin_.c \
    97         -  checkout_.c \
    98         -  clearsign_.c \
    99         -  clone_.c \
   100         -  comformat_.c \
   101         -  configure_.c \
   102         -  content_.c \
   103         -  db_.c \
   104         -  delta_.c \
   105         -  deltacmd_.c \
   106         -  descendants_.c \
   107         -  diff_.c \
   108         -  diffcmd_.c \
   109         -  doc_.c \
   110         -  encode_.c \
   111         -  file_.c \
   112         -  finfo_.c \
   113         -  graph_.c \
   114         -  http_.c \
   115         -  http_socket_.c \
   116         -  http_transport_.c \
   117         -  info_.c \
   118         -  login_.c \
   119         -  main_.c \
   120         -  manifest_.c \
   121         -  md5_.c \
   122         -  merge_.c \
   123         -  merge3_.c \
   124         -  name_.c \
   125         -  pivot_.c \
   126         -  pqueue_.c \
   127         -  printf_.c \
   128         -  rebuild_.c \
   129         -  report_.c \
   130         -  rss_.c \
   131         -  schema_.c \
   132         -  search_.c \
   133         -  setup_.c \
   134         -  sha1_.c \
   135         -  shun_.c \
   136         -  skins_.c \
   137         -  stat_.c \
   138         -  style_.c \
   139         -  sync_.c \
   140         -  tag_.c \
   141         -  th_main_.c \
   142         -  timeline_.c \
   143         -  tkt_.c \
   144         -  tktsetup_.c \
   145         -  undo_.c \
   146         -  update_.c \
   147         -  url_.c \
   148         -  user_.c \
   149         -  verify_.c \
   150         -  vfile_.c \
   151         -  wiki_.c \
   152         -  wikiformat_.c \
   153         -  winhttp_.c \
   154         -  xfer_.c \
   155         -  zip_.c
          123  +  $(OBJDIR)/add_.c \
          124  +  $(OBJDIR)/allrepo_.c \
          125  +  $(OBJDIR)/attach_.c \
          126  +  $(OBJDIR)/bag_.c \
          127  +  $(OBJDIR)/bisect_.c \
          128  +  $(OBJDIR)/blob_.c \
          129  +  $(OBJDIR)/branch_.c \
          130  +  $(OBJDIR)/browse_.c \
          131  +  $(OBJDIR)/captcha_.c \
          132  +  $(OBJDIR)/cgi_.c \
          133  +  $(OBJDIR)/checkin_.c \
          134  +  $(OBJDIR)/checkout_.c \
          135  +  $(OBJDIR)/clearsign_.c \
          136  +  $(OBJDIR)/clone_.c \
          137  +  $(OBJDIR)/comformat_.c \
          138  +  $(OBJDIR)/configure_.c \
          139  +  $(OBJDIR)/content_.c \
          140  +  $(OBJDIR)/db_.c \
          141  +  $(OBJDIR)/delta_.c \
          142  +  $(OBJDIR)/deltacmd_.c \
          143  +  $(OBJDIR)/descendants_.c \
          144  +  $(OBJDIR)/diff_.c \
          145  +  $(OBJDIR)/diffcmd_.c \
          146  +  $(OBJDIR)/doc_.c \
          147  +  $(OBJDIR)/encode_.c \
          148  +  $(OBJDIR)/event_.c \
          149  +  $(OBJDIR)/export_.c \
          150  +  $(OBJDIR)/file_.c \
          151  +  $(OBJDIR)/finfo_.c \
          152  +  $(OBJDIR)/glob_.c \
          153  +  $(OBJDIR)/graph_.c \
          154  +  $(OBJDIR)/gzip_.c \
          155  +  $(OBJDIR)/http_.c \
          156  +  $(OBJDIR)/http_socket_.c \
          157  +  $(OBJDIR)/http_ssl_.c \
          158  +  $(OBJDIR)/http_transport_.c \
          159  +  $(OBJDIR)/import_.c \
          160  +  $(OBJDIR)/info_.c \
          161  +  $(OBJDIR)/json_.c \
          162  +  $(OBJDIR)/json_artifact_.c \
          163  +  $(OBJDIR)/json_branch_.c \
          164  +  $(OBJDIR)/json_config_.c \
          165  +  $(OBJDIR)/json_diff_.c \
          166  +  $(OBJDIR)/json_dir_.c \
          167  +  $(OBJDIR)/json_finfo_.c \
          168  +  $(OBJDIR)/json_login_.c \
          169  +  $(OBJDIR)/json_query_.c \
          170  +  $(OBJDIR)/json_report_.c \
          171  +  $(OBJDIR)/json_tag_.c \
          172  +  $(OBJDIR)/json_timeline_.c \
          173  +  $(OBJDIR)/json_user_.c \
          174  +  $(OBJDIR)/json_wiki_.c \
          175  +  $(OBJDIR)/leaf_.c \
          176  +  $(OBJDIR)/login_.c \
          177  +  $(OBJDIR)/main_.c \
          178  +  $(OBJDIR)/manifest_.c \
          179  +  $(OBJDIR)/markdown_.c \
          180  +  $(OBJDIR)/markdown_html_.c \
          181  +  $(OBJDIR)/md5_.c \
          182  +  $(OBJDIR)/merge_.c \
          183  +  $(OBJDIR)/merge3_.c \
          184  +  $(OBJDIR)/moderate_.c \
          185  +  $(OBJDIR)/name_.c \
          186  +  $(OBJDIR)/path_.c \
          187  +  $(OBJDIR)/pivot_.c \
          188  +  $(OBJDIR)/popen_.c \
          189  +  $(OBJDIR)/pqueue_.c \
          190  +  $(OBJDIR)/printf_.c \
          191  +  $(OBJDIR)/rebuild_.c \
          192  +  $(OBJDIR)/regexp_.c \
          193  +  $(OBJDIR)/report_.c \
          194  +  $(OBJDIR)/rss_.c \
          195  +  $(OBJDIR)/schema_.c \
          196  +  $(OBJDIR)/search_.c \
          197  +  $(OBJDIR)/setup_.c \
          198  +  $(OBJDIR)/sha1_.c \
          199  +  $(OBJDIR)/shun_.c \
          200  +  $(OBJDIR)/skins_.c \
          201  +  $(OBJDIR)/sqlcmd_.c \
          202  +  $(OBJDIR)/stash_.c \
          203  +  $(OBJDIR)/stat_.c \
          204  +  $(OBJDIR)/style_.c \
          205  +  $(OBJDIR)/sync_.c \
          206  +  $(OBJDIR)/tag_.c \
          207  +  $(OBJDIR)/tar_.c \
          208  +  $(OBJDIR)/th_main_.c \
          209  +  $(OBJDIR)/timeline_.c \
          210  +  $(OBJDIR)/tkt_.c \
          211  +  $(OBJDIR)/tktsetup_.c \
          212  +  $(OBJDIR)/undo_.c \
          213  +  $(OBJDIR)/unicode_.c \
          214  +  $(OBJDIR)/update_.c \
          215  +  $(OBJDIR)/url_.c \
          216  +  $(OBJDIR)/user_.c \
          217  +  $(OBJDIR)/utf8_.c \
          218  +  $(OBJDIR)/verify_.c \
          219  +  $(OBJDIR)/vfile_.c \
          220  +  $(OBJDIR)/wiki_.c \
          221  +  $(OBJDIR)/wikiformat_.c \
          222  +  $(OBJDIR)/winhttp_.c \
          223  +  $(OBJDIR)/wysiwyg_.c \
          224  +  $(OBJDIR)/xfer_.c \
          225  +  $(OBJDIR)/xfersetup_.c \
          226  +  $(OBJDIR)/zip_.c
   156    227   
   157    228   OBJ = \
   158    229    $(OBJDIR)/add.o \
   159    230    $(OBJDIR)/allrepo.o \
   160    231    $(OBJDIR)/attach.o \
   161    232    $(OBJDIR)/bag.o \
          233  + $(OBJDIR)/bisect.o \
   162    234    $(OBJDIR)/blob.o \
   163    235    $(OBJDIR)/branch.o \
   164    236    $(OBJDIR)/browse.o \
   165    237    $(OBJDIR)/captcha.o \
   166    238    $(OBJDIR)/cgi.o \
   167    239    $(OBJDIR)/checkin.o \
   168    240    $(OBJDIR)/checkout.o \
................................................................................
   175    247    $(OBJDIR)/delta.o \
   176    248    $(OBJDIR)/deltacmd.o \
   177    249    $(OBJDIR)/descendants.o \
   178    250    $(OBJDIR)/diff.o \
   179    251    $(OBJDIR)/diffcmd.o \
   180    252    $(OBJDIR)/doc.o \
   181    253    $(OBJDIR)/encode.o \
          254  + $(OBJDIR)/event.o \
          255  + $(OBJDIR)/export.o \
   182    256    $(OBJDIR)/file.o \
   183    257    $(OBJDIR)/finfo.o \
          258  + $(OBJDIR)/glob.o \
   184    259    $(OBJDIR)/graph.o \
          260  + $(OBJDIR)/gzip.o \
   185    261    $(OBJDIR)/http.o \
   186    262    $(OBJDIR)/http_socket.o \
          263  + $(OBJDIR)/http_ssl.o \
   187    264    $(OBJDIR)/http_transport.o \
          265  + $(OBJDIR)/import.o \
   188    266    $(OBJDIR)/info.o \
          267  + $(OBJDIR)/json.o \
          268  + $(OBJDIR)/json_artifact.o \
          269  + $(OBJDIR)/json_branch.o \
          270  + $(OBJDIR)/json_config.o \
          271  + $(OBJDIR)/json_diff.o \
          272  + $(OBJDIR)/json_dir.o \
          273  + $(OBJDIR)/json_finfo.o \
          274  + $(OBJDIR)/json_login.o \
          275  + $(OBJDIR)/json_query.o \
          276  + $(OBJDIR)/json_report.o \
          277  + $(OBJDIR)/json_tag.o \
          278  + $(OBJDIR)/json_timeline.o \
          279  + $(OBJDIR)/json_user.o \
          280  + $(OBJDIR)/json_wiki.o \
          281  + $(OBJDIR)/leaf.o \
   189    282    $(OBJDIR)/login.o \
   190    283    $(OBJDIR)/main.o \
   191    284    $(OBJDIR)/manifest.o \
          285  + $(OBJDIR)/markdown.o \
          286  + $(OBJDIR)/markdown_html.o \
   192    287    $(OBJDIR)/md5.o \
   193    288    $(OBJDIR)/merge.o \
   194    289    $(OBJDIR)/merge3.o \
          290  + $(OBJDIR)/moderate.o \
   195    291    $(OBJDIR)/name.o \
          292  + $(OBJDIR)/path.o \
   196    293    $(OBJDIR)/pivot.o \
          294  + $(OBJDIR)/popen.o \
   197    295    $(OBJDIR)/pqueue.o \
   198    296    $(OBJDIR)/printf.o \
   199    297    $(OBJDIR)/rebuild.o \
          298  + $(OBJDIR)/regexp.o \
   200    299    $(OBJDIR)/report.o \
   201    300    $(OBJDIR)/rss.o \
   202    301    $(OBJDIR)/schema.o \
   203    302    $(OBJDIR)/search.o \
   204    303    $(OBJDIR)/setup.o \
   205    304    $(OBJDIR)/sha1.o \
   206    305    $(OBJDIR)/shun.o \
   207    306    $(OBJDIR)/skins.o \
          307  + $(OBJDIR)/sqlcmd.o \
          308  + $(OBJDIR)/stash.o \
   208    309    $(OBJDIR)/stat.o \
   209    310    $(OBJDIR)/style.o \
   210    311    $(OBJDIR)/sync.o \
   211    312    $(OBJDIR)/tag.o \
          313  + $(OBJDIR)/tar.o \
   212    314    $(OBJDIR)/th_main.o \
   213    315    $(OBJDIR)/timeline.o \
   214    316    $(OBJDIR)/tkt.o \
   215    317    $(OBJDIR)/tktsetup.o \
   216    318    $(OBJDIR)/undo.o \
          319  + $(OBJDIR)/unicode.o \
   217    320    $(OBJDIR)/update.o \
   218    321    $(OBJDIR)/url.o \
   219    322    $(OBJDIR)/user.o \
          323  + $(OBJDIR)/utf8.o \
   220    324    $(OBJDIR)/verify.o \
   221    325    $(OBJDIR)/vfile.o \
   222    326    $(OBJDIR)/wiki.o \
   223    327    $(OBJDIR)/wikiformat.o \
   224    328    $(OBJDIR)/winhttp.o \
          329  + $(OBJDIR)/wysiwyg.o \
   225    330    $(OBJDIR)/xfer.o \
          331  + $(OBJDIR)/xfersetup.o \
   226    332    $(OBJDIR)/zip.o
   227    333   
   228    334   APPNAME = fossil$(E)
   229    335   
   230    336   
   231    337   
   232    338   all:	$(OBJDIR) $(APPNAME)
   233    339   
   234    340   install:	$(APPNAME)
          341  +	mkdir -p $(INSTALLDIR)
   235    342   	mv $(APPNAME) $(INSTALLDIR)
   236    343   
   237    344   $(OBJDIR):
   238    345   	-mkdir $(OBJDIR)
   239    346   
   240         -translate:	$(SRCDIR)/translate.c
   241         -	$(BCC) -o translate $(SRCDIR)/translate.c
          347  +$(OBJDIR)/translate:	$(SRCDIR)/translate.c
          348  +	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
   242    349   
   243         -makeheaders:	$(SRCDIR)/makeheaders.c
   244         -	$(BCC) -o makeheaders $(SRCDIR)/makeheaders.c
          350  +$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
          351  +	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
   245    352   
   246         -mkindex:	$(SRCDIR)/mkindex.c
   247         -	$(BCC) -o mkindex $(SRCDIR)/mkindex.c
          353  +$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
          354  +	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
   248    355   
   249         -# WARNING. DANGER. Running the testsuite modifies the repository the
          356  +$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
          357  +	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
          358  +
          359  +# WARNING. DANGER. Running the test suite modifies the repository the
   250    360   # build is done from, i.e. the checkout belongs to. Do not sync/push
   251    361   # the repository after running the tests.
   252         -test:	$(APPNAME)
   253         -	$(TCLSH) test/tester.tcl $(APPNAME)
          362  +test:	$(OBJDIR) $(APPNAME)
          363  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
   254    364   
   255         -VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest
   256         -	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}'  $(SRCDIR)/../manifest.uuid >VERSION.h
   257         -	awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}'  $(SRCDIR)/../manifest.uuid >>VERSION.h
   258         -	awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}'  $(SRCDIR)/../manifest >>VERSION.h
          365  +$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
          366  +	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid  $(SRCDIR)/../manifest  $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
   259    367   
   260         -$(APPNAME):	headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
   261         -	$(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB)
          368  +# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
          369  +# to 1. If it is set to 1, then there is no need to build or link
          370  +# the sqlite3.o object. Instead, the system sqlite will be linked
          371  +# using -lsqlite3.
          372  +SQLITE3_OBJ.1 = 
          373  +SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
          374  +SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)
          375  +
          376  +# The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
          377  +# If it is set to 1, then we need to build the Tcl integration code and
          378  +# link to the Tcl library.
          379  +TCL_OBJ.0 = 
          380  +TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
          381  +TCL_OBJ. = $(TCL_OBJ.0)
          382  +
          383  +EXTRAOBJ =  $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE))  $(OBJDIR)/shell.o  $(OBJDIR)/th.o  $(OBJDIR)/th_lang.o  $(TCL_OBJ.$(FOSSIL_ENABLE_TCL))  $(OBJDIR)/cson_amalgamation.o
          384  +
          385  +$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
          386  +	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
   262    387   
   263    388   # This rule prevents make from using its default rules to try build
   264    389   # an executable named "manifest" out of the file named "manifest.c"
   265    390   #
   266    391   $(SRCDIR)/../manifest:	
   267    392   	# noop
   268    393   
   269    394   clean:	
   270         -	rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
   271         -	rm -f translate makeheaders mkindex page_index.h headers
   272         -	rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
          395  +	rm -rf $(OBJDIR)/* $(APPNAME)
   273    396   
   274         -page_index.h: $(TRANS_SRC) mkindex
   275         -	./mkindex $(TRANS_SRC) >$@
   276         -headers:	page_index.h makeheaders VERSION.h
   277         -	./makeheaders  add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
   278         -	touch headers
   279         -headers: Makefile
          397  +
          398  +$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
          399  +	$(OBJDIR)/mkindex $(TRANS_SRC) >$@
          400  +$(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
          401  +	$(OBJDIR)/makeheaders  $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
          402  +	touch $(OBJDIR)/headers
          403  +$(OBJDIR)/headers: Makefile
          404  +$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
   280    405   Makefile:
   281         -add_.c:	$(SRCDIR)/add.c translate
   282         -	./translate $(SRCDIR)/add.c >add_.c
   283         -
   284         -$(OBJDIR)/add.o:	add_.c add.h  $(SRCDIR)/config.h
   285         -	$(XTCC) -o $(OBJDIR)/add.o -c add_.c
   286         -
   287         -add.h:	headers
   288         -allrepo_.c:	$(SRCDIR)/allrepo.c translate
   289         -	./translate $(SRCDIR)/allrepo.c >allrepo_.c
   290         -
   291         -$(OBJDIR)/allrepo.o:	allrepo_.c allrepo.h  $(SRCDIR)/config.h
   292         -	$(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
   293         -
   294         -allrepo.h:	headers
   295         -attach_.c:	$(SRCDIR)/attach.c translate
   296         -	./translate $(SRCDIR)/attach.c >attach_.c
   297         -
   298         -$(OBJDIR)/attach.o:	attach_.c attach.h  $(SRCDIR)/config.h
   299         -	$(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
   300         -
   301         -attach.h:	headers
   302         -bag_.c:	$(SRCDIR)/bag.c translate
   303         -	./translate $(SRCDIR)/bag.c >bag_.c
   304         -
   305         -$(OBJDIR)/bag.o:	bag_.c bag.h  $(SRCDIR)/config.h
   306         -	$(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
   307         -
   308         -bag.h:	headers
   309         -blob_.c:	$(SRCDIR)/blob.c translate
   310         -	./translate $(SRCDIR)/blob.c >blob_.c
   311         -
   312         -$(OBJDIR)/blob.o:	blob_.c blob.h  $(SRCDIR)/config.h
   313         -	$(XTCC) -o $(OBJDIR)/blob.o -c blob_.c
   314         -
   315         -blob.h:	headers
   316         -branch_.c:	$(SRCDIR)/branch.c translate
   317         -	./translate $(SRCDIR)/branch.c >branch_.c
   318         -
   319         -$(OBJDIR)/branch.o:	branch_.c branch.h  $(SRCDIR)/config.h
   320         -	$(XTCC) -o $(OBJDIR)/branch.o -c branch_.c
   321         -
   322         -branch.h:	headers
   323         -browse_.c:	$(SRCDIR)/browse.c translate
   324         -	./translate $(SRCDIR)/browse.c >browse_.c
   325         -
   326         -$(OBJDIR)/browse.o:	browse_.c browse.h  $(SRCDIR)/config.h
   327         -	$(XTCC) -o $(OBJDIR)/browse.o -c browse_.c
   328         -
   329         -browse.h:	headers
   330         -captcha_.c:	$(SRCDIR)/captcha.c translate
   331         -	./translate $(SRCDIR)/captcha.c >captcha_.c
   332         -
   333         -$(OBJDIR)/captcha.o:	captcha_.c captcha.h  $(SRCDIR)/config.h
   334         -	$(XTCC) -o $(OBJDIR)/captcha.o -c captcha_.c
   335         -
   336         -captcha.h:	headers
   337         -cgi_.c:	$(SRCDIR)/cgi.c translate
   338         -	./translate $(SRCDIR)/cgi.c >cgi_.c
   339         -
   340         -$(OBJDIR)/cgi.o:	cgi_.c cgi.h  $(SRCDIR)/config.h
   341         -	$(XTCC) -o $(OBJDIR)/cgi.o -c cgi_.c
          406  +$(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
          407  +	$(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c
          408  +
          409  +$(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h  $(SRCDIR)/config.h
          410  +	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
          411  +
          412  +$(OBJDIR)/add.h:	$(OBJDIR)/headers
          413  +$(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(OBJDIR)/translate
          414  +	$(OBJDIR)/translate $(SRCDIR)/allrepo.c >$(OBJDIR)/allrepo_.c
          415  +
          416  +$(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h  $(SRCDIR)/config.h
          417  +	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
          418  +
          419  +$(OBJDIR)/allrepo.h:	$(OBJDIR)/headers
          420  +$(OBJDIR)/attach_.c:	$(SRCDIR)/attach.c $(OBJDIR)/translate
          421  +	$(OBJDIR)/translate $(SRCDIR)/attach.c >$(OBJDIR)/attach_.c
          422  +
          423  +$(OBJDIR)/attach.o:	$(OBJDIR)/attach_.c $(OBJDIR)/attach.h  $(SRCDIR)/config.h
          424  +	$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
          425  +
          426  +$(OBJDIR)/attach.h:	$(OBJDIR)/headers
          427  +$(OBJDIR)/bag_.c:	$(SRCDIR)/bag.c $(OBJDIR)/translate
          428  +	$(OBJDIR)/translate $(SRCDIR)/bag.c >$(OBJDIR)/bag_.c
          429  +
          430  +$(OBJDIR)/bag.o:	$(OBJDIR)/bag_.c $(OBJDIR)/bag.h  $(SRCDIR)/config.h
          431  +	$(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c
          432  +
          433  +$(OBJDIR)/bag.h:	$(OBJDIR)/headers
          434  +$(OBJDIR)/bisect_.c:	$(SRCDIR)/bisect.c $(OBJDIR)/translate
          435  +	$(OBJDIR)/translate $(SRCDIR)/bisect.c >$(OBJDIR)/bisect_.c
          436  +
          437  +$(OBJDIR)/bisect.o:	$(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h  $(SRCDIR)/config.h
          438  +	$(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c
          439  +
          440  +$(OBJDIR)/bisect.h:	$(OBJDIR)/headers
          441  +$(OBJDIR)/blob_.c:	$(SRCDIR)/blob.c $(OBJDIR)/translate
          442  +	$(OBJDIR)/translate $(SRCDIR)/blob.c >$(OBJDIR)/blob_.c
          443  +
          444  +$(OBJDIR)/blob.o:	$(OBJDIR)/blob_.c $(OBJDIR)/blob.h  $(SRCDIR)/config.h
          445  +	$(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c
          446  +
          447  +$(OBJDIR)/blob.h:	$(OBJDIR)/headers
          448  +$(OBJDIR)/branch_.c:	$(SRCDIR)/branch.c $(OBJDIR)/translate
          449  +	$(OBJDIR)/translate $(SRCDIR)/branch.c >$(OBJDIR)/branch_.c
          450  +
          451  +$(OBJDIR)/branch.o:	$(OBJDIR)/branch_.c $(OBJDIR)/branch.h  $(SRCDIR)/config.h
          452  +	$(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c
          453  +
          454  +$(OBJDIR)/branch.h:	$(OBJDIR)/headers
          455  +$(OBJDIR)/browse_.c:	$(SRCDIR)/browse.c $(OBJDIR)/translate
          456  +	$(OBJDIR)/translate $(SRCDIR)/browse.c >$(OBJDIR)/browse_.c
          457  +
          458  +$(OBJDIR)/browse.o:	$(OBJDIR)/browse_.c $(OBJDIR)/browse.h  $(SRCDIR)/config.h
          459  +	$(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c
          460  +
          461  +$(OBJDIR)/browse.h:	$(OBJDIR)/headers
          462  +$(OBJDIR)/captcha_.c:	$(SRCDIR)/captcha.c $(OBJDIR)/translate
          463  +	$(OBJDIR)/translate $(SRCDIR)/captcha.c >$(OBJDIR)/captcha_.c
          464  +
          465  +$(OBJDIR)/captcha.o:	$(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h  $(SRCDIR)/config.h
          466  +	$(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c
          467  +
          468  +$(OBJDIR)/captcha.h:	$(OBJDIR)/headers
          469  +$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(OBJDIR)/translate
          470  +	$(OBJDIR)/translate $(SRCDIR)/cgi.c >$(OBJDIR)/cgi_.c
          471  +
          472  +$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h  $(SRCDIR)/config.h
          473  +	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
          474  +
          475  +$(OBJDIR)/cgi.h:	$(OBJDIR)/headers
          476  +$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(OBJDIR)/translate
          477  +	$(OBJDIR)/translate $(SRCDIR)/checkin.c >$(OBJDIR)/checkin_.c
          478  +
          479  +$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h  $(SRCDIR)/config.h
          480  +	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c
          481  +
          482  +$(OBJDIR)/checkin.h:	$(OBJDIR)/headers
          483  +$(OBJDIR)/checkout_.c:	$(SRCDIR)/checkout.c $(OBJDIR)/translate
          484  +	$(OBJDIR)/translate $(SRCDIR)/checkout.c >$(OBJDIR)/checkout_.c
          485  +
          486  +$(OBJDIR)/checkout.o:	$(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h  $(SRCDIR)/config.h
          487  +	$(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c
          488  +
          489  +$(OBJDIR)/checkout.h:	$(OBJDIR)/headers
          490  +$(OBJDIR)/clearsign_.c:	$(SRCDIR)/clearsign.c $(OBJDIR)/translate
          491  +	$(OBJDIR)/translate $(SRCDIR)/clearsign.c >$(OBJDIR)/clearsign_.c
          492  +
          493  +$(OBJDIR)/clearsign.o:	$(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h  $(SRCDIR)/config.h
          494  +	$(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c
          495  +
          496  +$(OBJDIR)/clearsign.h:	$(OBJDIR)/headers
          497  +$(OBJDIR)/clone_.c:	$(SRCDIR)/clone.c $(OBJDIR)/translate
          498  +	$(OBJDIR)/translate $(SRCDIR)/clone.c >$(OBJDIR)/clone_.c
          499  +
          500  +$(OBJDIR)/clone.o:	$(OBJDIR)/clone_.c $(OBJDIR)/clone.h  $(SRCDIR)/config.h
          501  +	$(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
          502  +
          503  +$(OBJDIR)/clone.h:	$(OBJDIR)/headers
          504  +$(OBJDIR)/comformat_.c:	$(SRCDIR)/comformat.c $(OBJDIR)/translate
          505  +	$(OBJDIR)/translate $(SRCDIR)/comformat.c >$(OBJDIR)/comformat_.c
          506  +
          507  +$(OBJDIR)/comformat.o:	$(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h  $(SRCDIR)/config.h
          508  +	$(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c
          509  +
          510  +$(OBJDIR)/comformat.h:	$(OBJDIR)/headers
          511  +$(OBJDIR)/configure_.c:	$(SRCDIR)/configure.c $(OBJDIR)/translate
          512  +	$(OBJDIR)/translate $(SRCDIR)/configure.c >$(OBJDIR)/configure_.c
          513  +
          514  +$(OBJDIR)/configure.o:	$(OBJDIR)/configure_.c $(OBJDIR)/configure.h  $(SRCDIR)/config.h
          515  +	$(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c
          516  +
          517  +$(OBJDIR)/configure.h:	$(OBJDIR)/headers
          518  +$(OBJDIR)/content_.c:	$(SRCDIR)/content.c $(OBJDIR)/translate
          519  +	$(OBJDIR)/translate $(SRCDIR)/content.c >$(OBJDIR)/content_.c
          520  +
          521  +$(OBJDIR)/content.o:	$(OBJDIR)/content_.c $(OBJDIR)/content.h  $(SRCDIR)/config.h
          522  +	$(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c
          523  +
          524  +$(OBJDIR)/content.h:	$(OBJDIR)/headers
          525  +$(OBJDIR)/db_.c:	$(SRCDIR)/db.c $(OBJDIR)/translate
          526  +	$(OBJDIR)/translate $(SRCDIR)/db.c >$(OBJDIR)/db_.c
          527  +
          528  +$(OBJDIR)/db.o:	$(OBJDIR)/db_.c $(OBJDIR)/db.h  $(SRCDIR)/config.h
          529  +	$(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c
          530  +
          531  +$(OBJDIR)/db.h:	$(OBJDIR)/headers
          532  +$(OBJDIR)/delta_.c:	$(SRCDIR)/delta.c $(OBJDIR)/translate
          533  +	$(OBJDIR)/translate $(SRCDIR)/delta.c >$(OBJDIR)/delta_.c
          534  +
          535  +$(OBJDIR)/delta.o:	$(OBJDIR)/delta_.c $(OBJDIR)/delta.h  $(SRCDIR)/config.h
          536  +	$(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c
          537  +
          538  +$(OBJDIR)/delta.h:	$(OBJDIR)/headers
          539  +$(OBJDIR)/deltacmd_.c:	$(SRCDIR)/deltacmd.c $(OBJDIR)/translate
          540  +	$(OBJDIR)/translate $(SRCDIR)/deltacmd.c >$(OBJDIR)/deltacmd_.c
          541  +
          542  +$(OBJDIR)/deltacmd.o:	$(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h  $(SRCDIR)/config.h
          543  +	$(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c
          544  +
          545  +$(OBJDIR)/deltacmd.h:	$(OBJDIR)/headers
          546  +$(OBJDIR)/descendants_.c:	$(SRCDIR)/descendants.c $(OBJDIR)/translate
          547  +	$(OBJDIR)/translate $(SRCDIR)/descendants.c >$(OBJDIR)/descendants_.c
          548  +
          549  +$(OBJDIR)/descendants.o:	$(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h  $(SRCDIR)/config.h
          550  +	$(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c
          551  +
          552  +$(OBJDIR)/descendants.h:	$(OBJDIR)/headers
          553  +$(OBJDIR)/diff_.c:	$(SRCDIR)/diff.c $(OBJDIR)/translate
          554  +	$(OBJDIR)/translate $(SRCDIR)/diff.c >$(OBJDIR)/diff_.c
          555  +
          556  +$(OBJDIR)/diff.o:	$(OBJDIR)/diff_.c $(OBJDIR)/diff.h  $(SRCDIR)/config.h
          557  +	$(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c
          558  +
          559  +$(OBJDIR)/diff.h:	$(OBJDIR)/headers
          560  +$(OBJDIR)/diffcmd_.c:	$(SRCDIR)/diffcmd.c $(OBJDIR)/translate
          561  +	$(OBJDIR)/translate $(SRCDIR)/diffcmd.c >$(OBJDIR)/diffcmd_.c
          562  +
          563  +$(OBJDIR)/diffcmd.o:	$(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h  $(SRCDIR)/config.h
          564  +	$(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c
          565  +
          566  +$(OBJDIR)/diffcmd.h:	$(OBJDIR)/headers
          567  +$(OBJDIR)/doc_.c:	$(SRCDIR)/doc.c $(OBJDIR)/translate
          568  +	$(OBJDIR)/translate $(SRCDIR)/doc.c >$(OBJDIR)/doc_.c
          569  +
          570  +$(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h  $(SRCDIR)/config.h
          571  +	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
          572  +
          573  +$(OBJDIR)/doc.h:	$(OBJDIR)/headers
          574  +$(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(OBJDIR)/translate
          575  +	$(OBJDIR)/translate $(SRCDIR)/encode.c >$(OBJDIR)/encode_.c
          576  +
          577  +$(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h  $(SRCDIR)/config.h
          578  +	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
          579  +
          580  +$(OBJDIR)/encode.h:	$(OBJDIR)/headers
          581  +$(OBJDIR)/event_.c:	$(SRCDIR)/event.c $(OBJDIR)/translate
          582  +	$(OBJDIR)/translate $(SRCDIR)/event.c >$(OBJDIR)/event_.c
          583  +
          584  +$(OBJDIR)/event.o:	$(OBJDIR)/event_.c $(OBJDIR)/event.h  $(SRCDIR)/config.h
          585  +	$(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c
          586  +
          587  +$(OBJDIR)/event.h:	$(OBJDIR)/headers
          588  +$(OBJDIR)/export_.c:	$(SRCDIR)/export.c $(OBJDIR)/translate
          589  +	$(OBJDIR)/translate $(SRCDIR)/export.c >$(OBJDIR)/export_.c
          590  +
          591  +$(OBJDIR)/export.o:	$(OBJDIR)/export_.c $(OBJDIR)/export.h  $(SRCDIR)/config.h
          592  +	$(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c
          593  +
          594  +$(OBJDIR)/export.h:	$(OBJDIR)/headers
          595  +$(OBJDIR)/file_.c:	$(SRCDIR)/file.c $(OBJDIR)/translate
          596  +	$(OBJDIR)/translate $(SRCDIR)/file.c >$(OBJDIR)/file_.c
          597  +
          598  +$(OBJDIR)/file.o:	$(OBJDIR)/file_.c $(OBJDIR)/file.h  $(SRCDIR)/config.h
          599  +	$(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
          600  +
          601  +$(OBJDIR)/file.h:	$(OBJDIR)/headers
          602  +$(OBJDIR)/finfo_.c:	$(SRCDIR)/finfo.c $(OBJDIR)/translate
          603  +	$(OBJDIR)/translate $(SRCDIR)/finfo.c >$(OBJDIR)/finfo_.c
          604  +
          605  +$(OBJDIR)/finfo.o:	$(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h  $(SRCDIR)/config.h
          606  +	$(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c
          607  +
          608  +$(OBJDIR)/finfo.h:	$(OBJDIR)/headers
          609  +$(OBJDIR)/glob_.c:	$(SRCDIR)/glob.c $(OBJDIR)/translate
          610  +	$(OBJDIR)/translate $(SRCDIR)/glob.c >$(OBJDIR)/glob_.c
          611  +
          612  +$(OBJDIR)/glob.o:	$(OBJDIR)/glob_.c $(OBJDIR)/glob.h  $(SRCDIR)/config.h
          613  +	$(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c
          614  +
          615  +$(OBJDIR)/glob.h:	$(OBJDIR)/headers
          616  +$(OBJDIR)/graph_.c:	$(SRCDIR)/graph.c $(OBJDIR)/translate
          617  +	$(OBJDIR)/translate $(SRCDIR)/graph.c >$(OBJDIR)/graph_.c
          618  +
          619  +$(OBJDIR)/graph.o:	$(OBJDIR)/graph_.c $(OBJDIR)/graph.h  $(SRCDIR)/config.h
          620  +	$(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c
          621  +
          622  +$(OBJDIR)/graph.h:	$(OBJDIR)/headers
          623  +$(OBJDIR)/gzip_.c:	$(SRCDIR)/gzip.c $(OBJDIR)/translate
          624  +	$(OBJDIR)/translate $(SRCDIR)/gzip.c >$(OBJDIR)/gzip_.c
          625  +
          626  +$(OBJDIR)/gzip.o:	$(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h  $(SRCDIR)/config.h
          627  +	$(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c
          628  +
          629  +$(OBJDIR)/gzip.h:	$(OBJDIR)/headers
          630  +$(OBJDIR)/http_.c:	$(SRCDIR)/http.c $(OBJDIR)/translate
          631  +	$(OBJDIR)/translate $(SRCDIR)/http.c >$(OBJDIR)/http_.c
          632  +
          633  +$(OBJDIR)/http.o:	$(OBJDIR)/http_.c $(OBJDIR)/http.h  $(SRCDIR)/config.h
          634  +	$(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c
          635  +
          636  +$(OBJDIR)/http.h:	$(OBJDIR)/headers
          637  +$(OBJDIR)/http_socket_.c:	$(SRCDIR)/http_socket.c $(OBJDIR)/translate
          638  +	$(OBJDIR)/translate $(SRCDIR)/http_socket.c >$(OBJDIR)/http_socket_.c
          639  +
          640  +$(OBJDIR)/http_socket.o:	$(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h  $(SRCDIR)/config.h
          641  +	$(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c
          642  +
          643  +$(OBJDIR)/http_socket.h:	$(OBJDIR)/headers
          644  +$(OBJDIR)/http_ssl_.c:	$(SRCDIR)/http_ssl.c $(OBJDIR)/translate
          645  +	$(OBJDIR)/translate $(SRCDIR)/http_ssl.c >$(OBJDIR)/http_ssl_.c
          646  +
          647  +$(OBJDIR)/http_ssl.o:	$(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h  $(SRCDIR)/config.h
          648  +	$(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c
          649  +
          650  +$(OBJDIR)/http_ssl.h:	$(OBJDIR)/headers
          651  +$(OBJDIR)/http_transport_.c:	$(SRCDIR)/http_transport.c $(OBJDIR)/translate
          652  +	$(OBJDIR)/translate $(SRCDIR)/http_transport.c >$(OBJDIR)/http_transport_.c
          653  +
          654  +$(OBJDIR)/http_transport.o:	$(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h  $(SRCDIR)/config.h
          655  +	$(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c
          656  +
          657  +$(OBJDIR)/http_transport.h:	$(OBJDIR)/headers
          658  +$(OBJDIR)/import_.c:	$(SRCDIR)/import.c $(OBJDIR)/translate
          659  +	$(OBJDIR)/translate $(SRCDIR)/import.c >$(OBJDIR)/import_.c
          660  +
          661  +$(OBJDIR)/import.o:	$(OBJDIR)/import_.c $(OBJDIR)/import.h  $(SRCDIR)/config.h
          662  +	$(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c
          663  +
          664  +$(OBJDIR)/import.h:	$(OBJDIR)/headers
          665  +$(OBJDIR)/info_.c:	$(SRCDIR)/info.c $(OBJDIR)/translate
          666  +	$(OBJDIR)/translate $(SRCDIR)/info.c >$(OBJDIR)/info_.c
          667  +
          668  +$(OBJDIR)/info.o:	$(OBJDIR)/info_.c $(OBJDIR)/info.h  $(SRCDIR)/config.h
          669  +	$(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
          670  +
          671  +$(OBJDIR)/info.h:	$(OBJDIR)/headers
          672  +$(OBJDIR)/json_.c:	$(SRCDIR)/json.c $(OBJDIR)/translate
          673  +	$(OBJDIR)/translate $(SRCDIR)/json.c >$(OBJDIR)/json_.c
          674  +
          675  +$(OBJDIR)/json.o:	$(OBJDIR)/json_.c $(OBJDIR)/json.h  $(SRCDIR)/config.h
          676  +	$(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
          677  +
          678  +$(OBJDIR)/json.h:	$(OBJDIR)/headers
          679  +$(OBJDIR)/json_artifact_.c:	$(SRCDIR)/json_artifact.c $(OBJDIR)/translate
          680  +	$(OBJDIR)/translate $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
          681  +
          682  +$(OBJDIR)/json_artifact.o:	$(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h  $(SRCDIR)/config.h
          683  +	$(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
          684  +
          685  +$(OBJDIR)/json_artifact.h:	$(OBJDIR)/headers
          686  +$(OBJDIR)/json_branch_.c:	$(SRCDIR)/json_branch.c $(OBJDIR)/translate
          687  +	$(OBJDIR)/translate $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
          688  +
          689  +$(OBJDIR)/json_branch.o:	$(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h  $(SRCDIR)/config.h
          690  +	$(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
          691  +
          692  +$(OBJDIR)/json_branch.h:	$(OBJDIR)/headers
          693  +$(OBJDIR)/json_config_.c:	$(SRCDIR)/json_config.c $(OBJDIR)/translate
          694  +	$(OBJDIR)/translate $(SRCDIR)/json_config.c >$(OBJDIR)/json_config_.c
          695  +
          696  +$(OBJDIR)/json_config.o:	$(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h  $(SRCDIR)/config.h
          697  +	$(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c
          698  +
          699  +$(OBJDIR)/json_config.h:	$(OBJDIR)/headers
          700  +$(OBJDIR)/json_diff_.c:	$(SRCDIR)/json_diff.c $(OBJDIR)/translate
          701  +	$(OBJDIR)/translate $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
          702  +
          703  +$(OBJDIR)/json_diff.o:	$(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h  $(SRCDIR)/config.h
          704  +	$(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
          705  +
          706  +$(OBJDIR)/json_diff.h:	$(OBJDIR)/headers
          707  +$(OBJDIR)/json_dir_.c:	$(SRCDIR)/json_dir.c $(OBJDIR)/translate
          708  +	$(OBJDIR)/translate $(SRCDIR)/json_dir.c >$(OBJDIR)/json_dir_.c
          709  +
          710  +$(OBJDIR)/json_dir.o:	$(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h  $(SRCDIR)/config.h
          711  +	$(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c
          712  +
          713  +$(OBJDIR)/json_dir.h:	$(OBJDIR)/headers
          714  +$(OBJDIR)/json_finfo_.c:	$(SRCDIR)/json_finfo.c $(OBJDIR)/translate
          715  +	$(OBJDIR)/translate $(SRCDIR)/json_finfo.c >$(OBJDIR)/json_finfo_.c
          716  +
          717  +$(OBJDIR)/json_finfo.o:	$(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h  $(SRCDIR)/config.h
          718  +	$(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c
          719  +
          720  +$(OBJDIR)/json_finfo.h:	$(OBJDIR)/headers
          721  +$(OBJDIR)/json_login_.c:	$(SRCDIR)/json_login.c $(OBJDIR)/translate
          722  +	$(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
          723  +
          724  +$(OBJDIR)/json_login.o:	$(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h  $(SRCDIR)/config.h
          725  +	$(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
          726  +
          727  +$(OBJDIR)/json_login.h:	$(OBJDIR)/headers
          728  +$(OBJDIR)/json_query_.c:	$(SRCDIR)/json_query.c $(OBJDIR)/translate
          729  +	$(OBJDIR)/translate $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
          730  +
          731  +$(OBJDIR)/json_query.o:	$(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h  $(SRCDIR)/config.h
          732  +	$(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
          733  +
          734  +$(OBJDIR)/json_query.h:	$(OBJDIR)/headers
          735  +$(OBJDIR)/json_report_.c:	$(SRCDIR)/json_report.c $(OBJDIR)/translate
          736  +	$(OBJDIR)/translate $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
          737  +
          738  +$(OBJDIR)/json_report.o:	$(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h  $(SRCDIR)/config.h
          739  +	$(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
          740  +
          741  +$(OBJDIR)/json_report.h:	$(OBJDIR)/headers
          742  +$(OBJDIR)/json_tag_.c:	$(SRCDIR)/json_tag.c $(OBJDIR)/translate
          743  +	$(OBJDIR)/translate $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
          744  +
          745  +$(OBJDIR)/json_tag.o:	$(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h  $(SRCDIR)/config.h
          746  +	$(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
          747  +
          748  +$(OBJDIR)/json_tag.h:	$(OBJDIR)/headers
          749  +$(OBJDIR)/json_timeline_.c:	$(SRCDIR)/json_timeline.c $(OBJDIR)/translate
          750  +	$(OBJDIR)/translate $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
          751  +
          752  +$(OBJDIR)/json_timeline.o:	$(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h  $(SRCDIR)/config.h
          753  +	$(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
          754  +
          755  +$(OBJDIR)/json_timeline.h:	$(OBJDIR)/headers
          756  +$(OBJDIR)/json_user_.c:	$(SRCDIR)/json_user.c $(OBJDIR)/translate
          757  +	$(OBJDIR)/translate $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
          758  +
          759  +$(OBJDIR)/json_user.o:	$(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h  $(SRCDIR)/config.h
          760  +	$(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
          761  +
          762  +$(OBJDIR)/json_user.h:	$(OBJDIR)/headers
          763  +$(OBJDIR)/json_wiki_.c:	$(SRCDIR)/json_wiki.c $(OBJDIR)/translate
          764  +	$(OBJDIR)/translate $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
          765  +
          766  +$(OBJDIR)/json_wiki.o:	$(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h  $(SRCDIR)/config.h
          767  +	$(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
          768  +
          769  +$(OBJDIR)/json_wiki.h:	$(OBJDIR)/headers
          770  +$(OBJDIR)/leaf_.c:	$(SRCDIR)/leaf.c $(OBJDIR)/translate
          771  +	$(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
          772  +
          773  +$(OBJDIR)/leaf.o:	$(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h  $(SRCDIR)/config.h
          774  +	$(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
          775  +
          776  +$(OBJDIR)/leaf.h:	$(OBJDIR)/headers
          777  +$(OBJDIR)/login_.c:	$(SRCDIR)/login.c $(OBJDIR)/translate
          778  +	$(OBJDIR)/translate $(SRCDIR)/login.c >$(OBJDIR)/login_.c
          779  +
          780  +$(OBJDIR)/login.o:	$(OBJDIR)/login_.c $(OBJDIR)/login.h  $(SRCDIR)/config.h
          781  +	$(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c
          782  +
          783  +$(OBJDIR)/login.h:	$(OBJDIR)/headers
          784  +$(OBJDIR)/main_.c:	$(SRCDIR)/main.c $(OBJDIR)/translate
          785  +	$(OBJDIR)/translate $(SRCDIR)/main.c >$(OBJDIR)/main_.c
          786  +
          787  +$(OBJDIR)/main.o:	$(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h
          788  +	$(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c
          789  +
          790  +$(OBJDIR)/main.h:	$(OBJDIR)/headers
          791  +$(OBJDIR)/manifest_.c:	$(SRCDIR)/manifest.c $(OBJDIR)/translate
          792  +	$(OBJDIR)/translate $(SRCDIR)/manifest.c >$(OBJDIR)/manifest_.c
          793  +
          794  +$(OBJDIR)/manifest.o:	$(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h  $(SRCDIR)/config.h
          795  +	$(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c
          796  +
          797  +$(OBJDIR)/manifest.h:	$(OBJDIR)/headers
          798  +$(OBJDIR)/markdown_.c:	$(SRCDIR)/markdown.c $(OBJDIR)/translate
          799  +	$(OBJDIR)/translate $(SRCDIR)/markdown.c >$(OBJDIR)/markdown_.c
          800  +
          801  +$(OBJDIR)/markdown.o:	$(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h  $(SRCDIR)/config.h
          802  +	$(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c
          803  +
          804  +$(OBJDIR)/markdown.h:	$(OBJDIR)/headers
          805  +$(OBJDIR)/markdown_html_.c:	$(SRCDIR)/markdown_html.c $(OBJDIR)/translate
          806  +	$(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$(OBJDIR)/markdown_html_.c
          807  +
          808  +$(OBJDIR)/markdown_html.o:	$(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h  $(SRCDIR)/config.h
          809  +	$(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
          810  +
          811  +$(OBJDIR)/markdown_html.h:	$(OBJDIR)/headers
          812  +$(OBJDIR)/md5_.c:	$(SRCDIR)/md5.c $(OBJDIR)/translate
          813  +	$(OBJDIR)/translate $(SRCDIR)/md5.c >$(OBJDIR)/md5_.c
          814  +
          815  +$(OBJDIR)/md5.o:	$(OBJDIR)/md5_.c $(OBJDIR)/md5.h  $(SRCDIR)/config.h
          816  +	$(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c
          817  +
          818  +$(OBJDIR)/md5.h:	$(OBJDIR)/headers
          819  +$(OBJDIR)/merge_.c:	$(SRCDIR)/merge.c $(OBJDIR)/translate
          820  +	$(OBJDIR)/translate $(SRCDIR)/merge.c >$(OBJDIR)/merge_.c
          821  +
          822  +$(OBJDIR)/merge.o:	$(OBJDIR)/merge_.c $(OBJDIR)/merge.h  $(SRCDIR)/config.h
          823  +	$(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c
          824  +
          825  +$(OBJDIR)/merge.h:	$(OBJDIR)/headers
          826  +$(OBJDIR)/merge3_.c:	$(SRCDIR)/merge3.c $(OBJDIR)/translate
          827  +	$(OBJDIR)/translate $(SRCDIR)/merge3.c >$(OBJDIR)/merge3_.c
          828  +
          829  +$(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
          830  +	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
          831  +
          832  +$(OBJDIR)/merge3.h:	$(OBJDIR)/headers
          833  +$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
          834  +	$(OBJDIR)/translate $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
          835  +
          836  +$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
          837  +	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
          838  +
          839  +$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
          840  +$(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
          841  +	$(OBJDIR)/translate $(SRCDIR)/name.c >$(OBJDIR)/name_.c
          842  +
          843  +$(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
          844  +	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c
          845  +
          846  +$(OBJDIR)/name.h:	$(OBJDIR)/headers
          847  +$(OBJDIR)/path_.c:	$(SRCDIR)/path.c $(OBJDIR)/translate
          848  +	$(OBJDIR)/translate $(SRCDIR)/path.c >$(OBJDIR)/path_.c
          849  +
          850  +$(OBJDIR)/path.o:	$(OBJDIR)/path_.c $(OBJDIR)/path.h  $(SRCDIR)/config.h
          851  +	$(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c
          852  +
          853  +$(OBJDIR)/path.h:	$(OBJDIR)/headers
          854  +$(OBJDIR)/pivot_.c:	$(SRCDIR)/pivot.c $(OBJDIR)/translate
          855  +	$(OBJDIR)/translate $(SRCDIR)/pivot.c >$(OBJDIR)/pivot_.c
          856  +
          857  +$(OBJDIR)/pivot.o:	$(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h  $(SRCDIR)/config.h
          858  +	$(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c
          859  +
          860  +$(OBJDIR)/pivot.h:	$(OBJDIR)/headers
          861  +$(OBJDIR)/popen_.c:	$(SRCDIR)/popen.c $(OBJDIR)/translate
          862  +	$(OBJDIR)/translate $(SRCDIR)/popen.c >$(OBJDIR)/popen_.c
          863  +
          864  +$(OBJDIR)/popen.o:	$(OBJDIR)/popen_.c $(OBJDIR)/popen.h  $(SRCDIR)/config.h
          865  +	$(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c
          866  +
          867  +$(OBJDIR)/popen.h:	$(OBJDIR)/headers
          868  +$(OBJDIR)/pqueue_.c:	$(SRCDIR)/pqueue.c $(OBJDIR)/translate
          869  +	$(OBJDIR)/translate $(SRCDIR)/pqueue.c >$(OBJDIR)/pqueue_.c
          870  +
          871  +$(OBJDIR)/pqueue.o:	$(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h  $(SRCDIR)/config.h
          872  +	$(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c
          873  +
          874  +$(OBJDIR)/pqueue.h:	$(OBJDIR)/headers
          875  +$(OBJDIR)/printf_.c:	$(SRCDIR)/printf.c $(OBJDIR)/translate
          876  +	$(OBJDIR)/translate $(SRCDIR)/printf.c >$(OBJDIR)/printf_.c
          877  +
          878  +$(OBJDIR)/printf.o:	$(OBJDIR)/printf_.c $(OBJDIR)/printf.h  $(SRCDIR)/config.h
          879  +	$(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c
          880  +
          881  +$(OBJDIR)/printf.h:	$(OBJDIR)/headers
          882  +$(OBJDIR)/rebuild_.c:	$(SRCDIR)/rebuild.c $(OBJDIR)/translate
          883  +	$(OBJDIR)/translate $(SRCDIR)/rebuild.c >$(OBJDIR)/rebuild_.c
          884  +
          885  +$(OBJDIR)/rebuild.o:	$(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h  $(SRCDIR)/config.h
          886  +	$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
          887  +
          888  +$(OBJDIR)/rebuild.h:	$(OBJDIR)/headers
          889  +$(OBJDIR)/regexp_.c:	$(SRCDIR)/regexp.c $(OBJDIR)/translate
          890  +	$(OBJDIR)/translate $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
          891  +
          892  +$(OBJDIR)/regexp.o:	$(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h  $(SRCDIR)/config.h
          893  +	$(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
          894  +
          895  +$(OBJDIR)/regexp.h:	$(OBJDIR)/headers
          896  +$(OBJDIR)/report_.c:	$(SRCDIR)/report.c $(OBJDIR)/translate
          897  +	$(OBJDIR)/translate $(SRCDIR)/report.c >$(OBJDIR)/report_.c
          898  +
          899  +$(OBJDIR)/report.o:	$(OBJDIR)/report_.c $(OBJDIR)/report.h  $(SRCDIR)/config.h
          900  +	$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
          901  +
          902  +$(OBJDIR)/report.h:	$(OBJDIR)/headers
          903  +$(OBJDIR)/rss_.c:	$(SRCDIR)/rss.c $(OBJDIR)/translate
          904  +	$(OBJDIR)/translate $(SRCDIR)/rss.c >$(OBJDIR)/rss_.c
          905  +
          906  +$(OBJDIR)/rss.o:	$(OBJDIR)/rss_.c $(OBJDIR)/rss.h  $(SRCDIR)/config.h
          907  +	$(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c
          908  +
          909  +$(OBJDIR)/rss.h:	$(OBJDIR)/headers
          910  +$(OBJDIR)/schema_.c:	$(SRCDIR)/schema.c $(OBJDIR)/translate
          911  +	$(OBJDIR)/translate $(SRCDIR)/schema.c >$(OBJDIR)/schema_.c
          912  +
          913  +$(OBJDIR)/schema.o:	$(OBJDIR)/schema_.c $(OBJDIR)/schema.h  $(SRCDIR)/config.h
          914  +	$(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c
          915  +
          916  +$(OBJDIR)/schema.h:	$(OBJDIR)/headers
          917  +$(OBJDIR)/search_.c:	$(SRCDIR)/search.c $(OBJDIR)/translate
          918  +	$(OBJDIR)/translate $(SRCDIR)/search.c >$(OBJDIR)/search_.c
          919  +
          920  +$(OBJDIR)/search.o:	$(OBJDIR)/search_.c $(OBJDIR)/search.h  $(SRCDIR)/config.h
          921  +	$(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c
          922  +
          923  +$(OBJDIR)/search.h:	$(OBJDIR)/headers
          924  +$(OBJDIR)/setup_.c:	$(SRCDIR)/setup.c $(OBJDIR)/translate
          925  +	$(OBJDIR)/translate $(SRCDIR)/setup.c >$(OBJDIR)/setup_.c
          926  +
          927  +$(OBJDIR)/setup.o:	$(OBJDIR)/setup_.c $(OBJDIR)/setup.h  $(SRCDIR)/config.h
          928  +	$(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c
          929  +
          930  +$(OBJDIR)/setup.h:	$(OBJDIR)/headers
          931  +$(OBJDIR)/sha1_.c:	$(SRCDIR)/sha1.c $(OBJDIR)/translate
          932  +	$(OBJDIR)/translate $(SRCDIR)/sha1.c >$(OBJDIR)/sha1_.c
          933  +
          934  +$(OBJDIR)/sha1.o:	$(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h  $(SRCDIR)/config.h
          935  +	$(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c
          936  +
          937  +$(OBJDIR)/sha1.h:	$(OBJDIR)/headers
          938  +$(OBJDIR)/shun_.c:	$(SRCDIR)/shun.c $(OBJDIR)/translate
          939  +	$(OBJDIR)/translate $(SRCDIR)/shun.c >$(OBJDIR)/shun_.c
          940  +
          941  +$(OBJDIR)/shun.o:	$(OBJDIR)/shun_.c $(OBJDIR)/shun.h  $(SRCDIR)/config.h
          942  +	$(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c
          943  +
          944  +$(OBJDIR)/shun.h:	$(OBJDIR)/headers
          945  +$(OBJDIR)/skins_.c:	$(SRCDIR)/skins.c $(OBJDIR)/translate
          946  +	$(OBJDIR)/translate $(SRCDIR)/skins.c >$(OBJDIR)/skins_.c
          947  +
          948  +$(OBJDIR)/skins.o:	$(OBJDIR)/skins_.c $(OBJDIR)/skins.h  $(SRCDIR)/config.h
          949  +	$(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c
          950  +
          951  +$(OBJDIR)/skins.h:	$(OBJDIR)/headers
          952  +$(OBJDIR)/sqlcmd_.c:	$(SRCDIR)/sqlcmd.c $(OBJDIR)/translate
          953  +	$(OBJDIR)/translate $(SRCDIR)/sqlcmd.c >$(OBJDIR)/sqlcmd_.c
          954  +
          955  +$(OBJDIR)/sqlcmd.o:	$(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h  $(SRCDIR)/config.h
          956  +	$(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c
          957  +
          958  +$(OBJDIR)/sqlcmd.h:	$(OBJDIR)/headers
          959  +$(OBJDIR)/stash_.c:	$(SRCDIR)/stash.c $(OBJDIR)/translate
          960  +	$(OBJDIR)/translate $(SRCDIR)/stash.c >$(OBJDIR)/stash_.c
          961  +
          962  +$(OBJDIR)/stash.o:	$(OBJDIR)/stash_.c $(OBJDIR)/stash.h  $(SRCDIR)/config.h
          963  +	$(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c
          964  +
          965  +$(OBJDIR)/stash.h:	$(OBJDIR)/headers
          966  +$(OBJDIR)/stat_.c:	$(SRCDIR)/stat.c $(OBJDIR)/translate
          967  +	$(OBJDIR)/translate $(SRCDIR)/stat.c >$(OBJDIR)/stat_.c
          968  +
          969  +$(OBJDIR)/stat.o:	$(OBJDIR)/stat_.c $(OBJDIR)/stat.h  $(SRCDIR)/config.h
          970  +	$(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c
          971  +
          972  +$(OBJDIR)/stat.h:	$(OBJDIR)/headers
          973  +$(OBJDIR)/style_.c:	$(SRCDIR)/style.c $(OBJDIR)/translate
          974  +	$(OBJDIR)/translate $(SRCDIR)/style.c >$(OBJDIR)/style_.c
          975  +
          976  +$(OBJDIR)/style.o:	$(OBJDIR)/style_.c $(OBJDIR)/style.h  $(SRCDIR)/config.h
          977  +	$(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c
          978  +
          979  +$(OBJDIR)/style.h:	$(OBJDIR)/headers
          980  +$(OBJDIR)/sync_.c:	$(SRCDIR)/sync.c $(OBJDIR)/translate
          981  +	$(OBJDIR)/translate $(SRCDIR)/sync.c >$(OBJDIR)/sync_.c
          982  +
          983  +$(OBJDIR)/sync.o:	$(OBJDIR)/sync_.c $(OBJDIR)/sync.h  $(SRCDIR)/config.h
          984  +	$(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c
          985  +
          986  +$(OBJDIR)/sync.h:	$(OBJDIR)/headers
          987  +$(OBJDIR)/tag_.c:	$(SRCDIR)/tag.c $(OBJDIR)/translate
          988  +	$(OBJDIR)/translate $(SRCDIR)/tag.c >$(OBJDIR)/tag_.c
          989  +
          990  +$(OBJDIR)/tag.o:	$(OBJDIR)/tag_.c $(OBJDIR)/tag.h  $(SRCDIR)/config.h
          991  +	$(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c
          992  +
          993  +$(OBJDIR)/tag.h:	$(OBJDIR)/headers
          994  +$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(OBJDIR)/translate
          995  +	$(OBJDIR)/translate $(SRCDIR)/tar.c >$(OBJDIR)/tar_.c
          996  +
          997  +$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h  $(SRCDIR)/config.h
          998  +	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
          999  +
         1000  +$(OBJDIR)/tar.h:	$(OBJDIR)/headers
         1001  +$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(OBJDIR)/translate
         1002  +	$(OBJDIR)/translate $(SRCDIR)/th_main.c >$(OBJDIR)/th_main_.c
         1003  +
         1004  +$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h  $(SRCDIR)/config.h
         1005  +	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c
         1006  +
         1007  +$(OBJDIR)/th_main.h:	$(OBJDIR)/headers
         1008  +$(OBJDIR)/timeline_.c:	$(SRCDIR)/timeline.c $(OBJDIR)/translate
         1009  +	$(OBJDIR)/translate $(SRCDIR)/timeline.c >$(OBJDIR)/timeline_.c
         1010  +
         1011  +$(OBJDIR)/timeline.o:	$(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h  $(SRCDIR)/config.h
         1012  +	$(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c
         1013  +
         1014  +$(OBJDIR)/timeline.h:	$(OBJDIR)/headers
         1015  +$(OBJDIR)/tkt_.c:	$(SRCDIR)/tkt.c $(OBJDIR)/translate
         1016  +	$(OBJDIR)/translate $(SRCDIR)/tkt.c >$(OBJDIR)/tkt_.c
         1017  +
         1018  +$(OBJDIR)/tkt.o:	$(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h  $(SRCDIR)/config.h
         1019  +	$(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c
         1020  +
         1021  +$(OBJDIR)/tkt.h:	$(OBJDIR)/headers
         1022  +$(OBJDIR)/tktsetup_.c:	$(SRCDIR)/tktsetup.c $(OBJDIR)/translate
         1023  +	$(OBJDIR)/translate $(SRCDIR)/tktsetup.c >$(OBJDIR)/tktsetup_.c
         1024  +
         1025  +$(OBJDIR)/tktsetup.o:	$(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h  $(SRCDIR)/config.h
         1026  +	$(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c
         1027  +
         1028  +$(OBJDIR)/tktsetup.h:	$(OBJDIR)/headers
         1029  +$(OBJDIR)/undo_.c:	$(SRCDIR)/undo.c $(OBJDIR)/translate
         1030  +	$(OBJDIR)/translate $(SRCDIR)/undo.c >$(OBJDIR)/undo_.c
         1031  +
         1032  +$(OBJDIR)/undo.o:	$(OBJDIR)/undo_.c $(OBJDIR)/undo.h  $(SRCDIR)/config.h
         1033  +	$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
         1034  +
         1035  +$(OBJDIR)/undo.h:	$(OBJDIR)/headers
         1036  +$(OBJDIR)/unicode_.c:	$(SRCDIR)/unicode.c $(OBJDIR)/translate
         1037  +	$(OBJDIR)/translate $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
         1038  +
         1039  +$(OBJDIR)/unicode.o:	$(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h  $(SRCDIR)/config.h
         1040  +	$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
         1041  +
         1042  +$(OBJDIR)/unicode.h:	$(OBJDIR)/headers
         1043  +$(OBJDIR)/update_.c:	$(SRCDIR)/update.c $(OBJDIR)/translate
         1044  +	$(OBJDIR)/translate $(SRCDIR)/update.c >$(OBJDIR)/update_.c
         1045  +
         1046  +$(OBJDIR)/update.o:	$(OBJDIR)/update_.c $(OBJDIR)/update.h  $(SRCDIR)/config.h
         1047  +	$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
         1048  +
         1049  +$(OBJDIR)/update.h:	$(OBJDIR)/headers
         1050  +$(OBJDIR)/url_.c:	$(SRCDIR)/url.c $(OBJDIR)/translate
         1051  +	$(OBJDIR)/translate $(SRCDIR)/url.c >$(OBJDIR)/url_.c
         1052  +
         1053  +$(OBJDIR)/url.o:	$(OBJDIR)/url_.c $(OBJDIR)/url.h  $(SRCDIR)/config.h
         1054  +	$(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c
         1055  +
         1056  +$(OBJDIR)/url.h:	$(OBJDIR)/headers
         1057  +$(OBJDIR)/user_.c:	$(SRCDIR)/user.c $(OBJDIR)/translate
         1058  +	$(OBJDIR)/translate $(SRCDIR)/user.c >$(OBJDIR)/user_.c
         1059  +
         1060  +$(OBJDIR)/user.o:	$(OBJDIR)/user_.c $(OBJDIR)/user.h  $(SRCDIR)/config.h
         1061  +	$(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c
         1062  +
         1063  +$(OBJDIR)/user.h:	$(OBJDIR)/headers
         1064  +$(OBJDIR)/utf8_.c:	$(SRCDIR)/utf8.c $(OBJDIR)/translate
         1065  +	$(OBJDIR)/translate $(SRCDIR)/utf8.c >$(OBJDIR)/utf8_.c
         1066  +
         1067  +$(OBJDIR)/utf8.o:	$(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h  $(SRCDIR)/config.h
         1068  +	$(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c
         1069  +
         1070  +$(OBJDIR)/utf8.h:	$(OBJDIR)/headers
         1071  +$(OBJDIR)/verify_.c:	$(SRCDIR)/verify.c $(OBJDIR)/translate
         1072  +	$(OBJDIR)/translate $(SRCDIR)/verify.c >$(OBJDIR)/verify_.c
         1073  +
         1074  +$(OBJDIR)/verify.o:	$(OBJDIR)/verify_.c $(OBJDIR)/verify.h  $(SRCDIR)/config.h
         1075  +	$(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c
         1076  +
         1077  +$(OBJDIR)/verify.h:	$(OBJDIR)/headers
         1078  +$(OBJDIR)/vfile_.c:	$(SRCDIR)/vfile.c $(OBJDIR)/translate
         1079  +	$(OBJDIR)/translate $(SRCDIR)/vfile.c >$(OBJDIR)/vfile_.c
         1080  +
         1081  +$(OBJDIR)/vfile.o:	$(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h  $(SRCDIR)/config.h
         1082  +	$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
         1083  +
         1084  +$(OBJDIR)/vfile.h:	$(OBJDIR)/headers
         1085  +$(OBJDIR)/wiki_.c:	$(SRCDIR)/wiki.c $(OBJDIR)/translate
         1086  +	$(OBJDIR)/translate $(SRCDIR)/wiki.c >$(OBJDIR)/wiki_.c
         1087  +
         1088  +$(OBJDIR)/wiki.o:	$(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h  $(SRCDIR)/config.h
         1089  +	$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
         1090  +
         1091  +$(OBJDIR)/wiki.h:	$(OBJDIR)/headers
         1092  +$(OBJDIR)/wikiformat_.c:	$(SRCDIR)/wikiformat.c $(OBJDIR)/translate
         1093  +	$(OBJDIR)/translate $(SRCDIR)/wikiformat.c >$(OBJDIR)/wikiformat_.c
         1094  +
         1095  +$(OBJDIR)/wikiformat.o:	$(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h  $(SRCDIR)/config.h
         1096  +	$(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c
         1097  +
         1098  +$(OBJDIR)/wikiformat.h:	$(OBJDIR)/headers
         1099  +$(OBJDIR)/winhttp_.c:	$(SRCDIR)/winhttp.c $(OBJDIR)/translate
         1100  +	$(OBJDIR)/translate $(SRCDIR)/winhttp.c >$(OBJDIR)/winhttp_.c
         1101  +
         1102  +$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h  $(SRCDIR)/config.h
         1103  +	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c
         1104  +
         1105  +$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers
         1106  +$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(OBJDIR)/translate
         1107  +	$(OBJDIR)/translate $(SRCDIR)/wysiwyg.c >$(OBJDIR)/wysiwyg_.c
         1108  +
         1109  +$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h  $(SRCDIR)/config.h
         1110  +	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c
         1111  +
         1112  +$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers
         1113  +$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(OBJDIR)/translate
         1114  +	$(OBJDIR)/translate $(SRCDIR)/xfer.c >$(OBJDIR)/xfer_.c
         1115  +
         1116  +$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h  $(SRCDIR)/config.h
         1117  +	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c
         1118  +
         1119  +$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
         1120  +$(OBJDIR)/xfersetup_.c:	$(SRCDIR)/xfersetup.c $(OBJDIR)/translate
         1121  +	$(OBJDIR)/translate $(SRCDIR)/xfersetup.c >$(OBJDIR)/xfersetup_.c
         1122  +
         1123  +$(OBJDIR)/xfersetup.o:	$(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h  $(SRCDIR)/config.h
         1124  +	$(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c
   342   1125   
   343         -cgi.h:	headers
   344         -checkin_.c:	$(SRCDIR)/checkin.c translate
   345         -	./translate $(SRCDIR)/checkin.c >checkin_.c
   346         -
   347         -$(OBJDIR)/checkin.o:	checkin_.c checkin.h  $(SRCDIR)/config.h
   348         -	$(XTCC) -o $(OBJDIR)/checkin.o -c checkin_.c
   349         -
   350         -checkin.h:	headers
   351         -checkout_.c:	$(SRCDIR)/checkout.c translate
   352         -	./translate $(SRCDIR)/checkout.c >checkout_.c
   353         -
   354         -$(OBJDIR)/checkout.o:	checkout_.c checkout.h  $(SRCDIR)/config.h
   355         -	$(XTCC) -o $(OBJDIR)/checkout.o -c checkout_.c
   356         -
   357         -checkout.h:	headers
   358         -clearsign_.c:	$(SRCDIR)/clearsign.c translate
   359         -	./translate $(SRCDIR)/clearsign.c >clearsign_.c
   360         -
   361         -$(OBJDIR)/clearsign.o:	clearsign_.c clearsign.h  $(SRCDIR)/config.h
   362         -	$(XTCC) -o $(OBJDIR)/clearsign.o -c clearsign_.c
   363         -
   364         -clearsign.h:	headers
   365         -clone_.c:	$(SRCDIR)/clone.c translate
   366         -	./translate $(SRCDIR)/clone.c >clone_.c
   367         -
   368         -$(OBJDIR)/clone.o:	clone_.c clone.h  $(SRCDIR)/config.h
   369         -	$(XTCC) -o $(OBJDIR)/clone.o -c clone_.c
   370         -
   371         -clone.h:	headers
   372         -comformat_.c:	$(SRCDIR)/comformat.c translate
   373         -	./translate $(SRCDIR)/comformat.c >comformat_.c
   374         -
   375         -$(OBJDIR)/comformat.o:	comformat_.c comformat.h  $(SRCDIR)/config.h
   376         -	$(XTCC) -o $(OBJDIR)/comformat.o -c comformat_.c
   377         -
   378         -comformat.h:	headers
   379         -configure_.c:	$(SRCDIR)/configure.c translate
   380         -	./translate $(SRCDIR)/configure.c >configure_.c
   381         -
   382         -$(OBJDIR)/configure.o:	configure_.c configure.h  $(SRCDIR)/config.h
   383         -	$(XTCC) -o $(OBJDIR)/configure.o -c configure_.c
   384         -
   385         -configure.h:	headers
   386         -content_.c:	$(SRCDIR)/content.c translate
   387         -	./translate $(SRCDIR)/content.c >content_.c
   388         -
   389         -$(OBJDIR)/content.o:	content_.c content.h  $(SRCDIR)/config.h
   390         -	$(XTCC) -o $(OBJDIR)/content.o -c content_.c
   391         -
   392         -content.h:	headers
   393         -db_.c:	$(SRCDIR)/db.c translate
   394         -	./translate $(SRCDIR)/db.c >db_.c
   395         -
   396         -$(OBJDIR)/db.o:	db_.c db.h  $(SRCDIR)/config.h
   397         -	$(XTCC) -o $(OBJDIR)/db.o -c db_.c
   398         -
   399         -db.h:	headers
   400         -delta_.c:	$(SRCDIR)/delta.c translate
   401         -	./translate $(SRCDIR)/delta.c >delta_.c
         1126  +$(OBJDIR)/xfersetup.h:	$(OBJDIR)/headers
         1127  +$(OBJDIR)/zip_.c:	$(SRCDIR)/zip.c $(OBJDIR)/translate
         1128  +	$(OBJDIR)/translate $(SRCDIR)/zip.c >$(OBJDIR)/zip_.c
   402   1129   
   403         -$(OBJDIR)/delta.o:	delta_.c delta.h  $(SRCDIR)/config.h
   404         -	$(XTCC) -o $(OBJDIR)/delta.o -c delta_.c
   405         -
   406         -delta.h:	headers
   407         -deltacmd_.c:	$(SRCDIR)/deltacmd.c translate
   408         -	./translate $(SRCDIR)/deltacmd.c >deltacmd_.c
   409         -
   410         -$(OBJDIR)/deltacmd.o:	deltacmd_.c deltacmd.h  $(SRCDIR)/config.h
   411         -	$(XTCC) -o $(OBJDIR)/deltacmd.o -c deltacmd_.c
   412         -
   413         -deltacmd.h:	headers
   414         -descendants_.c:	$(SRCDIR)/descendants.c translate
   415         -	./translate $(SRCDIR)/descendants.c >descendants_.c
   416         -
   417         -$(OBJDIR)/descendants.o:	descendants_.c descendants.h  $(SRCDIR)/config.h
   418         -	$(XTCC) -o $(OBJDIR)/descendants.o -c descendants_.c
   419         -
   420         -descendants.h:	headers
   421         -diff_.c:	$(SRCDIR)/diff.c translate
   422         -	./translate $(SRCDIR)/diff.c >diff_.c
   423         -
   424         -$(OBJDIR)/diff.o:	diff_.c diff.h  $(SRCDIR)/config.h
   425         -	$(XTCC) -o $(OBJDIR)/diff.o -c diff_.c
   426         -
   427         -diff.h:	headers
   428         -diffcmd_.c:	$(SRCDIR)/diffcmd.c translate
   429         -	./translate $(SRCDIR)/diffcmd.c >diffcmd_.c
   430         -
   431         -$(OBJDIR)/diffcmd.o:	diffcmd_.c diffcmd.h  $(SRCDIR)/config.h
   432         -	$(XTCC) -o $(OBJDIR)/diffcmd.o -c diffcmd_.c
   433         -
   434         -diffcmd.h:	headers
   435         -doc_.c:	$(SRCDIR)/doc.c translate
   436         -	./translate $(SRCDIR)/doc.c >doc_.c
   437         -
   438         -$(OBJDIR)/doc.o:	doc_.c doc.h  $(SRCDIR)/config.h
   439         -	$(XTCC) -o $(OBJDIR)/doc.o -c doc_.c
   440         -
   441         -doc.h:	headers
   442         -encode_.c:	$(SRCDIR)/encode.c translate
   443         -	./translate $(SRCDIR)/encode.c >encode_.c
   444         -
   445         -$(OBJDIR)/encode.o:	encode_.c encode.h  $(SRCDIR)/config.h
   446         -	$(XTCC) -o $(OBJDIR)/encode.o -c encode_.c
   447         -
   448         -encode.h:	headers
   449         -file_.c:	$(SRCDIR)/file.c translate
   450         -	./translate $(SRCDIR)/file.c >file_.c
   451         -
   452         -$(OBJDIR)/file.o:	file_.c file.h  $(SRCDIR)/config.h
   453         -	$(XTCC) -o $(OBJDIR)/file.o -c file_.c
   454         -
   455         -file.h:	headers
   456         -finfo_.c:	$(SRCDIR)/finfo.c translate
   457         -	./translate $(SRCDIR)/finfo.c >finfo_.c
   458         -
   459         -$(OBJDIR)/finfo.o:	finfo_.c finfo.h  $(SRCDIR)/config.h
   460         -	$(XTCC) -o $(OBJDIR)/finfo.o -c finfo_.c
   461         -
   462         -finfo.h:	headers
   463         -graph_.c:	$(SRCDIR)/graph.c translate
   464         -	./translate $(SRCDIR)/graph.c >graph_.c
   465         -
   466         -$(OBJDIR)/graph.o:	graph_.c graph.h  $(SRCDIR)/config.h
   467         -	$(XTCC) -o $(OBJDIR)/graph.o -c graph_.c
   468         -
   469         -graph.h:	headers
   470         -http_.c:	$(SRCDIR)/http.c translate
   471         -	./translate $(SRCDIR)/http.c >http_.c
   472         -
   473         -$(OBJDIR)/http.o:	http_.c http.h  $(SRCDIR)/config.h
   474         -	$(XTCC) -o $(OBJDIR)/http.o -c http_.c
   475         -
   476         -http.h:	headers
   477         -http_socket_.c:	$(SRCDIR)/http_socket.c translate
   478         -	./translate $(SRCDIR)/http_socket.c >http_socket_.c
   479         -
   480         -$(OBJDIR)/http_socket.o:	http_socket_.c http_socket.h  $(SRCDIR)/config.h
   481         -	$(XTCC) -o $(OBJDIR)/http_socket.o -c http_socket_.c
   482         -
   483         -http_socket.h:	headers
   484         -http_transport_.c:	$(SRCDIR)/http_transport.c translate
   485         -	./translate $(SRCDIR)/http_transport.c >http_transport_.c
   486         -
   487         -$(OBJDIR)/http_transport.o:	http_transport_.c http_transport.h  $(SRCDIR)/config.h
   488         -	$(XTCC) -o $(OBJDIR)/http_transport.o -c http_transport_.c
   489         -
   490         -http_transport.h:	headers
   491         -info_.c:	$(SRCDIR)/info.c translate
   492         -	./translate $(SRCDIR)/info.c >info_.c
   493         -
   494         -$(OBJDIR)/info.o:	info_.c info.h  $(SRCDIR)/config.h
   495         -	$(XTCC) -o $(OBJDIR)/info.o -c info_.c
   496         -
   497         -info.h:	headers
   498         -login_.c:	$(SRCDIR)/login.c translate
   499         -	./translate $(SRCDIR)/login.c >login_.c
   500         -
   501         -$(OBJDIR)/login.o:	login_.c login.h  $(SRCDIR)/config.h
   502         -	$(XTCC) -o $(OBJDIR)/login.o -c login_.c
   503         -
   504         -login.h:	headers
   505         -main_.c:	$(SRCDIR)/main.c translate
   506         -	./translate $(SRCDIR)/main.c >main_.c
   507         -
   508         -$(OBJDIR)/main.o:	main_.c main.h page_index.h $(SRCDIR)/config.h
   509         -	$(XTCC) -o $(OBJDIR)/main.o -c main_.c
   510         -
   511         -main.h:	headers
   512         -manifest_.c:	$(SRCDIR)/manifest.c translate
   513         -	./translate $(SRCDIR)/manifest.c >manifest_.c
   514         -
   515         -$(OBJDIR)/manifest.o:	manifest_.c manifest.h  $(SRCDIR)/config.h
   516         -	$(XTCC) -o $(OBJDIR)/manifest.o -c manifest_.c
   517         -
   518         -manifest.h:	headers
   519         -md5_.c:	$(SRCDIR)/md5.c translate
   520         -	./translate $(SRCDIR)/md5.c >md5_.c
   521         -
   522         -$(OBJDIR)/md5.o:	md5_.c md5.h  $(SRCDIR)/config.h
   523         -	$(XTCC) -o $(OBJDIR)/md5.o -c md5_.c
         1130  +$(OBJDIR)/zip.o:	$(OBJDIR)/zip_.c $(OBJDIR)/zip.h  $(SRCDIR)/config.h
         1131  +	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
   524   1132   
   525         -md5.h:	headers
   526         -merge_.c:	$(SRCDIR)/merge.c translate
   527         -	./translate $(SRCDIR)/merge.c >merge_.c
   528         -
   529         -$(OBJDIR)/merge.o:	merge_.c merge.h  $(SRCDIR)/config.h
   530         -	$(XTCC) -o $(OBJDIR)/merge.o -c merge_.c
   531         -
   532         -merge.h:	headers
   533         -merge3_.c:	$(SRCDIR)/merge3.c translate
   534         -	./translate $(SRCDIR)/merge3.c >merge3_.c
   535         -
   536         -$(OBJDIR)/merge3.o:	merge3_.c merge3.h  $(SRCDIR)/config.h
   537         -	$(XTCC) -o $(OBJDIR)/merge3.o -c merge3_.c
   538         -
   539         -merge3.h:	headers
   540         -name_.c:	$(SRCDIR)/name.c translate
   541         -	./translate $(SRCDIR)/name.c >name_.c
   542         -
   543         -$(OBJDIR)/name.o:	name_.c name.h  $(SRCDIR)/config.h
   544         -	$(XTCC) -o $(OBJDIR)/name.o -c name_.c
   545         -
   546         -name.h:	headers
   547         -pivot_.c:	$(SRCDIR)/pivot.c translate
   548         -	./translate $(SRCDIR)/pivot.c >pivot_.c
   549         -
   550         -$(OBJDIR)/pivot.o:	pivot_.c pivot.h  $(SRCDIR)/config.h
   551         -	$(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c
   552         -
   553         -pivot.h:	headers
   554         -pqueue_.c:	$(SRCDIR)/pqueue.c translate
   555         -	./translate $(SRCDIR)/pqueue.c >pqueue_.c
   556         -
   557         -$(OBJDIR)/pqueue.o:	pqueue_.c pqueue.h  $(SRCDIR)/config.h
   558         -	$(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c
   559         -
   560         -pqueue.h:	headers
   561         -printf_.c:	$(SRCDIR)/printf.c translate
   562         -	./translate $(SRCDIR)/printf.c >printf_.c
   563         -
   564         -$(OBJDIR)/printf.o:	printf_.c printf.h  $(SRCDIR)/config.h
   565         -	$(XTCC) -o $(OBJDIR)/printf.o -c printf_.c
   566         -
   567         -printf.h:	headers
   568         -rebuild_.c:	$(SRCDIR)/rebuild.c translate
   569         -	./translate $(SRCDIR)/rebuild.c >rebuild_.c
   570         -
   571         -$(OBJDIR)/rebuild.o:	rebuild_.c rebuild.h  $(SRCDIR)/config.h
   572         -	$(XTCC) -o $(OBJDIR)/rebuild.o -c rebuild_.c
   573         -
   574         -rebuild.h:	headers
   575         -report_.c:	$(SRCDIR)/report.c translate
   576         -	./translate $(SRCDIR)/report.c >report_.c
   577         -
   578         -$(OBJDIR)/report.o:	report_.c report.h  $(SRCDIR)/config.h
   579         -	$(XTCC) -o $(OBJDIR)/report.o -c report_.c
   580         -
   581         -report.h:	headers
   582         -rss_.c:	$(SRCDIR)/rss.c translate
   583         -	./translate $(SRCDIR)/rss.c >rss_.c
   584         -
   585         -$(OBJDIR)/rss.o:	rss_.c rss.h  $(SRCDIR)/config.h
   586         -	$(XTCC) -o $(OBJDIR)/rss.o -c rss_.c
   587         -
   588         -rss.h:	headers
   589         -schema_.c:	$(SRCDIR)/schema.c translate
   590         -	./translate $(SRCDIR)/schema.c >schema_.c
   591         -
   592         -$(OBJDIR)/schema.o:	schema_.c schema.h  $(SRCDIR)/config.h
   593         -	$(XTCC) -o $(OBJDIR)/schema.o -c schema_.c
   594         -
   595         -schema.h:	headers
   596         -search_.c:	$(SRCDIR)/search.c translate
   597         -	./translate $(SRCDIR)/search.c >search_.c
   598         -
   599         -$(OBJDIR)/search.o:	search_.c search.h  $(SRCDIR)/config.h
   600         -	$(XTCC) -o $(OBJDIR)/search.o -c search_.c
   601         -
   602         -search.h:	headers
   603         -setup_.c:	$(SRCDIR)/setup.c translate
   604         -	./translate $(SRCDIR)/setup.c >setup_.c
   605         -
   606         -$(OBJDIR)/setup.o:	setup_.c setup.h  $(SRCDIR)/config.h
   607         -	$(XTCC) -o $(OBJDIR)/setup.o -c setup_.c
   608         -
   609         -setup.h:	headers
   610         -sha1_.c:	$(SRCDIR)/sha1.c translate
   611         -	./translate $(SRCDIR)/sha1.c >sha1_.c
   612         -
   613         -$(OBJDIR)/sha1.o:	sha1_.c sha1.h  $(SRCDIR)/config.h
   614         -	$(XTCC) -o $(OBJDIR)/sha1.o -c sha1_.c
   615         -
   616         -sha1.h:	headers
   617         -shun_.c:	$(SRCDIR)/shun.c translate
   618         -	./translate $(SRCDIR)/shun.c >shun_.c
   619         -
   620         -$(OBJDIR)/shun.o:	shun_.c shun.h  $(SRCDIR)/config.h
   621         -	$(XTCC) -o $(OBJDIR)/shun.o -c shun_.c
   622         -
   623         -shun.h:	headers
   624         -skins_.c:	$(SRCDIR)/skins.c translate
   625         -	./translate $(SRCDIR)/skins.c >skins_.c
   626         -
   627         -$(OBJDIR)/skins.o:	skins_.c skins.h  $(SRCDIR)/config.h
   628         -	$(XTCC) -o $(OBJDIR)/skins.o -c skins_.c
   629         -
   630         -skins.h:	headers
   631         -stat_.c:	$(SRCDIR)/stat.c translate
   632         -	./translate $(SRCDIR)/stat.c >stat_.c
   633         -
   634         -$(OBJDIR)/stat.o:	stat_.c stat.h  $(SRCDIR)/config.h
   635         -	$(XTCC) -o $(OBJDIR)/stat.o -c stat_.c
   636         -
   637         -stat.h:	headers
   638         -style_.c:	$(SRCDIR)/style.c translate
   639         -	./translate $(SRCDIR)/style.c >style_.c
   640         -
   641         -$(OBJDIR)/style.o:	style_.c style.h  $(SRCDIR)/config.h
   642         -	$(XTCC) -o $(OBJDIR)/style.o -c style_.c
   643         -
   644         -style.h:	headers
   645         -sync_.c:	$(SRCDIR)/sync.c translate
   646         -	./translate $(SRCDIR)/sync.c >sync_.c
   647         -
   648         -$(OBJDIR)/sync.o:	sync_.c sync.h  $(SRCDIR)/config.h
   649         -	$(XTCC) -o $(OBJDIR)/sync.o -c sync_.c
   650         -
   651         -sync.h:	headers
   652         -tag_.c:	$(SRCDIR)/tag.c translate
   653         -	./translate $(SRCDIR)/tag.c >tag_.c
   654         -
   655         -$(OBJDIR)/tag.o:	tag_.c tag.h  $(SRCDIR)/config.h
   656         -	$(XTCC) -o $(OBJDIR)/tag.o -c tag_.c
   657         -
   658         -tag.h:	headers
   659         -th_main_.c:	$(SRCDIR)/th_main.c translate
   660         -	./translate $(SRCDIR)/th_main.c >th_main_.c
   661         -
   662         -$(OBJDIR)/th_main.o:	th_main_.c th_main.h  $(SRCDIR)/config.h
   663         -	$(XTCC) -o $(OBJDIR)/th_main.o -c th_main_.c
   664         -
   665         -th_main.h:	headers
   666         -timeline_.c:	$(SRCDIR)/timeline.c translate
   667         -	./translate $(SRCDIR)/timeline.c >timeline_.c
   668         -
   669         -$(OBJDIR)/timeline.o:	timeline_.c timeline.h  $(SRCDIR)/config.h
   670         -	$(XTCC) -o $(OBJDIR)/timeline.o -c timeline_.c
   671         -
   672         -timeline.h:	headers
   673         -tkt_.c:	$(SRCDIR)/tkt.c translate
   674         -	./translate $(SRCDIR)/tkt.c >tkt_.c
   675         -
   676         -$(OBJDIR)/tkt.o:	tkt_.c tkt.h  $(SRCDIR)/config.h
   677         -	$(XTCC) -o $(OBJDIR)/tkt.o -c tkt_.c
   678         -
   679         -tkt.h:	headers
   680         -tktsetup_.c:	$(SRCDIR)/tktsetup.c translate
   681         -	./translate $(SRCDIR)/tktsetup.c >tktsetup_.c
   682         -
   683         -$(OBJDIR)/tktsetup.o:	tktsetup_.c tktsetup.h  $(SRCDIR)/config.h
   684         -	$(XTCC) -o $(OBJDIR)/tktsetup.o -c tktsetup_.c
   685         -
   686         -tktsetup.h:	headers
   687         -undo_.c:	$(SRCDIR)/undo.c translate
   688         -	./translate $(SRCDIR)/undo.c >undo_.c
   689         -
   690         -$(OBJDIR)/undo.o:	undo_.c undo.h  $(SRCDIR)/config.h
   691         -	$(XTCC) -o $(OBJDIR)/undo.o -c undo_.c
   692         -
   693         -undo.h:	headers
   694         -update_.c:	$(SRCDIR)/update.c translate
   695         -	./translate $(SRCDIR)/update.c >update_.c
   696         -
   697         -$(OBJDIR)/update.o:	update_.c update.h  $(SRCDIR)/config.h
   698         -	$(XTCC) -o $(OBJDIR)/update.o -c update_.c
   699         -
   700         -update.h:	headers
   701         -url_.c:	$(SRCDIR)/url.c translate
   702         -	./translate $(SRCDIR)/url.c >url_.c
   703         -
   704         -$(OBJDIR)/url.o:	url_.c url.h  $(SRCDIR)/config.h
   705         -	$(XTCC) -o $(OBJDIR)/url.o -c url_.c
   706         -
   707         -url.h:	headers
   708         -user_.c:	$(SRCDIR)/user.c translate
   709         -	./translate $(SRCDIR)/user.c >user_.c
   710         -
   711         -$(OBJDIR)/user.o:	user_.c user.h  $(SRCDIR)/config.h
   712         -	$(XTCC) -o $(OBJDIR)/user.o -c user_.c
   713         -
   714         -user.h:	headers
   715         -verify_.c:	$(SRCDIR)/verify.c translate
   716         -	./translate $(SRCDIR)/verify.c >verify_.c
   717         -
   718         -$(OBJDIR)/verify.o:	verify_.c verify.h  $(SRCDIR)/config.h
   719         -	$(XTCC) -o $(OBJDIR)/verify.o -c verify_.c
   720         -
   721         -verify.h:	headers
   722         -vfile_.c:	$(SRCDIR)/vfile.c translate
   723         -	./translate $(SRCDIR)/vfile.c >vfile_.c
   724         -
   725         -$(OBJDIR)/vfile.o:	vfile_.c vfile.h  $(SRCDIR)/config.h
   726         -	$(XTCC) -o $(OBJDIR)/vfile.o -c vfile_.c
   727         -
   728         -vfile.h:	headers
   729         -wiki_.c:	$(SRCDIR)/wiki.c translate
   730         -	./translate $(SRCDIR)/wiki.c >wiki_.c
   731         -
   732         -$(OBJDIR)/wiki.o:	wiki_.c wiki.h  $(SRCDIR)/config.h
   733         -	$(XTCC) -o $(OBJDIR)/wiki.o -c wiki_.c
   734         -
   735         -wiki.h:	headers
   736         -wikiformat_.c:	$(SRCDIR)/wikiformat.c translate
   737         -	./translate $(SRCDIR)/wikiformat.c >wikiformat_.c
   738         -
   739         -$(OBJDIR)/wikiformat.o:	wikiformat_.c wikiformat.h  $(SRCDIR)/config.h
   740         -	$(XTCC) -o $(OBJDIR)/wikiformat.o -c wikiformat_.c
   741         -
   742         -wikiformat.h:	headers
   743         -winhttp_.c:	$(SRCDIR)/winhttp.c translate
   744         -	./translate $(SRCDIR)/winhttp.c >winhttp_.c
   745         -
   746         -$(OBJDIR)/winhttp.o:	winhttp_.c winhttp.h  $(SRCDIR)/config.h
   747         -	$(XTCC) -o $(OBJDIR)/winhttp.o -c winhttp_.c
   748         -
   749         -winhttp.h:	headers
   750         -xfer_.c:	$(SRCDIR)/xfer.c translate
   751         -	./translate $(SRCDIR)/xfer.c >xfer_.c
   752         -
   753         -$(OBJDIR)/xfer.o:	xfer_.c xfer.h  $(SRCDIR)/config.h
   754         -	$(XTCC) -o $(OBJDIR)/xfer.o -c xfer_.c
   755         -
   756         -xfer.h:	headers
   757         -zip_.c:	$(SRCDIR)/zip.c translate
   758         -	./translate $(SRCDIR)/zip.c >zip_.c
   759         -
   760         -$(OBJDIR)/zip.o:	zip_.c zip.h  $(SRCDIR)/config.h
   761         -	$(XTCC) -o $(OBJDIR)/zip.o -c zip_.c
   762         -
   763         -zip.h:	headers
         1133  +$(OBJDIR)/zip.h:	$(OBJDIR)/headers
   764   1134   $(OBJDIR)/sqlite3.o:	$(SRCDIR)/sqlite3.c
   765         -	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
         1135  +	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
         1136  +
         1137  +$(OBJDIR)/shell.o:	$(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
         1138  +	$(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
   766   1139   
   767   1140   $(OBJDIR)/th.o:	$(SRCDIR)/th.c
   768         -	$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
         1141  +	$(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
   769   1142   
   770   1143   $(OBJDIR)/th_lang.o:	$(SRCDIR)/th_lang.c
   771         -	$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
         1144  +	$(XTCC) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
         1145  +
         1146  +$(OBJDIR)/th_tcl.o:	$(SRCDIR)/th_tcl.c
         1147  +	$(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
         1148  +
         1149  +
         1150  +$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
         1151  +	$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
         1152  +
         1153  +#
         1154  +# The list of all the targets that do not correspond to real files. This stops
         1155  +# 'make' from getting confused when someone makes an error in a rule.
         1156  +#
         1157  +
         1158  +.PHONY: all install test clean
   772   1159   

Changes to src/makeheaders.c.

     1         -static const char ident[] = "@(#) $Header: /cvstrac/cvstrac/makeheaders.c,v 1.4 2005/03/16 22:17:51 drh Exp $";
     2      1   /*
     3      2   ** This program is free software; you can redistribute it and/or
     4      3   ** modify it under the terms of the Simplified BSD License (also
     5      4   ** known as the "2-Clause License" or "FreeBSD License".)
     6         -
            5  +**
            6  +** Copyright 1993 D. Richard Hipp. All rights reserved.
            7  +**
            8  +** Redistribution and use in source and binary forms, with or
            9  +** without modification, are permitted provided that the following
           10  +** conditions are met:
           11  +**
           12  +**   1. Redistributions of source code must retain the above copyright
           13  +**      notice, this list of conditions and the following disclaimer.
           14  +**
           15  +**   2. Redistributions in binary form must reproduce the above copyright
           16  +**      notice, this list of conditions and the following disclaimer in
           17  +**      the documentation and/or other materials provided with the
           18  +**      distribution.
           19  +**
           20  +** This software is provided "as is" and any express or implied warranties,
           21  +** including, but not limited to, the implied warranties of merchantability
           22  +** and fitness for a particular purpose are disclaimed.  In no event shall
           23  +** the author or contributors be liable for any direct, indirect, incidental,
           24  +** special, exemplary, or consequential damages (including, but not limited
           25  +** to, procurement of substitute goods or services; loss of use, data or
           26  +** profits; or business interruption) however caused and on any theory of
           27  +** liability, whether in contract, strict liability, or tort (including
           28  +** negligence or otherwise) arising in any way out of the use of this
           29  +** software, even if advised of the possibility of such damage.
           30  +**
     7     31   ** This program is distributed in the hope that it will be useful,
     8     32   ** but without any warranty; without even the implied warranty of
     9     33   ** merchantability or fitness for a particular purpose.
    10     34   ** appropriate header files.
    11     35   */
    12     36   #include <stdio.h>
    13     37   #include <stdlib.h>
    14     38   #include <ctype.h>
    15     39   #include <memory.h>
    16     40   #include <sys/stat.h>
    17     41   #include <assert.h>
    18         -#ifndef WIN32
    19         -# include <unistd.h>
    20         -#else
           42  +#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
           43  +#  ifndef WIN32
           44  +#    define WIN32
           45  +#  endif
    21     46   # include <string.h>
           47  +#else
           48  +# include <unistd.h>
    22     49   #endif
    23     50   
    24     51   /*
    25     52   ** Macros for debugging.
    26     53   */
    27     54   #ifdef DEBUG
    28     55   static int debugMask = 0;
................................................................................
   298    325     int flags;             /* Various flags (DP_ and PS_ flags above) */
   299    326   };
   300    327   
   301    328   /*
   302    329   ** The following text line appears at the top of every file generated
   303    330   ** by this program.  By recognizing this line, the program can be sure
   304    331   ** never to read a file that it generated itself.
          332  +**
          333  +** The "#undef INTERFACE" part is a hack to work around a name collision
          334  +** in MSVC 2008.
   305    335   */
   306    336   const char zTopLine[] = 
   307         -  "/* \aThis file was automatically generated.  Do not edit! */\n";
          337  +  "/* \aThis file was automatically generated.  Do not edit! */\n"
          338  +  "#undef INTERFACE\n";
   308    339   #define nTopLine (sizeof(zTopLine)-1)
   309    340   
   310    341   /*
   311    342   ** The name of the file currently being parsed.
   312    343   */
   313    344   static char *zFilename;
   314    345   
................................................................................
   714    745   #define TT_Comment         4   /* Either C or C++ style comment */
   715    746   #define TT_Number          5   /* Any numeric constant */
   716    747   #define TT_String          6   /* String or character constants. ".." or '.' */
   717    748   #define TT_Braces          7   /* All text between { and a matching } */
   718    749   #define TT_EOF             8   /* End of file */
   719    750   #define TT_Error           9   /* An error condition */
   720    751   #define TT_BlockComment    10  /* A C-Style comment at the left margin that
   721         -                                * spans multple lines */
          752  +                                * spans multiple lines */
   722    753   #define TT_Other           0   /* None of the above */
   723    754   
   724    755   /*
   725    756   ** Get a single low-level token from the input file.  Update the
   726    757   ** file pointer so that it points to the first character beyond the
   727    758   ** token.
   728    759   **
................................................................................
  1453   1484   
  1454   1485     /*
  1455   1486     ** At this point, we know we have a type declaration that is bounded
  1456   1487     ** by pList and pEnd and has the name pName.
  1457   1488     */
  1458   1489   
  1459   1490     /*
  1460         -  ** If the braces are followed immedately by a semicolon, then we are
         1491  +  ** If the braces are followed immediately by a semicolon, then we are
  1461   1492     ** dealing a type declaration only.  There is not variable definition
  1462   1493     ** following the type declaration.  So reset...
  1463   1494     */
  1464   1495     if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){
  1465   1496       *pReset = ';';
  1466   1497       need_to_collapse = 0;
  1467   1498     }else{
................................................................................
  1849   1880   ** definition or a function prototype.  Return TRUE if we are dealing
  1850   1881   ** with a variable defintion and FALSE for a prototype.
  1851   1882   **
  1852   1883   ** pEnd is the token that ends the object.  It can be either a ';' or
  1853   1884   ** a '='.  If it is '=', then assume we have a variable definition.
  1854   1885   **
  1855   1886   ** If pEnd is ';', then the determination is more difficult.  We have
  1856         -** to search for an occurance of an ID followed immediately by '('.
         1887  +** to search for an occurrence of an ID followed immediately by '('.
  1857   1888   ** If found, we have a prototype.  Otherwise we are dealing with a
  1858   1889   ** variable definition.
  1859   1890   */
  1860   1891   static int isVariableDef(Token *pFirst, Token *pEnd){
  1861   1892     if( pEnd && pEnd->zText[0]=='=' && 
  1862   1893       (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0)
  1863   1894     ){
................................................................................
  2139   2170       ** and EXPORT_INTERFACE and LOCAL_INTERFACE
  2140   2171       */
  2141   2172       zArg = &zCmd[2];
  2142   2173       while( *zArg && isspace(*zArg) && *zArg!='\n' ){
  2143   2174         zArg++;
  2144   2175       }
  2145   2176       if( *zArg==0 || *zArg=='\n' ){ return 0; }
  2146         -    nArg = pToken->nText + (int)pToken->zText - (int)zArg;
         2177  +    nArg = pToken->nText + (int)(pToken->zText - zArg);
  2147   2178       if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
  2148   2179         PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
  2149   2180       }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
  2150   2181         PushIfMacro(0,0,0,pToken->nLine,PS_Export);
  2151   2182       }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
  2152   2183         PushIfMacro(0,0,0,pToken->nLine,PS_Local);
  2153   2184       }else{
................................................................................
  2158   2189       ** Push an #ifdef.
  2159   2190       */
  2160   2191       zArg = &zCmd[5];
  2161   2192       while( *zArg && isspace(*zArg) && *zArg!='\n' ){
  2162   2193         zArg++;
  2163   2194       }
  2164   2195       if( *zArg==0 || *zArg=='\n' ){ return 0; }
  2165         -    nArg = pToken->nText + (int)pToken->zText - (int)zArg;
         2196  +    nArg = pToken->nText + (int)(pToken->zText - zArg);
  2166   2197       PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
  2167   2198     }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
  2168   2199       /*
  2169   2200       ** Push an #ifndef.
  2170   2201       */
  2171   2202       zArg = &zCmd[6];
  2172   2203       while( *zArg && isspace(*zArg) && *zArg!='\n' ){
  2173   2204         zArg++;
  2174   2205       }
  2175   2206       if( *zArg==0 || *zArg=='\n' ){ return 0; }
  2176         -    nArg = pToken->nText + (int)pToken->zText - (int)zArg;
         2207  +    nArg = pToken->nText + (int)(pToken->zText - zArg);
  2177   2208       PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
  2178   2209     }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
  2179   2210       /*
  2180   2211       ** Invert the #if on the top of the stack 
  2181   2212       */
  2182   2213       if( ifStack==0 ){
  2183   2214         fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
................................................................................
  2315   2346                flags |= PS_Extern;
  2316   2347              }
  2317   2348              pStart = pList;
  2318   2349            }
  2319   2350            break;
  2320   2351   
  2321   2352          case 'i':
  2322         -         if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0 ){
         2353  +         if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0
         2354  +          && (flags & PS_Static)==0
         2355  +         ){
  2323   2356              nErr += ProcessInlineProc(pList,flags,&resetFlag);
  2324   2357            }
  2325   2358            break;
  2326   2359   
  2327   2360          case 'L':
  2328   2361            if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){
  2329   2362              flags |= PS_Local2;
................................................................................
  2601   2634         DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag);
  2602   2635       }else{
  2603   2636         DeclClearProperty(p,DP_Flag);
  2604   2637       }
  2605   2638     }
  2606   2639   
  2607   2640     /*
  2608         -  ** Call ScanText() recusively (this routine is called from ScanText())
         2641  +  ** Call ScanText() recursively (this routine is called from ScanText())
  2609   2642     ** to include declarations required to come before these declarations.
  2610   2643     */
  2611   2644     for(p=pDecl; p; p=p->pSameName){
  2612   2645       if( DeclHasProperty(p,DP_Flag) ){
  2613   2646         if( p->zDecl[0]=='#' ){
  2614   2647           ScanText(&p->zDecl[1],pState);
  2615   2648         }else{
................................................................................
  2725   2758       }
  2726   2759     }
  2727   2760     /* printf("END SCANTEXT\n"); */
  2728   2761   }
  2729   2762   
  2730   2763   /*
  2731   2764   ** Provide a full declaration to any object which so far has had only
  2732         -** a foward declaration.
         2765  +** a forward declaration.
  2733   2766   */
  2734   2767   static void CompleteForwardDeclarations(GenState *pState){
  2735   2768     Decl *pDecl;
  2736   2769     int progress;
  2737   2770   
  2738   2771     do{
  2739   2772       progress = 0;
................................................................................
  2795   2828         nErr++;
  2796   2829       }
  2797   2830     }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
  2798   2831       if( report ) fprintf(report,"error!\n");
  2799   2832       fprintf(stderr,
  2800   2833          "%s: Can't overwrite this file because it wasn't previously\n"
  2801   2834          "%*s  generated by 'makeheaders'.\n",
  2802         -       pFile->zHdr, strlen(pFile->zHdr), "");
         2835  +       pFile->zHdr, (int)strlen(pFile->zHdr), "");
  2803   2836       nErr++;
  2804   2837     }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
  2805   2838       if( report ) fprintf(report,"updated\n");
  2806   2839       if( WriteFile(pFile->zHdr,zNewVersion) ){
  2807   2840         fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
  2808   2841         nErr++;
  2809   2842       }
................................................................................
  2950   2983         }
  2951   2984       }
  2952   2985       if( nLabel==0 ) continue;
  2953   2986       zLabel[nLabel] = 0;
  2954   2987       InsertExtraDecl(pDecl);
  2955   2988       zDecl = pDecl->zDecl;
  2956   2989       if( zDecl==0 ) zDecl = pDecl->zFwd;
  2957         -    printf("%s %s %s %d %d %d %d %d %d\n",
         2990  +    printf("%s %s %s %p %d %d %d %d %d\n",
  2958   2991          pDecl->zName,
  2959   2992          zLabel,
  2960   2993          pDecl->zFile,
  2961         -       pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0,
         2994  +       pDecl->pComment,
  2962   2995          pDecl->pComment ? pDecl->pComment->nText+1 : 0,
  2963         -       pDecl->zIf ? strlen(pDecl->zIf)+1 : 0,
  2964         -       zDecl ? strlen(zDecl) : 0,
         2996  +       pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
         2997  +       zDecl ? (int)strlen(zDecl) : 0,
  2965   2998          pDecl->pComment ? pDecl->pComment->nLine : 0,
  2966   2999          pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
  2967   3000       );
  2968   3001       if( pDecl->pComment ){
  2969   3002         printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
  2970   3003       }
  2971   3004       if( pDecl->zIf ){
................................................................................
  3005   3038   static InFile *CreateInFile(char *zArg, int *pnErr){
  3006   3039     int nSrc;
  3007   3040     char *zSrc;
  3008   3041     InFile *pFile;
  3009   3042     int i;
  3010   3043   
  3011   3044     /* 
  3012         -  ** Get the name of the input file to be scanned
         3045  +  ** Get the name of the input file to be scanned.  The input file is
         3046  +  ** everything before the first ':' or the whole file if no ':' is seen.
         3047  +  **
         3048  +  ** Except, on windows, ignore any ':' that occurs as the second character
         3049  +  ** since it might be part of the drive specifier.  So really, the ":' has
         3050  +  ** to be the 3rd or later character in the name.  This precludes 1-character
         3051  +  ** file names, which really should not be a problem.
  3013   3052     */
  3014   3053     zSrc = zArg;
  3015         -  for(nSrc=0; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
         3054  +  for(nSrc=2; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
  3016   3055     pFile = SafeMalloc( sizeof(InFile) );
  3017   3056     memset(pFile,0,sizeof(InFile));
  3018   3057     pFile->zSrc = StrDup(zSrc,nSrc);
  3019   3058   
  3020   3059     /* Figure out if we are dealing with C or C++ code.  Assume any
  3021   3060     ** file with ".c" or ".h" is C code and all else is C++.
  3022   3061     */
................................................................................
  3029   3068     /*
  3030   3069     ** If a separate header file is specified, use it
  3031   3070     */
  3032   3071     if( zSrc[nSrc]==':' ){
  3033   3072       int nHdr;
  3034   3073       char *zHdr;
  3035   3074       zHdr = &zSrc[nSrc+1];
  3036         -    for(nHdr=0; zHdr[nHdr] && zHdr[nHdr]!=':'; nHdr++){}
         3075  +    for(nHdr=0; zHdr[nHdr]; nHdr++){}
  3037   3076       pFile->zHdr = StrDup(zHdr,nHdr);
  3038   3077     }
  3039   3078   
  3040   3079     /* Look for any 'c' or 'C' in the suffix of the file name and change
  3041   3080     ** that character to 'h' or 'H' respectively.  If no 'c' or 'C' is found,
  3042   3081     ** then assume we are dealing with a header.
  3043   3082     */

Changes to src/makeheaders.html.

    41     41   <li><a href=makeheaders.html#H0014>3.8 Caveats</a>
    42     42   </ul>
    43     43   <li><a href=makeheaders.html#H0015>4.0 Using Makeheaders To Generate Documentation</a>
    44     44   
    45     45   <li><a href=makeheaders.html#H0016>5.0 Compiling The Makeheaders Program</a>
    46     46   
    47     47   <li><a href=makeheaders.html#H0017>6.0 Summary And Conclusion</a>
    48         -</ul><a name=H0002>
           48  +</ul><a name="H0002"></a>
    49     49   <h2>1.0 Background</h2>
    50     50   
    51     51   <p>
    52     52   A piece of C source code can be one of two things:
    53     53   a <em>declaration</em> or a <em>definition</em>.
    54     54   A declaration is source text that gives information to the
    55     55   compiler but doesn't directly result in any code being generated.
................................................................................
    96     96   The .c files contain ``<code>#include</code>'' preprocessor statements
    97     97   that cause the contents of .h files to be included as part of the
    98     98   source code when the .c file is compiled.
    99     99   In this way, the .h files define the interface to a subsystem and
   100    100   the .c files define how the subsystem is implemented.
   101    101   </p>
   102    102   
   103         -<a name=H0003>
          103  +<a name="H0003"></a>
   104    104   <h3>1.1 Problems With The Traditional Approach</h3>
   105    105   
   106    106   <p>
   107    107   As the art of computer programming continues to advance, and the size
   108    108   and complexity of programs continues to swell, the traditional C
   109    109   approach of placing declarations and definitions in separate files begins
   110    110   to present the programmer with logistics and
................................................................................
   114    114   
   115    115   <p>
   116    116   <ol>
   117    117   <p><li>
   118    118   In large codes with many source files, it becomes difficult to determine
   119    119   which .h files should be included in which .c files.
   120    120   <p><li>
   121         -It is typically the case the a .h file will be forced to include
          121  +It is typically the case that a .h file will be forced to include
   122    122   another .h files, which in turn might include other .h files,
   123    123   and so forth.
   124    124   The .c file must be recompiled when any of the .h files in this chain
   125    125   are altered, but it can be difficult to determine what .h files are found
   126    126   in the include chain.
   127    127   A frequent Makefile error is to omit some .h files from a dependency
   128    128   list even though those files are on the include file chain.
................................................................................
   150    150   In a program with complex, interwoven data structures, the correct
   151    151   declaration order can become very difficult to determine manually, 
   152    152   especially when the declarations involved are spread out over several
   153    153   files.
   154    154   </ol>
   155    155   </p>
   156    156   
   157         -<a name=H0004>
          157  +<a name="H0004"></a>
   158    158   <h3>1.2 The Makeheaders Solution</h3>
   159    159   
   160    160   <p>
   161    161   The makeheaders program is designed to ameliorate the problems associated
   162    162   with the traditional C programming model by automatically generating
   163    163   the interface information in the .h files from 
   164    164   interface information contained in other .h files and
................................................................................
   213    213   so that makeheaders will be run automatically whenever the project
   214    214   is rebuilt.
   215    215   And the burden of running makeheaders is light.
   216    216   It will easily process tens of thousands of lines of source
   217    217   code per second.
   218    218   </p>
   219    219   
   220         -<a name=H0005>
          220  +<a name="H0005"></a>
   221    221   <h2>2.0 Running The Makeheaders Program</h2>
   222    222   
   223    223   <p>
   224    224   The makeheaders program is very easy to run.
   225    225   If you have a collection of C source code and include files in the working
   226    226   directory, then you can run makeheaders to generate appropriate .h
   227    227   files using the following command:
................................................................................
   361    361   you can prepend a ``./'' to its name in order to get it
   362    362   accepted by the command line parser.
   363    363   Or, you can insert the special option ``--'' on the command
   364    364   line to cause all subsequent command line arguments to be treated as
   365    365   filenames even if their names beginn with ``-''.
   366    366   </p>
   367    367   
   368         -<a name=H0006>
          368  +<a name="H0006"></a>
   369    369   <h2>3.0 Preparing Source Files For Use With Makeheaders</h2>
   370    370   
   371    371   <p>
   372    372   Very little has to be done to prepare source files for use with
   373    373   makeheaders since makeheaders will read and understand ordinary
   374    374   C code.
   375    375   But it is important that you structure your files in a way that
   376    376   makes sense in the makeheaders context.
   377    377   This section will describe several typical uses of makeheaders.
   378    378   </p>
   379    379   
   380         -<a name=H0007>
          380  +<a name="H0007"></a>
   381    381   <h3>3.1 The Basic Setup</h3>
   382    382   
   383    383   <p>
   384    384   The simpliest way to use makeheaders is to put all definitions in
   385    385   one or more .c files and all structure and type declarations in
   386    386   separate .h files.
   387    387   The only restriction is that you should take care to chose basenames 
................................................................................
   472    472   those entered manually be the programmer and others generated automatically
   473    473   by a prior run of makeheaders.
   474    474   But that is not a problem.
   475    475   The makeheaders program will recognize and ignore any files it 
   476    476   has previously generated that show up on its input list.
   477    477   </p>
   478    478   
   479         -<a name=H0008>
          479  +<a name="H0008"></a>
   480    480   <h3>3.2 What Declarations Get Copied</h3>
   481    481   
   482    482   <p>
   483    483   The following list details all of the code constructs that makeheaders
   484    484   will extract and place in
   485    485   the automatically generated .h files:
   486    486   </p>
................................................................................
   575    575   As a final note, we observe that automatically generated declarations
   576    576   are ordered as required by the ANSI-C programming language.
   577    577   If the declaration of some structure ``X'' requires a prior
   578    578   declaration of another structure ``Y'', then Y will appear
   579    579   first in the generated headers.
   580    580   </p>
   581    581   
   582         -<a name=H0009>
          582  +<a name="H0009"></a>
   583    583   <h3>3.3 How To Avoid Having To Write Any Header Files</h3>
   584    584   
   585    585   <p>
   586    586   In my experience, large projects work better if all of the manually
   587    587   written code is placed in .c files and all .h files are generated
   588    588   automatically.
   589    589   This is slightly different for the traditional C method of placing
................................................................................
   642    642   ``#if INTERFACE'' regions of .c files.
   643    643   Makeheaders treats all declarations alike, no matter where they
   644    644   come from.
   645    645   You should also note that a single .c file can contain as many
   646    646   ``#if INTERFACE'' regions as desired.
   647    647   </p>
   648    648   
   649         -<a name=H0010>
          649  +<a name="H0010"></a>
   650    650   <h3>3.4 Designating Declarations For Export</h3>
   651    651   
   652    652   <p>
   653    653   In a large project, one will often construct a hierarchy of
   654    654   interfaces.
   655    655   For example, you may have a group of 20 or so files that form
   656    656   a library used in several other parts of the system.
................................................................................
   731    731   The ``#if EXPORT_INTERFACE'' mechanism can be used in either
   732    732   .c or .h files.
   733    733   (The ``#if INTERFACE'' can also be used in both .h and .c files, 
   734    734   but since it's use in a .h file would be redundant, we haven't mentioned
   735    735   it before.)
   736    736   </p>
   737    737   
   738         -<a name=H0011>
          738  +<a name="H0011"></a>
   739    739   <h3>3.5 Local declarations processed by makeheaders</h3>
   740    740   
   741    741   <p>
   742    742   Structure declarations and typedefs that appear in .c files are normally
   743    743   ignored by makeheaders.
   744    744   Such declarations are only intended for use by the source file in which
   745    745   they appear and so makeheaders doesn't need to copy them into any
................................................................................
   769    769   A ``LOCAL_INTERFACE'' block works very much like the
   770    770   ``INTERFACE'' and ``EXPORT_INTERFACE''
   771    771   blocks described above, except that makeheaders insures that the
   772    772   objects declared in a LOCAL_INTERFACE are only visible to the
   773    773   file containing the LOCAL_INTERFACE.
   774    774   </p>
   775    775   
   776         -<a name=H0012>
          776  +<a name="H0012"></a>
   777    777   <h3>3.6 Using Makeheaders With C++ Code</h3>
   778    778   
   779    779   <p>
   780    780   You can use makeheaders to generate header files for C++ code, in
   781    781   addition to C.
   782    782   Makeheaders will recognize and copy both ``class'' declarations
   783    783   and inline function definitions, and it knows not to try to generate
................................................................................
   868    868   
   869    869   <p>
   870    870   Makeheaders does not understand more recent
   871    871   C++ syntax such as templates and namespaces.
   872    872   Perhaps these issued will be addressed in future revisions.
   873    873   </p>
   874    874   
   875         -<a name=H0013>
          875  +<a name="H0013"></a>
   876    876   <h3>3.7 Conditional Compilation</h3>
   877    877   
   878    878   <p>
   879    879   The makeheaders program understands and tracks the conditional 
   880    880   compilation constructs in the source code files it scans.
   881    881   Hence, if the following code appears in a source file
   882    882   <pre>
................................................................................
   901    901   <pre>
   902    902     #if 0
   903    903     #endif
   904    904   </pre>
   905    905   and treats the enclosed text as a comment.
   906    906   </p>
   907    907   
   908         -<a name=H0014>
          908  +<a name="H0014"></a>
   909    909   <h3>3.8 Caveats</h3>
   910    910   
   911    911   <p>
   912    912   The makeheaders system is designed to be robust
   913    913   but it is possible for a devious programmer to fool the system,
   914    914   usually with unhelpful consequences.
   915    915   This subsection is a guide to helping you avoid trouble.
................................................................................
   971    971   For most projects the code constructs that makeheaders cannot
   972    972   handle are very rare.
   973    973   As long as you avoid excessive cleverness, makeheaders will
   974    974   probably be able to figure out what you want and will do the right
   975    975   thing.
   976    976   </p>
   977    977   
   978         -<a name=H0015>
          978  +<a name="H0015"></a>
   979    979   <h2>4.0 Using Makeheaders To Generate Documentation</h2>
   980    980   
   981    981   <p>
   982    982   Many people have observed the advantages of generating program
   983    983   documentation directly from the source code:
   984    984   <ul>
   985    985   <li> Less effort is involved.  It is easier to write a program than
................................................................................
  1035   1035   <li> The complete text of a declaration for the object.
  1036   1036   </ul>
  1037   1037   The exact output format will not be described here.
  1038   1038   It is simple to understand and parse and should be obvious to
  1039   1039   anyone who inspects some sample output.
  1040   1040   </p>
  1041   1041   
  1042         -<a name=H0016>
         1042  +<a name="H0016"></a>
  1043   1043   <h2>5.0 Compiling The Makeheaders Program</h2>
  1044   1044   
  1045   1045   <p>
  1046   1046   The source code for makeheaders is a single file of ANSI-C code,
  1047   1047   less than 3000 lines in length.
  1048   1048   The program makes only modest demands of the system and C library
  1049   1049   and should compile without alteration on most ANSI C compilers
  1050   1050   and on most operating systems.
  1051   1051   It is known to compile using several variations of GCC for Unix
  1052   1052   as well as Cygwin32 and MSVC 5.0 for Win32.
  1053   1053   </p>
  1054   1054   
  1055         -<a name=H0017>
         1055  +<a name="H0017"></a>
  1056   1056   <h2>6.0 Summary And Conclusion</h2>
  1057   1057   
  1058   1058   <p>
  1059   1059   The makeheaders program will automatically generate a minimal header file 
  1060   1060   for each of a set of C source and header files, and will
  1061   1061   generate a composite header file for the entire source file suite,
  1062   1062   for either internal or external use.

Changes to src/makemake.tcl.

     1      1   #!/usr/bin/tclsh
     2      2   #
     3         -# Run this TCL script to generate the "main.mk" makefile.
            3  +# Run this TCL script to generate the various makefiles for a variety
            4  +# of platforms.  Files generated include:
     4      5   #
            6  +#     src/main.mk           # makefile for all unix systems
            7  +#     win/Makefile.mingw    # makefile for mingw on windows
            8  +#     win/Makefile.*        # makefiles for other windows compilers
            9  +#
           10  +# Run this script while in the "src" subdirectory.  Like this:
           11  +#
           12  +#      tclsh makemake.tcl
           13  +#
           14  +#############################################################################
     5     15   
     6     16   # Basenames of all source files that get preprocessed using
     7         -# "translate" and "makeheaders"
           17  +# "translate" and "makeheaders".  To add new source files to the
           18  +# project, simply add the basename to this list and rerun this script.
     8     19   #
     9     20   set src {
    10     21     add
    11     22     allrepo
    12     23     attach
    13     24     bag
           25  +  bisect
    14     26     blob
    15     27     branch
    16     28     browse
    17     29     captcha
    18     30     cgi
    19     31     checkin
    20     32     checkout
................................................................................
    27     39     delta
    28     40     deltacmd
    29     41     descendants
    30     42     diff
    31     43     diffcmd
    32     44     doc
    33     45     encode
           46  +  event
           47  +  export
    34     48     file
    35     49     finfo
           50  +  glob
    36     51     graph
           52  +  gzip
    37     53     http
    38     54     http_socket
    39     55     http_transport
           56  +  import
    40     57     info
           58  +  json
           59  +  json_artifact
           60  +  json_branch
           61  +  json_config
           62  +  json_diff
           63  +  json_dir
           64  +  json_finfo
           65  +  json_login
           66  +  json_query
           67  +  json_report
           68  +  json_tag
           69  +  json_timeline
           70  +  json_user
           71  +  json_wiki
           72  +  leaf
    41     73     login
    42     74     main
    43     75     manifest
           76  +  markdown
           77  +  markdown_html
    44     78     md5
    45     79     merge
    46     80     merge3
           81  +  moderate
    47     82     name
           83  +  path
    48     84     pivot
           85  +  popen
    49     86     pqueue
    50     87     printf
    51     88     rebuild
           89  +  regexp
    52     90     report
    53     91     rss
    54     92     schema
    55     93     search
    56     94     setup
    57     95     sha1
    58     96     shun
    59     97     skins
           98  +  sqlcmd
           99  +  stash
    60    100     stat
    61    101     style
    62    102     sync
    63    103     tag
          104  +  tar
    64    105     th_main
    65    106     timeline
    66    107     tkt
    67    108     tktsetup
    68    109     undo
          110  +  unicode
    69    111     update
    70    112     url
    71    113     user
          114  +  utf8
    72    115     verify
    73    116     vfile
    74    117     wiki
    75    118     wikiformat
    76    119     winhttp
          120  +  wysiwyg
    77    121     xfer
          122  +  xfersetup
    78    123     zip
          124  +  http_ssl
    79    125   }
    80    126   
    81    127   # Name of the final application
    82    128   #
    83    129   set name fossil
    84    130   
    85         -puts {# DO NOT EDIT
          131  +# The "writeln" command sends output to the target makefile.
          132  +#
          133  +proc writeln {args} {
          134  +  global output_file
          135  +  if {[lindex $args 0]=="-nonewline"} {
          136  +    puts -nonewline $output_file [lindex $args 1]
          137  +  } else {
          138  +    puts $output_file [lindex $args 0]
          139  +  }
          140  +}
          141  +
          142  +# STOP HERE.
          143  +# Unless the build procedures changes, you should not have to edit anything
          144  +# below this line.
          145  +
          146  +##############################################################################
          147  +##############################################################################
          148  +##############################################################################
          149  +# Start by generating the "main.mk" makefile used for all unix systems.
          150  +#
          151  +puts "building main.mk"
          152  +set output_file [open main.mk w]
          153  +fconfigure $output_file -translation binary
          154  +
          155  +writeln {#
          156  +##############################################################################
          157  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
          158  +##############################################################################
    86    159   #
    87    160   # This file is automatically generated.  Instead of editing this
    88         -# file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
          161  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
    89    162   # to regenerate this file.
    90    163   #
    91         -# This file is included by linux-gcc.mk or linux-mingw.mk or possible
    92         -# some other makefiles.  This file contains the rules that are common
    93         -# to building regardless of the target.
    94         -#
    95         -
    96         -XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
    97         -
    98         -}
    99         -puts -nonewline "SRC ="
   100         -foreach s [lsort $src] {
   101         -  puts -nonewline " \\\n  \$(SRCDIR)/$s.c"
   102         -}
   103         -puts "\n"
   104         -puts -nonewline "TRANS_SRC ="
   105         -foreach s [lsort $src] {
   106         -  puts -nonewline " \\\n  ${s}_.c"
   107         -}
   108         -puts "\n"
   109         -puts -nonewline "OBJ ="
   110         -foreach s [lsort $src] {
   111         -  puts -nonewline " \\\n \$(OBJDIR)/$s.o"
   112         -}
   113         -puts "\n"
   114         -puts "APPNAME = $name\$(E)"
   115         -puts "\n"
   116         -
   117         -puts {
          164  +# This file is included by primary Makefile.
          165  +#
          166  +
          167  +XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) -I$(OBJDIR)
          168  +
          169  +}
          170  +writeln -nonewline "SRC ="
          171  +foreach s [lsort $src] {
          172  +  writeln -nonewline " \\\n  \$(SRCDIR)/$s.c"
          173  +}
          174  +writeln "\n"
          175  +writeln -nonewline "TRANS_SRC ="
          176  +foreach s [lsort $src] {
          177  +  writeln -nonewline " \\\n  \$(OBJDIR)/${s}_.c"
          178  +}
          179  +writeln "\n"
          180  +writeln -nonewline "OBJ ="
          181  +foreach s [lsort $src] {
          182  +  writeln -nonewline " \\\n \$(OBJDIR)/$s.o"
          183  +}
          184  +writeln "\n"
          185  +writeln "APPNAME = $name\$(E)"
          186  +writeln "\n"
          187  +
          188  +writeln {
   118    189   all:	$(OBJDIR) $(APPNAME)
   119    190   
   120    191   install:	$(APPNAME)
          192  +	mkdir -p $(INSTALLDIR)
   121    193   	mv $(APPNAME) $(INSTALLDIR)
   122    194   
   123    195   $(OBJDIR):
   124    196   	-mkdir $(OBJDIR)
   125    197   
   126         -translate:	$(SRCDIR)/translate.c
   127         -	$(BCC) -o translate $(SRCDIR)/translate.c
          198  +$(OBJDIR)/translate:	$(SRCDIR)/translate.c
          199  +	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
   128    200   
   129         -makeheaders:	$(SRCDIR)/makeheaders.c
   130         -	$(BCC) -o makeheaders $(SRCDIR)/makeheaders.c
          201  +$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
          202  +	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
   131    203   
   132         -mkindex:	$(SRCDIR)/mkindex.c
   133         -	$(BCC) -o mkindex $(SRCDIR)/mkindex.c
          204  +$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
          205  +	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
   134    206   
   135         -# WARNING. DANGER. Running the testsuite modifies the repository the
          207  +$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
          208  +	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
          209  +
          210  +# WARNING. DANGER. Running the test suite modifies the repository the
   136    211   # build is done from, i.e. the checkout belongs to. Do not sync/push
   137    212   # the repository after running the tests.
   138         -test:	$(APPNAME)
   139         -	$(TCLSH) test/tester.tcl $(APPNAME)
   140         -
   141         -VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest
   142         -	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' \
   143         -		$(SRCDIR)/../manifest.uuid >VERSION.h
   144         -	awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' \
   145         -		$(SRCDIR)/../manifest.uuid >>VERSION.h
   146         -	awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\
   147         -		substr($$2,1,10),substr($$2,12)}' \
   148         -		$(SRCDIR)/../manifest >>VERSION.h
   149         -
   150         -$(APPNAME):	headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
   151         -	$(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB)
          213  +test:	$(OBJDIR) $(APPNAME)
          214  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
          215  +
          216  +$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
          217  +	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
          218  +		$(SRCDIR)/../manifest \
          219  +		$(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
          220  +
          221  +# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
          222  +# to 1. If it is set to 1, then there is no need to build or link
          223  +# the sqlite3.o object. Instead, the system sqlite will be linked
          224  +# using -lsqlite3.
          225  +SQLITE3_OBJ.1 = 
          226  +SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
          227  +SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)
          228  +
          229  +# The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
          230  +# If it is set to 1, then we need to build the Tcl integration code and
          231  +# link to the Tcl library.
          232  +TCL_OBJ.0 = 
          233  +TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
          234  +TCL_OBJ. = $(TCL_OBJ.0)
          235  +
          236  +EXTRAOBJ = \
          237  +  $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
          238  +  $(OBJDIR)/shell.o \
          239  +  $(OBJDIR)/th.o \
          240  +  $(OBJDIR)/th_lang.o \
          241  +  $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
          242  +  $(OBJDIR)/cson_amalgamation.o
          243  +
          244  +$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
          245  +	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
   152    246   
   153    247   # This rule prevents make from using its default rules to try build
   154    248   # an executable named "manifest" out of the file named "manifest.c"
   155    249   #
   156    250   $(SRCDIR)/../manifest:	
   157    251   	# noop
   158    252   
   159    253   clean:	
   160         -	rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
   161         -	rm -f translate makeheaders mkindex page_index.h headers}
          254  +	rm -rf $(OBJDIR)/* $(APPNAME)
   162    255   
   163         -set hfiles {}
   164         -foreach s [lsort $src] {lappend hfiles $s.h}
   165         -puts "\trm -f $hfiles\n"
          256  +}
   166    257   
   167    258   set mhargs {}
   168    259   foreach s [lsort $src] {
   169         -  append mhargs " ${s}_.c:$s.h"
          260  +  append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
   170    261     set extra_h($s) {}
   171    262   }
   172    263   append mhargs " \$(SRCDIR)/sqlite3.h"
   173    264   append mhargs " \$(SRCDIR)/th.h"
   174         -append mhargs " VERSION.h"
   175         -puts "page_index.h: \$(TRANS_SRC) mkindex"
   176         -puts "\t./mkindex \$(TRANS_SRC) >$@"
   177         -puts "headers:\tpage_index.h makeheaders VERSION.h"
   178         -puts "\t./makeheaders $mhargs"
   179         -puts "\ttouch headers"
   180         -puts "headers: Makefile"
   181         -puts "Makefile:"
   182         -set extra_h(main) page_index.h
   183         -
   184         -foreach s [lsort $src] {
   185         -  puts "${s}_.c:\t\$(SRCDIR)/$s.c translate"
   186         -  puts "\t./translate \$(SRCDIR)/$s.c >${s}_.c\n"
   187         -  puts "\$(OBJDIR)/$s.o:\t${s}_.c $s.h $extra_h($s) \$(SRCDIR)/config.h"
   188         -  puts "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c ${s}_.c\n"
   189         -  puts "$s.h:\theaders"
   190         -#  puts "\t./makeheaders $mhargs\n\ttouch headers\n"
   191         -#  puts "\t./makeheaders ${s}_.c:${s}.h\n"
   192         -}
   193         -
   194         -
   195         -puts "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
          265  +#append mhargs " \$(SRCDIR)/cson_amalgamation.h"
          266  +append mhargs " \$(OBJDIR)/VERSION.h"
          267  +writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
          268  +writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@"
          269  +writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h"
          270  +writeln "\t\$(OBJDIR)/makeheaders $mhargs"
          271  +writeln "\ttouch \$(OBJDIR)/headers"
          272  +writeln "\$(OBJDIR)/headers: Makefile"
          273  +writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/json_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h"
          274  +writeln "Makefile:"
          275  +set extra_h(main) \$(OBJDIR)/page_index.h
          276  +
          277  +foreach s [lsort $src] {
          278  +  writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
          279  +  writeln "\t\$(OBJDIR)/translate \$(SRCDIR)/$s.c >\$(OBJDIR)/${s}_.c\n"
          280  +  writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h $extra_h($s) \$(SRCDIR)/config.h"
          281  +  writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n"
          282  +  writeln "\$(OBJDIR)/$s.h:\t\$(OBJDIR)/headers"
          283  +}
          284  +
          285  +
          286  +writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
   196    287   set opt {-DSQLITE_OMIT_LOAD_EXTENSION=1}
   197    288   append opt " -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4"
   198    289   #append opt " -DSQLITE_ENABLE_FTS3=1"
          290  +append opt " -DSQLITE_ENABLE_STAT3"
   199    291   append opt " -Dlocaltime=fossil_localtime"
   200    292   append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0"
   201         -puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
          293  +set SQLITE_OPTIONS $opt
          294  +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
          295  +
          296  +writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
          297  +set opt {-Dmain=sqlite3_shell}
          298  +append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
          299  +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
          300  +
          301  +writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
          302  +writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
          303  +
          304  +writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
          305  +writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
          306  +
          307  +writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
          308  +writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n"
          309  +
          310  +set opt {}
          311  +writeln {
          312  +$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
          313  +	$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
          314  +
          315  +#
          316  +# The list of all the targets that do not correspond to real files. This stops
          317  +# 'make' from getting confused when someone makes an error in a rule.
          318  +#
          319  +
          320  +.PHONY: all install test clean
          321  +}
          322  +
          323  +close $output_file
          324  +#
          325  +# End of the main.mk output
          326  +##############################################################################
          327  +##############################################################################
          328  +##############################################################################
          329  +# Begin win/Makefile.mingw output
          330  +#
          331  +puts "building ../win/Makefile.mingw"
          332  +set output_file [open ../win/Makefile.mingw w]
          333  +fconfigure $output_file -translation binary
          334  +
          335  +writeln {#!/usr/bin/make
          336  +#
          337  +##############################################################################
          338  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
          339  +##############################################################################
          340  +#
          341  +# This file is automatically generated.  Instead of editing this
          342  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
          343  +# to regenerate this file.
          344  +#
          345  +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using
          346  +# MinGW or MinGW-w64.
          347  +#
          348  +
          349  +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers.
          350  +#    By default, this is an empty string (i.e. use the native compiler).
          351  +#
          352  +PREFIX =
          353  +# PREFIX = mingw32-
          354  +# PREFIX = i686-pc-mingw32-
          355  +# PREFIX = i686-w64-mingw32-
          356  +# PREFIX = x86_64-w64-mingw32-
          357  +
          358  +#### The toplevel directory of the source tree.  Fossil can be built
          359  +#    in a directory that is separate from the source tree.  Just change
          360  +#    the following to point from the build directory to the src/ folder.
          361  +#
          362  +SRCDIR = src
          363  +
          364  +#### The directory into which object code files should be written.
          365  +#
          366  +OBJDIR = wbld
          367  +
          368  +#### C Compiler and options for use in building executables that
          369  +#    will run on the platform that is doing the build.  This is used
          370  +#    to compile code-generator programs as part of the build process.
          371  +#    See TCC below for the C compiler for building the finished binary.
          372  +#
          373  +BCC = gcc
          374  +
          375  +#### Enable compiling with debug symbols (much larger binary)
          376  +#
          377  +# FOSSIL_ENABLE_SYMBOLS = 1
          378  +
          379  +#### Enable JSON (http://www.json.org) support using "cson"
          380  +#
          381  +# FOSSIL_ENABLE_JSON = 1
          382  +
          383  +#### Enable markdown support
          384  +#
          385  +# FOSSIL_ENABLE_MARKDOWN = 1
          386  +
          387  +#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
          388  +#
          389  +# FOSSIL_ENABLE_SSL = 1
          390  +
          391  +#### Enable scripting support via Tcl/Tk
          392  +#
          393  +# FOSSIL_ENABLE_TCL = 1
          394  +
          395  +#### Load Tcl using the stubs mechanism
          396  +#
          397  +# FOSSIL_ENABLE_TCL_STUBS = 1
          398  +
          399  +#### Use the Tcl source directory instead of the install directory?
          400  +#    This is useful when Tcl has been compiled statically with MinGW.
          401  +#
          402  +FOSSIL_TCL_SOURCE = 1
          403  +
          404  +#### Check if the workaround for the MinGW command line handling needs to
          405  +#    be enabled by default.
          406  +#
          407  +ifndef BROKEN_MINGW_CMDLINE
          408  +ifeq (,$(findstring w64-mingw32,$(PREFIX)))
          409  +BROKEN_MINGW_CMDLINE = 1
          410  +endif
          411  +endif
          412  +
          413  +#### The directories where the zlib include and library files are located.
          414  +#
          415  +ZINCDIR = $(SRCDIR)/../compat/zlib
          416  +ZLIBDIR = $(SRCDIR)/../compat/zlib
          417  +
          418  +#### The directories where the OpenSSL include and library files are located.
          419  +#    The recommended usage here is to use the Sysinternals junction tool
          420  +#    to create a hard link between an "openssl-1.x" sub-directory of the
          421  +#    Fossil source code directory and the target OpenSSL source directory.
          422  +#
          423  +OPENSSLINCDIR = $(SRCDIR)/../openssl-1.0.1c/include
          424  +OPENSSLLIBDIR = $(SRCDIR)/../openssl-1.0.1c
          425  +
          426  +#### Either the directory where the Tcl library is installed or the Tcl
          427  +#    source code directory resides (depending on the value of the macro
          428  +#    FOSSIL_TCL_SOURCE).  If this points to the Tcl install directory,
          429  +#    this directory must have "include" and "lib" sub-directories.  If
          430  +#    this points to the Tcl source code directory, this directory must
          431  +#    have "generic" and "win" sub-directories.  The recommended usage
          432  +#    here is to use the Sysinternals junction tool to create a hard
          433  +#    link between a "tcl-8.x" sub-directory of the Fossil source code
          434  +#    directory and the target Tcl directory.  This removes the need to
          435  +#    hard-code the necessary paths in this Makefile.
          436  +#
          437  +TCLDIR = $(SRCDIR)/../tcl-8.6
          438  +
          439  +#### The Tcl source code directory.  This defaults to the same value as
          440  +#    TCLDIR macro (above), which may not be correct.  This value will
          441  +#    only be used if the FOSSIL_TCL_SOURCE macro is defined.
          442  +#
          443  +TCLSRCDIR = $(TCLDIR)
          444  +
          445  +#### The Tcl include and library directories.  These values will only be
          446  +#    used if the FOSSIL_TCL_SOURCE macro is not defined.
          447  +#
          448  +TCLINCDIR = $(TCLDIR)/include
          449  +TCLLIBDIR = $(TCLDIR)/lib
          450  +
          451  +#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
          452  +#
          453  +ifdef FOSSIL_ENABLE_TCL_STUBS
          454  +LIBTCL = -ltclstub86
          455  +else
          456  +LIBTCL = -ltcl86
          457  +endif
          458  +
          459  +#### C Compile and options for use in building executables that
          460  +#    will run on the target platform.  This is usually the same
          461  +#    as BCC, unless you are cross-compiling.  This C compiler builds
          462  +#    the finished binary for fossil.  The BCC compiler above is used
          463  +#    for building intermediate code-generator tools.
          464  +#
          465  +TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
          466  +
          467  +#### Add the necessary command line options to build with debugging
          468  +#    symbols, if enabled.
          469  +#
          470  +ifdef FOSSIL_ENABLE_SYMBOLS
          471  +TCC += -g
          472  +endif
          473  +
          474  +#### Compile resources for use in building executables that will run
          475  +#    on the target platform.
          476  +#
          477  +RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
          478  +
          479  +# With HTTPS support
          480  +ifdef FOSSIL_ENABLE_SSL
          481  +TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
          482  +RCC += -I$(OPENSSLINCDIR)
          483  +endif
          484  +
          485  +# With Tcl support
          486  +ifdef FOSSIL_ENABLE_TCL
          487  +ifdef FOSSIL_TCL_SOURCE
          488  +TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          489  +RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          490  +else
          491  +TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
          492  +RCC += -I$(TCLINCDIR)
          493  +endif
          494  +endif
          495  +
          496  +# With MinGW command line handling workaround
          497  +ifdef BROKEN_MINGW_CMDLINE
          498  +TCC += -DBROKEN_MINGW_CMDLINE=1
          499  +RCC += -DBROKEN_MINGW_CMDLINE=1
          500  +endif
          501  +
          502  +# With HTTPS support
          503  +ifdef FOSSIL_ENABLE_SSL
          504  +TCC += -DFOSSIL_ENABLE_SSL=1
          505  +RCC += -DFOSSIL_ENABLE_SSL=1
          506  +endif
          507  +
          508  +# With Tcl support
          509  +ifdef FOSSIL_ENABLE_TCL
          510  +TCC += -DFOSSIL_ENABLE_TCL=1
          511  +RCC += -DFOSSIL_ENABLE_TCL=1
          512  +# Either statically linked or via stubs
          513  +ifdef FOSSIL_ENABLE_TCL_STUBS
          514  +TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          515  +RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          516  +else
          517  +TCC += -DSTATIC_BUILD
          518  +RCC += -DSTATIC_BUILD
          519  +endif
          520  +endif
          521  +
          522  +# With JSON support
          523  +ifdef FOSSIL_ENABLE_JSON
          524  +TCC += -DFOSSIL_ENABLE_JSON=1
          525  +RCC += -DFOSSIL_ENABLE_JSON=1
          526  +endif
          527  +
          528  +# With markdown support
          529  +ifdef FOSSIL_ENABLE_MARKDOWN
          530  +TCC += -DFOSSIL_ENABLE_MARKDOWN=1
          531  +RCC += -DFOSSIL_ENABLE_MARKDOWN=1
          532  +endif
          533  +
          534  +#### We add the -static option here so that we can build a static
          535  +#    executable that will run in a chroot jail.
          536  +#
          537  +LIB = -static
          538  +
          539  +# MinGW: If available, use the Unicode capable runtime startup code.
          540  +ifndef BROKEN_MINGW_CMDLINE
          541  +LIB += -municode
          542  +endif
          543  +
          544  +# OpenSSL: Add the necessary libraries required, if enabled.
          545  +ifdef FOSSIL_ENABLE_SSL
          546  +LIB += -lssl -lcrypto -lgdi32
          547  +endif
          548  +
          549  +# Tcl: Add the necessary libraries required, if enabled.
          550  +ifdef FOSSIL_ENABLE_TCL
          551  +LIB += $(LIBTCL)
          552  +endif
          553  +
          554  +#### Extra arguments for linking the finished binary.  Fossil needs
          555  +#    to link against the Z-Lib compression library.  There are no
          556  +#    other mandatory dependencies.
          557  +#
          558  +LIB += -lmingwex -lz
          559  +
          560  +#### These libraries MUST appear in the same order as they do for Tcl
          561  +#    or linking with it will not work (exact reason unknown).
          562  +#
          563  +ifdef FOSSIL_ENABLE_TCL
          564  +ifdef FOSSIL_ENABLE_TCL_STUBS
          565  +LIB += -lkernel32 -lws2_32
          566  +else
          567  +LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
          568  +endif
          569  +else
          570  +LIB += -lkernel32 -lws2_32
          571  +endif
          572  +
          573  +#### Tcl shell for use in running the fossil test suite.  This is only
          574  +#    used for testing.
          575  +#
          576  +TCLSH = tclsh
          577  +
          578  +#### Nullsoft installer MakeNSIS location
          579  +#
          580  +MAKENSIS = "$(ProgramFiles)\NSIS\MakeNSIS.exe"
          581  +
          582  +#### Include a configuration file that can override any one of these settings.
          583  +#
          584  +-include config.w32
          585  +
          586  +# STOP HERE
          587  +# You should not need to change anything below this line
          588  +#--------------------------------------------------------
          589  +XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
          590  +}
          591  +writeln -nonewline "SRC ="
          592  +foreach s [lsort $src] {
          593  +  writeln -nonewline " \\\n  \$(SRCDIR)/$s.c"
          594  +}
          595  +writeln "\n"
          596  +writeln -nonewline "TRANS_SRC ="
          597  +foreach s [lsort $src] {
          598  +  writeln -nonewline " \\\n  \$(OBJDIR)/${s}_.c"
          599  +}
          600  +writeln "\n"
          601  +writeln -nonewline "OBJ ="
          602  +foreach s [lsort $src] {
          603  +  writeln -nonewline " \\\n \$(OBJDIR)/$s.o"
          604  +}
          605  +writeln "\n"
          606  +writeln "APPNAME = ${name}.exe"
          607  +writeln {
          608  +#### If the USE_WINDOWS variable exists, it is assumed that we are building
          609  +#    inside of a Windows-style shell; otherwise, it is assumed that we are
          610  +#    building inside of a Unix-style shell.  Note that the "move" command is
          611  +#    broken when attempting to use it from the Windows shell via MinGW make
          612  +#    because the SHELL variable is only used for certain commands that are
          613  +#    recognized internally by make.
          614  +#
          615  +ifdef USE_WINDOWS
          616  +TRANSLATE   = $(subst /,\,$(OBJDIR)/translate)
          617  +MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders)
          618  +MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex)
          619  +VERSION     = $(subst /,\,$(OBJDIR)/version)
          620  +CP          = copy
          621  +MV          = copy
          622  +RM          = del /Q
          623  +MKDIR       = -mkdir
          624  +RMDIR       = rmdir /S /Q
          625  +else
          626  +TRANSLATE   = $(OBJDIR)/translate
          627  +MAKEHEADERS = $(OBJDIR)/makeheaders
          628  +MKINDEX     = $(OBJDIR)/mkindex
          629  +VERSION     = $(OBJDIR)/version
          630  +CP          = cp
          631  +MV          = mv
          632  +RM          = rm -f
          633  +MKDIR       = -mkdir -p
          634  +RMDIR       = rm -rf
          635  +endif}
          636  +
          637  +writeln {
          638  +all:	$(OBJDIR) $(APPNAME)
          639  +
          640  +$(OBJDIR)/fossil.o:	$(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h
          641  +ifdef USE_WINDOWS
          642  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
          643  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
          644  +else
          645  +	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
          646  +	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
          647  +endif
          648  +	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o
          649  +
          650  +install:	$(OBJDIR) $(APPNAME)
          651  +ifdef USE_WINDOWS
          652  +	$(MKDIR) $(subst /,\,$(INSTALLDIR))
          653  +	$(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR))
          654  +else
          655  +	$(MKDIR) $(INSTALLDIR)
          656  +	$(MV) $(APPNAME) $(INSTALLDIR)
          657  +endif
          658  +
          659  +$(OBJDIR):
          660  +ifdef USE_WINDOWS
          661  +	$(MKDIR) $(subst /,\,$(OBJDIR))
          662  +else
          663  +	$(MKDIR) $(OBJDIR)
          664  +endif
          665  +
          666  +$(OBJDIR)/translate:	$(SRCDIR)/translate.c
          667  +	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
          668  +
          669  +$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
          670  +	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
          671  +
          672  +$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
          673  +	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
          674  +
          675  +$(VERSION): $(SRCDIR)/mkversion.c
          676  +	$(BCC) -o $(OBJDIR)/version $(SRCDIR)/mkversion.c
          677  +
          678  +# WARNING. DANGER. Running the test suite modifies the repository the
          679  +# build is done from, i.e. the checkout belongs to. Do not sync/push
          680  +# the repository after running the tests.
          681  +test:	$(OBJDIR) $(APPNAME)
          682  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
          683  +
          684  +$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
          685  +	$(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
          686  +
          687  +EXTRAOBJ = \
          688  +  $(OBJDIR)/sqlite3.o \
          689  +  $(OBJDIR)/shell.o \
          690  +  $(OBJDIR)/th.o \
          691  +  $(OBJDIR)/th_lang.o \
          692  +  $(OBJDIR)/cson_amalgamation.o
          693  +
          694  +ifdef FOSSIL_ENABLE_TCL
          695  +EXTRAOBJ +=  $(OBJDIR)/th_tcl.o
          696  +endif
          697  +
          698  +zlib:
          699  +	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a
          700  +
          701  +$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib
          702  +	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o
          703  +
          704  +# This rule prevents make from using its default rules to try build
          705  +# an executable named "manifest" out of the file named "manifest.c"
          706  +#
          707  +$(SRCDIR)/../manifest:
          708  +	# noop
          709  +
          710  +clean:
          711  +ifdef USE_WINDOWS
          712  +	$(RM) $(subst /,\,$(APPNAME))
          713  +	$(RMDIR) $(subst /,\,$(OBJDIR))
          714  +else
          715  +	$(RM) $(APPNAME)
          716  +	$(RMDIR) $(OBJDIR)
          717  +endif
          718  +
          719  +setup: $(OBJDIR) $(APPNAME)
          720  +	$(MAKENSIS) ./fossil.nsi
          721  +}
          722  +
          723  +set mhargs {}
          724  +foreach s [lsort $src] {
          725  +  if {[string length $mhargs] > 0} {append mhargs " \\\n\t\t"}
          726  +  append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
          727  +  set extra_h($s) {}
          728  +}
          729  +append mhargs " \\\n\t\t\$(SRCDIR)/sqlite3.h"
          730  +append mhargs " \\\n\t\t\$(SRCDIR)/th.h"
          731  +append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h"
          732  +writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
          733  +writeln "\t\$(MKINDEX) \$(TRANS_SRC) >$@\n"
          734  +writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h"
          735  +writeln "\t\$(MAKEHEADERS) $mhargs"
          736  +writeln "\techo Done >\$(OBJDIR)/headers\n"
          737  +writeln "\$(OBJDIR)/headers: Makefile\n"
          738  +writeln "Makefile:\n"
          739  +set extra_h(main) \$(OBJDIR)/page_index.h
          740  +
          741  +foreach s [lsort $src] {
          742  +  writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
          743  +  writeln "\t\$(TRANSLATE) \$(SRCDIR)/$s.c >\$(OBJDIR)/${s}_.c\n"
          744  +  writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h $extra_h($s) \$(SRCDIR)/config.h"
          745  +  writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n"
          746  +  writeln "\$(OBJDIR)/${s}.h:\t\$(OBJDIR)/headers\n"
          747  +}
          748  +
          749  +
          750  +writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
          751  +set opt $SQLITE_OPTIONS
          752  +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
          753  +
          754  +set opt {}
          755  +writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
          756  +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n"
          757  +writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"
          758  +
          759  +writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
          760  +set opt {-Dmain=sqlite3_shell}
          761  +append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
          762  +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
          763  +
          764  +writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
          765  +writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
          766  +
          767  +writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
          768  +writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
          769  +
          770  +writeln {ifdef FOSSIL_ENABLE_TCL
          771  +$(OBJDIR)/th_tcl.o:	$(SRCDIR)/th_tcl.c
          772  +	$(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
          773  +endif}
          774  +
          775  +close $output_file
          776  +#
          777  +# End of the win/Makefile.mingw output
          778  +##############################################################################
          779  +##############################################################################
          780  +##############################################################################
          781  +# Begin win/Makefile.dmc output
          782  +#
          783  +puts "building ../win/Makefile.dmc"
          784  +set output_file [open ../win/Makefile.dmc w]
          785  +fconfigure $output_file -translation binary
          786  +
          787  +writeln {#
          788  +##############################################################################
          789  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
          790  +##############################################################################
          791  +#
          792  +# This file is automatically generated.  Instead of editing this
          793  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
          794  +# to regenerate this file.
          795  +#
          796  +B      = ..
          797  +SRCDIR = $B\src
          798  +OBJDIR = .
          799  +O      = .obj
          800  +E      = .exe
          801  +
          802  +
          803  +# Maybe DMDIR, SSL or INCL needs adjustment
          804  +DMDIR  = c:\DM
          805  +INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include
          806  +
          807  +#SSL   =  -DFOSSIL_ENABLE_SSL=1
          808  +SSL    =
          809  +
          810  +CFLAGS = -o
          811  +BCC    = $(DMDIR)\bin\dmc $(CFLAGS)
          812  +TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
          813  +LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
          814  +}
          815  +writeln "SQLITE_OPTIONS = $SQLITE_OPTIONS\n"
          816  +writeln -nonewline "SRC   = "
          817  +foreach s [lsort $src] {
          818  +  writeln -nonewline "${s}_.c "
          819  +}
          820  +writeln "\n"
          821  +writeln -nonewline "OBJ   = "
          822  +foreach s [lsort $src] {
          823  +  writeln -nonewline "\$(OBJDIR)\\$s\$O "
          824  +}
          825  +writeln "\$(OBJDIR)\\shell\$O \$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
          826  +writeln {
          827  +
          828  +RC=$(DMDIR)\bin\rcc
          829  +RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
          830  +
          831  +APPNAME = $(OBJDIR)\fossil$(E)
          832  +
          833  +all: $(APPNAME)
          834  +
          835  +$(APPNAME) : translate$E mkindex$E headers  $(OBJ) $(OBJDIR)\link
          836  +	cd $(OBJDIR) 
          837  +	$(DMDIR)\bin\link @link
          838  +
          839  +$(OBJDIR)\fossil.res:	$B\win\fossil.rc
          840  +	$(RC) $(RCFLAGS) -o$@ $**
          841  +
          842  +$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res}
          843  +writeln -nonewline "\t+echo "
          844  +foreach s [lsort $src] {
          845  +  writeln -nonewline "$s "
          846  +}
          847  +writeln "shell sqlite3 th th_lang > \$@"
          848  +writeln "\t+echo fossil >> \$@"
          849  +writeln "\t+echo fossil >> \$@"
          850  +writeln "\t+echo \$(LIBS) >> \$@"
          851  +writeln "\t+echo. >> \$@"
          852  +writeln "\t+echo fossil >> \$@"
          853  +
          854  +writeln {
          855  +translate$E: $(SRCDIR)\translate.c
          856  +	$(BCC) -o$@ $**
          857  +
          858  +makeheaders$E: $(SRCDIR)\makeheaders.c
          859  +	$(BCC) -o$@ $**
          860  +
          861  +mkindex$E: $(SRCDIR)\mkindex.c
          862  +	$(BCC) -o$@ $**
          863  +
          864  +version$E: $B\src\mkversion.c
          865  +	$(BCC) -o$@ $**
          866  +
          867  +$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
          868  +	$(TCC) -o$@ -c -Dmain=sqlite3_shell $(SQLITE_OPTIONS) $**
          869  +
          870  +$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
          871  +	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $**
          872  +
          873  +$(OBJDIR)\th$O : $(SRCDIR)\th.c
          874  +	$(TCC) -o$@ -c $**
          875  +
          876  +$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
          877  +	$(TCC) -o$@ -c $**
          878  +
          879  +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
          880  +	cp $@ $@
          881  +
          882  +VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
          883  +	+$** > $@
          884  +
          885  +page_index.h: mkindex$E $(SRC) 
          886  +	+$** > $@
          887  +
          888  +clean:
          889  +	-del $(OBJDIR)\*.obj
          890  +	-del *.obj *_.c *.h *.map
          891  +
          892  +realclean:
          893  +	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
          894  +
          895  +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
          896  +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
          897  +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
          898  +$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
          899  +$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
          900  +$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
          901  +$(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h
          902  +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
          903  +$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
          904  +$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
          905  +$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
          906  +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
          907  +$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
          908  +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
          909  +
          910  +
          911  +}
          912  +foreach s [lsort $src] {
          913  +  writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
          914  +  writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n"
          915  +  writeln "${s}_.c : \$(SRCDIR)\\$s.c"
          916  +  writeln "\t+translate\$E \$** > \$@\n"
          917  +}
          918  +
          919  +writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
          920  +foreach s [lsort $src] {
          921  +  writeln -nonewline "${s}_.c:$s.h "
          922  +}
          923  +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
          924  +writeln "\t@copy /Y nul: headers"
          925  +
          926  +close $output_file
          927  +#
          928  +# End of the win/Makefile.dmc output
          929  +##############################################################################
          930  +##############################################################################
          931  +##############################################################################
          932  +# Begin win/Makefile.msc output
          933  +#
          934  +puts "building ../win/Makefile.msc"
          935  +set output_file [open ../win/Makefile.msc w]
          936  +fconfigure $output_file -translation binary
          937  +
          938  +writeln {#
          939  +##############################################################################
          940  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
          941  +##############################################################################
          942  +#
          943  +# This file is automatically generated.  Instead of editing this
          944  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
          945  +# to regenerate this file.
          946  +#
          947  +B      = ..
          948  +SRCDIR = $B\src
          949  +OBJDIR = .
          950  +OX     = .
          951  +O      = .obj
          952  +E      = .exe
          953  +
          954  +# Uncomment below for SSL support
          955  +SSL =
          956  +SSLLIB =
          957  +# SSL = -DFOSSIL_ENABLE_SSL=1
          958  +# SSLLIB  = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
          959  +
          960  +# zlib options
          961  +ZINCDIR = $(B)\compat\zlib
          962  +ZLIBDIR = $(B)\compat\zlib
          963  +ZLIB    = zlib.lib
          964  +
          965  +# Uncomment to enable JSON API
          966  +# FOSSIL_ENABLE_JSON = 1
          967  +
          968  +# Uncomment to enable markdown support
          969  +# FOSSIL_ENABLE_MARKDOWN = 1
          970  +
          971  +INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
          972  +
          973  +CFLAGS = -nologo -MT -O2
          974  +BCC    = $(CC) $(CFLAGS)
          975  +TCC    = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
          976  +RCC    = rc -D_WIN32 -D_MSC_VER $(INCL)
          977  +LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
          978  +LIBDIR = -LIBPATH:$(ZLIBDIR)
          979  +
          980  +!ifdef FOSSIL_ENABLE_JSON
          981  +TCC = $(TCC) -DFOSSIL_ENABLE_JSON
          982  +RCC = $(RCC) -DFOSSIL_ENABLE_JSON
          983  +!endif
          984  +
          985  +!ifdef FOSSIL_ENABLE_MARKDOWN
          986  +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN
          987  +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN
          988  +!endif
          989  +}
          990  +regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
          991  +set j " \\\n                 "
          992  +writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n"
          993  +writeln -nonewline "SRC   = "
          994  +set i 0
          995  +foreach s [lsort $src] {
          996  +  if {$i > 0} {
          997  +    writeln " \\"
          998  +    writeln -nonewline "        "
          999  +  }
         1000  +  writeln -nonewline "${s}_.c"; incr i
         1001  +}
         1002  +writeln "\n"
         1003  +set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation]
         1004  +writeln -nonewline "OBJ   = "
         1005  +set i 0
         1006  +foreach s [lsort [concat $src $AdditionalObj]] {
         1007  +  if {$i > 0} {
         1008  +    writeln " \\"
         1009  +    writeln -nonewline "        "
         1010  +  }
         1011  +  writeln -nonewline "\$(OX)\\$s\$O"; incr i
         1012  +}
         1013  +writeln " \\"
         1014  +writeln -nonewline "        \$(OX)\\fossil.res\n"
         1015  +writeln {
         1016  +APPNAME = $(OX)\fossil$(E)
         1017  +
         1018  +all: $(OX) $(APPNAME)
         1019  +
         1020  +zlib:
         1021  +	@echo Building zlib from "$(ZLIBDIR)"...
         1022  +	@pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd
         1023  +
         1024  +$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib
         1025  +	cd $(OX) 
         1026  +	link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
         1027  +
         1028  +$(OX)\linkopts: $B\win\Makefile.msc}
         1029  +set redir {>}
         1030  +foreach s [lsort [concat $src $AdditionalObj]] {
         1031  +  writeln "\techo \$(OX)\\$s.obj $redir \$@"
         1032  +  set redir {>>}
         1033  +}
         1034  +writeln "\techo \$(LIBS) >> \$@\n\n"
         1035  +
         1036  +writeln {
         1037  +
         1038  +$(OX):
         1039  +	@-mkdir $@
         1040  +
         1041  +translate$E: $(SRCDIR)\translate.c
         1042  +	$(BCC) $**
         1043  +
         1044  +makeheaders$E: $(SRCDIR)\makeheaders.c
         1045  +	$(BCC) $**
         1046  +
         1047  +mkindex$E: $(SRCDIR)\mkindex.c
         1048  +	$(BCC) $**
         1049  +
         1050  +mkversion$E: $B\src\mkversion.c
         1051  +	$(BCC) $**
         1052  +
         1053  +$(OX)\shell$O : $(SRCDIR)\shell.c
         1054  +	$(TCC) /Fo$@ /Dmain=sqlite3_shell $(SQLITE_OPTIONS) -c $(SRCDIR)\shell.c
         1055  +
         1056  +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
         1057  +	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $**
         1058  +
         1059  +$(OX)\th$O : $(SRCDIR)\th.c
         1060  +	$(TCC) /Fo$@ -c $**
         1061  +
         1062  +$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
         1063  +	$(TCC) /Fo$@ -c $**
         1064  +
         1065  +VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
         1066  +	$** > $@
         1067  +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c
         1068  +	$(TCC) /Fo$@ -c $**
         1069  +
         1070  +page_index.h: mkindex$E $(SRC) 
         1071  +	$** > $@
         1072  +
         1073  +clean:
         1074  +	-del $(OX)\*.obj
         1075  +	-del *.obj
         1076  +	-del *_.c
         1077  +	-del *.h
         1078  +	-del *.map
         1079  +	-del *.manifest
         1080  +	-del headers
         1081  +	-del linkopts
         1082  +	-del *.res
         1083  +
         1084  +realclean: clean
         1085  +	-del $(APPNAME)
         1086  +	-del translate$E
         1087  +	-del mkindex$E
         1088  +	-del makeheaders$E
         1089  +	-del mkversion$E
         1090  +
         1091  +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
         1092  +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
         1093  +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
         1094  +$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
         1095  +$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
         1096  +$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
         1097  +$(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h
         1098  +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
         1099  +$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
         1100  +$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
         1101  +$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
         1102  +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
         1103  +$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
         1104  +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
         1105  +
         1106  +}
         1107  +foreach s [lsort $src] {
         1108  +  writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
         1109  +  writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
         1110  +  writeln "${s}_.c : \$(SRCDIR)\\$s.c"
         1111  +  writeln "\ttranslate\$E \$** > \$@\n"
         1112  +}
         1113  +
         1114  +writeln "fossil.res : \$B\\win\\fossil.rc"
         1115  +writeln "\t\$(RCC)  -fo \$@ \$**"
         1116  +
         1117  +writeln "headers: makeheaders\$E page_index.h VERSION.h"
         1118  +writeln -nonewline "\tmakeheaders\$E "
         1119  +set i 0
         1120  +foreach s [lsort $src] {
         1121  +  if {$i > 0} {
         1122  +    writeln " \\"
         1123  +    writeln -nonewline "\t\t\t"
         1124  +  }
         1125  +  writeln -nonewline "${s}_.c:$s.h"; incr i
         1126  +}
         1127  +writeln " \\\n\t\t\t\$(SRCDIR)\\sqlite3.h \\"
         1128  +writeln "\t\t\t\$(SRCDIR)\\th.h \\"
         1129  +writeln "\t\t\tVERSION.h \\"
         1130  +writeln "\t\t\t\$(SRCDIR)\\cson_amalgamation.h"
         1131  +writeln "\t@copy /Y nul: headers"
         1132  +
         1133  +
         1134  +close $output_file
         1135  +#
         1136  +# End of the win/Makefile.msc output
         1137  +##############################################################################
         1138  +##############################################################################
         1139  +##############################################################################
         1140  +# Begin win/Makefile.PellesCGMake output
         1141  +#
         1142  +puts "building ../win/Makefile.PellesCGMake"
         1143  +set output_file [open ../win/Makefile.PellesCGMake w]
         1144  +fconfigure $output_file -translation binary
         1145  +
         1146  +writeln {#
         1147  +##############################################################################
         1148  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
         1149  +##############################################################################
         1150  +#
         1151  +# This file is automatically generated.  Instead of editing this
         1152  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
         1153  +# to regenerate this file.
         1154  +#
         1155  +# HowTo
         1156  +# -----
         1157  +#
         1158  +# This is a Makefile to compile fossil with PellesC from
         1159  +#  http://www.smorgasbordet.com/pellesc/index.htm
         1160  +# In addition to the Compiler envrionment, you need
         1161  +#  gmake from http://sourceforge.net/projects/unxutils/, Pelles make version
         1162  +#        couldn't handle the complex dependencies in this build
         1163  +#  zlib sources
         1164  +# Then you do
         1165  +# 1. create a directory PellesC in the project root directory
         1166  +# 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation
         1167  +# 3. open a dos prompt window and change working directory into PellesC (step 1)
         1168  +# 4. run gmake -f ..\win\Makefile.PellesCGMake
         1169  +#
         1170  +# this file is tested with
         1171  +#   PellesC         5.00.13
         1172  +#   gmake           3.80
         1173  +#   zlib sources    1.2.5
         1174  +#   Windows XP SP 2
         1175  +# and
         1176  +#   PellesC         6.00.4
         1177  +#   gmake           3.80
         1178  +#   zlib sources    1.2.5
         1179  +#   Windows 7 Home Premium
         1180  +#  
         1181  +
         1182  +#  
         1183  +PellesCDir=c:\Programme\PellesC
         1184  +
         1185  +# Select between 32/64 bit code, default is 32 bit
         1186  +#TARGETVERSION=64
         1187  +
         1188  +ifeq ($(TARGETVERSION),64)
         1189  +# 64 bit version
         1190  +TARGETMACHINE_CC=amd64
         1191  +TARGETMACHINE_LN=amd64
         1192  +TARGETEXTEND=64
         1193  +else
         1194  +# 32 bit version
         1195  +TARGETMACHINE_CC=x86
         1196  +TARGETMACHINE_LN=ix86
         1197  +TARGETEXTEND=
         1198  +endif
         1199  +
         1200  +# define the project directories
         1201  +B=..
         1202  +SRCDIR=$(B)/src/
         1203  +WINDIR=$(B)/win/
         1204  +ZLIBSRCDIR=../../zlib/
         1205  +
         1206  +# define linker command and options
         1207  +LINK=$(PellesCDir)/bin/polink.exe
         1208  +LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib
         1209  +
         1210  +# define standard C-compiler and flags, used to compile
         1211  +# the fossil binary. Some special definitions follow for
         1212  +# special files follow
         1213  +CC=$(PellesCDir)\bin\pocc.exe
         1214  +DEFINES=-D_pgmptr=g.argv[0]
         1215  +CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
         1216  +INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)
         1217  +
         1218  +# define commands for building the windows resource files
         1219  +RESOURCE=fossil.res
         1220  +RC=$(PellesCDir)\bin\porc.exe
         1221  +RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION)
         1222  +
         1223  +# define the special utilities files, needed to generate
         1224  +# the automatically generated source files
         1225  +UTILS=translate.exe mkindex.exe makeheaders.exe
         1226  +UTILS_OBJ=$(UTILS:.exe=.obj)
         1227  +UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))
         1228  +
         1229  +# define the sqlite files, which need special flags on compile
         1230  +SQLITESRC=sqlite3.c
         1231  +ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
         1232  +SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
         1233  +SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
         1234  +
         1235  +# define the sqlite shell files, which need special flags on compile
         1236  +SQLITESHELLSRC=shell.c
         1237  +ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
         1238  +SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
         1239  +SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1
         1240  +
         1241  +# define the th scripting files, which need special flags on compile
         1242  +THSRC=th.c th_lang.c
         1243  +ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
         1244  +THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
         1245  +
         1246  +# define the zlib files, needed by this compile
         1247  +ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
         1248  +ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
         1249  +ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))
         1250  +
         1251  +# define all fossil sources, using the standard compile and
         1252  +# source generation. These are all files in SRCDIR, which are not
         1253  +# mentioned as special files above:
         1254  +ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC) $(ORIGSQLITESHELLSRC),$(wildcard $(SRCDIR)*.c))
         1255  +SRC=$(subst $(SRCDIR),,$(ORIGSRC))
         1256  +TRANSLATEDSRC=$(SRC:.c=_.c)
         1257  +TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj)
         1258  +
         1259  +# main target file is the application
         1260  +APPLICATION=fossil.exe
         1261  +
         1262  +# define the standard make target
         1263  +.PHONY:	default
         1264  +default:	page_index.h headers $(APPLICATION)
         1265  +
         1266  +# symbolic target to generate the source generate utils
         1267  +.PHONY:	utils
         1268  +utils:	$(UTILS)
         1269  +
         1270  +# link utils
         1271  +$(UTILS) version.exe:	%.exe:	%.obj
         1272  +	$(LINK) $(LINKFLAGS) -out:"$@" $<
         1273  +
         1274  +# compiling standard fossil utils
         1275  +$(UTILS_OBJ):	%.obj:	$(SRCDIR)%.c
         1276  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
         1277  +
         1278  +# compile special windows utils
         1279  +version.obj:	$(SRCDIR)mkversion.c
         1280  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
         1281  +
         1282  +# generate the translated c-source files
         1283  +$(TRANSLATEDSRC):	%_.c:	$(SRCDIR)%.c translate.exe
         1284  +	translate.exe $< >$@
         1285  +
         1286  +# generate the index source, containing all web references,..
         1287  +page_index.h:	$(TRANSLATEDSRC) mkindex.exe
         1288  +	mkindex.exe $(TRANSLATEDSRC) >$@
         1289  +
         1290  +# extracting version info from manifest
         1291  +VERSION.h:	version.exe ..\manifest.uuid ..\manifest ..\VERSION
         1292  +	version.exe ..\manifest.uuid ..\manifest ..\VERSION  > $@
         1293  +
         1294  +# generate the simplified headers
         1295  +headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h
         1296  +	makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
         1297  +	echo Done >$@
         1298  +
         1299  +# compile C sources with relevant options
         1300  +
         1301  +$(TRANSLATEDOBJ):	%_.obj:	%_.c %.h
         1302  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
         1303  +
         1304  +$(SQLITEOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)%.h
         1305  +	$(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@"
         1306  +
         1307  +$(SQLITESHELLOBJ):	%.obj:	$(SRCDIR)%.c
         1308  +	$(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@"
         1309  +
         1310  +$(THOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)th.h
         1311  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
         1312  +
         1313  +$(ZLIBOBJ):	%.obj:	$(ZLIBSRCDIR)%.c
         1314  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
         1315  +
         1316  +# create the windows resource with icon and version info
         1317  +$(RESOURCE):	%.res:	../win/%.rc ../win/*.ico
         1318  +	$(RC) $(RCFLAGS) $< -Fo"$@"
         1319  +
         1320  +# link the application
         1321  +$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
         1322  +	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)
         1323  +
         1324  +# cleanup
   202   1325   
   203         -puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
   204         -puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
         1326  +.PHONY: clean
         1327  +clean:
         1328  +	del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
         1329  +	del /F $(TRANSLATEDSRC)
         1330  +	del /F *.h headers
         1331  +	del /F $(RESOURCE)
   205   1332   
   206         -puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
   207         -puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
         1333  +.PHONY: clobber
         1334  +clobber: clean
         1335  +	del /F *.exe
         1336  +}

Changes to src/manifest.c.

    24     24   #include "manifest.h"
    25     25   #include <assert.h>
    26     26   
    27     27   #if INTERFACE
    28     28   /*
    29     29   ** Types of control files
    30     30   */
           31  +#define CFTYPE_ANY        0
    31     32   #define CFTYPE_MANIFEST   1
    32     33   #define CFTYPE_CLUSTER    2
    33     34   #define CFTYPE_CONTROL    3
    34     35   #define CFTYPE_WIKI       4
    35     36   #define CFTYPE_TICKET     5
    36     37   #define CFTYPE_ATTACHMENT 6
           38  +#define CFTYPE_EVENT      7
           39  +
           40  +/*
           41  +** File permissions used by Fossil internally.
           42  +*/
           43  +#define PERM_REG          0     /*  regular file  */
           44  +#define PERM_EXE          1     /*  executable    */
           45  +#define PERM_LNK          2     /*  symlink       */
           46  +
           47  +/*
           48  +** A single F-card within a manifest
           49  +*/
           50  +struct ManifestFile { 
           51  +  char *zName;           /* Name of a file */
           52  +  char *zUuid;           /* UUID of the file */
           53  +  char *zPerm;           /* File permissions */
           54  +  char *zPrior;          /* Prior name if the name was changed */
           55  +};
           56  +
    37     57   
    38     58   /*
    39     59   ** A parsed manifest or cluster.
    40     60   */
    41     61   struct Manifest {
    42     62     Blob content;         /* The original content blob */
    43     63     int type;             /* Type of artifact.  One of CFTYPE_xxxxx */
           64  +  int rid;              /* The blob-id for this manifest */
           65  +  char *zBaseline;      /* Baseline manifest.  The B card. */
           66  +  Manifest *pBaseline;  /* The actual baseline manifest */
    44     67     char *zComment;       /* Decoded comment.  The C card. */
    45     68     double rDate;         /* Date and time from D card.  0.0 if no D card. */
    46     69     char *zUser;          /* Name of the user from the U card. */
    47     70     char *zRepoCksum;     /* MD5 checksum of the baseline content.  R card. */
    48     71     char *zWiki;          /* Text of the wiki page.  W card. */
    49     72     char *zWikiTitle;     /* Name of the wiki page. L card. */
           73  +  double rEventDate;    /* Date of an event.  E card. */
           74  +  char *zEventId;       /* UUID for an event.  E card. */
    50     75     char *zTicketUuid;    /* UUID for a ticket. K card. */
    51     76     char *zAttachName;    /* Filename of an attachment. A card. */
    52     77     char *zAttachSrc;     /* UUID of document being attached. A card. */
    53     78     char *zAttachTarget;  /* Ticket or wiki that attachment applies to.  A card */
    54     79     int nFile;            /* Number of F cards */
    55     80     int nFileAlloc;       /* Slots allocated in aFile[] */
    56         -  struct { 
    57         -    char *zName;           /* Name of a file */
    58         -    char *zUuid;           /* UUID of the file */
    59         -    char *zPerm;           /* File permissions */
    60         -    char *zPrior;          /* Prior name if the name was changed */
    61         -    int iRename;           /* index of renamed name in prior/next manifest */
    62         -  } *aFile;             /* One entry for each F card */
           81  +  int iFile;            /* Index of current file in iterator */
           82  +  ManifestFile *aFile;  /* One entry for each F-card */
    63     83     int nParent;          /* Number of parents. */
    64     84     int nParentAlloc;     /* Slots allocated in azParent[] */
    65     85     char **azParent;      /* UUIDs of parents.  One for each P card argument */
           86  +  int nCherrypick;      /* Number of entries in aCherrypick[] */
           87  +  struct {            
           88  +    char *zCPTarget;    /* UUID of cherry-picked version w/ +|- prefix */
           89  +    char *zCPBase;      /* UUID of cherry-pick baseline. NULL for singletons */
           90  +  } *aCherrypick;
    66     91     int nCChild;          /* Number of cluster children */
    67     92     int nCChildAlloc;     /* Number of closts allocated in azCChild[] */
    68     93     char **azCChild;      /* UUIDs of referenced objects in a cluster. M cards */
    69     94     int nTag;             /* Number of T Cards */
    70     95     int nTagAlloc;        /* Slots allocated in aTag[] */
    71     96     struct { 
    72     97       char *zName;           /* Name of the tag */
................................................................................
    78    103     struct { 
    79    104       char *zName;           /* Key or field name */
    80    105       char *zValue;          /* Value of the field */
    81    106     } *aField;            /* One for each J card */
    82    107   };
    83    108   #endif
    84    109   
          110  +/*
          111  +** A cache of parsed manifests.  This reduces the number of
          112  +** calls to manifest_parse() when doing a rebuild.
          113  +*/
          114  +#define MX_MANIFEST_CACHE 6
          115  +static struct {
          116  +  int nxAge;
          117  +  int aAge[MX_MANIFEST_CACHE];
          118  +  Manifest *apManifest[MX_MANIFEST_CACHE];
          119  +} manifestCache;
          120  +
          121  +/*
          122  +** True if manifest_crosslink_begin() has been called but
          123  +** manifest_crosslink_end() is still pending.
          124  +*/
          125  +static int manifest_crosslink_busy = 0;
    85    126   
    86    127   /*
    87    128   ** Clear the memory allocated in a manifest object
    88    129   */
    89         -void manifest_clear(Manifest *p){
    90         -  blob_reset(&p->content);
    91         -  free(p->aFile);
    92         -  free(p->azParent);
    93         -  free(p->azCChild);
    94         -  free(p->aTag);
    95         -  free(p->aField);
    96         -  memset(p, 0, sizeof(*p));
    97         -}
          130  +void manifest_destroy(Manifest *p){
          131  +  if( p ){
          132  +    blob_reset(&p->content);
          133  +    free(p->aFile);
          134  +    free(p->azParent);
          135  +    free(p->azCChild);
          136  +    free(p->aTag);
          137  +    free(p->aField);
          138  +    free(p->aCherrypick);
          139  +    if( p->pBaseline ) manifest_destroy(p->pBaseline);
          140  +    memset(p, 0, sizeof(*p));
          141  +    fossil_free(p);
          142  +  }
          143  +}
          144  +
          145  +/*
          146  +** Add an element to the manifest cache using LRU replacement.
          147  +*/
          148  +void manifest_cache_insert(Manifest *p){
          149  +  while( p ){
          150  +    int i;
          151  +    Manifest *pBaseline = p->pBaseline;
          152  +    p->pBaseline = 0;
          153  +    for(i=0; i<MX_MANIFEST_CACHE; i++){
          154  +      if( manifestCache.apManifest[i]==0 ) break;
          155  +    }
          156  +    if( i>=MX_MANIFEST_CACHE ){
          157  +      int oldest = 0;
          158  +      int oldestAge = manifestCache.aAge[0];
          159  +      for(i=1; i<MX_MANIFEST_CACHE; i++){
          160  +        if( manifestCache.aAge[i]<oldestAge ){
          161  +          oldest = i;
          162  +          oldestAge = manifestCache.aAge[i];
          163  +        }
          164  +      }
          165  +      manifest_destroy(manifestCache.apManifest[oldest]);
          166  +      i = oldest;
          167  +    }
          168  +    manifestCache.aAge[i] = ++manifestCache.nxAge;
          169  +    manifestCache.apManifest[i] = p;
          170  +    p = pBaseline;
          171  +  }
          172  +}
          173  +
          174  +/*
          175  +** Try to extract a line from the manifest cache. Return 1 if found.
          176  +** Return 0 if not found.
          177  +*/
          178  +static Manifest *manifest_cache_find(int rid){
          179  +  int i;
          180  +  Manifest *p;
          181  +  for(i=0; i<MX_MANIFEST_CACHE; i++){
          182  +    if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){
          183  +      p = manifestCache.apManifest[i];
          184  +      manifestCache.apManifest[i] = 0;
          185  +      return p;
          186  +    }
          187  +  }
          188  +  return 0;
          189  +}
          190  +
          191  +/*
          192  +** Clear the manifest cache.
          193  +*/
          194  +void manifest_cache_clear(void){
          195  +  int i;
          196  +  for(i=0; i<MX_MANIFEST_CACHE; i++){
          197  +    if( manifestCache.apManifest[i] ){
          198  +      manifest_destroy(manifestCache.apManifest[i]);
          199  +    }
          200  +  }
          201  +  memset(&manifestCache, 0, sizeof(manifestCache));
          202  +}
          203  +
          204  +#ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
          205  +# define md5sum_init(X)
          206  +# define md5sum_step_text(X,Y)
          207  +#endif
          208  +
          209  +/*
          210  +** Return true if z points to the first character after a blank line.
          211  +** Tolerate either \r\n or \n line endings.
          212  +*/
          213  +static int after_blank_line(const char *z){
          214  +  if( z[-1]!='\n' ) return 0;
          215  +  if( z[-2]=='\n' ) return 1;
          216  +  if( z[-2]=='\r' && z[-3]=='\n' ) return 1;
          217  +  return 0;
          218  +}
          219  +
          220  +/*
          221  +** Remove the PGP signature from the artifact, if there is one.
          222  +*/
          223  +static void remove_pgp_signature(char **pz, int *pn){
          224  +  char *z = *pz;
          225  +  int n = *pn;
          226  +  int i;
          227  +  if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return;
          228  +  for(i=34; i<n && !after_blank_line(z+i); i++){}
          229  +  if( i>=n ) return;
          230  +  z += i;
          231  +  n -= i;
          232  +  *pz = z;
          233  +  for(i=n-1; i>=0; i--){
          234  +    if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){
          235  +      n = i+1;
          236  +      break;
          237  +    }
          238  +  }
          239  +  *pn = n;
          240  +  return;
          241  +}
          242  +
          243  +/*
          244  +** Verify the Z-card checksum on the artifact, if there is such a
          245  +** checksum.  Return 0 if there is no Z-card.  Return 1 if the Z-card
          246  +** exists and is correct.  Return 2 if the Z-card exists and has the wrong
          247  +** value.
          248  +**
          249  +**   0123456789 123456789 123456789 123456789 
          250  +**   Z aea84f4f863865a8d59d0384e4d2a41c
          251  +*/
          252  +static int verify_z_card(const char *z, int n){
          253  +  if( n<35 ) return 0;
          254  +  if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
          255  +  md5sum_init();
          256  +  md5sum_step_text(z, n-35);
          257  +  if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){
          258  +    return 1;
          259  +  }else{
          260  +    return 2;
          261  +  }
          262  +}
          263  +
          264  +/*
          265  +** A structure used for rapid parsing of the Manifest file
          266  +*/
          267  +typedef struct ManifestText ManifestText;
          268  +struct ManifestText {
          269  +  char *z;           /* The first character of the next token */
          270  +  char *zEnd;        /* One character beyond the end of the manifest */
          271  +  int atEol;         /* True if z points to the start of a new line */
          272  +};
          273  +
          274  +/*
          275  +** Return a pointer to the next token.  The token is zero-terminated.
          276  +** Return NULL if there are no more tokens on the current line.
          277  +*/
          278  +static char *next_token(ManifestText *p, int *pLen){
          279  +  char *z;
          280  +  char *zStart;
          281  +  int c;
          282  +  if( p->atEol ) return 0;
          283  +  zStart = z = p->z;
          284  +  while( (c=(*z))!=' ' && c!='\n' ){ z++; }
          285  +  *z = 0;
          286  +  p->z = &z[1];
          287  +  p->atEol = c=='\n';
          288  +  if( pLen ) *pLen = z - zStart;
          289  +  return zStart;
          290  +}
          291  +
          292  +/*
          293  +** Return the card-type for the next card.  Or, return 0 if there are no
          294  +** more cards or if we are not at the end of the current card.
          295  +*/
          296  +static char next_card(ManifestText *p){
          297  +  char c;
          298  +  if( !p->atEol || p->z>=p->zEnd ) return 0;
          299  +  c = p->z[0];
          300  +  if( p->z[1]==' ' ){
          301  +    p->z += 2;
          302  +    p->atEol = 0;
          303  +  }else if( p->z[1]=='\n' ){
          304  +    p->z += 2;
          305  +    p->atEol = 1;
          306  +  }else{
          307  +    c = 0;
          308  +  }
          309  +  return c;
          310  +}
          311  +
          312  +/*
          313  +** Shorthand for a control-artifact parsing error
          314  +*/
          315  +#define SYNTAX(T)  {zErr=(T); goto manifest_syntax_error;}
    98    316   
    99    317   /*
   100    318   ** Parse a blob into a Manifest object.  The Manifest object
   101    319   ** takes over the input blob and will free it when the
   102    320   ** Manifest object is freed.  Zeros are inserted into the blob
   103    321   ** as string terminators so that blob should not be used again.
   104    322   **
   105         -** Return TRUE if the content really is a control file of some
   106         -** kind.  Return FALSE if there are syntax errors.
          323  +** Return a pointer to an allocated Manifest object if the content
          324  +** really is a control file of some kind.  This object needs to be
          325  +** freed by a subsequent call to manifest_destroy().  Return NULL
          326  +** if there are syntax errors.
   107    327   **
   108    328   ** This routine is strict about the format of a control file.
   109    329   ** The format must match exactly or else it is rejected.  This
   110    330   ** rule minimizes the risk that a content file will be mistaken
   111    331   ** for a control file simply because they look the same.
   112    332   **
   113         -** The pContent is reset.  If TRUE is returned, then pContent will
   114         -** be reset when the Manifest object is cleared.  If FALSE is
          333  +** The pContent is reset.  If a pointer is returned, then pContent will
          334  +** be reset when the Manifest object is cleared.  If NULL is
   115    335   ** returned then the Manifest object is cleared automatically
   116    336   ** and pContent is reset before the return.
   117    337   **
   118    338   ** The entire file can be PGP clear-signed.  The signature is ignored.
   119    339   ** The file consists of zero or more cards, one card per line.
   120    340   ** (Except: the content of the W card can extend of multiple lines.)
   121    341   ** Each card is divided into tokens by a single space character.
   122    342   ** The first token is a single upper-case letter which is the card type.
   123    343   ** The card type determines the other parameters to the card.
   124    344   ** Cards must occur in lexicographical order.
   125    345   */
   126         -int manifest_parse(Manifest *p, Blob *pContent){
   127         -  int seenHeader = 0;
          346  +Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){
          347  +  Manifest *p;
   128    348     int seenZ = 0;
   129    349     int i, lineNo=0;
   130         -  Blob line, token, a1, a2, a3, a4;
          350  +  ManifestText x;
   131    351     char cPrevType = 0;
          352  +  char cType;
          353  +  char *z;
          354  +  int n;
          355  +  char *zUuid;
          356  +  int sz = 0;
          357  +  int isRepeat;
          358  +  static Bag seen;
          359  +  const char *zErr = 0;
   132    360   
          361  +  if( rid==0 ){
          362  +    isRepeat = 1;
          363  +  }else if( bag_find(&seen, rid) ){
          364  +    isRepeat = 1;
          365  +  }else{
          366  +    isRepeat = 0;
          367  +    bag_insert(&seen, rid);
          368  +  }
          369  +
          370  +  /* Every control artifact ends with a '\n' character.  Exit early
          371  +  ** if that is not the case for this artifact.
          372  +  */
          373  +  if( !isRepeat ) g.parseCnt[0]++;
          374  +  z = blob_materialize(pContent);
          375  +  n = blob_size(pContent);
          376  +  if( n<=0 || z[n-1]!='\n' ){
          377  +    blob_reset(pContent);
          378  +    blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length");
          379  +    return 0;
          380  +  }
          381  +
          382  +  /* Strip off the PGP signature if there is one.  Then verify the
          383  +  ** Z-card.
          384  +  */
          385  +  remove_pgp_signature(&z, &n);
          386  +  if( verify_z_card(z, n)==2 ){
          387  +    blob_reset(pContent);
          388  +    blob_appendf(pErr, "incorrect Z-card cksum");
          389  +    return 0;
          390  +  }
          391  +
          392  +  /* Verify that the first few characters of the artifact look like
          393  +  ** a control artifact.
          394  +  */
          395  +  if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
          396  +    blob_reset(pContent);
          397  +    blob_appendf(pErr, "line 1 not recognized");
          398  +    return 0;
          399  +  }
          400  +
          401  +  /* Allocate a Manifest object to hold the parsed control artifact.
          402  +  */
          403  +  p = fossil_malloc( sizeof(*p) );
   133    404     memset(p, 0, sizeof(*p));
   134    405     memcpy(&p->content, pContent, sizeof(p->content));
          406  +  p->rid = rid;
   135    407     blob_zero(pContent);
   136    408     pContent = &p->content;
   137    409   
   138         -  blob_zero(&a1);
   139         -  blob_zero(&a2);
   140         -  blob_zero(&a3);
   141         -  md5sum_init();
   142         -  while( blob_line(pContent, &line) ){
   143         -    char *z = blob_buffer(&line);
          410  +  /* Begin parsing, card by card.
          411  +  */
          412  +  x.z = z;
          413  +  x.zEnd = &z[n];
          414  +  x.atEol = 1;
          415  +  while( (cType = next_card(&x))!=0 && cType>=cPrevType ){
   144    416       lineNo++;
   145         -    if( z[0]=='-' ){
   146         -      if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){
   147         -        goto manifest_syntax_error;
   148         -      }
   149         -      if( seenHeader ){
   150         -        break;
   151         -      }
   152         -      while( blob_line(pContent, &line)>2 ){}
   153         -      if( blob_line(pContent, &line)==0 ) break;
   154         -      z = blob_buffer(&line);
   155         -    }
   156         -    if( z[0]<cPrevType ){
   157         -      /* Lines of a manifest must occur in lexicographical order */
   158         -      goto manifest_syntax_error;
   159         -    }
   160         -    cPrevType = z[0];
   161         -    seenHeader = 1;
   162         -    if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
   163         -    switch( z[0] ){
          417  +    switch( cType ){
   164    418         /*
   165    419         **     A <filename> <target> ?<source>?
   166    420         **
   167    421         ** Identifies an attachment to either a wiki page or a ticket.
   168    422         ** <source> is the artifact that is the attachment.  <source>
   169    423         ** is omitted to delete an attachment.  <target> is the name of
   170    424         ** a wiki page or ticket to which that attachment is connected.
   171    425         */
   172    426         case 'A': {
   173    427           char *zName, *zTarget, *zSrc;
   174         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   175         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   176         -        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
          428  +        int nTarget = 0, nSrc = 0;
          429  +        zName = next_token(&x, 0);
          430  +        zTarget = next_token(&x, &nTarget);
          431  +        zSrc = next_token(&x, &nSrc);
          432  +        if( zName==0 || zTarget==0 ) goto manifest_syntax_error;      
   177    433           if( p->zAttachName!=0 ) goto manifest_syntax_error;
   178         -        zName = blob_terminate(&a1);
   179         -        zTarget = blob_terminate(&a2);
   180         -        blob_token(&line, &a3);
   181         -        zSrc = blob_terminate(&a3);
   182    434           defossilize(zName);
   183         -        if( !file_is_simple_pathname(zName) ){
   184         -          goto manifest_syntax_error;
          435  +        if( !file_is_simple_pathname(zName, 0) ){
          436  +          SYNTAX("invalid filename on A-card");
   185    437           }
   186    438           defossilize(zTarget);
   187         -        if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
          439  +        if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
   188    440              && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
   189         -          goto manifest_syntax_error;
          441  +          SYNTAX("invalid target on A-card");
   190    442           }
   191         -        if( blob_size(&a3)>0
   192         -         && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
   193         -          goto manifest_syntax_error;
          443  +        if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
          444  +          SYNTAX("invalid source on A-card");
   194    445           }
   195    446           p->zAttachName = (char*)file_tail(zName);
   196    447           p->zAttachSrc = zSrc;
   197    448           p->zAttachTarget = zTarget;
   198    449           break;
   199    450         }
          451  +
          452  +      /*
          453  +      **    B <uuid>
          454  +      **
          455  +      ** A B-line gives the UUID for the baseline of a delta-manifest.
          456  +      */
          457  +      case 'B': {
          458  +        if( p->zBaseline ) SYNTAX("more than one B-card");
          459  +        p->zBaseline = next_token(&x, &sz);
          460  +        if( p->zBaseline==0 ) SYNTAX("missing UUID on B-card");
          461  +        if( sz!=UUID_SIZE || !validate16(p->zBaseline, UUID_SIZE) ){
          462  +          SYNTAX("invalid UUID on B-card");
          463  +        }
          464  +        break;
          465  +      }
          466  +
   200    467   
   201    468         /*
   202    469         **     C <comment>
   203    470         **
   204    471         ** Comment text is fossil-encoded.  There may be no more than
   205    472         ** one C line.  C lines are required for manifests and are
   206    473         ** disallowed on all other control files.
   207    474         */
   208    475         case 'C': {
   209         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   210         -        if( p->zComment!=0 ) goto manifest_syntax_error;
   211         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   212         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   213         -        p->zComment = blob_terminate(&a1);
          476  +        if( p->zComment!=0 ) SYNTAX("more than one C-card");
          477  +        p->zComment = next_token(&x, 0);
          478  +        if( p->zComment==0 ) SYNTAX("missing comment text on C-card");
   214    479           defossilize(p->zComment);
   215    480           break;
   216    481         }
   217    482   
   218    483         /*
   219    484         **     D <timestamp>
   220    485         **
   221    486         ** The timestamp should be ISO 8601.   YYYY-MM-DDtHH:MM:SS
   222    487         ** There can be no more than 1 D line.  D lines are required
   223    488         ** for all control files except for clusters.
   224    489         */
   225    490         case 'D': {
   226         -        char *zDate;
   227         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   228         -        if( p->rDate!=0.0 ) goto manifest_syntax_error;
   229         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   230         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   231         -        zDate = blob_terminate(&a1);
   232         -        p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
          491  +        if( p->rDate>0.0 ) SYNTAX("more than one D-card");
          492  +        p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
          493  +        if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card");
   233    494           break;
   234    495         }
   235    496   
   236    497         /*
   237         -      **     F <filename> <uuid> ?<permissions>? ?<old-name>?
          498  +      **     E <timestamp> <uuid>
          499  +      **
          500  +      ** An "event" card that contains the timestamp of the event in the 
          501  +      ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event.
          502  +      ** The event timestamp is distinct from the D timestamp.  The D
          503  +      ** timestamp is when the artifact was created whereas the E timestamp
          504  +      ** is when the specific event is said to occur.
          505  +      */
          506  +      case 'E': {
          507  +        if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
          508  +        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
          509  +        if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
          510  +        p->zEventId = next_token(&x, &sz);
          511  +        if( sz!=UUID_SIZE || !validate16(p->zEventId, UUID_SIZE) ){
          512  +          SYNTAX("malformed UUID on E-card");
          513  +        }
          514  +        break;
          515  +      }
          516  +
          517  +      /*
          518  +      **     F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
   238    519         **
   239    520         ** Identifies a file in a manifest.  Multiple F lines are
   240    521         ** allowed in a manifest.  F lines are not allowed in any
   241    522         ** other control file.  The filename and old-name are fossil-encoded.
   242    523         */
   243    524         case 'F': {
   244         -        char *zName, *zUuid, *zPerm, *zPriorName;
   245         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   246         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   247         -        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
   248         -        zName = blob_terminate(&a1);
   249         -        zUuid = blob_terminate(&a2);
   250         -        blob_token(&line, &a3);
   251         -        zPerm = blob_terminate(&a3);
   252         -        if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
   253         -        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
          525  +        char *zName, *zPerm, *zPriorName;
          526  +        zName = next_token(&x,0);
          527  +        if( zName==0 ) SYNTAX("missing filename on F-card");
   254    528           defossilize(zName);
   255         -        if( !file_is_simple_pathname(zName) ){
   256         -          goto manifest_syntax_error;
          529  +        if( !file_is_simple_pathname(zName, 0) ){
          530  +          SYNTAX("F-card filename is not a simple path");
   257    531           }
   258         -        blob_token(&line, &a4);
   259         -        zPriorName = blob_terminate(&a4);
   260         -        if( zPriorName[0] ){
          532  +        zUuid = next_token(&x, &sz);
          533  +        if( p->zBaseline==0 || zUuid!=0 ){
          534  +          if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
          535  +          if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("F-card UUID invalid");
          536  +        }
          537  +        zPerm = next_token(&x,0);
          538  +        zPriorName = next_token(&x,0);
          539  +        if( zPriorName ){
   261    540             defossilize(zPriorName);
   262         -          if( !file_is_simple_pathname(zPriorName) ){
   263         -            goto manifest_syntax_error;
          541  +          if( !file_is_simple_pathname(zPriorName, 0) ){
          542  +            SYNTAX("F-card old filename is not a simple path");
   264    543             }
   265         -        }else{
   266         -          zPriorName = 0;
   267    544           }
   268    545           if( p->nFile>=p->nFileAlloc ){
   269    546             p->nFileAlloc = p->nFileAlloc*2 + 10;
   270         -          p->aFile = realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) );
   271         -          if( p->aFile==0 ) fossil_panic("out of memory");
          547  +          p->aFile = fossil_realloc(p->aFile, 
          548  +                                    p->nFileAlloc*sizeof(p->aFile[0]) );
   272    549           }
   273    550           i = p->nFile++;
   274    551           p->aFile[i].zName = zName;
   275    552           p->aFile[i].zUuid = zUuid;
   276    553           p->aFile[i].zPerm = zPerm;
   277    554           p->aFile[i].zPrior = zPriorName;
   278         -        p->aFile[i].iRename = -1;
   279         -        if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
   280         -          goto manifest_syntax_error;
          555  +        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
          556  +          SYNTAX("incorrect F-card sort order");
   281    557           }
   282    558           break;
   283    559         }
   284    560   
   285    561         /*
   286    562         **     J <name> ?<value>?
   287    563         **
................................................................................
   288    564         ** Specifies a name value pair for ticket.  If the first character
   289    565         ** of <name> is "+" then the <value> is appended to any preexisting
   290    566         ** value.  If <value> is omitted then it is understood to be an
   291    567         ** empty string.
   292    568         */
   293    569         case 'J': {
   294    570           char *zName, *zValue;
   295         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   296         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   297         -        blob_token(&line, &a2);
   298         -        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
   299         -        zName = blob_terminate(&a1);
   300         -        zValue = blob_terminate(&a2);
          571  +        zName = next_token(&x,0);
          572  +        zValue = next_token(&x,0);
          573  +        if( zName==0 ) SYNTAX("name missing from J-card");
          574  +        if( zValue==0 ) zValue = "";
   301    575           defossilize(zValue);
   302    576           if( p->nField>=p->nFieldAlloc ){
   303    577             p->nFieldAlloc = p->nFieldAlloc*2 + 10;
   304         -          p->aField = realloc(p->aField,
          578  +          p->aField = fossil_realloc(p->aField,
   305    579                                  p->nFieldAlloc*sizeof(p->aField[0]) );
   306         -          if( p->aField==0 ) fossil_panic("out of memory");
   307    580           }
   308    581           i = p->nField++;
   309    582           p->aField[i].zName = zName;
   310    583           p->aField[i].zValue = zValue;
   311         -        if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
   312         -          goto manifest_syntax_error;
          584  +        if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
          585  +          SYNTAX("incorrect J-card sort order");
   313    586           }
   314    587           break;
   315    588         }
   316    589   
   317    590   
   318    591         /*
   319    592         **    K <uuid>
   320    593         **
   321    594         ** A K-line gives the UUID for the ticket which this control file
   322    595         ** is amending.
   323    596         */
   324    597         case 'K': {
   325         -        char *zUuid;
   326         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   327         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   328         -        zUuid = blob_terminate(&a1);
   329         -        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
   330         -        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
   331         -        if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
   332         -        p->zTicketUuid = zUuid;
          598  +        if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card");
          599  +        p->zTicketUuid = next_token(&x, &sz);
          600  +        if( sz!=UUID_SIZE ) SYNTAX("K-card UUID is the wrong size");
          601  +        if( !validate16(p->zTicketUuid, UUID_SIZE) ){
          602  +          SYNTAX("invalid K-card UUID");
          603  +        }
   333    604           break;
   334    605         }
   335    606   
   336    607         /*
   337    608         **     L <wikititle>
   338    609         **
   339    610         ** The wiki page title is fossil-encoded.  There may be no more than
   340    611         ** one L line.
   341    612         */
   342    613         case 'L': {
   343         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   344         -        if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
   345         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   346         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   347         -        p->zWikiTitle = blob_terminate(&a1);
          614  +        if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card");
          615  +        p->zWikiTitle = next_token(&x,0);
          616  +        if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card");
   348    617           defossilize(p->zWikiTitle);
   349    618           if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
   350         -          goto manifest_syntax_error;
          619  +          SYNTAX("L-card has malformed wiki name");
   351    620           }
   352    621           break;
   353    622         }
   354    623   
   355    624         /*
   356    625         **    M <uuid>
   357    626         **
   358    627         ** An M-line identifies another artifact by its UUID.  M-lines
   359    628         ** occur in clusters only.
   360    629         */
   361    630         case 'M': {
   362         -        char *zUuid;
   363         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   364         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   365         -        zUuid = blob_terminate(&a1);
   366         -        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
   367         -        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
          631  +        zUuid = next_token(&x, &sz);
          632  +        if( zUuid==0 ) SYNTAX("missing UUID on M-card");
          633  +        if( sz!=UUID_SIZE ) SYNTAX("wrong size for UUID on M-card");
          634  +        if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("UUID invalid on M-card");
   368    635           if( p->nCChild>=p->nCChildAlloc ){
   369    636             p->nCChildAlloc = p->nCChildAlloc*2 + 10;
   370         -          p->azCChild = 
   371         -             realloc(p->azCChild, p->nCChildAlloc*sizeof(p->azCChild[0]) );
   372         -          if( p->azCChild==0 ) fossil_panic("out of memory");
          637  +          p->azCChild = fossil_realloc(p->azCChild
          638  +                                 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
   373    639           }
   374    640           i = p->nCChild++;
   375    641           p->azCChild[i] = zUuid;
   376         -        if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
   377         -          goto manifest_syntax_error;
          642  +        if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
          643  +          SYNTAX("M-card in the wrong order");
   378    644           }
   379    645           break;
   380    646         }
   381    647   
   382    648         /*
   383    649         **     P <uuid> ...
   384    650         **
   385    651         ** Specify one or more other artifacts where are the parents of
   386    652         ** this artifact.  The first parent is the primary parent.  All
   387    653         ** others are parents by merge.
   388    654         */
   389    655         case 'P': {
   390         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   391         -        while( blob_token(&line, &a1) ){
   392         -          char *zUuid;
   393         -          if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
   394         -          zUuid = blob_terminate(&a1);
   395         -          if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
          656  +        while( (zUuid = next_token(&x, &sz))!=0 ){
          657  +          if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card");
          658  +          if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card");
   396    659             if( p->nParent>=p->nParentAlloc ){
   397    660               p->nParentAlloc = p->nParentAlloc*2 + 5;
   398         -            p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
   399         -            if( p->azParent==0 ) fossil_panic("out of memory");
          661  +            p->azParent = fossil_realloc(p->azParent,
          662  +                               p->nParentAlloc*sizeof(char*));
   400    663             }
   401    664             i = p->nParent++;
   402    665             p->azParent[i] = zUuid;
   403    666           }
   404    667           break;
   405    668         }
          669  +
          670  +      /*
          671  +      **     Q (+|-)<uuid> ?<uuid>?
          672  +      **
          673  +      ** Specify one or a range of checkins that are cherrypicked into
          674  +      ** this checkin ("+") or backed out of this checkin ("-").
          675  +      */
          676  +      case 'Q': {
          677  +        if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card");
          678  +        if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card");
          679  +        if( zUuid[0]!='+' && zUuid[0]!='-' ){
          680  +          SYNTAX("Q-card does not begin with '+' or '-'");
          681  +        }
          682  +        if( !validate16(&zUuid[1], UUID_SIZE) ){
          683  +          SYNTAX("invalid UUID on Q-card");
          684  +        }
          685  +        n = p->nCherrypick;
          686  +        p->nCherrypick++;
          687  +        p->aCherrypick = fossil_realloc(p->aCherrypick,
          688  +                                 p->nCherrypick*sizeof(p->aCherrypick[0]));
          689  +        p->aCherrypick[n].zCPTarget = zUuid;
          690  +        p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
          691  +        if( zUuid ){
          692  +          if( sz!=UUID_SIZE ) SYNTAX("wrong size second UUID in Q-card");
          693  +          if( !validate16(zUuid, UUID_SIZE) ){
          694  +            SYNTAX("invalid second UUID on Q-card");
          695  +          }
          696  +        }
          697  +        break;
          698  +      }
   406    699   
   407    700         /*
   408    701         **     R <md5sum>
   409    702         **
   410         -      ** Specify the MD5 checksum of the entire baseline in a
   411         -      ** manifest.
          703  +      ** Specify the MD5 checksum over the name and content of all files
          704  +      ** in the manifest.
   412    705         */
   413    706         case 'R': {
   414         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   415         -        if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
   416         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   417         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   418         -        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
   419         -        p->zRepoCksum = blob_terminate(&a1);
   420         -        if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
          707  +        if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card");
          708  +        p->zRepoCksum = next_token(&x, &sz);
          709  +        if( sz!=32 ) SYNTAX("wrong size cksum on R-card");
          710  +        if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum");
   421    711           break;
   422    712         }
   423    713   
   424    714         /*
   425    715         **    T (+|*|-)<tagname> <uuid> ?<value>?
   426    716         **
   427    717         ** Create or cancel a tag or property.  The tagname is fossil-encoded.
................................................................................
   433    723         ** The tag is applied to <uuid>.  If <uuid> is "*" then the tag is
   434    724         ** applied to the current manifest.  If <value> is provided then 
   435    725         ** the tag is really a property with the given value.
   436    726         **
   437    727         ** Tags are not allowed in clusters.  Multiple T lines are allowed.
   438    728         */
   439    729         case 'T': {
   440         -        char *zName, *zUuid, *zValue;
   441         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   442         -        if( blob_token(&line, &a1)==0 ){
   443         -          goto manifest_syntax_error;
   444         -        }
   445         -        if( blob_token(&line, &a2)==0 ){
   446         -          goto manifest_syntax_error;
   447         -        }
   448         -        zName = blob_terminate(&a1);
   449         -        zUuid = blob_terminate(&a2);
   450         -        if( blob_token(&line, &a3)==0 ){
   451         -          zValue = 0;
   452         -        }else{
   453         -          zValue = blob_terminate(&a3);
   454         -          defossilize(zValue);
   455         -        }
   456         -        if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
          730  +        char *zName, *zValue;
          731  +        zName = next_token(&x, 0);
          732  +        if( zName==0 ) SYNTAX("missing name on T-card");
          733  +        zUuid = next_token(&x, &sz);
          734  +        if( zUuid==0 ) SYNTAX("missing UUID on T-card");
          735  +        zValue = next_token(&x, 0);
          736  +        if( zValue ) defossilize(zValue);
          737  +        if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
   457    738             /* A valid uuid */
   458         -        }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
          739  +        }else if( sz==1 && zUuid[0]=='*' ){
   459    740             zUuid = 0;
   460    741           }else{
   461         -          goto manifest_syntax_error;
          742  +          SYNTAX("malformed UUID on T-card");
   462    743           }
   463    744           defossilize(zName);
   464    745           if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
   465         -          goto manifest_syntax_error;
          746  +          SYNTAX("T-card name does not begin with '-', '+', or '*'");
   466    747           }
   467    748           if( validate16(&zName[1], strlen(&zName[1])) ){
   468    749             /* Do not allow tags whose names look like UUIDs */
   469         -          goto manifest_syntax_error;
          750  +          SYNTAX("T-card name looks like a UUID");
   470    751           }
   471    752           if( p->nTag>=p->nTagAlloc ){
   472    753             p->nTagAlloc = p->nTagAlloc*2 + 10;
   473         -          p->aTag = realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
   474         -          if( p->aTag==0 ) fossil_panic("out of memory");
          754  +          p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
   475    755           }
   476    756           i = p->nTag++;
   477    757           p->aTag[i].zName = zName;
   478    758           p->aTag[i].zUuid = zUuid;
   479    759           p->aTag[i].zValue = zValue;
   480         -        if( i>0 && strcmp(p->aTag[i-1].zName, zName)>=0 ){
   481         -          goto manifest_syntax_error;
          760  +        if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){
          761  +          SYNTAX("T-card in the wrong order");
   482    762           }
   483    763           break;
   484    764         }
   485    765   
   486    766         /*
   487    767         **     U ?<login>?
   488    768         **
   489    769         ** Identify the user who created this control file by their
   490    770         ** login.  Only one U line is allowed.  Prohibited in clusters.
   491    771         ** If the user name is omitted, take that to be "anonymous".
   492    772         */
   493    773         case 'U': {
   494         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   495         -        if( p->zUser!=0 ) goto manifest_syntax_error;
   496         -        if( blob_token(&line, &a1)==0 ){
          774  +        if( p->zUser!=0 ) SYNTAX("more than on U-card");
          775  +        p->zUser = next_token(&x, 0);
          776  +        if( p->zUser==0 ){
   497    777             p->zUser = "anonymous";
   498    778           }else{
   499         -          p->zUser = blob_terminate(&a1);
   500    779             defossilize(p->zUser);
   501    780           }
   502         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   503    781           break;
   504    782         }
   505    783   
   506    784         /*
   507    785         **     W <size>
   508    786         **
   509    787         ** The next <size> bytes of the file contain the text of the wiki
   510    788         ** page.  There is always an extra \n before the start of the next
   511    789         ** record.
   512    790         */
   513    791         case 'W': {
   514         -        int size;
          792  +        char *zSize;
          793  +        unsigned size, oldsize, c;
   515    794           Blob wiki;
   516         -        md5sum_step_text(blob_buffer(&line), blob_size(&line));
   517         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   518         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   519         -        if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
   520         -        if( size<0 ) goto manifest_syntax_error;
   521         -        if( p->zWiki!=0 ) goto manifest_syntax_error;
          795  +        zSize = next_token(&x, 0);
          796  +        if( zSize==0 ) SYNTAX("missing size on W-card");
          797  +        if( x.atEol==0 ) SYNTAX("no content after W-card");
          798  +        for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
          799  +           size = oldsize*10 + c - '0';
          800  +           if( size<oldsize ) SYNTAX("size overflow on W-card");
          801  +           oldsize = size;
          802  +        }
          803  +        if( p->zWiki!=0 ) SYNTAX("more than one W-card");
   522    804           blob_zero(&wiki);
   523         -        if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
   524         -          goto manifest_syntax_error;
   525         -        }
   526         -        p->zWiki = blob_buffer(&wiki);
   527         -        md5sum_step_text(p->zWiki, size+1);
   528         -        if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
   529         -        p->zWiki[size] = 0;
          805  +        if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card");
          806  +        p->zWiki = x.z;
          807  +        x.z += size;
          808  +        if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
          809  +        x.z[0] = 0;
          810  +        x.z++;
   530    811           break;
   531    812         }
   532    813   
   533    814   
   534    815         /*
   535    816         **     Z <md5sum>
   536    817         **
................................................................................
   539    820         ** line.  This must be the last record.
   540    821         **
   541    822         ** This card is required for all control file types except for
   542    823         ** Manifest.  It is not required for manifest only for historical
   543    824         ** compatibility reasons.
   544    825         */
   545    826         case 'Z': {
   546         -        int rc;
   547         -        Blob hash;
   548         -        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
   549         -        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
   550         -        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
   551         -        if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
   552         -        md5sum_finish(&hash);
   553         -        rc = blob_compare(&hash, &a1);
   554         -        blob_reset(&hash);
   555         -        if( rc!=0 ) goto manifest_syntax_error;
          827  +        zUuid = next_token(&x, &sz);
          828  +        if( sz!=32 ) SYNTAX("wrong size for Z-card cksum");
          829  +        if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum");
   556    830           seenZ = 1;
   557    831           break;
   558    832         }
   559    833         default: {
   560         -        goto manifest_syntax_error;
          834  +        SYNTAX("unrecognized card");
   561    835         }
   562    836       }
   563    837     }
   564         -  if( !seenHeader ) goto manifest_syntax_error;
          838  +  if( x.z<x.zEnd ) SYNTAX("extra characters at end of card");
   565    839   
   566         -  if( p->nFile>0 || p->zRepoCksum!=0 ){
   567         -    if( p->nCChild>0 ) goto manifest_syntax_error;
   568         -    if( p->rDate==0.0 ) goto manifest_syntax_error;
   569         -    if( p->nField>0 ) goto manifest_syntax_error;
   570         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   571         -    if( p->zWiki ) goto manifest_syntax_error;
   572         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   573         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   574         -    if( p->zAttachName ) goto manifest_syntax_error;
          840  +  if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
          841  +    if( p->nCChild>0 ) SYNTAX("M-card in check-in");
          842  +    if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
          843  +    if( p->nField>0 ) SYNTAX("J-card in check-in");
          844  +    if( p->zTicketUuid ) SYNTAX("K-card in check-in");
          845  +    if( p->zWiki ) SYNTAX("W-card in check-in");
          846  +    if( p->zWikiTitle ) SYNTAX("L-card in check-in");
          847  +    if( p->zEventId ) SYNTAX("E-card in check-in");
          848  +    if( p->zTicketUuid ) SYNTAX("K-card in check-in");
          849  +    if( p->zAttachName ) SYNTAX("A-card in check-in");
   575    850       p->type = CFTYPE_MANIFEST;
   576    851     }else if( p->nCChild>0 ){
   577         -    if( p->rDate>0.0 ) goto manifest_syntax_error;
   578         -    if( p->zComment!=0 ) goto manifest_syntax_error;
   579         -    if( p->zUser!=0 ) goto manifest_syntax_error;
   580         -    if( p->nTag>0 ) goto manifest_syntax_error;
   581         -    if( p->nParent>0 ) goto manifest_syntax_error;
   582         -    if( p->nField>0 ) goto manifest_syntax_error;
   583         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   584         -    if( p->zWiki ) goto manifest_syntax_error;
   585         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   586         -    if( p->zAttachName ) goto manifest_syntax_error;
   587         -    if( !seenZ ) goto manifest_syntax_error;
          852  +    if( p->rDate>0.0
          853  +     || p->zComment!=0
          854  +     || p->zUser!=0
          855  +     || p->nTag>0
          856  +     || p->nParent>0
          857  +     || p->nField>0
          858  +     || p->zTicketUuid
          859  +     || p->zWiki
          860  +     || p->zWikiTitle
          861  +     || p->zEventId
          862  +     || p->zAttachName
          863  +    ){
          864  +      SYNTAX("cluster contains a card other than M- or Z-");
          865  +    }
          866  +    if( !seenZ ) SYNTAX("missing Z-card on cluster");
   588    867       p->type = CFTYPE_CLUSTER;
   589    868     }else if( p->nField>0 ){
   590         -    if( p->rDate==0.0 ) goto manifest_syntax_error;
   591         -    if( p->zWiki ) goto manifest_syntax_error;
   592         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   593         -    if( p->nCChild>0 ) goto manifest_syntax_error;
   594         -    if( p->nTag>0 ) goto manifest_syntax_error;
   595         -    if( p->zTicketUuid==0 ) goto manifest_syntax_error;
   596         -    if( p->zUser==0 ) goto manifest_syntax_error;
   597         -    if( p->zAttachName ) goto manifest_syntax_error;
   598         -    if( !seenZ ) goto manifest_syntax_error;
          869  +    if( p->rDate<=0.0 ) SYNTAX("missing date for ticket");
          870  +    if( p->zWiki ) SYNTAX("W-card in ticket");
          871  +    if( p->zWikiTitle ) SYNTAX("L-card in ticket");
          872  +    if( p->zEventId ) SYNTAX("E-card in ticket");
          873  +    if( p->nCChild>0 ) SYNTAX("M-card in ticket");
          874  +    if( p->nTag>0 ) SYNTAX("T-card in ticket");
          875  +    if( p->zTicketUuid==0 ) SYNTAX("missing K-card in ticket");
          876  +    if( p->zUser==0 ) SYNTAX("missing U-card in ticket");
          877  +    if( p->zAttachName ) SYNTAX("A-card in ticket");
          878  +    if( !seenZ ) SYNTAX("missing Z-card in ticket");
   599    879       p->type = CFTYPE_TICKET;
          880  +  }else if( p->zEventId ){
          881  +    if( p->rDate<=0.0 ) SYNTAX("missing date for event");
          882  +    if( p->nCChild>0 ) SYNTAX("M-card in event");
          883  +    if( p->zTicketUuid!=0 ) SYNTAX("K-card in event");
          884  +    if( p->zWikiTitle!=0 ) SYNTAX("L-card in event");
          885  +    if( p->zWiki==0 ) SYNTAX("W-card in event");
          886  +    if( p->zAttachName ) SYNTAX("A-card in event");
          887  +    for(i=0; i<p->nTag; i++){
          888  +      if( p->aTag[i].zName[0]!='+' ) SYNTAX("propagating tag in event");
          889  +      if( p->aTag[i].zUuid!=0 ) SYNTAX("non-self-referential tag in event");
          890  +    }
          891  +    if( !seenZ ) SYNTAX("Z-card missing in event");
          892  +    p->type = CFTYPE_EVENT;
   600    893     }else if( p->zWiki!=0 ){
   601         -    if( p->rDate==0.0 ) goto manifest_syntax_error;
   602         -    if( p->nCChild>0 ) goto manifest_syntax_error;
   603         -    if( p->nTag>0 ) goto manifest_syntax_error;
   604         -    if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
   605         -    if( p->zWikiTitle==0 ) goto manifest_syntax_error;
   606         -    if( p->zAttachName ) goto manifest_syntax_error;
   607         -    if( !seenZ ) goto manifest_syntax_error;
          894  +    if( p->rDate<=0.0 ) SYNTAX("date missing on wiki");
          895  +    if( p->nCChild>0 ) SYNTAX("M-card in wiki");
          896  +    if( p->nTag>0 ) SYNTAX("T-card in wiki");
          897  +    if( p->zTicketUuid!=0 ) SYNTAX("K-card in wiki");
          898  +    if( p->zWikiTitle==0 ) SYNTAX("L-card in wiki");
          899  +    if( p->zAttachName ) SYNTAX("A-card in wiki");
          900  +    if( !seenZ ) SYNTAX("missing Z-card on wiki");
   608    901       p->type = CFTYPE_WIKI;
   609    902     }else if( p->nTag>0 ){
   610         -    if( p->rDate<=0.0 ) goto manifest_syntax_error;
   611         -    if( p->nParent>0 ) goto manifest_syntax_error;
   612         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   613         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   614         -    if( p->zAttachName ) goto manifest_syntax_error;
   615         -    if( !seenZ ) goto manifest_syntax_error;
          903  +    if( p->rDate<=0.0 ) SYNTAX("date missing on tag");
          904  +    if( p->nParent>0 ) SYNTAX("P-card on tag");
          905  +    if( p->zWikiTitle ) SYNTAX("L-card on tag");
          906  +    if( p->zTicketUuid ) SYNTAX("K-card in tag");
          907  +    if( p->zAttachName ) SYNTAX("A-card in tag");
          908  +    if( !seenZ ) SYNTAX("missing Z-card on tag");
   616    909       p->type = CFTYPE_CONTROL;
   617    910     }else if( p->zAttachName ){
   618         -    if( p->nCChild>0 ) goto manifest_syntax_error;
   619         -    if( p->rDate==0.0 ) goto manifest_syntax_error;
   620         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   621         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   622         -    if( !seenZ ) goto manifest_syntax_error;
          911  +    if( p->nCChild>0 ) SYNTAX("M-card in attachment");
          912  +    if( p->rDate<=0.0 ) SYNTAX("missing date in attachment");
          913  +    if( p->zTicketUuid ) SYNTAX("K-card in attachment");
          914  +    if( p->zWikiTitle ) SYNTAX("L-card in attachment");
          915  +    if( !seenZ ) SYNTAX("missing Z-card on attachment");
   623    916       p->type = CFTYPE_ATTACHMENT;
   624    917     }else{
   625         -    if( p->nCChild>0 ) goto manifest_syntax_error;
   626         -    if( p->rDate<=0.0 ) goto manifest_syntax_error;
   627         -    if( p->nParent>0 ) goto manifest_syntax_error;
   628         -    if( p->nField>0 ) goto manifest_syntax_error;
   629         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   630         -    if( p->zWiki ) goto manifest_syntax_error;
   631         -    if( p->zWikiTitle ) goto manifest_syntax_error;
   632         -    if( p->zTicketUuid ) goto manifest_syntax_error;
   633         -    if( p->zAttachName ) goto manifest_syntax_error;
          918  +    if( p->nCChild>0 ) SYNTAX("M-card in check-in");
          919  +    if( p->rDate<=0.0 ) SYNTAX("missing date in check-in");
          920  +    if( p->nField>0 ) SYNTAX("J-card in check-in");
          921  +    if( p->zTicketUuid ) SYNTAX("K-card in check-in");
          922  +    if( p->zWikiTitle ) SYNTAX("L-card in check-in");
   634    923       p->type = CFTYPE_MANIFEST;
   635    924     }
   636    925     md5sum_init();
   637         -  return 1;
          926  +  if( !isRepeat ) g.parseCnt[p->type]++;
          927  +  return p;
   638    928   
   639    929   manifest_syntax_error:
   640         -  /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
          930  +  if( zErr ){
          931  +    blob_appendf(pErr, "line %d: %s", lineNo, zErr);
          932  +  }else{
          933  +    blob_appendf(pErr, "unknown error on line %d", lineNo);
          934  +  }
   641    935     md5sum_init();
   642         -  manifest_clear(p);
          936  +  manifest_destroy(p);
   643    937     return 0;
   644    938   }
          939  +
          940  +/*
          941  +** Get a manifest given the rid for the control artifact.  Return
          942  +** a pointer to the manifest on success or NULL if there is a failure.
          943  +*/
          944  +Manifest *manifest_get(int rid, int cfType){
          945  +  Blob content;
          946  +  Manifest *p;
          947  +  if( !rid ) return 0;
          948  +  p = manifest_cache_find(rid);
          949  +  if( p ){
          950  +    if( cfType!=CFTYPE_ANY && cfType!=p->type ){
          951  +      manifest_cache_insert(p);
          952  +      p = 0;
          953  +    }
          954  +    return p;
          955  +  }
          956  +  content_get(rid, &content);
          957  +  p = manifest_parse(&content, rid, 0);
          958  +  if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
          959  +    manifest_destroy(p);
          960  +    p = 0;
          961  +  }
          962  +  return p;
          963  +}
          964  +
          965  +/*
          966  +** Given a checkin name, load and parse the manifest for that checkin.
          967  +** Throw a fatal error if anything goes wrong.
          968  +*/
          969  +Manifest *manifest_get_by_name(const char *zName, int *pRid){
          970  +  int rid;
          971  +  Manifest *p;
          972  +
          973  +  rid = name_to_typed_rid(zName, "ci");
          974  +  if( !is_a_version(rid) ){
          975  +    fossil_fatal("no such checkin: %s", zName);
          976  +  }
          977  +  if( pRid ) *pRid = rid;
          978  +  p = manifest_get(rid, CFTYPE_MANIFEST);
          979  +  if( p==0 ){
          980  +    fossil_fatal("cannot parse manifest for checkin: %s", zName);
          981  +  }
          982  +  return p;
          983  +}
   645    984   
   646    985   /*
   647    986   ** COMMAND: test-parse-manifest
   648    987   **
   649         -** Usage: %fossil test-parse-manifest FILENAME
          988  +** Usage: %fossil test-parse-manifest FILENAME ?N?
   650    989   **
   651    990   ** Parse the manifest and discarded.  Use for testing only.
   652    991   */
   653    992   void manifest_test_parse_cmd(void){
   654         -  Manifest m;
          993  +  Manifest *p;
   655    994     Blob b;
   656         -  if( g.argc!=3 ){
          995  +  int i;
          996  +  int n = 1;
          997  +  sqlite3_open(":memory:", &g.db);
          998  +  if( g.argc!=3 && g.argc!=4 ){
   657    999       usage("FILENAME");
   658   1000     }
   659         -  db_must_be_within_tree();
   660   1001     blob_read_from_file(&b, g.argv[2]);
   661         -  manifest_parse(&m, &b);
   662         -  manifest_clear(&m);
         1002  +  if( g.argc>3 ) n = atoi(g.argv[3]);
         1003  +  for(i=0; i<n; i++){
         1004  +    Blob b2;
         1005  +    Blob err;
         1006  +    blob_copy(&b2, &b);
         1007  +    blob_zero(&err);
         1008  +    p = manifest_parse(&b2, 0, &err);
         1009  +    if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err));
         1010  +    blob_reset(&err);
         1011  +    manifest_destroy(p);
         1012  +  }
         1013  +}
         1014  +
         1015  +/*
         1016  +** Fetch the baseline associated with the delta-manifest p.
         1017  +** Return 0 on success.  If unable to parse the baseline,
         1018  +** throw an error.  If the baseline is a manifest, throw an
         1019  +** error if throwError is true, or record that p is an orphan
         1020  +** and return 1 if throwError is false.
         1021  +*/
         1022  +static int fetch_baseline(Manifest *p, int throwError){
         1023  +  if( p->zBaseline!=0 && p->pBaseline==0 ){
         1024  +    int rid = uuid_to_rid(p->zBaseline, 1);
         1025  +    p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST);
         1026  +    if( p->pBaseline==0 ){
         1027  +      if( !throwError ){
         1028  +        db_multi_exec(
         1029  +           "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)",
         1030  +           p->rid, rid
         1031  +        );
         1032  +        return 1;
         1033  +      }    
         1034  +      fossil_fatal("cannot access baseline manifest %S", p->zBaseline);
         1035  +    }
         1036  +  }
         1037  +  return 0;
         1038  +}
         1039  +
         1040  +/*
         1041  +** Rewind a manifest-file iterator back to the beginning of the manifest.
         1042  +*/
         1043  +void manifest_file_rewind(Manifest *p){
         1044  +  p->iFile = 0;
         1045  +  fetch_baseline(p, 1);
         1046  +  if( p->pBaseline ){
         1047  +    p->pBaseline->iFile = 0;
         1048  +  }
         1049  +}
         1050  +
         1051  +/*
         1052  +** Advance to the next manifest-file.
         1053  +**
         1054  +** Return NULL for end-of-records or if there is an error.  If an error
         1055  +** occurs and pErr!=0 then store 1 in *pErr.
         1056  +*/
         1057  +ManifestFile *manifest_file_next(
         1058  +  Manifest *p,   
         1059  +  int *pErr
         1060  +){
         1061  +  ManifestFile *pOut = 0;
         1062  +  if( pErr ) *pErr = 0;
         1063  +  if( p->pBaseline==0 ){
         1064  +    /* Manifest p is a baseline-manifest.  Just scan down the list
         1065  +    ** of files. */
         1066  +    if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
         1067  +  }else{
         1068  +    /* Manifest p is a delta-manifest.  Scan the baseline but amend the
         1069  +    ** file list in the baseline with changes described by p.
         1070  +    */
         1071  +    Manifest *pB = p->pBaseline;
         1072  +    int cmp;
         1073  +    while(1){
         1074  +      if( pB->iFile>=pB->nFile ){
         1075  +        /* We have used all entries out of the baseline.  Return the next
         1076  +        ** entry from the delta. */
         1077  +        if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
         1078  +        break;
         1079  +      }else if( p->iFile>=p->nFile ){
         1080  +        /* We have used all entries from the delta.  Return the next
         1081  +        ** entry from the baseline. */
         1082  +        if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++];
         1083  +        break;
         1084  +      }else if( (cmp = fossil_strcmp(pB->aFile[pB->iFile].zName,
         1085  +                              p->aFile[p->iFile].zName)) < 0 ){
         1086  +        /* The next baseline entry comes before the next delta entry.
         1087  +        ** So return the baseline entry. */
         1088  +        pOut = &pB->aFile[pB->iFile++];
         1089  +        break;
         1090  +      }else if( cmp>0 ){
         1091  +        /* The next delta entry comes before the next baseline
         1092  +        ** entry so return the delta entry */
         1093  +        pOut = &p->aFile[p->iFile++];
         1094  +        break;
         1095  +      }else if( p->aFile[p->iFile].zUuid ){
         1096  +        /* The next delta entry is a replacement for the next baseline
         1097  +        ** entry.  Skip the baseline entry and return the delta entry */
         1098  +        pB->iFile++;
         1099  +        pOut = &p->aFile[p->iFile++];
         1100  +        break;
         1101  +      }else{
         1102  +        /* The next delta entry is a delete of the next baseline
         1103  +        ** entry.  Skip them both.  Repeat the loop to find the next
         1104  +        ** non-delete entry. */
         1105  +        pB->iFile++;
         1106  +        p->iFile++;
         1107  +        continue;
         1108  +      }
         1109  +    }
         1110  +  }
         1111  +  return pOut;
   663   1112   }
   664   1113   
   665   1114   /*
   666   1115   ** Translate a filename into a filename-id (fnid).  Create a new fnid
   667   1116   ** if no previously exists.
   668   1117   */
   669   1118   static int filename_to_fnid(const char *zFilename){
................................................................................
   680   1129       db_static_prepare(&s1, "INSERT INTO filename(name) VALUES(:fn)");
   681   1130       db_bind_text(&s1, ":fn", zFilename);
   682   1131       db_exec(&s1);
   683   1132       fnid = db_last_insert_rowid();
   684   1133     }
   685   1134     return fnid;
   686   1135   }
         1136  +
         1137  +/*
         1138  +** Compute an appropriate mlink.mperm integer for the permission string
         1139  +** of a file.
         1140  +*/
         1141  +int manifest_file_mperm(ManifestFile *pFile){
         1142  +  int mperm = PERM_REG;
         1143  +  if( pFile && pFile->zPerm){
         1144  +    if( strstr(pFile->zPerm,"x")!=0 )
         1145  +      mperm = PERM_EXE;
         1146  +    else if( strstr(pFile->zPerm,"l")!=0 )
         1147  +      mperm = PERM_LNK;
         1148  +  }
         1149  +  return mperm;
         1150  +}
   687   1151   
   688   1152   /*
   689   1153   ** Add a single entry to the mlink table.  Also add the filename to
   690   1154   ** the filename table if it is not there already.
   691   1155   */
   692   1156   static void add_one_mlink(
   693   1157     int mid,                  /* The record ID of the manifest */
   694         -  const char *zFromUuid,    /* UUID for the mlink.pid field */
   695         -  const char *zToUuid,      /* UUID for the mlink.fid field */
         1158  +  const char *zFromUuid,    /* UUID for the mlink.pid. "" to add file */
         1159  +  const char *zToUuid,      /* UUID for the mlink.fid. "" to delete */
   696   1160     const char *zFilename,    /* Filename */
   697         -  const char *zPrior        /* Previous filename.  NULL if unchanged */
         1161  +  const char *zPrior,       /* Previous filename. NULL if unchanged */
         1162  +  int isPublic,             /* True if mid is not a private manifest */
         1163  +  int mperm                 /* 1: exec, 2: symlink */
   698   1164   ){
   699   1165     int fnid, pfnid, pid, fid;
   700   1166     static Stmt s1;
   701   1167   
   702   1168     fnid = filename_to_fnid(zFilename);
   703   1169     if( zPrior==0 ){
   704   1170       pfnid = 0;
   705   1171     }else{
   706   1172       pfnid = filename_to_fnid(zPrior);
   707   1173     }
   708         -  if( zFromUuid==0 ){
         1174  +  if( zFromUuid==0 || zFromUuid[0]==0 ){
   709   1175       pid = 0;
   710   1176     }else{
   711   1177       pid = uuid_to_rid(zFromUuid, 1);
   712   1178     }
   713         -  if( zToUuid==0 ){
         1179  +  if( zToUuid==0 || zToUuid[0]==0 ){
   714   1180       fid = 0;
   715   1181     }else{
   716   1182       fid = uuid_to_rid(zToUuid, 1);
         1183  +    if( isPublic ) content_make_public(fid);
   717   1184     }
   718   1185     db_static_prepare(&s1,
   719         -    "INSERT INTO mlink(mid,pid,fid,fnid,pfnid)"
   720         -    "VALUES(:m,:p,:f,:n,:pfn)"
         1186  +    "INSERT INTO mlink(mid,pid,fid,fnid,pfnid,mperm)"
         1187  +    "VALUES(:m,:p,:f,:n,:pfn,:mp)"
   721   1188     );
   722   1189     db_bind_int(&s1, ":m", mid);
   723   1190     db_bind_int(&s1, ":p", pid);
   724   1191     db_bind_int(&s1, ":f", fid);
   725   1192     db_bind_int(&s1, ":n", fnid);
   726   1193     db_bind_int(&s1, ":pfn", pfnid);
         1194  +  db_bind_int(&s1, ":mp", mperm);
   727   1195     db_exec(&s1);
   728   1196     if( pid && fid ){
   729   1197       content_deltify(pid, fid, 0);
   730   1198     }
   731   1199   }
   732   1200   
   733   1201   /*
   734         -** Locate a file named zName in the aFile[] array of the given
   735         -** manifest.  We assume that filenames are in sorted order.
   736         -** Use a binary search.  Return turn the index of the matching
   737         -** entry.  Or return -1 if not found.
         1202  +** Do a binary search to find a file in the p->aFile[] array.  
         1203  +**
         1204  +** As an optimization, guess that the file we seek is at index p->iFile.
         1205  +** That will usually be the case.  If it is not found there, then do the
         1206  +** actual binary search.
         1207  +**
         1208  +** Update p->iFile to be the index of the file that is found.
   738   1209   */
   739         -static int find_file_in_manifest(Manifest *p, const char *zName){
         1210  +static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){
   740   1211     int lwr, upr;
   741   1212     int c;
   742   1213     int i;
   743   1214     lwr = 0;
   744   1215     upr = p->nFile - 1;
         1216  +  if( p->iFile>=lwr && p->iFile<upr ){
         1217  +    c = fossil_strcmp(p->aFile[p->iFile+1].zName, zName);
         1218  +    if( c==0 ){
         1219  +      return &p->aFile[++p->iFile];
         1220  +    }else if( c>0 ){
         1221  +      upr = p->iFile;
         1222  +    }else{
         1223  +      lwr = p->iFile+1;
         1224  +    }
         1225  +  }
   745   1226     while( lwr<=upr ){
   746   1227       i = (lwr+upr)/2;
   747         -    c = strcmp(p->aFile[i].zName, zName);
         1228  +    c = fossil_strcmp(p->aFile[i].zName, zName);
   748   1229       if( c<0 ){
   749   1230         lwr = i+1;
   750   1231       }else if( c>0 ){
   751   1232         upr = i-1;
   752   1233       }else{
   753         -      return i;
         1234  +      p->iFile = i;
         1235  +      return &p->aFile[i];
   754   1236       }
   755   1237     }
   756         -  return -1;
         1238  +  return 0;
         1239  +}
         1240  +
         1241  +/*
         1242  +** Locate a file named zName in the aFile[] array of the given manifest.
         1243  +** Return a pointer to the appropriate ManifestFile object.  Return NULL
         1244  +** if not found.
         1245  +**
         1246  +** This routine works even if p is a delta-manifest.  The pointer 
         1247  +** returned might be to the baseline.
         1248  +**
         1249  +** We assume that filenames are in sorted order and use a binary search.
         1250  +*/
         1251  +ManifestFile *manifest_file_seek(Manifest *p, const char *zName){
         1252  +  ManifestFile *pFile;
         1253  +  
         1254  +  pFile = manifest_file_seek_base(p, zName);
         1255  +  if( pFile && pFile->zUuid==0 ) return 0;
         1256  +  if( pFile==0 && p->zBaseline ){
         1257  +    fetch_baseline(p, 1);
         1258  +    pFile = manifest_file_seek_base(p->pBaseline, zName);
         1259  +  }
         1260  +  return pFile;
         1261  +}
         1262  +
         1263  +/*
         1264  +** Look for a file in a manifest, taking the case-sensitive option
         1265  +** into account.  If case-sensitive is off, then files in any case
         1266  +** will match.
         1267  +*/
         1268  +ManifestFile *manifest_file_find(Manifest *p, const char *zName){
         1269  +  int i;
         1270  +  Manifest *pBase;
         1271  +  if( filenames_are_case_sensitive() ){
         1272  +    return manifest_file_seek(p, zName);
         1273  +  }
         1274  +  for(i=0; i<p->nFile; i++){
         1275  +    if( fossil_stricmp(zName, p->aFile[i].zName)==0 ){
         1276  +      return &p->aFile[i];
         1277  +    }
         1278  +  }
         1279  +  if( p->zBaseline==0 ) return 0;
         1280  +  fetch_baseline(p, 1);
         1281  +  pBase = p->pBaseline;
         1282  +  if( pBase==0 ) return 0;
         1283  +  for(i=0; i<pBase->nFile; i++){
         1284  +    if( fossil_stricmp(zName, pBase->aFile[i].zName)==0 ){
         1285  +      return &pBase->aFile[i];
         1286  +    }
         1287  +  }
         1288  +  return 0;
   757   1289   }
   758   1290   
   759   1291   /*
   760         -** Add mlink table entries associated with manifest cid.  The
   761         -** parent manifest is pid.
         1292  +** Add mlink table entries associated with manifest cid, pChild.  The
         1293  +** parent manifest is pid, pParent.  One of either pChild or pParent
         1294  +** will be NULL and it will be computed based on cid/pid.
   762   1295   **
   763         -** A single mlink entry is added for every file that changed content
   764         -** and/or name going from pid to cid.
         1296  +** A single mlink entry is added for every file that changed content,
         1297  +** name, and/or permissions going from pid to cid.
   765   1298   **
   766   1299   ** Deleted files have mlink.fid=0.
   767   1300   ** Added files have mlink.pid=0.
   768   1301   ** Edited files have both mlink.pid!=0 and mlink.fid!=0
   769   1302   */
   770   1303   static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
   771         -  Manifest other;
   772   1304     Blob otherContent;
   773         -  int i, j;
         1305  +  int otherRid;
         1306  +  int i, rc;
         1307  +  ManifestFile *pChildFile, *pParentFile;
         1308  +  Manifest **ppOther;
         1309  +  static Stmt eq;
         1310  +  int isPublic;                /* True if pChild is non-private */
   774   1311   
   775         -  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
   776         -    return;
   777         -  }
         1312  +  /* If mlink table entires are already set for cid, then abort early
         1313  +  ** doing no work.
         1314  +  */
         1315  +  db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
         1316  +  db_bind_int(&eq, ":mid", cid);
         1317  +  rc = db_step(&eq);
         1318  +  db_reset(&eq);
         1319  +  if( rc==SQLITE_ROW ) return;
         1320  +
         1321  +  /* Compute the value of the missing pParent or pChild parameter.
         1322  +  ** Fetch the baseline checkins for both.
         1323  +  */
   778   1324     assert( pParent==0 || pChild==0 );
   779   1325     if( pParent==0 ){
   780         -    pParent = &other;
   781         -    content_get(pid, &otherContent);
         1326  +    ppOther = &pParent;
         1327  +    otherRid = pid;
   782   1328     }else{
   783         -    pChild = &other;
   784         -    content_get(cid, &otherContent);
   785         -  }
   786         -  if( blob_size(&otherContent)==0 ) return;
   787         -  if( manifest_parse(&other, &otherContent)==0 ) return;
   788         -  content_deltify(pid, cid, 0);
   789         -
   790         -  /* Use the iRename fields to find the cross-linkage between
   791         -  ** renamed files.  */
   792         -  for(j=0; j<pChild->nFile; j++){
   793         -    const char *zPrior = pChild->aFile[j].zPrior;
   794         -    if( zPrior && zPrior[0] ){
   795         -      i = find_file_in_manifest(pParent, zPrior);
   796         -      if( i>=0 ){
   797         -        pChild->aFile[j].iRename = i;
   798         -        pParent->aFile[i].iRename = j;
   799         -      }
   800         -    }
   801         -  }
   802         -
   803         -  /* Construct the mlink entries */
   804         -  for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){
   805         -    int c;
   806         -    if( pParent->aFile[i].iRename>=0 ){
   807         -      i++;
   808         -    }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){
   809         -      add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0);
   810         -      i++;
   811         -    }else if( c>0 ){
   812         -      int rn = pChild->aFile[j].iRename;
   813         -      if( rn>=0 ){
   814         -        add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
   815         -                      pChild->aFile[j].zName, pParent->aFile[rn].zName);
   816         -      }else{
   817         -        add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
   818         -      }
   819         -      j++;
         1329  +    ppOther = &pChild;
         1330  +    otherRid = cid;
         1331  +  }
         1332  +  if( (*ppOther = manifest_cache_find(otherRid))==0 ){
         1333  +    content_get(otherRid, &otherContent);
         1334  +    if( blob_size(&otherContent)==0 ) return;
         1335  +    *ppOther = manifest_parse(&otherContent, otherRid, 0);
         1336  +    if( *ppOther==0 ) return;
         1337  +  }
         1338  +  if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
         1339  +    manifest_destroy(*ppOther);
         1340  +    return;
         1341  +  }
         1342  +  isPublic = !content_is_private(cid);
         1343  +
         1344  +  /* Try to make the parent manifest a delta from the child, if that
         1345  +  ** is an appropriate thing to do.  For a new baseline, make the 
         1346  +  ** previous baseline a delta from the current baseline.
         1347  +  */
         1348  +  if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
         1349  +    content_deltify(pid, cid, 0); 
         1350  +  }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
         1351  +    content_deltify(pParent->pBaseline->rid, cid, 0);
         1352  +  }
         1353  +
         1354  +  /* Remember all children less than a few seconds younger than their parent,
         1355  +  ** as we might want to fudge the times for those children.
         1356  +  */
         1357  +  if( pChild->rDate<pParent->rDate+AGE_FUDGE_WINDOW
         1358  +      && manifest_crosslink_busy
         1359  +  ){
         1360  +    db_multi_exec(
         1361  +       "INSERT OR REPLACE INTO time_fudge VALUES(%d, %.17g, %d, %.17g);",
         1362  +       pParent->rid, pParent->rDate, pChild->rid, pChild->rDate
         1363  +    );
         1364  +  }
         1365  +
         1366  +  /* First look at all files in pChild, ignoring its baseline.  This
         1367  +  ** is where most of the changes will be found.
         1368  +  */  
         1369  +  for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){
         1370  +    int mperm = manifest_file_mperm(pChildFile);
         1371  +    if( pChildFile->zPrior ){
         1372  +       pParentFile = manifest_file_seek(pParent, pChildFile->zPrior);
         1373  +       if( pParentFile ){
         1374  +         /* File with name change */
         1375  +         add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
         1376  +                       pChildFile->zName, pChildFile->zPrior, isPublic, mperm);
         1377  +       }else{
         1378  +         /* File name changed, but the old name is not found in the parent!
         1379  +         ** Treat this like a new file. */
         1380  +         add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
         1381  +                       isPublic, mperm);
         1382  +       }
   820   1383       }else{
   821         -      if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){
   822         -        add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid, 
   823         -                      pChild->aFile[j].zName, 0);
   824         -      }
   825         -      i++;
   826         -      j++;
   827         -    }
   828         -  }
   829         -  while( i<pParent->nFile ){
   830         -    if( pParent->aFile[i].iRename<0 ){
   831         -      add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0);
   832         -    }
   833         -    i++;
   834         -  }
   835         -  while( j<pChild->nFile ){
   836         -    int rn = pChild->aFile[j].iRename;
   837         -    if( rn>=0 ){
   838         -      add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
   839         -                    pChild->aFile[j].zName, pParent->aFile[rn].zName);
   840         -    }else{
   841         -      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
   842         -    }
   843         -    j++;
   844         -  }
   845         -  manifest_clear(&other);
   846         -}
   847         -
   848         -/*
   849         -** True if manifest_crosslink_begin() has been called but
   850         -** manifest_crosslink_end() is still pending.
   851         -*/
   852         -static int manifest_crosslink_busy = 0;
         1384  +       pParentFile = manifest_file_seek(pParent, pChildFile->zName);
         1385  +       if( pParentFile==0 ){
         1386  +         if( pChildFile->zUuid ){
         1387  +           /* A new file */
         1388  +           add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
         1389  +                         isPublic, mperm);
         1390  +         }
         1391  +       }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0
         1392  +              || manifest_file_mperm(pParentFile)!=mperm ){
         1393  +         /* Changes in file content or permissions */
         1394  +         add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
         1395  +                       pChildFile->zName, 0, isPublic, mperm);
         1396  +       }
         1397  +    }
         1398  +  }
         1399  +  if( pParent->zBaseline && pChild->zBaseline ){
         1400  +    /* Both parent and child are delta manifests.  Look for files that
         1401  +    ** are deleted or modified in the parent but which reappear or revert
         1402  +    ** to baseline in the child and show such files as being added or changed
         1403  +    ** in the child. */
         1404  +    for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
         1405  +      if( pParentFile->zUuid ){
         1406  +        pChildFile = manifest_file_seek_base(pChild, pParentFile->zName);
         1407  +        if( pChildFile==0 ){
         1408  +          /* The child file reverts to baseline.  Show this as a change */
         1409  +          pChildFile = manifest_file_seek(pChild, pParentFile->zName);
         1410  +          if( pChildFile ){
         1411  +            add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
         1412  +                          pChildFile->zName, 0, isPublic,
         1413  +                          manifest_file_mperm(pChildFile));
         1414  +          }
         1415  +        }
         1416  +      }else{
         1417  +        pChildFile = manifest_file_seek(pChild, pParentFile->zName);
         1418  +        if( pChildFile ){
         1419  +          /* File resurrected in the child after having been deleted in
         1420  +          ** the parent.  Show this as an added file. */
         1421  +          add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
         1422  +                        isPublic, manifest_file_mperm(pChildFile));
         1423  +        }
         1424  +      }
         1425  +    }
         1426  +  }else if( pChild->zBaseline==0 ){
         1427  +    /* pChild is a baseline.  Look for files that are present in pParent
         1428  +    ** but are missing from pChild and mark them as having been deleted. */
         1429  +    manifest_file_rewind(pParent);
         1430  +    while( (pParentFile = manifest_file_next(pParent,0))!=0 ){
         1431  +      pChildFile = manifest_file_seek(pChild, pParentFile->zName);
         1432  +      if( pChildFile==0 && pParentFile->zUuid!=0 ){
         1433  +        add_one_mlink(cid, pParentFile->zUuid, 0, pParentFile->zName, 0, 
         1434  +                      isPublic, 0);
         1435  +      }
         1436  +    }
         1437  +  }
         1438  +  manifest_cache_insert(*ppOther);
         1439  +}
   853   1440   
   854   1441   /*
   855   1442   ** Setup to do multiple manifest_crosslink() calls.
   856   1443   ** This is only required if processing ticket changes.
   857   1444   */
   858   1445   void manifest_crosslink_begin(void){
   859   1446     assert( manifest_crosslink_busy==0 );
   860   1447     manifest_crosslink_busy = 1;
   861   1448     db_begin_transaction();
   862         -  db_multi_exec("CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE)");
         1449  +  db_multi_exec(
         1450  +     "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);"
         1451  +     "CREATE TEMP TABLE time_fudge("
         1452  +     "  mid INTEGER PRIMARY KEY,"    /* The rid of a manifest */
         1453  +     "  m1 REAL,"                    /* The timestamp on mid */
         1454  +     "  cid INTEGER,"                /* A child or mid */
         1455  +     "  m2 REAL"                     /* Timestamp on the child */
         1456  +     ");"
         1457  +  );
   863   1458   }
   864   1459   
         1460  +#if INTERFACE
         1461  +/* Timestamps might be adjusted slightly to ensure that checkins appear
         1462  +** on the timeline in chronological order.  This is the maximum amount
         1463  +** of the adjustment window, in days.
         1464  +*/
         1465  +#define AGE_FUDGE_WINDOW      (2.0/86400.0)       /* 2 seconds */
         1466  +
         1467  +/* This is increment (in days) by which timestamps are adjusted for
         1468  +** use on the timeline.
         1469  +*/
         1470  +#define AGE_ADJUST_INCREMENT  (25.0/86400000.0)   /* 25 milliseconds */
         1471  +
         1472  +#endif /* LOCAL_INTERFACE */
         1473  +
   865   1474   /*
   866   1475   ** Finish up a sequence of manifest_crosslink calls.
   867   1476   */
   868   1477   void manifest_crosslink_end(void){
   869         -  Stmt q;
         1478  +  Stmt q, u;
         1479  +  int i;
   870   1480     assert( manifest_crosslink_busy==1 );
   871   1481     db_prepare(&q, "SELECT uuid FROM pending_tkt");
   872   1482     while( db_step(&q)==SQLITE_ROW ){
   873   1483       const char *zUuid = db_column_text(&q, 0);
   874   1484       ticket_rebuild_entry(zUuid);
   875   1485     }
   876   1486     db_finalize(&q);
   877   1487     db_multi_exec("DROP TABLE pending_tkt");
         1488  +
         1489  +  /* If multiple check-ins happen close together in time, adjust their
         1490  +  ** times by a few milliseconds to make sure they appear in chronological
         1491  +  ** order.
         1492  +  */
         1493  +  db_prepare(&q,
         1494  +      "UPDATE time_fudge SET m1=m2-:incr WHERE m1>=m2 AND m1<m2+:window"
         1495  +  );
         1496  +  db_bind_double(&q, ":incr", AGE_ADJUST_INCREMENT);
         1497  +  db_bind_double(&q, ":window", AGE_FUDGE_WINDOW);
         1498  +  db_prepare(&u,
         1499  +      "UPDATE time_fudge SET m2="
         1500  +         "(SELECT x.m1 FROM time_fudge AS x WHERE x.mid=time_fudge.cid)"
         1501  +  );
         1502  +  for(i=0; i<30; i++){
         1503  +    db_step(&q);
         1504  +    db_reset(&q);
         1505  +    if( sqlite3_changes(g.db)==0 ) break;
         1506  +    db_step(&u);
         1507  +    db_reset(&u);
         1508  +  }
         1509  +  db_finalize(&q);
         1510  +  db_finalize(&u);
         1511  +  db_multi_exec(
         1512  +    "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
         1513  +    " WHERE objid IN (SELECT mid FROM time_fudge);"
         1514  +    "DROP TABLE time_fudge;"
         1515  +  );
         1516  +
   878   1517     db_end_transaction(0);
   879   1518     manifest_crosslink_busy = 0;
   880   1519   }
   881   1520   
   882   1521   /*
   883   1522   ** Make an entry in the event table for a ticket change artifact.
   884   1523   */
................................................................................
   906   1545     }
   907   1546     zTitle = db_text("unknown", 
   908   1547       "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
   909   1548       zTitleExpr, pManifest->zTicketUuid
   910   1549     );
   911   1550     if( !isNew ){
   912   1551       for(i=0; i<pManifest->nField; i++){
   913         -      if( strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
         1552  +      if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
   914   1553           zNewStatus = pManifest->aField[i].zValue;
   915   1554         }
   916   1555       }
   917   1556       if( zNewStatus ){
   918         -      blob_appendf(&comment, "%h ticket [%.10s]: <i>%s</i>",
         1557  +      blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
   919   1558            zNewStatus, pManifest->zTicketUuid, zTitle
   920   1559         );
   921   1560         if( pManifest->nField>1 ){
   922   1561           blob_appendf(&comment, " plus %d other change%s",
   923   1562             pManifest->nField-1, pManifest->nField==2 ? "" : "s");
   924   1563         }
   925   1564         blob_appendf(&brief, "%h ticket [%.10s].",
   926   1565                      zNewStatus, pManifest->zTicketUuid);
   927   1566       }else{
   928   1567         zNewStatus = db_text("unknown", 
   929   1568            "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
   930   1569            zStatusColumn, pManifest->zTicketUuid
   931   1570         );
   932         -      blob_appendf(&comment, "Ticket [%.10s] <i>%s</i> status still %h with "
         1571  +      blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
   933   1572              "%d other change%s",
   934   1573              pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
   935   1574              pManifest->nField==1 ? "" : "s"
   936   1575         );
   937   1576         free(zNewStatus);
   938   1577         blob_appendf(&brief, "Ticket [%.10s]: %d change%s",
   939   1578              pManifest->zTicketUuid, pManifest->nField,
................................................................................
   962   1601   ** any key:
   963   1602   **
   964   1603   **      *  Manifest
   965   1604   **      *  Control
   966   1605   **      *  Wiki Page
   967   1606   **      *  Ticket Change
   968   1607   **      *  Cluster
         1608  +**      *  Attachment
         1609  +**      *  Event
   969   1610   **
   970   1611   ** If the input is a control artifact, then make appropriate entries
   971   1612   ** in the auxiliary tables of the database in order to crosslink the
   972   1613   ** artifact.
   973   1614   **
   974   1615   ** If global variable g.xlinkClusterOnly is true, then ignore all 
   975   1616   ** control artifacts other than clusters.
         1617  +**
         1618  +** This routine always resets the pContent blob before returning.
   976   1619   **
   977   1620   ** Historical note:  This routine original processed manifests only.
   978   1621   ** Processing for other control artifacts was added later.  The name
   979   1622   ** of the routine, "manifest_crosslink", and the name of this source
   980   1623   ** file, is a legacy of its original use.
   981   1624   */
   982   1625   int manifest_crosslink(int rid, Blob *pContent){
   983   1626     int i;
   984         -  Manifest m;
         1627  +  Manifest *p;
   985   1628     Stmt q;
   986   1629     int parentid = 0;
   987   1630   
   988         -  if( manifest_parse(&m, pContent)==0 ){
         1631  +  if( (p = manifest_cache_find(rid))!=0 ){
         1632  +    blob_reset(pContent);
         1633  +  }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
         1634  +    assert( blob_is_reset(pContent) || pContent==0 );
         1635  +    return 0;
         1636  +  }
         1637  +  if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
         1638  +    manifest_destroy(p);
         1639  +    assert( blob_is_reset(pContent) );
   989   1640       return 0;
   990   1641     }
   991         -  if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
   992         -    manifest_clear(&m);
         1642  +  if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){
         1643  +    manifest_destroy(p);
         1644  +    assert( blob_is_reset(pContent) );
   993   1645       return 0;
   994   1646     }
   995   1647     db_begin_transaction();
   996         -  if( m.type==CFTYPE_MANIFEST ){
         1648  +  if( p->type==CFTYPE_MANIFEST ){
   997   1649       if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
   998   1650         char *zCom;
   999         -      for(i=0; i<m.nParent; i++){
  1000         -        int pid = uuid_to_rid(m.azParent[i], 1);
         1651  +      for(i=0; i<p->nParent; i++){
         1652  +        int pid = uuid_to_rid(p->azParent[i], 1);
  1001   1653           db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
  1002         -                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
         1654  +                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
  1003   1655           if( i==0 ){
  1004         -          add_mlink(pid, 0, rid, &m);
         1656  +          add_mlink(pid, 0, rid, p);
  1005   1657             parentid = pid;
  1006   1658           }
  1007   1659         }
  1008   1660         db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
  1009   1661         while( db_step(&q)==SQLITE_ROW ){
  1010   1662           int cid = db_column_int(&q, 0);
  1011         -        add_mlink(rid, &m, cid, 0);
         1663  +        add_mlink(rid, p, cid, 0);
  1012   1664         }
  1013   1665         db_finalize(&q);
         1666  +      if( p->nParent==0 ){
         1667  +        /* For root files (files without parents) add mlink entries
         1668  +        ** showing all content as new. */
         1669  +        int isPublic = !content_is_private(rid);
         1670  +        for(i=0; i<p->nFile; i++){
         1671  +          add_one_mlink(rid, 0, p->aFile[i].zUuid, p->aFile[i].zName, 0,
         1672  +                        isPublic, manifest_file_mperm(&p->aFile[i]));
         1673  +        }
         1674  +      }
  1014   1675         db_multi_exec(
  1015   1676           "REPLACE INTO event(type,mtime,objid,user,comment,"
  1016         -                           "bgcolor,euser,ecomment)"
         1677  +                           "bgcolor,euser,ecomment,omtime)"
  1017   1678           "VALUES('ci',"
  1018   1679           "  coalesce("
  1019   1680           "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
  1020   1681           "    %.17g"
  1021   1682           "  ),"
  1022   1683           "  %d,%Q,%Q,"
  1023   1684           "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
  1024   1685           "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
  1025         -        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
  1026         -        TAG_DATE, rid, m.rDate,
  1027         -        rid, m.zUser, m.zComment, 
         1686  +        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),%.17g);",
         1687  +        TAG_DATE, rid, p->rDate,
         1688  +        rid, p->zUser, p->zComment, 
  1028   1689           TAG_BGCOLOR, rid,
  1029   1690           TAG_USER, rid,
  1030         -        TAG_COMMENT, rid
         1691  +        TAG_COMMENT, rid, p->rDate
  1031   1692         );
  1032   1693         zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
  1033   1694                           " WHERE rowid=last_insert_rowid()");
  1034         -      wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
         1695  +      wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
  1035   1696         free(zCom);
         1697  +
         1698  +      /* If this is a delta-manifest, record the fact that this repository
         1699  +      ** contains delta manifests, to free the "commit" logic to generate
         1700  +      ** new delta manifests.
         1701  +      */
         1702  +      if( p->zBaseline!=0 ){
         1703  +        static int once = 0;
         1704  +        if( !once ){
         1705  +          db_set_int("seen-delta-manifest", 1, 0);
         1706  +          once = 0;
         1707  +        }
         1708  +      }
  1036   1709       }
  1037   1710     }
  1038         -  if( m.type==CFTYPE_CLUSTER ){
  1039         -    tag_insert("cluster", 1, 0, rid, m.rDate, rid);
  1040         -    for(i=0; i<m.nCChild; i++){
         1711  +  if( p->type==CFTYPE_CLUSTER ){
         1712  +    static Stmt del1;
         1713  +    tag_insert("cluster", 1, 0, rid, p->rDate, rid);
         1714  +    db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid");
         1715  +    for(i=0; i<p->nCChild; i++){
  1041   1716         int mid;
  1042         -      mid = uuid_to_rid(m.azCChild[i], 1);
         1717  +      mid = uuid_to_rid(p->azCChild[i], 1);
  1043   1718         if( mid>0 ){
  1044         -        db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
         1719  +        db_bind_int(&del1, ":rid", mid);
         1720  +        db_step(&del1);
         1721  +        db_reset(&del1);
  1045   1722         }
  1046   1723       }
  1047   1724     }
  1048         -  if( m.type==CFTYPE_CONTROL || m.type==CFTYPE_MANIFEST ){
  1049         -    for(i=0; i<m.nTag; i++){
         1725  +  if( p->type==CFTYPE_CONTROL
         1726  +   || p->type==CFTYPE_MANIFEST
         1727  +   || p->type==CFTYPE_EVENT
         1728  +  ){
         1729  +    for(i=0; i<p->nTag; i++){
  1050   1730         int tid;
  1051   1731         int type;
  1052         -      if( m.aTag[i].zUuid ){
  1053         -        tid = uuid_to_rid(m.aTag[i].zUuid, 1);
         1732  +      if( p->aTag[i].zUuid ){
         1733  +        tid = uuid_to_rid(p->aTag[i].zUuid, 1);
  1054   1734         }else{
  1055   1735           tid = rid;
  1056   1736         }
  1057   1737         if( tid ){
  1058         -        switch( m.aTag[i].zName[0] ){
  1059         -          case '-':  type = 0;  break;  /* Cancel prior occurances */
         1738  +        switch( p->aTag[i].zName[0] ){
         1739  +          case '-':  type = 0;  break;  /* Cancel prior occurrences */
  1060   1740             case '+':  type = 1;  break;  /* Apply to target only */
  1061   1741             case '*':  type = 2;  break;  /* Propagate to descendants */
  1062   1742             default:
  1063         -            fossil_fatal("unknown tag type in manifest: %s", m.aTag);
         1743  +            fossil_fatal("unknown tag type in manifest: %s", p->aTag);
  1064   1744               return 0;
  1065   1745           }
  1066         -        tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue, 
  1067         -                   rid, m.rDate, tid);
         1746  +        tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, 
         1747  +                   rid, p->rDate, tid);
  1068   1748         }
  1069   1749       }
  1070   1750       if( parentid ){
  1071   1751         tag_propagate_all(parentid);
  1072   1752       }
  1073   1753     }
  1074         -  if( m.type==CFTYPE_WIKI ){
  1075         -    char *zTag = mprintf("wiki-%s", m.zWikiTitle);
         1754  +  if( p->type==CFTYPE_WIKI ){
         1755  +    char *zTag = mprintf("wiki-%s", p->zWikiTitle);
  1076   1756       int tagid = tag_findid(zTag, 1);
  1077   1757       int prior;
  1078   1758       char *zComment;
  1079   1759       int nWiki;
  1080   1760       char zLength[40];
  1081         -    while( isspace(m.zWiki[0]) ) m.zWiki++;
  1082         -    nWiki = strlen(m.zWiki);
         1761  +    while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
         1762  +    nWiki = strlen(p->zWiki);
  1083   1763       sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
  1084         -    tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
         1764  +    tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
  1085   1765       free(zTag);
  1086   1766       prior = db_int(0,
  1087   1767         "SELECT rid FROM tagxref"
  1088   1768         " WHERE tagid=%d AND mtime<%.17g"
  1089   1769         " ORDER BY mtime DESC",
  1090         -      tagid, m.rDate
         1770  +      tagid, p->rDate
  1091   1771       );
  1092   1772       if( prior ){
  1093   1773         content_deltify(prior, rid, 0);
  1094   1774       }
  1095   1775       if( nWiki>0 ){
  1096         -      zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
         1776  +      zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
  1097   1777       }else{
  1098         -      zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
         1778  +      zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
  1099   1779       }
  1100   1780       db_multi_exec(
  1101   1781         "REPLACE INTO event(type,mtime,objid,user,comment,"
  1102   1782         "                  bgcolor,euser,ecomment)"
  1103   1783         "VALUES('w',%.17g,%d,%Q,%Q,"
  1104   1784         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
  1105   1785         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
  1106   1786         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
  1107         -      m.rDate, rid, m.zUser, zComment, 
         1787  +      p->rDate, rid, p->zUser, zComment, 
  1108   1788         TAG_BGCOLOR, rid,
  1109   1789         TAG_BGCOLOR, rid,
  1110   1790         TAG_USER, rid,
  1111   1791         TAG_COMMENT, rid
  1112   1792       );
  1113   1793       free(zComment);
  1114   1794     }
  1115         -  if( m.type==CFTYPE_TICKET ){
         1795  +  if( p->type==CFTYPE_EVENT ){
         1796  +    char *zTag = mprintf("event-%s", p->zEventId);
         1797  +    int tagid = tag_findid(zTag, 1);
         1798  +    int prior, subsequent;
         1799  +    int nWiki;
         1800  +    char zLength[40];
         1801  +    while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
         1802  +    nWiki = strlen(p->zWiki);
         1803  +    sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
         1804  +    tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
         1805  +    free(zTag);
         1806  +    prior = db_int(0,
         1807  +      "SELECT rid FROM tagxref"
         1808  +      " WHERE tagid=%d AND mtime<%.17g AND rid!=%d"
         1809  +      " ORDER BY mtime DESC",
         1810  +      tagid, p->rDate, rid
         1811  +    );
         1812  +    subsequent = db_int(0,
         1813  +      "SELECT rid FROM tagxref"
         1814  +      " WHERE tagid=%d AND mtime>=%.17g AND rid!=%d"
         1815  +      " ORDER BY mtime",
         1816  +      tagid, p->rDate, rid
         1817  +    );
         1818  +    if( prior ){
         1819  +      content_deltify(prior, rid, 0);
         1820  +      if( !subsequent ){
         1821  +        db_multi_exec(
         1822  +          "DELETE FROM event"
         1823  +          " WHERE type='e'"
         1824  +          "   AND tagid=%d"
         1825  +          "   AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)",
         1826  +          tagid, tagid
         1827  +        );
         1828  +      }
         1829  +    }
         1830  +    if( subsequent ){
         1831  +      content_deltify(rid, subsequent, 0);
         1832  +    }else{
         1833  +      db_multi_exec(
         1834  +        "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
         1835  +        "VALUES('e',%.17g,%d,%d,%Q,%Q,"
         1836  +        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
         1837  +        p->rEventDate, rid, tagid, p->zUser, p->zComment, 
         1838  +        TAG_BGCOLOR, rid
         1839  +      );
         1840  +    }
         1841  +  }
         1842  +  if( p->type==CFTYPE_TICKET ){
  1116   1843       char *zTag;
  1117   1844   
  1118   1845       assert( manifest_crosslink_busy==1 );
  1119         -    zTag = mprintf("tkt-%s", m.zTicketUuid);
  1120         -    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
         1846  +    zTag = mprintf("tkt-%s", p->zTicketUuid);
         1847  +    tag_insert(zTag, 1, 0, rid, p->rDate, rid);
  1121   1848       free(zTag);
  1122   1849       db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
  1123         -                  m.zTicketUuid);
         1850  +                  p->zTicketUuid);
  1124   1851     }
  1125         -  if( m.type==CFTYPE_ATTACHMENT ){
         1852  +  if( p->type==CFTYPE_ATTACHMENT ){
  1126   1853       db_multi_exec(
  1127   1854          "INSERT INTO attachment(attachid, mtime, src, target,"
  1128   1855                                           "filename, comment, user)"
  1129   1856          "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
  1130         -       rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
  1131         -       (m.zComment ? m.zComment : ""), m.zUser
         1857  +       rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
         1858  +       (p->zComment ? p->zComment : ""), p->zUser
  1132   1859       );
  1133   1860       db_multi_exec(
  1134   1861          "UPDATE attachment SET isLatest = (mtime=="
  1135   1862             "(SELECT max(mtime) FROM attachment"
  1136   1863             "  WHERE target=%Q AND filename=%Q))"
  1137   1864          " WHERE target=%Q AND filename=%Q",
  1138         -       m.zAttachTarget, m.zAttachName,
  1139         -       m.zAttachTarget, m.zAttachName
         1865  +       p->zAttachTarget, p->zAttachName,
         1866  +       p->zAttachTarget, p->zAttachName
  1140   1867       );
  1141         -    if( strlen(m.zAttachTarget)!=UUID_SIZE
  1142         -     || !validate16(m.zAttachTarget, UUID_SIZE) 
         1868  +    if( strlen(p->zAttachTarget)!=UUID_SIZE
         1869  +     || !validate16(p->zAttachTarget, UUID_SIZE) 
  1143   1870       ){
  1144   1871         char *zComment;
  1145         -      if( m.zAttachSrc && m.zAttachSrc[0] ){
  1146         -        zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
  1147         -             m.zAttachName, m.zAttachTarget);
         1872  +      if( p->zAttachSrc && p->zAttachSrc[0] ){
         1873  +        zComment = mprintf(
         1874  +             "Add attachment [%R/artifact/%S|%h] to wiki page [%h]",
         1875  +             p->zAttachSrc, p->zAttachName, p->zAttachTarget);
  1148   1876         }else{
  1149   1877           zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
  1150         -             m.zAttachName, m.zAttachTarget);
         1878  +             p->zAttachName, p->zAttachTarget);
  1151   1879         }
  1152   1880         db_multi_exec(
  1153   1881           "REPLACE INTO event(type,mtime,objid,user,comment)"
  1154   1882           "VALUES('w',%.17g,%d,%Q,%Q)",
  1155         -        m.rDate, rid, m.zUser, zComment
         1883  +        p->rDate, rid, p->zUser, zComment
  1156   1884         );
  1157   1885         free(zComment);
  1158   1886       }else{
  1159   1887         char *zComment;
  1160         -      if( m.zAttachSrc && m.zAttachSrc[0] ){
  1161         -        zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
  1162         -             m.zAttachName, m.zAttachTarget);
         1888  +      if( p->zAttachSrc && p->zAttachSrc[0] ){
         1889  +        zComment = mprintf(
         1890  +             "Add attachment [%R/artifact/%S|%h] to ticket [%S]",
         1891  +             p->zAttachSrc, p->zAttachName, p->zAttachTarget);
  1163   1892         }else{
  1164   1893           zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
  1165         -             m.zAttachName, m.zAttachTarget);
         1894  +             p->zAttachName, p->zAttachTarget);
  1166   1895         }
  1167   1896         db_multi_exec(
  1168   1897           "REPLACE INTO event(type,mtime,objid,user,comment)"
  1169   1898           "VALUES('t',%.17g,%d,%Q,%Q)",
  1170         -        m.rDate, rid, m.zUser, zComment
         1899  +        p->rDate, rid, p->zUser, zComment
  1171   1900         );
  1172   1901         free(zComment);
  1173   1902       }
  1174   1903     }
         1904  +  if( p->type==CFTYPE_CONTROL ){
         1905  +    Blob comment;
         1906  +    int i;
         1907  +    const char *zName;
         1908  +    const char *zValue;
         1909  +    const char *zUuid;
         1910  +    int branchMove = 0;
         1911  +    blob_zero(&comment);
         1912  +    for(i=0; i<p->nTag; i++){
         1913  +      zUuid = p->aTag[i].zUuid;
         1914  +      if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
         1915  +        if( i>0 ) blob_append(&comment, " ", 1);
         1916  +        blob_appendf(&comment,
         1917  +           "Edit [%S]:",
         1918  +           zUuid);
         1919  +        branchMove = 0;
         1920  +      }
         1921  +      zName = p->aTag[i].zName;
         1922  +      zValue = p->aTag[i].zValue;
         1923  +      if( strcmp(zName, "*branch")==0 ){
         1924  +        blob_appendf(&comment,
         1925  +           " Move to branch [/timeline?r=%h&nd&dp=%S | %h].",
         1926  +           zValue, zUuid, zValue);
         1927  +        branchMove = 1;
         1928  +      }else if( strcmp(zName, "*bgcolor")==0 ){
         1929  +        blob_appendf(&comment,
         1930  +           " Change branch background color to \"%h\".", zValue);
         1931  +      }else if( strcmp(zName, "+bgcolor")==0 ){
         1932  +        blob_appendf(&comment,
         1933  +           " Change background color to \"%h\".", zValue);
         1934  +      }else if( strcmp(zName, "-bgcolor")==0 ){
         1935  +        blob_appendf(&comment, " Cancel background color.");
         1936  +      }else if( strcmp(zName, "+comment")==0 ){
         1937  +        blob_appendf(&comment, " Edit check-in comment.");
         1938  +      }else if( strcmp(zName, "+user")==0 ){
         1939  +        blob_appendf(&comment, " Change user to \"%h\".", zValue);
         1940  +      }else if( strcmp(zName, "+date")==0 ){
         1941  +        blob_appendf(&comment, " Timestamp %h.", zValue);
         1942  +      }else if( memcmp(zName, "-sym-",5)==0 ){
         1943  +        if( !branchMove ) blob_appendf(&comment, " Cancel tag %h.", &zName[5]);
         1944  +      }else if( memcmp(zName, "*sym-",5)==0 ){
         1945  +        if( !branchMove ){
         1946  +          blob_appendf(&comment, " Add propagating tag \"%h\".", &zName[5]);
         1947  +        }
         1948  +      }else if( memcmp(zName, "+sym-",5)==0 ){
         1949  +        blob_appendf(&comment, " Add tag \"%h\".", &zName[5]);
         1950  +      }else if( memcmp(zName, "-sym-",5)==0 ){
         1951  +        blob_appendf(&comment, " Cancel tag \"%h\".", &zName[5]);
         1952  +      }else if( strcmp(zName, "+closed")==0 ){
         1953  +        blob_appendf(&comment, " Marked \"Closed\".");
         1954  +      }else if( strcmp(zName, "-closed")==0 ){
         1955  +        blob_appendf(&comment, " Removed the \"Closed\" mark.");
         1956  +      }else {
         1957  +        if( zName[0]=='-' ){
         1958  +          blob_appendf(&comment, " Cancel \"%h\"", &zName[1]);
         1959  +        }else if( zName[0]=='+' ){
         1960  +          blob_appendf(&comment, " Add \"%h\"", &zName[1]);
         1961  +        }else{
         1962  +          blob_appendf(&comment, " Add propagating \"%h\"", &zName[1]);
         1963  +        }
         1964  +        if( zValue && zValue[0] ){
         1965  +          blob_appendf(&comment, " with value \"%h\".", zValue);
         1966  +        }else{
         1967  +          blob_appendf(&comment, ".");
         1968  +        }
         1969  +      }
         1970  +    }
         1971  +    /*blob_appendf(&comment, " &#91;[/info/%S | details]&#93;");*/
         1972  +    db_multi_exec(
         1973  +      "REPLACE INTO event(type,mtime,objid,user,comment)"
         1974  +      "VALUES('g',%.17g,%d,%Q,%Q)",
         1975  +      p->rDate, rid, p->zUser, blob_str(&comment)
         1976  +    );
         1977  +    blob_reset(&comment);
         1978  +  }
  1175   1979     db_end_transaction(0);
  1176         -  manifest_clear(&m);
         1980  +  if( p->type==CFTYPE_MANIFEST ){
         1981  +    manifest_cache_insert(p);
         1982  +  }else{
         1983  +    manifest_destroy(p);
         1984  +  }
         1985  +  assert( blob_is_reset(pContent) );
  1177   1986     return 1;
  1178   1987   }
         1988  +
         1989  +/*
         1990  +** COMMAND: test-crosslink
         1991  +**
         1992  +** Usage:  %fossil test-crosslink RECORDID
         1993  +**
         1994  +** Run the manifest_crosslink() routine on the artifact with the given
         1995  +** record ID.  This is typically done in the debugger.
         1996  +*/
         1997  +void test_crosslink_cmd(void){
         1998  +  int rid;
         1999  +  Blob content;
         2000  +  db_find_and_open_repository(0, 0);
         2001  +  if( g.argc!=3 ) usage("RECORDID");
         2002  +  rid = name_to_rid(g.argv[2]);
         2003  +  content_get(rid, &content);
         2004  +  manifest_crosslink(rid, &content);
         2005  +}

Added src/markdown.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code to parse a blob containing markdown text,
           19  +** using an external renderer.
           20  +*/
           21  +
           22  +#ifdef FOSSIL_ENABLE_MARKDOWN
           23  +
           24  +#include "config.h"
           25  +#include "markdown.h"
           26  +
           27  +#include <assert.h>
           28  +#include <string.h>
           29  +#include <stdlib.h>
           30  +
           31  +#define MKD_LI_END 8  /* internal list flag */
           32  +
           33  +/********************
           34  + * TYPE DEFINITIONS *
           35  + ********************/
           36  +
           37  +#if INTERFACE
           38  +
           39  +/* mkd_autolink -- type of autolink */
           40  +enum mkd_autolink {
           41  +  MKDA_NOT_AUTOLINK,    /* used internally when it is not an autolink*/
           42  +  MKDA_NORMAL,          /* normal http/http/ftp/etc link */
           43  +  MKDA_EXPLICIT_EMAIL,  /* e-mail link with explit mailto: */
           44  +  MKDA_IMPLICIT_EMAIL   /* e-mail link without mailto: */
           45  +};
           46  +
           47  +/* mkd_renderer -- functions for rendering parsed data */
           48  +struct mkd_renderer {
           49  +  /* document level callbacks */
           50  +  void (*prolog)(struct Blob *ob, void *opaque);
           51  +  void (*epilog)(struct Blob *ob, void *opaque);
           52  +
           53  +  /* block level callbacks - NULL skips the block */
           54  +  void (*blockcode)(struct Blob *ob, struct Blob *text, void *opaque);
           55  +  void (*blockquote)(struct Blob *ob, struct Blob *text, void *opaque);
           56  +  void (*blockhtml)(struct Blob *ob, struct Blob *text, void *opaque);
           57  +  void (*header)(struct Blob *ob, struct Blob *text,
           58  +            int level, void *opaque);
           59  +  void (*hrule)(struct Blob *ob, void *opaque);
           60  +  void (*list)(struct Blob *ob, struct Blob *text, int flags, void *opaque);
           61  +  void (*listitem)(struct Blob *ob, struct Blob *text,
           62  +            int flags, void *opaque);
           63  +  void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
           64  +  void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
           65  +              void *opaque);
           66  +  void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
           67  +              void *opaque);
           68  +  void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
           69  +              void *opaque);
           70  +
           71  +  /* span level callbacks - NULL or return 0 prints the span verbatim */
           72  +  int (*autolink)(struct Blob *ob, struct Blob *link,
           73  +          enum mkd_autolink type, void *opaque);
           74  +  int (*codespan)(struct Blob *ob, struct Blob *text, void *opaque);
           75  +  int (*double_emphasis)(struct Blob *ob, struct Blob *text,
           76  +            char c, void *opaque);
           77  +  int (*emphasis)(struct Blob *ob, struct Blob *text, char c,void*opaque);
           78  +  int (*image)(struct Blob *ob, struct Blob *link, struct Blob *title,
           79  +            struct Blob *alt, void *opaque);
           80  +  int (*linebreak)(struct Blob *ob, void *opaque);
           81  +  int (*link)(struct Blob *ob, struct Blob *link, struct Blob *title,
           82  +          struct Blob *content, void *opaque);
           83  +  int (*raw_html_tag)(struct Blob *ob, struct Blob *tag, void *opaque);
           84  +  int (*triple_emphasis)(struct Blob *ob, struct Blob *text,
           85  +            char c, void *opaque);
           86  +
           87  +  /* low level callbacks - NULL copies input directly into the output */
           88  +  void (*entity)(struct Blob *ob, struct Blob *entity, void *opaque);
           89  +  void (*normal_text)(struct Blob *ob, struct Blob *text, void *opaque);
           90  +
           91  +  /* renderer data */
           92  +  int max_work_stack; /* prevent arbitrary deep recursion, cf README */
           93  +  const char *emph_chars; /* chars that trigger emphasis rendering */
           94  +  void *opaque; /* opaque data send to every rendering callback */
           95  +};
           96  +
           97  +
           98  +
           99  +/*********
          100  + * FLAGS *
          101  + *********/
          102  +
          103  +/* list/listitem flags */
          104  +#define MKD_LIST_ORDERED  1
          105  +#define MKD_LI_BLOCK      2  /* <li> containing block data */
          106  +
          107  +/* table cell flags */
          108  +#define MKD_CELL_ALIGN_DEFAULT  0
          109  +#define MKD_CELL_ALIGN_LEFT     1
          110  +#define MKD_CELL_ALIGN_RIGHT    2
          111  +#define MKD_CELL_ALIGN_CENTER   3  /* LEFT | RIGHT */
          112  +#define MKD_CELL_ALIGN_MASK     3
          113  +#define MKD_CELL_HEAD           4
          114  +
          115  +
          116  +
          117  +/**********************
          118  + * EXPORTED FUNCTIONS *
          119  + **********************/
          120  +
          121  +/* markdown -- parses the input buffer and renders it into the output buffer */
          122  +void markdown(
          123  +  struct Blob *ob,
          124  +  struct Blob *ib,
          125  +  const struct mkd_renderer *rndr);
          126  +
          127  +
          128  +#endif /* INTERFACE */
          129  +
          130  +
          131  +/***************
          132  + * LOCAL TYPES *
          133  + ***************/
          134  +
          135  +/* link_ref -- reference to a link */
          136  +struct link_ref {
          137  +  struct Blob id;
          138  +  struct Blob link;
          139  +  struct Blob title;
          140  +};
          141  +
          142  +
          143  +/* char_trigger -- function pointer to render active chars */
          144  +/*   returns the number of chars taken care of */
          145  +/*   data is the pointer of the beginning of the span */
          146  +/*   offset is the number of valid chars before data */
          147  +struct render;
          148  +typedef size_t (*char_trigger)(
          149  +  struct Blob *ob,
          150  +  struct render *rndr,
          151  +  char *data,
          152  +  size_t offset,
          153  +  size_t size);
          154  +
          155  +
          156  +/* render -- structure containing one particular render */
          157  +struct render {
          158  +  struct mkd_renderer make;
          159  +  struct Blob refs;
          160  +  char_trigger active_char[256];
          161  +  int work_active;
          162  +  struct Blob *work;
          163  +};
          164  +
          165  +
          166  +/* html_tag -- structure for quick HTML tag search (inspired from discount) */
          167  +struct html_tag {
          168  +  char *text;
          169  +  int size;
          170  +};
          171  +
          172  +
          173  +
          174  +/********************
          175  + * GLOBAL VARIABLES *
          176  + ********************/
          177  +
          178  +/* block_tags -- recognised block tags, sorted by cmp_html_tag */
          179  +static struct html_tag block_tags[] = {
          180  +  { "p",            1 },
          181  +  { "dl",           2 },
          182  +  { "h1",           2 },
          183  +  { "h2",           2 },
          184  +  { "h3",           2 },
          185  +  { "h4",           2 },
          186  +  { "h5",           2 },
          187  +  { "h6",           2 },
          188  +  { "ol",           2 },
          189  +  { "ul",           2 },
          190  +  { "del",          3 },
          191  +  { "div",          3 },
          192  +  { "ins",          3 },
          193  +  { "pre",          3 },
          194  +  { "form",         4 },
          195  +  { "math",         4 },
          196  +  { "table",        5 },
          197  +  { "iframe",       6 },
          198  +  { "script",       6 },
          199  +  { "fieldset",     8 },
          200  +  { "noscript",     8 },
          201  +  { "blockquote",  10 }
          202  +};
          203  +
          204  +#define INS_TAG (block_tags + 12)
          205  +#define DEL_TAG (block_tags + 10)
          206  +
          207  +
          208  +
          209  +/***************************
          210  + * STATIC HELPER FUNCTIONS *
          211  + ***************************/
          212  +
          213  +/* build_ref_id -- collapse whitespace from input text to make it a ref id */
          214  +static int build_ref_id(struct Blob *id, const char *data, size_t size){
          215  +  size_t beg, i;
          216  +  char *id_data;
          217  +
          218  +  /* skip leading whitespace */
          219  +  while( size>0 && (data[0]==' ' || data[0]=='\t' || data[0]=='\n') ){
          220  +    data++;
          221  +    size--;
          222  +  }
          223  +
          224  +  /* skip trailing whitespace */
          225  +  while( size>0 && (data[size-1]==' '
          226  +   || data[size-1]=='\t'
          227  +   || data[size-1]=='\n')
          228  +  ){
          229  +    size--;
          230  +  }
          231  +  if( size==0 ) return -1;
          232  +
          233  +  /* making the ref id */
          234  +  i = 0;
          235  +  blob_reset(id);
          236  +  while( i<size ){
          237  +    /* copy non-whitespace into the output buffer */
          238  +    beg = i;
          239  +    while( i<size && !(data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){
          240  +      i++;
          241  +    }
          242  +    blob_append(id, data+beg, i-beg);
          243  +
          244  +    /* add a single space and skip all consecutive whitespace */
          245  +    if( i<size ) blob_append(id, " ", 1);
          246  +    while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
          247  +  }
          248  +
          249  +  /* turn upper-case ASCII into their lower-case counterparts */
          250  +  id_data = blob_buffer(id);
          251  +  for(i=0; i<blob_size(id); i++){
          252  +    if( id_data[i]>='A' && id_data[i]<='Z' ) id_data[i] += 'a' - 'A';
          253  +  }
          254  +  return 0;
          255  +}
          256  +
          257  +
          258  +/* cmp_link_ref -- comparison function for link_ref sorted arrays */
          259  +static int cmp_link_ref(const void *key, const void *array_entry){
          260  +  struct link_ref *lr = (void *)array_entry;
          261  +  return blob_compare((void *)key, &lr->id);
          262  +}
          263  +
          264  +
          265  +/* cmp_link_ref_sort -- comparison function for link_ref qsort */
          266  +static int cmp_link_ref_sort(const void *a, const void *b){
          267  +  struct link_ref *lra = (void *)a;
          268  +  struct link_ref *lrb = (void *)b;
          269  +  return blob_compare(&lra->id, &lrb->id);
          270  +}
          271  +
          272  +
          273  +/* cmp_html_tag -- comparison function for bsearch() (stolen from discount) */
          274  +static int cmp_html_tag(const void *a, const void *b){
          275  +  const struct html_tag *hta = a;
          276  +  const struct html_tag *htb = b;
          277  +  if( hta->size!=htb->size ) return hta->size-htb->size;
          278  +  return fossil_strnicmp(hta->text, htb->text, hta->size);
          279  +}
          280  +
          281  +
          282  +/* find_block_tag -- returns the current block tag */
          283  +static struct html_tag *find_block_tag(char *data, size_t size){
          284  +  size_t i = 0;
          285  +  struct html_tag key;
          286  +
          287  +  /* looking for the word end */
          288  +  while( i<size
          289  +   && ((data[i]>='0' && data[i]<='9')
          290  +       || (data[i]>='A' && data[i]<='Z')
          291  +       || (data[i]>='a' && data[i]<='z'))
          292  +  ){
          293  +    i++;
          294  +  }
          295  +  if( i>=size ) return 0;
          296  +
          297  +  /* binary search of the tag */
          298  +  key.text = data;
          299  +  key.size = i;
          300  +  return bsearch(&key,
          301  +                 block_tags,
          302  +                 (sizeof block_tags)/(sizeof block_tags[0]),
          303  +                 sizeof block_tags[0],
          304  +                 cmp_html_tag);
          305  +}
          306  +
          307  +
          308  +/* new_work_buffer -- get a new working buffer from the stack or create one */
          309  +static struct Blob *new_work_buffer(struct render *rndr){
          310  +  struct Blob *ret = 0;
          311  +
          312  +  if( rndr->work_active < rndr->make.max_work_stack ){
          313  +    ret = rndr->work + rndr->work_active;
          314  +    rndr->work_active += 1;
          315  +    blob_reset(ret);
          316  +  }
          317  +  return ret;
          318  +}
          319  +
          320  +
          321  +/* release_work_buffer -- release the given working buffer */
          322  +static void release_work_buffer(struct render *rndr, struct Blob *buf){
          323  +  if( !buf ) return;
          324  +  assert(rndr->work_active>0 && buf==(rndr->work+rndr->work_active-1));
          325  +  rndr->work_active -= 1;
          326  +}
          327  +
          328  +
          329  +
          330  +/****************************
          331  + * INLINE PARSING FUNCTIONS *
          332  + ****************************/
          333  +
          334  +/* is_mail_autolink -- looks for the address part of a mail autolink and '>' */
          335  +/* this is less strict than the original markdown e-mail address matching */
          336  +static size_t is_mail_autolink(char *data, size_t size){
          337  +  size_t i = 0, nb = 0;
          338  +  /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */
          339  +  while( i<size && (data[i]=='-'
          340  +   || data[i]=='.'
          341  +   || data[i]=='_'
          342  +   || data[i]=='@'
          343  +   || (data[i]>='a' && data[i]<='z')
          344  +   || (data[i]>='A' && data[i]<='Z')
          345  +   || (data[i]>='0' && data[i]<='9'))
          346  +  ){
          347  +    if( data[i]=='@' ) nb++;
          348  +    i++;
          349  +  }
          350  +  if( i>=size || data[i]!='>' || nb!=1 ) return 0;
          351  +  return i+1;
          352  +}
          353  +
          354  +
          355  +/* tag_length -- returns the length of the given tag, or 0 is it's not valid */
          356  +static size_t tag_length(char *data, size_t size, enum mkd_autolink *autolink){
          357  +  size_t i, j;
          358  +
          359  +  /* a valid tag can't be shorter than 3 chars */
          360  +  if( size<3 ) return 0;
          361  +
          362  +  /* begins with a '<' optionally followed by '/', followed by letter */
          363  +  if( data[0]!='<' ) return 0;
          364  +  i = (data[1]=='/') ? 2 : 1;
          365  +  if( (data[i]<'a' || data[i]>'z') &&  (data[i]<'A' || data[i]>'Z') ){
          366  +    return 0;
          367  +  }
          368  +
          369  +  /* scheme test */
          370  +  *autolink = MKDA_NOT_AUTOLINK;
          371  +  if( size>6
          372  +   && fossil_strnicmp(data+1, "http", 4)==0
          373  +   && (data[5]==':'
          374  +       || ((data[5]=='s' || data[5]=='S') && data[6]==':'))
          375  +  ){
          376  +    i = (data[5]==':') ? 6 : 7;
          377  +    *autolink = MKDA_NORMAL;
          378  +  }else if( size>5 && fossil_strnicmp(data+1, "ftp:", 4)==0 ){
          379  +    i = 5;
          380  +    *autolink = MKDA_NORMAL;
          381  +  }else if( size>7 && fossil_strnicmp(data+1, "mailto:", 7)==0 ){
          382  +    i = 8;
          383  +    /* not changing *autolink to go to the address test */
          384  +   }
          385  +
          386  +  /* completing autolink test: no whitespace or ' or " */
          387  +  if( i>=size || i=='>' ){
          388  +    *autolink = MKDA_NOT_AUTOLINK;
          389  +  }else if( *autolink ){
          390  +    j = i;
          391  +    while( i<size
          392  +     && data[i]!='>'
          393  +     && data[i]!='\''
          394  +     && data[i]!='"'
          395  +     && data[i]!=' '
          396  +     && data[i]!='\t'
          397  +     && data[i]!='\t'
          398  +    ){
          399  +      i++;
          400  +    }
          401  +    if( i>=size ) return 0;
          402  +    if( i>j && data[i]=='>' ) return i+1;
          403  +    /* one of the forbidden chars has been found */
          404  +    *autolink = MKDA_NOT_AUTOLINK;
          405  +  }else if( (j = is_mail_autolink(data+i, size-i))!=0 ){
          406  +    *autolink = (i==8) ? MKDA_EXPLICIT_EMAIL : MKDA_IMPLICIT_EMAIL;
          407  +    return i+j;
          408  +  }
          409  +
          410  +  /* looking for sometinhg looking like a tag end */
          411  +  while( i<size && data[i]!='>' ){ i++; }
          412  +  if( i>=size ) return 0;
          413  +  return i+1;
          414  +}
          415  +
          416  +
          417  +/* parse_inline -- parses inline markdown elements */
          418  +static void parse_inline(
          419  +  struct Blob *ob,
          420  +  struct render *rndr,
          421  +  char *data,
          422  +  size_t size
          423  +){
          424  +  size_t i = 0, end = 0;
          425  +  char_trigger action = 0;
          426  +  struct Blob work = BLOB_INITIALIZER;
          427  +
          428  +  while( i<size ){
          429  +    /* copying inactive chars into the output */
          430  +    while( end<size
          431  +     && (action = rndr->active_char[(unsigned char)data[end]])==0
          432  +    ){
          433  +      end++;
          434  +    }
          435  +    if( end>i ){
          436  +      if( rndr->make.normal_text ){
          437  +        blob_init(&work, data+i, end-i);
          438  +        rndr->make.normal_text(ob, &work, rndr->make.opaque);
          439  +      }else{
          440  +        blob_append(ob, data+i, end-i);
          441  +      }
          442  +    }
          443  +    if( end>=size ) break;
          444  +    i = end;
          445  +
          446  +    /* calling the trigger */
          447  +    end = action(ob, rndr, data+i, i, size-i);
          448  +    if( !end ){
          449  +      /* no action from the callback */
          450  +      end = i+1;
          451  +    }else{
          452  +      i += end;
          453  +      end = i;
          454  +    }
          455  +  }
          456  +}
          457  +
          458  +
          459  +/* find_emph_char -- looks for the next emph char, skipping other constructs */
          460  +static size_t find_emph_char(char *data, size_t size, char c){
          461  +  size_t i = 1;
          462  +
          463  +  while( i<size ){
          464  +    while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; }
          465  +    if( i>=size ) return 0;
          466  +    if( data[i]==c ) return i;
          467  +
          468  +    /* not counting escaped chars */
          469  +    if( i && data[i-1]=='\\' ){
          470  +      i++;
          471  +      continue;
          472  +    }
          473  +
          474  +    /* skipping a code span */
          475  +    if( data[i]=='`' ){
          476  +      size_t span_nb = 0, bt;
          477  +      size_t tmp_i = 0;
          478  +
          479  +      /* counting the number of opening backticks */
          480  +      while( i<size && data[i]=='`' ){
          481  +        i++;
          482  +        span_nb++;
          483  +      }
          484  +      if( i>=size ) return 0;
          485  +
          486  +      /* finding the matching closing sequence */
          487  +      bt = 0;
          488  +      while( i<size && bt<span_nb ){
          489  +        if( !tmp_i && data[i]==c ) tmp_i = i;
          490  +        if( data[i]=='`' ) bt += 1; else bt = 0;
          491  +        i++;
          492  +      }
          493  +      if( i>=size ) return tmp_i;
          494  +      i++;
          495  +
          496  +    /* skipping a link */
          497  +    }else if( data[i]=='[' ){
          498  +      size_t tmp_i = 0;
          499  +      char cc;
          500  +      i++;
          501  +      while( i<size && data[i]!=']' ){
          502  +        if( !tmp_i && data[i]==c ) tmp_i = i;
          503  +        i++;
          504  +      }
          505  +      i++;
          506  +      while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){
          507  +        i++;
          508  +      }
          509  +      if( i>=size ) return tmp_i;
          510  +      if( data[i]!='[' && data[i]!='(' ){ /* not a link*/
          511  +        if( tmp_i ) return tmp_i; else continue;
          512  +      }
          513  +      cc = data[i];
          514  +      i++;
          515  +      while( i<size && data[i]!=cc ){
          516  +        if( !tmp_i && data[i]==c ) tmp_i = i;
          517  +        i++;
          518  +      }
          519  +      if( i>=size ) return tmp_i;
          520  +      i++;
          521  +    }
          522  +  }
          523  +  return 0;
          524  +}
          525  +
          526  +
          527  +/* parse_emph1 -- parsing single emphase */
          528  +/* closed by a symbol not preceded by whitespace and not followed by symbol */
          529  +static size_t parse_emph1(
          530  +  struct Blob *ob,
          531  +  struct render *rndr,
          532  +  char *data,
          533  +  size_t size,
          534  +  char c
          535  +){
          536  +  size_t i = 0, len;
          537  +  struct Blob *work = 0;
          538  +  int r;
          539  +
          540  +  if( !rndr->make.emphasis ) return 0;
          541  +
          542  +  /* skipping one symbol if coming from emph3 */
          543  +  if( size>1 && data[0]==c && data[1]==c ) i = 1;
          544  +
          545  +  while( i<size ){
          546  +    len = find_emph_char(data+i, size-i, c);
          547  +    if( !len ) return 0;
          548  +    i += len;
          549  +    if( i>=size ) return 0;
          550  +
          551  +    if( i+1<size && data[i+1]==c ){
          552  +      i++;
          553  +      continue;
          554  +    }
          555  +    if( data[i]==c
          556  +     && data[i-1]!=' '
          557  +     && data[i-1]!='\t'
          558  +     && data[i-1]!='\n'
          559  +    ){
          560  +      work = new_work_buffer(rndr);
          561  +      if( !work ) return 0;
          562  +      parse_inline(work, rndr, data, i);
          563  +      r = rndr->make.emphasis(ob, work, c, rndr->make.opaque);
          564  +      release_work_buffer(rndr, work);
          565  +      return r ? i+1 : 0;
          566  +    }
          567  +  }
          568  +  return 0;
          569  +}
          570  +
          571  +
          572  +/* parse_emph2 -- parsing single emphase */
          573  +static size_t parse_emph2(
          574  +  struct Blob *ob,
          575  +  struct render *rndr,
          576  +  char *data,
          577  +  size_t size,
          578  +  char c
          579  +){
          580  +  size_t i = 0, len;
          581  +  struct Blob *work = 0;
          582  +  int r;
          583  +
          584  +  if( !rndr->make.double_emphasis ) return 0;
          585  +
          586  +  while( i<size ){
          587  +    len = find_emph_char(data+i, size-i, c);
          588  +    if( !len ) return 0;
          589  +    i += len;
          590  +    if( i+1<size
          591  +     && data[i]==c
          592  +     && data[i+1]==c
          593  +     && i
          594  +     && data[i-1]!=' '
          595  +     && data[i-1]!='\t'
          596  +     && data[i-1]!='\n'
          597  +    ){
          598  +      work = new_work_buffer(rndr);
          599  +      if( !work ) return 0;
          600  +      parse_inline(work, rndr, data, i);
          601  +      r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque);
          602  +      release_work_buffer(rndr, work);
          603  +      return r ? i+2 : 0;
          604  +    }
          605  +    i++;
          606  +  }
          607  +  return 0;
          608  +}
          609  +
          610  +
          611  +/* parse_emph3 -- parsing single emphase */
          612  +/* finds the first closing tag, and delegates to the other emph */
          613  +static size_t parse_emph3(
          614  +  struct Blob *ob,
          615  +  struct render *rndr,
          616  +  char *data,
          617  +  size_t size,
          618  +  char c
          619  +){
          620  +  size_t i = 0, len;
          621  +  int r;
          622  +
          623  +  while( i<size ){
          624  +    len = find_emph_char(data+i, size-i, c);
          625  +    if( !len ) return 0;
          626  +    i += len;
          627  +
          628  +    /* skip whitespace preceded symbols */
          629  +    if( data[i]!=c || data[i-1]==' ' || data[i-1]=='\t' || data[i-1]=='\n' ){
          630  +      continue;
          631  +    }
          632  +
          633  +    if( i+2<size
          634  +     && data[i+1]==c
          635  +     && data[i+2] == c
          636  +     && rndr->make.triple_emphasis
          637  +    ){
          638  +      /* triple symbol found */
          639  +      struct Blob *work = new_work_buffer(rndr);
          640  +      if( !work ) return 0;
          641  +      parse_inline(work, rndr, data, i);
          642  +      r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque);
          643  +      release_work_buffer(rndr, work);
          644  +      return r ? i+3 : 0;
          645  +    }else if( i+1<size && data[i+1]==c ){
          646  +      /* double symbol found, handing over to emph1 */
          647  +      len = parse_emph1(ob, rndr, data-2, size+2, c);
          648  +      return len ? len-2 : 0;
          649  +    }else{
          650  +      /* single symbol found, handing over to emph2 */
          651  +      len = parse_emph2(ob, rndr, data-1, size+1, c);
          652  +      return len ? len-1 : 0;
          653  +    }
          654  +  }
          655  +  return 0;
          656  +}
          657  +
          658  +
          659  +/* char_emphasis -- single and double emphasis parsing */
          660  +static size_t char_emphasis(
          661  +  struct Blob *ob,
          662  +  struct render *rndr,
          663  +  char *data,
          664  +  size_t offset,
          665  +  size_t size
          666  +){
          667  +  char c = data[0];
          668  +  size_t ret;
          669  +
          670  +  if( size>2 && data[1]!=c ){
          671  +    /* whitespace cannot follow an opening emphasis */
          672  +    if( data[1]==' '
          673  +     || data[1]=='\t'
          674  +     || data[1]=='\n'
          675  +     || (ret = parse_emph1(ob, rndr, data+1, size-1, c))==0
          676  +    ){
          677  +      return 0;
          678  +    }
          679  +    return ret+1;
          680  +  }
          681  +
          682  +  if( size>3 && data[1]==c && data[2]!=c ){
          683  +    if( data[2]==' '
          684  +     || data[2]=='\t'
          685  +     || data[2]=='\n'
          686  +     || (ret = parse_emph2(ob, rndr, data+2, size-2, c))==0
          687  +    ){
          688  +      return 0;
          689  +    }
          690  +    return ret+2;
          691  +  }
          692  +
          693  +  if( size>4 && data[1]==c && data[2]==c && data[3]!=c ){
          694  +    if( data[3]==' '
          695  +     || data[3]=='\t'
          696  +     || data[3]=='\n'
          697  +     || (ret = parse_emph3(ob, rndr, data+3, size-3, c))==0
          698  +    ){
          699  +      return 0;
          700  +    }
          701  +    return ret+3;
          702  +  }
          703  +  return 0;
          704  +}
          705  +
          706  +
          707  +/* char_linebreak -- '\n' preceded by two spaces (assuming linebreak != 0) */
          708  +static size_t char_linebreak(
          709  +  struct Blob *ob,
          710  +  struct render *rndr,
          711  +  char *data,
          712  +  size_t offset,
          713  +  size_t size
          714  +){
          715  +  if( offset<2 || data[-1]!=' ' || data[-2]!=' ' ) return 0;
          716  +  /* removing the last space from ob and rendering */
          717  +  if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]==' ' ) ob->nUsed--;
          718  +  return rndr->make.linebreak(ob, rndr->make.opaque) ? 1 : 0;
          719  +}
          720  +
          721  +
          722  +/* char_codespan -- '`' parsing a code span (assuming codespan != 0) */
          723  +static size_t char_codespan(
          724  +  struct Blob *ob,
          725  +  struct render *rndr,
          726  +  char *data,
          727  +  size_t offset,
          728  +  size_t size
          729  +){
          730  +  size_t end, nb = 0, i, f_begin, f_end;
          731  +
          732  +  /* counting the number of backticks in the delimiter */
          733  +  while( nb<size && data[nb]=='`' ){ nb++; }
          734  +
          735  +  /* finding the next delimiter */
          736  +  i = 0;
          737  +  for(end=nb; end<size && i<nb; end++){
          738  +    if( data[end]=='`' ) i++; else i = 0;
          739  +  }
          740  +  if( i<nb && end>=size ) return 0; /* no matching delimiter */
          741  +
          742  +  /* trimming outside whitespaces */
          743  +  f_begin = nb;
          744  +  while( f_begin<end && (data[f_begin]==' ' || data[f_begin]=='\t') ){
          745  +    f_begin++;
          746  +  }
          747  +  f_end = end-nb;
          748  +  while( f_end>nb && (data[f_end-1]==' ' || data[f_end-1]=='\t') ){ f_end--; }
          749  +
          750  +  /* real code span */
          751  +  if( f_begin<f_end ){
          752  +    struct Blob work = BLOB_INITIALIZER;
          753  +    blob_init(&work, data+f_begin, f_end-f_begin);
          754  +    if( !rndr->make.codespan(ob, &work, rndr->make.opaque) ) end = 0;
          755  +  }else{
          756  +    if( !rndr->make.codespan(ob, 0, rndr->make.opaque) ) end = 0;
          757  +  }
          758  +  return end;
          759  +}
          760  +
          761  +
          762  +/* char_escape -- '\\' backslash escape */
          763  +static size_t char_escape(
          764  +  struct Blob *ob,
          765  +  struct render *rndr,
          766  +  char *data,
          767  +  size_t offset,
          768  +  size_t size
          769  +){
          770  +  struct Blob work = BLOB_INITIALIZER;
          771  +  if( size>1 ){
          772  +    if( rndr->make.normal_text ){
          773  +      blob_init(&work, data+1,1);
          774  +      rndr->make.normal_text(ob, &work, rndr->make.opaque);
          775  +    }else{
          776  +      blob_append(ob, data+1, 1);
          777  +    }
          778  +  }
          779  +  return 2;
          780  +}
          781  +
          782  +
          783  +/* char_entity -- '&' escaped when it doesn't belong to an entity */
          784  +/* valid entities are assumed to be anything mathing &#?[A-Za-z0-9]+; */
          785  +static size_t char_entity(
          786  +  struct Blob *ob,
          787  +  struct render *rndr,
          788  +  char *data,
          789  +  size_t offset,
          790  +  size_t size
          791  +){
          792  +  size_t end = 1;
          793  +  struct Blob work = BLOB_INITIALIZER;
          794  +  if( end<size && data[end]=='#' ) end++;
          795  +  while( end<size
          796  +   && ((data[end]>='0' && data[end]<='9')
          797  +       || (data[end]>='a' && data[end]<='z')
          798  +       || (data[end]>='A' && data[end]<='Z'))
          799  +  ){
          800  +    end++;
          801  +  }
          802  +  if( end<size && data[end]==';' ){
          803  +    /* real entity */
          804  +    end++;
          805  +  }else{
          806  +    /* lone '&' */
          807  +    return 0;
          808  +  }
          809  +  if( rndr->make.entity ){
          810  +    blob_init(&work, data, end);
          811  +    rndr->make.entity(ob, &work, rndr->make.opaque);
          812  +  }else{
          813  +    blob_append(ob, data, end);
          814  +  }
          815  +  return end;
          816  +}
          817  +
          818  +
          819  +/* char_langle_tag -- '<' when tags or autolinks are allowed */
          820  +static size_t char_langle_tag(
          821  +  struct Blob *ob,
          822  +  struct render *rndr,
          823  +  char *data,
          824  +  size_t offset,
          825  +  size_t size
          826  +){
          827  +  enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
          828  +  size_t end = tag_length(data, size, &altype);
          829  +  struct Blob work = BLOB_INITIALIZER;
          830  +  int ret = 0;
          831  +  if( end ){
          832  +    if( rndr->make.autolink && altype!=MKDA_NOT_AUTOLINK ){
          833  +      blob_init(&work, data+1, end-2);
          834  +      ret = rndr->make.autolink(ob, &work, altype, rndr->make.opaque);
          835  +    }else if( rndr->make.raw_html_tag ){
          836  +      blob_init(&work, data, end);
          837  +      ret = rndr->make.raw_html_tag(ob, &work, rndr->make.opaque);
          838  +    }
          839  +  }
          840  +
          841  +  if( !ret ){
          842  +    return 0;
          843  +  }else{
          844  +    return end;
          845  +  }
          846  +}
          847  +
          848  +
          849  +/* get_link_inline -- extract inline-style link and title from parenthesed data*/
          850  +static int get_link_inline(
          851  +  struct Blob *link,
          852  +  struct Blob *title,
          853  +  char *data,
          854  +  size_t size
          855  +){
          856  +  size_t i = 0, mark;
          857  +  size_t link_b, link_e;
          858  +  size_t title_b = 0, title_e = 0;
          859  +
          860  +  /* skipping initial whitespace */
          861  +  while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
          862  +  link_b = i;
          863  +
          864  +  /* looking for link end: ' " */
          865  +  while( i<size && data[i]!='\'' && data[i]!='"' ){ i++; }
          866  +  link_e = i;
          867  +
          868  +  /* looking for title end if present */
          869  +  if( data[i]=='\'' || data[i]=='"' ){
          870  +    i++;
          871  +    title_b = i;
          872  +
          873  +    /* skipping whitespaces after title */
          874  +    title_e = size-1;
          875  +    while( title_e>title_b
          876  +     && (data[title_e]==' '
          877  +         || data[title_e]=='\t'
          878  +         || data[title_e]=='\n')
          879  +    ){
          880  +      title_e--;
          881  +    }
          882  +
          883  +    /* checking for closing quote presence */
          884  +    if (data[title_e] != '\'' &&  data[title_e] != '"') {
          885  +      title_b = title_e = 0;
          886  +      link_e = i;
          887  +    }
          888  +  }
          889  +
          890  +  /* remove whitespace at the end of the link */
          891  +  while( link_e>link_b
          892  +   && (data[link_e-1]==' '
          893  +       || data[link_e-1]=='\t'
          894  +       || data[link_e-1]=='\n')
          895  +  ){
          896  +    link_e--;
          897  +  }
          898  +
          899  +  /* remove optional angle brackets around the link */
          900  +  if( data[link_b]=='<' ) link_b += 1;
          901  +  if( data[link_e-1]=='>' ) link_e -= 1;
          902  +
          903  +  /* escape backslashed character from link */
          904  +  blob_reset(link);
          905  +  i = link_b;
          906  +  while( i<link_e ){
          907  +    mark = i;
          908  +    while( i<link_e && data[i]!='\\' ){ i++; }
          909  +    blob_append(link, data+mark, i-mark);
          910  +    while( i<link_e && data[i]=='\\' ){ i++; }
          911  +  }
          912  +
          913  +  /* handing back title */
          914  +  blob_reset(title);
          915  +  if( title_e>title_b ) blob_append(title, data+title_b, title_e-title_b);
          916  +
          917  +  /* this function always succeed */
          918  +  return 0;
          919  +}
          920  +
          921  +
          922  +/* get_link_ref -- extract referenced link and title from id */
          923  +static int get_link_ref(
          924  +  struct render *rndr,
          925  +  struct Blob *link,
          926  +  struct Blob *title,
          927  +  char *data,
          928  +  size_t size
          929  +){
          930  +  struct link_ref *lr;
          931  +
          932  +  /* find the link from its id (stored temporarily in link) */
          933  +  blob_reset(link);
          934  +  if( build_ref_id(link, data, size)<0 ) return -1;
          935  +  lr = bsearch(link,
          936  +               blob_buffer(&rndr->refs),
          937  +               blob_size(&rndr->refs)/sizeof(struct link_ref),
          938  +               sizeof (struct link_ref),
          939  +               cmp_link_ref);
          940  +  if( !lr ) return -1;
          941  +
          942  +  /* fill the output buffers */
          943  +  blob_reset(link);
          944  +  blob_reset(title);
          945  +  blob_append(link, blob_buffer(&lr->link), blob_size(&lr->link));
          946  +  blob_append(title, blob_buffer(&lr->title), blob_size(&lr->title));
          947  +  return 0;
          948  +}
          949  +
          950  +
          951  +/* char_link -- '[': parsing a link or an image */
          952  +static size_t char_link(
          953  +  struct Blob *ob,
          954  +  struct render *rndr,
          955  +  char *data,
          956  +  size_t offset,
          957  +  size_t size
          958  +){
          959  +  int is_img = (offset && data[-1] == '!'), level;
          960  +  size_t i = 1, txt_e;
          961  +  struct Blob *content = 0;
          962  +  struct Blob *link = 0;
          963  +  struct Blob *title = 0;
          964  +  int text_has_nl = 0, ret;
          965  +
          966  +  /* checking whether the correct renderer exists */
          967  +  if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
          968  +    return 0;
          969  +  }
          970  +
          971  +  /* looking for the matching closing bracket */
          972  +  for(level=1; i<size; i++){
          973  +    if( data[i]=='\n' )        text_has_nl = 1;
          974  +    else if( data[i-1]=='\\' ) continue;
          975  +    else if( data[i]=='[' )    level += 1;
          976  +    else if( data[i]==']' ){
          977  +      level--;
          978  +      if( level<=0 ) break;
          979  +    }
          980  +  }
          981  +  if( i>=size ) return 0;
          982  +  txt_e = i;
          983  +  i++;
          984  +
          985  +  /* skip any amount of whitespace or newline */
          986  +  /* (this is much more laxist than original markdown syntax) */
          987  +  while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
          988  +
          989  +  /* allocate temporary buffers to store content, link and title */
          990  +  content = new_work_buffer(rndr);
          991  +  link = new_work_buffer(rndr);
          992  +  title = new_work_buffer(rndr);
          993  +  if( !title ) return 0;
          994  +  ret = 0; /* error if we don't get to the callback */
          995  +
          996  +  /* inline style link */
          997  +  if( i<size && data[i]=='(' ){
          998  +    size_t span_end = i;
          999  +    while( span_end<size
         1000  +     && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
         1001  +    ){
         1002  +      span_end++;
         1003  +    }
         1004  +
         1005  +    if( span_end>=size
         1006  +     || get_link_inline(link, title, data+i+1, span_end-(i+1))<0
         1007  +    ){
         1008  +      goto char_link_cleanup;
         1009  +    }
         1010  +
         1011  +    i = span_end+1;
         1012  +
         1013  +  /* reference style link */
         1014  +  }else if( i<size && data[i]=='[' ){
         1015  +    char *id_data;
         1016  +    size_t id_size, id_end = i;
         1017  +
         1018  +    while( id_end<size && data[id_end]!=']' ){ id_end++; }
         1019  +
         1020  +    if( id_end>=size ) goto char_link_cleanup;
         1021  +
         1022  +    if( i+1==id_end ){
         1023  +      /* implicit id - use the contents */
         1024  +      id_data = data+1;
         1025  +      id_size = txt_e-1;
         1026  +    }else{
         1027  +      /* explici id - between brackets */
         1028  +      id_data = data+i+1;
         1029  +      id_size = id_end-(i+1);
         1030  +    }
         1031  +
         1032  +    if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
         1033  +      goto char_link_cleanup;
         1034  +    }
         1035  +
         1036  +    i = id_end+1;
         1037  +
         1038  +  /* shortcut reference style link */
         1039  +  }else{
         1040  +    if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
         1041  +      goto char_link_cleanup;
         1042  +    }
         1043  +
         1044  +    /* rewinding the whitespace */
         1045  +    i = txt_e+1;
         1046  +  }
         1047  +
         1048  +  /* building content: img alt is escaped, link content is parsed */
         1049  +  if( txt_e>1 ){
         1050  +    if( is_img ) blob_append(content, data+1, txt_e-1);
         1051  +    else parse_inline(content, rndr, data+1, txt_e-1);
         1052  +  }
         1053  +
         1054  +  /* calling the relevant rendering function */
         1055  +  if( is_img ){
         1056  +    if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
         1057  +    ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
         1058  +  }else{
         1059  +    ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
         1060  +  }
         1061  +
         1062  +  /* cleanup */
         1063  +char_link_cleanup:
         1064  +  release_work_buffer(rndr, title);
         1065  +  release_work_buffer(rndr, link);
         1066  +  release_work_buffer(rndr, content);
         1067  +  return ret ? i : 0;
         1068  +}
         1069  +
         1070  +
         1071  +
         1072  +/*********************************
         1073  + * BLOCK-LEVEL PARSING FUNCTIONS *
         1074  + *********************************/
         1075  +
         1076  +/* is_empty -- returns the line length when it is empty, 0 otherwise */
         1077  +static size_t is_empty(char *data, size_t size){
         1078  +  size_t i;
         1079  +  for(i=0; i<size && data[i]!='\n'; i++){
         1080  +    if( data[i]!=' ' && data[i]!='\t' ) return 0;
         1081  +  }
         1082  +  return i+1;
         1083  +}
         1084  +
         1085  +
         1086  +/* is_hrule -- returns whether a line is a horizontal rule */
         1087  +static int is_hrule(char *data, size_t size){
         1088  +  size_t i = 0, n = 0;
         1089  +  char c;
         1090  +
         1091  +  /* skipping initial spaces */
         1092  +  if( size<3 ) return 0;
         1093  +  if( data[0]==' ' ){
         1094  +    i++;
         1095  +    if( data[1]==' ' ){
         1096  +      i++;
         1097  +       if( data[2]==' ' ){
         1098  +         i++;
         1099  +       }
         1100  +     }
         1101  +   }
         1102  +
         1103  +  /* looking at the hrule char */
         1104  +  if( i+2>=size || (data[i]!='*' && data[i]!='-' && data[i]!='_') ) return 0;
         1105  +  c = data[i];
         1106  +
         1107  +  /* the whole line must be the char or whitespace */
         1108  +  while (i < size && data[i] != '\n') {
         1109  +    if( data[i]==c ){
         1110  +      n += 1;
         1111  +    }else if( data[i]!=' ' && data[i]!='\t' ){
         1112  +      return 0;
         1113  +    }
         1114  +    i++;
         1115  +  }
         1116  +
         1117  +  return n>=3;
         1118  +}
         1119  +
         1120  +
         1121  +/* is_headerline -- returns whether the line is a setext-style hdr underline */
         1122  +static int is_headerline(char *data, size_t size){
         1123  +  size_t i = 0;
         1124  +
         1125  +  /* test of level 1 header */
         1126  +  if( data[i]=='=' ){
         1127  +    for(i=1; i<size && data[i]=='='; i++);
         1128  +    while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1129  +    return (i>=size || data[i]=='\n') ? 1 : 0;
         1130  +  }
         1131  +
         1132  +  /* test of level 2 header */
         1133  +  if( data[i]=='-' ){
         1134  +    for(i=1; i<size && data[i]=='-'; i++);
         1135  +    while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1136  +    return (i>=size || data[i]=='\n') ? 2 : 0;
         1137  +  }
         1138  +
         1139  +  return 0;
         1140  +}
         1141  +
         1142  +
         1143  +/* is_table_sep -- returns wether there is a table separator at the given pos */
         1144  +static int is_table_sep(char *data, size_t pos){
         1145  +  return data[pos]=='|' && (pos==0 || data[pos-1]!='\\');
         1146  +}
         1147  +
         1148  +
         1149  +/* is_tableline -- returns the number of column tables in the given line */
         1150  +static int is_tableline(char *data, size_t size){
         1151  +  size_t i = 0;
         1152  +  int n_sep = 0, outer_sep = 0;
         1153  +
         1154  +  /* skip initial blanks */
         1155  +  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1156  +
         1157  +  /* check for initial '|' */
         1158  +  if( i<size && data[i]=='|') outer_sep++;
         1159  +
         1160  +  /* count the number of pipes in the line */
         1161  +  for(n_sep=0; i<size && data[i]!='\n'; i++){
         1162  +    if( is_table_sep(data, i) ) n_sep++;
         1163  +  }
         1164  +
         1165  +  /* march back to check for optional last '|' before blanks and EOL */
         1166  +  while( i && (data[i-1]==' ' || data[i-1]=='\t' || data[i-1]=='\n') ){ i--; }
         1167  +  if( i && is_table_sep(data, i-1) ) outer_sep += 1;
         1168  +
         1169  +  /* return the number of column or 0 if it's not a table line */
         1170  +  return (n_sep>0) ? (n_sep-outer_sep+1) : 0;
         1171  +}
         1172  +
         1173  +
         1174  +/* prefix_quote -- returns blockquote prefix length */
         1175  +static size_t prefix_quote(char *data, size_t size){
         1176  +  size_t i = 0;
         1177  +  if( i<size && data[i]==' ' ) i++;
         1178  +  if( i<size && data[i]==' ' ) i++;
         1179  +  if( i<size && data[i]==' ' ) i++;
         1180  +  if( i<size && data[i]=='>' ){
         1181  +    if( i+1<size && (data[i+1]==' ' || data[i+1]=='\t') ){
         1182  +      return i + 2;
         1183  +    }else{
         1184  +      return i + 1;
         1185  +    }
         1186  +  }else{
         1187  +    return 0;
         1188  +  }
         1189  +}
         1190  +
         1191  +
         1192  +/* prefix_code -- returns prefix length for block code*/
         1193  +static size_t prefix_code(char *data, size_t size){
         1194  +  if( size>0 && data[0]=='\t' ) return 1;
         1195  +  if( size>3 && data[0]==' ' && data[1]==' ' && data[2]==' ' && data[3]==' ' ){
         1196  +    return 4;
         1197  +  }
         1198  +  return 0;
         1199  +}
         1200  +
         1201  +/* prefix_oli -- returns ordered list item prefix */
         1202  +static size_t prefix_oli(char *data, size_t size){
         1203  +  size_t i = 0;
         1204  +  if( i<size && data[i]==' ') i++;
         1205  +  if( i<size && data[i]==' ') i++;
         1206  +  if( i<size && data[i]==' ') i++;
         1207  +
         1208  +  if( i>=size || data[i]<'0' || data[i]>'9' ) return 0;
         1209  +  while( i<size && data[i]>='0' && data[i]<='9' ){ i++; }
         1210  +
         1211  +  if( i+1>=size
         1212  +   || data[i]!='.'
         1213  +   || (data[i+1]!=' ' && data[i+1]!='\t')
         1214  +  ){
         1215  +   return 0;
         1216  +  }
         1217  +  i = i+2;
         1218  +  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1219  +  return i;
         1220  +}
         1221  +
         1222  +
         1223  +/* prefix_uli -- returns ordered list item prefix */
         1224  +static size_t prefix_uli(char *data, size_t size){
         1225  +  size_t i = 0;
         1226  +  if( i<size && data[i]==' ') i++;
         1227  +  if( i<size && data[i]==' ') i++;
         1228  +  if( i<size && data[i]==' ') i++;
         1229  +  if( i+1>=size
         1230  +   || (data[i]!='*' && data[i]!='+' && data[i]!='-')
         1231  +   || (data[i+1]!=' ' && data[i+1]!='\t')
         1232  +  ){
         1233  +    return 0;
         1234  +  }
         1235  +  i = i+2;
         1236  +  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1237  +  return i;
         1238  +}
         1239  +
         1240  +
         1241  +/* parse_block predeclaration */
         1242  +static void parse_block(
         1243  +  struct Blob *ob,
         1244  +  struct render *rndr,
         1245  +  char *data,
         1246  +  size_t size);
         1247  +
         1248  +
         1249  +/* parse_blockquote -- hanldes parsing of a blockquote fragment */
         1250  +static size_t parse_blockquote(
         1251  +  struct Blob *ob,
         1252  +  struct render *rndr,
         1253  +  char *data,
         1254  +  size_t size
         1255  +){
         1256  +  size_t beg, end = 0, pre, work_size = 0;
         1257  +  char *work_data = 0;
         1258  +  struct Blob *out = new_work_buffer(rndr);
         1259  +
         1260  +  beg = 0;
         1261  +  while( beg<size ){
         1262  +    for(end=beg+1; end<size && data[end-1]!='\n'; end++);
         1263  +    pre = prefix_quote(data+beg, end-beg);
         1264  +    if( pre ){
         1265  +      beg += pre; /* skipping prefix */
         1266  +    }else if( is_empty(data+beg, end-beg)
         1267  +     && (end>=size
         1268  +         || (prefix_quote(data+end, size-end)==0
         1269  +             && !is_empty(data+end, size-end)))
         1270  +    ){
         1271  +      /* empty line followed by non-quote line */
         1272  +      break;
         1273  +    }
         1274  +    if( beg<end ){ /* copy into the in-place working buffer */
         1275  +      if( !work_data ){
         1276  +        work_data = data+beg;
         1277  +      }else if( (data+beg)!=(work_data+work_size) ){
         1278  +        memmove(work_data+work_size, data+beg, end-beg);
         1279  +      }
         1280  +      work_size += end-beg;
         1281  +    }
         1282  +    beg = end;
         1283  +  }
         1284  +
         1285  +  if( rndr->make.blockquote ){
         1286  +    struct Blob fallback = BLOB_INITIALIZER;
         1287  +    if( out ){
         1288  +      parse_block(out, rndr, work_data, work_size);
         1289  +    }else{
         1290  +      blob_init(&fallback, work_data, work_size);
         1291  +    }
         1292  +    rndr->make.blockquote(ob, out ? out : &fallback, rndr->make.opaque);
         1293  +  }
         1294  +  release_work_buffer(rndr, out);
         1295  +  return end;
         1296  +}
         1297  +
         1298  +
         1299  +/* parse_blockquote -- hanldes parsing of a regular paragraph */
         1300  +static size_t parse_paragraph(
         1301  +  struct Blob *ob,
         1302  +  struct render *rndr,
         1303  +  char *data,
         1304  +  size_t size
         1305  +){
         1306  +  size_t i = 0, end = 0;
         1307  +  int level = 0;
         1308  +  char *work_data = data;
         1309  +  size_t work_size = 0;
         1310  +  struct Blob fallback = BLOB_INITIALIZER;
         1311  +
         1312  +  while( i<size ){
         1313  +    for(end=i+1; end<size && data[end-1]!='\n'; end++);
         1314  +    if( is_empty(data+i, size-i)
         1315  +     || (level = is_headerline(data+i, size-i))!= 0
         1316  +    ){
         1317  +      break;
         1318  +    }
         1319  +    if( (i && data[i]=='#') || is_hrule(data+i, size-i) ){
         1320  +      end = i;
         1321  +      break;
         1322  +    }
         1323  +    i = end;
         1324  +  }
         1325  +
         1326  +  work_size = i;
         1327  +  while( work_size && data[work_size-1]=='\n' ){ work_size--; }
         1328  +
         1329  +  if( !level ){
         1330  +    if( rndr->make.paragraph ){
         1331  +      struct Blob *tmp = new_work_buffer(rndr);
         1332  +      if( tmp ){
         1333  +        parse_inline(tmp, rndr, work_data, work_size);
         1334  +      }else{
         1335  +        blob_init(&fallback, work_data, work_size);
         1336  +      }
         1337  +      rndr->make.paragraph(ob, tmp ? tmp : &fallback, rndr->make.opaque);
         1338  +      release_work_buffer(rndr, tmp);
         1339  +    }
         1340  +  }else{
         1341  +    if( work_size ){
         1342  +      size_t beg;
         1343  +      i = work_size;
         1344  +      work_size -= 1;
         1345  +      while( work_size && data[work_size]!='\n' ){ work_size--; }
         1346  +      beg = work_size+1;
         1347  +      while( work_size && data[work_size-1]=='\n'){ work_size--; }
         1348  +      if( work_size ){
         1349  +        struct Blob *tmp = new_work_buffer(rndr);
         1350  +        if( tmp ){
         1351  +          parse_inline(tmp, rndr, work_data, work_size);
         1352  +        }else{
         1353  +          blob_init (&fallback, work_data, work_size);
         1354  +        }
         1355  +        if( rndr->make.paragraph ){
         1356  +          rndr->make.paragraph(ob, tmp ? tmp : &fallback, rndr->make.opaque);
         1357  +        }
         1358  +        release_work_buffer(rndr, tmp);
         1359  +        work_data += beg;
         1360  +        work_size = i - beg;
         1361  +      }else{
         1362  +        work_size = i;
         1363  +      }
         1364  +    }
         1365  +
         1366  +    if( rndr->make.header ){
         1367  +      struct Blob *span = new_work_buffer(rndr);
         1368  +      if( span ){
         1369  +        parse_inline(span, rndr, work_data, work_size);
         1370  +        rndr->make.header(ob, span, level, rndr->make.opaque);
         1371  +      }else{
         1372  +        blob_init(&fallback, work_data, work_size);
         1373  +        rndr->make.header(ob, &fallback, level, rndr->make.opaque);
         1374  +      }
         1375  +      release_work_buffer(rndr, span);
         1376  +    }
         1377  +  }
         1378  +  return end;
         1379  +}
         1380  +
         1381  +
         1382  +/* parse_blockquote -- hanldes parsing of a block-level code fragment */
         1383  +static size_t parse_blockcode(
         1384  +  struct Blob *ob,
         1385  +  struct render *rndr,
         1386  +  char *data,
         1387  +  size_t size
         1388  +){
         1389  +  size_t beg, end, pre;
         1390  +  struct Blob *work = new_work_buffer(rndr);
         1391  +  if( !work ) work = ob;
         1392  +
         1393  +  beg = 0;
         1394  +  while( beg<size ){
         1395  +    for(end=beg+1; end<size && data[end-1]!='\n'; end++);
         1396  +    pre = prefix_code(data+beg, end-beg);
         1397  +    if( pre ){
         1398  +      beg += pre; /* skipping prefix */
         1399  +    }else if( !is_empty(data+beg, end-beg) ){
         1400  +      /* non-empty non-prefixed line breaks the pre */
         1401  +      break;
         1402  +    }
         1403  +    if( beg<end ){
         1404  +      /* verbatim copy to the working buffer, escaping entities */
         1405  +      if( is_empty(data + beg, end - beg) ){
         1406  +        blob_append(work, "\n", 1);
         1407  +      }else{
         1408  +        blob_append(work, data+beg, end-beg);
         1409  +      }
         1410  +    }
         1411  +    beg = end;
         1412  +  }
         1413  +
         1414  +  end = blob_size(work);
         1415  +  while( end>0 && blob_buffer(work)[end-1]=='\n' ){ end--; }
         1416  +  work->nUsed = end;
         1417  +  blob_append(work, "\n", 1);
         1418  +
         1419  +  if( work!=ob ){
         1420  +    if( rndr->make.blockcode ){
         1421  +      rndr->make.blockcode(ob, work, rndr->make.opaque);
         1422  +    }
         1423  +    release_work_buffer(rndr, work);
         1424  +  }
         1425  +  return beg;
         1426  +}
         1427  +
         1428  +
         1429  +/* parse_listitem -- parsing of a single list item */
         1430  +/*  assuming initial prefix is already removed */
         1431  +static size_t parse_listitem(
         1432  +  struct Blob *ob,
         1433  +  struct render *rndr,
         1434  +  char *data,
         1435  +  size_t size,
         1436  +  int *flags
         1437  +){
         1438  +  struct Blob fallback = BLOB_INITIALIZER;
         1439  +  struct Blob *work = 0, *inter = 0;
         1440  +  size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
         1441  +  int in_empty = 0, has_inside_empty = 0;
         1442  +
         1443  +  /* keeping track of the first indentation prefix */
         1444  +  if( size>1 && data[0]==' ' ){
         1445  +    orgpre = 1;
         1446  +    if( size>2 && data[1]==' ' ){
         1447  +      orgpre = 2;
         1448  +      if( size>3 && data[2]==' ' ){
         1449  +        orgpre = 3;
         1450  +      }
         1451  +    }
         1452  +   }
         1453  +  beg = prefix_uli(data, size);
         1454  +  if( !beg ) beg = prefix_oli(data, size);
         1455  +  if( !beg ) return 0;
         1456  +  /* skipping to the beginning of the following line */
         1457  +  end = beg;
         1458  +  while( end<size && data[end-1]!='\n' ){ end++; }
         1459  +
         1460  +  /* getting working buffers */
         1461  +  work = new_work_buffer(rndr);
         1462  +  inter = new_work_buffer(rndr);
         1463  +  if( !work ) work = &fallback;
         1464  +
         1465  +  /* putting the first line into the working buffer */
         1466  +  blob_append(work, data+beg, end-beg);
         1467  +  beg = end;
         1468  +
         1469  +  /* process the following lines */
         1470  +  while( beg<size ){
         1471  +    end++;
         1472  +    while( end<size && data[end-1]!='\n' ){ end++; }
         1473  +
         1474  +    /* process an empty line */
         1475  +    if( is_empty(data+beg, end-beg) ){
         1476  +      in_empty = 1;
         1477  +      beg = end;
         1478  +      continue;
         1479  +    }
         1480  +
         1481  +    /* computing the indentation */
         1482  +    i = 0;
         1483  +    if( end-beg>1 && data[beg]==' ' ){
         1484  +      i = 1;
         1485  +      if( end-beg>2 && data[beg+1]==' ' ){
         1486  +        i = 2;
         1487  +        if( end-beg>3 && data[beg+2]==' ' ){
         1488  +          i = 3;
         1489  +          if( end-beg>3 && data[beg+3]==' ' ){
         1490  +            i = 4;
         1491  +          }
         1492  +        }
         1493  +      }
         1494  +    }
         1495  +    pre = i;
         1496  +    if( data[beg]=='\t' ){ i = 1; pre = 8; }
         1497  +
         1498  +    /* checking for a new item */
         1499  +    if( (prefix_uli(data+beg+i, end-beg-i) && !is_hrule(data+beg+i, end-beg-i))
         1500  +     || prefix_oli(data+beg+i, end-beg-i)
         1501  +    ){
         1502  +      if( in_empty ) has_inside_empty = 1;
         1503  +      if( pre == orgpre ){ /* the following item must have */
         1504  +        break;             /* the same indentation */
         1505  +      }
         1506  +      if( !sublist ) sublist = blob_size(work);
         1507  +
         1508  +    /* joining only indented stuff after empty lines */
         1509  +    }else if( in_empty && i<4 && data[beg]!='\t' ){
         1510  +        *flags |= MKD_LI_END;
         1511  +        break;
         1512  +    }else if( in_empty ){
         1513  +      blob_append(work, "\n", 1);
         1514  +      has_inside_empty = 1;
         1515  +    }
         1516  +    in_empty = 0;
         1517  +
         1518  +    /* adding the line without prefix into the working buffer */
         1519  +    blob_append(work, data+beg+i, end-beg-i);
         1520  +    beg = end;
         1521  +  }
         1522  +
         1523  +  /* non-recursive fallback when working buffer stack is full */
         1524  +  if( !inter ){
         1525  +    if( rndr->make.listitem ){
         1526  +      rndr->make.listitem(ob, work, *flags, rndr->make.opaque);
         1527  +    }
         1528  +    if( work!=&fallback ) release_work_buffer(rndr, work);
         1529  +    blob_zero(&fallback);
         1530  +    return beg;
         1531  +  }
         1532  +
         1533  +  /* render of li contents */
         1534  +  if( has_inside_empty ) *flags |= MKD_LI_BLOCK;
         1535  +  if( *flags & MKD_LI_BLOCK ){
         1536  +    /* intermediate render of block li */
         1537  +    if( sublist && sublist<blob_size(work) ){
         1538  +      parse_block(inter, rndr, blob_buffer(work), sublist);
         1539  +      parse_block(inter,
         1540  +                  rndr,
         1541  +                  blob_buffer(work)+sublist,
         1542  +                  blob_size(work)-sublist);
         1543  +    }else{
         1544  +      parse_block(inter, rndr, blob_buffer(work), blob_size(work));
         1545  +    }
         1546  +  }else{
         1547  +    /* intermediate render of inline li */
         1548  +    if( sublist && sublist<blob_size(work) ){
         1549  +      parse_inline(inter, rndr, blob_buffer(work), sublist);
         1550  +      parse_block(inter,
         1551  +                  rndr,
         1552  +                  blob_buffer(work)+sublist,
         1553  +                  blob_size(work)-sublist);
         1554  +    }else{
         1555  +      parse_inline(inter, rndr, blob_buffer(work), blob_size(work));
         1556  +    }
         1557  +  }
         1558  +
         1559  +  /* render of li itself */
         1560  +  if( rndr->make.listitem ){
         1561  +    rndr->make.listitem(ob, inter, *flags, rndr->make.opaque);
         1562  +  }
         1563  +  release_work_buffer(rndr, inter);
         1564  +  if( work!=&fallback ) release_work_buffer(rndr, work);
         1565  +  blob_zero(&fallback);
         1566  +  return beg;
         1567  +}
         1568  +
         1569  +
         1570  +/* parse_list -- parsing ordered or unordered list block */
         1571  +static size_t parse_list(
         1572  +  struct Blob *ob,
         1573  +  struct render *rndr,
         1574  +  char *data,
         1575  +  size_t size,
         1576  +  int flags
         1577  +){
         1578  +  struct Blob fallback = BLOB_INITIALIZER;
         1579  +  struct Blob *work = new_work_buffer(rndr);
         1580  +  size_t i = 0, j;
         1581  +  if( !work ) work = &fallback;
         1582  +
         1583  +  while( i<size ){
         1584  +    j = parse_listitem(work, rndr, data+i, size-i, &flags);
         1585  +    i += j;
         1586  +    if( !j || (flags & MKD_LI_END) ) break;
         1587  +  }
         1588  +
         1589  +  if( rndr->make.list ) rndr->make.list(ob, work, flags, rndr->make.opaque);
         1590  +  if( work!=&fallback ) release_work_buffer(rndr, work);
         1591  +  blob_zero(&fallback);
         1592  +  return i;
         1593  +}
         1594  +
         1595  +
         1596  +/* parse_atxheader -- parsing of atx-style headers */
         1597  +static size_t parse_atxheader(
         1598  +  struct Blob *ob,
         1599  +  struct render *rndr,
         1600  +  char *data,
         1601  +  size_t size
         1602  +){
         1603  +  int level = 0;
         1604  +  size_t i, end, skip, span_beg, span_size;
         1605  +
         1606  +  if( !size || data[0]!='#' ) return 0;
         1607  +
         1608  +  while( level<size && level<6 && data[level]=='#' ){ level++; }
         1609  +  for(i=level; i<size && (data[i]==' ' || data[i]=='\t'); i++);
         1610  +  span_beg = i;
         1611  +
         1612  +  for(end=i; end<size && data[end]!='\n'; end++);
         1613  +  skip = end;
         1614  +  if( end<=i ) return parse_paragraph(ob, rndr, data, size);
         1615  +  while( end && data[end-1]=='#' ){ end--; }
         1616  +  while( end && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; }
         1617  +  if( end<=i ) return parse_paragraph(ob, rndr, data, size);
         1618  +
         1619  +  span_size = end-span_beg;
         1620  +  if( rndr->make.header ){
         1621  +    struct Blob fallback = BLOB_INITIALIZER;
         1622  +    struct Blob *span = new_work_buffer(rndr);
         1623  +
         1624  +    if( span ){
         1625  +      parse_inline(span, rndr, data+span_beg, span_size);
         1626  +    }else{
         1627  +      blob_init(&fallback, data+span_beg, span_size);
         1628  +    }
         1629  +    rndr->make.header(ob, span ? span : &fallback, level, rndr->make.opaque);
         1630  +    release_work_buffer(rndr, span);
         1631  +  }
         1632  +  return skip;
         1633  +}
         1634  +
         1635  +
         1636  +/* htmlblock_end -- checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
         1637  +/*  returns the length on match, 0 otherwise */
         1638  +static size_t htmlblock_end(struct html_tag *tag, char *data, size_t size){
         1639  +  size_t i, w;
         1640  +
         1641  +  /* assuming data[0]=='<' && data[1]=='/' already tested */
         1642  +
         1643  +  /* checking tag is a match */
         1644  +  if( (tag->size+3)>=size
         1645  +    || fossil_strnicmp(data+2, tag->text, tag->size)
         1646  +    || data[tag->size+2]!='>'
         1647  +  ){
         1648  +    return 0;
         1649  +  }
         1650  +
         1651  +  /* checking white lines */
         1652  +  i = tag->size + 3;
         1653  +  w = 0;
         1654  +  if( i<size && (w = is_empty(data+i, size-i))==0 ){
         1655  +    return 0; /* non-blank after tag */
         1656  +  }
         1657  +  i += w;
         1658  +  w = 0;
         1659  +
         1660  +  if( i<size && (w = is_empty(data + i, size - i))==0 ){
         1661  +    return 0; /* non-blank line after tag line */
         1662  +  }
         1663  +  return i+w;
         1664  +}
         1665  +
         1666  +
         1667  +/* parse_htmlblock -- parsing of inline HTML block */
         1668  +static size_t parse_htmlblock(
         1669  +  struct Blob *ob,
         1670  +  struct render *rndr,
         1671  +  char *data,
         1672  +  size_t size
         1673  +){
         1674  +  size_t i, j = 0;
         1675  +  struct html_tag *curtag;
         1676  +  int found;
         1677  +  size_t work_size = 0;
         1678  +  struct Blob work = BLOB_INITIALIZER;
         1679  +
         1680  +  /* identification of the opening tag */
         1681  +  if( size<2 || data[0]!='<' ) return 0;
         1682  +  curtag = find_block_tag(data+1, size-1);
         1683  +
         1684  +  /* handling of special cases */
         1685  +  if( !curtag ){
         1686  +
         1687  +    /* HTML comment, laxist form */
         1688  +    if( size>5 && data[1]=='!' && data[2]=='-' && data[3]=='-' ){
         1689  +      i = 5;
         1690  +      while( i<size && !(data[i-2]=='-' && data[i-1]=='-' && data[i]=='>') ){
         1691  +        i++;
         1692  +      }
         1693  +      i++;
         1694  +      if( i<size ){
         1695  +        j = is_empty(data+i, size-i);
         1696  +        if( j ){
         1697  +          work_size = i+j;
         1698  +          if( !rndr->make.blockhtml ) return work_size;
         1699  +          blob_init(&work, data, work_size);
         1700  +          rndr->make.blockhtml(ob, &work, rndr->make.opaque);
         1701  +          return work_size;
         1702  +        }
         1703  +      }
         1704  +    }
         1705  +
         1706  +    /* HR, which is the only self-closing block tag considered */
         1707  +    if( size>4
         1708  +      && (data[1]=='h' || data[1]=='H')
         1709  +      && (data[2]=='r' || data[2]=='R')
         1710  +    ){
         1711  +      i = 3;
         1712  +      while( i<size && data[i]!='>' ){ i++; }
         1713  +      if( i+1<size ){
         1714  +        i += 1;
         1715  +        j = is_empty(data+i, size-i);
         1716  +        if( j ){
         1717  +          work_size = i+j;
         1718  +          if( !rndr->make.blockhtml ) return work_size;
         1719  +          blob_init(&work, data, work_size);
         1720  +          rndr->make.blockhtml(ob, &work, rndr->make.opaque);
         1721  +          return work_size;
         1722  +        }
         1723  +      }
         1724  +    }
         1725  +
         1726  +    /* no special case recognised */
         1727  +    return 0;
         1728  +  }
         1729  +
         1730  +  /* looking for an unindented matching closing tag */
         1731  +  /*  followed by a blank line */
         1732  +  i = 1;
         1733  +  found = 0;
         1734  +#if 0
         1735  +  while( i<size ){
         1736  +    i++;
         1737  +    while( i<size && !(data[i-2]=='\n' && data[i-1]=='<' && data[i]=='/') ){
         1738  +      i++;
         1739  +    }
         1740  +    if( (i+2+curtag->size)>=size ) break;
         1741  +    j = htmlblock_end(curtag, data+i-1, size-i+1);
         1742  +    if (j) {
         1743  +      i += j-1;
         1744  +      found = 1;
         1745  +      break;
         1746  +    }
         1747  +  }
         1748  +#endif
         1749  +
         1750  +  /* if not found, trying a second pass looking for indented match */
         1751  +  /* but not if tag is "ins" or "del" (following original Markdown.pl) */
         1752  +  if( !found && curtag!=INS_TAG && curtag!=DEL_TAG ){
         1753  +    i = 1;
         1754  +    while( i<size ){
         1755  +      i++;
         1756  +      while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; }
         1757  +      if( (i+2+curtag->size)>=size ) break;
         1758  +      j = htmlblock_end(curtag, data+i-1, size-i+1);
         1759  +      if (j) {
         1760  +        i += j-1;
         1761  +        found = 1;
         1762  +        break;
         1763  +      }
         1764  +    }
         1765  +  }
         1766  +
         1767  +  if( !found ) return 0;
         1768  +
         1769  +  /* the end of the block has been found */
         1770  +  blob_init(&work, data, i);
         1771  +  if( rndr->make.blockhtml ){
         1772  +    rndr->make.blockhtml(ob, &work, rndr->make.opaque);
         1773  +  }
         1774  +  return i;
         1775  +}
         1776  +
         1777  +
         1778  +/* parse_table_cell -- parse a cell inside a table */
         1779  +static void parse_table_cell(
         1780  +  struct Blob *ob,     /* output blob */
         1781  +  struct render *rndr, /* renderer description */
         1782  +  char *data,          /* input text */
         1783  +  size_t size,         /* input text size */
         1784  +  int flags            /* table flags */
         1785  +){
         1786  +  struct Blob fallback = BLOB_INITIALIZER;
         1787  +  struct Blob *span = new_work_buffer(rndr);
         1788  +
         1789  +  if( span ){
         1790  +    parse_inline(span, rndr, data, size);
         1791  +  }else{
         1792  +    blob_init(&fallback, data, size);
         1793  +  }
         1794  +  rndr->make.table_cell(ob, span ? span : &fallback, flags, rndr->make.opaque);
         1795  +  release_work_buffer(rndr, span);
         1796  +}
         1797  +
         1798  +
         1799  +/* parse_table_row -- parse an input line into a table row */
         1800  +static size_t parse_table_row(
         1801  +  struct Blob *ob,        /* output blob for rendering */
         1802  +  struct render *rndr,    /* renderer description */
         1803  +  char *data,             /* input text */
         1804  +  size_t size,            /* input text size */
         1805  +  int *aligns,            /* array of default alignment for columns */
         1806  +  size_t align_size,      /* number of columns with default alignment */
         1807  +  int flags               /* table flags */
         1808  +){
         1809  +  size_t i = 0, col = 0;
         1810  +  size_t beg, end, total = 0;
         1811  +  struct Blob *cells = new_work_buffer(rndr);
         1812  +  int align;
         1813  +
         1814  +  /* skip leading blanks and sperator */
         1815  +  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1816  +  if( i<size && data[i]=='|' ) i++;
         1817  +
         1818  +  /* go over all the cells */
         1819  +  while( i<size && total==0 ){
         1820  +    /* check optional left/center align marker */
         1821  +    align = 0;
         1822  +    if( data[i]==':' ){
         1823  +      align |= MKD_CELL_ALIGN_LEFT;
         1824  +      i++;
         1825  +    }
         1826  +
         1827  +    /* skip blanks */
         1828  +    while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1829  +    beg = i;
         1830  +
         1831  +    /* forward to the next separator or EOL */
         1832  +    while( i<size && !is_table_sep(data, i) && data[i]!='\n' ){ i++; }
         1833  +    end = i;
         1834  +    if( i<size ){
         1835  +      i++;
         1836  +      if( data[i-1]=='\n' ) total = i;
         1837  +    }
         1838  +
         1839  +    /* check optional right/center align marker */
         1840  +    if( i>beg && data[end-1]==':' ){
         1841  +      align |= MKD_CELL_ALIGN_RIGHT;
         1842  +      end--;
         1843  +    }
         1844  +
         1845  +    /* remove trailing blanks */
         1846  +    while( end>beg && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; }
         1847  +
         1848  +    /* skip the last cell if it was only blanks */
         1849  +    /* (because it is only the optional end separator) */
         1850  +    if( total && end<=beg ) continue;
         1851  +
         1852  +    /* fallback on default alignment if not explicit */
         1853  +    if( align==0 && aligns && col<align_size ) align = aligns[col];
         1854  +
         1855  +    /* render cells */
         1856  +    if( cells ) parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
         1857  +
         1858  +    col++;
         1859  +  }
         1860  +
         1861  +  /* render the whole row and clean up */
         1862  +  if( cells ){
         1863  +    rndr->make.table_row(ob, cells, flags, rndr->make.opaque);
         1864  +  }else{
         1865  +    struct Blob fallback = BLOB_INITIALIZER;
         1866  +    blob_init(&fallback, data, total ? total : size);
         1867  +    rndr->make.table_row(ob, &fallback, flags, rndr->make.opaque);
         1868  +  }
         1869  +  release_work_buffer(rndr, cells);
         1870  +  return total ? total : size;
         1871  +}
         1872  +
         1873  +
         1874  +/* parse_table -- parsing of a whole table */
         1875  +static size_t parse_table(
         1876  +  struct Blob *ob,
         1877  +  struct render *rndr,
         1878  +  char *data,
         1879  +  size_t size
         1880  +){
         1881  +  size_t i = 0, head_end, col;
         1882  +  size_t align_size = 0;
         1883  +  int *aligns = 0;
         1884  +  struct Blob fallback = BLOB_INITIALIZER;
         1885  +  struct Blob *head = 0;
         1886  +  struct Blob *rows = new_work_buffer(rndr);
         1887  +  if( !rows ) rows = &fallback;
         1888  +
         1889  +  /* skip the first (presumably header) line */
         1890  +  while( i<size && data[i]!='\n' ){ i++; }
         1891  +  head_end = i;
         1892  +
         1893  +  /* fallback on end of input */
         1894  +  if( i>=size ){
         1895  +    parse_table_row(rows, rndr, data, size, 0, 0, 0);
         1896  +    rndr->make.table(ob, 0, rows, rndr->make.opaque);
         1897  +    if( rows!=&fallback ) release_work_buffer(rndr, rows);
         1898  +    return i;
         1899  +  }
         1900  +
         1901  +  /* attempt to parse a table rule, i.e. blanks, dash, colons and sep */
         1902  +  i++;
         1903  +  col = 0;
         1904  +  while( i<size
         1905  +    && (data[i]==' '
         1906  +      || data[i]=='\t'
         1907  +      || data[i]=='-'
         1908  +      || data[i] == ':'
         1909  +      || data[i] =='|')
         1910  +  ){
         1911  +    if( data[i] == '|' ) align_size++;
         1912  +    if( data[i] == ':' ) col = 1;
         1913  +    i += 1;
         1914  +  }
         1915  +
         1916  +  if( i<size && data[i]=='\n' ){
         1917  +    align_size++;
         1918  +
         1919  +    /* render the header row */
         1920  +    head = new_work_buffer(rndr);
         1921  +    if( head ){
         1922  +      parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD);
         1923  +    }
         1924  +
         1925  +    /* parse alignments if provided */
         1926  +    if( col && (aligns=malloc(align_size * sizeof *aligns))!=0 ){
         1927  +      for(i=0; i<align_size; i++) aligns[i] = 0;
         1928  +      col = 0;
         1929  +      i = head_end+1;
         1930  +
         1931  +      /* skip initial white space and optional separator */
         1932  +      while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
         1933  +      if( data[i]=='|' ) i++;
         1934  +
         1935  +      /* compute default alignment for each column */
         1936  +      while (i < size && data[i] != '\n') {
         1937  +        if (data[i] == ':')
         1938  +          aligns[col] |= MKD_CELL_ALIGN_LEFT;
         1939  +        while (i < size
         1940  +        && data[i] != '|' && data[i] != '\n')
         1941  +          i += 1;
         1942  +        if (data[i - 1] == ':')
         1943  +          aligns[col] |= MKD_CELL_ALIGN_RIGHT;
         1944  +        if (i < size && data[i] == '|')
         1945  +          i += 1;
         1946  +        col += 1; }
         1947  +    }
         1948  +
         1949  +    /* point i to the beginning of next line/row */
         1950  +    i++;
         1951  +
         1952  +  }else{
         1953  +    /* there is no valid ruler, continuing without header */
         1954  +    i = 0;
         1955  +  }
         1956  +
         1957  +  /* render the table body lines */
         1958  +  while( i<size && is_tableline(data + i, size - i) ){
         1959  +    i += parse_table_row(rows, rndr, data+i, size-i, aligns, align_size, 0);
         1960  +  }
         1961  +
         1962  +  /* render the full table */
         1963  +  rndr->make.table(ob, head, rows, rndr->make.opaque);
         1964  +
         1965  +  /* cleanup */
         1966  +  if( head ) release_work_buffer(rndr, head);
         1967  +  if( rows!=&fallback ) release_work_buffer(rndr, rows);
         1968  +  free(aligns);
         1969  +  return i;
         1970  +}
         1971  +
         1972  +
         1973  +/* parse_block -- parsing of one block, returning next char to parse */
         1974  +static void parse_block(
         1975  +  struct Blob *ob,        /* output blob */
         1976  +  struct render *rndr,    /* renderer internal state */
         1977  +  char *data,             /* input text */
         1978  +  size_t size             /* input text size */
         1979  +){
         1980  +  size_t beg, end, i;
         1981  +  char *txt_data;
         1982  +  int has_table = (rndr->make.table
         1983  +    && rndr->make.table_row
         1984  +    && rndr->make.table_cell);
         1985  +
         1986  +  beg = 0;
         1987  +  while( beg<size ){
         1988  +    txt_data = data+beg;
         1989  +    end = size-beg;
         1990  +    if( data[beg]=='#' ){
         1991  +      beg += parse_atxheader(ob, rndr, txt_data, end);
         1992  +    }else if( data[beg]=='<'
         1993  +      && rndr->make.blockhtml
         1994  +      && (i = parse_htmlblock(ob, rndr, txt_data, end))!=0
         1995  +    ){
         1996  +      beg += i;
         1997  +    }else if( (i=is_empty(txt_data, end))!=0 ){
         1998  +      beg += i;
         1999  +    }else if( is_hrule(txt_data, end) ){
         2000  +      if( rndr->make.hrule ) rndr->make.hrule(ob, rndr->make.opaque);
         2001  +      while( beg<size && data[beg]!='\n' ){ beg++; }
         2002  +      beg++;
         2003  +    }else if( prefix_quote(txt_data, end) ){
         2004  +      beg += parse_blockquote(ob, rndr, txt_data, end);
         2005  +    }else if( prefix_code(txt_data, end) ){
         2006  +      beg += parse_blockcode(ob, rndr, txt_data, end);
         2007  +    }else if( prefix_uli(txt_data, end) ){
         2008  +      beg += parse_list(ob, rndr, txt_data, end, 0);
         2009  +    }else if( prefix_oli(txt_data, end) ){
         2010  +      beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
         2011  +    }else if( has_table && is_tableline(txt_data, end) ){
         2012  +      beg += parse_table(ob, rndr, txt_data, end);
         2013  +    }else{
         2014  +      beg += parse_paragraph(ob, rndr, txt_data, end);
         2015  +    }
         2016  +  }
         2017  +}
         2018  +
         2019  +
         2020  +
         2021  +/*********************
         2022  + * REFERENCE PARSING *
         2023  + *********************/
         2024  +
         2025  +/* is_ref -- returns whether a line is a reference or not */
         2026  +static int is_ref(
         2027  +  char *data,         /* input text */
         2028  +  size_t beg,         /* offset of the beginning of the line */
         2029  +  size_t end,         /* offset of the end of the text */
         2030  +  size_t *last,       /* last character of the link */
         2031  +  struct Blob *refs   /* arry of link references */
         2032  +){
         2033  +  size_t i = 0;
         2034  +  size_t id_offset, id_end;
         2035  +  size_t link_offset, link_end;
         2036  +  size_t title_offset, title_end;
         2037  +  size_t line_end;
         2038  +  struct link_ref lr = {
         2039  +    BLOB_INITIALIZER,
         2040  +    BLOB_INITIALIZER,
         2041  +    BLOB_INITIALIZER
         2042  +  };
         2043  +
         2044  +  /* up to 3 optional leading spaces */
         2045  +  if( beg+3>=end ) return 0;
         2046  +  if( data[beg]==' ' ){
         2047  +    i = 1;
         2048  +    if( data[beg+1]==' ' ){
         2049  +      i = 2;
         2050  +      if( data[beg+2]==' ' ){
         2051  +        i = 3;
         2052  +        if( data[beg+3]==' ' ) return 0;
         2053  +      }
         2054  +    }
         2055  +  }
         2056  +  i += beg;
         2057  +
         2058  +  /* id part: anything but a newline between brackets */
         2059  +  if( data[i]!='[' ) return 0;
         2060  +  i++;
         2061  +  id_offset = i;
         2062  +  while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
         2063  +  if( i>=end || data[i]!=']' ) return 0;
         2064  +  id_end = i;
         2065  +
         2066  +  /* spacer: colon (space | tab)* newline? (space | tab)* */
         2067  +  i++;
         2068  +  if( i>=end || data[i]!=':' ) return 0;
         2069  +  i++;
         2070  +  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
         2071  +  if( i<end && (data[i]=='\n' || data[i]=='\r') ){
         2072  +    i++;
         2073  +    if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
         2074  +  }
         2075  +  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
         2076  +  if( i>=end ) return 0;
         2077  +
         2078  +  /* link: whitespace-free sequence, optionally between angle brackets */
         2079  +  if( data[i]=='<' ) i++;
         2080  +  link_offset = i;
         2081  +  while( i<end
         2082  +   && data[i]!=' '
         2083  +   && data[i]!='\t'
         2084  +   && data[i]!='\n'
         2085  +   && data[i]!='\r'
         2086  +  ){
         2087  +    i += 1;
         2088  +  }
         2089  +  if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
         2090  +
         2091  +  /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
         2092  +  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
         2093  +  if( i<end
         2094  +   && data[i]!='\n'
         2095  +   && data[i]!='\r'
         2096  +   && data[i]!='\''
         2097  +   && data[i]!='"'
         2098  +   && data[i]!='('
         2099  +  ){
         2100  +    return 0;
         2101  +  }
         2102  +  line_end = 0;
         2103  +  /* computing end-of-line */
         2104  +  if( i>=end || data[i]=='\r' || data[i]=='\n' ) line_end = i;
         2105  +  if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ) line_end = i+1;
         2106  +
         2107  +  /* optional (space|tab)* spacer after a newline */
         2108  +  if( line_end ){
         2109  +    i = line_end+1;
         2110  +    while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
         2111  +  }
         2112  +
         2113  +  /* optional title: any non-newline sequence enclosed in '"()
         2114  +          alone on its line */
         2115  +  title_offset = title_end = 0;
         2116  +  if( i+1<end && (data[i]=='\'' || data[i]=='"' || data[i]=='(') ){
         2117  +    i += 1;
         2118  +    title_offset = i;
         2119  +    /* looking for EOL */
         2120  +    while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; }
         2121  +    if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ){
         2122  +      title_end = i + 1;
         2123  +    }else{
         2124  +      title_end = i;
         2125  +    }
         2126  +    /* stepping back */
         2127  +    i--;
         2128  +    while( i>title_offset && (data[i]==' ' || data[i]=='\t') ){ i--; }
         2129  +    if( i>title_offset && (data[i]=='\'' || data[i]=='"' || data[i]==')') ){
         2130  +      line_end = title_end;
         2131  +      title_end = i;
         2132  +    }
         2133  +  }
         2134  +  if( !line_end ) return 0; /* garbage after the link */
         2135  +
         2136  +  /* a valid ref has been found, filling-in return structures */
         2137  +  if( last ) *last = line_end;
         2138  +  if( !refs ) return 1;
         2139  +  if( build_ref_id(&lr.id, data+id_offset, id_end-id_offset)<0 ) return 0;
         2140  +  blob_append(&lr.link, data+link_offset, link_end-link_offset);
         2141  +  if( title_end>title_offset ){
         2142  +    blob_append(&lr.title, data+title_offset, title_end-title_offset);
         2143  +  }
         2144  +  blob_append(refs, (char *)&lr, sizeof lr);
         2145  +  return 1;
         2146  +}
         2147  +
         2148  +
         2149  +
         2150  +/**********************
         2151  + * EXPORTED FUNCTIONS *
         2152  + **********************/
         2153  +
         2154  +/* markdown -- parses the input buffer and renders it into the output buffer */
         2155  +void markdown(
         2156  +  struct Blob *ob,                   /* output blob for rendered text */
         2157  +  struct Blob *ib,                   /* input blob in markdown */
         2158  +  const struct mkd_renderer *rndrer  /* renderer descriptor (callbacks) */
         2159  +){
         2160  +  struct link_ref *lr;
         2161  +  struct Blob text = BLOB_INITIALIZER;
         2162  +  size_t i, beg, end;
         2163  +  struct render rndr;
         2164  +  char *ib_data;
         2165  +
         2166  +  /* filling the render structure */
         2167  +  if( !rndrer ) return;
         2168  +  rndr.make = *rndrer;
         2169  +  if( rndr.make.max_work_stack<1 ) rndr.make.max_work_stack = 1;
         2170  +  rndr.work_active = 0;
         2171  +  rndr.work = fossil_malloc(rndr.make.max_work_stack * sizeof *rndr.work);
         2172  +  for(i=0; i<rndr.make.max_work_stack; i++) rndr.work[i] = text;
         2173  +  rndr.refs = text;
         2174  +  for(i=0; i<256; i++) rndr.active_char[i] = 0;
         2175  +  if( (rndr.make.emphasis
         2176  +    || rndr.make.double_emphasis
         2177  +    || rndr.make.triple_emphasis)
         2178  +   && rndr.make.emph_chars
         2179  +  ){
         2180  +    for(i=0; rndr.make.emph_chars[i]; i++){
         2181  +      rndr.active_char[(unsigned char)rndr.make.emph_chars[i]] = char_emphasis;
         2182  +    }
         2183  +  }
         2184  +  if( rndr.make.codespan ) rndr.active_char['`'] = char_codespan;
         2185  +  if( rndr.make.linebreak ) rndr.active_char['\n'] = char_linebreak;
         2186  +  if( rndr.make.image || rndr.make.link ) rndr.active_char['['] = char_link;
         2187  +  rndr.active_char['<'] = char_langle_tag;
         2188  +  rndr.active_char['\\'] = char_escape;
         2189  +  rndr.active_char['&'] = char_entity;
         2190  +
         2191  +  /* first pass: looking for references, copying everything else */
         2192  +  beg = 0;
         2193  +  ib_data = blob_buffer(ib);
         2194  +  while( beg<blob_size(ib) ){ /* iterating over lines */
         2195  +    if( is_ref(ib_data, beg, blob_size(ib), &end, &rndr.refs) ){
         2196  +      beg = end;
         2197  +    }else{ /* skipping to the next line */
         2198  +      end = beg;
         2199  +      while( end<blob_size(ib) && ib_data[end]!='\n' && ib_data[end]!='\r' ){
         2200  +        end += 1;
         2201  +      }
         2202  +      /* adding the line body if present */
         2203  +      if( end>beg ) blob_append(&text, ib_data + beg, end - beg);
         2204  +      while( end<blob_size(ib) && (ib_data[end]=='\n' || ib_data[end]=='\r') ){
         2205  +        /* add one \n per newline */
         2206  +        if( ib_data[end]=='\n'
         2207  +         || (end+1<blob_size(ib) && ib_data[end+1]!='\n')
         2208  +        ){
         2209  +          blob_append(&text, "\n", 1);
         2210  +        }
         2211  +        end += 1;
         2212  +      }
         2213  +      beg = end;
         2214  +    }
         2215  +  }
         2216  +
         2217  +  /* sorting the reference array */
         2218  +  if( blob_size(&rndr.refs) ){
         2219  +    qsort(blob_buffer(&rndr.refs),
         2220  +          blob_size(&rndr.refs)/sizeof(struct link_ref),
         2221  +          sizeof(struct link_ref),
         2222  +          cmp_link_ref_sort);
         2223  +  }
         2224  +
         2225  +  /* second pass: actual rendering */
         2226  +  if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque);
         2227  +  parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text));
         2228  +  if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque);
         2229  +
         2230  +  /* clean-up */
         2231  +  blob_zero(&text);
         2232  +  lr = (struct link_ref *)blob_buffer(&rndr.refs);
         2233  +  end = blob_size(&rndr.refs)/sizeof(struct link_ref);
         2234  +  for(i=0; i<end; i++){
         2235  +    blob_zero(&lr[i].id);
         2236  +    blob_zero(&lr[i].link);
         2237  +    blob_zero(&lr[i].title);
         2238  +  }
         2239  +  blob_zero(&rndr.refs);
         2240  +  blobarray_zero(rndr.work, rndr.make.max_work_stack);
         2241  +  fossil_free(rndr.work);
         2242  +}
         2243  +
         2244  +#endif /* def FOSSIL_ENABLE_MARKDOWN */

Added src/markdown_html.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains callbacks for the markdown parser that generate
           19  +** XHTML output.
           20  +*/
           21  +
           22  +#ifdef FOSSIL_ENABLE_MARKDOWN
           23  +
           24  +#include "config.h"
           25  +#include "markdown_html.h"
           26  +
           27  +#if INTERFACE
           28  +
           29  +void markdown_to_html(
           30  +  struct Blob *input_markdown,
           31  +  struct Blob *output_title,
           32  +  struct Blob *output_body);
           33  +
           34  +#endif /* INTERFACE */
           35  +
           36  +
           37  +/* INTER_BLOCK -- skip a line between block level elements */
           38  +#define INTER_BLOCK(ob) \
           39  +  do { if( blob_size(ob)>0 ) blob_append(ob, "\n", 1); } while (0)
           40  +
           41  +/* BLOB_APPEND_LITTERAL -- append a string litteral to a blob */
           42  +#define BLOB_APPEND_LITTERAL(blob, litteral) \
           43  +  blob_append((blob), "" litteral, (sizeof litteral)-1)
           44  +  /*
           45  +   * The empty string in the second argument leads to a syntax error
           46  +   * when the macro is not used with a string litteral. Unfortunately
           47  +   * the error is not overly explicit.
           48  +   */
           49  +
           50  +/* BLOB_APPEND_BLOB -- append blob contents to another */
           51  +#define BLOB_APPEND_BLOB(dest, src) \
           52  +  blob_append((dest), blob_buffer(src), blob_size(src))
           53  +
           54  +
           55  +/* HTML escape */
           56  +
           57  +static void html_escape(struct Blob *ob, const char *data, size_t size){
           58  +  size_t beg = 0, i = 0;
           59  +  while( i<size ){
           60  +    beg = i;
           61  +    while( i<size
           62  +     && data[i]!='<'
           63  +     && data[i]!='>'
           64  +     && data[i]!='"'
           65  +     && data[i]!='&'
           66  +    ){
           67  +      i++;
           68  +    }
           69  +    blob_append(ob, data+beg, i-beg);
           70  +    while( i<size ){
           71  +      if( data[i]=='<' ){
           72  +        BLOB_APPEND_LITTERAL(ob, "&lt;");
           73  +      }else if( data[i]=='>' ){
           74  +        BLOB_APPEND_LITTERAL(ob, "&gt;");
           75  +      }else if( data[i]=='&' ){
           76  +        BLOB_APPEND_LITTERAL(ob, "&amp;");
           77  +      }else if( data[i]=='"' ){
           78  +        BLOB_APPEND_LITTERAL(ob, "&quot;");
           79  +      }else{
           80  +        break;
           81  +      }
           82  +      i++;
           83  +    }
           84  +  }
           85  +}
           86  +
           87  +
           88  +/* HTML block tags */
           89  +
           90  +static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){
           91  +  char *data = blob_buffer(text);
           92  +  size_t first = 0, size = blob_size(text);
           93  +  INTER_BLOCK(ob);
           94  +  while( first<size && data[first]=='\n' ) first++;
           95  +  while( size>first && data[size-1]=='\n' ) size--;
           96  +  blob_append(ob, data+first, size-first);
           97  +  BLOB_APPEND_LITTERAL(ob, "\n");
           98  +}
           99  +
          100  +static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
          101  +  INTER_BLOCK(ob);
          102  +  BLOB_APPEND_LITTERAL(ob, "<pre><code>");
          103  +  html_escape(ob, blob_buffer(text), blob_size(text));
          104  +  BLOB_APPEND_LITTERAL(ob, "</code></pre>\n");
          105  +}
          106  +
          107  +static void html_blockquote(struct Blob *ob, struct Blob *text, void *opaque){
          108  +  INTER_BLOCK(ob);
          109  +  BLOB_APPEND_LITTERAL(ob, "<blockquote>\n");
          110  +  BLOB_APPEND_BLOB(ob, text);
          111  +  BLOB_APPEND_LITTERAL(ob, "</blockquote>\n");
          112  +}
          113  +
          114  +static void html_header(
          115  +  struct Blob *ob,
          116  +  struct Blob *text,
          117  +  int level,
          118  +  void *opaque
          119  +){
          120  +  struct Blob *title = opaque;
          121  +  /* The first header at the beginning of a text is considered as
          122  +   * a title and not output. */
          123  +  if( blob_size(ob)==0 && blob_size(title)==0 ){
          124  +    BLOB_APPEND_BLOB(title, text);
          125  +    return;
          126  +  }
          127  +  INTER_BLOCK(ob);
          128  +  blob_appendf(ob, "<h%d>", level);
          129  +  BLOB_APPEND_BLOB(ob, text);
          130  +  blob_appendf(ob, "</h%d>", level);
          131  +}
          132  +
          133  +static void html_hrule(struct Blob *ob, void *opaque){
          134  +  INTER_BLOCK(ob);
          135  +  BLOB_APPEND_LITTERAL(ob, "<hr />\n");
          136  +}
          137  +
          138  +
          139  +static void html_list(
          140  +  struct Blob *ob,
          141  +  struct Blob *text,
          142  +  int flags,
          143  +  void *opaque
          144  +){
          145  +  char ol[] = "ol";
          146  +  char ul[] = "ul";
          147  +  char *tag = (flags & MKD_LIST_ORDERED) ? ol : ul;
          148  +  INTER_BLOCK(ob);
          149  +  blob_appendf(ob, "<%s>\n", tag);
          150  +  BLOB_APPEND_BLOB(ob, text);
          151  +  blob_appendf(ob, "</%s>\n", tag);
          152  +}
          153  +
          154  +static void html_list_item(
          155  +  struct Blob *ob,
          156  +  struct Blob *text,
          157  +  int flags,
          158  +  void *opaque
          159  +){
          160  +  char *text_data = blob_buffer(text);
          161  +  size_t text_size = blob_size(text);
          162  +  while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--;
          163  +  BLOB_APPEND_LITTERAL(ob, "<li>");
          164  +  blob_append(ob, text_data, text_size);
          165  +  BLOB_APPEND_LITTERAL(ob, "</li>\n");
          166  +}
          167  +
          168  +static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){
          169  +  INTER_BLOCK(ob);
          170  +  BLOB_APPEND_LITTERAL(ob, "<p>");
          171  +  BLOB_APPEND_BLOB(ob, text);
          172  +  BLOB_APPEND_LITTERAL(ob, "</p>\n");
          173  +}
          174  +
          175  +
          176  +static void html_table(
          177  +  struct Blob *ob,
          178  +  struct Blob *head_row,
          179  +  struct Blob *rows,
          180  +  void *opaque
          181  +){
          182  +  INTER_BLOCK(ob);
          183  +  BLOB_APPEND_LITTERAL(ob, "<table>\n");
          184  +  if( head_row && blob_size(head_row)>0 ){
          185  +    BLOB_APPEND_LITTERAL(ob, "<thead>\n");
          186  +    BLOB_APPEND_BLOB(ob, head_row);
          187  +    BLOB_APPEND_LITTERAL(ob, "</thead>\n<tbody>\n");
          188  +  }
          189  +  if( rows ){
          190  +    BLOB_APPEND_BLOB(ob, rows);
          191  +  }
          192  +  if( head_row && blob_size(head_row)>0 ){
          193  +    BLOB_APPEND_LITTERAL(ob, "</tbody>\n");
          194  +  }
          195  +  BLOB_APPEND_LITTERAL(ob, "</table>\n");
          196  +}
          197  +
          198  +static void html_table_cell(
          199  +  struct Blob *ob,
          200  +  struct Blob *text,
          201  +  int flags,
          202  +  void *opaque
          203  +){
          204  +  if( flags & MKD_CELL_HEAD ){
          205  +    BLOB_APPEND_LITTERAL(ob, "    <th");
          206  +  }else{
          207  +    BLOB_APPEND_LITTERAL(ob, "    <td");
          208  +  }
          209  +  switch( flags & MKD_CELL_ALIGN_MASK ){
          210  +    case MKD_CELL_ALIGN_LEFT: {
          211  +      BLOB_APPEND_LITTERAL(ob, " align=\"left\"");
          212  +      break;
          213  +    }
          214  +    case MKD_CELL_ALIGN_RIGHT: {
          215  +      BLOB_APPEND_LITTERAL(ob, " align=\"right\"");
          216  +      break;
          217  +    }
          218  +    case MKD_CELL_ALIGN_CENTER: {
          219  +      BLOB_APPEND_LITTERAL(ob, " align=\"center\"");
          220  +      break;
          221  +    }
          222  +  }
          223  +  BLOB_APPEND_LITTERAL(ob, ">");
          224  +  BLOB_APPEND_BLOB(ob, text);
          225  +  if( flags & MKD_CELL_HEAD ){
          226  +    BLOB_APPEND_LITTERAL(ob, "</th>\n");
          227  +  }else{
          228  +    BLOB_APPEND_LITTERAL(ob, "</td>\n");
          229  +  }
          230  +}
          231  +
          232  +static void html_table_row(
          233  +  struct Blob *ob,
          234  +  struct Blob *cells,
          235  +  int flags,
          236  +  void *opaque
          237  +){
          238  +  BLOB_APPEND_LITTERAL(ob, "  <tr>\n");
          239  +  BLOB_APPEND_BLOB(ob, cells);
          240  +  BLOB_APPEND_LITTERAL(ob, "  </tr>\n");
          241  +}
          242  +
          243  +
          244  +
          245  +/* HTML span tags */
          246  +
          247  +static int html_raw_span(struct Blob *ob, struct Blob *text, void *opaque){
          248  +  BLOB_APPEND_BLOB(ob, text);
          249  +  return 1;
          250  +}
          251  +
          252  +static int html_autolink(
          253  +  struct Blob *ob,
          254  +  struct Blob *link,
          255  +  enum mkd_autolink type,
          256  +  void *opaque
          257  +){
          258  +  if( !link || blob_size(link)<=0 ) return 0;
          259  +  BLOB_APPEND_LITTERAL(ob, "<a href=\"");
          260  +  if( type==MKDA_IMPLICIT_EMAIL ) BLOB_APPEND_LITTERAL(ob, "mailto:");
          261  +  html_escape(ob, blob_buffer(link), blob_size(link));
          262  +  BLOB_APPEND_LITTERAL(ob, "\">");
          263  +  if( type==MKDA_EXPLICIT_EMAIL && blob_size(link)>7 ){
          264  +    /* remove "mailto:" from displayed text */
          265  +    html_escape(ob, blob_buffer(link)+7, blob_size(link)-7);
          266  +  }else{
          267  +    html_escape(ob, blob_buffer(link), blob_size(link));
          268  +  }
          269  +  BLOB_APPEND_LITTERAL(ob, "</a>");
          270  +  return 1;
          271  +}
          272  +
          273  +static int html_code_span(struct Blob *ob, struct Blob *text, void *opaque){
          274  +  BLOB_APPEND_LITTERAL(ob, "<code>");
          275  +  html_escape(ob, blob_buffer(text), blob_size(text));
          276  +  BLOB_APPEND_LITTERAL(ob, "</code>");
          277  +  return 1;
          278  +}
          279  +
          280  +static int html_double_emphasis(
          281  +  struct Blob *ob,
          282  +  struct Blob *text,
          283  +  char c,
          284  +  void *opaque
          285  +){
          286  +  BLOB_APPEND_LITTERAL(ob, "<strong>");
          287  +  BLOB_APPEND_BLOB(ob, text);
          288  +  BLOB_APPEND_LITTERAL(ob, "</strong>");
          289  +  return 1;
          290  +}
          291  +
          292  +static int html_emphasis(
          293  +  struct Blob *ob,
          294  +  struct Blob *text,
          295  +  char c,
          296  +  void *opaque
          297  +){
          298  +  BLOB_APPEND_LITTERAL(ob, "<em>");
          299  +  BLOB_APPEND_BLOB(ob, text);
          300  +  BLOB_APPEND_LITTERAL(ob, "</em>");
          301  +  return 1;
          302  +}
          303  +
          304  +static int html_image(
          305  +  struct Blob *ob,
          306  +  struct Blob *link,
          307  +  struct Blob *title,
          308  +  struct Blob *alt,
          309  +  void *opaque
          310  +){
          311  +  BLOB_APPEND_LITTERAL(ob, "<img src=\"");
          312  +  html_escape(ob, blob_buffer(link), blob_size(link));
          313  +  BLOB_APPEND_LITTERAL(ob, "\" alt=\"");
          314  +  html_escape(ob, blob_buffer(alt), blob_size(alt));
          315  +  if( title && blob_size(title)>0 ){
          316  +    BLOB_APPEND_LITTERAL(ob, "\" title=\"");
          317  +    html_escape(ob, blob_buffer(title), blob_size(title));
          318  +  }
          319  +  BLOB_APPEND_LITTERAL(ob, "\" />");
          320  +  return 1;
          321  +}
          322  +
          323  +static int html_line_break(struct Blob *ob, void *opaque){
          324  +  BLOB_APPEND_LITTERAL(ob, "<br />\n");
          325  +  return 1;
          326  +}
          327  +
          328  +static int html_link(
          329  +  struct Blob *ob,
          330  +  struct Blob *link,
          331  +  struct Blob *title,
          332  +  struct Blob *content,
          333  +  void *opaque
          334  +){
          335  +  BLOB_APPEND_LITTERAL(ob, "<a href=\"");
          336  +  html_escape(ob, blob_buffer(link), blob_size(link));
          337  +  if( title && blob_size(title)>0 ){
          338  +    BLOB_APPEND_LITTERAL(ob, "\" title=\"");
          339  +    html_escape(ob, blob_buffer(title), blob_size(title));
          340  +  }
          341  +  BLOB_APPEND_LITTERAL(ob, "\">");
          342  +  BLOB_APPEND_BLOB(ob, content);
          343  +  BLOB_APPEND_LITTERAL(ob, "</a>");
          344  +  return 1;
          345  +}
          346  +
          347  +static int html_triple_emphasis(
          348  +  struct Blob *ob,
          349  +  struct Blob *text,
          350  +  char c,
          351  +  void *opaque
          352  +){
          353  +  BLOB_APPEND_LITTERAL(ob, "<strong><em>");
          354  +  BLOB_APPEND_BLOB(ob, text);
          355  +  BLOB_APPEND_LITTERAL(ob, "</em></strong>");
          356  +  return 1;
          357  +}
          358  +
          359  +
          360  +static void html_normal_text(struct Blob *ob, struct Blob *text, void *opaque){
          361  +  html_escape(ob, blob_buffer(text), blob_size(text));
          362  +}
          363  +
          364  +
          365  +void markdown_to_html(
          366  +  struct Blob *input_markdown,
          367  +  struct Blob *output_title,
          368  +  struct Blob *output_body
          369  +){
          370  +  struct mkd_renderer html_renderer = {
          371  +    0, 0,  /* no prolog or epilog */
          372  +
          373  +    /* block level elements */
          374  +    html_blockcode,
          375  +    html_blockquote,
          376  +    html_raw_block,
          377  +    html_header,
          378  +    html_hrule,
          379  +    html_list,
          380  +    html_list_item,
          381  +    html_paragraph,
          382  +    html_table,
          383  +    html_table_cell,
          384  +    html_table_row,
          385  +
          386  +    /* span level elements */
          387  +    html_autolink,
          388  +    html_code_span,
          389  +    html_double_emphasis,
          390  +    html_emphasis,
          391  +    html_image,
          392  +    html_line_break,
          393  +    html_link,
          394  +    html_raw_span,
          395  +    html_triple_emphasis,
          396  +
          397  +    /* low level elements */
          398  +    0,  /* entities are copied verbatim */
          399  +    html_normal_text,
          400  +
          401  +    /* misc. parameters */
          402  +    64, /* maximum stack */
          403  +    "*_", /* emphasis characters */
          404  +    output_title /* opaque data */
          405  +  };
          406  +  blob_reset(output_title);
          407  +  blob_reset(output_body);
          408  +  markdown(output_body, input_markdown, &html_renderer);
          409  +}
          410  +
          411  +#endif /* def FOSSIL_ENABLE_MARKDOWN */

Changes to src/md5.c.

    39     39     int isInit;
    40     40     uint32 buf[4];
    41     41     uint32 bits[2];
    42     42     unsigned char in[64];
    43     43   };
    44     44   typedef struct Context MD5Context;
    45     45   
           46  +#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
           47  +# define byteReverse(A,B)
           48  +#else
    46     49   /*
    47         - * Note: this code is harmless on little-endian machines.
           50  + * Convert an array of integers to little-endian.
           51  + * Note: this code is a no-op on little-endian machines.
    48     52    */
    49     53   static void byteReverse (unsigned char *buf, unsigned longs){
    50     54           uint32 t;
    51     55           do {
    52     56                   t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
    53     57                               ((unsigned)buf[1]<<8 | buf[0]);
    54     58                   *(uint32 *)buf = t;
    55     59                   buf += 4;
    56     60           } while (--longs);
    57     61   }
           62  +#endif
           63  +
    58     64   /* The four core functions - F1 is optimized somewhat */
    59     65   
    60     66   /* #define F1(x, y, z) (x & y | ~x & z) */
    61     67   #define F1(x, y, z) (z ^ (x & (y ^ z)))
    62     68   #define F2(x, y, z) F1(z, x, y)
    63     69   #define F3(x, y, z) (x ^ y ^ z)
    64     70   #define F4(x, y, z) (y ^ (x | ~z))
................................................................................
   256    262           /* Append length in bits and transform */
   257    263           ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
   258    264           ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
   259    265   
   260    266           MD5Transform(ctx->buf, (uint32 *)ctx->in);
   261    267           byteReverse((unsigned char *)ctx->buf, 4);
   262    268           memcpy(digest, ctx->buf, 16);
   263         -        memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
          269  +        memset(ctx, 0, sizeof(*ctx));    /* In case it's sensitive */
   264    270   }
   265    271   
   266    272   /*
   267    273   ** Convert a digest into base-16.  digest should be declared as
   268    274   ** "unsigned char digest[16]" in the calling function.  The MD5
   269    275   ** digest is stored in the first 16 bytes.  zBuf should
   270    276   ** be "char zBuf[33]".
................................................................................
   312    318   
   313    319   /*
   314    320   ** Add the content of a blob to the incremental MD5 checksum.
   315    321   */
   316    322   void md5sum_step_blob(Blob *p){
   317    323     md5sum_step_text(blob_buffer(p), blob_size(p));
   318    324   }
          325  +
          326  +/*
          327  +** For trouble-shooting only:
          328  +**
          329  +** Report the current state of the incremental checksum.
          330  +*/
          331  +const char *md5sum_current_state(void){
          332  +  unsigned int cksum = 0;
          333  +  unsigned int *pFirst, *pLast;
          334  +  static char zResult[12];
          335  +
          336  +  pFirst = (unsigned int*)&incrCtx;
          337  +  pLast = (unsigned int*)((&incrCtx)+1);
          338  +  while( pFirst<pLast ){
          339  +    cksum += *pFirst;
          340  +    pFirst++;
          341  +  }
          342  +  sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum);
          343  +  return zResult;
          344  +}
   319    345   
   320    346   /*
   321    347   ** Finish the incremental MD5 checksum.  Store the result in blob pOut
   322    348   ** if pOut!=0.  Also return a pointer to the result.  
   323    349   **
   324    350   ** This resets the incremental checksum preparing for the next round
   325    351   ** of computation.  The return pointer points to a static buffer that
................................................................................
   338    364     }
   339    365     return zOut;
   340    366   }
   341    367   
   342    368   
   343    369   /*
   344    370   ** Compute the MD5 checksum of a file on disk.  Store the resulting
   345         -** checksum in the blob pCksum.  pCksum is assumed to be ininitialized.
          371  +** checksum in the blob pCksum.  pCksum is assumed to be initialized.
   346    372   **
   347    373   ** Return the number of errors.
   348    374   */
   349    375   int md5sum_file(const char *zFilename, Blob *pCksum){
   350    376     FILE *in;
   351    377     MD5Context ctx;
   352    378     unsigned char zResult[16];
   353    379     char zBuf[10240];
   354    380   
   355         -  in = fopen(zFilename,"rb");
          381  +  in = fossil_fopen(zFilename,"rb");
   356    382     if( in==0 ){
   357    383       return 1;
   358    384     }
   359    385     MD5Init(&ctx);
   360    386     for(;;){
   361    387       int n;
   362    388       n = fread(zBuf, 1, sizeof(zBuf), in);
................................................................................
   393    419     MD5Final(zResult, &ctx);
   394    420     DigestToBase16(zResult, blob_buffer(pCksum));
   395    421     return 0;
   396    422   }
   397    423   
   398    424   
   399    425   /*
   400         -** COMMAND: test-md5sum
          426  +** COMMAND: md5sum*
          427  +** Usage: %fossil md5sum FILES....
   401    428   **
   402    429   ** Compute an MD5 checksum of all files named on the command-line.
   403         -** If an file is named "-" then take its content from standard input.
          430  +** If a file is named "-" then content is read from standard input.
   404    431   */
   405    432   void md5sum_test(void){
   406    433     int i;
   407    434     Blob in;
   408    435     Blob cksum;
   409    436     
   410    437     for(i=2; i<g.argc; i++){
          438  +    blob_init(&cksum, "********** not found ***********", -1);
   411    439       if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){
   412    440         blob_read_from_channel(&in, stdin, -1);
   413    441         md5sum_blob(&in, &cksum);
   414    442       }else{
   415    443         md5sum_file(g.argv[i], &cksum);
   416    444       }
   417         -    printf("%s  %s\n", blob_str(&cksum), g.argv[i]);
          445  +    fossil_print("%s  %s\n", blob_str(&cksum), g.argv[i]);
   418    446       blob_reset(&cksum);
   419    447     }
   420    448   }

Changes to src/merge.c.

    18     18   ** This file contains code used to merge two or more branches into
    19     19   ** a single tree.
    20     20   */
    21     21   #include "config.h"
    22     22   #include "merge.h"
    23     23   #include <assert.h>
    24     24   
           25  +/*
           26  +** Print information about a particular check-in.
           27  +*/
           28  +void print_checkin_description(int rid, int indent, const char *zLabel){
           29  +  Stmt q;
           30  +  db_prepare(&q,
           31  +     "SELECT datetime(mtime,'localtime'),"
           32  +     "       coalesce(euser,user), coalesce(ecomment,comment),"
           33  +     "       (SELECT uuid FROM blob WHERE rid=%d),"
           34  +     "       (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
           35  +     "         WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
           36  +     "           AND tagxref.rid=%d AND tagxref.tagtype>0)"
           37  +     "  FROM event WHERE objid=%d", rid, rid, rid);
           38  +  if( db_step(&q)==SQLITE_ROW ){
           39  +    const char *zTagList = db_column_text(&q, 4);
           40  +    char *zCom;
           41  +    if( zTagList && zTagList[0] ){
           42  +      zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
           43  +    }else{
           44  +      zCom = mprintf("%s", db_column_text(&q,2));
           45  +    }
           46  +    fossil_print("%-*s [%S] by %s on %s\n%*s", 
           47  +       indent-1, zLabel,
           48  +       db_column_text(&q, 3),
           49  +       db_column_text(&q, 1),
           50  +       db_column_text(&q, 0),
           51  +       indent, "");
           52  +    comment_print(zCom, indent, 78);
           53  +    fossil_free(zCom);
           54  +  }
           55  +  db_finalize(&q);
           56  +}
           57  +
    25     58   
    26     59   /*
    27     60   ** COMMAND: merge
    28     61   **
    29         -** Usage: %fossil merge [--cherrypick] [--backout] VERSION
           62  +** Usage: %fossil merge ?OPTIONS? ?VERSION?
    30     63   **
    31         -** The argument is a version that should be merged into the current
    32         -** checkout.  All changes from VERSION back to the nearest common
    33         -** ancestor are merged.  Except, if either of the --cherrypick or
           64  +** The argument VERSION is a version that should be merged into the
           65  +** current checkout.  All changes from VERSION back to the nearest
           66  +** common ancestor are merged.  Except, if either of the --cherrypick or
    34     67   ** --backout options are used only the changes associated with the
    35     68   ** single check-in VERSION are merged.  The --backout option causes
    36     69   ** the changes associated with VERSION to be removed from the current
    37     70   ** checkout rather than added.
           71  +**
           72  +** If the VERSION argument is omitted, then Fossil attempts to find
           73  +** a recent fork on the current branch to merge.
    38     74   **
    39     75   ** Only file content is merged.  The result continues to use the
    40     76   ** file and directory names from the current checkout even if those
    41     77   ** names might have been changed in the branch being merged in.
    42     78   **
    43     79   ** Other options:
    44     80   **
           81  +**   --baseline BASELINE     Use BASELINE as the "pivot" of the merge instead
           82  +**                           of the nearest common ancestor.  This allows
           83  +**                           a sequence of changes in a branch to be merged
           84  +**                           without having to merge the entire branch.
           85  +**
    45     86   **   --detail                Show additional details of the merge
    46     87   **
    47     88   **   --binary GLOBPATTERN    Treat files that match GLOBPATTERN as binary
    48     89   **                           and do not try to merge parallel changes.  This
    49     90   **                           option overrides the "binary-glob" setting.
           91  +**
           92  +**   --nochange | -n         Dryrun:  do not actually make any changes; just
           93  +**                           show what would have happened.
           94  +**
           95  +**   --case-sensitive BOOL   Override the case-sensitive setting.  If false,
           96  +**                           files whose names differ only in case are taken
           97  +**                           to be the same file.
           98  +**
           99  +**   --force | -f            Force the merge even if it would be a no-op.
    50    100   */
    51    101   void merge_cmd(void){
    52         -  int vid;              /* Current version */
    53         -  int mid;              /* Version we are merging against */
    54         -  int pid;              /* The pivot version - most recent common ancestor */
          102  +  int vid;              /* Current version "V" */
          103  +  int mid;              /* Version we are merging from "M" */
          104  +  int pid;              /* The pivot version - most recent common ancestor P */
    55    105     int detailFlag;       /* True if the --detail option is present */
    56    106     int pickFlag;         /* True if the --cherrypick option is present */
    57         -  int backoutFlag;      /* True if the --backout optioni is present */
          107  +  int backoutFlag;      /* True if the --backout option is present */
          108  +  int nochangeFlag;     /* True if the --nochange or -n option is present */
          109  +  int forceFlag;        /* True if the --force or -f option is present */
    58    110     const char *zBinGlob; /* The value of --binary */
          111  +  const char *zPivot;   /* The value of --baseline */
          112  +  int debugFlag;        /* True if --debug is present */
          113  +  int nChng;            /* Number of file name changes */
          114  +  int *aChng;           /* An array of file name changes */
          115  +  int i;                /* Loop counter */
          116  +  int nConflict = 0;    /* Number of conflicts seen */
          117  +  int nOverwrite = 0;   /* Number of unmanaged files overwritten */
          118  +  int caseSensitive;    /* True for case-sensitive filenames */
    59    119     Stmt q;
    60    120   
          121  +
          122  +  /* Notation:
          123  +  **
          124  +  **      V     The current checkout
          125  +  **      M     The version being merged in
          126  +  **      P     The "pivot" - the most recent common ancestor of V and M.
          127  +  */
          128  +
          129  +  undo_capture_command_line();
    61    130     detailFlag = find_option("detail",0,0)!=0;
    62    131     pickFlag = find_option("cherrypick",0,0)!=0;
    63    132     backoutFlag = find_option("backout",0,0)!=0;
          133  +  debugFlag = find_option("debug",0,0)!=0;
    64    134     zBinGlob = find_option("binary",0,1);
    65         -  if( g.argc!=3 ){
    66         -    usage("VERSION");
    67         -  }
          135  +  nochangeFlag = find_option("nochange","n",0)!=0;
          136  +  forceFlag = find_option("force","f",0)!=0;
          137  +  zPivot = find_option("baseline",0,1);
          138  +  capture_case_sensitive_option();
          139  +  verify_all_options();
    68    140     db_must_be_within_tree();
          141  +  caseSensitive = filenames_are_case_sensitive();
    69    142     if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
    70    143     vid = db_lget_int("checkout", 0);
    71    144     if( vid==0 ){
    72    145       fossil_fatal("nothing is checked out");
    73    146     }
    74         -  mid = name_to_rid(g.argv[2]);
    75         -  if( mid==0 ){
    76         -    fossil_fatal("not a version: %s", g.argv[2]);
    77         -  }
    78         -  if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
    79         -    fossil_fatal("not a version: %s", g.argv[2]);
    80         -  }
    81         -  if( pickFlag || backoutFlag ){
          147  +
          148  +  /* Find mid, the artifactID of the version to be merged into the current
          149  +  ** check-out */
          150  +  if( g.argc==3 ){
          151  +    /* Mid is specified as an argument on the command-line */
          152  +    mid = name_to_typed_rid(g.argv[2], "ci");
          153  +    if( mid==0 || !is_a_version(mid) ){
          154  +      fossil_fatal("not a version: %s", g.argv[2]);
          155  +    }
          156  +  }else if( g.argc==2 ){
          157  +    /* No version specified on the command-line so pick the most recent
          158  +    ** leaf that is (1) not the version currently checked out and (2)
          159  +    ** has not already been merged into the current checkout and (3)
          160  +    ** the leaf is not closed and (4) the leaf is in the same branch
          161  +    ** as the current checkout. 
          162  +    */
          163  +    Stmt q;
          164  +    if( pickFlag || backoutFlag ){
          165  +      fossil_fatal("cannot use --cherrypick or --backout with a fork merge");
          166  +    }
          167  +    mid = db_int(0,
          168  +      "SELECT leaf.rid"
          169  +      "  FROM leaf, event"
          170  +      " WHERE leaf.rid=event.objid"
          171  +      "   AND leaf.rid!=%d"                                /* Constraint (1) */
          172  +      "   AND leaf.rid NOT IN (SELECT merge FROM vmerge)"  /* Constraint (2) */
          173  +      "   AND NOT EXISTS(SELECT 1 FROM tagxref"            /* Constraint (3) */
          174  +                    "     WHERE rid=leaf.rid"
          175  +                    "       AND tagid=%d"
          176  +                    "       AND tagtype>0)"
          177  +      "   AND (SELECT value FROM tagxref"                  /* Constraint (4) */
          178  +            "   WHERE tagid=%d AND rid=%d AND tagtype>0) ="
          179  +            " (SELECT value FROM tagxref"
          180  +            "   WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)"
          181  +      " ORDER BY event.mtime DESC LIMIT 1",
          182  +      vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH
          183  +    );
          184  +    if( mid==0 ){
          185  +      fossil_fatal("no unmerged forks of branch \"%s\"",
          186  +        db_text(0, "SELECT value FROM tagxref"
          187  +                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
          188  +                   TAG_BRANCH, vid)
          189  +      );
          190  +    }
          191  +    db_prepare(&q,
          192  +      "SELECT blob.uuid,"
          193  +          "   datetime(event.mtime,'localtime'),"
          194  +          "   coalesce(ecomment, comment),"
          195  +          "   coalesce(euser, user)"
          196  +      "  FROM event, blob"
          197  +      " WHERE event.objid=%d AND blob.rid=%d",
          198  +      mid, mid
          199  +    );
          200  +    if( db_step(&q)==SQLITE_ROW ){
          201  +      char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
          202  +            db_column_text(&q, 0), db_column_text(&q, 1),
          203  +            db_column_text(&q, 3), db_column_text(&q, 2));
          204  +      comment_print(zCom, 0, 79);
          205  +      fossil_free(zCom);
          206  +    }
          207  +    db_finalize(&q);
          208  +  }else{
          209  +    usage("?OPTIONS? ?VERSION?");
          210  +    return;
          211  +  }
          212  +
          213  +  if( zPivot ){
          214  +    pid = name_to_typed_rid(zPivot, "ci");
          215  +    if( pid==0 || !is_a_version(pid) ){
          216  +      fossil_fatal("not a version: %s", zPivot);
          217  +    }
          218  +    if( pickFlag ){
          219  +      fossil_fatal("incompatible options: --cherrypick & --baseline");
          220  +    }
          221  +  }else if( pickFlag || backoutFlag ){
    82    222       pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
    83    223       if( pid<=0 ){
    84    224         fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
    85    225       }
    86         -    if( backoutFlag ){
    87         -      int t = pid;
    88         -      pid = mid;
    89         -      mid = t;
    90         -    }
    91    226     }else{
    92    227       pivot_set_primary(mid);
    93    228       pivot_set_secondary(vid);
    94    229       db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
    95    230       while( db_step(&q)==SQLITE_ROW ){
    96    231         pivot_set_secondary(db_column_int(&q,0));
    97    232       }
    98    233       db_finalize(&q);
    99    234       pid = pivot_find();
   100    235       if( pid<=0 ){
   101         -      fossil_fatal("cannot find a common ancestor between the current"
          236  +      fossil_fatal("cannot find a common ancestor between the current "
   102    237                      "checkout and %s", g.argv[2]);
   103    238       }
   104    239     }
   105         -  if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){
   106         -    fossil_fatal("not a version: record #%d", mid);
          240  +  if( backoutFlag ){
          241  +    int t = pid;
          242  +    pid = mid;
          243  +    mid = t;
   107    244     }
   108         -  vfile_check_signature(vid, 1);
          245  +  if( !is_a_version(pid) ){
          246  +    fossil_fatal("not a version: record #%d", pid);
          247  +  }
          248  +  if( !forceFlag && mid==pid ){
          249  +    fossil_print("Merge skipped because it is a no-op. "
          250  +                 " Use --force to override.\n");
          251  +    return;
          252  +  }
          253  +  if( detailFlag ){
          254  +    print_checkin_description(mid, 12, "merge-from:");
          255  +    print_checkin_description(pid, 12, "baseline:");
          256  +  }
          257  +  vfile_check_signature(vid, CKSIG_ENOTFILE);
   109    258     db_begin_transaction();
   110         -  undo_begin();
          259  +  if( !nochangeFlag ) undo_begin();
   111    260     load_vfile_from_rid(mid);
   112    261     load_vfile_from_rid(pid);
          262  +  if( debugFlag ){
          263  +    char *z;
          264  +    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
          265  +    fossil_print("P=%d %z\n", pid, z);
          266  +    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
          267  +    fossil_print("M=%d %z\n", mid, z);
          268  +    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
          269  +    fossil_print("V=%d %z\n", vid, z);
          270  +  }
   113    271   
   114    272     /*
   115    273     ** The vfile.pathname field is used to match files against each other.  The
   116    274     ** FV table contains one row for each each unique filename in
   117    275     ** in the current checkout, the pivot, and the version being merged.
   118    276     */
   119    277     db_multi_exec(
   120    278       "DROP TABLE IF EXISTS fv;"
   121    279       "CREATE TEMP TABLE fv("
   122         -    "  fn TEXT PRIMARY KEY,"      /* The filename */
          280  +    "  fn TEXT PRIMARY KEY COLLATE %s,"  /* The filename */
   123    281       "  idv INTEGER,"              /* VFILE entry for current version */
   124    282       "  idp INTEGER,"              /* VFILE entry for the pivot */
   125    283       "  idm INTEGER,"              /* VFILE entry for version merging in */
   126    284       "  chnged BOOLEAN,"           /* True if current version has been edited */
   127    285       "  ridv INTEGER,"             /* Record ID for current version */
   128    286       "  ridp INTEGER,"             /* Record ID for pivot */
   129         -    "  ridm INTEGER"              /* Record ID for merge */
   130         -    ");"
   131         -    "INSERT OR IGNORE INTO fv"
   132         -    " SELECT pathname, 0, 0, 0, 0, 0, 0, 0 FROM vfile"
          287  +    "  ridm INTEGER,"             /* Record ID for merge */
          288  +    "  isexe BOOLEAN,"            /* Execute permission enabled */
          289  +    "  fnp TEXT,"                 /* The filename in the pivot */
          290  +    "  fnm TEXT,"                 /* the filename in the merged version */
          291  +    "  islinkv BOOLEAN,"          /* True if current version is a symlink */
          292  +    "  islinkm BOOLEAN"           /* True if merged version in is a symlink */
          293  +    ");",
          294  +    caseSensitive ? "binary" : "nocase"
   133    295     );
   134         -  db_prepare(&q,
   135         -    "SELECT id, pathname, rid FROM vfile"
   136         -    " WHERE vid=%d", pid
          296  +
          297  +  /* Add files found in V
          298  +  */
          299  +  db_multi_exec(
          300  +    "INSERT OR IGNORE"
          301  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          302  +    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "
          303  +    " FROM vfile WHERE vid=%d",
          304  +    vid
   137    305     );
   138         -  while( db_step(&q)==SQLITE_ROW ){
   139         -    int id = db_column_int(&q, 0);
   140         -    const char *fn = db_column_text(&q, 1);
   141         -    int rid = db_column_int(&q, 2);
   142         -    db_multi_exec(
   143         -      "UPDATE fv SET idp=%d, ridp=%d WHERE fn=%Q",
   144         -      id, rid, fn
   145         -    );
          306  +
          307  +  /*
          308  +  ** Compute name changes from P->V
          309  +  */
          310  +  find_filename_changes(pid, vid, 0, &nChng, &aChng, debugFlag ? "P->V" : 0);
          311  +  if( nChng ){
          312  +    for(i=0; i<nChng; i++){
          313  +      char *z;
          314  +      z = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
          315  +      db_multi_exec(
          316  +        "UPDATE fv SET fnp=%Q, fnm=%Q"
          317  +        " WHERE fn=(SELECT name FROM filename WHERE fnid=%d)",
          318  +        z, z, aChng[i*2+1]
          319  +      );
          320  +      free(z);
          321  +    }
          322  +    fossil_free(aChng);
          323  +    db_multi_exec("UPDATE fv SET fnm=fnp WHERE fnp!=fn");
   146    324     }
   147         -  db_finalize(&q);
   148         -  db_prepare(&q,
   149         -    "SELECT id, pathname, rid FROM vfile"
   150         -    " WHERE vid=%d", mid
          325  +
          326  +  /* Add files found in P but not in V
          327  +  */
          328  +  db_multi_exec(
          329  +    "INSERT OR IGNORE"
          330  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          331  +    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
          332  +    "   FROM vfile"
          333  +    "  WHERE vid=%d AND pathname NOT IN (SELECT fnp FROM fv)",
          334  +    pid
   151    335     );
   152         -  while( db_step(&q)==SQLITE_ROW ){
   153         -    int id = db_column_int(&q, 0);
   154         -    const char *fn = db_column_text(&q, 1);
   155         -    int rid = db_column_int(&q, 2);
   156         -    db_multi_exec(
   157         -      "UPDATE fv SET idm=%d, ridm=%d WHERE fn=%Q",
   158         -      id, rid, fn
   159         -    );
          336  +
          337  +  /*
          338  +  ** Compute name changes from P->M
          339  +  */
          340  +  find_filename_changes(pid, mid, 0, &nChng, &aChng, debugFlag ? "P->M" : 0);
          341  +  if( nChng ){
          342  +    if( nChng>4 ) db_multi_exec("CREATE INDEX fv_fnp ON fv(fnp)");
          343  +    for(i=0; i<nChng; i++){
          344  +      db_multi_exec(
          345  +        "UPDATE fv SET fnm=(SELECT name FROM filename WHERE fnid=%d)"
          346  +        " WHERE fnp=(SELECT name FROM filename WHERE fnid=%d)",
          347  +        aChng[i*2+1], aChng[i*2]
          348  +      );
          349  +    }
          350  +    fossil_free(aChng);
   160    351     }
   161         -  db_finalize(&q);
   162         -  db_prepare(&q,
   163         -    "SELECT id, pathname, rid, chnged FROM vfile"
   164         -    " WHERE vid=%d", vid
          352  +
          353  +  /* Add files found in M but not in P or V.
          354  +  */
          355  +  db_multi_exec(
          356  +    "INSERT OR IGNORE"
          357  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          358  +    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
          359  +    "   FROM vfile"
          360  +    "  WHERE vid=%d"
          361  +    "    AND pathname NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
          362  +    mid
          363  +  );
          364  +
          365  +  /*
          366  +  ** Compute the file version ids for P and M.
          367  +  */
          368  +  db_multi_exec(
          369  +    "UPDATE fv SET"
          370  +    " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnp),0),"
          371  +    " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnp),0),"
          372  +    " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnm),0),"
          373  +    " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnm),0),"
          374  +    " islinkv=coalesce((SELECT islink FROM vfile"
          375  +                    " WHERE vid=%d AND pathname=fnm),0),"
          376  +    " islinkm=coalesce((SELECT islink FROM vfile"
          377  +                    " WHERE vid=%d AND pathname=fnm),0)",
          378  +    pid, pid, mid, mid, vid, mid
   165    379     );
   166         -  while( db_step(&q)==SQLITE_ROW ){
   167         -    int id = db_column_int(&q, 0);
   168         -    const char *fn = db_column_text(&q, 1);
   169         -    int rid = db_column_int(&q, 2);
   170         -    int chnged = db_column_int(&q, 3);
   171         -    db_multi_exec(
   172         -      "UPDATE fv SET idv=%d, ridv=%d, chnged=%d WHERE fn=%Q",
   173         -      id, rid, chnged, fn
          380  +
          381  +  if( debugFlag ){
          382  +    db_prepare(&q,
          383  +       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
          384  +       "       isexe, islinkv, islinkm FROM fv"
   174    385       );
          386  +    while( db_step(&q)==SQLITE_ROW ){
          387  +       fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
          388  +                    " islinkv=%d islinkm=%d\n",
          389  +          db_column_int(&q, 0),
          390  +          db_column_int(&q, 5),
          391  +          db_column_int(&q, 6),
          392  +          db_column_int(&q, 7),
          393  +          db_column_int(&q, 4),
          394  +          db_column_int(&q, 8),
          395  +          db_column_int(&q, 9),
          396  +          db_column_int(&q, 10));
          397  +       fossil_print("     fn  = [%s]\n", db_column_text(&q, 1));
          398  +       fossil_print("     fnp = [%s]\n", db_column_text(&q, 2));
          399  +       fossil_print("     fnm = [%s]\n", db_column_text(&q, 3));
          400  +    }
          401  +    db_finalize(&q);
   175    402     }
   176         -  db_finalize(&q);
   177    403   
   178    404     /*
   179         -  ** Find files in mid and vid but not in pid and report conflicts.
   180         -  ** The file in mid will be ignored.  It will be treated as if it
          405  +  ** Find files in M and V but not in P and report conflicts.
          406  +  ** The file in M will be ignored.  It will be treated as if it
   181    407     ** does not exist.
   182    408     */
   183    409     db_prepare(&q,
   184    410       "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
   185    411     );
   186    412     while( db_step(&q)==SQLITE_ROW ){
   187    413       int idm = db_column_int(&q, 0);
   188    414       char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
   189         -    printf("WARNING: conflict on %s\n", zName);
          415  +    fossil_warning("WARNING - no common ancestor: %s", zName);
   190    416       free(zName);
   191    417       db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
   192    418     }
   193    419     db_finalize(&q);
   194    420   
   195    421     /*
   196         -  ** Add to vid files that are not in pid but are in mid
          422  +  ** Add to V files that are not in V or P but are in M
   197    423     */
   198         -  db_prepare(&q, 
   199         -    "SELECT idm, rowid, fn FROM fv WHERE idp=0 AND idv=0 AND idm>0"
          424  +  db_prepare(&q,
          425  +    "SELECT idm, rowid, fnm FROM fv AS x"
          426  +    " WHERE idp=0 AND idv=0 AND idm>0"
   200    427     );
   201    428     while( db_step(&q)==SQLITE_ROW ){
   202    429       int idm = db_column_int(&q, 0);
   203    430       int rowid = db_column_int(&q, 1);
   204    431       int idv;
   205    432       const char *zName;
          433  +    char *zFullName;
   206    434       db_multi_exec(
   207         -      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,pathname)"
   208         -      "  SELECT %d,3,0,rid,mrid,pathname FROM vfile WHERE id=%d",
          435  +      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
          436  +      "  SELECT %d,3,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
   209    437         vid, idm
   210    438       );
   211    439       idv = db_last_insert_rowid();
   212    440       db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
   213    441       zName = db_column_text(&q, 2);
   214         -    printf("ADDED %s\n", zName);
   215         -    undo_save(zName);
   216         -    vfile_to_disk(0, idm, 0);
          442  +    zFullName = mprintf("%s%s", g.zLocalRoot, zName);
          443  +    if( file_wd_isfile_or_link(zFullName) ){
          444  +      fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
          445  +      nOverwrite++;
          446  +    }else{
          447  +      fossil_print("ADDED %s\n", zName);
          448  +    }
          449  +    fossil_free(zFullName);
          450  +    if( !nochangeFlag ){
          451  +      undo_save(zName);
          452  +      vfile_to_disk(0, idm, 0, 0);
          453  +    }
   217    454     }
   218    455     db_finalize(&q);
   219    456     
   220    457     /*
   221         -  ** Find files that have changed from pid->mid but not pid->vid. 
   222         -  ** Copy the mid content over into vid.
          458  +  ** Find files that have changed from P->M but not P->V. 
          459  +  ** Copy the M content over into V.
   223    460     */
   224    461     db_prepare(&q,
   225         -    "SELECT idv, ridm FROM fv"
          462  +    "SELECT idv, ridm, fn, islinkm FROM fv"
   226    463       " WHERE idp>0 AND idv>0 AND idm>0"
   227    464       "   AND ridm!=ridp AND ridv=ridp AND NOT chnged"
   228    465     );
   229    466     while( db_step(&q)==SQLITE_ROW ){
   230    467       int idv = db_column_int(&q, 0);
   231    468       int ridm = db_column_int(&q, 1);
   232         -    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
          469  +    const char *zName = db_column_text(&q, 2);
          470  +    int islinkm = db_column_int(&q, 3);
   233    471       /* Copy content from idm over into idv.  Overwrite idv. */
   234         -    printf("UPDATE %s\n", zName);
   235         -    undo_save(zName);
   236         -    db_multi_exec(
   237         -      "UPDATE vfile SET mrid=%d, chnged=2 WHERE id=%d", ridm, idv
   238         -    );
   239         -    vfile_to_disk(0, idv, 0);
   240         -    free(zName);
          472  +    fossil_print("UPDATE %s\n", zName);
          473  +    if( !nochangeFlag ){
          474  +      undo_save(zName);
          475  +      db_multi_exec(
          476  +        "UPDATE vfile SET mtime=0, mrid=%d, chnged=2, islink=%d "
          477  +        " WHERE id=%d", ridm, islinkm, idv
          478  +      );
          479  +      vfile_to_disk(0, idv, 0, 0);
          480  +    }
   241    481     }
   242    482     db_finalize(&q);
   243    483   
   244    484     /*
   245         -  ** Do a three-way merge on files that have changes pid->mid and pid->vid
          485  +  ** Do a three-way merge on files that have changes on both P->M and P->V.
   246    486     */
   247    487     db_prepare(&q,
   248         -    "SELECT ridm, idv, ridp, ridv, %s FROM fv"
          488  +    "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
   249    489       " WHERE idp>0 AND idv>0 AND idm>0"
   250    490       "   AND ridm!=ridp AND (ridv!=ridp OR chnged)",
   251    491       glob_expr("fv.fn", zBinGlob)
   252    492     );
   253    493     while( db_step(&q)==SQLITE_ROW ){
   254    494       int ridm = db_column_int(&q, 0);
   255    495       int idv = db_column_int(&q, 1);
   256    496       int ridp = db_column_int(&q, 2);
   257    497       int ridv = db_column_int(&q, 3);
   258    498       int isBinary = db_column_int(&q, 4);
          499  +    const char *zName = db_column_text(&q, 5);
          500  +    int isExe = db_column_int(&q, 6);
          501  +    int islinkv = db_column_int(&q, 7);
          502  +    int islinkm = db_column_int(&q, 8);
   259    503       int rc;
   260         -    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
   261    504       char *zFullPath;
   262         -    Blob m, p, v, r;
          505  +    Blob m, p, r;
   263    506       /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */
   264    507       if( detailFlag ){
   265         -      printf("MERGE %s  (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv);
          508  +      fossil_print("MERGE %s  (pivot=%d v1=%d v2=%d)\n", 
          509  +                   zName, ridp, ridm, ridv);
   266    510       }else{
   267         -      printf("MERGE %s\n", zName);
          511  +      fossil_print("MERGE %s\n", zName);
   268    512       }
   269         -    undo_save(zName);
   270         -    zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
   271         -    content_get(ridp, &p);
   272         -    content_get(ridm, &m);
   273         -    blob_zero(&v);
   274         -    blob_read_from_file(&v, zFullPath);
   275         -    if( isBinary ){
   276         -      rc = -1;
   277         -      blob_zero(&r);
          513  +    if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){
          514  +      fossil_print("***** Cannot merge symlink %s\n", zName);
          515  +      nConflict++;        
   278    516       }else{
   279         -      rc = blob_merge(&p, &m, &v, &r);
   280         -    }
   281         -    if( rc>=0 ){
   282         -      blob_write_to_file(&r, zFullPath);
   283         -      if( rc>0 ){
   284         -        printf("***** %d merge conflicts in %s\n", rc, zName);
          517  +      undo_save(zName);
          518  +      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
          519  +      content_get(ridp, &p);
          520  +      content_get(ridm, &m);
          521  +      if( isBinary ){
          522  +        rc = -1;
          523  +        blob_zero(&r);
          524  +      }else{
          525  +        unsigned mergeFlags = nochangeFlag ? MERGE_DRYRUN : 0;
          526  +        rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags);
          527  +      }
          528  +      if( rc>=0 ){
          529  +        if( !nochangeFlag ){
          530  +          blob_write_to_file(&r, zFullPath);
          531  +          file_wd_setexe(zFullPath, isExe);
          532  +        }
          533  +        db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
          534  +        if( rc>0 ){
          535  +          fossil_print("***** %d merge conflicts in %s\n", rc, zName);
          536  +          nConflict++;
          537  +        }
          538  +      }else{
          539  +        fossil_print("***** Cannot merge binary file %s\n", zName);
          540  +        nConflict++;
   285    541         }
   286         -    }else{
   287         -      printf("***** Cannot merge binary file %s\n", zName);
          542  +      blob_reset(&p);
          543  +      blob_reset(&m);
          544  +      blob_reset(&r);
   288    545       }
   289         -    free(zName);
   290         -    blob_reset(&p);
   291         -    blob_reset(&m);
   292         -    blob_reset(&v);
   293         -    blob_reset(&r);
   294    546       db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
   295    547                     idv,ridm);
   296    548     }
   297    549     db_finalize(&q);
   298    550   
   299    551     /*
   300         -  ** Drop files from vid that are in pid but not in mid
          552  +  ** Drop files that are in P and V but not in M
   301    553     */
   302    554     db_prepare(&q,
   303         -    "SELECT idv FROM fv"
          555  +    "SELECT idv, fn, chnged FROM fv"
   304    556       " WHERE idp>0 AND idv>0 AND idm=0"
   305    557     );
   306    558     while( db_step(&q)==SQLITE_ROW ){
   307    559       int idv = db_column_int(&q, 0);
   308         -    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
          560  +    const char *zName = db_column_text(&q, 1);
          561  +    int chnged = db_column_int(&q, 2);
   309    562       /* Delete the file idv */
   310         -    printf("DELETE %s\n", zName);
          563  +    fossil_print("DELETE %s\n", zName);
          564  +    if( chnged ){
          565  +      fossil_warning("WARNING: local edits lost for %s\n", zName);
          566  +      nConflict++;
          567  +    }
   311    568       undo_save(zName);
   312    569       db_multi_exec(
   313    570         "UPDATE vfile SET deleted=1 WHERE id=%d", idv
   314    571       );
   315         -    free(zName);
          572  +    if( !nochangeFlag ){
          573  +      char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
          574  +      file_delete(zFullPath);
          575  +      free(zFullPath);
          576  +    }
          577  +  }
          578  +  db_finalize(&q);
          579  +
          580  +  /*
          581  +  ** Rename files that have taken a rename on P->M but which keep the same
          582  +  ** name o P->V.   If a file is renamed on P->V only or on both P->V and
          583  +  ** P->M then we retain the V name of the file.
          584  +  */
          585  +  db_prepare(&q,
          586  +    "SELECT idv, fnp, fnm FROM fv"
          587  +    " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
          588  +  );
          589  +  while( db_step(&q)==SQLITE_ROW ){
          590  +    int idv = db_column_int(&q, 0);
          591  +    const char *zOldName = db_column_text(&q, 1);
          592  +    const char *zNewName = db_column_text(&q, 2);
          593  +    fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
          594  +    undo_save(zOldName);
          595  +    undo_save(zNewName);
          596  +    db_multi_exec(
          597  +      "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
          598  +      " WHERE id=%d AND vid=%d", zNewName, idv, vid
          599  +    );
          600  +    if( !nochangeFlag ){
          601  +      char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
          602  +      char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
          603  +      if( file_wd_islink(zFullOldPath) ){
          604  +        symlink_copy(zFullOldPath, zFullNewPath);
          605  +      }else{
          606  +        file_copy(zFullOldPath, zFullNewPath);
          607  +      }
          608  +      file_delete(zFullOldPath);
          609  +      free(zFullNewPath);
          610  +      free(zFullOldPath);
          611  +    }
   316    612     }
   317    613     db_finalize(&q);
   318         -  
          614  +
          615  +
          616  +  /* Report on conflicts
          617  +  */
          618  +  if( nConflict ){
          619  +    fossil_warning("WARNING: %d merge conflicts", nConflict);
          620  +  }
          621  +  if( nOverwrite ){
          622  +    fossil_warning("WARNING: %d unmanaged files were overwritten",
          623  +                   nOverwrite);
          624  +  }
          625  +  if( nochangeFlag ){
          626  +    fossil_warning("REMINDER: this was a dry run -"
          627  +                   " no file were actually changed.");
          628  +  }
          629  +
   319    630     /*
   320    631     ** Clean up the mid and pid VFILE entries.  Then commit the changes.
   321    632     */
   322    633     db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   323         -  if( !pickFlag ){
   324         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
          634  +  db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
          635  +                pickFlag ? -1 : (backoutFlag ? -2 : 0), mid);
          636  +  if( pickFlag ){
          637  +    /* For a cherry-pick merge, make the default check-in comment the same
          638  +    ** as the check-in comment on the check-in that is being merged in. */
          639  +    db_multi_exec(
          640  +       "REPLACE INTO vvar(name,value)"
          641  +       " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
          642  +       "  WHERE type='ci' AND objid=%d",
          643  +       mid
          644  +    );
   325    645     }
   326    646     undo_finish();
   327         -  db_end_transaction(0);
          647  +  db_end_transaction(nochangeFlag);
   328    648   }

Changes to src/merge3.c.

    25     25   #define ISDEBUG 1
    26     26   #else
    27     27   #define DEBUG(X)
    28     28   #define ISDEBUG 0
    29     29   #endif
    30     30   
    31     31   /* The minimum of two integers */
    32         -#define min(A,B)  (A<B?A:B)
           32  +#ifndef min
           33  +#  define min(A,B)  (A<B?A:B)
           34  +#endif
    33     35   
    34     36   /*
    35     37   ** Compare N lines of text from pV1 and pV2.  If the lines
    36     38   ** are the same, return true.  Return false if one or more of the N
    37     39   ** lines are different.
    38     40   **
    39     41   ** The cursors on both pV1 and pV2 is unchanged by this comparison.
    40     42   */
    41     43   static int sameLines(Blob *pV1, Blob *pV2, int N){
    42     44     char *z1, *z2;
    43     45     int i;
           46  +  char c;
    44     47   
    45     48     if( N==0 ) return 1;
    46     49     z1 = &blob_buffer(pV1)[blob_tell(pV1)];
    47     50     z2 = &blob_buffer(pV2)[blob_tell(pV2)];
    48         -  for(i=0; z1[i]==z2[i]; i++){
    49         -    if( z1[i]=='\n' ){
           51  +  for(i=0; (c=z1[i])==z2[i]; i++){
           52  +    if( c=='\n' || c==0 ){
    50     53         N--;
    51         -      if( N==0 ) return 1;
           54  +      if( N==0 || c==0 ) return 1;
    52     55       }
    53     56     }
    54     57     return 0;
    55     58   }
    56     59   
    57     60   /*
    58     61   ** Look at the next edit triple in both aC1 and aC2.  (An "edit triple" is
    59     62   ** three integers describing the number of copies, deletes, and inserts in
    60     63   ** moving from the original to the edited copy of the file.) If the three
    61         -** integers of the edit triples describe an identical edit, then return 1.  
           64  +** integers of the edit triples describe an identical edit, then return 1.
    62     65   ** If the edits are different, return 0.
    63     66   */
    64     67   static int sameEdit(
    65     68     int *aC1,      /* Array of edit integers for file 1 */
    66     69     int *aC2,      /* Array of edit integers for file 2 */
    67     70     Blob *pV1,     /* Text of file 1 */
    68     71     Blob *pV2      /* Text of file 2 */
................................................................................
    78     81   ** The aC[] array contains triples of integers.  Within each triple, the
    79     82   ** elements are:
    80     83   **
    81     84   **   (0)  The number of lines to copy
    82     85   **   (1)  The number of lines to delete
    83     86   **   (2)  The number of liens to insert
    84     87   **
    85         -** Suppose we want to advance over sz lines of the originl file.  This routine
           88  +** Suppose we want to advance over sz lines of the original file.  This routine
    86     89   ** returns true if that advance would land us on a copy operation.  It
    87     90   ** returns false if the advance would end on a delete.
    88     91   */
    89     92   static int ends_at_CPY(int *aC, int sz){
    90     93     while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
    91     94       if( aC[0]>=sz ) return 1;
    92     95       sz -= aC[0];
................................................................................
   128    131       blob_copy_lines(pOut, pSrc, aC[i+2]);
   129    132       sz -= aC[i] + aC[i+1];
   130    133       i += 3;
   131    134     }
   132    135     return i;
   133    136   }
   134    137   
          138  +/*
          139  +** Text of boundary markers for merge conflicts.
          140  +*/
          141  +static char const * const mergeMarker[] = {
          142  + /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
          143  +  "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
          144  +  "======= COMMON ANCESTOR content follows ============================\n",
          145  +  "======= MERGED IN content follows ==================================\n",
          146  +  ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
          147  +};
   135    148   
   136    149   
   137    150   /*
   138    151   ** Do a three-way merge.  Initialize pOut to contain the result.
   139    152   **
   140    153   ** The merge is an edit against pV2.  Both pV1 and pV2 have a
   141    154   ** common origin at pPivot.  Apply the changes of pPivot ==> pV1
   142    155   ** to pV2.
   143    156   **
   144    157   ** The return is 0 upon complete success. If any input file is binary,
   145    158   ** -1 is returned and pOut is unmodified.  If there are merge
   146         -** conflicts, the merge proceeds as best as it can and the number 
          159  +** conflicts, the merge proceeds as best as it can and the number
   147    160   ** of conflicts is returns
   148    161   */
   149         -int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){
          162  +static int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){
   150    163     int *aC1;              /* Changes from pPivot to pV1 */
   151    164     int *aC2;              /* Changes from pPivot to pV2 */
   152    165     int i1, i2;            /* Index into aC1[] and aC2[] */
   153    166     int nCpy, nDel, nIns;  /* Number of lines to copy, delete, or insert */
   154    167     int limit1, limit2;    /* Sizes of aC1[] and aC2[] */
   155    168     int nConflict = 0;     /* Number of merge conflicts seen so far */
   156         -  static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n";
   157         -  static const char zMid[]   = "============================\n";
   158         -  static const char zEnd[]   = "<<<<<<< END MERGE CONFLICT\n";
   159    169   
   160    170     blob_zero(pOut);         /* Merge results stored in pOut */
   161    171   
   162    172     /* Compute the edits that occur from pPivot => pV1 (into aC1)
   163    173     ** and pPivot => pV2 (into aC2).  Each of the aC1 and aC2 arrays is
   164    174     ** an array of integer triples.  Within each triple, the first integer
   165    175     ** is the number of lines of text to copy directly from the pivot,
   166    176     ** the second integer is the number of lines of text to omit from the
   167    177     ** pivot, and the third integer is the number of lines of text that are
   168    178     ** inserted.  The edit array ends with a triple of 0,0,0.
   169    179     */
   170         -  aC1 = text_diff(pPivot, pV1, 0, 0);
   171         -  aC2 = text_diff(pPivot, pV2, 0, 0);
          180  +  aC1 = text_diff(pPivot, pV1, 0, 0, 0);
          181  +  aC2 = text_diff(pPivot, pV2, 0, 0, 0);
   172    182     if( aC1==0 || aC2==0 ){
   173    183       free(aC1);
   174    184       free(aC2);
   175    185       return -1;
   176    186     }
   177    187   
   178    188     blob_rewind(pV1);        /* Rewind inputs:  Needed to reconstruct output */
................................................................................
   186    196     limit2 = i2;
   187    197   
   188    198     DEBUG(
   189    199       for(i1=0; i1<limit1; i1+=3){
   190    200         printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]);
   191    201       }
   192    202       for(i2=0; i2<limit2; i2+=3){
   193         -     printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
          203  +      printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
   194    204       }
   195    205     )
   196    206   
   197    207     /* Loop over the two edit vectors and use them to compute merged text
   198    208     ** which is written into pOut.  i1 and i2 are multiples of 3 which are
   199    209     ** indices into aC1[] and aC2[] to the edit triple currently being
   200    210     ** processed
................................................................................
   256    266         */
   257    267         int sz = 1;    /* Size of the conflict in lines */
   258    268         nConflict++;
   259    269         while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
   260    270           sz++;
   261    271         }
   262    272         DEBUG( printf("CONFLICT %d\n", sz); )
   263         -      blob_appendf(pOut, zBegin);
          273  +      blob_appendf(pOut, mergeMarker[0]);
   264    274         i1 = output_one_side(pOut, pV1, aC1, i1, sz);
   265         -      blob_appendf(pOut, zMid);
          275  +      blob_appendf(pOut, mergeMarker[1]);
          276  +      blob_copy_lines(pOut, pPivot, sz);
          277  +      blob_appendf(pOut, mergeMarker[2]);
   266    278         i2 = output_one_side(pOut, pV2, aC2, i2, sz);
   267         -      blob_appendf(pOut, zEnd);
   268         -      blob_copy_lines(0, pPivot, sz);
   269         -    }
          279  +      blob_appendf(pOut, mergeMarker[3]);
          280  +   }
   270    281   
   271    282       /* If we are finished with an edit triple, advance to the next
   272    283       ** triple.
   273    284       */
   274    285       if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
   275    286       if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
   276    287     }
................................................................................
   290    301       blob_copy_lines(pOut, pV2, aC2[i2+2]);
   291    302     }
   292    303   
   293    304     free(aC1);
   294    305     free(aC2);
   295    306     return nConflict;
   296    307   }
          308  +
          309  +/*
          310  +** Return true if the input string contains a merge marker on a line by
          311  +** itself.
          312  +*/
          313  +int contains_merge_marker(Blob *p){
          314  +  int i, j;
          315  +  int len = (int)strlen(mergeMarker[0]);
          316  +  const char *z = blob_buffer(p);
          317  +  int n = blob_size(p) - len + 1;
          318  +  assert( len==(int)strlen(mergeMarker[1]) );
          319  +  assert( len==(int)strlen(mergeMarker[2]) );
          320  +  assert( len==(int)strlen(mergeMarker[3]) );
          321  +  assert( sizeof(mergeMarker)/sizeof(mergeMarker[0])==4 );
          322  +  for(i=0; i<n; ){
          323  +    for(j=0; j<4; j++){
          324  +      if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1;
          325  +    }
          326  +    while( i<n && z[i]!='\n' ){ i++; }
          327  +    while( i<n && z[i]=='\n' ){ i++; }
          328  +  }
          329  +  return 0;
          330  +}
          331  +
          332  +/*
          333  +** Return true if the named file contains an unresolved merge marker line.
          334  +*/
          335  +int file_contains_merge_marker(const char *zFullpath){
          336  +  Blob file;
          337  +  int rc;
          338  +  blob_read_from_file(&file, zFullpath);
          339  +  rc = contains_merge_marker(&file);
          340  +  blob_reset(&file);
          341  +  return rc;
          342  +}
   297    343   
   298    344   /*
   299    345   ** COMMAND:  test-3-way-merge
          346  +**
          347  +** Usage: %fossil test-3-way-merge PIVOT V1 V2 MERGED
   300    348   **
   301    349   ** Combine change in going from PIVOT->VERSION1 with the change going
   302    350   ** from PIVOT->VERSION2 and write the combined changes into MERGED.
   303    351   */
   304    352   void delta_3waymerge_cmd(void){
   305    353     Blob pivot, v1, v2, merged;
   306    354     if( g.argc!=6 ){
   307         -    fprintf(stderr,"Usage: %s %s PIVOT V1 V2 MERGED\n", g.argv[0], g.argv[1]);
   308         -    exit(1);
          355  +    usage("PIVOT V1 V2 MERGED");
   309    356     }
   310    357     if( blob_read_from_file(&pivot, g.argv[2])<0 ){
   311         -    fprintf(stderr,"cannot read %s\n", g.argv[2]);
   312         -    exit(1);
          358  +    fossil_fatal("cannot read %s\n", g.argv[2]);
   313    359     }
   314    360     if( blob_read_from_file(&v1, g.argv[3])<0 ){
   315         -    fprintf(stderr,"cannot read %s\n", g.argv[3]);
   316         -    exit(1);
          361  +    fossil_fatal("cannot read %s\n", g.argv[3]);
   317    362     }
   318    363     if( blob_read_from_file(&v2, g.argv[4])<0 ){
   319         -    fprintf(stderr,"cannot read %s\n", g.argv[4]);
   320         -    exit(1);
          364  +    fossil_fatal("cannot read %s\n", g.argv[4]);
   321    365     }
   322    366     blob_merge(&pivot, &v1, &v2, &merged);
   323    367     if( blob_write_to_file(&merged, g.argv[5])<blob_size(&merged) ){
   324         -    fprintf(stderr,"cannot write %s\n", g.argv[4]);
   325         -    exit(1);
          368  +    fossil_fatal("cannot write %s\n", g.argv[4]);
   326    369     }
   327    370     blob_reset(&pivot);
   328    371     blob_reset(&v1);
   329    372     blob_reset(&v2);
   330    373     blob_reset(&merged);
   331    374   }
          375  +
          376  +/*
          377  +** aSubst is an array of string pairs.  The first element of each pair is
          378  +** a string that begins with %.  The second element is a replacement for that
          379  +** string.
          380  +**
          381  +** This routine makes a copy of zInput into memory obtained from malloc and
          382  +** performance all applicable substitutions on that string.
          383  +*/
          384  +char *string_subst(const char *zInput, int nSubst, const char **azSubst){
          385  +  Blob x;
          386  +  int i, j;
          387  +  blob_zero(&x);
          388  +  while( zInput[0] ){
          389  +    for(i=0; zInput[i] && zInput[i]!='%'; i++){}
          390  +    if( i>0 ){
          391  +      blob_append(&x, zInput, i);
          392  +      zInput += i;
          393  +    }
          394  +    if( zInput[0]==0 ) break;
          395  +    for(j=0; j<nSubst; j+=2){
          396  +      int n = strlen(azSubst[j]);
          397  +      if( strncmp(zInput, azSubst[j], n)==0 ){
          398  +        blob_append(&x, azSubst[j+1], -1);
          399  +        zInput += n;
          400  +        break;
          401  +      }
          402  +    }
          403  +    if( j>=nSubst ){
          404  +      blob_append(&x, "%", 1);
          405  +      zInput++;
          406  +    }
          407  +  }
          408  +  return blob_str(&x);
          409  +}
          410  +
          411  +#if INTERFACE
          412  +/*
          413  +** Flags to the 3-way merger
          414  +*/
          415  +#define MERGE_DRYRUN  0x0001
          416  +#endif
          417  +
          418  +
          419  +/*
          420  +** This routine is a wrapper around blob_merge() with the following
          421  +** enhancements:
          422  +**
          423  +**    (1) If the merge-command is defined, then use the external merging
          424  +**        program specified instead of the built-in blob-merge to do the
          425  +**        merging.  Panic if the external merger fails.
          426  +**        ** Not currently implemented **
          427  +**
          428  +**    (2) If gmerge-command is defined and there are merge conflicts in
          429  +**        blob_merge() then invoke the external graphical merger to resolve
          430  +**        the conflicts.
          431  +**
          432  +**    (3) If a merge conflict occurs and gmerge-command is not defined,
          433  +**        then write the pivot, original, and merge-in files to the
          434  +**        filesystem.
          435  +*/
          436  +int merge_3way(
          437  +  Blob *pPivot,       /* Common ancestor (older) */
          438  +  const char *zV1,    /* Name of file for version merging into (mine) */
          439  +  Blob *pV2,          /* Version merging from (yours) */
          440  +  Blob *pOut,         /* Output written here */
          441  +  unsigned mergeFlags /* Flags that control operation */
          442  +){
          443  +  Blob v1;            /* Content of zV1 */
          444  +  int rc;             /* Return code of subroutines and this routine */
          445  +
          446  +  blob_read_from_file(&v1, zV1);
          447  +  rc = blob_merge(pPivot, &v1, pV2, pOut);
          448  +  if( rc!=0 && (mergeFlags & MERGE_DRYRUN)==0 ){
          449  +    char *zPivot;       /* Name of the pivot file */
          450  +    char *zOrig;        /* Name of the original content file */
          451  +    char *zOther;       /* Name of the merge file */
          452  +
          453  +    zPivot = file_newname(zV1, "baseline", 1);
          454  +    blob_write_to_file(pPivot, zPivot);
          455  +    zOrig = file_newname(zV1, "original", 1);
          456  +    blob_write_to_file(&v1, zOrig);
          457  +    zOther = file_newname(zV1, "merge", 1);
          458  +    blob_write_to_file(pV2, zOther);
          459  +    if( rc>0 ){
          460  +      const char *zGMerge;   /* Name of the gmerge command */
          461  +
          462  +      zGMerge = db_get("gmerge-command", 0);
          463  +      if( zGMerge && zGMerge[0] ){
          464  +        char *zOut;     /* Temporary output file */
          465  +        char *zCmd;     /* Command to invoke */
          466  +        const char *azSubst[8];  /* Strings to be substituted */
          467  +
          468  +        zOut = file_newname(zV1, "output", 1);
          469  +        azSubst[0] = "%baseline";  azSubst[1] = zPivot;
          470  +        azSubst[2] = "%original";  azSubst[3] = zOrig;
          471  +        azSubst[4] = "%merge";     azSubst[5] = zOther;
          472  +        azSubst[6] = "%output";    azSubst[7] = zOut;
          473  +        zCmd = string_subst(zGMerge, 8, azSubst);
          474  +        printf("%s\n", zCmd); fflush(stdout);
          475  +        fossil_system(zCmd);
          476  +        if( file_wd_size(zOut)>=0 ){
          477  +          blob_read_from_file(pOut, zOut);
          478  +          file_delete(zPivot);
          479  +          file_delete(zOrig);
          480  +          file_delete(zOther);
          481  +          file_delete(zOut);
          482  +        }
          483  +        fossil_free(zCmd);
          484  +        fossil_free(zOut);
          485  +      }
          486  +    }
          487  +    fossil_free(zPivot);
          488  +    fossil_free(zOrig);
          489  +    fossil_free(zOther);
          490  +  }
          491  +  blob_reset(&v1);
          492  +  return rc;
          493  +}

Changes to src/mkindex.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** Build a static hash table that maps URLs into functions to generate
    19         -** web pages.
           18  +** This program scans Fossil source code files looking for special
           19  +** comments that indicate a command-line command or a webpage.  This
           20  +** routine collects information about these entry points and then
           21  +** generates (on standard output) C code used by Fossil to dispatch
           22  +** to those entry points.
    20     23   **
    21     24   ** The source code is scanned for comment lines of the form:
    22     25   **
    23     26   **       WEBPAGE:  /abc/xyz
    24     27   **
    25     28   ** This comment should be followed by a function definition of the
    26     29   ** form:
................................................................................
    31     34   ** webpage name into pointers to the function.
    32     35   **
    33     36   ** We also scan for comments lines of this form:
    34     37   **
    35     38   **       COMMAND:  cmdname
    36     39   **
    37     40   ** These entries build a constant table used to map command names into
    38         -** functions.
           41  +** functions.  If cmdname ends with "*" then the command is a second-tier
           42  +** command that is not displayed by the "fossil help" command.  The
           43  +** final "*" is not considered to be part of the command name.
           44  +**
           45  +** Comment text following COMMAND: through the end of the comment is
           46  +** understood to be help text for the command specified.  This help
           47  +** text is accumulated and a table containing the text for each command
           48  +** is generated.  That table is used implement the "fossil help" command
           49  +** and the "/help" HTTP method.
           50  +**
           51  +** Multiple occurrences of WEBPAGE: or COMMAND: (but not both) can appear
           52  +** before each function name.  In this way, webpages and commands can
           53  +** have aliases.
    39     54   */
    40     55   #include <stdio.h>
    41     56   #include <stdlib.h>
    42     57   #include <ctype.h>
    43     58   #include <assert.h>
    44     59   #include <string.h>
    45     60   
    46     61   /*
    47     62   ** Each entry looks like this:
    48     63   */
    49     64   typedef struct Entry {
    50     65     int eType;
           66  +  char *zIf;
    51     67     char *zFunc;
    52     68     char *zPath;
    53     69     char *zHelp;
    54     70   } Entry;
    55     71   
    56     72   /*
    57     73   ** Maximum number of entries
    58     74   */
    59     75   #define N_ENTRY 500
    60     76   
    61     77   /*
    62     78   ** Maximum size of a help message
    63     79   */
    64         -#define MX_HELP 10000
           80  +#define MX_HELP 25000
    65     81   
    66     82   /*
    67     83   ** Table of entries
    68     84   */
    69     85   Entry aEntry[N_ENTRY];
    70     86   
    71     87   /*
    72     88   ** Current help message accumulator
    73     89   */
    74     90   char zHelp[MX_HELP];
    75     91   int nHelp;
    76     92   
           93  +/*
           94  +** Most recently encountered #if
           95  +*/
           96  +char zIf[200];
           97  +
    77     98   /*
    78     99   ** How many entries are used
    79    100   */
    80    101   int nUsed;
    81    102   int nFixed;
    82    103   
    83    104   /*
................................................................................
   118    139     if( zLine[i]=='/' ) i++;
   119    140     for(j=0; zLine[i+j] && !isspace(zLine[i+j]); j++){}
   120    141     aEntry[nUsed].eType = eType;
   121    142     aEntry[nUsed].zPath = string_dup(&zLine[i], j);
   122    143     aEntry[nUsed].zFunc = 0;
   123    144     nUsed++;
   124    145   }
          146  +
          147  +/*
          148  +** Check to see if the current line is an #if and if it is, add it to
          149  +** the zIf[] string.  If the current line is an #endif or #else or #elif
          150  +** then cancel the current zIf[] string.
          151  +*/
          152  +void scan_for_if(const char *zLine){
          153  +  int i;
          154  +  int len;
          155  +  if( zLine[0]!='#' ) return;
          156  +  for(i=1; isspace(zLine[i]); i++){}
          157  +  if( zLine[i]==0 ) return;
          158  +  len = strlen(&zLine[i]);
          159  +  if( memcmp(&zLine[i],"if",2)==0 ){
          160  +    zIf[0] = '#';
          161  +    memcpy(&zIf[1], &zLine[i], len+1);
          162  +  }else if( zLine[i]=='e' ){
          163  +    zIf[0] = 0;
          164  +  }
          165  +}
   125    166   
   126    167   /*
   127    168   ** Scan a line for a function that implements a web page or command.
   128    169   */
   129    170   void scan_for_func(char *zLine){
   130    171     int i,j,k;
   131    172     char *z;
   132    173     if( nUsed<=nFixed ) return;
   133         -  if( strncmp(zLine, "**", 2)==0 && isspace(zLine[2])
   134         -       && strlen(zLine)<sizeof(zHelp)-nHelp-1 && nUsed>nFixed ){
          174  +  if( strncmp(zLine, "**", 2)==0
          175  +   && isspace(zLine[2])
          176  +   && strlen(zLine)<sizeof(zHelp)-nHelp-1
          177  +   && nUsed>nFixed
          178  +   && memcmp(zLine,"** COMMAND:",11)!=0
          179  +  ){
   135    180       if( zLine[2]=='\n' ){
   136    181         zHelp[nHelp++] = '\n';
   137    182       }else{
   138    183         if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
   139    184         strcpy(&zHelp[nHelp], &zLine[3]);
   140    185         nHelp += strlen(&zHelp[nHelp]);
   141    186       }
................................................................................
   158    203     for(k=0; k<nHelp && isspace(zHelp[k]); k++){}
   159    204     if( k<nHelp ){
   160    205       z = string_dup(&zHelp[k], nHelp-k);
   161    206     }else{
   162    207       z = 0;
   163    208     }
   164    209     for(k=nFixed; k<nUsed; k++){
          210  +    aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
   165    211       aEntry[k].zFunc = string_dup(&zLine[i], j);
   166    212       aEntry[k].zHelp = z;
   167    213     }
   168    214     i+=j;
   169    215     while( isspace(zLine[i]) ){ i++; }
   170    216     if( zLine[i]!='(' ) goto page_skip;
   171    217     nFixed = nUsed;
................................................................................
   198    244   */
   199    245   void build_table(void){
   200    246     int i;
   201    247     int nType0;
   202    248   
   203    249     qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
   204    250     for(i=0; i<nFixed; i++){
          251  +    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
   205    252       printf("extern void %s(void);\n", aEntry[i].zFunc);
          253  +    if( aEntry[i].zIf ) printf("#endif\n");
   206    254     }
   207    255     printf(
   208    256       "typedef struct NameMap NameMap;\n"
   209    257       "struct NameMap {\n"
   210    258       "  const char *zName;\n"
   211    259       "  void (*xFunc)(void);\n"
          260  +    "  char cmdFlags;\n"
   212    261       "};\n"
          262  +    "#define CMDFLAG_1ST_TIER  0x01\n"
          263  +    "#define CMDFLAG_2ND_TIER  0x02\n"
          264  +    "#define CMDFLAG_TEST      0x04\n"
   213    265       "static const NameMap aWebpage[] = {\n"
   214    266     );
   215    267     for(i=0; i<nFixed && aEntry[i].eType==0; i++){
   216         -    printf("  { \"%s\",%*s %s },\n",
   217         -      aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
   218         -      aEntry[i].zFunc
          268  +    const char *z = aEntry[i].zPath;
          269  +    int n = strlen(z);
          270  +    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
          271  +    printf("  { \"%s\",%*s %s,%*s 1 },\n",
          272  +      z,
          273  +      25-n, "",
          274  +      aEntry[i].zFunc,
          275  +      (int)(35-strlen(aEntry[i].zFunc)), ""
   219    276       );
          277  +    if( aEntry[i].zIf ) printf("#endif\n");
   220    278     }
   221    279     printf("};\n");
   222    280     nType0 = i;
   223    281     printf(
   224    282       "static const NameMap aCommand[] = {\n"
   225    283     );
   226    284     for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){
   227         -    printf("  { \"%s\",%*s %s },\n",
   228         -      aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
   229         -      aEntry[i].zFunc
          285  +    const char *z = aEntry[i].zPath;
          286  +    int n = strlen(z);
          287  +    int cmdFlags = 0x01;
          288  +    if( z[n-1]=='*' ){
          289  +      n--;
          290  +      cmdFlags = 0x02;
          291  +    }else if( memcmp(z, "test-", 5)==0 ){
          292  +      cmdFlags = 0x04;
          293  +    }
          294  +    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
          295  +    printf("  { \"%.*s\",%*s %s,%*s %d },\n",
          296  +      n, z,
          297  +      25-n, "",
          298  +      aEntry[i].zFunc,
          299  +      (int)(35-strlen(aEntry[i].zFunc)), "",
          300  +      cmdFlags
   230    301       );
          302  +    if( aEntry[i].zIf ) printf("#endif\n");
   231    303     }
   232    304     printf("};\n");
   233    305     for(i=nType0; i<nFixed; i++){
   234    306       char *z = aEntry[i].zHelp;
   235    307       if( z && z[0] ){
          308  +      if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
   236    309         printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
   237    310         printf("  \"");
   238    311         while( *z ){
   239    312           if( *z=='\n' ){
   240    313             printf("\\n\"\n  \"");
   241    314           }else if( *z=='"' ){
   242    315             printf("\\\"");
   243    316           }else{
   244    317             putchar(*z);
   245    318           }
   246    319           z++;
   247    320         }
   248    321         printf("\";\n");
          322  +      if( aEntry[i].zIf ) printf("#endif\n");
   249    323         aEntry[i].zHelp[0] = 0;
   250    324       }
   251    325     }
   252    326     printf(
   253    327       "static const char * const aCmdHelp[] = {\n"
   254    328     );
   255    329     for(i=nType0; i<nFixed; i++){
          330  +    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
   256    331       if( aEntry[i].zHelp==0 ){
   257    332         printf("  0,\n");
   258    333       }else{
   259    334         printf("  zHelp_%s,\n", aEntry[i].zFunc);
   260    335       }
          336  +    if( aEntry[i].zIf ) printf("#endif\n");
   261    337     }
   262    338     printf("};\n");
   263    339   }
   264    340   
   265    341   /*
   266    342   ** Process a single file of input
   267    343   */
................................................................................
   271    347     if( in==0 ){
   272    348       fprintf(stderr,"%s: cannot open\n", zFile);
   273    349       return;
   274    350     }
   275    351     nLine = 0;
   276    352     while( fgets(zLine, sizeof(zLine), in) ){
   277    353       nLine++;
          354  +    scan_for_if(zLine);
   278    355       scan_for_label("WEBPAGE:",zLine,0);
   279    356       scan_for_label("COMMAND:",zLine,1);
   280    357       scan_for_func(zLine);
   281    358     }
   282    359     fclose(in);
   283    360     nUsed = nFixed; 
   284    361   }

Added src/mkversion.c.

            1  +/*
            2  +** This C program generates the "VERSION.h" header file from information
            3  +** extracted out of the "manifest", "manifest.uuid", and "VERSION" files.
            4  +** Call this program with three arguments:
            5  +**
            6  +**     ./a.out manifest.uuid manifest VERSION
            7  +**
            8  +** Note that the manifest.uuid and manifest files are generated by Fossil.
            9  +*/
           10  +#include <stdio.h>
           11  +#include <string.h>
           12  +
           13  +int main(int argc, char *argv[]){
           14  +    FILE *m,*u,*v;
           15  +    char *z;
           16  +    int i, x, d;
           17  +    char b[1000];
           18  +    char vx[1000];
           19  +    memset(b,0,sizeof(b));
           20  +    memset(vx,0,sizeof(vx));
           21  +    u = fopen(argv[1],"r");
           22  +    fgets(b, sizeof(b)-1,u);
           23  +    fclose(u);
           24  +    for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
           25  +    *z = 0;
           26  +    printf("#define MANIFEST_UUID \"%s\"\n",b);
           27  +    printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
           28  +    m = fopen(argv[2],"r");
           29  +    while(b ==  fgets(b, sizeof(b)-1,m)){
           30  +        if(0 == strncmp("D ",b,2)){
           31  +            printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
           32  +            printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
           33  +        }
           34  +    }
           35  +    fclose(m);
           36  +    v = fopen(argv[3],"r");
           37  +    fgets(b, sizeof(b)-1,v);
           38  +    fclose(v);
           39  +    for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
           40  +    *z = 0;
           41  +    printf("#define RELEASE_VERSION \"%s\"\n", b);   
           42  +    x=0;
           43  +    i=0;
           44  +    z=b;
           45  +    while(1){
           46  +      if( z[0]>='0' && z[0]<='9' ){
           47  +        x = x*10 + z[0] - '0';
           48  +      }else{
           49  +        sprintf(&vx[i],"%02d",x);
           50  +        i += 2;
           51  +        x = 0;
           52  +        if( z[0]==0 ) break;
           53  +      }
           54  +      z++;
           55  +    }
           56  +    for(z=vx; z[0]=='0'; z++){}
           57  +    printf("#define RELEASE_VERSION_NUMBER %s\n", z);
           58  +    memset(vx,0,sizeof(vx));
           59  +    strcpy(vx,b);
           60  +    d = 0;
           61  +    for(z=vx; z[0]; z++){
           62  +      if( z[0]!='.' ) continue;
           63  +      if ( d<3 ){
           64  +        z[0] = ',';
           65  +        d++;
           66  +      }else{
           67  +        z[0] = '\0';
           68  +        break;
           69  +      }
           70  +    }
           71  +    printf("#define RELEASE_RESOURCE_VERSION %s", vx);
           72  +    while( d<3 ){ printf(",0"); d++; }
           73  +    printf("\n");
           74  +    return 0;
           75  +}

Added src/moderate.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to deal with moderator actions for
           19  +** Wiki and Tickets.
           20  +*/
           21  +#include "config.h"
           22  +#include "moderate.h"
           23  +#include <assert.h>
           24  +
           25  +/*
           26  +** Create a table to represent pending moderation requests, if the
           27  +** table does not already exist.
           28  +*/
           29  +void moderation_table_create(void){
           30  +  db_multi_exec(
           31  +     "CREATE TABLE IF NOT EXISTS modreq(\n"
           32  +     "  objid INTEGER PRIMARY KEY,\n"        /* Record pending approval */
           33  +     "  attachRid INT,\n"                    /* Object attached */
           34  +     "  tktid TEXT\n"                        /* Associated ticket id */
           35  +     ");\n"
           36  +  );
           37  +}
           38  +
           39  +/*
           40  +** Return TRUE if the modreq table exists
           41  +*/
           42  +int moderation_table_exists(void){
           43  +  static int modreqExists = -1;
           44  +  if( modreqExists<0 ){
           45  +    modreqExists = db_exists("SELECT 1 FROM %s.sqlite_master"
           46  +                             " WHERE name='modreq'", db_name("repository"));
           47  +  }
           48  +  return modreqExists;
           49  +}
           50  +
           51  +/*
           52  +** Return TRUE if the object specified is being held for moderation.
           53  +*/
           54  +int moderation_pending(int rid){
           55  +  static Stmt q;
           56  +  int rc;
           57  +  if( rid==0 || !moderation_table_exists() ) return 0;
           58  +  db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid");
           59  +  db_bind_int(&q, ":objid", rid);
           60  +  rc = db_step(&q)==SQLITE_ROW;
           61  +  db_reset(&q);
           62  +  return rc;
           63  +}
           64  +
           65  +/*
           66  +** Check to see if the object identified by RID is used for anything.
           67  +*/
           68  +static int object_used(int rid){
           69  +  static const char *aTabField[] = {
           70  +     "modreq",     "attachRid",
           71  +     "mlink",      "mid",
           72  +     "mlink",      "fid",
           73  +     "tagxref",    "srcid",
           74  +     "tagxref",    "rid",
           75  +  };
           76  +  int i;
           77  +  for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){
           78  +    if( db_exists("SELECT 1 FROM %s WHERE %s=%d",
           79  +                  aTabField[i], aTabField[i+1], rid) ) return 1;
           80  +  }
           81  +  return 0;
           82  +}
           83  +
           84  +/*
           85  +** Delete a moderation item given by objid
           86  +*/
           87  +void moderation_disapprove(int objid){
           88  +  Stmt q;
           89  +  char *zTktid;
           90  +  int attachRid = 0;
           91  +  int rid;
           92  +  if( !moderation_pending(objid) ) return;
           93  +  db_begin_transaction();
           94  +  rid = objid;
           95  +  while( rid && content_is_private(rid) ){
           96  +    db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
           97  +    while( db_step(&q)==SQLITE_ROW ){
           98  +      int ridUser = db_column_int(&q, 0);
           99  +      content_undelta(ridUser);
          100  +    }
          101  +    db_finalize(&q);
          102  +    db_multi_exec(
          103  +      "DELETE FROM blob WHERE rid=%d;"
          104  +      "DELETE FROM delta WHERE rid=%d;"
          105  +      "DELETE FROM event WHERE objid=%d;"
          106  +      "DELETE FROM tagxref WHERE rid=%d;"
          107  +      "DELETE FROM private WHERE rid=%d;"
          108  +      "DELETE FROM attachment WHERE attachid=%d;",
          109  +      rid, rid, rid, rid, rid, rid
          110  +    );
          111  +    zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid);
          112  +    if( zTktid && zTktid[0] ){
          113  +      ticket_rebuild_entry(zTktid);
          114  +      fossil_free(zTktid);
          115  +    }
          116  +    attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid);
          117  +    if( rid==objid ){
          118  +      db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
          119  +    }
          120  +    if( attachRid && object_used(attachRid) ) attachRid = 0;
          121  +    rid = attachRid;
          122  +  }
          123  +  db_end_transaction(0);
          124  +}
          125  +
          126  +/*
          127  +** Approve an object held for moderation.
          128  +*/
          129  +void moderation_approve(int rid){
          130  +  if( !moderation_pending(rid) ) return;
          131  +  db_begin_transaction();
          132  +  db_multi_exec(
          133  +    "DELETE FROM private WHERE rid=%d;"
          134  +    "INSERT OR IGNORE INTO unclustered VALUES(%d);"
          135  +    "INSERT OR IGNORE INTO unsent VALUES(%d);",
          136  +    rid, rid, rid
          137  +  );
          138  +  db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
          139  +  db_end_transaction(0);
          140  +}
          141  +
          142  +/*
          143  +** WEBPAGE: modreq
          144  +**
          145  +** Show all pending moderation request
          146  +*/
          147  +void modreq_page(void){
          148  +  Blob sql;
          149  +  Stmt q;
          150  +
          151  +  login_check_credentials();
          152  +  if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(); return; }
          153  +  style_header("Pending Moderation Requests");
          154  +  @ <h2>All Pending Moderation Requests</h2>
          155  +  if( moderation_table_exists() ){
          156  +    blob_init(&sql, timeline_query_for_www(), -1);
          157  +    blob_appendf(&sql,
          158  +        " AND event.objid IN (SELECT objid FROM modreq)"
          159  +        " ORDER BY event.mtime DESC"
          160  +    );
          161  +    db_prepare(&q, blob_str(&sql));
          162  +    www_print_timeline(&q, 0, 0, 0, 0);
          163  +    db_finalize(&q);
          164  +  }
          165  +  style_footer();
          166  +}

Changes to src/name.c.

    20     20   **
    21     21   ** A user-supplied object name is any unique prefix of a valid UUID but
    22     22   ** not necessarily in canonical form.  
    23     23   */
    24     24   #include "config.h"
    25     25   #include "name.h"
    26     26   #include <assert.h>
           27  +
           28  +/*
           29  +** Return TRUE if the string begins with something that looks roughly
           30  +** like an ISO date/time string.  The SQLite date/time functions will
           31  +** have the final say-so about whether or not the date/time string is
           32  +** well-formed.
           33  +*/
           34  +int fossil_isdate(const char *z){
           35  +  if( !fossil_isdigit(z[0]) ) return 0;
           36  +  if( !fossil_isdigit(z[1]) ) return 0;
           37  +  if( !fossil_isdigit(z[2]) ) return 0;
           38  +  if( !fossil_isdigit(z[3]) ) return 0;
           39  +  if( z[4]!='-') return 0;
           40  +  if( !fossil_isdigit(z[5]) ) return 0;
           41  +  if( !fossil_isdigit(z[6]) ) return 0;
           42  +  if( z[7]!='-') return 0;
           43  +  if( !fossil_isdigit(z[8]) ) return 0;
           44  +  if( !fossil_isdigit(z[9]) ) return 0;
           45  +  return 1;
           46  +}
           47  +
           48  +/*
           49  +** Convert a symbolic name into a RID.  Acceptable forms:
           50  +**
           51  +**   *  SHA1 hash
           52  +**   *  SHA1 hash prefix of at least 4 characters
           53  +**   *  Symbolic Name
           54  +**   *  "tag:" + symbolic name
           55  +**   *  Date or date-time 
           56  +**   *  "date:" + Date or date-time
           57  +**   *  symbolic-name ":" date-time
           58  +**   *  "tip"
           59  +**
           60  +** The following additional forms are available in local checkouts:
           61  +**
           62  +**   *  "current"
           63  +**   *  "prev" or "previous"
           64  +**   *  "next"
           65  +**
           66  +** Return the RID of the matching artifact.  Or return 0 if the name does not
           67  +** match any known object.  Or return -1 if the name is ambiguous.
           68  +**
           69  +** The zType parameter specifies the type of artifact: ci, t, w, e, g. 
           70  +** If zType is NULL or "" or "*" then any type of artifact will serve.
           71  +** zType is "ci" in most use cases since we are usually searching for
           72  +** a check-in.
           73  +*/
           74  +int symbolic_name_to_rid(const char *zTag, const char *zType){
           75  +  int vid;
           76  +  int rid = 0;
           77  +  int nTag;
           78  +  int i;
           79  +
           80  +  if( zType==0 || zType[0]==0 ) zType = "*";
           81  +  if( zTag==0 || zTag[0]==0 ) return 0;
           82  +
           83  +  /* special keyword: "tip" */
           84  +  if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
           85  +    rid = db_int(0,
           86  +      "SELECT objid"
           87  +      "  FROM event"
           88  +      " WHERE type='ci'"
           89  +      " ORDER BY event.mtime DESC"
           90  +    );
           91  +    if( rid ) return rid;
           92  +  }
           93  +
           94  +  /* special keywords: "prev", "previous", "current", and "next" */
           95  +  if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
           96  +    if( fossil_strcmp(zTag, "current")==0 ){
           97  +      rid = vid;
           98  +    }else if( fossil_strcmp(zTag, "prev")==0 
           99  +              || fossil_strcmp(zTag, "previous")==0 ){
          100  +      rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid);
          101  +    }else if( fossil_strcmp(zTag, "next")==0 ){
          102  +      rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
          103  +                      "  ORDER BY isprim DESC, mtime DESC", vid);
          104  +    }
          105  +    if( rid ) return rid;
          106  +  }
          107  +
          108  +  /* Date and times */
          109  +  if( memcmp(zTag, "date:", 5)==0 ){
          110  +    rid = db_int(0, 
          111  +      "SELECT objid FROM event"
          112  +      " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'"
          113  +      " ORDER BY mtime DESC LIMIT 1",
          114  +      &zTag[5], zType);
          115  +    return rid;
          116  +  }
          117  +  if( fossil_isdate(zTag) ){
          118  +    rid = db_int(0, 
          119  +      "SELECT objid FROM event"
          120  +      " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'"
          121  +      " ORDER BY mtime DESC LIMIT 1",
          122  +      zTag, zType);
          123  +    if( rid) return rid;
          124  +  }
          125  +
          126  +  /* Deprecated date & time formats:   "local:" + date-time and
          127  +  ** "utc:" + date-time */
          128  +  if( memcmp(zTag, "local:", 6)==0 ){
          129  +    rid = db_int(0, 
          130  +      "SELECT objid FROM event"
          131  +      " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
          132  +      " ORDER BY mtime DESC LIMIT 1",
          133  +      &zTag[6], zType);
          134  +    return rid;
          135  +  }
          136  +  if( memcmp(zTag, "utc:", 4)==0 ){
          137  +    rid = db_int(0, 
          138  +      "SELECT objid FROM event"
          139  +      " WHERE mtime<=julianday('%qz') AND type GLOB '%q'"
          140  +      " ORDER BY mtime DESC LIMIT 1",
          141  +      &zTag[4], zType);
          142  +    return rid;
          143  +  }
          144  +
          145  +  /* "tag:" + symbolic-name */
          146  +  if( memcmp(zTag, "tag:", 4)==0 ){
          147  +    rid = db_int(0,
          148  +       "SELECT event.objid, max(event.mtime)"
          149  +       "  FROM tag, tagxref, event"
          150  +       " WHERE tag.tagname='sym-%q' "
          151  +       "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
          152  +       "   AND event.objid=tagxref.rid "
          153  +       "   AND event.type GLOB '%q'",
          154  +       &zTag[4], zType
          155  +    );
          156  +    return rid;
          157  +  }
          158  +  
          159  +  /* root:TAG -> The origin of the branch */
          160  +  if( memcmp(zTag, "root:", 5)==0 ){
          161  +    Stmt q;
          162  +    int rc;
          163  +    char *zBr;
          164  +    rid = symbolic_name_to_rid(zTag+5, zType);
          165  +    zBr = db_text("trunk","SELECT value FROM tagxref"
          166  +                          " WHERE rid=%d AND tagid=%d"
          167  +                          " AND tagtype>0",
          168  +                          rid, TAG_BRANCH);
          169  +    db_prepare(&q,
          170  +      "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
          171  +                         " WHERE tagid=%d AND tagtype>0"
          172  +                         "   AND value=%Q AND rid=plink.pid)"
          173  +      "  FROM plink"
          174  +      " WHERE cid=:cid AND isprim",
          175  +      TAG_BRANCH, zBr
          176  +    );
          177  +    fossil_free(zBr);
          178  +    do{
          179  +      db_reset(&q);
          180  +      db_bind_int(&q, ":cid", rid);
          181  +      rc = db_step(&q);
          182  +      if( rc!=SQLITE_ROW ) break;
          183  +      rid = db_column_int(&q, 0);
          184  +    }while( db_column_int(&q, 1)==1 && rid>0 );
          185  +    db_finalize(&q);
          186  +    return rid;
          187  +  }
          188  +
          189  +  /* symbolic-name ":" date-time */
          190  +  nTag = strlen(zTag);
          191  +  for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
          192  +  if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){
          193  +    char *zDate = mprintf("%s", &zTag[i+1]);
          194  +    char *zTagBase = mprintf("%.*s", i, zTag);
          195  +    int nDate = strlen(zDate);
          196  +    if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
          197  +      zDate[nDate-3] = 'z';
          198  +      zDate[nDate-2] = 0;
          199  +    }
          200  +    rid = db_int(0,
          201  +      "SELECT event.objid, max(event.mtime)"
          202  +      "  FROM tag, tagxref, event"
          203  +      " WHERE tag.tagname='sym-%q' "
          204  +      "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
          205  +      "   AND event.objid=tagxref.rid "
          206  +      "   AND event.mtime<=julianday(%Q)"
          207  +      "   AND event.type GLOB '%q'",
          208  +      zTagBase, zDate, zType
          209  +    );
          210  +    return rid;
          211  +  }
          212  +
          213  +  /* SHA1 hash or prefix */
          214  +  if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){
          215  +    Stmt q;
          216  +    char zUuid[UUID_SIZE+1];
          217  +    memcpy(zUuid, zTag, nTag+1);
          218  +    canonical16(zUuid, nTag);
          219  +    rid = 0;
          220  +    if( zType[0]=='*' ){
          221  +      db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid);
          222  +    }else{
          223  +      db_prepare(&q,
          224  +        "SELECT blob.rid"
          225  +        "  FROM blob, event"
          226  +        " WHERE blob.uuid GLOB '%s*'"
          227  +        "   AND event.objid=blob.rid"
          228  +        "   AND event.type GLOB '%q'",
          229  +        zUuid, zType
          230  +      );
          231  +    }
          232  +    if( db_step(&q)==SQLITE_ROW ){
          233  +      rid = db_column_int(&q, 0);
          234  +      if( db_step(&q)==SQLITE_ROW ) rid = -1;
          235  +    }
          236  +    db_finalize(&q);
          237  +    if( rid ) return rid;
          238  +  }
          239  +
          240  +  /* Symbolic name */
          241  +  rid = db_int(0,
          242  +    "SELECT event.objid, max(event.mtime)"
          243  +    "  FROM tag, tagxref, event"
          244  +    " WHERE tag.tagname='sym-%q' "
          245  +    "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
          246  +    "   AND event.objid=tagxref.rid "
          247  +    "   AND event.type GLOB '%q'",
          248  +    zTag, zType
          249  +  );
          250  +  if( rid>0 ) return rid;
          251  +
          252  +  /* Undocumented:  numeric tags get translated directly into the RID */
          253  +  for(i=0; fossil_isdigit(zTag[i]); i++){}
          254  +  if( zTag[i]==0 ){
          255  +    if( strcmp(zType,"*")==0 ){
          256  +      rid = atoi(zTag);
          257  +    }else{
          258  +      rid = db_int(0, 
          259  +        "SELECT event.objid"
          260  +        "  FROM event"
          261  +        " WHERE event.objid=%s"
          262  +        "   AND event.type GLOB '%q'", zTag, zType);
          263  +    }
          264  +  }
          265  +  return rid;
          266  +}
          267  +
    27    268   
    28    269   /*
    29    270   ** This routine takes a user-entered UUID which might be in mixed
    30    271   ** case and might only be a prefix of the full UUID and converts it
    31    272   ** into the full-length UUID in canonical form.
    32    273   **
    33    274   ** If the input is not a UUID or a UUID prefix, then try to resolve
................................................................................
    38    279   ** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date.
    39    280   ** If the input is of the form "date:*" or "localtime:*" or "utc:*" then
    40    281   ** always resolve the name as a date.
    41    282   **
    42    283   ** Return 0 on success.  Return 1 if the name cannot be resolved.
    43    284   ** Return 2 name is ambiguous.
    44    285   */
    45         -int name_to_uuid(Blob *pName, int iErrPriority){
    46         -  int rc;
    47         -  int sz;
    48         -  sz = blob_size(pName);
    49         -  if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){
    50         -    char *zUuid;
    51         -    const char *zName = blob_str(pName);
    52         -    if( memcmp(zName, "tag:", 4)==0 ){
    53         -      zName += 4;
    54         -      zUuid = tag_to_uuid(zName);
    55         -    }else{
    56         -      zUuid = tag_to_uuid(zName);
    57         -      if( zUuid==0 ){
    58         -        zUuid = date_to_uuid(zName);
    59         -      }
    60         -    }
    61         -    if( zUuid ){
    62         -      blob_reset(pName);
    63         -      blob_append(pName, zUuid, -1);
    64         -      free(zUuid);
    65         -      return 0;
    66         -    }
    67         -    fossil_error(iErrPriority, "not a valid object name: %s", zName);
          286  +int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){
          287  +  char *zName = blob_str(pName);
          288  +  int rid = symbolic_name_to_rid(zName, zType);
          289  +  if( rid<0 ){
          290  +    fossil_error(iErrPriority, "ambiguous name: %s", zName);
          291  +    return 2;
          292  +  }else if( rid==0 ){
          293  +    fossil_error(iErrPriority, "not found: %s", zName);
    68    294       return 1;
    69         -  }
    70         -  blob_materialize(pName);
    71         -  canonical16(blob_buffer(pName), sz);
    72         -  if( sz==UUID_SIZE ){
    73         -    rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName);
    74         -    if( rc ){
    75         -      fossil_error(iErrPriority, "no such artifact: %b", pName);
    76         -      blob_reset(pName);
    77         -    }
    78         -  }else if( sz<UUID_SIZE && sz>=4 ){
    79         -    Stmt q;
    80         -    db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName);
    81         -    if( db_step(&q)!=SQLITE_ROW ){
    82         -      char *zUuid;
    83         -      db_finalize(&q);
    84         -      zUuid = tag_to_uuid(blob_str(pName));
    85         -      if( zUuid ){
    86         -        blob_reset(pName);
    87         -        blob_append(pName, zUuid, -1);
    88         -        free(zUuid);
    89         -        return 0;
    90         -      }
    91         -      fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName);
    92         -      return 1;
    93         -    }
          295  +  }else{
    94    296       blob_reset(pName);
    95         -    blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0));
    96         -    if( db_step(&q)==SQLITE_ROW ){
    97         -      fossil_error(iErrPriority, 
    98         -         "multiple artifacts match"
    99         -      );
   100         -      blob_reset(pName);
   101         -      db_finalize(&q);
   102         -      return 2;
   103         -    }
   104         -    db_finalize(&q);
   105         -    rc = 0;
   106         -  }else{
   107         -    rc = 0;
          297  +    db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
          298  +    return 0;
   108    299     }
   109         -  return rc;
   110    300   }
   111    301   
   112    302   /*
   113         -** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
          303  +** This routine is similar to name_to_uuid() except in the form it
          304  +** takes its parameters and returns its value, and in that it does not
          305  +** treat errors as fatal. zName must be a UUID, as described for
          306  +** name_to_uuid(). zType is also as described for that function. If
          307  +** zName does not resolve, 0 is returned. If it is ambiguous, a
          308  +** negative value is returned. On success the rid is returned and
          309  +** pUuid (if it is not NULL) is set to the a newly-allocated string,
          310  +** the full UUID, which must eventually be free()d by the caller.
   114    311   */
   115         -static int is_date(const char *z){
   116         -  if( !isdigit(z[0]) ) return 0;
   117         -  if( !isdigit(z[1]) ) return 0;
   118         -  if( !isdigit(z[2]) ) return 0;
   119         -  if( !isdigit(z[3]) ) return 0;
   120         -  if( z[4]!='-') return 0;
   121         -  if( !isdigit(z[5]) ) return 0;
   122         -  if( !isdigit(z[6]) ) return 0;
   123         -  if( z[7]!='-') return 0;
   124         -  if( !isdigit(z[8]) ) return 0;
   125         -  if( !isdigit(z[9]) ) return 0;
   126         -  return 1;
          312  +int name_to_uuid2(char const *zName, const char *zType, char **pUuid){
          313  +  int rid = symbolic_name_to_rid(zName, zType);
          314  +  if((rid>0) && pUuid){
          315  +    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
          316  +  }
          317  +  return rid;
   127    318   }
   128    319   
   129         -/*
   130         -** Convert a symbolic tag name into the UUID of a check-in that contains
   131         -** that tag.  If the tag appears on multiple check-ins, return the UUID
   132         -** of the most recent check-in with the tag.
   133         -**
   134         -** If the input string is of the form:
   135         -**
   136         -**      tag:date
   137         -**
   138         -** Then return the UUID of the oldest check-in with that tag that is
   139         -** not older than 'date'.
   140         -**
   141         -** An input of "tip" returns the most recent check-in.
   142         -**
   143         -** Memory to hold the returned string comes from malloc() and needs to
   144         -** be freed by the caller.
   145         -*/
   146         -char *tag_to_uuid(const char *zTag){
   147         -  char *zUuid = 
   148         -    db_text(0,
   149         -       "SELECT blob.uuid"
   150         -       "  FROM tag, tagxref, event, blob"
   151         -       " WHERE tag.tagname='sym-'||%Q "
   152         -       "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
   153         -       "   AND event.objid=tagxref.rid "
   154         -       "   AND blob.rid=event.objid "
   155         -       " ORDER BY event.mtime DESC ",
   156         -       zTag
   157         -    );
   158         -  if( zUuid==0 ){
   159         -    int nTag = strlen(zTag);
   160         -    int i;
   161         -    for(i=0; i<nTag-10; i++){
   162         -      if( zTag[i]==':' && is_date(&zTag[i+1]) ){
   163         -        char *zDate = mprintf("%s", &zTag[i+1]);
   164         -        char *zTagBase = mprintf("%.*s", i, zTag);
   165         -        int nDate = strlen(zDate);
   166         -        int useUtc = 0;
   167         -        if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
   168         -          nDate -= 3;
   169         -          zDate[nDate] = 0;
   170         -          useUtc = 1;
   171         -        }
   172         -        zUuid = db_text(0,
   173         -          "SELECT blob.uuid"
   174         -          "  FROM tag, tagxref, event, blob"
   175         -          " WHERE tag.tagname='sym-'||%Q "
   176         -          "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
   177         -          "   AND event.objid=tagxref.rid "
   178         -          "   AND blob.rid=event.objid "
   179         -          "   AND event.mtime<=julianday(%Q %s)"
   180         -          " ORDER BY event.mtime DESC ",
   181         -          zTagBase, zDate, (useUtc ? "" : ",'utc'")
   182         -        );
   183         -        break;
   184         -      }
   185         -    }
   186         -    if( zUuid==0 && strcmp(zTag, "tip")==0 ){
   187         -      zUuid = db_text(0,
   188         -        "SELECT blob.uuid"
   189         -        "  FROM event, blob"
   190         -        " WHERE event.type='ci'"
   191         -        "   AND blob.rid=event.objid"
   192         -        " ORDER BY event.mtime DESC"
   193         -      );
   194         -    }
   195         -  }
   196         -  return zUuid;
   197         -}
   198    320   
   199         -/*
   200         -** Convert a date/time string into a UUID.
   201         -**
   202         -** Input forms accepted:
   203         -**
   204         -**    date:DATE
   205         -**    local:DATE
   206         -**    utc:DATE
   207         -**
   208         -** The DATE is interpreted as localtime unless the "utc:" prefix is used
   209         -** or a "utc" string appears at the end of the DATE string.
   210         -*/
   211         -char *date_to_uuid(const char *zDate){
   212         -  int useUtc = 0;
   213         -  int n;
   214         -  char *zCopy = 0;
   215         -  char *zUuid;
   216         -
   217         -  if( memcmp(zDate, "date:", 5)==0 ){
   218         -    zDate += 5;
   219         -  }else if( memcmp(zDate, "local:", 6)==0 ){
   220         -    zDate += 6;
   221         -  }else if( memcmp(zDate, "utc:", 4)==0 ){
   222         -    zDate += 4;
   223         -    useUtc = 1;
   224         -  }
   225         -  n = strlen(zDate);
   226         -  if( n<10 || !is_date(zDate) ) return 0;
   227         -  if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
   228         -    zCopy = mprintf("%s", zDate);
   229         -    zCopy[n-3] = 0;
   230         -    zDate = zCopy;
   231         -    n -= 3;
   232         -    useUtc = 1;
   233         -  }
   234         -  zUuid = db_text(0,
   235         -    "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)"
   236         -    "  FROM event"
   237         -    " WHERE mtime<=julianday(%Q %s) AND type='ci'"
   238         -    " ORDER BY mtime DESC LIMIT 1",
   239         -    zDate, useUtc ? "" : ",'utc'"
   240         -  );
   241         -  free(zCopy);
   242         -  return zUuid;
   243         -}
   244    321   
   245    322   /*
   246    323   ** COMMAND:  test-name-to-id
   247    324   **
   248    325   ** Convert a name to a full artifact ID.
   249    326   */
   250    327   void test_name_to_id(void){
   251    328     int i;
   252    329     Blob name;
   253    330     db_must_be_within_tree();
   254    331     for(i=2; i<g.argc; i++){
   255    332       blob_init(&name, g.argv[i], -1);
   256         -    printf("%s -> ", g.argv[i]);
   257         -    if( name_to_uuid(&name, 1) ){
   258         -      printf("ERROR: %s\n", g.zErrMsg);
          333  +    fossil_print("%s -> ", g.argv[i]);
          334  +    if( name_to_uuid(&name, 1, "*") ){
          335  +      fossil_print("ERROR: %s\n", g.zErrMsg);
   259    336         fossil_error_reset();
   260    337       }else{
   261         -      printf("%s\n", blob_buffer(&name));
          338  +      fossil_print("%s\n", blob_buffer(&name));
   262    339       }
   263    340       blob_reset(&name);
   264    341     }
   265    342   }
   266    343   
   267    344   /*
   268         -** Convert a name to a rid.  If the name is a small integer value then
   269         -** just use atoi() to do the conversion.  If the name contains alphabetic
   270         -** characters or is not an existing rid, then use name_to_uuid then
   271         -** convert the uuid to a rid.
          345  +** Convert a name to a rid.  If the name can be any of the various forms
          346  +** accepted:
          347  +**
          348  +**   * SHA1 hash or prefix thereof
          349  +**   * symbolic name
          350  +**   * date
          351  +**   * label:date
          352  +**   * prev, previous
          353  +**   * next
          354  +**   * tip
   272    355   **
   273    356   ** This routine is used by command-line routines to resolve command-line inputs
   274    357   ** into a rid.
   275    358   */
   276         -int name_to_rid(const char *zName){
   277         -  int i;
          359  +int name_to_typed_rid(const char *zName, const char *zType){
   278    360     int rid;
   279         -  Blob name;
   280    361   
   281    362     if( zName==0 || zName[0]==0 ) return 0;
   282         -  blob_init(&name, zName, -1);
   283         -  if( name_to_uuid(&name, -1) ){
   284         -    blob_reset(&name);
   285         -    for(i=0; zName[i] && isdigit(zName[i]); i++){}
   286         -    if( zName[i]==0 ){
   287         -      rid = atoi(zName);
   288         -      if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
   289         -        return rid;
   290         -      }
   291         -    }
   292         -    fossil_error(1, "no such artifact: %s", zName);
          363  +  rid = symbolic_name_to_rid(zName, zType);
          364  +  if( rid<0 ){
          365  +    fossil_error(1, "ambiguous name: %s", zName);
          366  +    return 0;
          367  +  }else if( rid==0 ){
          368  +    fossil_error(1, "not found: %s", zName);
   293    369       return 0;
   294    370     }else{
   295         -    rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
   296         -    blob_reset(&name);
          371  +    return rid;
   297    372     }
   298         -  return rid;
          373  +}
          374  +int name_to_rid(const char *zName){
          375  +  return name_to_typed_rid(zName, "*");
   299    376   }
   300    377   
   301    378   /*
   302    379   ** WEBPAGE: ambiguous
   303    380   ** URL: /ambiguous?name=UUID&src=WEBPAGE
   304    381   ** 
   305         -** The UUID given by the name paramager is ambiguous.  Display a page
          382  +** The UUID given by the name parameter is ambiguous.  Display a page
   306    383   ** that shows all possible choices and let the user select between them.
   307    384   */
   308    385   void ambiguous_page(void){
   309    386     Stmt q;
   310    387     const char *zName = P("name");  
   311    388     const char *zSrc = P("src");
   312    389     char *z;
................................................................................
   320    397     @ <ol>
   321    398     z = mprintf("%s", zName);
   322    399     canonical16(z, strlen(z));
   323    400     db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z);
   324    401     while( db_step(&q)==SQLITE_ROW ){
   325    402       const char *zUuid = db_column_text(&q, 0);
   326    403       int rid = db_column_int(&q, 1);
   327         -    @ <li><p><a href="%s(g.zBaseURL)/%T(zSrc)/%S(zUuid)">
          404  +    @ <li><p><a href="%s(g.zTop)/%T(zSrc)/%S(zUuid)">
   328    405       @ %S(zUuid)</a> -
   329    406       object_description(rid, 0, 0);
   330    407       @ </p></li>
   331    408     }
   332    409     @ </ol>
   333    410     style_footer();
   334    411   }
................................................................................
   336    413   /*
   337    414   ** Convert the name in CGI parameter zParamName into a rid and return that
   338    415   ** rid.  If the CGI parameter is missing or is not a valid artifact tag,
   339    416   ** return 0.  If the CGI parameter is ambiguous, redirect to a page that
   340    417   ** shows all possibilities and do not return.
   341    418   */
   342    419   int name_to_rid_www(const char *zParamName){
   343         -  int i, rc;
   344    420     int rid;
   345    421     const char *zName = P(zParamName);
   346         -  Blob name;
   347         -
          422  +#ifdef FOSSIL_ENABLE_JSON
          423  +  if(!zName && fossil_has_json()){
          424  +    zName = json_find_option_cstr(zParamName,NULL,NULL);
          425  +  }
          426  +#endif
   348    427     if( zName==0 || zName[0]==0 ) return 0;
   349         -  blob_init(&name, zName, -1);
   350         -  rc = name_to_uuid(&name, -1);
   351         -  if( rc==1 ){
   352         -    blob_reset(&name);
   353         -    for(i=0; zName[i] && isdigit(zName[i]); i++){}
   354         -    if( zName[i]==0 ){
   355         -      rid = atoi(zName);
   356         -      if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
   357         -        return rid;
   358         -      }
   359         -    }
   360         -    return 0;
   361         -  }else if( rc==2 ){
          428  +  rid = symbolic_name_to_rid(zName, "*");
          429  +  if( rid<0 ){
   362    430       cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
   363         -    return 0;
   364         -  }else{
   365         -    rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
   366         -    blob_reset(&name);
          431  +    rid = 0;
   367    432     }
   368    433     return rid;
   369    434   }
   370    435   
          436  +/*
          437  +** COMMAND: whatis*
          438  +** Usage: %fossil whatis NAME
          439  +**
          440  +** Resolve the symbol NAME into its canonical 40-character SHA1-hash
          441  +** artifact name and provide a description of what role that artifact
          442  +** plays.
          443  +*/
          444  +void whatis_cmd(void){
          445  +  int rid;
          446  +  const char *zName;
          447  +  int fExtra;
          448  +  db_find_and_open_repository(0,0);
          449  +  fExtra = find_option("verbose","v",0)!=0;
          450  +  if( g.argc!=3 ) usage("whatis NAME");
          451  +  zName = g.argv[2];
          452  +  rid = symbolic_name_to_rid(zName, 0);
          453  +  if( rid<0 ){
          454  +    fossil_print("Ambiguous artifact name prefix: %s\n", zName);
          455  +  }else if( rid==0 ){
          456  +    fossil_print("Unknown artifact: %s\n", zName);
          457  +  }else{
          458  +    Stmt q;
          459  +    db_prepare(&q,
          460  +       "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr,"
          461  +       "       (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
          462  +       "         WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
          463  +       "           AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
          464  +       "  FROM blob, rcvfrom"
          465  +       " WHERE rid=%d"
          466  +       "   AND rcvfrom.rcvid=blob.rcvid",
          467  +       rid);
          468  +    if( db_step(&q)==SQLITE_ROW ){
          469  +      const char *zTagList = db_column_text(&q, 4);
          470  +      if( fExtra ){
          471  +        fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
          472  +        fossil_print("size:     %d bytes\n", db_column_int(&q,1));
          473  +        fossil_print("received: %s from %s\n",
          474  +           db_column_text(&q, 2),
          475  +           db_column_text(&q, 3));
          476  +      }else{
          477  +        fossil_print("artifact: %s\n", db_column_text(&q,0));
          478  +        fossil_print("size:     %d bytes\n", db_column_int(&q,1));
          479  +      }
          480  +      if( zTagList && zTagList[0] ){
          481  +        fossil_print("tags:     %s\n", zTagList);
          482  +      }
          483  +    }
          484  +    db_finalize(&q);
          485  +    db_prepare(&q,
          486  +       "SELECT type, datetime(mtime,'localtime'),"
          487  +       "       coalesce(euser,user), coalesce(ecomment,comment)"
          488  +       "  FROM event WHERE objid=%d", rid);
          489  +    if( db_step(&q)==SQLITE_ROW ){
          490  +      const char *zType;
          491  +      switch( db_column_text(&q,0)[0] ){
          492  +        case 'c':  zType = "Check-in";       break;
          493  +        case 'w':  zType = "Wiki-edit";      break;
          494  +        case 'e':  zType = "Event";          break;
          495  +        case 't':  zType = "Ticket-change";  break;
          496  +        case 'g':  zType = "Tag-change";     break;
          497  +        default:   zType = "Unknown";        break;
          498  +      }
          499  +      fossil_print("type:     %s by %s on %s\n", zType, db_column_text(&q,2),
          500  +                   db_column_text(&q, 1));
          501  +      fossil_print("comment:  ");
          502  +      comment_print(db_column_text(&q,3), 10, 78);
          503  +    }
          504  +    db_finalize(&q);
          505  +    db_prepare(&q,
          506  +      "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime'),"
          507  +      "       coalesce(euser,user), coalesce(ecomment,comment)"
          508  +      "  FROM mlink, filename, blob, event"
          509  +      " WHERE mlink.fid=%d"
          510  +      "   AND filename.fnid=mlink.fnid"
          511  +      "   AND event.objid=mlink.mid"
          512  +      "   AND blob.rid=mlink.mid"
          513  +      " ORDER BY event.mtime DESC /*sort*/",
          514  +      rid);
          515  +    while( db_step(&q)==SQLITE_ROW ){
          516  +      fossil_print("file:     %s\n", db_column_text(&q,0));
          517  +      fossil_print("          part of [%.10s] by %s on %s\n",
          518  +        db_column_text(&q, 1),
          519  +        db_column_text(&q, 3),
          520  +        db_column_text(&q, 2));
          521  +      fossil_print("          ");
          522  +      comment_print(db_column_text(&q,4), 10, 78);
          523  +    }
          524  +    db_finalize(&q);
          525  +  }
          526  +}

Added src/path.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@sqlite.org
           14  +**
           15  +*******************************************************************************
           16  +**
           17  +** This file contains code used to trace paths of through the
           18  +** directed acyclic graph (DAG) of checkins.
           19  +*/
           20  +#include "config.h"
           21  +#include "path.h"
           22  +#include <assert.h>
           23  +
           24  +#if INTERFACE
           25  +/* Nodes for the paths through the DAG.
           26  +*/
           27  +struct PathNode {
           28  +  int rid;                 /* ID for this node */
           29  +  u8 fromIsParent;         /* True if pFrom is the parent of rid */
           30  +  u8 isPrim;               /* True if primary side of common ancestor */
           31  +  u8 isHidden;             /* Abbreviate output in "fossil bisect ls" */
           32  +  PathNode *pFrom;         /* Node we came from */
           33  +  union {
           34  +    PathNode *pPeer;       /* List of nodes of the same generation */
           35  +    PathNode *pTo;         /* Next on path from beginning to end */
           36  +  } u;
           37  +  PathNode *pAll;        /* List of all nodes */
           38  +};
           39  +#endif
           40  +
           41  +/*
           42  +** Local variables for this module
           43  +*/
           44  +static struct {
           45  +  PathNode *pCurrent;   /* Current generation of nodes */
           46  +  PathNode *pAll;       /* All nodes */
           47  +  Bag seen;             /* Nodes seen before */
           48  +  int nStep;            /* Number of steps from first to last */
           49  +  PathNode *pStart;     /* Earliest node */
           50  +  PathNode *pPivot;     /* Common ancestor of pStart and pEnd */
           51  +  PathNode *pEnd;       /* Most recent */
           52  +} path;
           53  +
           54  +/*
           55  +** Return the first (last) element of the computed path.
           56  +*/
           57  +PathNode *path_first(void){ return path.pStart; }
           58  +PathNode *path_last(void){ return path.pEnd; }
           59  +
           60  +/*
           61  +** Return the number of steps in the computed path.
           62  +*/
           63  +int path_length(void){ return path.nStep; }
           64  +
           65  +/*
           66  +** Create a new node
           67  +*/
           68  +static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
           69  +  PathNode *p;
           70  +
           71  +  p = fossil_malloc( sizeof(*p) );
           72  +  memset(p, 0, sizeof(*p));
           73  +  p->rid = rid;
           74  +  p->fromIsParent = isParent;
           75  +  p->pFrom = pFrom;
           76  +  p->u.pPeer = path.pCurrent;
           77  +  path.pCurrent = p;
           78  +  p->pAll = path.pAll;
           79  +  path.pAll = p;
           80  +  bag_insert(&path.seen, rid);
           81  +  return p;
           82  +}
           83  +
           84  +/*
           85  +** Reset memory used by the shortest path algorithm.
           86  +*/
           87  +void path_reset(void){
           88  +  PathNode *p;
           89  +  while( path.pAll ){
           90  +    p = path.pAll;
           91  +    path.pAll = p->pAll;
           92  +    fossil_free(p);
           93  +  }
           94  +  bag_clear(&path.seen);
           95  +  memset(&path, 0, sizeof(path));
           96  +}
           97  +
           98  +/*
           99  +** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
          100  +*/
          101  +static void path_reverse_path(void){
          102  +  PathNode *p;
          103  +  assert( path.pEnd!=0 );
          104  +  for(p=path.pEnd; p && p->pFrom; p = p->pFrom){
          105  +    p->pFrom->u.pTo = p;
          106  +  }
          107  +  path.pEnd->u.pTo = 0;
          108  +  assert( p==path.pStart );
          109  +}
          110  +
          111  +/*
          112  +** Compute the shortest path from iFrom to iTo
          113  +**
          114  +** If directOnly is true, then use only the "primary" links from parent to
          115  +** child.  In other words, ignore merges.
          116  +**
          117  +** Return a pointer to the beginning of the path (the iFrom node).  
          118  +** Elements of the path can be traversed by following the PathNode.u.pTo
          119  +** pointer chain.
          120  +**
          121  +** Return NULL if no path is found.
          122  +*/
          123  +PathNode *path_shortest(
          124  +  int iFrom,          /* Path starts here */
          125  +  int iTo,            /* Path ends here */
          126  +  int directOnly,     /* No merge links if true */
          127  +  int oneWayOnly      /* Parent->child only if true */
          128  +){
          129  +  Stmt s;
          130  +  PathNode *pPrev;
          131  +  PathNode *p;
          132  +
          133  +  path_reset();
          134  +  path.pStart = path_new_node(iFrom, 0, 0);
          135  +  if( iTo==iFrom ){
          136  +    path.pEnd = path.pStart;
          137  +    return path.pStart;
          138  +  }
          139  +  if( oneWayOnly && directOnly ){
          140  +    db_prepare(&s, 
          141  +        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
          142  +    );
          143  +  }else if( oneWayOnly ){
          144  +    db_prepare(&s, 
          145  +        "SELECT cid, 1 FROM plink WHERE pid=:pid "
          146  +    );
          147  +  }else if( directOnly ){
          148  +    db_prepare(&s, 
          149  +        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
          150  +        "UNION ALL "
          151  +        "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim"
          152  +    );
          153  +  }else{
          154  +    db_prepare(&s, 
          155  +        "SELECT cid, 1 FROM plink WHERE pid=:pid "
          156  +        "UNION ALL "
          157  +        "SELECT pid, 0 FROM plink WHERE cid=:pid"
          158  +    );
          159  +  }
          160  +  while( path.pCurrent ){
          161  +    path.nStep++;
          162  +    pPrev = path.pCurrent;
          163  +    path.pCurrent = 0;
          164  +    while( pPrev ){
          165  +      db_bind_int(&s, ":pid", pPrev->rid);
          166  +      while( db_step(&s)==SQLITE_ROW ){
          167  +        int cid = db_column_int(&s, 0);
          168  +        int isParent = db_column_int(&s, 1);
          169  +        if( bag_find(&path.seen, cid) ) continue;
          170  +        p = path_new_node(cid, pPrev, isParent);
          171  +        if( cid==iTo ){
          172  +          db_finalize(&s);
          173  +          path.pEnd = p;
          174  +          path_reverse_path();
          175  +          return path.pStart;
          176  +        }
          177  +      }
          178  +      db_reset(&s);
          179  +      pPrev = pPrev->u.pPeer;
          180  +    }
          181  +  }
          182  +  db_finalize(&s);
          183  +  path_reset();
          184  +  return 0;
          185  +}
          186  +
          187  +/*
          188  +** Find the mid-point of the path.  If the path contains fewer than
          189  +** 2 steps, return 0.
          190  +*/
          191  +PathNode *path_midpoint(void){
          192  +  PathNode *p;
          193  +  int i;
          194  +  if( path.nStep<2 ) return 0;
          195  +  for(p=path.pEnd, i=0; p && i<path.nStep/2; p=p->pFrom, i++){}
          196  +  return p;
          197  +}
          198  +
          199  +/*
          200  +** COMMAND:  test-shortest-path
          201  +**
          202  +** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2
          203  +**
          204  +** Report the shortest path between two checkins.  If the --no-merge flag
          205  +** is used, follow only direct parent-child paths and omit merge links.
          206  +*/
          207  +void shortest_path_test_cmd(void){
          208  +  int iFrom;
          209  +  int iTo;
          210  +  PathNode *p;
          211  +  int n;
          212  +  int directOnly;
          213  +  int oneWay;
          214  +
          215  +  db_find_and_open_repository(0,0);
          216  +  directOnly = find_option("no-merge",0,0)!=0;
          217  +  oneWay = find_option("one-way",0,0)!=0;
          218  +  if( g.argc!=4 ) usage("VERSION1 VERSION2");
          219  +  iFrom = name_to_rid(g.argv[2]);
          220  +  iTo = name_to_rid(g.argv[3]);
          221  +  p = path_shortest(iFrom, iTo, directOnly, oneWay);
          222  +  if( p==0 ){
          223  +    fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
          224  +  }
          225  +  for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
          226  +    char *z;
          227  +    z = db_text(0,
          228  +      "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
          229  +      "  FROM blob, event"
          230  +      " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
          231  +      p->rid, p->rid);
          232  +    fossil_print("%4d: %5d %s", n, p->rid, z);
          233  +    fossil_free(z);
          234  +    if( p->u.pTo ){
          235  +      fossil_print(" is a %s of\n", 
          236  +                   p->u.pTo->fromIsParent ? "parent" : "child");
          237  +    }else{
          238  +      fossil_print("\n");
          239  +    }
          240  +  }
          241  +}
          242  +
          243  +/*
          244  +** Find the closest common ancestor of two nodes.  "Closest" means the
          245  +** fewest number of arcs.
          246  +*/
          247  +int path_common_ancestor(int iMe, int iYou){
          248  +  Stmt s;
          249  +  PathNode *pPrev;
          250  +  PathNode *p;
          251  +  Bag me, you;
          252  +
          253  +  if( iMe==iYou ) return iMe;
          254  +  if( iMe==0 || iYou==0 ) return 0;
          255  +  path_reset();
          256  +  path.pStart = path_new_node(iMe, 0, 0);
          257  +  path.pStart->isPrim = 1;
          258  +  path.pEnd = path_new_node(iYou, 0, 0);
          259  +  db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
          260  +  bag_init(&me);
          261  +  bag_insert(&me, iMe);
          262  +  bag_init(&you);
          263  +  bag_insert(&you, iYou);
          264  +  while( path.pCurrent ){
          265  +    pPrev = path.pCurrent;
          266  +    path.pCurrent = 0;
          267  +    while( pPrev ){
          268  +      db_bind_int(&s, ":cid", pPrev->rid);
          269  +      while( db_step(&s)==SQLITE_ROW ){
          270  +        int pid = db_column_int(&s, 0);
          271  +        if( bag_find(pPrev->isPrim ? &you : &me, pid) ){
          272  +          /* pid is the common ancestor */
          273  +          PathNode *pNext;
          274  +          for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
          275  +          assert( p!=0 );
          276  +          pNext = p;
          277  +          while( pNext ){
          278  +            pNext = p->pFrom;
          279  +            p->pFrom = pPrev;
          280  +            pPrev = p;
          281  +            p = pNext;
          282  +          }
          283  +          if( pPrev==path.pStart ) path.pStart = path.pEnd;
          284  +          path.pEnd = pPrev;
          285  +          path_reverse_path();
          286  +          db_finalize(&s);
          287  +          return pid;
          288  +        }else if( bag_find(&path.seen, pid) ){
          289  +          /* pid is just an alternative path on one of the legs */
          290  +          continue;
          291  +        }
          292  +        p = path_new_node(pid, pPrev, 0);
          293  +        p->isPrim = pPrev->isPrim;
          294  +        bag_insert(pPrev->isPrim ? &me : &you, pid);
          295  +      }
          296  +      db_reset(&s);
          297  +      pPrev = pPrev->u.pPeer;
          298  +    }
          299  +  }
          300  +  db_finalize(&s);
          301  +  path_reset();
          302  +  return 0;
          303  +}
          304  +
          305  +/*
          306  +** COMMAND:  test-ancestor-path
          307  +**
          308  +** Usage: %fossil test-ancestor-path VERSION1 VERSION2
          309  +**
          310  +** Report the path from VERSION1 to VERSION2 through their most recent
          311  +** common ancestor.
          312  +*/
          313  +void ancestor_path_test_cmd(void){
          314  +  int iFrom;
          315  +  int iTo;
          316  +  int iPivot;
          317  +  PathNode *p;
          318  +  int n;
          319  +
          320  +  db_find_and_open_repository(0,0);
          321  +  if( g.argc!=4 ) usage("VERSION1 VERSION2");
          322  +  iFrom = name_to_rid(g.argv[2]);
          323  +  iTo = name_to_rid(g.argv[3]);
          324  +  iPivot = path_common_ancestor(iFrom, iTo);
          325  +  for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
          326  +    char *z;
          327  +    z = db_text(0,
          328  +      "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
          329  +      "  FROM blob, event"
          330  +      " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
          331  +      p->rid, p->rid);
          332  +    fossil_print("%4d: %5d %s", n, p->rid, z);
          333  +    fossil_free(z);
          334  +    if( p->rid==iFrom ) fossil_print(" VERSION1");
          335  +    if( p->rid==iTo ) fossil_print(" VERSION2");
          336  +    if( p->rid==iPivot ) fossil_print(" PIVOT");
          337  +    fossil_print("\n");
          338  +  }
          339  +}
          340  +
          341  +
          342  +/*
          343  +** A record of a file rename operation.
          344  +*/
          345  +typedef struct NameChange NameChange;
          346  +struct NameChange {
          347  +  int origName;        /* Original name of file */
          348  +  int curName;         /* Current name of the file */
          349  +  int newName;         /* Name of file in next version */
          350  +  NameChange *pNext;   /* List of all name changes */
          351  +};
          352  +
          353  +/*
          354  +** Compute all file name changes that occur going from checkin iFrom
          355  +** to checkin iTo.
          356  +**
          357  +** The number of name changes is written into *pnChng.  For each name
          358  +** change, two integers are allocated for *piChng.  The first is the 
          359  +** filename.fnid for the original name as seen in check-in iFrom and
          360  +** the second is for new name as it is used in check-in iTo.
          361  +**
          362  +** Space to hold *piChng is obtained from fossil_malloc() and should
          363  +** be released by the caller.
          364  +**
          365  +** This routine really has nothing to do with path.  It is located
          366  +** in this path.c module in order to leverage some of the path
          367  +** infrastructure.
          368  +*/
          369  +void find_filename_changes(
          370  +  int iFrom,               /* Ancestor check-in */
          371  +  int iTo,                 /* Recent check-in */
          372  +  int revOk,               /* Ok to move backwards (child->parent) if true */
          373  +  int *pnChng,             /* Number of name changes along the path */
          374  +  int **aiChng,            /* Name changes */
          375  +  const char *zDebug       /* Generate trace output if no NULL */
          376  +){
          377  +  PathNode *p;             /* For looping over path from iFrom to iTo */
          378  +  NameChange *pAll = 0;    /* List of all name changes seen so far */
          379  +  NameChange *pChng;       /* For looping through the name change list */
          380  +  int nChng = 0;           /* Number of files whose names have changed */
          381  +  int *aChng;              /* Two integers per name change */
          382  +  int i;                   /* Loop counter */
          383  +  Stmt q1;                 /* Query of name changes */
          384  +
          385  +  *pnChng = 0;
          386  +  *aiChng = 0;
          387  +  if( iFrom==iTo ) return;
          388  +  path_reset();
          389  +  p = path_shortest(iFrom, iTo, 1, revOk==0);
          390  +  if( p==0 ) return;
          391  +  path_reverse_path();
          392  +  db_prepare(&q1,
          393  +     "SELECT pfnid, fnid FROM mlink"
          394  +     " WHERE mid=:mid AND (pfnid>0 OR fid==0)"
          395  +     " ORDER BY pfnid"
          396  +  );
          397  +  for(p=path.pStart; p; p=p->u.pTo){
          398  +    int fnid, pfnid;
          399  +    if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){
          400  +      /* Skip nodes where the parent is not on the path */
          401  +      continue;
          402  +    }
          403  +    db_bind_int(&q1, ":mid", p->rid);
          404  +    while( db_step(&q1)==SQLITE_ROW ){
          405  +      fnid = db_column_int(&q1, 1);
          406  +      pfnid = db_column_int(&q1, 0);
          407  +      if( pfnid==0 ){
          408  +        pfnid = fnid;
          409  +        fnid = 0;
          410  +      }
          411  +      if( !p->fromIsParent ){
          412  +        int t = fnid;
          413  +        fnid = pfnid;
          414  +        pfnid = t;
          415  +      }
          416  +      if( zDebug ){
          417  +        fossil_print("%s at %d%s %.10z: %d[%z] -> %d[%z]\n",
          418  +           zDebug, p->rid, p->fromIsParent ? ">" : "<",
          419  +           db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid),
          420  +           pfnid,
          421  +           db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid),
          422  +           fnid,
          423  +           db_text(0, "SELECT name FROM filename WHERE fnid=%d", fnid));
          424  +      }
          425  +      for(pChng=pAll; pChng; pChng=pChng->pNext){
          426  +        if( pChng->curName==pfnid ){
          427  +          pChng->newName = fnid;
          428  +          break;
          429  +        }
          430  +      }
          431  +      if( pChng==0 && fnid>0 ){
          432  +        pChng = fossil_malloc( sizeof(*pChng) );
          433  +        pChng->pNext = pAll;
          434  +        pAll = pChng;
          435  +        pChng->origName = pfnid;
          436  +        pChng->curName = pfnid;
          437  +        pChng->newName = fnid;
          438  +        nChng++;
          439  +      }
          440  +    }
          441  +    for(pChng=pAll; pChng; pChng=pChng->pNext){
          442  +      pChng->curName = pChng->newName;
          443  +    }
          444  +    db_reset(&q1);
          445  +  }
          446  +  db_finalize(&q1);
          447  +  if( nChng ){
          448  +    aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
          449  +    for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
          450  +      if( pChng->newName==0 ) continue;
          451  +      if( pChng->origName==0 ) continue;
          452  +      if( pChng->newName==pChng->origName ) continue;
          453  +      aChng[i] = pChng->origName;
          454  +      aChng[i+1] = pChng->newName;
          455  +      if( zDebug ){
          456  +        fossil_print("%s summary %d[%z] -> %d[%z]\n",
          457  +           zDebug,
          458  +           aChng[i],
          459  +           db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i]),
          460  +           aChng[i+1],
          461  +           db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i+1]));
          462  +      }
          463  +      i += 2;
          464  +    }
          465  +    *pnChng = i/2;
          466  +    while( pAll ){
          467  +      pChng = pAll;
          468  +      pAll = pAll->pNext;
          469  +      fossil_free(pChng);
          470  +    }
          471  +  }
          472  +}
          473  +
          474  +/*
          475  +** COMMAND: test-name-changes
          476  +**
          477  +** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2
          478  +**
          479  +** Show all filename changes that occur going from VERSION1 to VERSION2
          480  +*/
          481  +void test_name_change(void){
          482  +  int iFrom;
          483  +  int iTo;
          484  +  int *aChng;
          485  +  int nChng;
          486  +  int i;
          487  +  const char *zDebug = 0;
          488  +  int revOk = 0;
          489  +
          490  +  db_find_and_open_repository(0,0);
          491  +  zDebug = find_option("debug",0,0)!=0 ? "debug" : 0;
          492  +  revOk = find_option("bidirectional",0,0)!=0;
          493  +  if( g.argc<4 ) usage("VERSION1 VERSION2");
          494  +  while( g.argc>=4 ){
          495  +    iFrom = name_to_rid(g.argv[2]);
          496  +    iTo = name_to_rid(g.argv[3]);
          497  +    find_filename_changes(iFrom, iTo, revOk, &nChng, &aChng, zDebug);
          498  +    fossil_print("------ Changes for (%d) %s -> (%d) %s\n",
          499  +                 iFrom, g.argv[2], iTo, g.argv[3]);
          500  +    for(i=0; i<nChng; i++){
          501  +      char *zFrom, *zTo;
          502  +
          503  +      zFrom = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
          504  +      zTo = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]);
          505  +      fossil_print("[%s] -> [%s]\n", zFrom, zTo);
          506  +      fossil_free(zFrom);
          507  +      fossil_free(zTo);
          508  +    }
          509  +    fossil_free(aChng);
          510  +    g.argv += 2;
          511  +    g.argc -= 2;
          512  +  }
          513  +}

Changes to src/pivot.c.

    48     48       "DELETE FROM aqueue;"
    49     49       "CREATE INDEX IF NOT EXISTS aqueue_idx1 ON aqueue(pending, mtime);"
    50     50     );
    51     51   
    52     52     /* Insert the primary record */
    53     53     db_multi_exec( 
    54     54       "INSERT INTO aqueue(rid, mtime, pending, src)"
    55         -    "  SELECT %d, mtime, 1, 1 FROM plink WHERE cid=%d LIMIT 1",
           55  +    "  SELECT %d, mtime, 1, 1 FROM event WHERE objid=%d AND type='ci' LIMIT 1",
    56     56       rid, rid
    57     57     );
    58     58   }
    59     59   
    60     60   /*
    61     61   ** Set a secondary file.  The primary file must be set first.  There
    62     62   ** must be at least one secondary but there can be more than one if
    63     63   ** desired.
    64     64   */
    65     65   void pivot_set_secondary(int rid){
    66     66     /* Insert the primary record */
    67     67     db_multi_exec( 
    68     68       "INSERT OR IGNORE INTO aqueue(rid, mtime, pending, src)"
    69         -    "  SELECT %d, mtime, 1, 0 FROM plink WHERE cid=%d",
           69  +    "  SELECT %d, mtime, 1, 0 FROM event WHERE objid=%d AND type='ci'",
    70     70       rid, rid
    71     71     );
    72     72   }
    73     73   
    74     74   /*
    75     75   ** Find the most recent common ancestor of the primary and one of
    76     76   ** the secondaries.  Return its rid.  Return 0 if no common ancestor

Added src/popen.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains an implementation of a bi-directional popen().
           19  +*/
           20  +#include "config.h"
           21  +#include "popen.h"
           22  +
           23  +#ifdef _WIN32
           24  +#include <windows.h>
           25  +#include <fcntl.h>
           26  +/*
           27  +** Print a fatal error and quit.
           28  +*/
           29  +static void win32_fatal_error(const char *zMsg){
           30  +  fossil_fatal("%s", zMsg);
           31  +}
           32  +#endif
           33  +
           34  +/*
           35  +** The following macros are used to cast pointers to integers and
           36  +** integers to pointers.  The way you do this varies from one compiler
           37  +** to the next, so we have developed the following set of #if statements
           38  +** to generate appropriate macros for a wide range of compilers.
           39  +**
           40  +** The correct "ANSI" way to do this is to use the intptr_t type. 
           41  +** Unfortunately, that typedef is not available on all compilers, or
           42  +** if it is available, it requires an #include of specific headers
           43  +** that vary from one machine to the next.
           44  +**
           45  +** This code is copied out of SQLite.
           46  +*/
           47  +#if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
           48  +# define INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
           49  +# define PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
           50  +#elif !defined(__GNUC__)       /* Works for compilers other than LLVM */
           51  +# define INT_TO_PTR(X)  ((void*)&((char*)0)[X])
           52  +# define PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
           53  +#elif defined(HAVE_STDINT_H)   /* Use this case if we have ANSI headers */
           54  +# define INT_TO_PTR(X)  ((void*)(intptr_t)(X))
           55  +# define PTR_TO_INT(X)  ((int)(intptr_t)(X))
           56  +#else                          /* Generates a warning - but it always works */
           57  +# define INT_TO_PTR(X)  ((void*)(X))
           58  +# define PTR_TO_INT(X)  ((int)(X))
           59  +#endif
           60  +
           61  +
           62  +#ifdef _WIN32
           63  +/*
           64  +** On windows, create a child process and specify the stdin, stdout,
           65  +** and stderr channels for that process to use.
           66  +**
           67  +** Return the number of errors.
           68  +*/
           69  +static int win32_create_child_process(
           70  +  wchar_t *zCmd,       /* The command that the child process will run */
           71  +  HANDLE hIn,          /* Standard input */
           72  +  HANDLE hOut,         /* Standard output */
           73  +  HANDLE hErr,         /* Standard error */
           74  +  DWORD *pChildPid     /* OUT: Child process handle */
           75  +){
           76  +  STARTUPINFOW si;
           77  +  PROCESS_INFORMATION pi;
           78  +  BOOL rc;
           79  +
           80  +  memset(&si, 0, sizeof(si));
           81  +  si.cb = sizeof(si);
           82  +  si.dwFlags = STARTF_USESTDHANDLES;
           83  +  SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE);
           84  +  si.hStdInput  = hIn;
           85  +  SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE);
           86  +  si.hStdOutput = hOut;
           87  +  SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE);
           88  +  si.hStdError  = hErr;
           89  +  rc = CreateProcessW(
           90  +     NULL,  /* Application Name */
           91  +     zCmd,  /* Command-line */
           92  +     NULL,  /* Process attributes */
           93  +     NULL,  /* Thread attributes */
           94  +     TRUE,  /* Inherit Handles */
           95  +     0,     /* Create flags  */
           96  +     NULL,  /* Environment */
           97  +     NULL,  /* Current directory */
           98  +     &si,   /* Startup Info */
           99  +     &pi    /* Process Info */
          100  +  );
          101  +  if( rc ){
          102  +    CloseHandle( pi.hProcess );
          103  +    CloseHandle( pi.hThread );
          104  +    *pChildPid = pi.dwProcessId;
          105  +  }else{
          106  +    win32_fatal_error("cannot create child process");
          107  +  }
          108  +  return rc!=0;
          109  +}
          110  +#endif
          111  +
          112  +/*
          113  +** Create a child process running shell command "zCmd".  *ppOut is
          114  +** a FILE that becomes the standard input of the child process.  
          115  +** (The caller writes to *ppOut in order to send text to the child.)
          116  +** *ppIn is stdout from the child process.  (The caller
          117  +** reads from *ppIn in order to receive input from the child.)
          118  +** Note that *ppIn is an unbuffered file descriptor, not a FILE.
          119  +** The process ID of the child is written into *pChildPid.
          120  +**
          121  +** Return the number of errors.
          122  +*/
          123  +int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){
          124  +#ifdef _WIN32
          125  +  HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
          126  +  SECURITY_ATTRIBUTES saAttr;    
          127  +  DWORD childPid = 0;
          128  +  int fd;
          129  +
          130  +  saAttr.nLength = sizeof(saAttr);
          131  +  saAttr.bInheritHandle = TRUE;
          132  +  saAttr.lpSecurityDescriptor = NULL; 
          133  +  hStderr = GetStdHandle(STD_ERROR_HANDLE);
          134  +  if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
          135  +    win32_fatal_error("cannot create pipe for stdout");
          136  +  }
          137  +  SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);
          138  +
          139  +  if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
          140  +    win32_fatal_error("cannot create pipe for stdin");
          141  +  }
          142  +  SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
          143  +  
          144  +  win32_create_child_process(fossil_utf8_to_unicode(zCmd),
          145  +                             hStdinRd, hStdoutWr, hStderr,&childPid);
          146  +  *pChildPid = childPid;
          147  +  *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
          148  +  fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
          149  +  *ppOut = _fdopen(fd, "w");
          150  +  CloseHandle(hStdinRd); 
          151  +  CloseHandle(hStdoutWr);
          152  +  return 0;
          153  +#else
          154  +  int pin[2], pout[2];
          155  +  *pfdIn = 0;
          156  +  *ppOut = 0;
          157  +  *pChildPid = 0;
          158  +
          159  +  if( pipe(pin)<0 ){
          160  +    return 1;
          161  +  }
          162  +  if( pipe(pout)<0 ){
          163  +    close(pin[0]);
          164  +    close(pin[1]);
          165  +    return 1;
          166  +  }
          167  +  *pChildPid = fork();
          168  +  if( *pChildPid<0 ){
          169  +    close(pin[0]);
          170  +    close(pin[1]);
          171  +    close(pout[0]);
          172  +    close(pout[1]);
          173  +    *pChildPid = 0;
          174  +    return 1;
          175  +  }
          176  +  if( *pChildPid==0 ){
          177  +    int fd;
          178  +    int nErr = 0;
          179  +    /* This is the child process */
          180  +    close(0);
          181  +    fd = dup(pout[0]);
          182  +    if( fd!=0 ) nErr++;
          183  +    close(pout[0]);
          184  +    close(pout[1]);
          185  +    close(1);
          186  +    fd = dup(pin[1]);
          187  +    if( fd!=1 ) nErr++;
          188  +    close(pin[0]);
          189  +    close(pin[1]);
          190  +    execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
          191  +    return 1;
          192  +  }else{
          193  +    /* This is the parent process */
          194  +    close(pin[1]);
          195  +    *pfdIn = pin[0];
          196  +    close(pout[0]);
          197  +    *ppOut = fdopen(pout[1], "w");
          198  +    return 0;
          199  +  }
          200  +#endif
          201  +}
          202  +
          203  +/*
          204  +** Close the connection to a child process previously created using
          205  +** popen2().  Kill off the child process, then close the pipes.
          206  +*/
          207  +void pclose2(int fdIn, FILE *pOut, int childPid){
          208  +#ifdef _WIN32
          209  +  /* Not implemented, yet */
          210  +  close(fdIn);
          211  +  fclose(pOut);
          212  +#else
          213  +  close(fdIn);
          214  +  fclose(pOut);
          215  +  kill(childPid, SIGINT);
          216  +#endif
          217  +}

Changes to src/pqueue.c.

    20     20   ** value.  We can insert integers with each integer tied to its
    21     21   ** value then extract the integer with the smallest value.
    22     22   **
    23     23   ** The way this queue is used, we never expect it to contain more
    24     24   ** than 2 or 3 elements, so a simple array is sufficient as the
    25     25   ** implementation.  This could give worst case O(N) insert times,
    26     26   ** but because of the nature of the problem we expect O(1) performance.
           27  +**
           28  +** Compatibility note:  Some versions of OpenSSL export a symbols
           29  +** like "pqueue_insert".  This is, technically, a bug in OpenSSL.
           30  +** We work around it here by using "pqueuex_" instead of "pqueue_".
    27     31   */
    28     32   #include "config.h"
    29     33   #include "pqueue.h"
    30     34   #include <assert.h>
    31     35   
    32     36   
    33     37   #if INTERFACE
................................................................................
    36     40   ** Integers must be positive.
    37     41   */
    38     42   struct PQueue {
    39     43     int cnt;   /* Number of entries in the queue */
    40     44     int sz;    /* Number of slots in a[] */
    41     45     struct QueueElement {
    42     46       int id;          /* ID of the element */
           47  +    void *p;         /* Content pointer */
    43     48       double value;    /* Value of element.  Kept in ascending order */
    44     49     } *a;
    45     50   };
    46     51   #endif
    47     52   
    48     53   /*
    49     54   ** Initialize a PQueue structure
    50     55   */
    51         -void pqueue_init(PQueue *p){
           56  +void pqueuex_init(PQueue *p){
    52     57     memset(p, 0, sizeof(*p));
    53     58   }
    54     59   
    55     60   /*
    56     61   ** Destroy a PQueue.  Delete all of its content.
    57     62   */
    58         -void pqueue_clear(PQueue *p){
           63  +void pqueuex_clear(PQueue *p){
    59     64     free(p->a);
    60         -  pqueue_init(p);
           65  +  pqueuex_init(p);
    61     66   }
    62     67   
    63     68   /*
    64     69   ** Change the size of the queue so that it contains N slots
    65     70   */
    66         -static void pqueue_resize(PQueue *p, int N){
    67         -  p->a = realloc(p->a, sizeof(p->a[0])*N);
           71  +static void pqueuex_resize(PQueue *p, int N){
           72  +  p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
    68     73     p->sz = N;
    69     74   }
    70     75   
    71     76   /*
    72     77   ** Insert element e into the queue.
    73     78   */
    74         -void pqueue_insert(PQueue *p, int e, double v){
           79  +void pqueuex_insert(PQueue *p, int e, double v, void *pData){
    75     80     int i, j;
    76     81     if( p->cnt+1>p->sz ){
    77         -    pqueue_resize(p, p->cnt+5);
           82  +    pqueuex_resize(p, p->cnt+5);
    78     83     }
    79     84     for(i=0; i<p->cnt; i++){
    80     85       if( p->a[i].value>v ){
    81     86         for(j=p->cnt; j>i; j--){
    82     87           p->a[j] = p->a[j-1];
    83     88         }
    84     89         break;
    85     90       }
    86     91     }
    87     92     p->a[i].id = e;
           93  +  p->a[i].p = pData;
    88     94     p->a[i].value = v;
    89     95     p->cnt++;
    90     96   }
    91     97   
    92     98   /*
    93     99   ** Extract the first element from the queue (the element with
    94    100   ** the smallest value) and return its ID.  Return 0 if the queue
    95    101   ** is empty.
    96    102   */
    97         -int pqueue_extract(PQueue *p){
          103  +int pqueuex_extract(PQueue *p, void **pp){
    98    104     int e, i;
    99    105     if( p->cnt==0 ){
          106  +    if( pp ) *pp = 0;
   100    107       return 0;
   101    108     }
   102    109     e = p->a[0].id;
          110  +  if( pp ) *pp = p->a[0].p;
   103    111     for(i=0; i<p->cnt-1; i++){
   104    112       p->a[i] = p->a[i+1];
   105    113     }
   106    114     p->cnt--;
   107    115     return e;
   108    116   }

Changes to src/printf.c.

    22     22   
    23     23   /*
    24     24   ** Conversion types fall into various categories as defined by the
    25     25   ** following enumeration.
    26     26   */
    27     27   #define etRADIX       1 /* Integer types.  %d, %x, %o, and so forth */
    28     28   #define etFLOAT       2 /* Floating point.  %f */
    29         -#define etEXP         3 /* Exponentional notation. %e and %E */
           29  +#define etEXP         3 /* Exponential notation. %e and %E */
    30     30   #define etGENERIC     4 /* Floating or exponential, depending on exponent. %g */
    31     31   #define etSIZE        5 /* Return number of characters processed so far. %n */
    32     32   #define etSTRING      6 /* Strings. %s */
    33     33   #define etDYNSTRING   7 /* Dynamically allocated strings. %z */
    34     34   #define etPERCENT     8 /* Percent symbol. %% */
    35     35   #define etCHARX       9 /* Characters. %c */
    36     36   #define etERROR      10 /* Used to indicate no such conversion type */
................................................................................
    42     42                             NULL pointers replaced by SQL NULL.  %Q */
    43     43   #define etPOINTER    15 /* The %p conversion */
    44     44   #define etHTMLIZE    16 /* Make text safe for HTML */
    45     45   #define etHTTPIZE    17 /* Make text safe for HTTP.  "/" encoded as %2f */
    46     46   #define etURLIZE     18 /* Make text safe for HTTP.  "/" not encoded */
    47     47   #define etFOSSILIZE  19 /* The fossil header encoding format. */
    48     48   #define etPATH       20 /* Path type */
    49         -#define etWIKISTR    21 /* Wiki text rendered from a char* */
    50         -#define etWIKIBLOB   22 /* Wiki text rendered from a Blob* */
    51         -#define etSTRINGID   23 /* String with length limit for a UUID prefix */
           49  +#define etWIKISTR    21 /* Timeline comment text rendered from a char*: %w */
           50  +#define etSTRINGID   23 /* String with length limit for a UUID prefix: %S */
           51  +#define etROOT       24 /* String value of g.zTop: % */
    52     52   
    53     53   
    54     54   /*
    55     55   ** An "etByte" is an 8-bit unsigned value.
    56     56   */
    57     57   typedef unsigned char etByte;
    58     58   
................................................................................
    89     89     {  'g',  0, 1, etGENERIC,    30, 0 },
    90     90     {  'z',  0, 6, etDYNSTRING,  0,  0 },
    91     91     {  'q',  0, 4, etSQLESCAPE,  0,  0 },
    92     92     {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
    93     93     {  'b',  0, 2, etBLOB,       0,  0 },
    94     94     {  'B',  0, 2, etBLOBSQL,    0,  0 },
    95     95     {  'w',  0, 2, etWIKISTR,    0,  0 },
    96         -  {  'W',  0, 2, etWIKIBLOB,   0,  0 },
    97     96     {  'h',  0, 4, etHTMLIZE,    0,  0 },
           97  +  {  'R',  0, 0, etROOT,       0,  0 },
    98     98     {  't',  0, 4, etHTTPIZE,    0,  0 },  /* "/" -> "%2F" */
    99     99     {  'T',  0, 4, etURLIZE,     0,  0 },  /* "/" unchanged */
   100    100     {  'F',  0, 4, etFOSSILIZE,  0,  0 },
   101    101     {  'S',  0, 4, etSTRINGID,   0,  0 },
   102    102     {  'c',  0, 0, etCHARX,      0,  0 },
   103    103     {  'o',  8, 0, etRADIX,      0,  2 },
   104    104     {  'u', 10, 0, etRADIX,      0,  0 },
................................................................................
   152    152   ** is an alias for strlen().
   153    153   */
   154    154   static int StrNLen32(const char *z, int N){
   155    155     int n = 0;
   156    156     while( (N-- != 0) && *(z++)!=0 ){ n++; }
   157    157     return n;
   158    158   }
          159  +
          160  +/*
          161  +** Return an appropriate set of flags for wiki_convert() for displaying
          162  +** comments on a timeline.  These flag settings are determined by
          163  +** configuration parameters.
          164  +*/
          165  +static int wiki_convert_flags(void){
          166  +  static int wikiFlags = 0;
          167  +  if( wikiFlags==0 ){
          168  +    if( db_get_boolean("timeline-block-markup", 0) ){
          169  +      wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
          170  +    }else{
          171  +      wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
          172  +    }
          173  +    if( db_get_boolean("timeline-plaintext", 0) ){
          174  +      wikiFlags |= WIKI_LINKSONLY;
          175  +    }
          176  +  }
          177  +  return wikiFlags;
          178  +}
          179  +
   159    180   
   160    181   
   161    182   /*
   162    183   ** The root program.  All variations call this core.
   163    184   **
   164    185   ** INPUTS:
   165    186   **   func   This is a pointer to a function taking three arguments
................................................................................
   239    260       if( (c=(*++fmt))==0 ){
   240    261         errorflag = 1;
   241    262         blob_append(pBlob,"%",1);
   242    263         count++;
   243    264         break;
   244    265       }
   245    266       /* Find out what flags are present */
   246         -    flag_leftjustify = flag_plussign = flag_blanksign = 
          267  +    flag_leftjustify = flag_plussign = flag_blanksign =
   247    268        flag_alternateform = flag_altform2 = flag_zeropad = 0;
   248    269       done = 0;
   249    270       do{
   250    271         switch( c ){
   251    272           case '-':   flag_leftjustify = 1;     break;
   252    273           case '+':   flag_plussign = 1;        break;
   253    274           case ' ':   flag_blanksign = 1;       break;
................................................................................
   543    564           break;
   544    565         case etPERCENT:
   545    566           buf[0] = '%';
   546    567           bufpt = buf;
   547    568           length = 1;
   548    569           break;
   549    570         case etCHARX:
   550         -        c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
          571  +        c = buf[0] = va_arg(ap,int);
   551    572           if( precision>=0 ){
   552    573             for(idx=1; idx<precision; idx++) buf[idx] = c;
   553    574             length = precision;
   554    575           }else{
   555    576             length =1;
   556    577           }
   557    578           bufpt = buf;
................................................................................
   558    579           break;
   559    580         case etPATH: {
   560    581           int i;
   561    582           int limit = flag_alternateform ? va_arg(ap,int) : -1;
   562    583           char *e = va_arg(ap,char*);
   563    584           if( e==0 ){e="";}
   564    585           length = StrNLen32(e, limit);
   565         -        zExtra = bufpt = malloc(length+1);
          586  +        zExtra = bufpt = fossil_malloc(length+1);
   566    587           for( i=0; i<length; i++ ){
   567    588             if( e[i]=='\\' ){
   568    589               bufpt[i]='/';
   569    590             }else{
   570    591               bufpt[i]=e[i];
   571    592             }
   572    593           }
   573    594           bufpt[length]='\0';
   574    595           break;
          596  +      }
          597  +      case etROOT: {
          598  +        bufpt = g.zTop ? g.zTop : "";
          599  +        length = (int)strlen(bufpt);
          600  +        break;
   575    601         }
   576    602         case etSTRINGID: {
   577    603           precision = 16;
   578    604           /* Fall through */
   579    605         }
   580    606         case etSTRING:
   581    607         case etDYNSTRING: {
................................................................................
   603    629           Blob *pBlob = va_arg(ap, Blob*);
   604    630           char *zOrig = blob_buffer(pBlob);
   605    631           int i, j, n, cnt;
   606    632           n = blob_size(pBlob);
   607    633           if( limit>=0 && limit<n ) n = limit;
   608    634           for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; }
   609    635           if( n+cnt+2 > etBUFSIZE ){
   610         -          bufpt = zExtra = malloc( n + cnt + 2 );
          636  +          bufpt = zExtra = fossil_malloc( n + cnt + 2 );
   611    637           }else{
   612    638             bufpt = buf;
   613    639           }
   614    640           bufpt[0] = '\'';
   615    641           for(i=0, j=1; i<n; i++, j++){
   616    642             if( zOrig[i]=='\'' ){ bufpt[j++] = '\''; }
   617    643             bufpt[j] = zOrig[i];
................................................................................
   632    658           if( limit<0 ) limit = strlen(escarg);
   633    659           for(i=n=0; i<limit; i++){
   634    660             if( escarg[i]=='\'' )  n++;
   635    661           }
   636    662           needQuote = !isnull && xtype==etSQLESCAPE2;
   637    663           n += i + 1 + needQuote*2;
   638    664           if( n>etBUFSIZE ){
   639         -          bufpt = zExtra = malloc( n );
   640         -          if( bufpt==0 ) return -1;
          665  +          bufpt = zExtra = fossil_malloc( n );
   641    666           }else{
   642    667             bufpt = buf;
   643    668           }
   644    669           j = 0;
   645    670           if( needQuote ) bufpt[j++] = '\'';
   646    671           for(i=0; i<limit; i++){
   647    672             bufpt[j++] = ch = escarg[i];
................................................................................
   690    715           break;
   691    716         }
   692    717         case etWIKISTR: {
   693    718           int limit = flag_alternateform ? va_arg(ap,int) : -1;
   694    719           char *zWiki = va_arg(ap, char*);
   695    720           Blob wiki;
   696    721           blob_init(&wiki, zWiki, limit);
   697         -        wiki_convert(&wiki, pBlob, WIKI_INLINE);
          722  +        wiki_convert(&wiki, pBlob, wiki_convert_flags());
   698    723           blob_reset(&wiki);
   699    724           length = width = 0;
   700    725           break;
   701    726         }
   702         -      case etWIKIBLOB: {
   703         -        Blob *pWiki = va_arg(ap, Blob*);
   704         -        wiki_convert(pWiki, pBlob, WIKI_INLINE);
   705         -        length = width = 0;
   706         -        break;
   707         -      }
   708    727         case etERROR:
   709    728           buf[0] = '%';
   710    729           buf[1] = c;
   711    730           errorflag = 0;
   712    731           idx = 1+(c!=0);
   713    732           blob_append(pBlob,"%",idx);
   714    733           count += idx;
................................................................................
   745    764             blob_append(pBlob,spaces,etSPACESIZE);
   746    765             nspace -= etSPACESIZE;
   747    766           }
   748    767           if( nspace>0 ) blob_append(pBlob,spaces,nspace);
   749    768         }
   750    769       }
   751    770       if( zExtra ){
   752         -      free(zExtra);
          771  +      fossil_free(zExtra);
   753    772       }
   754    773     }/* End for loop over the format string */
   755    774     return errorflag ? -1 : count;
   756    775   } /* End of function */
   757    776   
   758    777   /*
   759         -** Print into memory obtained from malloc().
          778  +** Print into memory obtained from fossil_malloc().
   760    779   */
   761    780   char *mprintf(const char *zFormat, ...){
   762    781     va_list ap;
   763    782     char *z;
   764    783     va_start(ap,zFormat);
   765    784     z = vmprintf(zFormat, ap);
   766    785     va_end(ap);
................................................................................
   796    815     g.iErrPriority = iPriority;
   797    816   }
   798    817   void fossil_error_reset(void){
   799    818     free(g.zErrMsg);
   800    819     g.zErrMsg = 0;
   801    820     g.iErrPriority = 0;
   802    821   }
          822  +
          823  +/* True if the last character standard output cursor is setting at
          824  +** the beginning of a blank link.  False if a \r has been to move the
          825  +** cursor to the beginning of the line or if not at the beginning of
          826  +** a line.
          827  +** was a \n
          828  +*/
          829  +static int stdoutAtBOL = 1;
          830  +
          831  +/*
          832  +** Write to standard output or standard error.
          833  +**
          834  +** On windows, transform the output into the current terminal encoding
          835  +** if the output is going to the screen.  If output is redirected into
          836  +** a file, no translation occurs.  No translation ever occurs on unix.
          837  +*/
          838  +void fossil_puts(const char *z, int toStdErr){
          839  +  int n = (int)strlen(z);
          840  +  if( n==0 ) return;
          841  +  if( toStdErr==0 ) stdoutAtBOL = (z[n-1]=='\n');
          842  +#if defined(_WIN32)
          843  +  if( fossil_utf8_to_console(z, n, toStdErr) >= 0 ){
          844  +    return;
          845  +  }
          846  +#endif
          847  +  assert( toStdErr==0 || toStdErr==1 );
          848  +  fwrite(z, 1, n, toStdErr ? stderr : stdout);
          849  +  fflush(toStdErr ? stderr : stdout);
          850  +}
          851  +
          852  +/*
          853  +** Force the standard output cursor to move to the beginning 
          854  +** of a line, if it is not there already.
          855  +*/
          856  +void fossil_force_newline(void){
          857  +  if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0);
          858  +}
   803    859   
   804    860   /*
   805    861   ** Write output for user consumption.  If g.cgiOutput is enabled, then
   806    862   ** send the output as part of the CGI reply.  If g.cgiOutput is false,
   807    863   ** then write on standard output.
   808    864   */
   809    865   void fossil_print(const char *zFormat, ...){
   810    866     va_list ap;
   811    867     va_start(ap, zFormat);
   812    868     if( g.cgiOutput ){
   813    869       cgi_vprintf(zFormat, ap);
   814    870     }else{
   815         -    vprintf(zFormat, ap);
          871  +    Blob b = empty_blob;
          872  +    vxprintf(&b, zFormat, ap);
          873  +    fossil_puts(blob_str(&b), 0);
          874  +    blob_reset(&b);
          875  +  }
          876  +  va_end(ap);
          877  +}
          878  +
          879  +/*
          880  +** Print a trace message on standard error.
          881  +*/
          882  +void fossil_trace(const char *zFormat, ...){
          883  +  va_list ap;
          884  +  Blob b;
          885  +  va_start(ap, zFormat);
          886  +  b = empty_blob;
          887  +  vxprintf(&b, zFormat, ap);
          888  +  fossil_puts(blob_str(&b), 1);
          889  +  blob_reset(&b);
          890  +  va_end(ap);
          891  +}
          892  +
          893  +/*
          894  +** Like strcmp() except that it accepts NULL pointers.  NULL sorts before
          895  +** all non-NULL string pointers.  Also, this strcmp() is a binary comparison
          896  +** that does not consider locale.
          897  +*/
          898  +int fossil_strcmp(const char *zA, const char *zB){
          899  +  if( zA==0 ){
          900  +    if( zB==0 ) return 0;
          901  +    return -1;
          902  +  }else if( zB==0 ){
          903  +    return +1;
          904  +  }else{
          905  +    int a, b;
          906  +    do{
          907  +      a = *zA++;
          908  +      b = *zB++;
          909  +    }while( a==b && a!=0 );
          910  +    return ((unsigned char)a) - (unsigned char)b;
          911  +  }
          912  +}
          913  +int fossil_strncmp(const char *zA, const char *zB, int nByte){
          914  +  if( zA==0 ){
          915  +    if( zB==0 ) return 0;
          916  +    return -1;
          917  +  }else if( zB==0 ){
          918  +    return +1;
          919  +  }else if( nByte>0 ){
          920  +    int a, b;
          921  +    do{
          922  +      a = *zA++;
          923  +      b = *zB++;
          924  +    }while( a==b && a!=0 && (--nByte)>0 );
          925  +    return ((unsigned char)a) - (unsigned char)b;
          926  +  }else{
          927  +    return 0;
          928  +  }
          929  +}
          930  +
          931  +/*
          932  +** Case insensitive string comparison.
          933  +*/
          934  +int fossil_strnicmp(const char *zA, const char *zB, int nByte){
          935  +  if( zA==0 ){
          936  +    if( zB==0 ) return 0;
          937  +    return -1;
          938  +  }else if( zB==0 ){
          939  +    return +1;
          940  +  }
          941  +  if( nByte<0 ) nByte = strlen(zB);
          942  +  return sqlite3_strnicmp(zA, zB, nByte);
          943  +}
          944  +int fossil_stricmp(const char *zA, const char *zB){
          945  +  int nByte;
          946  +  int rc;
          947  +  if( zA==0 ){
          948  +    if( zB==0 ) return 0;
          949  +    return -1;
          950  +  }else if( zB==0 ){
          951  +    return +1;
   816    952     }
          953  +  nByte = strlen(zB);
          954  +  rc = sqlite3_strnicmp(zA, zB, nByte);
          955  +  if( rc==0 && zA[nByte] ) rc = 1;
          956  +  return rc;
   817    957   }

Changes to src/rebuild.c.

    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code used to rebuild the database.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "rebuild.h"
    22     22   #include <assert.h>
           23  +#include <errno.h>
    23     24   
    24     25   /*
    25         -** Schema changes
           26  +** Make changes to the stable part of the schema (the part that is not
           27  +** simply deleted and reconstructed on a rebuild) to bring the schema
           28  +** up to the latest.
    26     29   */
    27         -static const char zSchemaUpdates[] =
           30  +static const char zSchemaUpdates1[] =
    28     31   @ -- Index on the delta table
    29     32   @ --
    30     33   @ CREATE INDEX IF NOT EXISTS delta_i1 ON delta(srcid);
    31     34   @
    32     35   @ -- Artifacts that should not be processed are identified in the
    33     36   @ -- "shun" table.  Artifacts that are control-file forgeries or
    34     37   @ -- spam or artifacts whose contents violate administrative policy
................................................................................
    35     38   @ -- can be shunned in order to prevent them from contaminating
    36     39   @ -- the repository.
    37     40   @ --
    38     41   @ -- Shunned artifacts do not exist in the blob table.  Hence they
    39     42   @ -- have not artifact ID (rid) and we thus must store their full
    40     43   @ -- UUID.
    41     44   @ --
    42         -@ CREATE TABLE IF NOT EXISTS shun(uuid UNIQUE);
           45  +@ CREATE TABLE IF NOT EXISTS shun(
           46  +@   uuid UNIQUE,          -- UUID of artifact to be shunned. Canonical form
           47  +@   mtime INTEGER,        -- When added.  Seconds since 1970
           48  +@   scom TEXT             -- Optional text explaining why the shun occurred
           49  +@ );
    43     50   @
    44     51   @ -- Artifacts that should not be pushed are stored in the "private"
    45     52   @ -- table.  
    46     53   @ --
    47     54   @ CREATE TABLE IF NOT EXISTS private(rid INTEGER PRIMARY KEY);
    48     55   @
    49         -@ -- An entry in this table describes a database query that generates a
    50         -@ -- table of tickets.
    51         -@ --
    52         -@ CREATE TABLE IF NOT EXISTS reportfmt(
    53         -@    rn integer primary key,  -- Report number
    54         -@    owner text,              -- Owner of this report format (not used)
    55         -@    title text,              -- Title of this report
    56         -@    cols text,               -- A color-key specification
    57         -@    sqlcode text             -- An SQL SELECT statement for this report
    58         -@ );
    59         -@
    60     56   @ -- Some ticket content (such as the originators email address or contact
    61     57   @ -- information) needs to be obscured to protect privacy.  This is achieved
    62     58   @ -- by storing an SHA1 hash of the content.  For display, the hash is
    63     59   @ -- mapped back into the original text using this table.  
    64     60   @ --
    65     61   @ -- This table contains sensitive information and should not be shared
    66     62   @ -- with unauthorized users.
    67     63   @ --
    68     64   @ CREATE TABLE IF NOT EXISTS concealed(
    69         -@   hash TEXT PRIMARY KEY,
    70         -@   content TEXT
           65  +@   hash TEXT PRIMARY KEY,    -- The SHA1 hash of content
           66  +@   mtime INTEGER,            -- Time created.  Seconds since 1970
           67  +@   content TEXT              -- Content intended to be concealed
           68  +@ );
           69  +;
           70  +static const char zSchemaUpdates2[] =
           71  +@ -- An entry in this table describes a database query that generates a
           72  +@ -- table of tickets.
           73  +@ --
           74  +@ CREATE TABLE IF NOT EXISTS reportfmt(
           75  +@    rn INTEGER PRIMARY KEY,  -- Report number
           76  +@    owner TEXT,              -- Owner of this report format (not used)
           77  +@    title TEXT UNIQUE,       -- Title of this report
           78  +@    mtime INTEGER,           -- Time last modified.  Seconds since 1970
           79  +@    cols TEXT,               -- A color-key specification
           80  +@    sqlcode TEXT             -- An SQL SELECT statement for this report
    71     81   @ );
    72     82   ;
    73     83   
           84  +static void rebuild_update_schema(void){
           85  +  int rc;
           86  +  db_multi_exec(zSchemaUpdates1);
           87  +  db_multi_exec(zSchemaUpdates2);
           88  +
           89  +  rc = db_exists("SELECT 1 FROM sqlite_master"
           90  +                 " WHERE name='user' AND sql GLOB '* mtime *'");
           91  +  if( rc==0 ){
           92  +    db_multi_exec(
           93  +      "CREATE TEMP TABLE temp_user AS SELECT * FROM user;"
           94  +      "DROP TABLE user;"
           95  +      "CREATE TABLE user(\n"
           96  +      "  uid INTEGER PRIMARY KEY,\n"
           97  +      "  login TEXT UNIQUE,\n"
           98  +      "  pw TEXT,\n"
           99  +      "  cap TEXT,\n"
          100  +      "  cookie TEXT,\n"
          101  +      "  ipaddr TEXT,\n"
          102  +      "  cexpire DATETIME,\n"
          103  +      "  info TEXT,\n"
          104  +      "  mtime DATE,\n"
          105  +      "  photo BLOB\n"
          106  +      ");"
          107  +      "INSERT OR IGNORE INTO user"
          108  +        " SELECT uid, login, pw, cap, cookie,"
          109  +               " ipaddr, cexpire, info, now(), photo FROM temp_user;"
          110  +      "DROP TABLE temp_user;"
          111  +    );
          112  +  }
          113  +
          114  +  rc = db_exists("SELECT 1 FROM sqlite_master"
          115  +                 " WHERE name='config' AND sql GLOB '* mtime *'");
          116  +  if( rc==0 ){
          117  +    db_multi_exec(
          118  +      "ALTER TABLE config ADD COLUMN mtime INTEGER;"
          119  +      "UPDATE config SET mtime=now();"
          120  +    );
          121  +  }
          122  +
          123  +  rc = db_exists("SELECT 1 FROM sqlite_master"
          124  +                 " WHERE name='shun' AND sql GLOB '* mtime *'");
          125  +  if( rc==0 ){
          126  +    db_multi_exec(
          127  +      "ALTER TABLE shun ADD COLUMN mtime INTEGER;"
          128  +      "ALTER TABLE shun ADD COLUMN scom TEXT;"
          129  +      "UPDATE shun SET mtime=now();"
          130  +    );
          131  +  }
          132  +
          133  +  rc = db_exists("SELECT 1 FROM sqlite_master"
          134  +                 " WHERE name='reportfmt' AND sql GLOB '* mtime *'");
          135  +  if( rc==0 ){
          136  +    db_multi_exec(
          137  +      "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;"
          138  +      "DROP TABLE reportfmt;"
          139  +    );
          140  +    db_multi_exec(zSchemaUpdates2);
          141  +    db_multi_exec(
          142  +      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
          143  +        " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;"
          144  +      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
          145  +        " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()"
          146  +        "   FROM old_fmt;"
          147  +    );
          148  +  }
          149  +
          150  +  rc = db_exists("SELECT 1 FROM sqlite_master"
          151  +                 " WHERE name='concealed' AND sql GLOB '* mtime *'");
          152  +  if( rc==0 ){
          153  +    db_multi_exec(
          154  +      "ALTER TABLE concealed ADD COLUMN mtime INTEGER;"
          155  +      "UPDATE concealed SET mtime=now();"
          156  +    );
          157  +  }
          158  +}  
          159  +
    74    160   /*
    75         -** Variables used for progress information
          161  +** Variables used to store state information about an on-going "rebuild"
          162  +** or "deconstruct".
    76    163   */
    77    164   static int totalSize;       /* Total number of artifacts to process */
    78    165   static int processCnt;      /* Number processed so far */
    79    166   static int ttyOutput;       /* Do progress output */
    80    167   static Bag bagDone;         /* Bag of records rebuilt */
          168  +
          169  +static char *zFNameFormat;  /* Format string for filenames on deconstruct */
          170  +static int prefixLength;    /* Length of directory prefix for deconstruct */
          171  +
          172  +
          173  +/*
          174  +** Draw the percent-complete message.
          175  +** The input is actually the permill complete.
          176  +*/
          177  +static void percent_complete(int permill){
          178  +  static int lastOutput = -1;
          179  +  if( permill>lastOutput ){
          180  +    fossil_print("  %d.%d%% complete...\r", permill/10, permill%10);
          181  +    fflush(stdout);
          182  +    lastOutput = permill;
          183  +  }
          184  +}
          185  +
    81    186   
    82    187   /*
    83    188   ** Called after each artifact is processed
    84    189   */
    85         -static void rebuild_step_done(rid){
          190  +static void rebuild_step_done(int rid){
    86    191     /* assert( bag_find(&bagDone, rid)==0 ); */
    87    192     bag_insert(&bagDone, rid);
    88    193     if( ttyOutput ){
    89    194       processCnt++;
    90         -    if (!g.fQuiet) {
    91         -      printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
    92         -      fflush(stdout);
          195  +    if (!g.fQuiet && totalSize>0) {
          196  +      percent_complete((processCnt*1000)/totalSize);
    93    197       }
    94    198     }
    95    199   }
    96    200   
    97    201   /*
    98    202   ** Rebuild cross-referencing information for the artifact
    99    203   ** rid with content pBase and all of its descendants.  This
   100    204   ** routine clears the content buffer before returning.
          205  +**
          206  +** If the zFNameFormat variable is set, then this routine is
          207  +** called to run "fossil deconstruct" instead of the usual
          208  +** "fossil rebuild".  In that case, instead of rebuilding the
          209  +** cross-referencing information, write the file content out
          210  +** to the appropriate directory.
          211  +**
          212  +** In both cases, this routine automatically recurses to process
          213  +** other artifacts that are deltas off of the current artifact.
          214  +** This is the most efficient way to extract all of the original
          215  +** artifact content from the Fossil repository.
   101    216   */
   102    217   static void rebuild_step(int rid, int size, Blob *pBase){
   103         -  Stmt q1;
          218  +  static Stmt q1;
   104    219     Bag children;
   105    220     Blob copy;
   106    221     Blob *pUse;
   107    222     int nChild, i, cid;
   108    223   
   109         -  /* Fix up the "blob.size" field if needed. */
   110         -  if( size!=blob_size(pBase) ){
   111         -    db_multi_exec(
   112         -       "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
   113         -    );
   114         -  }
   115         -
   116         -  /* Find all children of artifact rid */
   117         -  db_prepare(&q1, "SELECT rid FROM delta WHERE srcid=%d", rid);
   118         -  bag_init(&children);
   119         -  while( db_step(&q1)==SQLITE_ROW ){
   120         -    int cid = db_column_int(&q1, 0);
   121         -    if( !bag_find(&bagDone, cid) ){
   122         -      bag_insert(&children, cid);
   123         -    }
   124         -  }
   125         -  nChild = bag_count(&children);
   126         -  db_finalize(&q1);
   127         -
   128         -  /* Crosslink the artifact */
   129         -  if( nChild==0 ){
   130         -    pUse = pBase;
   131         -  }else{
   132         -    blob_copy(&copy, pBase);
   133         -    pUse = &copy;
   134         -  }
   135         -  manifest_crosslink(rid, pUse);
   136         -  blob_reset(pUse);
   137         -
   138         -  /* Call all children recursively */
   139         -  for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
   140         -    Stmt q2;
   141         -    int sz;
   142         -    if( nChild==i ){
          224  +  while( rid>0 ){
          225  +
          226  +    /* Fix up the "blob.size" field if needed. */
          227  +    if( size!=blob_size(pBase) ){
          228  +      db_multi_exec(
          229  +         "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
          230  +      );
          231  +    }
          232  +  
          233  +    /* Find all children of artifact rid */
          234  +    db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
          235  +    db_bind_int(&q1, ":rid", rid);
          236  +    bag_init(&children);
          237  +    while( db_step(&q1)==SQLITE_ROW ){
          238  +      int cid = db_column_int(&q1, 0);
          239  +      if( !bag_find(&bagDone, cid) ){
          240  +        bag_insert(&children, cid);
          241  +      }
          242  +    }
          243  +    nChild = bag_count(&children);
          244  +    db_reset(&q1);
          245  +  
          246  +    /* Crosslink the artifact */
          247  +    if( nChild==0 ){
   143    248         pUse = pBase;
   144    249       }else{
   145    250         blob_copy(&copy, pBase);
   146    251         pUse = &copy;
   147    252       }
   148         -    db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
   149         -    if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
   150         -      Blob delta;
   151         -      db_ephemeral_blob(&q2, 0, &delta);
   152         -      blob_uncompress(&delta, &delta);
   153         -      blob_delta_apply(pUse, &delta, pUse);
   154         -      blob_reset(&delta);
   155         -      db_finalize(&q2);
   156         -      rebuild_step(cid, sz, pUse);
          253  +    if( zFNameFormat==0 ){
          254  +      /* We are doing "fossil rebuild" */
          255  +      manifest_crosslink(rid, pUse);
   157    256       }else{
   158         -      db_finalize(&q2);
          257  +      /* We are doing "fossil deconstruct" */
          258  +      char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          259  +      char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
          260  +      blob_write_to_file(pUse,zFile);
          261  +      free(zFile);
          262  +      free(zUuid);
   159    263         blob_reset(pUse);
   160    264       }
          265  +    assert( blob_is_reset(pUse) );
          266  +    rebuild_step_done(rid);
          267  +  
          268  +    /* Call all children recursively */
          269  +    rid = 0;
          270  +    for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
          271  +      static Stmt q2;
          272  +      int sz;
          273  +      db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
          274  +      db_bind_int(&q2, ":rid", cid);
          275  +      if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
          276  +        Blob delta, next;
          277  +        db_ephemeral_blob(&q2, 0, &delta);
          278  +        blob_uncompress(&delta, &delta);
          279  +        blob_delta_apply(pBase, &delta, &next);
          280  +        blob_reset(&delta);
          281  +        db_reset(&q2);
          282  +        if( i<nChild ){
          283  +          rebuild_step(cid, sz, &next);
          284  +        }else{
          285  +          /* Tail recursion */
          286  +          rid = cid;
          287  +          size = sz;
          288  +          blob_reset(pBase);
          289  +          *pBase = next;
          290  +        }
          291  +      }else{
          292  +        db_reset(&q2);
          293  +        blob_reset(pBase);
          294  +      }
          295  +    }
          296  +    bag_clear(&children);
   161    297     }
   162         -  bag_clear(&children);
   163         -  rebuild_step_done(rid);
   164    298   }
   165    299   
   166    300   /*
   167         -** Check to see if the the "sym-trunk" tag exists.  If not, create it
          301  +** Check to see if the "sym-trunk" tag exists.  If not, create it
   168    302   ** and attach it to the very first check-in.
   169    303   */
   170    304   static void rebuild_tag_trunk(void){
   171    305     int tagid = db_int(0, "SELECT 1 FROM tag WHERE tagname='sym-trunk'");
   172    306     int rid;
   173    307     char *zUuid;
   174    308   
................................................................................
   176    310     rid = db_int(0, "SELECT pid FROM plink AS x WHERE NOT EXISTS("
   177    311                     "  SELECT 1 FROM plink WHERE cid=x.pid)");
   178    312     if( rid==0 ) return;
   179    313   
   180    314     /* Add the trunk tag to the root of the whole tree */
   181    315     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   182    316     if( zUuid==0 ) return;
   183         -  tag_add_artifact("sym-", "trunk", zUuid, 0, 2);
   184         -  tag_add_artifact("", "branch", zUuid, "trunk", 2);
          317  +  tag_add_artifact("sym-", "trunk", zUuid, 0, 2, 0, 0);
          318  +  tag_add_artifact("", "branch", zUuid, "trunk", 2, 0, 0);
   185    319   }
   186    320   
   187    321   /*
   188         -** Core function to rebuild the infomration in the derived tables of a
          322  +** Core function to rebuild the information in the derived tables of a
   189    323   ** fossil repository from the blobs. This function is shared between
   190    324   ** 'rebuild_database' ('rebuild') and 'reconstruct_cmd'
   191    325   ** ('reconstruct'), both of which have to regenerate this information
   192    326   ** from scratch.
   193    327   **
   194    328   ** If the randomize parameter is true, then the BLOBs are deliberately
   195    329   ** extracted in a random order.  This feature is used to test the
   196    330   ** ability of fossil to accept records in any order and still
   197    331   ** construct a sane repository.
   198    332   */
   199         -int rebuild_db(int randomize, int doOut){
          333  +int rebuild_db(int randomize, int doOut, int doClustering){
   200    334     Stmt s;
   201    335     int errCnt = 0;
   202    336     char *zTable;
          337  +  int incrSize;
   203    338   
   204    339     bag_init(&bagDone);
   205    340     ttyOutput = doOut;
   206    341     processCnt = 0;
   207         -  if (!g.fQuiet) {
   208         -    printf("0 (0%%)...\r");
   209         -    fflush(stdout);
          342  +  if (ttyOutput && !g.fQuiet) {
          343  +    percent_complete(0);
   210    344     }
   211         -  db_multi_exec(zSchemaUpdates);
          345  +  rebuild_update_schema();
   212    346     for(;;){
   213    347       zTable = db_text(0,
   214         -       "SELECT name FROM sqlite_master"
          348  +       "SELECT name FROM sqlite_master /*scan*/"
   215    349          " WHERE type='table'"
   216    350          " AND name NOT IN ('blob','delta','rcvfrom','user',"
   217    351                            "'config','shun','private','reportfmt',"
   218         -                         "'concealed')"
          352  +                         "'concealed','accesslog','modreq')"
          353  +       " AND name NOT GLOB 'sqlite_*'"
   219    354       );
   220    355       if( zTable==0 ) break;
   221    356       db_multi_exec("DROP TABLE %Q", zTable);
   222    357       free(zTable);
   223    358     }
   224    359     db_multi_exec(zRepositorySchema2);
   225    360     ticket_create_table(0);
................................................................................
   232    367     db_multi_exec(
   233    368        "DELETE FROM unclustered"
   234    369        " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
   235    370     );
   236    371     db_multi_exec(
   237    372       "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
   238    373     );
   239         -  totalSize = db_int(0, "SELECT count(*) FROM blob");
          374  +
          375  +  /* The following should be count(*) instead of max(rid). max(rid) is
          376  +  ** an adequate approximation, however, and is much faster for large
          377  +  ** repositories. */
          378  +  totalSize = db_int(0, "SELECT max(rid) FROM blob");
          379  +  incrSize = totalSize/100;
          380  +  totalSize += incrSize*2;
   240    381     db_prepare(&s,
   241         -     "SELECT rid, size FROM blob"
          382  +     "SELECT rid, size FROM blob /*scan*/"
   242    383        " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
   243    384        "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
   244    385     );
   245    386     manifest_crosslink_begin();
   246    387     while( db_step(&s)==SQLITE_ROW ){
   247    388       int rid = db_column_int(&s, 0);
   248    389       int size = db_column_int(&s, 1);
................................................................................
   270    411         db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
   271    412         rebuild_step_done(rid);
   272    413       }
   273    414     }
   274    415     db_finalize(&s);
   275    416     manifest_crosslink_end();
   276    417     rebuild_tag_trunk();
          418  +  if( ttyOutput && !g.fQuiet && totalSize>0 ){
          419  +    processCnt += incrSize;
          420  +    percent_complete((processCnt*1000)/totalSize);
          421  +  }
          422  +  if( doClustering ) create_cluster();
          423  +  if( ttyOutput && !g.fQuiet && totalSize>0 ){
          424  +    processCnt += incrSize;
          425  +    percent_complete((processCnt*1000)/totalSize);
          426  +  }
   277    427     if(!g.fQuiet && ttyOutput ){
   278         -    printf("\n");
          428  +    percent_complete(1000);
          429  +    fossil_print("\n");
   279    430     }
   280    431     return errCnt;
   281    432   }
   282    433   
   283    434   /*
   284         -** COMMAND:  rebuild
          435  +** Attempt to convert more full-text blobs into delta-blobs for
          436  +** storage efficiency.
          437  +*/
          438  +static void extra_deltification(void){
          439  +  Stmt q;
          440  +  int topid, previd, rid;
          441  +  int prevfnid, fnid;
          442  +  db_begin_transaction();
          443  +  db_prepare(&q,
          444  +     "SELECT rid FROM event, blob"
          445  +     " WHERE blob.rid=event.objid"
          446  +     "   AND event.type='ci'"
          447  +     "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
          448  +     " ORDER BY event.mtime DESC"
          449  +  );
          450  +  topid = previd = 0;
          451  +  while( db_step(&q)==SQLITE_ROW ){
          452  +    rid = db_column_int(&q, 0);
          453  +    if( topid==0 ){
          454  +      topid = previd = rid;
          455  +    }else{
          456  +      if( content_deltify(rid, previd, 0)==0 && previd!=topid ){
          457  +        content_deltify(rid, topid, 0);
          458  +      }
          459  +      previd = rid;
          460  +    }
          461  +  }
          462  +  db_finalize(&q);
          463  +
          464  +  db_prepare(&q,
          465  +     "SELECT blob.rid, mlink.fnid FROM blob, mlink, plink"
          466  +     " WHERE NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
          467  +     "   AND mlink.fid=blob.rid"
          468  +     "   AND mlink.mid=plink.cid"
          469  +     "   AND plink.cid=mlink.mid"
          470  +     " ORDER BY mlink.fnid, plink.mtime DESC"
          471  +  );
          472  +  prevfnid = 0;
          473  +  while( db_step(&q)==SQLITE_ROW ){
          474  +    rid = db_column_int(&q, 0);
          475  +    fnid = db_column_int(&q, 1);
          476  +    if( prevfnid!=fnid ){
          477  +      prevfnid = fnid;
          478  +      topid = previd = rid;
          479  +    }else{
          480  +      if( content_deltify(rid, previd, 0)==0 && previd!=topid ){
          481  +        content_deltify(rid, topid, 0);
          482  +      }
          483  +      previd = rid;
          484  +    }
          485  +  }
          486  +  db_finalize(&q);
          487  +
          488  +  db_end_transaction(0);
          489  +}
          490  +
          491  +
          492  +/* Reconstruct the private table.  The private table contains the rid
          493  +** of every manifest that is tagged with "private" and every file that
          494  +** is not used by a manifest that is not private.
          495  +*/
          496  +static void reconstruct_private_table(void){
          497  +  db_multi_exec(
          498  +    "CREATE TEMP TABLE private_ckin(rid INTEGER PRIMARY KEY);"
          499  +    "INSERT INTO private_ckin "
          500  +        " SELECT rid FROM tagxref WHERE tagid=%d AND tagtype>0;"
          501  +    "INSERT OR IGNORE INTO private"
          502  +        " SELECT fid FROM mlink"
          503  +        " EXCEPT SELECT fid FROM mlink WHERE mid NOT IN private_ckin;"
          504  +    "INSERT OR IGNORE INTO private SELECT rid FROM private_ckin;"
          505  +    "DROP TABLE private_ckin;", TAG_PRIVATE
          506  +  );
          507  +  fix_private_blob_dependencies(0);
          508  +}
          509  +
          510  +
          511  +/*
          512  +** COMMAND: rebuild
   285    513   **
   286         -** Usage: %fossil rebuild ?REPOSITORY?
          514  +** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS?
   287    515   **
   288    516   ** Reconstruct the named repository database from the core
   289    517   ** records.  Run this command after updating the fossil
   290    518   ** executable in a way that changes the database schema.
          519  +**
          520  +** Options:
          521  +**   --cluster     Compute clusters for unclustered artifacts
          522  +**   --compress    Strive to make the database as small as possible
          523  +**   --force       Force the rebuild to complete even if errors are seen
          524  +**   --noverify    Skip the verification of changes to the BLOB table
          525  +**   --pagesize N  Set the database pagesize to N. (512..65536 and power of 2)
          526  +**   --randomize   Scan artifacts in a random order
          527  +**   --vacuum      Run VACUUM on the database after rebuilding
          528  +**   --deanalyze   Remove ANALYZE tables from the database
          529  +**   --analyze     Run ANALYZE on the database after rebuilding
          530  +**   --wal         Set Write-Ahead-Log journalling mode on the database
          531  +**   --stats       Show artifact statistics after rebuilding
          532  +**
          533  +** See also: deconstruct, reconstruct
   291    534   */
   292    535   void rebuild_database(void){
   293    536     int forceFlag;
   294    537     int randomizeFlag;
   295    538     int errCnt;
          539  +  int omitVerify;
          540  +  int doClustering;
          541  +  const char *zPagesize;
          542  +  int newPagesize = 0;
          543  +  int activateWal;
          544  +  int runVacuum;
          545  +  int runDeanalyze;
          546  +  int runAnalyze;
          547  +  int runCompress;
          548  +  int showStats;
   296    549   
          550  +  omitVerify = find_option("noverify",0,0)!=0;
   297    551     forceFlag = find_option("force","f",0)!=0;
   298    552     randomizeFlag = find_option("randomize", 0, 0)!=0;
          553  +  doClustering = find_option("cluster", 0, 0)!=0;
          554  +  runVacuum = find_option("vacuum",0,0)!=0;
          555  +  runDeanalyze = find_option("deanalyze",0,0)!=0;
          556  +  runAnalyze = find_option("analyze",0,0)!=0;
          557  +  runCompress = find_option("compress",0,0)!=0;
          558  +  zPagesize = find_option("pagesize",0,1);
          559  +  showStats = find_option("stats",0,0)!=0;
          560  +  if( zPagesize ){
          561  +    newPagesize = atoi(zPagesize);
          562  +    if( newPagesize<512 || newPagesize>65536
          563  +        || (newPagesize&(newPagesize-1))!=0
          564  +    ){
          565  +      fossil_fatal("page size must be a power of two between 512 and 65536");
          566  +    }
          567  +  }
          568  +  activateWal = find_option("wal",0,0)!=0;
   299    569     if( g.argc==3 ){
   300    570       db_open_repository(g.argv[2]);
   301    571     }else{
   302         -    db_find_and_open_repository(1);
          572  +    db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
   303    573       if( g.argc!=2 ){
   304    574         usage("?REPOSITORY-FILENAME?");
   305    575       }
   306         -    db_close();
          576  +    db_close(1);
   307    577       db_open_repository(g.zRepositoryName);
   308    578     }
   309    579     db_begin_transaction();
   310    580     ttyOutput = 1;
   311         -  errCnt = rebuild_db(randomizeFlag, 1);
          581  +  errCnt = rebuild_db(randomizeFlag, 1, doClustering);
          582  +  reconstruct_private_table();
          583  +  db_multi_exec(
          584  +    "REPLACE INTO config(name,value,mtime) VALUES('content-schema','%s',now());"
          585  +    "REPLACE INTO config(name,value,mtime) VALUES('aux-schema','%s',now());",
          586  +    CONTENT_SCHEMA, AUX_SCHEMA
          587  +  );
   312    588     if( errCnt && !forceFlag ){
   313         -    printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
   314         -            errCnt);
          589  +    fossil_print(
          590  +      "%d errors. Rolling back changes. Use --force to force a commit.\n",
          591  +      errCnt
          592  +    );
   315    593       db_end_transaction(1);
   316    594     }else{
          595  +    if( runCompress ){
          596  +      fossil_print("Extra delta compression... "); fflush(stdout);
          597  +      extra_deltification();
          598  +      runVacuum = 1;
          599  +    }
          600  +    if( omitVerify ) verify_cancel();
   317    601       db_end_transaction(0);
          602  +    if( runCompress ) fossil_print("done\n");
          603  +    db_close(0);
          604  +    db_open_repository(g.zRepositoryName);
          605  +    if( newPagesize ){
          606  +      db_multi_exec("PRAGMA page_size=%d", newPagesize);
          607  +      runVacuum = 1;
          608  +    }
          609  +    if( runDeanalyze ){
          610  +      db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;"
          611  +                    "DROP TABLE IF EXISTS sqlite_stat3;");
          612  +    }
          613  +    if( runAnalyze ){
          614  +      fossil_print("Analyzing the database... "); fflush(stdout);
          615  +      db_multi_exec("ANALYZE;");
          616  +      fossil_print("done\n");
          617  +    }
          618  +    if( runVacuum ){
          619  +      fossil_print("Vacuuming the database... "); fflush(stdout);
          620  +      db_multi_exec("VACUUM");
          621  +      fossil_print("done\n");
          622  +    }
          623  +    if( activateWal ){
          624  +      db_multi_exec("PRAGMA journal_mode=WAL;");
          625  +    }
          626  +  }
          627  +  if( showStats ){
          628  +    static struct { int idx; const char *zLabel; } aStat[] = {
          629  +       { CFTYPE_ANY,       "Artifacts:" },
          630  +       { CFTYPE_MANIFEST,  "Manifests:" },
          631  +       { CFTYPE_CLUSTER,   "Clusters:" },
          632  +       { CFTYPE_CONTROL,   "Tags:" },
          633  +       { CFTYPE_WIKI,      "Wikis:" },
          634  +       { CFTYPE_TICKET,    "Tickets:" },
          635  +       { CFTYPE_ATTACHMENT,"Attachments:" },
          636  +       { CFTYPE_EVENT,     "Events:" },
          637  +    };
          638  +    int i;
          639  +    int subtotal = 0;
          640  +    for(i=0; i<count(aStat); i++){
          641  +      int k = aStat[i].idx;
          642  +      fossil_print("%-15s %6d\n", aStat[i].zLabel, g.parseCnt[k]);
          643  +      if( k>0 ) subtotal += g.parseCnt[k];
          644  +    }
          645  +    fossil_print("%-15s %6d\n", "Other:", g.parseCnt[CFTYPE_ANY] - subtotal);
   318    646     }
   319    647   }
   320    648   
   321    649   /*
   322         -** COMMAND:  test-detach
          650  +** COMMAND:  test-detach  ?REPOSITORY?
   323    651   **
   324    652   ** Change the project-code and make other changes in order to prevent
   325    653   ** the repository from ever again pushing or pulling to other
   326    654   ** repositories.  Used to create a "test" repository for development
   327    655   ** testing by cloning a working project repository.
   328    656   */
   329    657   void test_detach_cmd(void){
   330         -  db_find_and_open_repository(1);
          658  +  db_find_and_open_repository(0, 2);
   331    659     db_begin_transaction();
   332    660     db_multi_exec(
   333    661       "DELETE FROM config WHERE name='last-sync-url';"
   334    662       "UPDATE config SET value=lower(hex(randomblob(20)))"
   335    663       " WHERE name='project-code';"
   336    664       "UPDATE config SET value='detached-' || value"
   337    665       " WHERE name='project-name' AND value NOT GLOB 'detached-*';"
   338    666     );
   339    667     db_end_transaction(0);
   340    668   }
   341    669   
   342    670   /*
   343         -** COMMAND: scrub
   344         -** %fossil scrub [--verily] [--force] [REPOSITORY]
          671  +** COMMAND:  test-create-clusters
          672  +**
          673  +** Create clusters for all unclustered artifacts if the number of unclustered
          674  +** artifacts exceeds the current clustering threshold.
          675  +*/
          676  +void test_createcluster_cmd(void){
          677  +  if( g.argc==3 ){
          678  +    db_open_repository(g.argv[2]);
          679  +  }else{
          680  +    db_find_and_open_repository(0, 0);
          681  +    if( g.argc!=2 ){
          682  +      usage("?REPOSITORY-FILENAME?");
          683  +    }
          684  +    db_close(1);
          685  +    db_open_repository(g.zRepositoryName);
          686  +  }
          687  +  db_begin_transaction();
          688  +  create_cluster();
          689  +  db_end_transaction(0);  
          690  +}
          691  +
          692  +/*
          693  +** COMMAND: test-clusters
          694  +**
          695  +** Verify that all non-private and non-shunned artifacts are accessible
          696  +** through the cluster chain.
          697  +*/
          698  +void test_clusters_cmd(void){
          699  +  Bag pending;
          700  +  Stmt q;
          701  +  int n;
          702  +  
          703  +  db_find_and_open_repository(0, 2);
          704  +  bag_init(&pending);
          705  +  db_multi_exec(
          706  +    "CREATE TEMP TABLE xdone(x INTEGER PRIMARY KEY);"
          707  +    "INSERT INTO xdone SELECT rid FROM unclustered;"
          708  +    "INSERT OR IGNORE INTO xdone SELECT rid FROM private;"
          709  +    "INSERT OR IGNORE INTO xdone"
          710  +         " SELECT blob.rid FROM shun JOIN blob USING(uuid);"
          711  +  );
          712  +  db_prepare(&q,
          713  +    "SELECT rid FROM unclustered WHERE rid IN"
          714  +    " (SELECT rid FROM tagxref WHERE tagid=%d)", TAG_CLUSTER
          715  +  );
          716  +  while( db_step(&q)==SQLITE_ROW ){
          717  +    bag_insert(&pending, db_column_int(&q, 0));
          718  +  }
          719  +  db_finalize(&q);
          720  +  while( bag_count(&pending)>0 ){
          721  +    Manifest *p;
          722  +    int rid = bag_first(&pending);
          723  +    int i;
          724  +    
          725  +    bag_remove(&pending, rid);
          726  +    p = manifest_get(rid, CFTYPE_CLUSTER);
          727  +    if( p==0 ){
          728  +      fossil_fatal("bad cluster: rid=%d", rid);
          729  +    }
          730  +    for(i=0; i<p->nCChild; i++){
          731  +      const char *zUuid = p->azCChild[i];
          732  +      int crid = name_to_rid(zUuid);
          733  +      if( crid==0 ){
          734  +         fossil_warning("cluster (rid=%d) references unknown artifact %s",
          735  +                        rid, zUuid);
          736  +         continue;
          737  +      }
          738  +      db_multi_exec("INSERT OR IGNORE INTO xdone VALUES(%d)", crid);
          739  +      if( db_exists("SELECT 1 FROM tagxref WHERE tagid=%d AND rid=%d",
          740  +                    TAG_CLUSTER, crid) ){
          741  +        bag_insert(&pending, crid);
          742  +      }
          743  +    }
          744  +    manifest_destroy(p);
          745  +  }
          746  +  n = db_int(0, "SELECT count(*) FROM /*scan*/"
          747  +                "  (SELECT rid FROM blob EXCEPT SELECT x FROM xdone)");
          748  +  if( n==0 ){
          749  +    fossil_print("all artifacts reachable through clusters\n");
          750  +  }else{
          751  +    fossil_print("%d unreachable artifacts:\n", n);
          752  +    db_prepare(&q, "SELECT rid, uuid FROM blob WHERE rid NOT IN xdone");
          753  +    while( db_step(&q)==SQLITE_ROW ){
          754  +      fossil_print("  %3d %s\n", db_column_int(&q,0), db_column_text(&q,1));
          755  +    }
          756  +    db_finalize(&q);
          757  +  }
          758  +}
          759  +
          760  +/*
          761  +** COMMAND: scrub*
          762  +** %fossil scrub ?OPTIONS? ?REPOSITORY?
   345    763   **
   346    764   ** The command removes sensitive information (such as passwords) from a
   347         -** repository so that the respository can be sent to an untrusted reader.
          765  +** repository so that the repository can be sent to an untrusted reader.
   348    766   **
   349    767   ** By default, only passwords are removed.  However, if the --verily option
   350    768   ** is added, then private branches, concealed email addresses, IP
   351    769   ** addresses of correspondents, and similar privacy-sensitive fields
   352         -** are also purged.
          770  +** are also purged.  If the --private option is used, then only private
          771  +** branches are removed and all other information is left intact.
   353    772   **
   354         -** This command permanently deletes the scrubbed information.  The effects
   355         -** of this command are irreversible.  Use with caution.
          773  +** This command permanently deletes the scrubbed information. THE EFFECTS
          774  +** OF THIS COMMAND ARE IRREVERSIBLE. USE WITH CAUTION!
   356    775   **
   357    776   ** The user is prompted to confirm the scrub unless the --force option
   358    777   ** is used.
          778  +**
          779  +** Options:
          780  +**   --force     do not prompt for confirmation
          781  +**   --private   only private branches are removed from the repository
          782  +**   --verily    scrub real thoroughly (see above)
   359    783   */
   360    784   void scrub_cmd(void){
   361    785     int bVerily = find_option("verily",0,0)!=0;
   362    786     int bForce = find_option("force", "f", 0)!=0;
          787  +  int privateOnly = find_option("private",0,0)!=0;
   363    788     int bNeedRebuild = 0;
   364         -  if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
   365         -  if( g.argc==2 ){
   366         -    db_must_be_within_tree();
   367         -  }else{
   368         -    db_open_repository(g.argv[2]);
   369         -  }
          789  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 2);
   370    790     if( !bForce ){
   371    791       Blob ans;
          792  +    char cReply;
   372    793       blob_zero(&ans);
   373         -    prompt_user("Scrubbing the repository will permanently remove user\n"
   374         -                "passwords and other information. Changes cannot be undone.\n"
   375         -                "Continue (y/N)? ", &ans);
   376         -    if( blob_str(&ans)[0]!='y' ){
   377         -      exit(1);
          794  +    prompt_user(
          795  +         "Scrubbing the repository will permanently delete information.\n"
          796  +         "Changes cannot be undone.  Continue (y/N)? ", &ans);
          797  +    cReply = blob_str(&ans)[0];
          798  +    if( cReply!='y' && cReply!='Y' ){
          799  +      fossil_exit(1);
   378    800       }
   379    801     }
   380    802     db_begin_transaction();
   381         -  db_multi_exec(
   382         -    "UPDATE user SET pw='';"
   383         -    "DELETE FROM config WHERE name GLOB 'last-sync-*';"
   384         -  );
   385         -  if( bVerily ){
          803  +  if( privateOnly || bVerily ){
   386    804       bNeedRebuild = db_exists("SELECT 1 FROM private");
          805  +    delete_private_content();
          806  +  }
          807  +  if( !privateOnly ){
   387    808       db_multi_exec(
   388         -      "DELETE FROM concealed;"
   389         -      "UPDATE rcvfrom SET ipaddr='unknown';"
   390         -      "UPDATE user SET photo=NULL, info='';"
   391         -      "INSERT INTO shun SELECT uuid FROM blob WHERE rid IN private;"
          809  +      "UPDATE user SET pw='';"
          810  +      "DELETE FROM config WHERE name GLOB 'last-sync-*';"
          811  +      "DELETE FROM config WHERE name GLOB 'peer-*';"
          812  +      "DELETE FROM config WHERE name GLOB 'login-group-*';"
          813  +      "DELETE FROM config WHERE name GLOB 'skin:*';"
          814  +      "DELETE FROM config WHERE name GLOB 'subrepo:*';"
   392    815       );
          816  +    if( bVerily ){
          817  +      db_multi_exec(
          818  +        "DELETE FROM concealed;"
          819  +        "UPDATE rcvfrom SET ipaddr='unknown';"
          820  +        "DROP TABLE IF EXISTS accesslog;"
          821  +        "UPDATE user SET photo=NULL, info='';"
          822  +      );
          823  +    }
   393    824     }
   394    825     if( !bNeedRebuild ){
   395    826       db_end_transaction(0);
   396    827       db_multi_exec("VACUUM;");
   397    828     }else{
   398         -    rebuild_db(0, 1);
          829  +    rebuild_db(0, 1, 0);
   399    830       db_end_transaction(0);
   400    831     }
   401    832   }
          833  +
          834  +/*
          835  +** Recursively read all files from the directory zPath and install
          836  +** every file read as a new artifact in the repository.
          837  +*/
          838  +void recon_read_dir(char *zPath){
          839  +  DIR *d;
          840  +  struct dirent *pEntry;
          841  +  Blob aContent; /* content of the just read artifact */
          842  +  static int nFileRead = 0;
          843  +  void *zUnicodePath;
          844  +  char *zUtf8Name;
          845  +
          846  +  zUnicodePath = fossil_utf8_to_unicode(zPath);
          847  +  d = opendir(zUnicodePath);
          848  +  if( d ){
          849  +    while( (pEntry=readdir(d))!=0 ){
          850  +      Blob path;
          851  +      char *zSubpath;
          852  +
          853  +      if( pEntry->d_name[0]=='.' ){
          854  +        continue;
          855  +      }
          856  +      zUtf8Name = fossil_filename_to_utf8(pEntry->d_name);
          857  +      zSubpath = mprintf("%s/%s", zPath, zUtf8Name);
          858  +      fossil_filename_free(zUtf8Name);
          859  +      if( file_isdir(zSubpath)==1 ){
          860  +        recon_read_dir(zSubpath);
          861  +      }
          862  +      blob_init(&path, 0, 0);
          863  +      blob_appendf(&path, "%s", zSubpath);
          864  +      if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
          865  +        fossil_panic("some unknown error occurred while reading \"%s\"", 
          866  +                     blob_str(&path));
          867  +      }
          868  +      content_put(&aContent);
          869  +      blob_reset(&path);
          870  +      blob_reset(&aContent);
          871  +      free(zSubpath);
          872  +      fossil_print("\r%d", ++nFileRead);
          873  +      fflush(stdout);
          874  +    }
          875  +    closedir(d);
          876  +  }else {
          877  +    fossil_panic("encountered error %d while trying to open \"%s\".",
          878  +                  errno, g.argv[3]);
          879  +  }
          880  +  fossil_unicode_free(zUnicodePath);
          881  +}
          882  +
          883  +/*
          884  +** COMMAND: reconstruct*
          885  +**
          886  +** Usage: %fossil reconstruct FILENAME DIRECTORY
          887  +**
          888  +** This command studies the artifacts (files) in DIRECTORY and
          889  +** reconstructs the fossil record from them. It places the new
          890  +** fossil repository in FILENAME. Subdirectories are read, files
          891  +** with leading '.' in the filename are ignored.
          892  +**
          893  +** See also: deconstruct, rebuild
          894  +*/
          895  +void reconstruct_cmd(void) {
          896  +  char *zPassword;
          897  +  if( g.argc!=4 ){
          898  +    usage("FILENAME DIRECTORY");
          899  +  }
          900  +  if( file_isdir(g.argv[3])!=1 ){
          901  +    fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
          902  +    usage("FILENAME DIRECTORY");
          903  +  }
          904  +  db_create_repository(g.argv[2]);
          905  +  db_open_repository(g.argv[2]);
          906  +  db_open_config(0);
          907  +  db_begin_transaction();
          908  +  db_initial_setup(0, 0, 0, 1);
          909  +
          910  +  fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
          911  +  recon_read_dir(g.argv[3]);
          912  +  fossil_print("\nBuilding the Fossil repository...\n");
          913  +
          914  +  rebuild_db(0, 1, 1);
          915  +  reconstruct_private_table();
          916  +
          917  +  /* Skip the verify_before_commit() step on a reconstruct.  Most artifacts
          918  +  ** will have been changed and verification therefore takes a really, really
          919  +  ** long time.
          920  +  */
          921  +  verify_cancel();
          922  +  
          923  +  db_end_transaction(0);
          924  +  fossil_print("project-id: %s\n", db_get("project-code", 0));
          925  +  fossil_print("server-id: %s\n", db_get("server-code", 0));
          926  +  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
          927  +  fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
          928  +}
          929  +
          930  +/*
          931  +** COMMAND: deconstruct*
          932  +**
          933  +** Usage %fossil deconstruct ?OPTIONS? DESTINATION
          934  +**
          935  +**
          936  +** This command exports all artifacts of a given repository and
          937  +** writes all artifacts to the file system. The DESTINATION directory
          938  +** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
          939  +** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters.
          940  +** If -L|--prefixlength is given, the length (default 2) of the directory
          941  +** prefix can be set to 0,1,..,9 characters.
          942  +** 
          943  +** Options:
          944  +**   -R|--repository REPOSITORY  deconstruct given REPOSITORY
          945  +**   -L|--prefixlength N         set the length of the names of the DESTINATION
          946  +**                               subdirectories to N
          947  +**   --private                   Include private artifacts.
          948  +**
          949  +** See also: rebuild, reconstruct
          950  +*/
          951  +void deconstruct_cmd(void){
          952  +  const char *zDestDir;
          953  +  const char *zPrefixOpt;
          954  +  Stmt        s;
          955  +  int privateFlag;
          956  +
          957  +  /* get and check prefix length argument and build format string */
          958  +  zPrefixOpt=find_option("prefixlength","L",1);
          959  +  if( !zPrefixOpt ){
          960  +    prefixLength = 2;
          961  +  }else{
          962  +    if( zPrefixOpt[0]>='0' && zPrefixOpt[0]<='9' && !zPrefixOpt[1] ){
          963  +      prefixLength = (int)(*zPrefixOpt-'0');
          964  +    }else{
          965  +      fossil_fatal("N(%s) is not a valid prefix length!",zPrefixOpt);
          966  +    }
          967  +  }
          968  +  /* open repository and open query for all artifacts */
          969  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
          970  +  privateFlag = find_option("private",0,0)!=0;
          971  +  verify_all_options();
          972  +  /* check number of arguments */
          973  +  if( g.argc!=3 ){
          974  +    usage ("?OPTIONS? DESTINATION");
          975  +  }
          976  +  /* get and check argument destination directory */
          977  +  zDestDir = g.argv[g.argc-1];
          978  +  if( !*zDestDir  || !file_isdir(zDestDir)) {
          979  +    fossil_fatal("DESTINATION(%s) is not a directory!",zDestDir);
          980  +  }
          981  +#ifndef _WIN32
          982  +  if( file_access(zDestDir, W_OK) ){
          983  +    fossil_fatal("DESTINATION(%s) is not writeable!",zDestDir);
          984  +  }
          985  +#else
          986  +  /* write access on windows is not checked, errors will be
          987  +  ** detected on blob_write_to_file
          988  +  */
          989  +#endif
          990  +  if( prefixLength ){
          991  +    zFNameFormat = mprintf("%s/%%.%ds/%%s",zDestDir,prefixLength);
          992  +  }else{
          993  +    zFNameFormat = mprintf("%s/%%s",zDestDir);
          994  +  }
          995  +
          996  +  bag_init(&bagDone);
          997  +  ttyOutput = 1;
          998  +  processCnt = 0;
          999  +  if (!g.fQuiet) {
         1000  +    fossil_print("0 (0%%)...\r");
         1001  +    fflush(stdout);
         1002  +  }
         1003  +  totalSize = db_int(0, "SELECT count(*) FROM blob");
         1004  +  db_prepare(&s,
         1005  +     "SELECT rid, size FROM blob /*scan*/"
         1006  +     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
         1007  +     "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid) %s",
         1008  +     privateFlag==0 ? "AND rid NOT IN private" : ""
         1009  +  );
         1010  +  while( db_step(&s)==SQLITE_ROW ){
         1011  +    int rid = db_column_int(&s, 0);
         1012  +    int size = db_column_int(&s, 1);
         1013  +    if( size>=0 ){
         1014  +      Blob content;
         1015  +      content_get(rid, &content);
         1016  +      rebuild_step(rid, size, &content);
         1017  +    }
         1018  +  }
         1019  +  db_finalize(&s);
         1020  +  db_prepare(&s,
         1021  +     "SELECT rid, size FROM blob"
         1022  +     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s",
         1023  +     privateFlag==0 ? "AND rid NOT IN private" : ""
         1024  +  );
         1025  +  while( db_step(&s)==SQLITE_ROW ){
         1026  +    int rid = db_column_int(&s, 0);
         1027  +    int size = db_column_int(&s, 1);
         1028  +    if( size>=0 ){
         1029  +      if( !bag_find(&bagDone, rid) ){
         1030  +        Blob content;
         1031  +        content_get(rid, &content);
         1032  +        rebuild_step(rid, size, &content);
         1033  +      }
         1034  +    }
         1035  +  }
         1036  +  db_finalize(&s);
         1037  +  if(!g.fQuiet && ttyOutput ){
         1038  +    fossil_print("\n");
         1039  +  }
         1040  +
         1041  +  /* free filename format string */
         1042  +  free(zFNameFormat);
         1043  +  zFNameFormat = 0;
         1044  +}

Added src/regexp.c.

            1  +/*
            2  +** Copyright (c) 2013 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +**
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file was adapted from the test_regexp.c file in SQLite3.  That
           19  +** file is in the public domain.
           20  +**
           21  +** The code in this file implements a compact but reasonably
           22  +** efficient regular-expression matcher for posix extended regular
           23  +** expressions against UTF8 text.  The following syntax is supported:
           24  +**
           25  +**     X*      zero or more occurrences of X
           26  +**     X+      one or more occurrences of X
           27  +**     X?      zero or one occurrences of X
           28  +**     X{p,q}  between p and q occurrences of X
           29  +**     (X)     match X
           30  +**     X|Y     X or Y
           31  +**     ^X      X occurring at the beginning of the string
           32  +**     X$      X occurring at the end of the string
           33  +**     .       Match any single character
           34  +**     \c      Character c where c is one of \{}()[]|*+?.
           35  +**     \c      C-language escapes for c in afnrtv.  ex: \t or \n
           36  +**     \uXXXX  Where XXXX is exactly 4 hex digits, unicode value XXXX
           37  +**     \xXX    Where XX is exactly 2 hex digits, unicode value XX
           38  +**     [abc]   Any single character from the set abc
           39  +**     [^abc]  Any single character not in the set abc
           40  +**     [a-z]   Any single character in the range a-z
           41  +**     [^a-z]  Any single character not in the range a-z
           42  +**     \b      Word boundary
           43  +**     \w      Word character.  [A-Za-z0-9_]
           44  +**     \W      Non-word character
           45  +**     \d      Digit
           46  +**     \D      Non-digit
           47  +**     \s      Whitespace character
           48  +**     \S      Non-whitespace character
           49  +**
           50  +** A nondeterministic finite automaton (NFA) is used for matching, so the
           51  +** performance is bounded by O(N*M) where N is the size of the regular
           52  +** expression and M is the size of the input string.  The matcher never
           53  +** exhibits exponential behavior.  Note that the X{p,q} operator expands
           54  +** to p copies of X following by q-p copies of X? and that the size of the
           55  +** regular expression in the O(N*M) performance bound is computed after
           56  +** this expansion.
           57  +*/
           58  +#include "config.h"
           59  +#include "regexp.h"
           60  +
           61  +/* The end-of-input character */
           62  +#define RE_EOF            0    /* End of input */
           63  +
           64  +/* The NFA is implemented as sequence of opcodes taken from the following
           65  +** set.  Each opcode has a single integer argument.
           66  +*/
           67  +#define RE_OP_MATCH       1    /* Match the one character in the argument */
           68  +#define RE_OP_ANY         2    /* Match any one character.  (Implements ".") */
           69  +#define RE_OP_ANYSTAR     3    /* Special optimized version of .* */
           70  +#define RE_OP_FORK        4    /* Continue to both next and opcode at iArg */
           71  +#define RE_OP_GOTO        5    /* Jump to opcode at iArg */
           72  +#define RE_OP_ACCEPT      6    /* Halt and indicate a successful match */
           73  +#define RE_OP_CC_INC      7    /* Beginning of a [...] character class */
           74  +#define RE_OP_CC_EXC      8    /* Beginning of a [^...] character class */
           75  +#define RE_OP_CC_VALUE    9    /* Single value in a character class */
           76  +#define RE_OP_CC_RANGE   10    /* Range of values in a character class */
           77  +#define RE_OP_WORD       11    /* Perl word character [A-Za-z0-9_] */
           78  +#define RE_OP_NOTWORD    12    /* Not a perl word character */
           79  +#define RE_OP_DIGIT      13    /* digit:  [0-9] */
           80  +#define RE_OP_NOTDIGIT   14    /* Not a digit */
           81  +#define RE_OP_SPACE      15    /* space:  [ \t\n\r\v\f] */
           82  +#define RE_OP_NOTSPACE   16    /* Not a digit */
           83  +#define RE_OP_BOUNDARY   17    /* Boundary between word and non-word */
           84  +
           85  +/* Each opcode is a "state" in the NFA */
           86  +typedef unsigned short ReStateNumber;
           87  +
           88  +/* Because this is an NFA and not a DFA, multiple states can be active at
           89  +** once.  An instance of the following object records all active states in
           90  +** the NFA.  The implementation is optimized for the common case where the
           91  +** number of actives states is small.
           92  +*/
           93  +typedef struct ReStateSet {
           94  +  unsigned nState;            /* Number of current states */
           95  +  ReStateNumber *aState;      /* Current states */
           96  +} ReStateSet;
           97  +
           98  +#if INTERFACE
           99  +/* An input string read one character at a time.
          100  +*/
          101  +struct ReInput {
          102  +  const unsigned char *z;  /* All text */
          103  +  int i;                   /* Next byte to read */
          104  +  int mx;                  /* EOF when i>=mx */
          105  +};
          106  +
          107  +/* A compiled NFA (or an NFA that is in the process of being compiled) is
          108  +** an instance of the following object.
          109  +*/
          110  +struct ReCompiled {
          111  +  ReInput sIn;                /* Regular expression text */
          112  +  const char *zErr;           /* Error message to return */
          113  +  char *aOp;                  /* Operators for the virtual machine */
          114  +  int *aArg;                  /* Arguments to each operator */
          115  +  unsigned (*xNextChar)(ReInput*);  /* Next character function */
          116  +  char zInit[12];             /* Initial text to match */
          117  +  int nInit;                  /* Number of characters in zInit */
          118  +  unsigned nState;            /* Number of entries in aOp[] and aArg[] */
          119  +  unsigned nAlloc;            /* Slots allocated for aOp[] and aArg[] */
          120  +};
          121  +#endif
          122  +
          123  +/* Add a state to the given state set if it is not already there */
          124  +static void re_add_state(ReStateSet *pSet, int newState){
          125  +  unsigned i;
          126  +  for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
          127  +  pSet->aState[pSet->nState++] = newState;
          128  +}
          129  +
          130  +/* Extract the next unicode character from *pzIn and return it.  Advance
          131  +** *pzIn to the first byte past the end of the character returned.  To
          132  +** be clear:  this routine converts utf8 to unicode.  This routine is 
          133  +** optimized for the common case where the next character is a single byte.
          134  +*/
          135  +static unsigned re_next_char(ReInput *p){
          136  +  unsigned c;
          137  +  if( p->i>=p->mx ) return 0;
          138  +  c = p->z[p->i++];
          139  +  if( c>=0x80 ){
          140  +    if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
          141  +      c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
          142  +      if( c<0x80 ) c = 0xfffd;
          143  +    }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
          144  +           && (p->z[p->i+1]&0xc0)==0x80 ){
          145  +      c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
          146  +      p->i += 2;
          147  +      if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
          148  +    }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
          149  +           && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
          150  +      c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
          151  +                       | (p->z[p->i+2]&0x3f);
          152  +      p->i += 3;
          153  +      if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
          154  +    }else{
          155  +      c = 0xfffd;
          156  +    }
          157  +  }
          158  +  return c;
          159  +}
          160  +static unsigned re_next_char_nocase(ReInput *p){
          161  +  unsigned c = re_next_char(p);
          162  +  return unicode_fold(c,1);
          163  +}
          164  +
          165  +/* Return true if c is a perl "word" character:  [A-Za-z0-9_] */
          166  +static int re_word_char(int c){
          167  +  return unicode_isalnum(c) || c=='_';
          168  +}
          169  +
          170  +/* Return true if c is a "digit" character:  [0-9] */
          171  +static int re_digit_char(int c){
          172  +  return (c>='0' && c<='9');
          173  +}
          174  +
          175  +/* Return true if c is a perl "space" character:  [ \t\r\n\v\f] */
          176  +static int re_space_char(int c){
          177  +  return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
          178  +}
          179  +
          180  +/* Run a compiled regular expression on the zero-terminated input
          181  +** string zIn[].  Return true on a match and false if there is no match.
          182  +*/
          183  +int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
          184  +  ReStateSet aStateSet[2], *pThis, *pNext;
          185  +  ReStateNumber aSpace[100];
          186  +  ReStateNumber *pToFree;
          187  +  unsigned int i = 0;
          188  +  unsigned int iSwap = 0;
          189  +  int c = RE_EOF+1;
          190  +  int cPrev = 0;
          191  +  int rc = 0;
          192  +  ReInput in;
          193  +
          194  +  in.z = zIn;
          195  +  in.i = 0;
          196  +  in.mx = nIn>=0 ? nIn : strlen((char const*)zIn);
          197  +
          198  +  /* Look for the initial prefix match, if there is one. */
          199  +  if( pRe->nInit ){
          200  +    unsigned char x = pRe->zInit[0];
          201  +    while( in.i+pRe->nInit<=in.mx 
          202  +        && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0)
          203  +    ){
          204  +      in.i++;
          205  +    }
          206  +    if( in.i+pRe->nInit>in.mx ) return 0;
          207  +  }
          208  +
          209  +  if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
          210  +    pToFree = 0;
          211  +    aStateSet[0].aState = aSpace;
          212  +  }else{
          213  +    pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState );
          214  +    if( pToFree==0 ) return -1;
          215  +    aStateSet[0].aState = pToFree;
          216  +  }
          217  +  aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
          218  +  pNext = &aStateSet[1];
          219  +  pNext->nState = 0;
          220  +  re_add_state(pNext, 0);
          221  +  while( c!=RE_EOF && pNext->nState>0 ){
          222  +    cPrev = c;
          223  +    c = pRe->xNextChar(&in);
          224  +    pThis = pNext;
          225  +    pNext = &aStateSet[iSwap];
          226  +    iSwap = 1 - iSwap;
          227  +    pNext->nState = 0;
          228  +    for(i=0; i<pThis->nState; i++){
          229  +      int x = pThis->aState[i];
          230  +      switch( pRe->aOp[x] ){
          231  +        case RE_OP_MATCH: {
          232  +          if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
          233  +          break;
          234  +        }
          235  +        case RE_OP_ANY: {
          236  +          re_add_state(pNext, x+1);
          237  +          break;
          238  +        }
          239  +        case RE_OP_WORD: {
          240  +          if( re_word_char(c) ) re_add_state(pNext, x+1);
          241  +          break;
          242  +        }
          243  +        case RE_OP_NOTWORD: {
          244  +          if( !re_word_char(c) ) re_add_state(pNext, x+1);
          245  +          break;
          246  +        }
          247  +        case RE_OP_DIGIT: {
          248  +          if( re_digit_char(c) ) re_add_state(pNext, x+1);
          249  +          break;
          250  +        }
          251  +        case RE_OP_NOTDIGIT: {
          252  +          if( !re_digit_char(c) ) re_add_state(pNext, x+1);
          253  +          break;
          254  +        }
          255  +        case RE_OP_SPACE: {
          256  +          if( re_space_char(c) ) re_add_state(pNext, x+1);
          257  +          break;
          258  +        }
          259  +        case RE_OP_NOTSPACE: {
          260  +          if( !re_space_char(c) ) re_add_state(pNext, x+1);
          261  +          break;
          262  +        }
          263  +        case RE_OP_BOUNDARY: {
          264  +          if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
          265  +          break;
          266  +        }
          267  +        case RE_OP_ANYSTAR: {
          268  +          re_add_state(pNext, x);
          269  +          re_add_state(pThis, x+1);
          270  +          break;
          271  +        }
          272  +        case RE_OP_FORK: {
          273  +          re_add_state(pThis, x+pRe->aArg[x]);
          274  +          re_add_state(pThis, x+1);
          275  +          break;
          276  +        }
          277  +        case RE_OP_GOTO: {
          278  +          re_add_state(pThis, x+pRe->aArg[x]);
          279  +          break;
          280  +        }
          281  +        case RE_OP_ACCEPT: {
          282  +          rc = 1;
          283  +          goto re_match_end;
          284  +        }
          285  +        case RE_OP_CC_INC:
          286  +        case RE_OP_CC_EXC: {
          287  +          int j = 1;
          288  +          int n = pRe->aArg[x];
          289  +          int hit = 0;
          290  +          for(j=1; j>0 && j<n; j++){
          291  +            if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
          292  +              if( pRe->aArg[x+j]==c ){
          293  +                hit = 1;
          294  +                j = -1;
          295  +              }
          296  +            }else{
          297  +              if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
          298  +                hit = 1;
          299  +                j = -1;
          300  +              }else{
          301  +                j++;
          302  +              }
          303  +            }
          304  +          }
          305  +          if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
          306  +          if( hit ) re_add_state(pNext, x+n);
          307  +          break;            
          308  +        }
          309  +      }
          310  +    }
          311  +  }
          312  +  for(i=0; i<pNext->nState; i++){
          313  +    if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
          314  +  }
          315  +re_match_end:
          316  +  fossil_free(pToFree);
          317  +  return rc;
          318  +}
          319  +
          320  +/* Resize the opcode and argument arrays for an RE under construction.
          321  +*/
          322  +static int re_resize(ReCompiled *p, int N){
          323  +  char *aOp;
          324  +  int *aArg;
          325  +  aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
          326  +  if( aOp==0 ) return 1;
          327  +  p->aOp = aOp;
          328  +  aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
          329  +  if( aArg==0 ) return 1;
          330  +  p->aArg = aArg;
          331  +  p->nAlloc = N;
          332  +  return 0;
          333  +}
          334  +
          335  +/* Insert a new opcode and argument into an RE under construction.  The
          336  +** insertion point is just prior to existing opcode iBefore.
          337  +*/
          338  +static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
          339  +  int i;
          340  +  if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
          341  +  for(i=p->nState; i>iBefore; i--){
          342  +    p->aOp[i] = p->aOp[i-1];
          343  +    p->aArg[i] = p->aArg[i-1];
          344  +  }
          345  +  p->nState++;
          346  +  p->aOp[iBefore] = op;
          347  +  p->aArg[iBefore] = arg;
          348  +  return iBefore;
          349  +}
          350  +
          351  +/* Append a new opcode and argument to the end of the RE under construction.
          352  +*/
          353  +static int re_append(ReCompiled *p, int op, int arg){
          354  +  return re_insert(p, p->nState, op, arg);
          355  +}
          356  +
          357  +/* Make a copy of N opcodes starting at iStart onto the end of the RE
          358  +** under construction.
          359  +*/
          360  +static void re_copy(ReCompiled *p, int iStart, int N){
          361  +  if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
          362  +  memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
          363  +  memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
          364  +  p->nState += N;
          365  +}
          366  +
          367  +/* Return true if c is a hexadecimal digit character:  [0-9a-fA-F]
          368  +** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c).  If
          369  +** c is not a hex digit *pV is unchanged.
          370  +*/
          371  +static int re_hex(int c, int *pV){
          372  +  if( c>='0' && c<='9' ){
          373  +    c -= '0';
          374  +  }else if( c>='a' && c<='f' ){
          375  +    c -= 'a' - 10;
          376  +  }else if( c>='A' && c<='F' ){
          377  +    c -= 'A' - 10;
          378  +  }else{
          379  +    return 0;
          380  +  }
          381  +  *pV = (*pV)*16 + (c & 0xff);
          382  +  return 1;
          383  +}
          384  +
          385  +/* A backslash character has been seen, read the next character and
          386  +** return its interpretation.
          387  +*/
          388  +static unsigned re_esc_char(ReCompiled *p){
          389  +  static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
          390  +  static const char zTrans[] = "\a\f\n\r\t\v";
          391  +  int i, v = 0;
          392  +  char c;
          393  +  if( p->sIn.i>=p->sIn.mx ) return 0;
          394  +  c = p->sIn.z[p->sIn.i];
          395  +  if( c=='u' && p->sIn.i+4<p->sIn.mx ){
          396  +    const unsigned char *zIn = p->sIn.z + p->sIn.i;
          397  +    if( re_hex(zIn[1],&v)
          398  +     && re_hex(zIn[2],&v)
          399  +     && re_hex(zIn[3],&v)
          400  +     && re_hex(zIn[4],&v)
          401  +    ){
          402  +      p->sIn.i += 5;
          403  +      return v;
          404  +    }
          405  +  }
          406  +  if( c=='x' && p->sIn.i+2<p->sIn.mx ){
          407  +    const unsigned char *zIn = p->sIn.z + p->sIn.i;
          408  +    if( re_hex(zIn[1],&v)
          409  +     && re_hex(zIn[2],&v)
          410  +    ){
          411  +      p->sIn.i += 3;
          412  +      return v;
          413  +    }
          414  +  }
          415  +  for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
          416  +  if( zEsc[i] ){
          417  +    if( i<6 ) c = zTrans[i];
          418  +    p->sIn.i++;
          419  +  }else{
          420  +    p->zErr = "unknown \\ escape";
          421  +  }
          422  +  return c;
          423  +}
          424  +
          425  +/* Forward declaration */
          426  +static const char *re_subcompile_string(ReCompiled*);
          427  +
          428  +/* Peek at the next byte of input */
          429  +static unsigned char rePeek(ReCompiled *p){
          430  +  return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
          431  +}
          432  +
          433  +/* Compile RE text into a sequence of opcodes.  Continue up to the
          434  +** first unmatched ")" character, then return.  If an error is found,
          435  +** return a pointer to the error message string.
          436  +*/
          437  +static const char *re_subcompile_re(ReCompiled *p){
          438  +  const char *zErr;
          439  +  int iStart, iEnd, iGoto;
          440  +  iStart = p->nState;
          441  +  zErr = re_subcompile_string(p);
          442  +  if( zErr ) return zErr;
          443  +  while( rePeek(p)=='|' ){
          444  +    iEnd = p->nState;
          445  +    re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
          446  +    iGoto = re_append(p, RE_OP_GOTO, 0);
          447  +    p->sIn.i++;
          448  +    zErr = re_subcompile_string(p);
          449  +    if( zErr ) return zErr;
          450  +    p->aArg[iGoto] = p->nState - iGoto;
          451  +  }
          452  +  return 0;
          453  +}
          454  +
          455  +/* Compile an element of regular expression text (anything that can be
          456  +** an operand to the "|" operator).  Return NULL on success or a pointer
          457  +** to the error message if there is a problem.
          458  +*/
          459  +static const char *re_subcompile_string(ReCompiled *p){
          460  +  int iPrev = -1;
          461  +  int iStart;
          462  +  unsigned c;
          463  +  const char *zErr;
          464  +  while( (c = p->xNextChar(&p->sIn))!=0 ){
          465  +    iStart = p->nState;
          466  +    switch( c ){
          467  +      case '|':
          468  +      case '$': 
          469  +      case ')': {
          470  +        p->sIn.i--;
          471  +        return 0;
          472  +      }
          473  +      case '(': {
          474  +        zErr = re_subcompile_re(p);
          475  +        if( zErr ) return zErr;
          476  +        if( rePeek(p)!=')' ) return "unmatched '('";
          477  +        p->sIn.i++;
          478  +        break;
          479  +      }
          480  +      case '.': {
          481  +        if( rePeek(p)=='*' ){
          482  +          re_append(p, RE_OP_ANYSTAR, 0);
          483  +          p->sIn.i++;
          484  +        }else{ 
          485  +          re_append(p, RE_OP_ANY, 0);
          486  +        }
          487  +        break;
          488  +      }
          489  +      case '*': {
          490  +        if( iPrev<0 ) return "'*' without operand";
          491  +        re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
          492  +        re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
          493  +        break;
          494  +      }
          495  +      case '+': {
          496  +        if( iPrev<0 ) return "'+' without operand";
          497  +        re_append(p, RE_OP_FORK, iPrev - p->nState);
          498  +        break;
          499  +      }
          500  +      case '?': {
          501  +        if( iPrev<0 ) return "'?' without operand";
          502  +        re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
          503  +        break;
          504  +      }
          505  +      case '{': {
          506  +        int m = 0, n = 0;
          507  +        int sz, j;
          508  +        if( iPrev<0 ) return "'{m,n}' without operand";
          509  +        while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
          510  +        n = m;
          511  +        if( c==',' ){
          512  +          p->sIn.i++;
          513  +          n = 0;
          514  +          while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
          515  +        }
          516  +        if( c!='}' ) return "unmatched '{'";
          517  +        if( n>0 && n<m ) return "n less than m in '{m,n}'";
          518  +        p->sIn.i++;
          519  +        sz = p->nState - iPrev;
          520  +        if( m==0 ){
          521  +          if( n==0 ) return "both m and n are zero in '{m,n}'";
          522  +          re_insert(p, iPrev, RE_OP_FORK, sz+1);
          523  +          n--;
          524  +        }else{
          525  +          for(j=1; j<m; j++) re_copy(p, iPrev, sz);
          526  +        }
          527  +        for(j=m; j<n; j++){
          528  +          re_append(p, RE_OP_FORK, sz+1);
          529  +          re_copy(p, iPrev, sz);
          530  +        }
          531  +        if( n==0 && m>0 ){
          532  +          re_append(p, RE_OP_FORK, -sz);
          533  +        }
          534  +        break;
          535  +      }
          536  +      case '[': {
          537  +        int iFirst = p->nState;
          538  +        if( rePeek(p)=='^' ){
          539  +          re_append(p, RE_OP_CC_EXC, 0);
          540  +          p->sIn.i++;
          541  +        }else{
          542  +          re_append(p, RE_OP_CC_INC, 0);
          543  +        }
          544  +        while( (c = p->xNextChar(&p->sIn))!=0 ){
          545  +          if( c=='[' && rePeek(p)==':' ){
          546  +            return "POSIX character classes not supported";
          547  +          }
          548  +          if( c=='\\' ) c = re_esc_char(p);
          549  +          if( rePeek(p)=='-' ){
          550  +            re_append(p, RE_OP_CC_RANGE, c);
          551  +            p->sIn.i++;
          552  +            c = p->xNextChar(&p->sIn);
          553  +            if( c=='\\' ) c = re_esc_char(p);
          554  +            re_append(p, RE_OP_CC_RANGE, c);
          555  +          }else{
          556  +            re_append(p, RE_OP_CC_VALUE, c);
          557  +          }
          558  +          if( rePeek(p)==']' ){ p->sIn.i++; break; }
          559  +        }
          560  +        if( c==0 ) return "unclosed '['";
          561  +        p->aArg[iFirst] = p->nState - iFirst;
          562  +        break;
          563  +      }
          564  +      case '\\': {
          565  +        int specialOp = 0;
          566  +        switch( rePeek(p) ){
          567  +          case 'b': specialOp = RE_OP_BOUNDARY;   break;
          568  +          case 'd': specialOp = RE_OP_DIGIT;      break;
          569  +          case 'D': specialOp = RE_OP_NOTDIGIT;   break;
          570  +          case 's': specialOp = RE_OP_SPACE;      break;
          571  +          case 'S': specialOp = RE_OP_NOTSPACE;   break;
          572  +          case 'w': specialOp = RE_OP_WORD;       break;
          573  +          case 'W': specialOp = RE_OP_NOTWORD;    break;
          574  +        }
          575  +        if( specialOp ){
          576  +          p->sIn.i++;
          577  +          re_append(p, specialOp, 0);
          578  +        }else{
          579  +          c = re_esc_char(p);
          580  +          re_append(p, RE_OP_MATCH, c);
          581  +        }
          582  +        break;
          583  +      }
          584  +      default: {
          585  +        re_append(p, RE_OP_MATCH, c);
          586  +        break;
          587  +      }
          588  +    }
          589  +    iPrev = iStart;
          590  +  }
          591  +  return 0;
          592  +}
          593  +
          594  +/* Free and reclaim all the memory used by a previously compiled
          595  +** regular expression.  Applications should invoke this routine once
          596  +** for every call to re_compile() to avoid memory leaks.
          597  +*/
          598  +void re_free(ReCompiled *pRe){
          599  +  if( pRe ){
          600  +    fossil_free(pRe->aOp);
          601  +    fossil_free(pRe->aArg);
          602  +    fossil_free(pRe);
          603  +  }
          604  +}
          605  +
          606  +/*
          607  +** Compile a textual regular expression in zIn[] into a compiled regular
          608  +** expression suitable for us by re_match() and return a pointer to the
          609  +** compiled regular expression in *ppRe.  Return NULL on success or an
          610  +** error message if something goes wrong.
          611  +*/
          612  +const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
          613  +  ReCompiled *pRe;
          614  +  const char *zErr;
          615  +  int i, j;
          616  +
          617  +  *ppRe = 0;
          618  +  pRe = fossil_malloc( sizeof(*pRe) );
          619  +  if( pRe==0 ){
          620  +    return "out of memory";
          621  +  }
          622  +  memset(pRe, 0, sizeof(*pRe));
          623  +  pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
          624  +  if( re_resize(pRe, 30) ){
          625  +    re_free(pRe);
          626  +    return "out of memory";
          627  +  }
          628  +  if( zIn[0]=='^' ){
          629  +    zIn++;
          630  +  }else{
          631  +    re_append(pRe, RE_OP_ANYSTAR, 0);
          632  +  }
          633  +  pRe->sIn.z = (unsigned char*)zIn;
          634  +  pRe->sIn.i = 0;
          635  +  pRe->sIn.mx = strlen(zIn);
          636  +  zErr = re_subcompile_re(pRe);
          637  +  if( zErr ){
          638  +    re_free(pRe);
          639  +    return zErr;
          640  +  }
          641  +  if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
          642  +    re_append(pRe, RE_OP_MATCH, RE_EOF);
          643  +    re_append(pRe, RE_OP_ACCEPT, 0);
          644  +    *ppRe = pRe;
          645  +  }else if( pRe->sIn.i>=pRe->sIn.mx ){
          646  +    re_append(pRe, RE_OP_ACCEPT, 0);
          647  +    *ppRe = pRe;
          648  +  }else{
          649  +    re_free(pRe);
          650  +    return "unrecognized character";
          651  +  }
          652  +
          653  +  /* The following is a performance optimization.  If the regex begins with
          654  +  ** ".*" (if the input regex lacks an initial "^") and afterwards there are
          655  +  ** one or more matching characters, enter those matching characters into
          656  +  ** zInit[].  The re_match() routine can then search ahead in the input 
          657  +  ** string looking for the initial match without having to run the whole
          658  +  ** regex engine over the string.  Do not worry able trying to match
          659  +  ** unicode characters beyond plane 0 - those are very rare and this is
          660  +  ** just an optimization. */
          661  +  if( pRe->aOp[0]==RE_OP_ANYSTAR ){
          662  +    for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
          663  +      unsigned x = pRe->aArg[i];
          664  +      if( x<=127 ){
          665  +        pRe->zInit[j++] = x;
          666  +      }else if( x<=0xfff ){
          667  +        pRe->zInit[j++] = 0xc0 | (x>>6);
          668  +        pRe->zInit[j++] = 0x80 | (x&0x3f);
          669  +      }else if( x<=0xffff ){
          670  +        pRe->zInit[j++] = 0xd0 | (x>>12);
          671  +        pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
          672  +        pRe->zInit[j++] = 0x80 | (x&0x3f);
          673  +      }else{
          674  +        break;
          675  +      }
          676  +    }
          677  +    if( j>0 && pRe->zInit[j-1]==0 ) j--;
          678  +    pRe->nInit = j;
          679  +  }
          680  +  return pRe->zErr;
          681  +}
          682  +
          683  +/*
          684  +** Implementation of the regexp() SQL function.  This function implements
          685  +** the build-in REGEXP operator.  The first argument to the function is the
          686  +** pattern and the second argument is the string.  So, the SQL statements:
          687  +**
          688  +**       A REGEXP B
          689  +**
          690  +** is implemented as regexp(B,A).
          691  +*/
          692  +static void re_sql_func(
          693  +  sqlite3_context *context, 
          694  +  int argc, 
          695  +  sqlite3_value **argv
          696  +){
          697  +  ReCompiled *pRe;          /* Compiled regular expression */
          698  +  const char *zPattern;     /* The regular expression */
          699  +  const unsigned char *zStr;/* String being searched */
          700  +  const char *zErr;         /* Compile error message */
          701  +
          702  +  pRe = sqlite3_get_auxdata(context, 0);
          703  +  if( pRe==0 ){
          704  +    zPattern = (const char*)sqlite3_value_text(argv[0]);
          705  +    if( zPattern==0 ) return;
          706  +    zErr = re_compile(&pRe, zPattern, 0);
          707  +    if( zErr ){
          708  +      sqlite3_result_error(context, zErr, -1);
          709  +      return;
          710  +    }
          711  +    if( pRe==0 ){
          712  +      sqlite3_result_error_nomem(context);
          713  +      return;
          714  +    }
          715  +    sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
          716  +  }
          717  +  zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
          718  +  if( zStr!=0 ){
          719  +    sqlite3_result_int(context, re_match(pRe, zStr, -1));
          720  +  }
          721  +}
          722  +
          723  +/*
          724  +** Invoke this routine in order to install the REGEXP function in an
          725  +** SQLite database connection.
          726  +**
          727  +** Use:
          728  +**
          729  +**      sqlite3_auto_extension(sqlite3_add_regexp_func);
          730  +**
          731  +** to cause this extension to be automatically loaded into each new
          732  +** database connection.
          733  +*/
          734  +int re_add_sql_func(sqlite3 *db){
          735  +  return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
          736  +                                 re_sql_func, 0, 0);
          737  +}
          738  +
          739  +/*
          740  +** Run a "grep" over a single file
          741  +*/
          742  +static void grep(ReCompiled *pRe, const char *zFile, FILE *in){
          743  +  int ln = 0;
          744  +  int n;
          745  +  char zLine[2000];
          746  +  while( fgets(zLine, sizeof(zLine), in) ){
          747  +    ln++;
          748  +    n = (int)strlen(zLine);
          749  +    while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--;
          750  +    if( re_match(pRe, (const unsigned char*)zLine, n) ){
          751  +      printf("%s:%d:%.*s\n", zFile, ln, n, zLine);
          752  +    }
          753  +  }
          754  +}
          755  +
          756  +/*
          757  +** COMMAND: test-grep
          758  +**
          759  +** Usage: %fossil test-grep REGEXP [FILE...]
          760  +**
          761  +** Run a regular expression match over the named disk files, or against
          762  +** standard input if no disk files are named on the command-line.
          763  +**
          764  +** Options:
          765  +**
          766  +**   -i|--ignore-case    Ignore case
          767  +*/
          768  +void re_test_grep(void){
          769  +  ReCompiled *pRe;
          770  +  const char *zErr;
          771  +  int ignoreCase = find_option("ignore-case","i",0)!=0;
          772  +
          773  +  if( g.argc<3 ){
          774  +    usage("REGEXP [FILE...]");
          775  +  }
          776  +  zErr = re_compile(&pRe, g.argv[2], ignoreCase);
          777  +  if( zErr ) fossil_fatal("%s", zErr);
          778  +  if( g.argc==3 ){
          779  +    grep(pRe, "-", stdin);
          780  +  }else{
          781  +    int i;
          782  +    for(i=3; i<g.argc; i++){
          783  +      FILE *in = fossil_fopen(g.argv[i], "rb");
          784  +      if( in==0 ){
          785  +        fossil_warning("cannot open \"%s\"", g.argv[i]);
          786  +      }else{
          787  +        grep(pRe, g.argv[i], in);
          788  +        fclose(in);
          789  +      }
          790  +    }
          791  +  }
          792  +  re_free(pRe);
          793  +}

Changes to src/report.c.

    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **  
    18     18   ** Code to generate the ticket listings
    19     19   */
    20     20   #include "config.h"
           21  +#include <time.h>
    21     22   #include "report.h"
    22     23   #include <assert.h>
    23     24   
    24     25   /* Forward references to static routines */
    25     26   static void report_format_hints(void);
    26     27   
    27     28   /*
................................................................................
    31     32     const char *zScript;
    32     33     Blob ril;   /* Report Item List */
    33     34     Stmt q;
    34     35     int rn = 0;
    35     36     int cnt = 0;
    36     37   
    37     38     login_check_credentials();
    38         -  if( !g.okRdTkt && !g.okNewTkt ){ login_needed(); return; }
           39  +  if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
    39     40     style_header("Ticket Main Menu");
    40     41     if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
    41     42     zScript = ticket_reportlist_code();
    42     43     if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
    43     44     
    44     45     blob_zero(&ril);
    45     46     ticket_init();
    46     47   
    47     48     db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title");
    48     49     while( db_step(&q)==SQLITE_ROW ){
    49     50       const char *zTitle = db_column_text(&q, 1);
    50     51       const char *zOwner = db_column_text(&q, 2);
    51         -    if( zTitle[0] =='_' && !g.okTktFmt ){
           52  +    if( zTitle[0] =='_' && !g.perm.TktFmt ){
    52     53         continue;
    53     54       }
    54     55       rn = db_column_int(&q, 0);
    55     56       cnt++;
    56     57       blob_appendf(&ril, "<li>");
    57     58       if( zTitle[0] == '_' ){
    58     59         blob_appendf(&ril, "%s", zTitle);
    59     60       } else {
    60         -      blob_appendf(&ril, "<a href=\"rptview?rn=%d\" rel=\"nofollow\">%h</a>", rn, zTitle);
           61  +      blob_appendf(&ril, "%z%h</a>", href("%R/rptview?rn=%d", rn), zTitle);
    61     62       }
    62     63       blob_appendf(&ril, "&nbsp;&nbsp;&nbsp;");
    63         -    if( g.okWrite && zOwner && zOwner[0] ){
    64         -      blob_appendf(&ril, "(by <i>%h</i></i>) ", zOwner);
           64  +    if( g.perm.Write && zOwner && zOwner[0] ){
           65  +      blob_appendf(&ril, "(by <i>%h</i>) ", zOwner);
    65     66       }
    66         -    if( g.okTktFmt ){
    67         -      blob_appendf(&ril, "[<a href=\"rptedit?rn=%d&amp;copy=1\" rel=\"nofollow\">copy</a>] ", rn);
           67  +    if( g.perm.TktFmt ){
           68  +      blob_appendf(&ril, "[%zcopy</a>] ",
           69  +                   href("%R/rptedit?rn=%d&copy=1", rn));
    68     70       }
    69         -    if( g.okAdmin || (g.okWrTkt && zOwner && strcmp(g.zLogin,zOwner)==0) ){
    70         -      blob_appendf(&ril, "[<a href=\"rptedit?rn=%d\" rel=\"nofollow\">edit</a>] ", rn);
           71  +    if( g.perm.Admin 
           72  +     || (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0)
           73  +    ){
           74  +      blob_appendf(&ril, "[%zedit</a>]", 
           75  +                         href("%R/rptedit?rn=%d", rn));
    71     76       }
    72         -    if( g.okTktFmt ){
    73         -      blob_appendf(&ril, "[<a href=\"rptsql?rn=%d\" rel=\"nofollow\">sql</a>] ", rn);
           77  +    if( g.perm.TktFmt ){
           78  +      blob_appendf(&ril, "[%zsql</a>]",
           79  +                         href("%R/rptsql?rn=%d", rn));
    74     80       }
    75     81       blob_appendf(&ril, "</li>\n");
    76     82     }
    77     83   
    78     84     Th_Store("report_items", blob_str(&ril));
    79     85     
    80     86     Th_Render(zScript);
................................................................................
    86     92   }
    87     93   
    88     94   /*
    89     95   ** Remove whitespace from both ends of a string.
    90     96   */
    91     97   char *trim_string(const char *zOrig){
    92     98     int i;
    93         -  while( isspace(*zOrig) ){ zOrig++; }
           99  +  while( fossil_isspace(*zOrig) ){ zOrig++; }
    94    100     i = strlen(zOrig);
    95         -  while( i>0 && isspace(zOrig[i-1]) ){ i--; }
          101  +  while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; }
    96    102     return mprintf("%.*s", i, zOrig);
    97    103   }
    98    104   
    99    105   /*
   100    106   ** Extract a numeric (integer) value from a string.
   101    107   */
   102    108   char *extract_integer(const char *zOrig){
   103    109     if( zOrig == NULL || zOrig[0] == 0 ) return "";
   104         -  while( *zOrig && !isdigit(*zOrig) ){ zOrig++; }
          110  +  while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; }
   105    111     if( *zOrig ){
   106    112       /* we have a digit. atoi() will get as much of the number as it
   107    113       ** can. We'll run it through mprintf() to get a string. Not
   108    114       ** an efficient way to do it, but effective.
   109    115       */
   110    116       return mprintf("%d", atoi(zOrig));
   111    117     }
   112    118     return "";
   113    119   }
   114    120   
   115    121   /*
   116    122   ** Remove blank lines from the beginning of a string and
   117         -** all whitespace from the end. Removes whitespace preceeding a NL,
          123  +** all whitespace from the end. Removes whitespace preceding a NL,
   118    124   ** which also converts any CRNL sequence into a single NL.
   119    125   */
   120    126   char *remove_blank_lines(const char *zOrig){
   121    127     int i, j, n;
   122    128     char *z;
   123         -  for(i=j=0; isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; }
          129  +  for(i=j=0; fossil_isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; }
   124    130     n = strlen(&zOrig[j]);
   125         -  while( n>0 && isspace(zOrig[j+n-1]) ){ n--; }
          131  +  while( n>0 && fossil_isspace(zOrig[j+n-1]) ){ n--; }
   126    132     z = mprintf("%.*s", n, &zOrig[j]);
   127    133     for(i=j=0; z[i]; i++){
   128         -    if( z[i+1]=='\n' && z[i]!='\n' && isspace(z[i]) ){
          134  +    if( z[i+1]=='\n' && z[i]!='\n' && fossil_isspace(z[i]) ){
   129    135         z[j] = z[i];
   130         -      while(isspace(z[j]) && z[j] != '\n' ){ j--; }
          136  +      while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; }
   131    137         j++;
   132    138         continue;
   133    139       }
   134    140   
   135    141       z[j++] = z[i];
   136    142     }
   137    143     z[j] = 0;
................................................................................
   143    149   
   144    150   /*
   145    151   ** This is the SQLite authorizer callback used to make sure that the
   146    152   ** SQL statements entered by users do not try to do anything untoward.
   147    153   ** If anything suspicious is tried, set *(char**)pError to an error
   148    154   ** message obtained from malloc.
   149    155   */
   150         -static int report_query_authorizer(
          156  +int report_query_authorizer(
   151    157     void *pError,
   152    158     int code,
   153    159     const char *zArg1,
   154    160     const char *zArg2,
   155    161     const char *zArg3,
   156    162     const char *zArg4
   157    163   ){
................................................................................
   162    168     }
   163    169     switch( code ){
   164    170       case SQLITE_SELECT:
   165    171       case SQLITE_FUNCTION: {
   166    172         break;
   167    173       }
   168    174       case SQLITE_READ: {
   169         -      static const char *azAllowed[] = {
          175  +      static const char *const azAllowed[] = {
   170    176            "ticket",
          177  +         "ticketchng",
   171    178            "blob",
   172    179            "filename",
   173    180            "mlink",
   174    181            "plink",
   175    182            "event",
   176    183            "tag",
   177    184            "tagxref",
   178    185         };
   179    186         int i;
   180    187         for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){
   181         -        if( strcasecmp(zArg1, azAllowed[i])==0 ) break;
          188  +        if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break;
   182    189         }
   183    190         if( i>=sizeof(azAllowed)/sizeof(azAllowed[0]) ){
   184    191           *(char**)pError = mprintf("access to table \"%s\" is restricted",zArg1);
   185    192           rc = SQLITE_DENY;
   186         -      }else if( !g.okRdAddr && strncmp(zArg2, "private_", 8)==0 ){
          193  +      }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){
   187    194           rc = SQLITE_IGNORE;
   188    195         }
   189    196         break;
   190    197       }
   191    198       default: {
   192    199         *(char**)pError = mprintf("only SELECT statements are allowed");
   193    200         rc = SQLITE_DENY;
   194    201         break;
   195    202       }
   196    203     }
   197    204     return rc;
   198    205   }
          206  +
          207  +/*
          208  +** Activate the query authorizer
          209  +*/
          210  +static void report_restrict_sql(char **pzErr){
          211  +  (void)fossil_localtime(0);
          212  +  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
          213  +}
          214  +static void report_unrestrict_sql(void){
          215  +  sqlite3_set_authorizer(g.db, 0, 0);
          216  +}
   199    217   
   200    218   
   201    219   /*
   202    220   ** Check the given SQL to see if is a valid query that does not
   203    221   ** attempt to do anything dangerous.  Return 0 on success and a
   204    222   ** pointer to an error message string (obtained from malloc) if
   205    223   ** there is a problem.
................................................................................
   210    228     const char *zTail;
   211    229     sqlite3_stmt *pStmt;
   212    230     int rc;
   213    231   
   214    232     /* First make sure the SQL is a single query command by verifying that
   215    233     ** the first token is "SELECT" and that there are no unquoted semicolons.
   216    234     */
   217         -  for(i=0; isspace(zSql[i]); i++){}
   218         -  if( strncasecmp(&zSql[i],"select",6)!=0 ){
          235  +  for(i=0; fossil_isspace(zSql[i]); i++){}
          236  +  if( fossil_strnicmp(&zSql[i],"select",6)!=0 ){
   219    237       return mprintf("The SQL must be a SELECT statement");
   220    238     }
   221    239     for(i=0; zSql[i]; i++){
   222    240       if( zSql[i]==';' ){
   223    241         int bad;
   224    242         int c = zSql[i+1];
   225    243         zSql[i+1] = 0;
................................................................................
   232    250           return mprintf("Semi-colon detected! "
   233    251                          "Only a single SQL statement is allowed");
   234    252         }
   235    253       }
   236    254     }
   237    255     
   238    256     /* Compile the statement and check for illegal accesses or syntax errors. */
   239         -  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr);
          257  +  report_restrict_sql(&zErr);
   240    258     rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, &zTail);
   241    259     if( rc!=SQLITE_OK ){
   242    260       zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db));
   243    261     }
          262  +  if( !sqlite3_stmt_readonly(pStmt) ){
          263  +    zErr = mprintf("SQL must not modify the database");
          264  +  }
   244    265     if( pStmt ){
   245    266       sqlite3_finalize(pStmt);
   246    267     }
   247         -  sqlite3_set_authorizer(g.db, 0, 0);
          268  +  report_unrestrict_sql();
   248    269     return zErr;
   249    270   }
   250    271   
   251    272   /*
   252    273   ** WEBPAGE: /rptsql
   253    274   */
   254    275   void view_see_sql(void){
................................................................................
   256    277     const char *zTitle;
   257    278     const char *zSQL;
   258    279     const char *zOwner;
   259    280     const char *zClrKey;
   260    281     Stmt q;
   261    282   
   262    283     login_check_credentials();
   263         -  if( !g.okTktFmt ){
          284  +  if( !g.perm.TktFmt ){
   264    285       login_needed();
   265    286       return;
   266    287     }
   267    288     rn = atoi(PD("rn","0"));
   268    289     db_prepare(&q, "SELECT title, sqlcode, owner, cols "
   269    290                      "FROM reportfmt WHERE rn=%d",rn);
   270    291     style_header("SQL For Report Format Number %d", rn);
................................................................................
   275    296     }
   276    297     zTitle = db_column_text(&q, 0);
   277    298     zSQL = db_column_text(&q, 1);
   278    299     zOwner = db_column_text(&q, 2);
   279    300     zClrKey = db_column_text(&q, 3);
   280    301     @ <table cellpadding=0 cellspacing=0 border=0>
   281    302     @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
   282         -  @ <td colspan=3>%h(zTitle)</td></tr>
          303  +  @ <td colspan="3">%h(zTitle)</td></tr>
   283    304     @ <tr><td valign="top" align="right">Owner:</td><td></td>
   284         -  @ <td colspan=3>%h(zOwner)</td></tr>
          305  +  @ <td colspan="3">%h(zOwner)</td></tr>
   285    306     @ <tr><td valign="top" align="right">SQL:</td><td></td>
   286    307     @ <td valign="top"><pre>
   287    308     @ %h(zSQL)
   288    309     @ </pre></td>
   289    310     @ <td width=15></td><td valign="top">
   290    311     output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
   291    312     @ </td>
................................................................................
   304    325     const char *z;
   305    326     const char *zOwner;
   306    327     const char *zClrKey;
   307    328     char *zSQL;
   308    329     char *zErr = 0;
   309    330   
   310    331     login_check_credentials();
   311         -  if( !g.okTktFmt ){
          332  +  if( !g.perm.TktFmt ){
   312    333       login_needed();
   313    334       return;
   314    335     }
   315    336     /*view_add_functions(0);*/
   316    337     rn = atoi(PD("rn","0"));
   317    338     zTitle = P("t");
   318    339     zOwner = PD("w",g.zLogin);
................................................................................
   326    347       return;
   327    348     }else if( rn>0 && P("del1") ){
   328    349       zTitle = db_text(0, "SELECT title FROM reportfmt "
   329    350                            "WHERE rn=%d", rn);
   330    351       if( zTitle==0 ) cgi_redirect("reportlist");
   331    352   
   332    353       style_header("Are You Sure?");
   333         -    @ <form action="rptedit" method="POST">
          354  +    @ <form action="rptedit" method="post">
   334    355       @ <p>You are about to delete all traces of the report
   335    356       @ <strong>%h(zTitle)</strong> from
   336    357       @ the database.  This is an irreversible operation.  All records
   337    358       @ related to this report will be removed and cannot be recovered.</p>
   338    359       @
   339    360       @ <input type="hidden" name="rn" value="%d(rn)">
   340    361       login_insert_csrf_secret();
................................................................................
   352    373       if( zSQL[0]==0 ){
   353    374         zErr = "Please supply an SQL query statement";
   354    375       }else if( (zTitle = trim_string(zTitle))[0]==0 ){
   355    376         zErr = "Please supply a title"; 
   356    377       }else{
   357    378         zErr = verify_sql_statement(zSQL);
   358    379       }
          380  +    if( zErr==0
          381  +     && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d",
          382  +                  zTitle, rn)
          383  +    ){
          384  +      zErr = mprintf("There is already another report named \"%h\"", zTitle);
          385  +    }
   359    386       if( zErr==0 ){
   360    387         login_verify_csrf_secret();
   361    388         if( rn>0 ){
   362    389           db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
   363         -                      " owner=%Q, cols=%Q WHERE rn=%d",
          390  +                      " owner=%Q, cols=%Q, mtime=now() WHERE rn=%d",
   364    391              zTitle, zSQL, zOwner, zClrKey, rn);
   365    392         }else{
   366         -        db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
   367         -           "VALUES(%Q,%Q,%Q,%Q)",
          393  +        db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
          394  +           "VALUES(%Q,%Q,%Q,%Q,now())",
   368    395              zTitle, zSQL, zOwner, zClrKey);
   369    396           rn = db_last_insert_rowid();
   370    397         }
   371    398         cgi_redirect(mprintf("rptview?rn=%d", rn));
   372    399         return;
   373    400       }
   374    401     }else if( rn==0 ){
................................................................................
   395    422     if( zOwner==0 ) zOwner = g.zLogin;
   396    423     style_submenu_element("Cancel", "Cancel", "reportlist");
   397    424     if( rn>0 ){
   398    425       style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
   399    426     }
   400    427     style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
   401    428     if( zErr ){
   402         -    @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
          429  +    @ <blockquote class="reportError">%h(zErr)</blockquote>
   403    430     }
   404         -  @ <form action="rptedit" method="POST">
   405         -  @ <input type="hidden" name="rn" value="%d(rn)">
   406         -  @ <p>Report Title:<br>
   407         -  @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
   408         -  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
          431  +  @ <form action="rptedit" method="post"><div>
          432  +  @ <input type="hidden" name="rn" value="%d(rn)" />
          433  +  @ <p>Report Title:<br />
          434  +  @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p>
          435  +  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
   409    436     @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
   410    437     @ </p>
   411    438     login_insert_csrf_secret();
   412         -  if( g.okAdmin ){
          439  +  if( g.perm.Admin ){
   413    440       @ <p>Report owner:
   414         -    @ <input type="text" name="w" size="20" value="%h(zOwner)">
          441  +    @ <input type="text" name="w" size="20" value="%h(zOwner)" />
   415    442       @ </p>
   416    443     } else {
   417         -    @ <input type="hidden" name="w" value="%h(zOwner)">
          444  +    @ <input type="hidden" name="w" value="%h(zOwner)" />
   418    445     }
   419    446     @ <p>Enter an optional color key in the following box.  (If blank, no
   420    447     @ color key is displayed.)  Each line contains the text for a single
   421    448     @ entry in the key.  The first token of each line is the background
   422         -  @ color for that line.<br>
          449  +  @ color for that line.<br />
   423    450     @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
   424    451     @ </p>
   425         -  if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
          452  +  if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
   426    453       @ <p>This report format is owned by %h(zOwner).  You are not allowed
   427    454       @ to change it.</p>
   428    455       @ </form>
   429    456       report_format_hints();
   430    457       style_footer();
   431    458       return;
   432    459     }
   433         -  @ <input type="submit" value="Apply Changes">
          460  +  @ <input type="submit" value="Apply Changes" />
   434    461     if( rn>0 ){
   435         -    @ <input type="submit" value="Delete This Report" name="del1">
          462  +    @ <input type="submit" value="Delete This Report" name="del1" />
   436    463     }
   437         -  @ </form>
          464  +  @ </div></form>
   438    465     report_format_hints();
   439    466     style_footer();
   440    467   }
   441    468   
   442    469   /*
   443    470   ** Output a bunch of text that provides information about report
   444    471   ** formats
................................................................................
   446    473   static void report_format_hints(void){
   447    474     char *zSchema;
   448    475     zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
   449    476     if( zSchema==0 ){
   450    477       zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
   451    478                           " WHERE name='ticket'");
   452    479     }
   453         -  @ <hr><h3>TICKET Schema</h3>
          480  +  @ <hr /><h3>TICKET Schema</h3>
   454    481     @ <blockquote><pre>
   455    482     @ %h(zSchema)
   456    483     @ </pre></blockquote>
   457    484     @ <h3>Notes</h3>
   458    485     @ <ul>
   459    486     @ <li><p>The SQL must consist of a single SELECT statement</p></li>
   460    487     @
   461    488     @ <li><p>If a column of the result set is named "#" then that column
   462    489     @ is assumed to hold a ticket number.  A hyperlink will be created from
   463    490     @ that column to a detailed view of the ticket.</p></li>
   464    491     @
   465    492     @ <li><p>If a column of the result set is named "bgcolor" then the content
   466    493     @ of that column determines the background color of the row.</p></li>
          494  +  @
          495  +  @ <li><p>The text of all columns prior to the first column whose name begins
          496  +  @ with underscore ("_") is shown character-for-character as it appears in
          497  +  @ the database.  In other words, it is assumed to have a mimetype of
          498  +  @ text/plain.
   467    499     @
   468    500     @ <li><p>The first column whose name begins with underscore ("_") and all
   469         -  @ subsequent columns are shown on their own rows in the table.  This might
   470         -  @ be useful for displaying the description of tickets.
          501  +  @ subsequent columns are shown on their own rows in the table and with
          502  +  @ wiki formatting.  In other words, such rows are shown with a mimetype
          503  +  @ of text/x-fossil-wiki.  This is recommended for the "description" field
          504  +  @ of tickets.
   471    505     @ </p></li>
   472    506     @
   473    507     @ <li><p>The query can join other tables in the database besides TICKET.
   474    508     @ </p></li>
   475    509     @ </ul>
   476    510     @
   477    511     @ <h3>Examples</h3>
   478    512     @ <p>In this example, the first column in the result set is named
   479    513     @ "bgcolor".  The value of this column is not displayed.  Instead, it
   480    514     @ selects the background color of each row based on the TICKET.STATUS
   481    515     @ field of the database.  The color key at the right shows the various
   482    516     @ color codes.</p>
   483         -  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
   484         -  @ <tr bgcolor="#f2dcdc"><td align="center">new or active</td></tr>
   485         -  @ <tr bgcolor="#e8e8bd"><td align="center">review</td></tr>
   486         -  @ <tr bgcolor="#cfe8bd"><td align="center">fixed</td></tr>
   487         -  @ <tr bgcolor="#bde5d6"><td align="center">tested</td></tr>
   488         -  @ <tr bgcolor="#cacae5"><td align="center">defer</td></tr>
   489         -  @ <tr bgcolor="#c8c8c8"><td align="center">closed</td></tr>
          517  +  @ <table class="rpteditex">
          518  +  @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">new or active</td></tr>
          519  +  @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">review</td></tr>
          520  +  @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">fixed</td></tr>
          521  +  @ <tr style="background-color:#bde5d6;"><td class="rpteditex">tested</td></tr>
          522  +  @ <tr style="background-color:#cacae5;"><td class="rpteditex">defer</td></tr>
          523  +  @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">closed</td></tr>
   490    524     @ </table>
   491    525     @ <blockquote><pre>
   492    526     @ SELECT
   493    527     @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
   494    528     @        WHEN status='review' THEN '#e8e8bd'
   495    529     @        WHEN status='fixed' THEN '#cfe8bd'
   496    530     @        WHEN status='tested' THEN '#bde5d6'
................................................................................
   508    542     @   priority AS 'Pri',
   509    543     @   title AS 'Title'
   510    544     @ FROM ticket
   511    545     @ </pre></blockquote>
   512    546     @ <p>To base the background color on the TICKET.PRIORITY or
   513    547     @ TICKET.SEVERITY fields, substitute the following code for the
   514    548     @ first column of the query:</p>
   515         -  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
   516         -  @ <tr bgcolor="#f2dcdc"><td align="center">1</td></tr>
   517         -  @ <tr bgcolor="#e8e8bd"><td align="center">2</td></tr>
   518         -  @ <tr bgcolor="#cfe8bd"><td align="center">3</td></tr>
   519         -  @ <tr bgcolor="#cacae5"><td align="center">4</td></tr>
   520         -  @ <tr bgcolor="#c8c8c8"><td align="center">5</td></tr>
          549  +  @ <table class="rpteditex">
          550  +  @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">1</td></tr>
          551  +  @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">2</td></tr>
          552  +  @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">3</td></tr>
          553  +  @ <tr style="background-color:#cacae5;"><td class="rpteditex">4</td></tr>
          554  +  @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">5</td></tr>
   521    555     @ </table>
   522    556     @ <blockquote><pre>
   523    557     @ SELECT
   524    558     @   CASE priority WHEN 1 THEN '#f2dcdc'
   525    559     @        WHEN 2 THEN '#e8e8bd'
   526    560     @        WHEN 3 THEN '#cfe8bd'
   527    561     @        WHEN 4 THEN '#cacae5'
................................................................................
   561    595     @    owner AS 'By',
   562    596     @    subsystem AS 'Subsys',
   563    597     @    sdate(changetime) AS 'Changed',
   564    598     @    assignedto AS 'Assigned',
   565    599     @    severity AS 'Svr',
   566    600     @    priority AS 'Pri',
   567    601     @    title AS 'Title',
   568         -  @    description AS '_Description',   -- When the column name begins with '_'
   569         -  @    remarks AS '_Remarks'            -- the data is shown on a separate row.
          602  +  @    description AS '_Description',  -- When the column name begins with '_'
          603  +  @    remarks AS '_Remarks'           -- content is rendered as wiki
   570    604     @  FROM ticket
   571    605     @ </pre></blockquote>
   572    606     @
   573    607     @ <p>Or, to see part of the description on the same row, use the
   574    608     @ <b>wiki()</b> function with some string manipulation. Using the
   575    609     @ <b>tkt()</b> function on the ticket number will also generate a linked
   576    610     @ field, but without the extra <i>edit</i> column:
................................................................................
   581    615     @    title AS 'Title',
   582    616     @    wiki(substr(description,0,80)) AS 'Description'
   583    617     @  FROM ticket
   584    618     @ </pre></blockquote>
   585    619     @
   586    620   }
   587    621   
   588         -#if 0 /* NOT USED */
   589         -static void column_header(int rn,const char *zCol, int nCol, int nSorted,
   590         -    const char *zDirection, const char *zExtra
   591         -){
   592         -  int set = (nCol==nSorted);
   593         -  int desc = !strcmp(zDirection,"DESC");
   594         -
   595         -  /*
   596         -  ** Clicking same column header 3 times in a row resets any sorting.
   597         -  ** Note that we link to rptview, which means embedded reports will get
   598         -  ** sent to the actual report view page as soon as a user tries to do
   599         -  ** any sorting. I don't see that as a Bad Thing.
   600         -  */
   601         -  if(set && desc){
   602         -    @ <th bgcolor="%s(BG1)" class="bkgnd1">
   603         -    @   <a href="rptview?rn=%d(rn)%s(zExtra)">%h(zCol)</a></th>
   604         -  }else{
   605         -    if(set){
   606         -      @ <th bgcolor="%s(BG1)" class="bkgnd1"><a
   607         -    }else{
   608         -      @ <th><a
   609         -    }
   610         -    @ href="rptview?rn=%d(rn)&amp;order_by=%d(nCol)&amp;\
   611         -    @ order_dir=%s(desc?"ASC":"DESC")\
   612         -    @ %s(zExtra)">%h(zCol)</a></th>
   613         -  }
   614         -}
   615         -#endif
   616         -
   617    622   /*
   618    623   ** The state of the report generation.
   619    624   */
   620    625   struct GenerateHTML {
   621    626     int rn;          /* Report number */
   622    627     int nCount;      /* Row number */
   623    628     int nCol;        /* Number of columns */
   624    629     int isMultirow;  /* True if multiple table rows per query result row */
   625    630     int iNewRow;     /* Index of first column that goes on separate row */
   626    631     int iBg;         /* Index of column that defines background color */
          632  +  int wikiFlags;   /* Flags passed into wiki_convert() */
          633  +  const char *zWikiStart;    /* HTML before display of multi-line wiki */
          634  +  const char *zWikiEnd;      /* HTML after display of multi-line wiki */
   627    635   };
   628    636   
   629    637   /*
   630    638   ** The callback function for db_query
   631    639   */
   632    640   static int generate_html(
   633    641     void *pUser,     /* Pointer to output state */
................................................................................
   634    642     int nArg,        /* Number of columns in this result row */
   635    643     char **azArg,    /* Text of data in all columns */
   636    644     char **azName    /* Names of the columns */
   637    645   ){
   638    646     struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
   639    647     int i;
   640    648     const char *zTid;  /* Ticket UUID.  (value of column named '#') */
   641         -  int rn;            /* Report number */
   642    649     char *zBg = 0;     /* Use this background color */
   643         -  char zPage[30];    /* Text version of the ticket number */
   644         -
   645         -  /* Get the report number
   646         -  */
   647         -  rn = pState->rn;
   648    650   
   649    651     /* Do initialization
   650    652     */
   651    653     if( pState->nCount==0 ){
   652    654       /* Turn off the authorizer.  It is no longer doing anything since the
   653    655       ** query has already been prepared.
   654    656       */
................................................................................
   659    661       ** rows in the table.
   660    662       */
   661    663       pState->nCol = 0;
   662    664       pState->isMultirow = 0;
   663    665       pState->iNewRow = -1;
   664    666       pState->iBg = -1;
   665    667       for(i=0; i<nArg; i++){
   666         -      if( azName[i][0]=='b' && strcmp(azName[i],"bgcolor")==0 ){
          668  +      if( azName[i][0]=='b' && fossil_strcmp(azName[i],"bgcolor")==0 ){
   667    669           pState->iBg = i;
   668    670           continue;
   669    671         }
   670         -      if( g.okWrite && azName[i][0]=='#' ){
          672  +      if( g.perm.Write && azName[i][0]=='#' ){
   671    673           pState->nCol++;
   672    674         }
   673    675         if( !pState->isMultirow ){
   674    676           if( azName[i][0]=='_' ){
   675    677             pState->isMultirow = 1;
   676    678             pState->iNewRow = i;
          679  +          pState->wikiFlags = WIKI_NOBADLINKS;
          680  +          pState->zWikiStart = "";
          681  +          pState->zWikiEnd = "";
          682  +          if( P("plaintext") ){
          683  +            pState->wikiFlags |= WIKI_LINKSONLY;
          684  +            pState->zWikiStart = "<pre class='verbatim'>";
          685  +            pState->zWikiEnd = "</pre>";
          686  +            style_submenu_element("Formatted", "Formatted",
          687  +                                  "%R/rptview?rn=%d", pState->rn);
          688  +          }else{
          689  +            style_submenu_element("Plaintext", "Plaintext",
          690  +                                  "%R/rptview?rn=%d&plaintext", pState->rn);
          691  +          }
   677    692           }else{
   678    693             pState->nCol++;
   679    694           }
   680    695         }
   681    696       }
   682    697   
   683    698       /* The first time this routine is called, output a table header
   684    699       */
   685         -    @ <tr>
          700  +    @ <thead><tr>
   686    701       zTid = 0;
   687    702       for(i=0; i<nArg; i++){
   688    703         char *zName = azName[i];
   689    704         if( i==pState->iBg ) continue;
   690    705         if( pState->iNewRow>=0 && i>=pState->iNewRow ){
   691         -        if( g.okWrite && zTid ){
          706  +        if( g.perm.Write && zTid ){
   692    707             @ <th>&nbsp;</th>
   693    708             zTid = 0;
   694    709           }
   695    710           if( zName[0]=='_' ) zName++;
   696    711           @ </tr><tr><th colspan=%d(pState->nCol)>%h(zName)</th>
   697    712         }else{
   698    713           if( zName[0]=='#' ){
   699    714             zTid = zName;
   700    715           }
   701    716           @ <th>%h(zName)</th>
   702    717         }
   703    718       }
   704         -    if( g.okWrite && zTid ){
          719  +    if( g.perm.Write && zTid ){
   705    720         @ <th>&nbsp;</th>
   706    721       }
   707         -    @ </tr>
          722  +    @ </tr></thead><tbody>
   708    723     }
   709    724     if( azArg==0 ){
   710    725       @ <tr><td colspan="%d(pState->nCol)">
   711    726       @ <i>No records match the report criteria</i>
   712    727       @ </td></tr>
   713    728       return 0;
   714    729     }
................................................................................
   721    736       @ <tr><td colspan=%d(pState->nCol)><font size=1>&nbsp;</font></td></tr>
   722    737     }
   723    738   
   724    739     /* Output the data for this entry from the database
   725    740     */
   726    741     zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
   727    742     if( zBg==0 ) zBg = "white";
   728         -  @ <tr bgcolor="%h(zBg)">
          743  +  @ <tr style="background-color:%h(zBg)">
   729    744     zTid = 0;
   730         -  zPage[0] = 0;
   731    745     for(i=0; i<nArg; i++){
   732    746       char *zData;
   733    747       if( i==pState->iBg ) continue;
   734    748       zData = azArg[i];
   735    749       if( zData==0 ) zData = "";
   736    750       if( pState->iNewRow>=0 && i>=pState->iNewRow ){
   737         -      if( zTid && g.okWrite ){
   738         -        @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td>
          751  +      if( zTid && g.perm.Write ){
          752  +        @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td>
   739    753           zTid = 0;
   740    754         }
   741    755         if( zData[0] ){
   742    756           Blob content;
   743         -        @ </tr><tr bgcolor="%h(zBg)"><td colspan=%d(pState->nCol)>
          757  +        @ </tr>
          758  +        @ <tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
          759  +        @ %s(pState->zWikiStart)
   744    760           blob_init(&content, zData, -1);
   745         -        wiki_convert(&content, 0, 0);
          761  +        wiki_convert(&content, 0, pState->wikiFlags);
   746    762           blob_reset(&content);
          763  +        @ %s(pState->zWikiEnd)
   747    764         }
   748    765       }else if( azName[i][0]=='#' ){
   749    766         zTid = zData;
   750         -      if( g.okHistory ){
   751         -        @ <td valign="top"><a href="tktview?name=%h(zData)">%h(zData)</a></td>
   752         -      }else{
   753         -        @ <td valign="top">%h(zData)</td>
   754         -      }
          767  +      @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
   755    768       }else if( zData[0]==0 ){
   756    769         @ <td valign="top">&nbsp;</td>
   757    770       }else{
   758    771         @ <td valign="top">
   759    772         @ %h(zData)
   760    773         @ </td>
   761    774       }
   762    775     }
   763         -  if( zTid && g.okWrite ){
   764         -    @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td>
          776  +  if( zTid && g.perm.Write ){
          777  +    @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td>
   765    778     }
   766    779     @ </tr>
   767    780     return 0;
   768    781   }
   769    782   
   770    783   /*
   771    784   ** Output the text given in the argument.  Convert tabs and newlines into
   772    785   ** spaces.
   773    786   */
   774    787   static void output_no_tabs(const char *z){
   775    788     while( z && z[0] ){
   776    789       int i, j;
   777         -    for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){}
          790  +    for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){}
   778    791       if( i>0 ){
   779    792         cgi_printf("%.*s", i, z);
   780    793       }
   781         -    for(j=i; isspace(z[j]); j++){}
          794  +    for(j=i; fossil_isspace(z[j]); j++){}
   782    795       if( j>i ){
   783    796         cgi_printf("%*s", j-i, "");
   784    797       }
   785    798       z += j;
   786    799     }
   787    800   }
   788    801   
................................................................................
   814    827   
   815    828   /*
   816    829   ** Generate HTML that describes a color key.
   817    830   */
   818    831   void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){
   819    832     int i, j, k;
   820    833     char *zSafeKey, *zToFree;
   821         -  while( isspace(*zClrKey) ) zClrKey++;
          834  +  while( fossil_isspace(*zClrKey) ) zClrKey++;
   822    835     if( zClrKey[0]==0 ) return;
   823    836     @ <table %s(zTabArgs)>
   824    837     if( horiz ){
   825    838       @ <tr>
   826    839     }
   827    840     zToFree = zSafeKey = mprintf("%h", zClrKey);
   828    841     while( zSafeKey[0] ){
   829         -    while( isspace(*zSafeKey) ) zSafeKey++;
   830         -    for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
   831         -    for(j=i; isspace(zSafeKey[j]); j++){}
          842  +    while( fossil_isspace(*zSafeKey) ) zSafeKey++;
          843  +    for(i=0; zSafeKey[i] && !fossil_isspace(zSafeKey[i]); i++){}
          844  +    for(j=i; fossil_isspace(zSafeKey[j]); j++){}
   832    845       for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
   833    846       if( !horiz ){
   834         -      cgi_printf("<tr bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
          847  +      cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n",
   835    848           i, zSafeKey, k-j, &zSafeKey[j]);
   836    849       }else{
   837         -      cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
          850  +      cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n",
   838    851           i, zSafeKey, k-j, &zSafeKey[j]);
   839    852       }
   840    853       zSafeKey += k;
   841    854     }
   842    855     free(zToFree);
   843    856     if( horiz ){
   844    857       @ </tr>
   845    858     }
   846    859     @ </table>
   847    860   }
          861  +
          862  +/*
          863  +** Execute a single read-only SQL statement.  Invoke xCallback() on each
          864  +** row.
          865  +*/
          866  +int sqlite3_exec_readonly(
          867  +  sqlite3 *db,                /* The database on which the SQL executes */
          868  +  const char *zSql,           /* The SQL to be executed */
          869  +  sqlite3_callback xCallback, /* Invoke this callback routine */
          870  +  void *pArg,                 /* First argument to xCallback() */
          871  +  char **pzErrMsg             /* Write error messages here */
          872  +){
          873  +  int rc = SQLITE_OK;         /* Return code */
          874  +  const char *zLeftover;      /* Tail of unprocessed SQL */
          875  +  sqlite3_stmt *pStmt = 0;    /* The current SQL statement */
          876  +  char **azCols = 0;          /* Names of result columns */
          877  +  int nCol;                   /* Number of columns of output */
          878  +  char **azVals = 0;          /* Text of all output columns */
          879  +  int i;                      /* Loop counter */
          880  +
          881  +  pStmt = 0;
          882  +  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
          883  +  assert( rc==SQLITE_OK || pStmt==0 );
          884  +  if( rc!=SQLITE_OK ){
          885  +    return rc;
          886  +  }
          887  +  if( !pStmt ){
          888  +    /* this happens for a comment or white-space */
          889  +    return SQLITE_OK;
          890  +  }
          891  +  if( !sqlite3_stmt_readonly(pStmt) ){
          892  +    sqlite3_finalize(pStmt);
          893  +    return SQLITE_ERROR;
          894  +  }
          895  +
          896  +  i = sqlite3_bind_parameter_index(pStmt, "$login");
          897  +  if( i ) sqlite3_bind_text(pStmt, i, g.zLogin, -1, SQLITE_TRANSIENT);
          898  +
          899  +  nCol = sqlite3_column_count(pStmt);
          900  +  azVals = fossil_malloc(2*nCol*sizeof(const char*) + 1);
          901  +  while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
          902  +    if( azCols==0 ){
          903  +      azCols = &azVals[nCol];
          904  +      for(i=0; i<nCol; i++){
          905  +        azCols[i] = (char *)sqlite3_column_name(pStmt, i);
          906  +      }
          907  +    }
          908  +    for(i=0; i<nCol; i++){
          909  +      azVals[i] = (char *)sqlite3_column_text(pStmt, i);
          910  +    }
          911  +    if( xCallback(pArg, nCol, azVals, azCols) ){
          912  +      break;
          913  +    }
          914  +  }
          915  +  rc = sqlite3_finalize(pStmt);
          916  +  fossil_free(azVals);
          917  +  return rc;
          918  +}
          919  +
          920  +/*
          921  +** Output Javascript code that will enables sorting of the table with
          922  +** the id zTableId by clicking.
          923  +**
          924  +** The javascript is derived from:
          925  +**
          926  +**     http://www.webtoolkit.info/sortable-html-table.html
          927  +**
          928  +*/
          929  +static void output_table_sorting_javascript(const char *zTableId){
          930  +  @ <script>
          931  +  @ function SortableTable(tableEl){
          932  +  @   this.tbody = tableEl.getElementsByTagName('tbody');
          933  +  @   this.sort = function (cell) {
          934  +  @     var column = cell.cellIndex;
          935  +  @     this.sortIndex = column;
          936  +  @     var newRows = new Array();
          937  +  @     for (j = 0; j < this.tbody[0].rows.length; j++) {
          938  +  @        newRows[j] = this.tbody[0].rows[j];
          939  +  @     }
          940  +  @     newRows.sort(this.sortText);
          941  +  @     if (cell.getAttribute("sortdir") == 'down') {
          942  +  @        newRows.reverse();
          943  +  @        cell.setAttribute('sortdir','up');
          944  +  @     } else {
          945  +  @        cell.setAttribute('sortdir','down');
          946  +  @     }
          947  +  @     for (i=0;i<newRows.length;i++) {
          948  +  @       this.tbody[0].appendChild(newRows[i]);
          949  +  @     }
          950  +  @   }
          951  +  @   this.sortText = function(a,b) {
          952  +  @     var i = thisObject.sortIndex;
          953  +  @     aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
          954  +  @     bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
          955  +  @     if(aa==bb) return 0;
          956  +  @     if(aa<bb) return -1;
          957  +  @     return 1;
          958  +  @   }
          959  +  @   var thisObject = this;
          960  +  @   var x = tableEl.getElementsByTagName('thead');
          961  +  @   if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){
          962  +  @     return;
          963  +  @   }
          964  +  @   if(x && x[0].rows && x[0].rows.length > 0) {
          965  +  @     var sortRow = x[0].rows[0];
          966  +  @   } else {
          967  +  @     return;
          968  +  @   }
          969  +  @   for (var i=0; i<sortRow.cells.length; i++) {
          970  +  @     sortRow.cells[i].sTable = this;
          971  +  @     sortRow.cells[i].onclick = function () {
          972  +  @       this.sTable.sort(this);
          973  +  @       return false;
          974  +  @     }
          975  +  @   }
          976  +  @ }
          977  +  @ var t = new SortableTable(gebi("%s(zTableId)"));
          978  +  @ </script>
          979  +}
   848    980   
   849    981   
   850    982   /*
   851    983   ** WEBPAGE: /rptview
   852    984   **
   853    985   ** Generate a report.  The rn query parameter is the report number
   854    986   ** corresponding to REPORTFMT.RN.  If the tablist query parameter exists,
................................................................................
   864    996     char *zClrKey;
   865    997     int tabs;
   866    998     Stmt q;
   867    999     char *zErr1 = 0;
   868   1000     char *zErr2 = 0;
   869   1001   
   870   1002     login_check_credentials();
   871         -  if( !g.okRdTkt ){ login_needed(); return; }
         1003  +  if( !g.perm.RdTkt ){ login_needed(); return; }
   872   1004     rn = atoi(PD("rn","0"));
   873   1005     if( rn==0 ){
   874   1006       cgi_redirect("reportlist");
   875   1007       return;
   876   1008     }
   877   1009     tabs = P("tablist")!=0;
   878   1010     /* view_add_functions(tabs); */
................................................................................
   905   1037   
   906   1038     count = 0;
   907   1039     if( !tabs ){
   908   1040       struct GenerateHTML sState;
   909   1041   
   910   1042       db_multi_exec("PRAGMA empty_result_callbacks=ON");
   911   1043       style_submenu_element("Raw", "Raw", 
   912         -      "rptview?tablist=1&%s", PD("QUERY_STRING",""));
   913         -    if( g.okAdmin 
   914         -       || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
         1044  +      "rptview?tablist=1&%h", PD("QUERY_STRING",""));
         1045  +    if( g.perm.Admin 
         1046  +       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
   915   1047         style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
   916   1048       }
   917         -    if( g.okTktFmt ){
         1049  +    if( g.perm.TktFmt ){
   918   1050         style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
   919   1051       }
   920         -    if( g.okNewTkt ){
         1052  +    if( g.perm.NewTkt ){
   921   1053         style_submenu_element("New Ticket", "Create a new ticket",
   922   1054           "%s/tktnew", g.zTop);
   923   1055       }
   924   1056       style_header(zTitle);
   925   1057       output_color_key(zClrKey, 1, 
   926         -        "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
   927         -    @ <table border=1 cellpadding=2 cellspacing=0 class="report">
         1058  +        "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
         1059  +    @ <table border="1" cellpadding="2" cellspacing="0" class="report"
         1060  +    @  id="reportTable">
   928   1061       sState.rn = rn;
   929   1062       sState.nCount = 0;
   930         -    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
   931         -    sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
   932         -    sqlite3_set_authorizer(g.db, 0, 0);
   933         -    @ </table>
         1063  +    report_restrict_sql(&zErr1);
         1064  +    sqlite3_exec_readonly(g.db, zSql, generate_html, &sState, &zErr2);
         1065  +    report_unrestrict_sql();
         1066  +    @ </tbody></table>
   934   1067       if( zErr1 ){
   935         -      @ <p><font color="red"><b>Error: %h(zErr1)</b></font></p>
         1068  +      @ <p class="reportError">Error: %h(zErr1)</p>
   936   1069       }else if( zErr2 ){
   937         -      @ <p><font color="red"><b>Error: %h(zErr2)</b></font></p>
         1070  +      @ <p class="reportError">Error: %h(zErr2)</p>
   938   1071       }
         1072  +    output_table_sorting_javascript("reportTable");
   939   1073       style_footer();
   940   1074     }else{
   941         -    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
   942         -    sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
   943         -    sqlite3_set_authorizer(g.db, 0, 0);
         1075  +    report_restrict_sql(&zErr1);
         1076  +    sqlite3_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
         1077  +    report_unrestrict_sql();
   944   1078       cgi_set_content_type("text/plain");
   945   1079     }
   946   1080   }
         1081  +
         1082  +/*
         1083  +** report number for full table ticket export
         1084  +*/
         1085  +static const char zFullTicketRptRn[] = "0";
         1086  +
         1087  +/*
         1088  +** report title for full table ticket export
         1089  +*/
         1090  +static const char zFullTicketRptTitle[] = "full ticket export";
         1091  +
         1092  +/*
         1093  +** show all reports, which can be used for ticket show.
         1094  +** Output is written to stdout as tab delimited table
         1095  +*/
         1096  +void rpt_list_reports(void){
         1097  +  Stmt q;
         1098  +  char const aRptOutFrmt[] = "%s\t%s\n";
         1099  +
         1100  +  fossil_print("Available reports:\n");
         1101  +  fossil_print(aRptOutFrmt,"report number","report title");
         1102  +  fossil_print(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle);
         1103  +  db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn");
         1104  +  while( db_step(&q)==SQLITE_ROW ){
         1105  +    const char *zRn = db_column_text(&q, 0);
         1106  +    const char *zTitle = db_column_text(&q, 1);
         1107  +
         1108  +    fossil_print(aRptOutFrmt,zRn,zTitle);
         1109  +  }
         1110  +  db_finalize(&q);
         1111  +}
         1112  +
         1113  +/*
         1114  +** user defined separator used by ticket show command
         1115  +*/
         1116  +static const char *zSep = 0;
         1117  +
         1118  +/*
         1119  +** select the quoting algorithm for "ticket show"
         1120  +*/
         1121  +#if INTERFACE
         1122  +typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding;
         1123  +#endif
         1124  +static tTktShowEncoding tktEncode = tktNoTab;
         1125  +
         1126  +/*
         1127  +** Output the text given in the argument.  Convert tabs and newlines into
         1128  +** spaces.
         1129  +*/
         1130  +static void output_no_tabs_file(const char *z){
         1131  +  switch( tktEncode ){
         1132  +    case tktFossilize:
         1133  +      { char *zFosZ;
         1134  +
         1135  +        if( z && *z ){
         1136  +          zFosZ = fossilize(z,-1);
         1137  +          fossil_print("%s",zFosZ);
         1138  +          free(zFosZ);
         1139  +        }
         1140  +        break;
         1141  +      }
         1142  +    default:
         1143  +      while( z && z[0] ){
         1144  +        int i, j;
         1145  +        for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){}
         1146  +        if( i>0 ){
         1147  +          fossil_print("%.*s", i, z);
         1148  +        }
         1149  +        for(j=i; fossil_isspace(z[j]); j++){}
         1150  +        if( j>i ){
         1151  +          fossil_print("%*s", j-i, "");
         1152  +        }
         1153  +        z += j;
         1154  +      }
         1155  +      break; 
         1156  +  }
         1157  +}
         1158  +
         1159  +/*
         1160  +** Output a row as a tab-separated line of text.
         1161  +*/
         1162  +int output_separated_file(
         1163  +  void *pUser,     /* Pointer to row-count integer */
         1164  +  int nArg,        /* Number of columns in this result row */
         1165  +  char **azArg,    /* Text of data in all columns */
         1166  +  char **azName    /* Names of the columns */
         1167  +){
         1168  +  int *pCount = (int*)pUser;
         1169  +  int i;
         1170  +
         1171  +  if( *pCount==0 ){
         1172  +    for(i=0; i<nArg; i++){
         1173  +      output_no_tabs_file(azName[i]);
         1174  +      fossil_print("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
         1175  +    }
         1176  +  }
         1177  +  ++*pCount;
         1178  +  for(i=0; i<nArg; i++){
         1179  +    output_no_tabs_file(azArg[i]);
         1180  +    fossil_print("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
         1181  +  }
         1182  +  return 0;
         1183  +}
         1184  +
         1185  +/*
         1186  +** Generate a report.  The rn query parameter is the report number.
         1187  +** The output is written to stdout as flat file. The zFilter parameter
         1188  +** is a full WHERE-condition.
         1189  +*/
         1190  +void rptshow( 
         1191  +    const char *zRep,
         1192  +    const char *zSepIn,
         1193  +    const char *zFilter,
         1194  +    tTktShowEncoding enc
         1195  +){
         1196  +  Stmt q;
         1197  +  char *zSql;
         1198  +  char *zErr1 = 0;
         1199  +  char *zErr2 = 0;
         1200  +  int count = 0;
         1201  +  int rn;
         1202  +
         1203  +  if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
         1204  +    zSql = "SELECT * FROM ticket";
         1205  +  }else{
         1206  +    rn = atoi(zRep);
         1207  +    if( rn ){
         1208  +      db_prepare(&q,
         1209  +       "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
         1210  +    }else{
         1211  +      db_prepare(&q,
         1212  +       "SELECT sqlcode FROM reportfmt WHERE title=%Q", zRep);
         1213  +    }
         1214  +    if( db_step(&q)!=SQLITE_ROW ){
         1215  +      db_finalize(&q);
         1216  +      rpt_list_reports();
         1217  +      fossil_fatal("unknown report format(%s)!",zRep);
         1218  +    }
         1219  +    zSql = db_column_malloc(&q, 0);
         1220  +    db_finalize(&q);
         1221  +  }
         1222  +  if( zFilter ){
         1223  +    zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
         1224  +  }
         1225  +  count = 0;
         1226  +  tktEncode = enc;
         1227  +  zSep = zSepIn;
         1228  +  report_restrict_sql(&zErr1);
         1229  +  sqlite3_exec_readonly(g.db, zSql, output_separated_file, &count, &zErr2);
         1230  +  report_unrestrict_sql();
         1231  +  if( zFilter ){
         1232  +    free(zSql);
         1233  +  }
         1234  +}

Changes to src/rss.c.

    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code used to create a RSS feed for the CGI interface.
    19     19   */
    20     20   #include "config.h"
           21  +#include <time.h>
    21     22   #include "rss.h"
    22     23   #include <assert.h>
    23         -#include <time.h>
    24     24   
    25     25   /*
    26     26   ** WEBPAGE: timeline.rss
    27     27   */
    28     28   void page_timeline_rss(void){
    29     29     Stmt q;
    30     30     int nLine=0;
................................................................................
    42     42       @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
    43     43       @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
    44     44       @ FROM event, blob
    45     45       @ WHERE blob.rid=event.objid
    46     46     ;
    47     47   
    48     48     login_check_credentials();
    49         -  if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ 
           49  +  if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ 
    50     50       return;
    51     51     }
    52     52   
    53     53     blob_zero(&bSQL);
    54     54     blob_append( &bSQL, zSQL1, -1 );
    55     55   
    56     56     if( zType[0]!='a' ){
    57         -    if( zType[0]=='c' && !g.okRead ) zType = "x";
    58         -    if( zType[0]=='w' && !g.okRdWiki ) zType = "x";
    59         -    if( zType[0]=='t' && !g.okRdTkt ) zType = "x";
           57  +    if( zType[0]=='c' && !g.perm.Read ) zType = "x";
           58  +    if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x";
           59  +    if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x";
    60     60       blob_appendf(&bSQL, " AND event.type=%Q", zType);
    61     61     }else{
    62         -    if( !g.okRead ){
    63         -      if( g.okRdTkt && g.okRdWiki ){
           62  +    if( !g.perm.Read ){
           63  +      if( g.perm.RdTkt && g.perm.RdWiki ){
    64     64           blob_append(&bSQL, " AND event.type!='ci'", -1);
    65         -      }else if( g.okRdTkt ){
           65  +      }else if( g.perm.RdTkt ){
    66     66           blob_append(&bSQL, " AND event.type=='t'", -1);
    67     67         }else{
    68     68           blob_append(&bSQL, " AND event.type=='w'", -1);
    69     69         }
    70         -    }else if( !g.okRdWiki ){
    71         -      if( g.okRdTkt ){
           70  +    }else if( !g.perm.RdWiki ){
           71  +      if( g.perm.RdTkt ){
    72     72           blob_append(&bSQL, " AND event.type!='w'", -1);
    73     73         }else{
    74     74           blob_append(&bSQL, " AND event.type=='ci'", -1);
    75     75         }
    76         -    }else if( !g.okRdTkt ){
    77         -      assert( !g.okRdTkt &&& g.okRead && g.okRdWiki );
           76  +    }else if( !g.perm.RdTkt ){
           77  +      assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki );
    78     78         blob_append(&bSQL, " AND event.type!='t'", -1);
    79     79       }
    80     80     }
    81     81   
    82     82     blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );
    83     83   
    84     84     cgi_set_content_type("application/rss+xml");
................................................................................
    92     92     if( zProjectDescr==0 ){
    93     93       zProjectDescr = zProjectName;
    94     94     }
    95     95   
    96     96     zPubDate = cgi_rfc822_datestamp(time(NULL));
    97     97   
    98     98     @ <?xml version="1.0"?>
    99         -  @ <rss version="2.0">
           99  +  @ <rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
   100    100     @   <channel>
   101    101     @     <title>%h(zProjectName)</title>
   102    102     @     <link>%s(g.zBaseURL)</link>
   103    103     @     <description>%h(zProjectDescr)</description>
   104    104     @     <pubDate>%s(zPubDate)</pubDate>
   105    105     @     <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator>
          106  +  free(zPubDate);
   106    107     db_prepare(&q, blob_str(&bSQL));
   107    108     blob_reset( &bSQL );
   108    109     while( db_step(&q)==SQLITE_ROW && nLine<=nLimit ){
   109    110       const char *zId = db_column_text(&q, 1);
   110    111       const char *zCom = db_column_text(&q, 3);
   111    112       const char *zAuthor = db_column_text(&q, 4);
   112    113       char *zPrefix = "";
................................................................................
   124    125         zPrefix = "*MERGE* ";
   125    126       }else if( nChild>1 ){
   126    127         zPrefix = "*FORK* ";
   127    128       }
   128    129   
   129    130       @     <item>
   130    131       @       <title>%s(zPrefix)%h(zCom)</title>
   131         -    @       <link>%s(g.zBaseURL)/ci/%s(zId)</link>
          132  +    @       <link>%s(g.zBaseURL)/info/%s(zId)</link>
   132    133       @       <description>%s(zPrefix)%h(zCom)</description>
   133    134       @       <pubDate>%s(zDate)</pubDate>
   134         -    @       <author>%h(zAuthor)</author>
   135         -    @       <guid>%s(g.zBaseURL)/ci/%s(zId)</guid>
          135  +    @       <dc:creator>%h(zAuthor)</dc:creator>
          136  +    @       <guid>%s(g.zBaseURL)/info/%s(zId)</guid>
   136    137       @     </item>
   137    138       free(zDate);
   138    139       nLine++;
   139    140     }
   140    141   
   141    142     db_finalize(&q);
   142    143     @   </channel>
   143    144     @ </rss>
   144    145   
   145    146     if( zFreeProjectName != 0 ){
   146    147       free( zFreeProjectName );
   147    148     }
   148    149   }

Changes to src/schema.c.

    37     37   /*
    38     38   ** The content tables have a content version number which rarely
    39     39   ** changes.  The aux tables have an arbitrary version number (typically
    40     40   ** a date) which can change frequently.  When the content schema changes,
    41     41   ** we have to execute special procedures to update the schema.  When
    42     42   ** the aux schema changes, all we need to do is rebuild the database.
    43     43   */
    44         -#define CONTENT_SCHEMA  "1"
    45         -#define AUX_SCHEMA      "2006-12-23"
           44  +#define CONTENT_SCHEMA  "2"
           45  +#define AUX_SCHEMA      "2011-04-25 19:50"
    46     46   
    47     47   #endif /* INTERFACE */
    48     48   
    49     49   
    50     50   /*
    51     51   ** The schema for a repository database.  
    52     52   **
    53     53   ** Schema1[] contains parts of the schema that are fixed and unchanging
    54     54   ** across versions.  Schema2[] contains parts of the schema that can
    55     55   ** change from one version to the next.  The information in Schema2[]
    56         -** can be reconstructed from the information in Schema1[].
           56  +** is reconstructed from the information in Schema1[] by the "rebuild"
           57  +** operation.
    57     58   */
    58     59   const char zRepositorySchema1[] = 
    59     60   @ -- The BLOB and DELTA tables contain all records held in the repository.
    60     61   @ --
    61         -@ -- The BLOB.CONTENT column is always compressed using libz.  This
           62  +@ -- The BLOB.CONTENT column is always compressed using zlib.  This
    62     63   @ -- column might hold the full text of the record or it might hold
    63     64   @ -- a delta that is able to reconstruct the record from some other
    64     65   @ -- record.  If BLOB.CONTENT holds a delta, then a DELTA table entry
    65     66   @ -- will exist for the record and that entry will point to another
    66     67   @ -- entry that holds the source of the delta.  Deltas can be chained.
           68  +@ --
           69  +@ -- The blob and delta tables collectively hold the "global state" of
           70  +@ -- a Fossil repository.  
    67     71   @ --
    68     72   @ CREATE TABLE blob(
    69     73   @   rid INTEGER PRIMARY KEY,        -- Record ID
    70     74   @   rcvid INTEGER,                  -- Origin of this record
    71     75   @   size INTEGER,                   -- Size of content. -1 for a phantom.
    72     76   @   uuid TEXT UNIQUE NOT NULL,      -- SHA1 hash of the content
    73     77   @   content BLOB,                   -- Compressed content of this record
................................................................................
    75     79   @ );
    76     80   @ CREATE TABLE delta(
    77     81   @   rid INTEGER PRIMARY KEY,                 -- Record ID
    78     82   @   srcid INTEGER NOT NULL REFERENCES blob   -- Record holding source document
    79     83   @ );
    80     84   @ CREATE INDEX delta_i1 ON delta(srcid);
    81     85   @
           86  +@ -------------------------------------------------------------------------
           87  +@ -- The BLOB and DELTA tables above hold the "global state" of a Fossil
           88  +@ -- project; the stuff that is normally exchanged during "sync".  The
           89  +@ -- "local state" of a repository is contained in the remaining tables of
           90  +@ -- the zRepositorySchema1 string.  
           91  +@ -------------------------------------------------------------------------
           92  +@
    82     93   @ -- Whenever new blobs are received into the repository, an entry
    83     94   @ -- in this table records the source of the blob.
    84     95   @ --
    85     96   @ CREATE TABLE rcvfrom(
    86     97   @   rcvid INTEGER PRIMARY KEY,      -- Received-From ID
    87     98   @   uid INTEGER REFERENCES user,    -- User login
    88         -@   mtime DATETIME,                 -- Time or receipt
           99  +@   mtime DATETIME,                 -- Time of receipt.  Julian day.
    89    100   @   nonce TEXT UNIQUE,              -- Nonce used for login
    90    101   @   ipaddr TEXT                     -- Remote IP address.  NULL for direct.
    91    102   @ );
    92    103   @
    93    104   @ -- Information about users
    94    105   @ --
    95    106   @ -- The user.pw field can be either cleartext of the password, or
................................................................................
    97    108   @ -- characters long we assume it is a SHA1 hash.  Otherwise, it is
    98    109   @ -- cleartext.  The sha1_shared_secret() routine computes the password
    99    110   @ -- hash based on the project-code, the user login, and the cleartext
   100    111   @ -- password.
   101    112   @ --
   102    113   @ CREATE TABLE user(
   103    114   @   uid INTEGER PRIMARY KEY,        -- User ID
   104         -@   login TEXT,                     -- login name of the user
          115  +@   login TEXT UNIQUE,              -- login name of the user
   105    116   @   pw TEXT,                        -- password
   106    117   @   cap TEXT,                       -- Capabilities of this user
   107    118   @   cookie TEXT,                    -- WWW login cookie
   108    119   @   ipaddr TEXT,                    -- IP address for which cookie is valid
   109    120   @   cexpire DATETIME,               -- Time when cookie expires
   110    121   @   info TEXT,                      -- contact information
          122  +@   mtime DATE,                     -- last change.  seconds since 1970
   111    123   @   photo BLOB                      -- JPEG image of this user
   112    124   @ );
   113    125   @
   114    126   @ -- The VAR table holds miscellanous information about the repository.
   115    127   @ -- in the form of name-value pairs.
   116    128   @ --
   117    129   @ CREATE TABLE config(
   118    130   @   name TEXT PRIMARY KEY NOT NULL,  -- Primary name of the entry
   119    131   @   value CLOB,                      -- Content of the named parameter
          132  +@   mtime DATE,                      -- last modified.  seconds since 1970
   120    133   @   CHECK( typeof(name)='text' AND length(name)>=1 )
   121    134   @ );
   122    135   @
   123    136   @ -- Artifacts that should not be processed are identified in the
   124    137   @ -- "shun" table.  Artifacts that are control-file forgeries or
   125    138   @ -- spam or artifacts whose contents violate administrative policy
   126    139   @ -- can be shunned in order to prevent them from contaminating
   127    140   @ -- the repository.
   128    141   @ --
   129    142   @ -- Shunned artifacts do not exist in the blob table.  Hence they
   130    143   @ -- have not artifact ID (rid) and we thus must store their full
   131    144   @ -- UUID.
   132    145   @ --
   133         -@ CREATE TABLE shun(uuid UNIQUE);
          146  +@ CREATE TABLE shun(
          147  +@   uuid UNIQUE,          -- UUID of artifact to be shunned. Canonical form
          148  +@   mtime DATE,           -- When added.  seconds since 1970
          149  +@   scom TEXT             -- Optional text explaining why the shun occurred
          150  +@ );
   134    151   @
   135    152   @ -- Artifacts that should not be pushed are stored in the "private"
   136    153   @ -- table.  Private artifacts are omitted from the "unclustered" and
   137    154   @ -- "unsent" tables.
   138    155   @ --
   139    156   @ CREATE TABLE private(rid INTEGER PRIMARY KEY);
   140    157   @
   141    158   @ -- An entry in this table describes a database query that generates a
   142    159   @ -- table of tickets.
   143    160   @ --
   144    161   @ CREATE TABLE reportfmt(
   145         -@    rn integer primary key,  -- Report number
   146         -@    owner text,              -- Owner of this report format (not used)
   147         -@    title text,              -- Title of this report
   148         -@    cols text,               -- A color-key specification
   149         -@    sqlcode text             -- An SQL SELECT statement for this report
          162  +@    rn INTEGER PRIMARY KEY,  -- Report number
          163  +@    owner TEXT,              -- Owner of this report format (not used)
          164  +@    title TEXT UNIQUE,       -- Title of this report
          165  +@    mtime DATE,              -- Last modified.  seconds since 1970
          166  +@    cols TEXT,               -- A color-key specification
          167  +@    sqlcode TEXT             -- An SQL SELECT statement for this report
   150    168   @ );
   151         -@ INSERT INTO reportfmt(title,cols,sqlcode) VALUES('All Tickets','#ffffff Key:
          169  +@ INSERT INTO reportfmt(title,mtime,cols,sqlcode) 
          170  +@ VALUES('All Tickets',julianday('1970-01-01'),'#ffffff Key:
   152    171   @ #f2dcdc Active
   153    172   @ #e8e8e8 Review
   154    173   @ #cfe8bd Fixed
   155    174   @ #bde5d6 Tested
   156    175   @ #cacae5 Deferred
   157    176   @ #c8c8c8 Closed','SELECT
   158    177   @   CASE WHEN status IN (''Open'',''Verified'') THEN ''#f2dcdc''
................................................................................
   174    193   @ -- by storing an SHA1 hash of the content.  For display, the hash is
   175    194   @ -- mapped back into the original text using this table.  
   176    195   @ --
   177    196   @ -- This table contains sensitive information and should not be shared
   178    197   @ -- with unauthorized users.
   179    198   @ --
   180    199   @ CREATE TABLE concealed(
   181         -@   hash TEXT PRIMARY KEY,
   182         -@   content TEXT
          200  +@   hash TEXT PRIMARY KEY,    -- The SHA1 hash of content
          201  +@   mtime DATE,               -- Time created.  Seconds since 1970
          202  +@   content TEXT              -- Content intended to be concealed
   183    203   @ );
   184    204   ;
   185    205   
   186    206   const char zRepositorySchema2[] =
   187    207   @ -- Filenames
   188    208   @ --
   189    209   @ CREATE TABLE filename(
   190    210   @   fnid INTEGER PRIMARY KEY,    -- Filename ID
   191    211   @   name TEXT UNIQUE             -- Name of file page
   192    212   @ );
   193    213   @
   194         -@ -- Linkages between manifests, files created by that manifest, and
          214  +@ -- Linkages between checkins, files created by each checkin, and
   195    215   @ -- the names of those files.
   196    216   @ --
   197         -@ -- pid==0 if the file is added by check-in mid.
   198         -@ -- fid==0 if the file is removed by check-in mid.
          217  +@ -- pid==0 if the file is added by checkin mid.
          218  +@ -- fid==0 if the file is removed by checkin mid.
   199    219   @ --
   200    220   @ CREATE TABLE mlink(
   201    221   @   mid INTEGER REFERENCES blob,        -- Manifest ID where change occurs
   202    222   @   pid INTEGER REFERENCES blob,        -- File ID in parent manifest
   203    223   @   fid INTEGER REFERENCES blob,        -- Changed file ID in this manifest
   204    224   @   fnid INTEGER REFERENCES filename,   -- Name of the file
   205         -@   pfnid INTEGER REFERENCES filename   -- Previous name. 0 if unchanged
          225  +@   pfnid INTEGER REFERENCES filename,  -- Previous name. 0 if unchanged
          226  +@   mperm INTEGER                       -- File permissions.  1==exec
   206    227   @ );
   207    228   @ CREATE INDEX mlink_i1 ON mlink(mid);
   208    229   @ CREATE INDEX mlink_i2 ON mlink(fnid);
   209    230   @ CREATE INDEX mlink_i3 ON mlink(fid);
   210    231   @ CREATE INDEX mlink_i4 ON mlink(pid);
   211    232   @
   212         -@ -- Parent/child linkages
          233  +@ -- Parent/child linkages between checkins
   213    234   @ --
   214    235   @ CREATE TABLE plink(
   215    236   @   pid INTEGER REFERENCES blob,    -- Parent manifest
   216    237   @   cid INTEGER REFERENCES blob,    -- Child manifest
   217    238   @   isprim BOOLEAN,                 -- pid is the primary parent of cid
   218         -@   mtime DATETIME,                 -- the date/time stamp on cid
          239  +@   mtime DATETIME,                 -- the date/time stamp on cid.  Julian day.
   219    240   @   UNIQUE(pid, cid)
   220    241   @ );
   221         -@ CREATE INDEX plink_i2 ON plink(cid);
          242  +@ CREATE INDEX plink_i2 ON plink(cid,pid);
          243  +@
          244  +@ -- A "leaf" checkin is a checkin that has no children in the same
          245  +@ -- branch.  The set of all leaves is easily computed with a join,
          246  +@ -- between the plink and tagxref tables, but it is a slower join for
          247  +@ -- very large repositories (repositories with 100,000 or more checkins)
          248  +@ -- and so it makes sense to precompute the set of leaves.  There is
          249  +@ -- one entry in the following table for each leaf.
          250  +@ --
          251  +@ CREATE TABLE leaf(rid INTEGER PRIMARY KEY);
   222    252   @
   223    253   @ -- Events used to generate a timeline
   224    254   @ --
   225    255   @ CREATE TABLE event(
   226         -@   type TEXT,                      -- Type of event
   227         -@   mtime DATETIME,                 -- Date and time when the event occurs
          256  +@   type TEXT,                      -- Type of event: 'ci', 'w', 'e', 't', 'g'
          257  +@   mtime DATETIME,                 -- Time of occurrence. Julian day.
   228    258   @   objid INTEGER PRIMARY KEY,      -- Associated record ID
   229    259   @   tagid INTEGER,                  -- Associated ticket or wiki name tag
   230    260   @   uid INTEGER REFERENCES user,    -- User who caused the event
   231    261   @   bgcolor TEXT,                   -- Color set by 'bgcolor' property
   232    262   @   euser TEXT,                     -- User set by 'user' property
   233    263   @   user TEXT,                      -- Name of the user
   234    264   @   ecomment TEXT,                  -- Comment set by 'comment' property
   235    265   @   comment TEXT,                   -- Comment describing the event
   236         -@   brief TEXT                      -- Short comment when tagid already seen
          266  +@   brief TEXT,                     -- Short comment when tagid already seen
          267  +@   omtime DATETIME                 -- Original unchanged date+time, or NULL
   237    268   @ );
   238    269   @ CREATE INDEX event_i1 ON event(mtime);
   239    270   @
   240    271   @ -- A record of phantoms.  A phantom is a record for which we know the
   241    272   @ -- UUID but we do not (yet) know the file content.
   242    273   @ --
   243    274   @ CREATE TABLE phantom(
   244    275   @   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
   245    276   @ );
          277  +@
          278  +@ -- A record of orphaned delta-manifests.  An orphan is a delta-manifest
          279  +@ -- for which we have content, but its baseline-manifest is a phantom.
          280  +@ -- We have to track all orphan maniftests so that when the baseline arrives,
          281  +@ -- we know to process the orphaned deltas.
          282  +@ CREATE TABLE orphan(
          283  +@   rid INTEGER PRIMARY KEY,        -- Delta manifest with a phantom baseline
          284  +@   baseline INTEGER                -- Phantom baseline of this orphan
          285  +@ );
          286  +@ CREATE INDEX orphan_baseline ON orphan(baseline);
   246    287   @
   247    288   @ -- Unclustered records.  An unclustered record is a record (including
   248    289   @ -- a cluster records themselves) that is not mentioned by some other
   249    290   @ -- cluster.
   250    291   @ --
   251    292   @ -- Phantoms are usually included in the unclustered table.  A new cluster
   252    293   @ -- will never be created that contains a phantom.  But another repository
................................................................................
   283    324   @ INSERT INTO tag VALUES(3, 'user');            -- TAG_USER
   284    325   @ INSERT INTO tag VALUES(4, 'date');            -- TAG_DATE
   285    326   @ INSERT INTO tag VALUES(5, 'hidden');          -- TAG_HIDDEN
   286    327   @ INSERT INTO tag VALUES(6, 'private');         -- TAG_PRIVATE
   287    328   @ INSERT INTO tag VALUES(7, 'cluster');         -- TAG_CLUSTER
   288    329   @ INSERT INTO tag VALUES(8, 'branch');          -- TAG_BRANCH
   289    330   @ INSERT INTO tag VALUES(9, 'closed');          -- TAG_CLOSED
          331  +@ INSERT INTO tag VALUES(10,'parent');          -- TAG_PARENT
   290    332   @
   291    333   @ -- Assignments of tags to baselines.  Note that we allow tags to
   292    334   @ -- have values assigned to them.  So we are not really dealing with
   293    335   @ -- tags here.  These are really properties.  But we are going to
   294    336   @ -- keep calling them tags because in many cases the value is ignored.
   295    337   @ --
   296    338   @ CREATE TABLE tagxref(
   297    339   @   tagid INTEGER REFERENCES tag,   -- The tag that added or removed
   298         -@   tagtype INTEGER,                -- 0:cancel  1:single  2:branch
          340  +@   tagtype INTEGER,                -- 0:-,cancel  1:+,single  2:*,propagate
   299    341   @   srcid INTEGER REFERENCES blob,  -- Artifact of tag. 0 for propagated tags
   300    342   @   origid INTEGER REFERENCES blob, -- check-in holding propagated tag
   301    343   @   value TEXT,                     -- Value of the tag.  Might be NULL.
   302         -@   mtime TIMESTAMP,                -- Time of addition or removal
          344  +@   mtime TIMESTAMP,                -- Time of addition or removal. Julian day
   303    345   @   rid INTEGER REFERENCE blob,     -- Artifact tag is applied to
   304    346   @   UNIQUE(rid, tagid)
   305    347   @ );
   306    348   @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
   307    349   @
   308    350   @ -- When a hyperlink occurs from one artifact to another (for example
   309    351   @ -- when a check-in comment refers to a ticket) an entry is made in
................................................................................
   310    352   @ -- the following table for that hyperlink.  This table is used to
   311    353   @ -- facilitate the display of "back links".
   312    354   @ --
   313    355   @ CREATE TABLE backlink(
   314    356   @   target TEXT,           -- Where the hyperlink points to
   315    357   @   srctype INT,           -- 0: check-in  1: ticket  2: wiki
   316    358   @   srcid INT,             -- rid for checkin or wiki.  tkt_id for ticket.
   317         -@   mtime TIMESTAMP,       -- time that the hyperlink was added
          359  +@   mtime TIMESTAMP,       -- time that the hyperlink was added. Julian day.
   318    360   @   UNIQUE(target, srctype, srcid)
   319    361   @ );
   320    362   @ CREATE INDEX backlink_src ON backlink(srcid, srctype);
   321    363   @
   322    364   @ -- Each attachment is an entry in the following table.  Only
   323    365   @ -- the most recent attachment (identified by the D card) is saved.
   324    366   @ --
   325    367   @ CREATE TABLE attachment(
   326    368   @   attachid INTEGER PRIMARY KEY,   -- Local id for this attachment
   327    369   @   isLatest BOOLEAN DEFAULT 0,     -- True if this is the one to use
   328         -@   mtime TIMESTAMP,                -- Time when attachment last changed
          370  +@   mtime TIMESTAMP,                -- Last changed.  Julian day.
   329    371   @   src TEXT,                       -- UUID of the attachment.  NULL to delete
   330    372   @   target TEXT,                    -- Object attached to. Wikiname or Tkt UUID
   331    373   @   filename TEXT,                  -- Filename for the attachment
   332    374   @   comment TEXT,                   -- Comment associated with this attachment
   333    375   @   user TEXT                       -- Name of user adding attachment
   334    376   @ );
   335    377   @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
................................................................................
   341    383   @ -- same change in tktsetup.c.
   342    384   @ --
   343    385   @ CREATE TABLE ticket(
   344    386   @   -- Do not change any column that begins with tkt_
   345    387   @   tkt_id INTEGER PRIMARY KEY,
   346    388   @   tkt_uuid TEXT UNIQUE,
   347    389   @   tkt_mtime DATE,
          390  +@   tkt_ctime DATE,
   348    391   @   -- Add as many field as required below this line
   349    392   @   type TEXT,
   350    393   @   status TEXT,
   351    394   @   subsystem TEXT,
   352    395   @   priority TEXT,
   353    396   @   severity TEXT,
   354    397   @   foundin TEXT,
   355    398   @   private_contact TEXT,
   356    399   @   resolution TEXT,
   357    400   @   title TEXT,
   358    401   @   comment TEXT
   359    402   @ );
          403  +@ CREATE TABLE ticketchng(
          404  +@   -- Do not change any column that begins with tkt_
          405  +@   tkt_id INTEGER REFERENCES ticket,
          406  +@   tkt_rid INTEGER REFERENCES blob,
          407  +@   tkt_mtime DATE,
          408  +@   -- Add as many fields as required below this line
          409  +@   login TEXT,
          410  +@   username TEXT,
          411  +@   mimetype TEXT,
          412  +@   icomment TEXT
          413  +@ );
          414  +@ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime);
   360    415   ;
   361    416   
   362    417   /*
   363    418   ** Predefined tagid values
   364    419   */
   365    420   #if INTERFACE
   366    421   # define TAG_BGCOLOR    1     /* Set the background color for display */
................................................................................
   368    423   # define TAG_USER       3     /* User who made a checking */
   369    424   # define TAG_DATE       4     /* The date of a check-in */
   370    425   # define TAG_HIDDEN     5     /* Do not display or sync */
   371    426   # define TAG_PRIVATE    6     /* Display but do not sync */
   372    427   # define TAG_CLUSTER    7     /* A cluster */
   373    428   # define TAG_BRANCH     8     /* Value is name of the current branch */
   374    429   # define TAG_CLOSED     9     /* Do not display this check-in as a leaf */
          430  +# define TAG_PARENT     10    /* Change to parentage on a checkin */
   375    431   #endif
   376    432   #if EXPORT_INTERFACE
   377         -# define MAX_INT_TAG    9     /* The largest pre-assigned tag id */
          433  +# define MAX_INT_TAG    16    /* The largest pre-assigned tag id */
   378    434   #endif
   379    435   
   380    436   /*
   381    437   ** The schema for the locate FOSSIL database file found at the root
   382    438   ** of very check-out.  This database contains the complete state of
   383    439   ** the checkout.
   384    440   */
................................................................................
   416    472   @ --
   417    473   @ CREATE TABLE vfile(
   418    474   @   id INTEGER PRIMARY KEY,           -- ID of the checked out file
   419    475   @   vid INTEGER REFERENCES blob,      -- The baseline this file is part of.
   420    476   @   chnged INT DEFAULT 0,             -- 0:unchnged 1:edited 2:m-chng 3:m-add
   421    477   @   deleted BOOLEAN DEFAULT 0,        -- True if deleted 
   422    478   @   isexe BOOLEAN,                    -- True if file should be executable
          479  +@   islink BOOLEAN,                    -- True if file should be symlink
   423    480   @   rid INTEGER,                      -- Originally from this repository record
   424    481   @   mrid INTEGER,                     -- Based on this record due to a merge
   425         -@   mtime INTEGER,                    -- Modification time of file on disk
          482  +@   mtime INTEGER,                    -- Mtime of file on disk. sec since 1970
   426    483   @   pathname TEXT,                    -- Full pathname relative to root
   427    484   @   origname TEXT,                    -- Original pathname. NULL if unchanged
   428    485   @   UNIQUE(pathname,vid)
   429    486   @ );
   430    487   @
   431    488   @ -- This table holds a record of uncommitted merges in the local
   432    489   @ -- file tree.  If a VFILE entry with id has merged with another
   433    490   @ -- record, there is an entry in this table with (id,merge) where
   434    491   @ -- merge is the RECORD table entry that the file merged against.
   435         -@ -- An id of 0 here means the version record itself.
          492  +@ -- An id of 0 here means the version record itself.  When id==(-1)
          493  +@ -- that is a cherrypick merge and id==(-2) is a backout merge.
   436    494   @
   437    495   @ CREATE TABLE vmerge(
   438    496   @   id INTEGER REFERENCES vfile,      -- VFILE entry that has been merged
   439    497   @   merge INTEGER,                    -- Merged with this record
   440    498   @   UNIQUE(id, merge)
   441    499   @ );
   442    500   @   
   443    501   ;

Changes to src/search.c.

    40     40   */
    41     41   Search *search_init(const char *zPattern){
    42     42     int nPattern = strlen(zPattern);
    43     43     Search *p;
    44     44     char *z;
    45     45     int i;
    46     46   
    47         -  p = malloc( nPattern + sizeof(*p) + 1);
    48         -  if( p==0 ) fossil_panic("out of memory");
           47  +  p = fossil_malloc( nPattern + sizeof(*p) + 1);
    49     48     z = (char*)&p[1];
    50         -  strcpy(z, zPattern);
           49  +  memcpy(z, zPattern, nPattern+1);
    51     50     memset(p, 0, sizeof(*p));
    52     51     while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){
    53         -    while( !isalnum(*z) && *z ){ z++; }
           52  +    while( !fossil_isalnum(*z) && *z ){ z++; }
    54     53       if( *z==0 ) break;
    55     54       p->a[p->nTerm].z = z;
    56         -    for(i=1; isalnum(z[i]) || z[i]=='_'; i++){}
           55  +    for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){}
    57     56       p->a[p->nTerm].n = i;
    58     57       z += i;
    59     58       p->nTerm++;
    60     59     }
    61     60     return p;
    62     61   }
    63     62   
................................................................................
    92     91   };
    93     92   
    94     93   /*
    95     94   ** Compare a search pattern against an input string and return a score.
    96     95   **
    97     96   ** Scoring:
    98     97   **   *  All terms must match at least once or the score is zero
    99         -**   *  10 bonus points if the first occurrance is an exact match
           98  +**   *  10 bonus points if the first occurrence is an exact match
   100     99   **   *  1 additional point for each subsequent match of the same word
   101    100   **   *  Extra points of two consecutive words of the pattern are consecutive
   102    101   **      in the document
   103    102   */
   104    103   int search_score(Search *p, const char *zDoc){
   105    104     int iPrev = 999;
   106    105     int score = 10;
................................................................................
   164    163     sqlite3_create_function(g.db, "score", 1, SQLITE_UTF8, p,
   165    164        search_score_sqlfunc, 0, 0);
   166    165   }
   167    166   
   168    167   /*
   169    168   ** Testing the search function.
   170    169   **
   171         -** COMMAND: search
          170  +** COMMAND: search*
   172    171   ** %fossil search pattern...
   173    172   **
   174    173   ** Search for timeline entries matching the pattern.
   175    174   */
   176    175   void search_cmd(void){
   177    176     Search *p;
   178    177     Blob pattern;
................................................................................
   202    201     );
   203    202     iBest = db_int(0, "SELECT max(x) FROM srch");
   204    203     db_prepare(&q, 
   205    204       "SELECT rid, uuid, date, comment, 0, 0 FROM srch"
   206    205       " WHERE x>%d ORDER BY x DESC, date DESC",
   207    206       iBest/3
   208    207     );
   209         -  print_timeline(&q, 1000);
          208  +  print_timeline(&q, 1000, 0);
   210    209     db_finalize(&q);
   211    210   }

Changes to src/setup.c.

    17     17   **
    18     18   ** Implementation of the Setup page
    19     19   */
    20     20   #include <assert.h>
    21     21   #include "config.h"
    22     22   #include "setup.h"
    23     23   
           24  +/*
           25  +** The table of web pages supported by this application is generated
           26  +** automatically by the "mkindex" program and written into a file
           27  +** named "page_index.h".  We include that file here to get access
           28  +** to the table.
           29  +*/
           30  +#include "page_index.h"
    24     31   
    25     32   /*
    26     33   ** Output a single entry for a menu generated using an HTML table.
    27     34   ** If zLink is not NULL or an empty string, then it is the page that
    28     35   ** the menu entry will hyperlink to.  If zLink is NULL or "", then
    29     36   ** the menu entry has no hyperlink - it is disabled.
    30     37   */
................................................................................
    35     42   ){
    36     43     @ <tr><td valign="top" align="right">
    37     44     if( zLink && zLink[0] ){
    38     45       @ <a href="%s(zLink)">%h(zTitle)</a>
    39     46     }else{
    40     47       @ %h(zTitle)
    41     48     }
    42         -  @ </td><td valign="top">%h(zDesc)</td></tr>
           49  +  @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr>
    43     50   }
    44     51   
    45     52   /*
    46     53   ** WEBPAGE: /setup
    47     54   */
    48     55   void setup_page(void){
    49     56     login_check_credentials();
    50         -  if( !g.okSetup ){
           57  +  if( !g.perm.Setup ){
    51     58       login_needed();
    52     59     }
    53     60   
    54     61     style_header("Server Administration");
    55         -  @ <table border="0" cellspacing="20">
           62  +
           63  +  /* Make sure the header contains <base href="...">.   Issue a warning
           64  +  ** if it does not. */
           65  +  if( !cgi_header_contains("<base href=") ){
           66  +    @ <p class="generalError"><b>Configuration Error:</b> Please add
           67  +    @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
           68  +    @ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
           69  +  }
           70  +
           71  +  @ <table border="0" cellspacing="7">
    56     72     setup_menu_entry("Users", "setup_ulist",
    57     73       "Grant privileges to individual users.");
    58     74     setup_menu_entry("Access", "setup_access",
    59     75       "Control access settings.");
    60     76     setup_menu_entry("Configuration", "setup_config",
    61     77       "Configure the WWW components of the repository");
           78  +  setup_menu_entry("Settings", "setup_settings",
           79  +    "Web interface to the \"fossil settings\" command");
    62     80     setup_menu_entry("Timeline", "setup_timeline",
    63     81       "Timeline display preferences");
           82  +  setup_menu_entry("Login-Group", "setup_login_group",
           83  +    "Manage single sign-on between this repository and others"
           84  +    " on the same server");
    64     85     setup_menu_entry("Tickets", "tktsetup",
    65     86       "Configure the trouble-ticketing system for this repository");
           87  +  setup_menu_entry("Transfers", "xfersetup",
           88  +    "Configure the transfer system for this repository");
    66     89     setup_menu_entry("Skins", "setup_skin",
    67     90       "Select from a menu of prepackaged \"skins\" for the web interface");
    68     91     setup_menu_entry("CSS", "setup_editcss",
    69     92       "Edit the Cascading Style Sheet used by all pages of this repository");
    70     93     setup_menu_entry("Header", "setup_header",
    71     94       "Edit HTML text inserted at the top of every page");
    72     95     setup_menu_entry("Footer", "setup_footer",
    73     96       "Edit HTML text inserted at the bottom of every page");
           97  +  setup_menu_entry("Moderation", "setup_modreq",
           98  +    "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
           99  +    " changes and attachments.");
          100  +  setup_menu_entry("Ad-Unit", "setup_adunit",
          101  +    "Edit HTML text for an ad unit inserted after the menu bar");
    74    102     setup_menu_entry("Logo", "setup_logo",
    75         -    "Change the logo image for the server");
          103  +    "Change the logo and background images for the server");
    76    104     setup_menu_entry("Shunned", "shun",
    77    105       "Show artifacts that are shunned by this repository");
    78    106     setup_menu_entry("Log", "rcvfromlist",
    79    107       "A record of received artifacts and their sources");
          108  +  setup_menu_entry("User-Log", "access_log",
          109  +    "A record of login attempts");
    80    110     setup_menu_entry("Stats", "stat",
    81    111       "Display repository statistics");
          112  +  setup_menu_entry("SQL", "admin_sql",
          113  +    "Enter raw SQL commands");
          114  +  setup_menu_entry("TH1", "admin_th1",
          115  +    "Enter raw TH1 commands");
    82    116     @ </table>
    83    117   
    84    118     style_footer();
    85    119   }
    86    120   
    87    121   /*
    88    122   ** WEBPAGE: setup_ulist
    89    123   **
    90    124   ** Show a list of users.  Clicking on any user jumps to the edit
    91    125   ** screen for that user.
    92    126   */
    93    127   void setup_ulist(void){
    94    128     Stmt s;
          129  +  int prevLevel = 0;
    95    130   
    96    131     login_check_credentials();
    97         -  if( !g.okAdmin ){
          132  +  if( !g.perm.Admin ){
    98    133       login_needed();
    99    134       return;
   100    135     }
   101    136   
   102    137     style_submenu_element("Add", "Add User", "setup_uedit");
   103    138     style_header("User List");
   104         -  @ <table border="0" cellpadding="0" cellspacing="25">
   105         -  @ <tr><td valign="top">
   106         -  @ <b>Users:</b>
   107         -  @ <table border="1" cellpadding="10"><tr><td>
   108         -  @ <table cellspacing=0 cellpadding=0 border=0>
   109         -  @ <tr>
   110         -  @   <th align="right">User&nbsp;ID</th><td width="20">&nbsp;</td>
   111         -  @   <th>Capabilities</th><td width="15">&nbsp;</td>
   112         -  @   <th>Contact&nbsp;Info</th>
   113         -  @ </tr>
   114         -  db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
          139  +  @ <table class="usetupLayoutTable">
          140  +  @ <tr><td class="usetupColumnLayout">
          141  +  @ <span class="note">Users:</span>
          142  +  @ <table class="usetupUserList">
          143  +  prevLevel = 0;
          144  +  db_prepare(&s, 
          145  +     "SELECT uid, login, cap, info, 1 FROM user"
          146  +     " WHERE login IN ('anonymous','nobody','developer','reader') "
          147  +     " UNION ALL "
          148  +     "SELECT uid, login, cap, info, 2 FROM user"
          149  +     " WHERE login NOT IN ('anonymous','nobody','developer','reader') "
          150  +     "ORDER BY 5, 2"
          151  +  );
   115    152     while( db_step(&s)==SQLITE_ROW ){
          153  +    int iLevel = db_column_int(&s, 4);
   116    154       const char *zCap = db_column_text(&s, 2);
   117         -    if( strstr(zCap, "s") ) zCap = "s";
          155  +    const char *zLogin = db_column_text(&s, 1);
          156  +    if( iLevel>prevLevel ){
          157  +      if( prevLevel>0 ){
          158  +        @ <tr><td colspan="3"><hr></td></tr>
          159  +      }
          160  +      if( iLevel==1 ){
          161  +        @ <tr>
          162  +        @   <th class="usetupListUser"
          163  +        @    style="text-align: right;padding-right: 20px;">Category</th>
          164  +        @   <th class="usetupListCap"
          165  +        @    style="text-align: center;padding-right: 15px;">Capabilities</th>
          166  +        @   <th class="usetupListCon"
          167  +        @    style="text-align: left;">Notes</th>
          168  +        @ </tr>
          169  +      }else{
          170  +        @ <tr>
          171  +        @   <th class="usetupListUser"
          172  +        @    style="text-align: right;padding-right: 20px;">User&nbsp;ID</th>
          173  +        @   <th class="usetupListCap"
          174  +        @    style="text-align: center;padding-right: 15px;">Capabilities</th>
          175  +        @   <th class="usetupListCon"
          176  +        @    style="text-align: left;">Contact&nbsp;Info</th>
          177  +        @ </tr>
          178  +      }
          179  +      prevLevel = iLevel;
          180  +    }
   118    181       @ <tr>
   119         -    @ <td align="right">
   120         -    if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
          182  +    @ <td class="usetupListUser"
          183  +    @     style="text-align: right;padding-right: 20px;white-space:nowrap;">
          184  +    if( g.perm.Admin && (zCap[0]!='s' || g.perm.Setup) ){
   121    185         @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
   122    186       }
   123         -    @ <nobr>%h(db_column_text(&s,1))</nobr>
   124         -    if( g.okAdmin ){
          187  +    @ %h(zLogin)
          188  +    if( g.perm.Admin ){
   125    189         @ </a>
   126    190       }
   127         -    @ </td><td>&nbsp;&nbsp;&nbsp;</td>
   128         -    @ <td align="center">%s(zCap)</td>
   129         -    @ <td>&nbsp;&nbsp;&nbsp;</td>
   130         -    @ <td align="left">%s(db_column_text(&s,3))</td>
          191  +    @ </td>
          192  +    @ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td>
          193  +    @ <td  class="usetupListCon"  style="text-align: left;">%h(db_column_text(&s,3))</td>
   131    194       @ </tr>
   132    195     }
   133         -  @ </table></td></tr></table>
   134         -  @ <td valign="top">
   135         -  @ <b>Notes:</b>
          196  +  @ </table>
          197  +  @ </td><td class="usetupColumnLayout">
          198  +  @ <span class="note">Notes:</span>
   136    199     @ <ol>
   137    200     @ <li><p>The permission flags are as follows:</p>
   138    201     @ <table>
   139    202        @ <tr><td valign="top"><b>a</b></td>
   140    203        @   <td><i>Admin:</i> Create and delete users</td></tr>
   141    204        @ <tr><td valign="top"><b>b</b></td>
   142    205        @   <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
................................................................................
   155    218        @   repository history</td></tr>
   156    219        @ <tr><td valign="top"><b>i</b></td>
   157    220        @   <td><i>Check-In:</i> Commit new versions in the repository</td></tr>
   158    221        @ <tr><td valign="top"><b>j</b></td>
   159    222        @   <td><i>Read-Wiki:</i> View wiki pages</td></tr>
   160    223        @ <tr><td valign="top"><b>k</b></td>
   161    224        @   <td><i>Write-Wiki:</i> Edit wiki pages</td></tr>
          225  +     @ <tr><td valign="top"><b>l</b></td>
          226  +     @   <td><i>Mod-Wiki:</i> Moderator for wiki pages</td></tr>
   162    227        @ <tr><td valign="top"><b>m</b></td>
   163    228        @   <td><i>Append-Wiki:</i> Append to wiki pages</td></tr>
   164    229        @ <tr><td valign="top"><b>n</b></td>
   165    230        @   <td><i>New-Tkt:</i> Create new tickets</td></tr>
   166    231        @ <tr><td valign="top"><b>o</b></td>
   167    232        @   <td><i>Check-Out:</i> Check out versions</td></tr>
   168    233        @ <tr><td valign="top"><b>p</b></td>
   169    234        @   <td><i>Password:</i> Change your own password</td></tr>
          235  +     @ <tr><td valign="top"><b>q</b></td>
          236  +     @   <td><i>Mod-Tkt:</i> Moderator for tickets</td></tr>
   170    237        @ <tr><td valign="top"><b>r</b></td>
   171    238        @   <td><i>Read-Tkt:</i> View tickets</td></tr>
   172    239        @ <tr><td valign="top"><b>s</b></td>
   173    240        @   <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr>
   174    241        @ <tr><td valign="top"><b>t</b></td>
   175    242        @   <td><i>Tkt-Report:</i> Create new bug summary reports</td></tr>
   176    243        @ <tr><td valign="top"><b>u</b></td>
................................................................................
   177    244        @   <td><i>Reader:</i> Inherit privileges of
   178    245        @   user <tt>reader</tt></td></tr>
   179    246        @ <tr><td valign="top"><b>v</b></td>
   180    247        @   <td><i>Developer:</i> Inherit privileges of
   181    248        @   user <tt>developer</tt></td></tr>
   182    249        @ <tr><td valign="top"><b>w</b></td>
   183    250        @   <td><i>Write-Tkt:</i> Edit tickets</td></tr>
          251  +     @ <tr><td valign="top"><b>x</b></td>
          252  +     @   <td><i>Private:</i> Push and/or pull private branches</td></tr>
   184    253        @ <tr><td valign="top"><b>z</b></td>
   185    254        @   <td><i>Zip download:</i> Download a baseline via the
   186         -     @   <tt>/zip</tt> URL even without check<b>o</b>ut
   187         -     @    and <b>h</b>istory permissions</td></tr>
          255  +     @   <tt>/zip</tt> URL even without 
          256  +     @    check<span class="capability">o</span>ut
          257  +     @    and <span class="capability">h</span>istory permissions</td></tr>
   188    258     @ </table>
   189    259     @ </li>
   190    260     @
   191    261     @ <li><p>
   192         -  @ Every user, logged in or not, inherits the privileges of <b>nobody</b>.
          262  +  @ Every user, logged in or not, inherits the privileges of
          263  +  @ <span class="usertype">nobody</span>.
   193    264     @ </p></li>
   194    265     @
   195    266     @ <li><p>
   196         -  @ Any human can login as <b>anonymous</b> since the password is
   197         -  @ clearly displayed on the login page for them to type.  The purpose
   198         -  @ of requiring anonymous to log in is to prevent access by spiders.
          267  +  @ Any human can login as <span class="usertype">anonymous</span> since the
          268  +  @ password is clearly displayed on the login page for them to type. The
          269  +  @ purpose of requiring anonymous to log in is to prevent access by spiders.
   199    270     @ Every logged-in user inherits the combined privileges of
   200         -  @ <b>anonymous</b> and
   201         -  @ <b>nobody</b>.
          271  +  @ <span class="usertype">anonymous</span> and
          272  +  @ <span class="usertype">nobody</span>.
   202    273     @ </p></li>
   203    274     @
   204    275     @ <li><p>
   205         -  @ Users with privilege <b>v</b> inherit the combined privileges of
   206         -  @ <b>developer</b>, <b>anonymous</b>, and <b>nobody</b>.
          276  +  @ Users with privilege <span class="capability">v</span> inherit the combined
          277  +  @ privileges of <span class="usertype">developer</span>,
          278  +  @ <span class="usertype">anonymous</span>, and
          279  +  @ <span class="usertype">nobody</span>.
   207    280     @ </p></li>
   208    281     @
   209    282     @ </ol>
   210    283     @ </td></tr></table>
   211    284     style_footer();
   212    285   }
   213    286   
................................................................................
   226    299   }
   227    300   
   228    301   /*
   229    302   ** WEBPAGE: /setup_uedit
   230    303   */
   231    304   void user_edit(void){
   232    305     const char *zId, *zLogin, *zInfo, *zCap, *zPw;
   233         -  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
   234         -  char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
   235         -  char *oat, *oau, *oav, *oab, *oaz;
   236         -  const char *inherit[128];
          306  +  const char *zGroup;
          307  +  const char *zOldLogin;
   237    308     int doWrite;
   238         -  int uid;
          309  +  int uid, i;
   239    310     int higherUser = 0;  /* True if user being edited is SETUP and the */
   240    311                          /* user doing the editing is ADMIN.  Disallow editing */
          312  +  char *inherit[128];
          313  +  int a[128];
          314  +  char *oa[128];
   241    315   
   242         -  /* Must have ADMIN privleges to access this page
          316  +  /* Must have ADMIN privileges to access this page
   243    317     */
   244    318     login_check_credentials();
   245         -  if( !g.okAdmin ){ login_needed(); return; }
          319  +  if( !g.perm.Admin ){ login_needed(); return; }
   246    320   
   247    321     /* Check to see if an ADMIN user is trying to edit a SETUP account.
   248    322     ** Don't allow that.
   249    323     */
   250    324     zId = PD("id", "0");
   251    325     uid = atoi(zId);
   252         -  if( zId && !g.okSetup && uid>0 ){
          326  +  if( zId && !g.perm.Setup && uid>0 ){
   253    327       char *zOldCaps;
   254    328       zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid);
   255    329       higherUser = zOldCaps && strchr(zOldCaps,'s');
   256    330     }
   257    331   
   258    332     if( P("can") ){
   259    333       cgi_redirect("setup_ulist");
................................................................................
   262    336   
   263    337     /* If we have all the necessary information, write the new or
   264    338     ** modified user record.  After writing the user record, redirect
   265    339     ** to the page that displays a list of users.
   266    340     */
   267    341     doWrite = cgi_all("login","info","pw") && !higherUser;
   268    342     if( doWrite ){
   269         -    char zCap[50];
   270         -    int i = 0;
   271         -    int aa = P("aa")!=0;
   272         -    int ab = P("ab")!=0;
   273         -    int ad = P("ad")!=0;
   274         -    int ae = P("ae")!=0;
   275         -    int ai = P("ai")!=0;
   276         -    int aj = P("aj")!=0;
   277         -    int ak = P("ak")!=0;
   278         -    int an = P("an")!=0;
   279         -    int ao = P("ao")!=0;
   280         -    int ap = P("ap")!=0;
   281         -    int ar = P("ar")!=0;
   282         -    int as = g.okSetup && P("as")!=0;
   283         -    int aw = P("aw")!=0;
   284         -    int ac = P("ac")!=0;
   285         -    int af = P("af")!=0;
   286         -    int am = P("am")!=0;
   287         -    int ah = P("ah")!=0;
   288         -    int ag = P("ag")!=0;
   289         -    int at = P("at")!=0;
   290         -    int au = P("au")!=0;
   291         -    int av = P("av")!=0;
   292         -    int az = P("az")!=0;
   293         -    if( aa ){ zCap[i++] = 'a'; }
   294         -    if( ab ){ zCap[i++] = 'b'; }
   295         -    if( ac ){ zCap[i++] = 'c'; }
   296         -    if( ad ){ zCap[i++] = 'd'; }
   297         -    if( ae ){ zCap[i++] = 'e'; }
   298         -    if( af ){ zCap[i++] = 'f'; }
   299         -    if( ah ){ zCap[i++] = 'h'; }
   300         -    if( ag ){ zCap[i++] = 'g'; }
   301         -    if( ai ){ zCap[i++] = 'i'; }
   302         -    if( aj ){ zCap[i++] = 'j'; }
   303         -    if( ak ){ zCap[i++] = 'k'; }
   304         -    if( am ){ zCap[i++] = 'm'; }
   305         -    if( an ){ zCap[i++] = 'n'; }
   306         -    if( ao ){ zCap[i++] = 'o'; }
   307         -    if( ap ){ zCap[i++] = 'p'; }
   308         -    if( ar ){ zCap[i++] = 'r'; }
   309         -    if( as ){ zCap[i++] = 's'; }
   310         -    if( at ){ zCap[i++] = 't'; }
   311         -    if( au ){ zCap[i++] = 'u'; }
   312         -    if( av ){ zCap[i++] = 'v'; }
   313         -    if( aw ){ zCap[i++] = 'w'; }
   314         -    if( az ){ zCap[i++] = 'z'; }
          343  +    char c;
          344  +    char zCap[50], zNm[4];
          345  +    zNm[0] = 'a';
          346  +    zNm[2] = 0;
          347  +    for(i=0, c='a'; c<='z'; c++){
          348  +      zNm[1] = c;
          349  +      a[c&0x7f] = (c!='s' || g.perm.Setup) && P(zNm)!=0;
          350  +      if( a[c&0x7f] ) zCap[i++] = c;
          351  +    }
   315    352   
   316    353       zCap[i] = 0;
   317    354       zPw = P("pw");
   318    355       zLogin = P("login");
          356  +    if( strlen(zLogin)==0 ){
          357  +      style_header("User Creation Error");
          358  +      @ <span class="loginError">Empty login not allowed.</span>
          359  +      @
          360  +      @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
          361  +      style_footer();
          362  +      return;      
          363  +    }
   319    364       if( isValidPwString(zPw) ){
   320         -      zPw = sha1_shared_secret(zPw, zLogin);
          365  +      zPw = sha1_shared_secret(zPw, zLogin, 0);
   321    366       }else{
   322    367         zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
   323    368       }
          369  +    zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
   324    370       if( uid>0 &&
   325    371           db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
   326    372       ){
   327    373         style_header("User Creation Error");
   328         -      @ <font color="red">Login "%h(zLogin)" is already used by a different
   329         -      @ user.</font>
          374  +      @ <span class="loginError">Login "%h(zLogin)" is already used by
          375  +      @ a different user.</span>
   330    376         @
   331    377         @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
   332    378         style_footer();
   333    379         return;
   334    380       }
   335    381       login_verify_csrf_secret();
   336    382       db_multi_exec(
   337         -       "REPLACE INTO user(uid,login,info,pw,cap) "
   338         -       "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')",
          383  +       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
          384  +       "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s',now())",
   339    385         uid, P("login"), P("info"), zPw, zCap
   340    386       );
          387  +    if( atoi(PD("all","0"))>0 ){
          388  +      Blob sql;
          389  +      char *zErr = 0;
          390  +      blob_zero(&sql);
          391  +      if( zOldLogin==0 ){
          392  +        blob_appendf(&sql,
          393  +          "INSERT INTO user(login)"
          394  +          "  SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);",
          395  +          zLogin, zLogin
          396  +        );
          397  +        zOldLogin = zLogin;
          398  +      }
          399  +      blob_appendf(&sql, 
          400  +        "UPDATE user SET login=%Q,"
          401  +        "  pw=coalesce(shared_secret(%Q,%Q,"
          402  +                "(SELECT value FROM config WHERE name='project-code')),pw),"
          403  +        "  info=%Q,"
          404  +        "  cap=%Q,"
          405  +        "  mtime=now()"
          406  +        " WHERE login=%Q;",
          407  +        zLogin, P("pw"), zLogin, P("info"), zCap,
          408  +        zOldLogin
          409  +      );
          410  +      login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
          411  +      blob_reset(&sql);
          412  +      if( zErr ){
          413  +        style_header("User Change Error");
          414  +        @ <span class="loginError">%s(zErr)</span>
          415  +        @
          416  +        @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
          417  +        style_footer();
          418  +        return;
          419  +      }
          420  +    }
   341    421       cgi_redirect("setup_ulist");
   342    422       return;
   343    423     }
   344    424   
   345    425     /* Load the existing information about the user, if any
   346    426     */
   347    427     zLogin = "";
   348    428     zInfo = "";
   349    429     zCap = "";
   350    430     zPw = "";
   351         -  oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
   352         -        oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
          431  +  for(i='a'; i<='z'; i++) oa[i] = "";
   353    432     if( uid ){
   354    433       zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
   355    434       zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
   356    435       zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
   357    436       zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
   358         -    if( strchr(zCap, 'a') ) oaa = " checked";
   359         -    if( strchr(zCap, 'b') ) oab = " checked";
   360         -    if( strchr(zCap, 'c') ) oac = " checked";
   361         -    if( strchr(zCap, 'd') ) oad = " checked";
   362         -    if( strchr(zCap, 'e') ) oae = " checked";
   363         -    if( strchr(zCap, 'f') ) oaf = " checked";
   364         -    if( strchr(zCap, 'g') ) oag = " checked";
   365         -    if( strchr(zCap, 'h') ) oah = " checked";
   366         -    if( strchr(zCap, 'i') ) oai = " checked";
   367         -    if( strchr(zCap, 'j') ) oaj = " checked";
   368         -    if( strchr(zCap, 'k') ) oak = " checked";
   369         -    if( strchr(zCap, 'm') ) oam = " checked";
   370         -    if( strchr(zCap, 'n') ) oan = " checked";
   371         -    if( strchr(zCap, 'o') ) oao = " checked";
   372         -    if( strchr(zCap, 'p') ) oap = " checked";
   373         -    if( strchr(zCap, 'r') ) oar = " checked";
   374         -    if( strchr(zCap, 's') ) oas = " checked";
   375         -    if( strchr(zCap, 't') ) oat = " checked";
   376         -    if( strchr(zCap, 'u') ) oau = " checked";
   377         -    if( strchr(zCap, 'v') ) oav = " checked";
   378         -    if( strchr(zCap, 'w') ) oaw = " checked";
   379         -    if( strchr(zCap, 'z') ) oaz = " checked";
          437  +    for(i=0; zCap[i]; i++){
          438  +      char c = zCap[i];
          439  +      if( c>='a' && c<='z' ) oa[c&0x7f] = " checked=\"checked\"";
          440  +    }
   380    441     }
   381    442   
   382    443     /* figure out inherited permissions */
   383    444     memset(inherit, 0, sizeof(inherit));
   384         -  if( strcmp(zLogin, "developer") ){
          445  +  if( fossil_strcmp(zLogin, "developer") ){
   385    446       char *z1, *z2;
   386    447       z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
   387    448       while( z1 && *z1 ){
   388         -      inherit[0x7f & *(z1++)] = "<font color=\"red\">&bull;</font>";
          449  +      inherit[0x7f & *(z1++)] =
          450  +         "<span class=\"ueditInheritDeveloper\">&bull;</span>";
   389    451       }
   390    452       free(z2);
   391    453     }
   392         -  if( strcmp(zLogin, "reader") ){
          454  +  if( fossil_strcmp(zLogin, "reader") ){
   393    455       char *z1, *z2;
   394    456       z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
   395    457       while( z1 && *z1 ){
   396         -      inherit[0x7f & *(z1++)] = "<font color=\"black\">&bull;</font>";
          458  +      inherit[0x7f & *(z1++)] =
          459  +          "<span class=\"ueditInheritReader\">&bull;</span>";
   397    460       }
   398    461       free(z2);
   399    462     }
   400         -  if( strcmp(zLogin, "anonymous") ){
          463  +  if( fossil_strcmp(zLogin, "anonymous") ){
   401    464       char *z1, *z2;
   402    465       z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
   403    466       while( z1 && *z1 ){
   404         -      inherit[0x7f & *(z1++)] = "<font color=\"blue\">&bull;</font>";
          467  +      inherit[0x7f & *(z1++)] =
          468  +           "<span class=\"ueditInheritAnonymous\">&bull;</span>";
   405    469       }
   406    470       free(z2);
   407    471     }
   408         -  if( strcmp(zLogin, "nobody") ){
          472  +  if( fossil_strcmp(zLogin, "nobody") ){
   409    473       char *z1, *z2;
   410    474       z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
   411    475       while( z1 && *z1 ){
   412         -      inherit[0x7f & *(z1++)] = "<font color=\"green\">&bull;</font>";
          476  +      inherit[0x7f & *(z1++)] =
          477  +           "<span class=\"ueditInheritNobody\">&bull;</span>";
   413    478       }
   414    479       free(z2);
   415    480     }
   416    481   
   417    482     /* Begin generating the page
   418    483     */
   419    484     style_submenu_element("Cancel", "Cancel", "setup_ulist");
   420    485     if( uid ){
   421    486       style_header(mprintf("Edit User %h", zLogin));
   422    487     }else{
   423    488       style_header("Add A New User");
   424    489     }
   425         -  @ <table align="left" hspace="20" vspace="10"><tr><td>
   426         -  @ <form action="%s(g.zPath)" method="POST">
          490  +  @ <div class="ueditCapBox">
          491  +  @ <form action="%s(g.zPath)" method="post"><div>
   427    492     login_insert_csrf_secret();
   428    493     @ <table>
   429    494     @ <tr>
   430         -  @   <td align="right"><nobr>User ID:</nobr></td>
          495  +  @   <td class="usetupEditLabel">User ID:</td>
   431    496     if( uid ){
   432         -    @   <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
          497  +    @   <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
   433    498     }else{
   434         -    @   <td>(new user)<input type="hidden" name="id" value=0></td>
          499  +    @   <td>(new user)<input type="hidden" name="id" value="0" /></td>
   435    500     }
   436    501     @ </tr>
          502  +  @ <tr>
          503  +  @   <td class="usetupEditLabel">Login:</td>
          504  +  @   <td><input type="text" name="login" value="%h(zLogin)" /></td>
          505  +  @ </tr>
   437    506     @ <tr>
   438         -  @   <td align="right"><nobr>Login:</nobr></td>
   439         -  @   <td><input type="text" name="login" value="%h(zLogin)"></td>
          507  +  @   <td class="usetupEditLabel">Contact&nbsp;Info:</td>
          508  +  @   <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
   440    509     @ </tr>
   441    510     @ <tr>
   442         -  @   <td align="right"><nobr>Contact&nbsp;Info:</nobr></td>
   443         -  @   <td><input type="text" name="info" size=40 value="%h(zInfo)"></td>
   444         -  @ </tr>
   445         -  @ <tr>
   446         -  @   <td align="right" valign="top">Capabilities:</td>
          511  +  @   <td class="usetupEditLabel">Capabilities:</td>
   447    512     @   <td>
   448    513   #define B(x) inherit[x]
   449         -  if( g.okSetup ){
   450         -    @    <input type="checkbox" name="as"%s(oas)/>%s(B('s'))Setup<br>
   451         -  }
   452         -  @    <input type="checkbox" name="aa"%s(oaa)/>%s(B('a'))Admin<br>
   453         -  @    <input type="checkbox" name="ad"%s(oad)/>%s(B('d'))Delete<br>
   454         -  @    <input type="checkbox" name="ae"%s(oae)/>%s(B('e'))Email<br>
   455         -  @    <input type="checkbox" name="ap"%s(oap)/>%s(B('p'))Password<br>
   456         -  @    <input type="checkbox" name="ai"%s(oai)/>%s(B('i'))Check-In<br>
   457         -  @    <input type="checkbox" name="ao"%s(oao)/>%s(B('o'))Check-Out<br>
   458         -  @    <input type="checkbox" name="ah"%s(oah)/>%s(B('h'))History<br>
   459         -  @    <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
   460         -  @    <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
   461         -  @    <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
   462         -  @    <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
   463         -  @    <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
   464         -  @    <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
   465         -  @    <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
   466         -  @    <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
   467         -  @    <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
   468         -  @    <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
   469         -  @    <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
   470         -  @    <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
   471         -  @    <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
   472         -  @    <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
          514  +  @ <table border=0><tr><td valign="top">
          515  +  if( g.perm.Setup ){
          516  +    @  <label><input type="checkbox" name="as"%s(oa['s']) />%s(B('s'))Setup
          517  +    @  </label><br />
          518  +  }
          519  +  @  <label><input type="checkbox" name="aa"%s(oa['a']) />%s(B('a'))Admin
          520  +  @  </label><br />
          521  +  @  <label><input type="checkbox" name="ad"%s(oa['d']) />%s(B('d'))Delete
          522  +  @  </label><br />
          523  +  @  <label><input type="checkbox" name="ae"%s(oa['e']) />%s(B('e'))Email
          524  +  @  </label><br />
          525  +  @  <label><input type="checkbox" name="ap"%s(oa['p']) />%s(B('p'))Password
          526  +  @  </label><br />
          527  +  @  <label><input type="checkbox" name="ai"%s(oa['i']) />%s(B('i'))Check-In
          528  +  @  </label><br />
          529  +  @  <label><input type="checkbox" name="ao"%s(oa['o']) />%s(B('o'))Check-Out
          530  +  @  </label><br />
          531  +  @  <label><input type="checkbox" name="ah"%s(oa['h']) />%s(B('h'))Hyperlinks
          532  +  @  </label><br />
          533  +  @  <label><input type="checkbox" name="ab"%s(oa['b']) />%s(B('b'))Attachments
          534  +  @  </label><br />
          535  +  @ </td><td><td width="40"></td><td valign="top">
          536  +  @  <label><input type="checkbox" name="au"%s(oa['u']) />%s(B('u'))Reader
          537  +  @  </label><br />
          538  +  @  <label><input type="checkbox" name="av"%s(oa['v']) />%s(B('v'))Developer
          539  +  @  </label><br />
          540  +  @  <label><input type="checkbox" name="ag"%s(oa['g']) />%s(B('g'))Clone
          541  +  @  </label><br />
          542  +  @  <label><input type="checkbox" name="aj"%s(oa['j']) />%s(B('j'))Read Wiki
          543  +  @  </label><br />
          544  +  @  <label><input type="checkbox" name="af"%s(oa['f']) />%s(B('f'))New Wiki
          545  +  @  </label><br />
          546  +  @  <label><input type="checkbox" name="am"%s(oa['m']) />%s(B('m'))Append Wiki
          547  +  @  </label><br />
          548  +  @  <label><input type="checkbox" name="ak"%s(oa['k']) />%s(B('k'))Write Wiki
          549  +  @  </label><br />
          550  +  @  <label><input type="checkbox" name="al"%s(oa['l']) />%s(B('l'))Moderate
          551  +  @  Wiki</label><br />
          552  +  @ </td><td><td width="40"></td><td valign="top">
          553  +  @  <label><input type="checkbox" name="ar"%s(oa['r']) />%s(B('r'))Read Ticket
          554  +  @  </label><br />
          555  +  @  <label><input type="checkbox" name="an"%s(oa['n']) />%s(B('n'))New Tickets
          556  +  @  </label><br />
          557  +  @  <label><input type="checkbox" name="ac"%s(oa['c']) />%s(B('c'))Append
          558  +  @  To Ticket </label><br />
          559  +  @  <label><input type="checkbox" name="aw"%s(oa['w']) />%s(B('w'))Write
          560  +  @  Tickets </label><br />
          561  +  @  <label><input type="checkbox" name="aq"%s(oa['q']) />%s(B('q'))Moderate
          562  +  @  Tickets </label><br />
          563  +  @  <label><input type="checkbox" name="at"%s(oa['t']) />%s(B('t'))Ticket
          564  +  @  Report </label><br />
          565  +  @  <label><input type="checkbox" name="ax"%s(oa['x']) />%s(B('x'))Private
          566  +  @  </label><br />
          567  +  @  <label><input type="checkbox" name="az"%s(oa['z']) />%s(B('z'))Download
          568  +  @  Zip </label>
          569  +  @ </td></tr></table>
   473    570     @   </td>
   474    571     @ </tr>
   475    572     @ <tr>
   476    573     @   <td align="right">Password:</td>
   477    574     if( zPw[0] ){
   478    575       /* Obscure the password for all users */
   479         -    @   <td><input type="password" name="pw" value="**********"></td>
          576  +    @   <td><input type="password" name="pw" value="**********" /></td>
   480    577     }else{
   481    578       /* Show an empty password as an empty input field */
   482         -    @   <td><input type="password" name="pw" value=""></td>
          579  +    @   <td><input type="password" name="pw" value="" /></td>
   483    580     }
   484    581     @ </tr>
          582  +  zGroup = login_group_name();
          583  +  if( zGroup ){
          584  +    @ <tr>
          585  +    @ <td valign="top" align="right">Scope:</td>
          586  +    @ <td valign="top">
          587  +    @ <input type="radio" name="all" checked value="0">
          588  +    @ Apply changes to this repository only.<br />
          589  +    @ <input type="radio" name="all" value="1">
          590  +    @ Apply changes to all repositories in the "<b>%h(zGroup)</b>"
          591  +    @ login group.</td></tr>
          592  +  }
   485    593     if( !higherUser ){
   486    594       @ <tr>
   487         -    @   <td>&nbsp</td>
   488         -    @   <td><input type="submit" name="submit" value="Apply Changes">
          595  +    @   <td>&nbsp;</td>
          596  +    @   <td><input type="submit" name="submit" value="Apply Changes" /></td>
   489    597       @ </tr>
   490    598     }
   491         -  @ </table></td></tr></table>
          599  +  @ </table>
          600  +  @ </div></form>
          601  +  @ </div>
   492    602     @ <h2>Privileges And Capabilities:</h2>
   493    603     @ <ul>
   494    604     if( higherUser ){
   495         -    @ <li><p><font color="blue"><b>
          605  +    @ <li><p class=missingPriv">
   496    606       @ User %h(zLogin) has Setup privileges and you only have Admin privileges
   497    607       @ so you are not permitted to make changes to %h(zLogin).
   498         -    @ </b></font></p></li>
          608  +    @ </p></li>
   499    609       @
   500    610     }
   501    611     @ <li><p>
   502         -  @ The <b>Setup</b> user can make arbitrary configuration changes.
   503         -  @ An <b>Admin</b> user can add other users and change user privileges
          612  +  @ The <span class="capability">Setup</span> user can make arbitrary
          613  +  @ configuration changes. An <span class="usertype">Admin</span> user
          614  +  @ can add other users and change user privileges
   504    615     @ and reset user passwords.  Both automatically get all other privileges
   505    616     @ listed below.  Use these two settings with discretion.
   506    617     @ </p></li>
   507    618     @
   508    619     @ <li><p>
   509         -  @ The "<font color="green"><big>&bull;</big></font>" mark indicates
   510         -  @ the privileges of "nobody" that are available to all users
   511         -  @ regardless of whether or not they are logged in.
          620  +  @ The "<span class="ueditInheritNobody"><big>&bull;</big></span>" mark
          621  +  @ indicates the privileges of <span class="usertype">nobody</span> that
          622  +  @ are available to all users regardless of whether or not they are logged in.
          623  +  @ </p></li>
          624  +  @
          625  +  @ <li><p>
          626  +  @ The "<span class="ueditInheritAnonymous"><big>&bull;</big></span>" mark
          627  +  @ indicates the privileges of <span class="usertype">anonymous</span> that
          628  +  @ are inherited by all logged-in users.
          629  +  @ </p></li>
          630  +  @
          631  +  @ <li><p>
          632  +  @ The "<span class="ueditInheritDeveloper"><big>&bull;</big></span>" mark
          633  +  @ indicates the privileges of <span class="usertype">developer</span> that
          634  +  @ are inherited by all users with the
          635  +  @ <span class="capability">Developer</span> privilege.
          636  +  @ </p></li>
          637  +  @
          638  +  @ <li><p>
          639  +  @ The "<span class="ueditInheritReader"><big>&bull;</big></span>" mark
          640  +  @ indicates the privileges of <span class="usertype">reader</span> that
          641  +  @ are inherited by all users with the <span class="capability">Reader</span>
          642  +  @ privilege.
          643  +  @ </p></li>
          644  +  @
          645  +  @ <li><p>
          646  +  @ The <span class="capability">Delete</span> privilege give the user the
          647  +  @ ability to erase wiki, tickets, and attachments that have been added
          648  +  @ by anonymous users.  This capability is intended for deletion of spam. 
          649  +  @ The delete capability is only in effect for 24 hours after the item
          650  +  @ is first posted.  The <span class="usertype">Setup</span> user can
          651  +  @ delete anything at any time.
          652  +  @ </p></li>
          653  +  @
          654  +  @ <li><p>
          655  +  @ The <span class="capability">Hyperlinks</span> privilege allows a user
          656  +  @ to see most hyperlinks. This is recommended ON for most logged-in users
          657  +  @ but OFF for user "nobody" to avoid problems with spiders trying to walk
          658  +  @ every diff and annotation of every historical check-in and file.
          659  +  @ </p></li>
          660  +  @
          661  +  @ <li><p>
          662  +  @ The <span class="capability">Zip</span> privilege allows a user to
          663  +  @ see the "download as ZIP"
          664  +  @ hyperlink and permits access to the <tt>/zip</tt> page.  This allows
          665  +  @ users to download ZIP archives without granting other rights like
          666  +  @ <span class="capability">Read</span> or
          667  +  @ <span class="capability">Hyperlink</span>.  The "z" privilege is recommended
          668  +  @ for user <span class="usertype">nobody</span> so that automatic package
          669  +  @ downloaders can obtain the sources without going through the login
          670  +  @ procedure.
   512    671     @ </p></li>
   513    672     @
   514    673     @ <li><p>
   515         -  @ The "<font color="blue"><big>&bull;</big></font>" mark indicates
   516         -  @ the privileges of "anonymous" that are inherited by all logged-in users.
          674  +  @ The <span class="capability">Check-in</span> privilege allows remote
          675  +  @ users to "push". The <span class="capability">Check-out</span> privilege
          676  +  @ allows remote users to "pull". The <span class="capability">Clone</span>
          677  +  @ privilege allows remote users to "clone".
   517    678     @ </p></li>
   518    679     @
   519    680     @ <li><p>
   520         -  @ The "<font color="red"><big>&bull;</big></font>" mark indicates
   521         -  @ the privileges of "developer" that are inherited by all users with
   522         -  @ the <b>Developer</b> privilege.
          681  +  @ The <span class="capability">Read Wiki</span>,
          682  +  @ <span class="capability">New Wiki</span>,
          683  +  @ <span class="capability">Append Wiki</span>, and
          684  +  @ <b>Write Wiki</b> privileges control access to wiki pages.  The
          685  +  @ <span class="capability">Read Ticket</span>,
          686  +  @ <span class="capability">New Ticket</span>,
          687  +  @ <span class="capability">Append Ticket</span>, and
          688  +  @ <span class="capability">Write Ticket</span> privileges control access
          689  +  @ to trouble tickets.
          690  +  @ The <span class="capability">Ticket Report</span> privilege allows
          691  +  @ the user to create or edit ticket report formats.
   523    692     @ </p></li>
   524    693     @
   525    694     @ <li><p>
   526         -  @ The "<font color="black"><big>&bull;</big></font>" mark indicates
   527         -  @ the privileges of "reader" that are inherited by all users with
   528         -  @ the <b>Reader</b> privilege.
   529         -  @ </p></li>
   530         -  @
   531         -  @ <li><p>
   532         -  @ The <b>Delete</b> privilege give the user the ability to erase
   533         -  @ wiki, tickets, and attachments that have been added by anonymous
   534         -  @ users.  This capability is intended for deletion of spam.  The
   535         -  @ delete capability is only in effect for 24 hours after the item
   536         -  @ is first posted.  The Setup user can delete anything at any time.
          695  +  @ Users with the <span class="capability">Password</span> privilege
          696  +  @ are allowed to change their own password.  Recommended ON for most
          697  +  @ users but OFF for special users <span class="usertype">developer</span>,
          698  +  @ <span class="usertype">anonymous</span>,
          699  +  @ and <span class="usertype">nobody</span>.
   537    700     @ </p></li>
   538    701     @
   539    702     @ <li><p>
   540         -  @ The <b>History</b> privilege allows a user to see most hyperlinks.
   541         -  @ This is recommended ON for most logged-in users but OFF for
   542         -  @ user "nobody" to avoid problems with spiders trying to walk every
   543         -  @ historical version of every baseline and file.
   544         -  @ </p></li>
   545         -  @
   546         -  @ <li><p>
   547         -  @ The <b>Zip</b> privilege allows a user to see the "download as ZIP"
   548         -  @ hyperlink and permits access to the <tt>/zip</tt> page.  This allows
   549         -  @ users to download ZIP archives without granting other rights like
   550         -  @ <b>Read</b> or <b>History</b>.  This privilege is recommended for
   551         -  @ user <b>nobody</b> so that automatic package downloaders can obtain
   552         -  @ the sources without going through the login procedure.
          703  +  @ The <span class="capability">EMail</span> privilege allows the display of
          704  +  @ sensitive information such as the email address of users and contact
          705  +  @ information on tickets. Recommended OFF for 
          706  +  @ <span class="usertype">anonymous</span> and for
          707  +  @ <span class="usertype">nobody</span> but ON for
          708  +  @ <span class="usertype">developer</span>.
   553    709     @ </p></li>
   554    710     @
   555    711     @ <li><p>
   556         -  @ The <b>Check-in</b> privilege allows remote users to "push".
   557         -  @ The <b>Check-out</b> privilege allows remote users to "pull".
   558         -  @ The <b>Clone</b> privilege allows remote users to "clone".
   559         -  @ </li><p>
   560         -  @
   561         -  @ <li><p>
   562         -  @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
   563         -  @ <b>Write Wiki</b> privileges control access to wiki pages.  The
   564         -  @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
   565         -  @ <b>Write Ticket</b> privileges control access to trouble tickets.
   566         -  @ The <b>Ticket Report</b> privilege allows the user to create or edit
   567         -  @ ticket report formats.
          712  +  @ The <span class="capability">Attachment</span> privilege is needed in
          713  +  @ order to add attachments to tickets or wiki.  Write privilege on the
          714  +  @ ticket or wiki is also required.
   568    715     @ </p></li>
   569    716     @
   570         -  @ <li><p>
   571         -  @ Users with the <b>Password</b> privilege are allowed to change their
   572         -  @ own password.  Recommended ON for most users but OFF for special
   573         -  @ users "developer", "anonymous", and "nobody".
   574         -  @ </p></li>
   575         -  @
   576         -  @ <li><p>
   577         -  @ The <b>EMail</b> privilege allows the display of sensitive information
   578         -  @ such as the email address of users and contact information on tickets.
   579         -  @ Recommended OFF for "anonymous" and for "nobody" but ON for
   580         -  @ "developer".
   581         -  @ </p></li>
   582         -  @
   583         -  @ <li><p>
   584         -  @ The <b>Attachment</b> privilege is needed in order to add attachments
   585         -  @ to tickets or wiki.  Write privilege on the ticket or wiki is also
   586         -  @ required.</p></li>
   587         -  @
   588    717     @ <li><p>
   589    718     @ Login is prohibited if the password is an empty string.
   590    719     @ </p></li>
   591    720     @ </ul>
   592    721     @
   593    722     @ <h2>Special Logins</h2>
   594    723     @
   595    724     @ <ul>
   596    725     @ <li><p>
   597         -  @ No login is required for user "<b>nobody</b>".  The capabilities
   598         -  @ of the <b>nobody</b> user are inherited by all users, regardless of
   599         -  @ whether or not they are logged in.  To disable universal access
   600         -  @ to the repository, make sure no user named "<b>nobody</b>" exists or
   601         -  @ that the <b>nobody</b> user has no capabilities enabled.
   602         -  @ The password for <b>nobody</b> is ignore.  To avoid problems with
   603         -  @ spiders overloading the server, it is recommended
   604         -  @ that the 'h' (History) capability be turned off for the <b>nobody</b>
   605         -  @ user.
          726  +  @ No login is required for user <span class="usertype">nobody</span>. The
          727  +  @ capabilities of the <span class="usertype">nobody</span> user are
          728  +  @ inherited by all users, regardless of whether or not they are logged in.
          729  +  @ To disable universal access to the repository, make sure no user named 
          730  +  @ <span class="usertype">nobody</span> exists or that the
          731  +  @ <span class="usertype">nobody</span> user has no capabilities
          732  +  @ enabled. The password for <span class="usertype">nobody</span> is ignore.
          733  +  @ To avoid problems with spiders overloading the server, it is recommended
          734  +  @ that the <span class="capability">h</span> (Hyperlinks) capability be
          735  +  @ turned off for the <span class="usertype">nobody</span> user.
   606    736     @ </p></li>
   607    737     @
   608    738     @ <li><p>
   609         -  @ Login is required for user "<b>anonymous</b>" but the password
   610         -  @ is displayed on the login screen beside the password entry box
          739  +  @ Login is required for user <span class="usertype">anonymous</span> but the
          740  +  @ password is displayed on the login screen beside the password entry box
   611    741     @ so anybody who can read should be able to login as anonymous.
   612    742     @ On the other hand, spiders and web-crawlers will typically not
   613         -  @ be able to login.  Set the capabilities of the anonymous user
   614         -  @ to things that you want any human to be able to do, but not any
          743  +  @ be able to login.  Set the capabilities of the
          744  +  @ <span class="usertype">anonymous</span>
          745  +  @ user to things that you want any human to be able to do, but not any
   615    746     @ spider.  Every other logged-in user inherits the privileges of
   616         -  @ <b>anonymous</b>.
          747  +  @ <span class="usertype">anonymous</span>.
   617    748     @ </p></li>
   618    749     @
   619    750     @ <li><p>
   620         -  @ The "<b>developer</b>" user is intended as a template for trusted users
   621         -  @ with check-in privileges.  When adding new trusted users, simply
   622         -  @ select the <b>Developer</b> privilege to cause the new user to inherit
   623         -  @ all privileges of the "developer" user.  Similarly, the "<b>reader</b>"
   624         -  @ user is a template for users who are allowed more access than anonymous,
   625         -  @ but less than a developer.
          751  +  @ The <span class="usertype">developer</span> user is intended as a template
          752  +  @ for trusted users with check-in privileges. When adding new trusted users,
          753  +  @ simply select the <span class="capability">developer</span> privilege to
          754  +  @ cause the new user to inherit all privileges of the 
          755  +  @ <span class="usertype">developer</span>
          756  +  @ user.  Similarly, the <span class="usertype">reader</span> user is a 
          757  +  @ template for users who are allowed more access than
          758  +  @ <span class="usertype">anonymous</span>,
          759  +  @ but less than a <span class="usertype">developer</span>.
   626    760     @ </p></li>
   627    761     @ </ul>
   628         -  @ </form>
   629    762     style_footer();
   630    763   }
   631    764   
   632    765   
   633    766   /*
   634    767   ** Generate a checkbox for an attribute.
   635    768   */
................................................................................
   641    774   ){
   642    775     const char *zQ = P(zQParm);
   643    776     int iVal = db_get_boolean(zVar, dfltVal);
   644    777     if( zQ==0 && P("submit") ){
   645    778       zQ = "off";
   646    779     }
   647    780     if( zQ ){
   648         -    int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
          781  +    int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
   649    782       if( iQ!=iVal ){
   650    783         login_verify_csrf_secret();
   651    784         db_set(zVar, iQ ? "1" : "0", 0);
   652    785         iVal = iQ;
   653    786       }
   654    787     }
   655    788     if( iVal ){
   656         -    @ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
          789  +    @ <input type="checkbox" name="%s(zQParm)" checked="checked" />
          790  +    @ <b>%s(zLabel)</b>
   657    791     }else{
   658         -    @ <input type="checkbox" name="%s(zQParm)"><b>%s(zLabel)</b></input>
          792  +    @ <input type="checkbox" name="%s(zQParm)" /> <b>%s(zLabel)</b>
   659    793     }
   660    794   }
   661    795   
   662    796   /*
   663    797   ** Generate an entry box for an attribute.
   664    798   */
   665    799   void entry_attribute(
................................................................................
   667    801     int width,            /* Width of the entry box */
   668    802     const char *zVar,     /* The corresponding row in the VAR table */
   669    803     const char *zQParm,   /* The query parameter */
   670    804     char *zDflt     /* Default value if VAR table entry does not exist */
   671    805   ){
   672    806     const char *zVal = db_get(zVar, zDflt);
   673    807     const char *zQ = P(zQParm);
   674         -  if( zQ && strcmp(zQ,zVal)!=0 ){
          808  +  if( zQ && fossil_strcmp(zQ,zVal)!=0 ){
   675    809       login_verify_csrf_secret();
   676    810       db_set(zVar, zQ, 0);
   677    811       zVal = zQ;
   678    812     }
   679         -  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
          813  +  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" />
   680    814     @ <b>%s(zLabel)</b>
   681    815   }
   682    816   
   683    817   /*
   684    818   ** Generate a text box for an attribute.
   685    819   */
   686    820   static void textarea_attribute(
................................................................................
   689    823     int cols,             /* Columns in the textarea */
   690    824     const char *zVar,     /* The corresponding row in the VAR table */
   691    825     const char *zQP,      /* The query parameter */
   692    826     const char *zDflt     /* Default value if VAR table entry does not exist */
   693    827   ){
   694    828     const char *z = db_get(zVar, (char*)zDflt);
   695    829     const char *zQ = P(zQP);
   696         -  if( zQ && strcmp(zQ,z)!=0 ){
          830  +  if( zQ && fossil_strcmp(zQ,z)!=0 ){
   697    831       login_verify_csrf_secret();
   698    832       db_set(zVar, zQ, 0);
   699    833       z = zQ;
   700    834     }
   701    835     if( rows>0 && cols>0 ){
   702         -    @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
   703         -    @ <b>%s(zLabel)</b>
          836  +    @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)"
          837  +    @ cols="%d(cols)">%h(z)</textarea>
          838  +    if (zLabel && *zLabel){
          839  +      @ <span class="textareaLabel">%s(zLabel)</span>
          840  +    }
   704    841     }
   705    842   }
   706    843   
   707    844   
   708    845   /*
   709    846   ** WEBPAGE: setup_access
   710    847   */
   711    848   void setup_access(void){
   712    849     login_check_credentials();
   713         -  if( !g.okSetup ){
          850  +  if( !g.perm.Setup ){
   714    851       login_needed();
   715    852     }
   716    853   
   717    854     style_header("Access Control Settings");
   718    855     db_begin_transaction();
   719         -  @ <form action="%s(g.zBaseURL)/setup_access" method="POST">
          856  +  @ <form action="%s(g.zTop)/setup_access" method="post"><div>
   720    857     login_insert_csrf_secret();
   721         -  @ <hr>
          858  +  @ <hr />
   722    859     onoff_attribute("Require password for local access",
   723    860        "localauth", "localauth", 0);
   724         -  @ <p>When enabled, the password sign-in is required for
   725         -  @ web access coming from 127.0.0.1.  When disabled, web access
   726         -  @ from 127.0.0.1 is allows without any login - the user id is selected
   727         -  @ from the ~/.fossil database. Password login is always required
   728         -  @ for incoming web connections on internet addresses other than
   729         -  @ 127.0.0.1.</p></li>
   730         -
   731         -  @ <hr>
          861  +  @ <p>When enabled, the password sign-in is always required for
          862  +  @ web access.  When disabled, unrestricted web access from 127.0.0.1
          863  +  @ is allowed for the <a href="%s(g.zTop)/help/ui">fossil ui</a> command or
          864  +  @ from the <a href="%s(g.zTop)/help/server">fossil server</a>,
          865  +  @ <a href="%s(g.zTop)/help/http">fossil http</a> commands when the
          866  +  @ "--localauth" command line options is used, or from the
          867  +  @ <a href="%s(g.zTop)/help/cgi">fossil cgi</a> if a line containing
          868  +  @ the word "localauth" appears in the CGI script.
          869  +  @
          870  +  @ <p>A password is always required if any one or more
          871  +  @ of the following are true:
          872  +  @ <ol>
          873  +  @ <li> This button is checked
          874  +  @ <li> The inbound TCP/IP connection is not from 127.0.0.1
          875  +  @ <li> The server is started using either of the
          876  +  @ <a href="%s(g.zTop)/help/server">fossil server</a> or
          877  +  @ <a href="%s(g.zTop)/help/server">fossil http</a> commands
          878  +  @ without the "--localauth" option.
          879  +  @ <li> The server is started from CGI without the "localauth" keyword
          880  +  @ in the CGI script.
          881  +  @ </ol>
          882  +  @
          883  +  @ <hr />
          884  +  onoff_attribute("Enable /test_env",
          885  +     "test_env_enable", "test_env_enable", 0);
          886  +  @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
          887  +  @ users.  When disabled (the default) only users Admin and Setup can visit
          888  +  @ the /test_env page.
          889  +  @ </p>
          890  +  @
          891  +  @ <hr />
   732    892     onoff_attribute("Allow REMOTE_USER authentication",
   733    893        "remote_user_ok", "remote_user_ok", 0);
   734    894     @ <p>When enabled, if the REMOTE_USER environment variable is set to the
   735    895     @ login name of a valid user and no other login credentials are available,
   736    896     @ then the REMOTE_USER is accepted as an authenticated user.
   737         -  @ </p></li>
   738         -
   739         -  @ <hr>
          897  +  @ </p>
          898  +  @
          899  +  @ <hr />
          900  +  entry_attribute("IP address terms used in login cookie", 3, 
          901  +                  "ip-prefix-terms", "ipt", "2");
          902  +  @ <p>The number of octets of of the IP address used in the login cookie.
          903  +  @ Set to zero to omit the IP address from the login cookie.  A value of
          904  +  @ 2 is recommended.
          905  +  @ </p>
          906  +  @
          907  +  @ <hr />
   740    908     entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
   741    909     @ <p>The number of hours for which a login is valid.  This must be a
   742         -  @ positive number.  The default is 8760 hours which is approximately equal
          910  +  @ positive number.  The default is 8766 hours which is approximately equal
   743    911     @ to a year.</p>
   744    912   
   745         -  @ <hr>
          913  +  @ <hr />
   746    914     entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
   747    915                     "5000000");
   748    916     @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
   749    917     @ to this many bytes, uncompressed.  If the client requires more data
   750    918     @ than this, then the client will issue multiple HTTP requests.
   751    919     @ Values below 1 million are not recommended.  5 million is a
   752    920     @ reasonable number.</p>
   753    921   
   754         -  @ <hr>
          922  +  @ <hr />
          923  +  entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
          924  +                  "30");
          925  +
          926  +  @ <p>Fossil tries to spend less than this many seconds gathering
          927  +  @ the out-bound data of sync, clone, and pull packets.
          928  +  @ If the client request takes longer, a partial reply is given similar
          929  +  @ to the download packet limit. 30s is a reasonable default.</p>
          930  +
          931  +  @ <hr />
          932  +  onoff_attribute(
          933  +      "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript",
          934  +      "auto-hyperlink", "autohyperlink", 1);
          935  +  @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
          936  +  @ including user "nobody", as long as (1) the User-Agent string in the
          937  +  @ HTTP header indicates that the request is coming from an actual human
          938  +  @ being and not a a robot or spider and (2) the user agent is able to
          939  +  @ run Javascript in order to set the href= attribute of hyperlinks.  Bots
          940  +  @ and spiders can specify whatever User-Agent string they that want and
          941  +  @ they can run javascript just like browsers.  But most bots don't go to
          942  +  @ that much trouble so this is normally an effective defense.</p>
          943  +  @
          944  +  @ <p>You do not normally want a bot to walk your entire repository because
          945  +  @ if it does, your server will end up computing diffs and annotations for
          946  +  @ every historical version of every file and creating ZIPs and tarballs of
          947  +  @ every historical check-in, which can use a lot of CPU and bandwidth
          948  +  @ even for relatively small projects.</p>
          949  +
          950  +  @ <hr />
          951  +  onoff_attribute("Require a CAPTCHA if not logged in",
          952  +                  "require-captcha", "reqcapt", 1);
          953  +  @ <p>Require a CAPTCHA for edit operations (appending, creating, or
          954  +  @ editing wiki or tickets or adding attachments to wiki or tickets)
          955  +  @ for users who are not logged in.</p>
          956  +
          957  +  @ <hr />
          958  +  entry_attribute("Public pages", 30, "public-pages",
          959  +                  "pubpage", "");
          960  +  @ <p>A comma-separated list of glob patterns for pages that are accessible
          961  +  @ without needing a login and using the privileges given by the
          962  +  @ "Default privileges" setting below.  Example use case: Set this field
          963  +  @ to "/doc/trunk/www/*" to give anonymous users read-only permission to the 
          964  +  @ latest version of the embedded documentation in the www/ folder without
          965  +  @ allowing them to see the rest of the source code.
          966  +  @ </p>
          967  +
          968  +  @ <hr />
          969  +  onoff_attribute("Allow users to register themselves",
          970  +                  "self-register", "selfregister", 0);
          971  +  @ <p>Allow users to register themselves through the HTTP UI. 
          972  +  @ The registration form always requires filling in a CAPTCHA 
          973  +  @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
          974  +  @ can register under any user name. This option is useful for public projects
          975  +  @ where you do not want everyone in any ticket discussion to be named 
          976  +  @ "Anonymous".</p>
          977  +
          978  +  @ <hr />
          979  +  entry_attribute("Default privileges", 10, "default-perms",
          980  +                  "defaultperms", "u");
          981  +  @ <p>Permissions given to users that... <ul><li>register themselves using
          982  +  @ the self-registration procedure (if enabled), or <li>access "public"
          983  +  @ pages identified by the public-pages glob pattern above, or <li>
          984  +  @ are users newly created by the administrator.</ul>
          985  +  @ </p>
          986  +
          987  +  @ <hr />
   755    988     onoff_attribute("Show javascript button to fill in CAPTCHA",
   756    989                     "auto-captcha", "autocaptcha", 0);
   757    990     @ <p>When enabled, a button appears on the login screen for user
   758    991     @ "anonymous" that will automatically fill in the CAPTCHA password.
   759         -  @ This is less secure that forcing the user to do it manually, but is
          992  +  @ This is less secure than forcing the user to do it manually, but is
   760    993     @ probably secure enough and it is certainly more convenient for
   761    994     @ anonymous users.</p>
   762    995   
   763         -  @ <hr>
   764         -  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
   765         -  @ </form>
          996  +  @ <hr />
          997  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
          998  +  @ </div></form>
   766    999     db_end_transaction(0);
   767   1000     style_footer();
   768   1001   }
         1002  +
         1003  +/*
         1004  +** WEBPAGE: setup_login_group
         1005  +*/
         1006  +void setup_login_group(void){
         1007  +  const char *zGroup;
         1008  +  char *zErrMsg = 0;
         1009  +  Blob fullName;
         1010  +  char *zSelfRepo;
         1011  +  const char *zRepo = PD("repo", "");
         1012  +  const char *zLogin = PD("login", "");
         1013  +  const char *zPw = PD("pw", "");
         1014  +  const char *zNewName = PD("newname", "New Login Group");
         1015  +
         1016  +  login_check_credentials();
         1017  +  if( !g.perm.Setup ){
         1018  +    login_needed();
         1019  +  }
         1020  +  file_canonical_name(g.zRepositoryName, &fullName, 0);
         1021  +  zSelfRepo = mprintf(blob_str(&fullName));
         1022  +  blob_reset(&fullName);
         1023  +  if( P("join")!=0 ){
         1024  +    login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
         1025  +  }else if( P("leave") ){
         1026  +    login_group_leave(&zErrMsg);
         1027  +  }
         1028  +  style_header("Login Group Configuration");
         1029  +  if( zErrMsg ){
         1030  +    @ <p class="generalError">%s(zErrMsg)</p>
         1031  +  }
         1032  +  zGroup = login_group_name();
         1033  +  if( zGroup==0 ){
         1034  +    @ <p>This repository (in the file named "%h(zSelfRepo)")
         1035  +    @ is not currently part of any login-group.
         1036  +    @ To join a login group, fill out the form below.</p>
         1037  +    @
         1038  +    @ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
         1039  +    login_insert_csrf_secret();
         1040  +    @ <blockquote><table border="0">
         1041  +    @
         1042  +    @ <tr><td align="right"><b>Repository filename in group to join:</b></td>
         1043  +    @ <td width="5"></td><td>
         1044  +    @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
         1045  +    @
         1046  +    @ <tr><td align="right"><b>Login on the above repo:</b></td>
         1047  +    @ <td width="5"></td><td>
         1048  +    @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
         1049  +    @
         1050  +    @ <tr><td align="right"><b>Password:</b></td>
         1051  +    @ <td width="5"></td><td>
         1052  +    @ <input type="password" size="20" name="pw"></td></tr>
         1053  +    @
         1054  +    @ <tr><td align="right"><b>Name of login-group:</b></td>
         1055  +    @ <td width="5"></td><td>
         1056  +    @ <input type="text" size="30" value="%h(zNewName)" name="newname">
         1057  +    @ (only used if creating a new login-group).</td></tr>
         1058  +    @
         1059  +    @ <tr><td colspan="3" align="center">
         1060  +    @ <input type="submit" value="Join" name="join"></td></tr>
         1061  +    @ </table></blockquote></div></form>
         1062  +  }else{
         1063  +    Stmt q;
         1064  +    int n = 0;
         1065  +    @ <p>This repository (in the file "%h(zSelfRepo)")
         1066  +    @ is currently part of the "<b>%h(zGroup)</b>" login group.
         1067  +    @ Other repositories in that group are:</p>
         1068  +    @ <table border="0" cellspacing="4">
         1069  +    @ <tr><td colspan="2"><th align="left">Project Name<td>
         1070  +    @ <th align="left">Repository File</tr>
         1071  +    db_prepare(&q,
         1072  +       "SELECT value,"
         1073  +       "       (SELECT value FROM config"
         1074  +       "         WHERE name=('peer-name-' || substr(x.name,11)))"
         1075  +       "  FROM config AS x"
         1076  +       " WHERE name GLOB 'peer-repo-*'"
         1077  +       " ORDER BY value"
         1078  +    );
         1079  +    while( db_step(&q)==SQLITE_ROW ){
         1080  +      const char *zRepo = db_column_text(&q, 0);
         1081  +      const char *zTitle = db_column_text(&q, 1);
         1082  +      n++;
         1083  +      @ <tr><td align="right">%d(n).</td><td width="4">
         1084  +      @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
         1085  +    }
         1086  +    db_finalize(&q);
         1087  +    @ </table>
         1088  +    @
         1089  +    @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div>
         1090  +    login_insert_csrf_secret();
         1091  +    @ To leave this login group press
         1092  +    @ <input type="submit" value="Leave Login Group" name="leave">
         1093  +    @ </form></p>
         1094  +  }
         1095  +  style_footer();
         1096  +}
   769   1097   
   770   1098   /*
   771   1099   ** WEBPAGE: setup_timeline
   772   1100   */
   773   1101   void setup_timeline(void){
         1102  +  double tmDiff;
         1103  +  char zTmDiff[20];
   774   1104     login_check_credentials();
   775         -  if( !g.okSetup ){
         1105  +  if( !g.perm.Setup ){
   776   1106       login_needed();
   777   1107     }
   778   1108   
   779   1109     style_header("Timeline Display Preferences");
   780   1110     db_begin_transaction();
   781         -  @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
         1111  +  @ <form action="%s(g.zTop)/setup_timeline" method="post"><div>
   782   1112     login_insert_csrf_secret();
   783   1113   
   784         -  @ <hr>
         1114  +  @ <hr />
   785   1115     onoff_attribute("Allow block-markup in timeline",
   786   1116                     "timeline-block-markup", "tbm", 0);
   787   1117     @ <p>In timeline displays, check-in comments can be displayed with or
   788   1118     @ without block markup (paragraphs, tables, etc.)</p>
   789   1119   
   790         -  @ <hr>
         1120  +  @ <hr />
         1121  +  onoff_attribute("Plaintext comments on timelines",
         1122  +                  "timeline-plaintext", "tpt", 0);
         1123  +  @ <p>In timeline displays, check-in comments are displayed literally,
         1124  +  @ without any wiki or HTML interpretation.</p>
         1125  +
         1126  +  @ <hr />
   791   1127     onoff_attribute("Use Universal Coordinated Time (UTC)",
   792   1128                     "timeline-utc", "utc", 1);
   793   1129     @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
   794         -  @ Zulu) instead of in local time.</p>
         1130  +  @ Zulu) instead of in local time.  On this server, local time is currently
         1131  +  g.fTimeFormat = 2;
         1132  +  tmDiff = db_double(0.0, "SELECT julianday('now')");
         1133  +  tmDiff = db_double(0.0, 
         1134  +        "SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
         1135  +        tmDiff, tmDiff);
         1136  +  sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
         1137  +  if( strcmp(zTmDiff, "0.0")==0 ){
         1138  +    @ the same as UTC and so this setting will make no difference in
         1139  +    @ the display.</p>
         1140  +  }else if( tmDiff<0.0 ){
         1141  +    sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", -tmDiff);
         1142  +    @ %s(zTmDiff) hours behind UTC.</p>
         1143  +  }else{
         1144  +    @ %s(zTmDiff) hours ahead of UTC.</p>
         1145  +  }
   795   1146   
   796         -  @ <hr>
         1147  +  @ <hr />
   797   1148     onoff_attribute("Show version differences by default",
   798   1149                     "show-version-diffs", "vdiff", 0);
   799   1150     @ <p>On the version-information pages linked from the timeline can either
   800   1151     @ show complete diffs of all file changes, or can just list the names of
   801   1152     @ the files that have changed.  Users can get to either page by
   802   1153     @ clicking.  This setting selects the default.</p>
   803   1154   
   804         -  @ <hr>
         1155  +  @ <hr />
   805   1156     entry_attribute("Max timeline comment length", 6,
   806   1157                     "timeline-max-comment", "tmc", "0");
   807   1158     @ <p>The maximum length of a comment to be displayed in a timeline.
   808   1159     @ "0" there is no length limit.</p>
   809   1160   
   810         -  @ <hr>
   811         -  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
   812         -  @ </form>
         1161  +  @ <hr />
         1162  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
         1163  +  @ </div></form>
         1164  +  db_end_transaction(0);
         1165  +  style_footer();
         1166  +}
         1167  +
         1168  +/*
         1169  +** WEBPAGE: setup_settings
         1170  +*/
         1171  +void setup_settings(void){
         1172  +  struct stControlSettings const *pSet;
         1173  +
         1174  +  login_check_credentials();
         1175  +  if( !g.perm.Setup ){
         1176  +    login_needed();
         1177  +  }
         1178  +
         1179  +  style_header("Settings");
         1180  +  db_open_local();
         1181  +  db_begin_transaction();
         1182  +  @ <p>This page provides a simple interface to the "fossil setting" command.
         1183  +  @ See the "fossil help setting" output below for further information on
         1184  +  @ the meaning of each setting.</p><hr />
         1185  +  @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
         1186  +  @ <table border="0"><tr><td valign="top">
         1187  +  login_insert_csrf_secret();
         1188  +  for(pSet=ctrlSettings; pSet->name!=0; pSet++){
         1189  +    if( pSet->width==0 ){
         1190  +      onoff_attribute(pSet->name, pSet->name,
         1191  +                      pSet->var!=0 ? pSet->var : pSet->name,
         1192  +                      is_truth(pSet->def));
         1193  +      if( pSet->versionable ){
         1194  +        @  (v)<br />
         1195  +      } else {
         1196  +        @ <br />
         1197  +      }
         1198  +    }
         1199  +  }
         1200  +  @ </td><td style="width:50px;"></td><td valign="top">
         1201  +  for(pSet=ctrlSettings; pSet->name!=0; pSet++){
         1202  +    if( pSet->width!=0 ){
         1203  +      entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name,
         1204  +                      pSet->var!=0 ? pSet->var : pSet->name,
         1205  +                      (char*)pSet->def);
         1206  +      if( pSet->versionable ){
         1207  +        @  (v)<br />
         1208  +      } else {
         1209  +        @ <br />
         1210  +      }
         1211  +    }
         1212  +  }
         1213  +  @ </td></tr></table>
         1214  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
         1215  +  @ </div></form>
         1216  +  @ <p>Settings marked with (v) are 'versionable' and will be overridden
         1217  +  @ by the contents of files named <tt>.fossil-settings/PROPERTY</tt>.</p>
         1218  +  @ <hr /><p>
         1219  +  @ These settings work in the same way, as the <kbd>set</kbd>
         1220  +  @ commandline:<br />
         1221  +  @ </p><pre>%s(zHelp_setting_cmd)</pre>
   813   1222     db_end_transaction(0);
   814   1223     style_footer();
   815   1224   }
   816   1225   
   817   1226   /*
   818   1227   ** WEBPAGE: setup_config
   819   1228   */
   820   1229   void setup_config(void){
   821   1230     login_check_credentials();
   822         -  if( !g.okSetup ){
         1231  +  if( !g.perm.Setup ){
   823   1232       login_needed();
   824   1233     }
   825   1234   
   826   1235     style_header("WWW Configuration");
   827   1236     db_begin_transaction();
   828         -  @ <form action="%s(g.zBaseURL)/setup_config" method="POST">
         1237  +  @ <form action="%s(g.zTop)/setup_config" method="post"><div>
   829   1238     login_insert_csrf_secret();
   830   1239     @ <hr />
   831   1240     entry_attribute("Project Name", 60, "project-name", "pn", "");
   832   1241     @ <p>Give your project a name so visitors know what this site is about.
   833   1242     @ The project name will also be used as the RSS feed title.</p>
   834   1243     @ <hr />
   835         -  textarea_attribute("Project Description", 5, 60,
         1244  +  textarea_attribute("Project Description", 3, 80,
   836   1245                        "project-description", "pd", "");
   837   1246     @ <p>Describe your project. This will be used in page headers for search
   838   1247     @ engines as well as a short RSS description.</p>
   839   1248     @ <hr />
         1249  +  onoff_attribute("Enable WYSIWYG Wiki Editing",
         1250  +                  "wysiwyg-wiki", "wysiwyg-wiki", 0);
         1251  +  @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages.
         1252  +  @ The WYSIWYG editor generates HTML instead of markup, which makes
         1253  +  @ subsequent manual editing more difficult.</p>
         1254  +  @ <hr />
   840   1255     entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
   841   1256     @ <p>Enter the pathname of the page to display when the "Home" menu
   842   1257     @ option is selected and when no pathname is
   843   1258     @ specified in the URL.  For example, if you visit the url:</p>
   844   1259     @
   845         -  @ <blockquote>%h(g.zBaseURL)</blockquote>
         1260  +  @ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
   846   1261     @
   847   1262     @ <p>And you have specified an index page of "/home" the above will
   848   1263     @ automatically redirect to:</p>
   849   1264     @
   850         -  @ <blockquote>%h(g.zBaseURL)/home</blockquote>
         1265  +  @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
   851   1266     @
   852   1267     @ <p>The default "/home" page displays a Wiki page with the same name
   853   1268     @ as the Project Name specified above.  Some sites prefer to redirect
   854   1269     @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
         1270  +  @
         1271  +  @ <p>Note:  To avoid a redirect loop or other problems, this entry must
         1272  +  @ begin with "/" and it must specify a valid page.  For example,
         1273  +  @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the
         1274  +  @ leading "/".</p>
   855   1275     @ <hr />
   856   1276     onoff_attribute("Use HTML as wiki markup language",
   857   1277       "wiki-use-html", "wiki-use-html", 0);
   858         -  @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed but
   859         -  @ all other wiki formatting will be ignored. This option is helpful if you have
   860         -  @ chosen to use a rich HTML editor for wiki markup such as TinyMCE.</p>
         1278  +  @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
         1279  +  @ but all other wiki formatting will be ignored. This option is helpful
         1280  +  @ if you have chosen to use a rich HTML editor for wiki markup such as
         1281  +  @ TinyMCE.</p>
   861   1282     @ <p><strong>CAUTION:</strong> when
   862   1283     @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
   863   1284     @ No sanitization is done. This means that it is very possible for malicious
   864   1285     @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
   865   1286     @ <p>This should <strong>only</strong> be enabled when wiki editing is limited
   866   1287     @ to trusted users. It should <strong>not</strong> be used on a publically
   867   1288     @ editable wiki.</p>
   868   1289     @ <hr />
   869         -  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
   870         -  @ </form>
         1290  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
         1291  +  @ </div></form>
   871   1292     db_end_transaction(0);
   872   1293     style_footer();
   873   1294   }
   874   1295   
   875   1296   /*
   876   1297   ** WEBPAGE: setup_editcss
   877   1298   */
   878   1299   void setup_editcss(void){
   879   1300     login_check_credentials();
   880         -  if( !g.okSetup ){
         1301  +  if( !g.perm.Setup ){
   881   1302       login_needed();
   882   1303     }
   883   1304     db_begin_transaction();
   884   1305     if( P("clear")!=0 ){
   885   1306       db_multi_exec("DELETE FROM config WHERE name='css'");
   886   1307       cgi_replace_parameter("css", zDefaultCSS);
   887   1308       db_end_transaction(0);
   888   1309       cgi_redirect("setup_editcss");
   889         -  }else{
   890         -    textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
   891   1310     }
   892   1311     if( P("submit")!=0 ){
         1312  +    textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
   893   1313       db_end_transaction(0);
   894   1314       cgi_redirect("setup_editcss");
   895   1315     }
   896   1316     style_header("Edit CSS");
   897         -  @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
         1317  +  @ <form action="%s(g.zTop)/setup_editcss" method="post"><div>
   898   1318     login_insert_csrf_secret();
   899   1319     @ Edit the CSS below:<br />
   900         -  textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
         1320  +  textarea_attribute("", 35, 80, "css", "css", zDefaultCSS);
   901   1321     @ <br />
   902         -  @ <input type="submit" name="submit" value="Apply Changes">
   903         -  @ <input type="submit" name="clear" value="Revert To Default">
   904         -  @ </form>
   905         -  @ <p><b>Note:</b> Press your browser Reload button after modifying the
   906         -  @ CSS in order to pull in the modified CSS file.</p>
   907         -  @ <hr>
         1322  +  @ <input type="submit" name="submit" value="Apply Changes" />
         1323  +  @ <input type="submit" name="clear" value="Revert To Default" />
         1324  +  @ </div></form>
         1325  +  @ <p><span class="note">Note:</span> Press your browser Reload button after
         1326  +  @ modifying the CSS in order to pull in the modified CSS file.</p>
         1327  +  @ <hr />
   908   1328     @ The default CSS is shown below for reference.  Other examples
   909   1329     @ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
   910   1330     @ See also the <a href="setup_header">header</a> and
   911   1331     @ <a href="setup_footer">footer</a> editing screens.
   912   1332     @ <blockquote><pre>
   913         -  @ %h(zDefaultCSS)
         1333  +  cgi_append_default_css();
   914   1334     @ </pre></blockquote>
   915   1335     style_footer();
   916   1336     db_end_transaction(0);
   917   1337   }
   918   1338   
   919   1339   /*
   920   1340   ** WEBPAGE: setup_header
   921   1341   */
   922   1342   void setup_header(void){
   923   1343     login_check_credentials();
   924         -  if( !g.okSetup ){
         1344  +  if( !g.perm.Setup ){
   925   1345       login_needed();
   926   1346     }
   927   1347     db_begin_transaction();
   928   1348     if( P("clear")!=0 ){
   929   1349       db_multi_exec("DELETE FROM config WHERE name='header'");
   930   1350       cgi_replace_parameter("header", zDefaultHeader);
   931         -  }else{
         1351  +  }else if( P("submit")!=0 ){
   932   1352       textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
         1353  +  }else if( P("fixbase")!=0 ){
         1354  +    const char *z = db_get("header", (char*)zDefaultHeader);
         1355  +    char *zHead = strstr(z, "<head>");
         1356  +    if( strstr(z, "<base href=")==0 && zHead!=0 ){
         1357  +      char *zNew;
         1358  +      char *zTail = &zHead[6];
         1359  +      while( fossil_isspace(zTail[0]) ) zTail++;
         1360  +      zNew = mprintf("%.*s\n<base href=\"$baseurl/$current_page\" />\n%s",
         1361  +                     zHead+6-z, z, zTail);
         1362  +      cgi_replace_parameter("header", zNew);
         1363  +      db_set("header", zNew, 0);
         1364  +    }
   933   1365     }
         1366  +
   934   1367     style_header("Edit Page Header");
   935         -  @ <form action="%s(g.zBaseURL)/setup_header" method="POST">
         1368  +  @ <form action="%R/setup_header" method="post"><div>
         1369  +
         1370  +  /* Make sure the header contains <base href="...">.   Issue a warning
         1371  +  ** if it does not. */
         1372  +  if( !cgi_header_contains("<base href=") ){
         1373  +    @ <p class="generalError">Please add
         1374  +    @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
         1375  +    @ <tt>&lt;head&gt;</tt> in the header!
         1376  +    @ <input type="submit" name="fixbase" value="Add &lt;base&gt; Now"></p>
         1377  +  }
         1378  +
   936   1379     login_insert_csrf_secret();
   937   1380     @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
   938   1381     @ generate the beginning of every page through start of the main
   939   1382     @ menu.</p>
   940         -  textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
         1383  +  textarea_attribute("", 35, 80, "header", "header", zDefaultHeader);
   941   1384     @ <br />
   942         -  @ <input type="submit" name="submit" value="Apply Changes">
   943         -  @ <input type="submit" name="clear" value="Revert To Default">
   944         -  @ </form>
   945         -  @ <hr>
         1385  +  @ <input type="submit" name="submit" value="Apply Changes" />
         1386  +  @ <input type="submit" name="clear" value="Revert To Default" />
         1387  +  @ </div></form>
         1388  +  @ <hr />
   946   1389     @ The default header is shown below for reference.  Other examples
   947   1390     @ of headers can be seen on the <a href="setup_skin">skins page</a>.
   948   1391     @ See also the <a href="setup_editcss">CSS</a> and
   949   1392     @ <a href="setup_footer">footer</a> editing screeens.
   950   1393     @ <blockquote><pre>
   951   1394     @ %h(zDefaultHeader)
   952   1395     @ </pre></blockquote>
................................................................................
   955   1398   }
   956   1399   
   957   1400   /*
   958   1401   ** WEBPAGE: setup_footer
   959   1402   */
   960   1403   void setup_footer(void){
   961   1404     login_check_credentials();
   962         -  if( !g.okSetup ){
         1405  +  if( !g.perm.Setup ){
   963   1406       login_needed();
   964   1407     }
   965   1408     db_begin_transaction();
   966   1409     if( P("clear")!=0 ){
   967   1410       db_multi_exec("DELETE FROM config WHERE name='footer'");
   968   1411       cgi_replace_parameter("footer", zDefaultFooter);
   969         -  }else{
   970         -    textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
   971   1412     }
         1413  +
   972   1414     style_header("Edit Page Footer");
   973         -  @ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
         1415  +  @ <form action="%s(g.zTop)/setup_footer" method="post"><div>
   974   1416     login_insert_csrf_secret();
   975   1417     @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
   976   1418     @ generate the end of every page.</p>
   977   1419     textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
   978   1420     @ <br />
   979         -  @ <input type="submit" name="submit" value="Apply Changes">
   980         -  @ <input type="submit" name="clear" value="Revert To Default">
   981         -  @ </form>
   982         -  @ <hr>
         1421  +  @ <input type="submit" name="submit" value="Apply Changes" />
         1422  +  @ <input type="submit" name="clear" value="Revert To Default" />
         1423  +  @ </div></form>
         1424  +  @ <hr />
   983   1425     @ The default footer is shown below for reference.  Other examples
   984   1426     @ of footers can be seen on the <a href="setup_skin">skins page</a>.
   985   1427     @ See also the <a href="setup_editcss">CSS</a> and
   986   1428     @ <a href="setup_header">header</a> editing screens.
   987   1429     @ <blockquote><pre>
   988   1430     @ %h(zDefaultFooter)
   989   1431     @ </pre></blockquote>
   990   1432     style_footer();
   991   1433     db_end_transaction(0);
   992   1434   }
         1435  +
         1436  +/*
         1437  +** WEBPAGE: setup_modreq
         1438  +*/
         1439  +void setup_modreq(void){
         1440  +  login_check_credentials();
         1441  +  if( !g.perm.Setup ){
         1442  +    login_needed();
         1443  +  }
         1444  +
         1445  +  style_header("Moderator For Wiki And Tickets");
         1446  +  db_begin_transaction();
         1447  +  @ <form action="%R/setup_modreq" method="post"><div>
         1448  +  login_insert_csrf_secret();
         1449  +  @ <hr />
         1450  +  onoff_attribute("Moderate ticket changes",
         1451  +     "modreq-tkt", "modreq-tkt", 0);
         1452  +  @ <p>When enabled, any change to tickets is subject to the approval
         1453  +  @ a ticket moderator - a user with the "q" or Mod-Tkt privilege.
         1454  +  @ Ticket changes enter the system and are shown locally, but are not
         1455  +  @ synced until they are approved.  The moderator has the option to 
         1456  +  @ delete the change rather than approve it.  Ticket changes made by
         1457  +  @ a user who hwas the Mod-Tkt privilege are never subject to
         1458  +  @ moderation.
         1459  +  @
         1460  +  @ <hr />
         1461  +  onoff_attribute("Moderate wiki changes",
         1462  +     "modreq-wiki", "modreq-wiki", 0);
         1463  +  @ <p>When enabled, any change to wiki is subject to the approval
         1464  +  @ a ticket moderator - a user with the "l" or Mod-Wiki privilege.
         1465  +  @ Wiki changes enter the system and are shown locally, but are not
         1466  +  @ synced until they are approved.  The moderator has the option to 
         1467  +  @ delete the change rather than approve it.  Wiki changes made by
         1468  +  @ a user who has the Mod-Wiki privilege are never subject to
         1469  +  @ moderation.
         1470  +  @ </p>
         1471  + 
         1472  +  @ <hr />
         1473  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
         1474  +  @ </div></form>
         1475  +  db_end_transaction(0);
         1476  +  style_footer();
         1477  +
         1478  +}
         1479  +
         1480  +/*
         1481  +** WEBPAGE: setup_adunit
         1482  +*/
         1483  +void setup_adunit(void){
         1484  +  login_check_credentials();
         1485  +  if( !g.perm.Setup ){
         1486  +    login_needed();
         1487  +  }
         1488  +  db_begin_transaction();
         1489  +  if( P("clear")!=0 ){
         1490  +    db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'");
         1491  +    cgi_replace_parameter("adunit","");
         1492  +  }
         1493  +
         1494  +  style_header("Edit Ad Unit");
         1495  +  @ <form action="%s(g.zTop)/setup_adunit" method="post"><div>
         1496  +  login_insert_csrf_secret();
         1497  +  @ <p>Edit HTML text for an ad unit that will be inserted after the
         1498  +  @ menu bar and above the content of every page.</p>
         1499  +  textarea_attribute("", 20, 80, "adunit", "adunit", "");
         1500  +  @ <br />
         1501  +  onoff_attribute("Omit ads to administrator",
         1502  +     "adunit-omit-if-admin", "oia", 0);
         1503  +  @ <br />
         1504  +  onoff_attribute("Omit ads to logged-in users",
         1505  +     "adunit-omit-if-user", "oiu", 0);
         1506  +  @ <br />
         1507  +  @ <input type="submit" name="submit" value="Apply Changes" />
         1508  +  @ <input type="submit" name="clear" value="Delete Ad-Unit" />
         1509  +  @ </div></form>
         1510  +  style_footer();
         1511  +  db_end_transaction(0);
         1512  +}
   993   1513   
   994   1514   /*
   995   1515   ** WEBPAGE: setup_logo
   996   1516   */
   997   1517   void setup_logo(void){
   998         -  const char *zMime = "image/gif";
   999         -  const char *aImg = P("im");
  1000         -  int szImg = atoi(PD("im:bytes","0"));
  1001         -  if( szImg>0 ){
  1002         -    zMime = PD("im:mimetype","image/gif");
         1518  +  const char *zLogoMime = db_get("logo-mimetype","image/gif");
         1519  +  const char *aLogoImg = P("logoim");
         1520  +  int szLogoImg = atoi(PD("logoim:bytes","0"));
         1521  +  const char *zBgMime = db_get("background-mimetype","image/gif");
         1522  +  const char *aBgImg = P("bgim");
         1523  +  int szBgImg = atoi(PD("bgim:bytes","0"));
         1524  +  if( szLogoImg>0 ){
         1525  +    zLogoMime = PD("logoim:mimetype","image/gif");
         1526  +  }
         1527  +  if( szBgImg>0 ){
         1528  +    zBgMime = PD("bgim:mimetype","image/gif");
  1003   1529     }
  1004   1530     login_check_credentials();
  1005         -  if( !g.okSetup ){
         1531  +  if( !g.perm.Setup ){
  1006   1532       login_needed();
  1007   1533     }
  1008   1534     db_begin_transaction();
  1009         -  if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){
         1535  +  if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){
         1536  +    Blob img;
         1537  +    Stmt ins;
         1538  +    blob_init(&img, aLogoImg, szLogoImg);
         1539  +    db_prepare(&ins,
         1540  +        "REPLACE INTO config(name,value,mtime)"
         1541  +        " VALUES('logo-image',:bytes,now())"
         1542  +    );
         1543  +    db_bind_blob(&ins, ":bytes", &img);
         1544  +    db_step(&ins);
         1545  +    db_finalize(&ins);
         1546  +    db_multi_exec(
         1547  +       "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())",
         1548  +       zLogoMime
         1549  +    );
         1550  +    db_end_transaction(0);
         1551  +    cgi_redirect("setup_logo");
         1552  +  }else if( P("clrlogo")!=0 ){
         1553  +    db_multi_exec(
         1554  +       "DELETE FROM config WHERE name IN "
         1555  +           "('logo-image','logo-mimetype')"
         1556  +    );
         1557  +    db_end_transaction(0);
         1558  +    cgi_redirect("setup_logo");
         1559  +  }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){
  1010   1560       Blob img;
  1011   1561       Stmt ins;
  1012         -    blob_init(&img, aImg, szImg);
         1562  +    blob_init(&img, aBgImg, szBgImg);
  1013   1563       db_prepare(&ins,
  1014         -        "REPLACE INTO config(name, value)"
  1015         -        " VALUES('logo-image',:bytes)"
         1564  +        "REPLACE INTO config(name,value,mtime)"
         1565  +        " VALUES('background-image',:bytes,now())"
  1016   1566       );
  1017   1567       db_bind_blob(&ins, ":bytes", &img);
  1018   1568       db_step(&ins);
  1019   1569       db_finalize(&ins);
  1020   1570       db_multi_exec(
  1021         -       "REPLACE INTO config(name, value) VALUES('logo-mimetype',%Q)",
  1022         -       zMime
         1571  +       "REPLACE INTO config(name,value,mtime)"
         1572  +       " VALUES('background-mimetype',%Q,now())",
         1573  +       zBgMime
  1023   1574       );
  1024   1575       db_end_transaction(0);
  1025   1576       cgi_redirect("setup_logo");
  1026         -  }else if( P("clr")!=0 ){
         1577  +  }else if( P("clrbg")!=0 ){
  1027   1578       db_multi_exec(
  1028         -       "DELETE FROM config WHERE name GLOB 'logo-*'"
         1579  +       "DELETE FROM config WHERE name IN "
         1580  +           "('background-image','background-mimetype')"
  1029   1581       );
  1030   1582       db_end_transaction(0);
  1031   1583       cgi_redirect("setup_logo");
  1032   1584     }
  1033         -  style_header("Edit Project Logo");
  1034         -  @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
  1035         -  @ like this:</p>
  1036         -  @ <blockquote><img src="%s(g.zTop)/logo" alt="logo"></blockquote>
         1585  +  style_header("Edit Project Logo And Background");
         1586  +  @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
         1587  +  @ and looks like this:</p>
         1588  +  @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" border="1" />
         1589  +  @ </p></blockquote>
  1037   1590     @
         1591  +  @ <form action="%s(g.zTop)/setup_logo" method="post"
         1592  +  @  enctype="multipart/form-data"><div>
  1038   1593     @ <p>The logo is accessible to all users at this URL:
  1039   1594     @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
  1040   1595     @ The logo may or may not appear on each
  1041   1596     @ page depending on the <a href="setup_editcss">CSS</a> and
  1042         -  @ <a href="setup_header">header setup</a>.</p>
  1043         -  @
  1044         -  @ <form action="%s(g.zBaseURL)/setup_logo" method="POST"
  1045         -  @  enctype="multipart/form-data">
  1046         -  @ <p>To set a new logo image, select a file to use as the logo using
  1047         -  @ the entry box below and then press the "Change Logo" button.</p>
         1597  +  @ <a href="setup_header">header setup</a>.
         1598  +  @ To change the logo image, use the following form:</p>
  1048   1599     login_insert_csrf_secret();
  1049   1600     @ Logo Image file:
  1050         -  @ <input type="file" name="im" size="60" accepts="image/*"><br>
  1051         -  @ <input type="submit" name="set" value="Change Logo">
  1052         -  @ <input type="submit" name="clr" value="Revert To Default">
  1053         -  @ </form>
         1601  +  @ <input type="file" name="logoim" size="60" accept="image/*" />
         1602  +  @ <p align="center">
         1603  +  @ <input type="submit" name="setlogo" value="Change Logo" />
         1604  +  @ <input type="submit" name="clrlogo" value="Revert To Default" /></p>
         1605  +  @ </div></form>
         1606  +  @ <hr />
         1607  +  @
         1608  +  @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
         1609  +  @ and looks like this:</p>
         1610  +  @ <blockquote><p><img src="%s(g.zTop)/background" alt="background" border=1 />
         1611  +  @ </p></blockquote>
         1612  +  @
         1613  +  @ <form action="%s(g.zTop)/setup_logo" method="post"
         1614  +  @  enctype="multipart/form-data"><div>
         1615  +  @ <p>The background image is accessible to all users at this URL:
         1616  +  @ <a href="%s(g.zBaseURL)/background">%s(g.zBaseURL)/background</a>.
         1617  +  @ The background image may or may not appear on each
         1618  +  @ page depending on the <a href="setup_editcss">CSS</a> and
         1619  +  @ <a href="setup_header">header setup</a>.
         1620  +  @ To change the background image, use the following form:</p>
         1621  +  login_insert_csrf_secret();
         1622  +  @ Background image file:
         1623  +  @ <input type="file" name="bgim" size="60" accept="image/*" />
         1624  +  @ <p align="center">
         1625  +  @ <input type="submit" name="setbg" value="Change Background" />
         1626  +  @ <input type="submit" name="clrbg" value="Revert To Default" /></p>
         1627  +  @ </div></form>
         1628  +  @ <hr />
  1054   1629     @
  1055         -  @ <p><b>Note:</b>  Your browser has probably cached the logo image, so
  1056         -  @ you will probably need to press the Reload button on your browser after
  1057         -  @ changing the logo to provoke your browser to reload the new logo image.
  1058         -  @ </p>
         1630  +  @ <p><span class="note">Note:</span>  Your browser has probably cached these
         1631  +  @ images, so you may need to press the Reload button before changes will
         1632  +  @ take effect. </p>
  1059   1633     style_footer();
  1060   1634     db_end_transaction(0);
  1061   1635   }
         1636  +
         1637  +/*
         1638  +** Prevent the RAW SQL feature from being used to ATTACH a different
         1639  +** database and query it.
         1640  +**
         1641  +** Actually, the RAW SQL feature only does a single statement per request.
         1642  +** So it is not possible to ATTACH and then do a separate query.  This
         1643  +** routine is not strictly necessary, therefore.  But it does not hurt
         1644  +** to be paranoid.
         1645  +*/
         1646  +int raw_sql_query_authorizer(
         1647  +  void *pError,
         1648  +  int code,
         1649  +  const char *zArg1,
         1650  +  const char *zArg2,
         1651  +  const char *zArg3,
         1652  +  const char *zArg4
         1653  +){
         1654  +  if( code==SQLITE_ATTACH ){
         1655  +    return SQLITE_DENY;
         1656  +  }
         1657  +  return SQLITE_OK;
         1658  +}
         1659  +
         1660  +
         1661  +/*
         1662  +** WEBPAGE: admin_sql
         1663  +**
         1664  +** Run raw SQL commands against the database file using the web interface.
         1665  +*/
         1666  +void sql_page(void){
         1667  +  const char *zQ = P("q");
         1668  +  int go = P("go")!=0;
         1669  +  login_check_credentials();
         1670  +  if( !g.perm.Setup ){
         1671  +    login_needed();
         1672  +  }
         1673  +  db_begin_transaction();
         1674  +  style_header("Raw SQL Commands");
         1675  +  @ <p><b>Caution:</b> There are no restrictions on the SQL that can be
         1676  +  @ run by this page.  You can do serious and irrepairable damage to the
         1677  +  @ repository.  Proceed with extreme caution.</p>
         1678  +  @
         1679  +  @ <p>Only a the first statement in the entry box will be run.
         1680  +  @ Any subsequent statements will be silently ignored.</p>
         1681  +  @
         1682  +  @ <p>Database names:<ul><li>repository &rarr; %s(db_name("repository"))
         1683  +  if( g.configOpen ){
         1684  +    @ <li>config &rarr; %s(db_name("configdb"))
         1685  +  }
         1686  +  if( g.localOpen ){
         1687  +    @ <li>local-checkout &rarr; %s(db_name("localdb"))
         1688  +  }
         1689  +  @ </ul></p>
         1690  +  @
         1691  +  @ <form method="post" action="%s(g.zTop)/admin_sql">
         1692  +  login_insert_csrf_secret();
         1693  +  @ SQL:<br />
         1694  +  @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
         1695  +  @ <input type="submit" name="go" value="Run SQL">
         1696  +  @ <input type="submit" name="schema" value="Show Schema">
         1697  +  @ <input type="submit" name="tablelist" value="List Tables">
         1698  +  @ </form>
         1699  +  if( P("schema") ){
         1700  +    zQ = sqlite3_mprintf(
         1701  +            "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL",
         1702  +            db_name("repository"));
         1703  +    go = 1;
         1704  +  }else if( P("tablelist") ){
         1705  +    zQ = sqlite3_mprintf(
         1706  +            "SELECT name FROM %s.sqlite_master WHERE type='table'"
         1707  +            " ORDER BY name",
         1708  +            db_name("repository"));
         1709  +    go = 1;
         1710  +  }
         1711  +  if( go ){
         1712  +    sqlite3_stmt *pStmt;
         1713  +    int rc;
         1714  +    const char *zTail;
         1715  +    int nCol;
         1716  +    int nRow = 0;
         1717  +    int i;
         1718  +    @ <hr />
         1719  +    login_verify_csrf_secret();
         1720  +    sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
         1721  +    rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
         1722  +    if( rc!=SQLITE_OK ){
         1723  +      @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
         1724  +      sqlite3_finalize(pStmt);
         1725  +    }else if( pStmt==0 ){
         1726  +      /* No-op */
         1727  +    }else if( (nCol = sqlite3_column_count(pStmt))==0 ){
         1728  +      sqlite3_step(pStmt);
         1729  +      rc = sqlite3_finalize(pStmt);
         1730  +      if( rc ){
         1731  +        @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
         1732  +      }
         1733  +    }else{
         1734  +      @ <table border=1>
         1735  +      while( sqlite3_step(pStmt)==SQLITE_ROW ){
         1736  +        if( nRow==0 ){
         1737  +          @ <tr>
         1738  +          for(i=0; i<nCol; i++){
         1739  +            @ <th>%h(sqlite3_column_name(pStmt, i))</th>
         1740  +          }
         1741  +          @ </tr>
         1742  +        }
         1743  +        nRow++;
         1744  +        @ <tr>
         1745  +        for(i=0; i<nCol; i++){
         1746  +          switch( sqlite3_column_type(pStmt, i) ){
         1747  +            case SQLITE_INTEGER:
         1748  +            case SQLITE_FLOAT: {
         1749  +               @ <td align="right" valign="top">
         1750  +               @ %s(sqlite3_column_text(pStmt, i))</td>
         1751  +               break;
         1752  +            }
         1753  +            case SQLITE_NULL: {
         1754  +               @ <td valign="top" align="center"><i>NULL</i></td>
         1755  +               break;
         1756  +            }
         1757  +            case SQLITE_TEXT: {
         1758  +               const char *zText = (const char*)sqlite3_column_text(pStmt, i);
         1759  +               @ <td align="left" valign="top"
         1760  +               @ style="white-space:pre;">%h(zText)</td>
         1761  +               break;
         1762  +            }
         1763  +            case SQLITE_BLOB: {
         1764  +               @ <td valign="top" align="center">
         1765  +               @ <i>%d(sqlite3_column_bytes(pStmt, i))-byte BLOB</i></td>
         1766  +               break;
         1767  +            }
         1768  +          }
         1769  +        }
         1770  +        @ </tr>
         1771  +      }
         1772  +      sqlite3_finalize(pStmt);
         1773  +      @ </table>
         1774  +    }
         1775  +  }
         1776  +  style_footer();
         1777  +}
         1778  +
         1779  +
         1780  +/*
         1781  +** WEBPAGE: admin_th1
         1782  +**
         1783  +** Run raw TH1 commands using the web interface.  If Tcl integration was
         1784  +** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
         1785  +** may be run as well.
         1786  +*/
         1787  +void th1_page(void){
         1788  +  const char *zQ = P("q");
         1789  +  int go = P("go")!=0;
         1790  +  login_check_credentials();
         1791  +  if( !g.perm.Setup ){
         1792  +    login_needed();
         1793  +  }
         1794  +  db_begin_transaction();
         1795  +  style_header("Raw TH1 Commands");
         1796  +  @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
         1797  +  @ run by this page.  If Tcl integration was enabled at compile-time and
         1798  +  @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
         1799  +  @
         1800  +  @ <form method="post" action="%s(g.zTop)/admin_th1">
         1801  +  login_insert_csrf_secret();
         1802  +  @ TH1:<br />
         1803  +  @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
         1804  +  @ <input type="submit" name="go" value="Run TH1">
         1805  +  @ </form>
         1806  +  if( go ){
         1807  +    const char *zR;
         1808  +    int rc;
         1809  +    int n;
         1810  +    @ <hr />
         1811  +    login_verify_csrf_secret();
         1812  +    rc = Th_Eval(g.interp, 0, zQ, -1);
         1813  +    zR = Th_GetResult(g.interp, &n);
         1814  +    if( rc==TH_OK ){
         1815  +      @ <pre class="th1result">%h(zR)</pre>
         1816  +    }else{
         1817  +      @ <pre class="th1error">%h(zR)</pre>
         1818  +    }
         1819  +  }
         1820  +  style_footer();
         1821  +}

Changes to src/sha1.c.

     1      1   /*
     2         -** This implementation of SHA1 is adapted from the example implementation
     3         -** contained in RFC-3174.
            2  +** This implementation of SHA1.
     4      3   */
     5         -#include <stdint.h>
     6      4   #include <sys/types.h>
     7      5   #include "config.h"
     8      6   #include "sha1.h"
     9      7   
    10         -/*
    11         - * If you do not have the ISO standard stdint.h header file, then you
    12         - * must typdef the following:
    13         - *    name              meaning
    14         - *  uint32_t         unsigned 32 bit integer
    15         - *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
    16         - *
    17         - */
    18         -#define SHA1HashSize 20
    19         -#define shaSuccess 0
    20         -#define shaInputTooLong 1
    21         -#define shaStateError 2
    22      8   
    23      9   /*
    24         - *  This structure will hold context information for the SHA-1
    25         - *  hashing operation
    26         - */
           10  +** The SHA1 implementation below is adapted from:
           11  +**
           12  +**  $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $
           13  +**  $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $
           14  +**
           15  +** SHA-1 in C
           16  +** By Steve Reid <steve@edmweb.com>
           17  +** 100% Public Domain
           18  +*/
    27     19   typedef struct SHA1Context SHA1Context;
    28     20   struct SHA1Context {
    29         -    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
    30         -
    31         -    uint32_t Length_Low;            /* Message length in bits      */
    32         -    uint32_t Length_High;           /* Message length in bits      */
    33         -
    34         -    int Message_Block_Index;   /* Index into message block array   */
    35         -    uint8_t Message_Block[64];      /* 512-bit message blocks      */
    36         -
    37         -    int Computed;               /* Is the digest computed?         */
    38         -    int Corrupted;             /* Is the message digest corrupted? */
           21  +  unsigned int state[5];
           22  +  unsigned int count[2];
           23  +  unsigned char buffer[64];
    39     24   };
    40     25   
    41     26   /*
    42         - *  sha1.c
    43         - *
    44         - *  Description:
    45         - *      This file implements the Secure Hashing Algorithm 1 as
    46         - *      defined in FIPS PUB 180-1 published April 17, 1995.
           27  + * blk0() and blk() perform the initial expand.
           28  + * I got the idea of expanding during the round function from SSLeay
    47     29    *
    48         - *      The SHA-1, produces a 160-bit message digest for a given
    49         - *      data stream.  It should take about 2**n steps to find a
    50         - *      message with the same digest as a given message and
    51         - *      2**(n/2) to find any two messages with the same digest,
    52         - *      when n is the digest size in bits.  Therefore, this
    53         - *      algorithm can serve as a means of providing a
    54         - *      "fingerprint" for a message.
    55         - *
    56         - *  Portability Issues:
    57         - *      SHA-1 is defined in terms of 32-bit "words".  This code
    58         - *      uses <stdint.h> (included via "sha1.h" to define 32 and 8
    59         - *      bit unsigned integer types.  If your C compiler does not
    60         - *      support 32 bit unsigned integers, this code is not
    61         - *      appropriate.
    62         - *
    63         - *  Caveats:
    64         - *      SHA-1 is designed to work with messages less than 2^64 bits
    65         - *      long.  Although SHA-1 allows a message digest to be generated
    66         - *      for messages of any number of bits less than 2^64, this
    67         - *      implementation only works with messages with a length that is
    68         - *      a multiple of the size of an 8-bit character.
           30  + * blk0le() for little-endian and blk0be() for big-endian.
           31  + */
           32  +#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
           33  +/*
           34  + * GCC by itself only generates left rotates.  Use right rotates if
           35  + * possible to be kinder to dinky implementations with iterative rotate
           36  + * instructions.
           37  + */
           38  +#define SHA_ROT(op, x, k) \
           39  +        ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
           40  +#define rol(x,k) SHA_ROT("roll", x, k)
           41  +#define ror(x,k) SHA_ROT("rorl", x, k)
           42  +
           43  +#else
           44  +/* Generic C equivalent */
           45  +#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
           46  +#define rol(x,k) SHA_ROT(x,k,32-(k))
           47  +#define ror(x,k) SHA_ROT(x,32-(k),k)
           48  +#endif
           49  +
           50  +
           51  +#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \
           52  +    |(rol(block[i],8)&0x00FF00FF))
           53  +#define blk0be(i) block[i]
           54  +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
           55  +    ^block[(i+2)&15]^block[i&15],1))
           56  +
           57  +/*
           58  + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
    69     59    *
    70         - */
    71         -
    72         -/*
    73         - *  Define the SHA1 circular left shift macro
           60  + * Rl0() for little-endian and Rb0() for big-endian.  Endianness is 
           61  + * determined at run-time.
    74     62    */
    75         -#define SHA1CircularShift(bits,word) \
    76         -                (((word) << (bits)) | ((word) >> (32-(bits))))
    77         -
    78         -/* Local Function Prototyptes */
    79         -static void SHA1PadMessage(SHA1Context *);
    80         -static void SHA1ProcessMessageBlock(SHA1Context *);
           63  +#define Rl0(v,w,x,y,z,i) \
           64  +    z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
           65  +#define Rb0(v,w,x,y,z,i) \
           66  +    z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
           67  +#define R1(v,w,x,y,z,i) \
           68  +    z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
           69  +#define R2(v,w,x,y,z,i) \
           70  +    z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
           71  +#define R3(v,w,x,y,z,i) \
           72  +    z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
           73  +#define R4(v,w,x,y,z,i) \
           74  +    z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);
    81     75   
    82     76   /*
    83         - *  SHA1Reset
    84         - *
    85         - *  Description:
    86         - *      This function will initialize the SHA1Context in preparation
    87         - *      for computing a new SHA1 message digest.
    88         - *
    89         - *  Parameters:
    90         - *      context: [in/out]
    91         - *          The context to reset.
    92         - *
    93         - *  Returns:
    94         - *      sha Error Code.
    95         - *
           77  + * Hash a single 512-bit block. This is the core of the algorithm.
    96     78    */
    97         -static int SHA1Reset(SHA1Context *context)
    98         -{
    99         -    context->Length_Low             = 0;
   100         -    context->Length_High            = 0;
   101         -    context->Message_Block_Index    = 0;
           79  +#define a qq[0]
           80  +#define b qq[1]
           81  +#define c qq[2]
           82  +#define d qq[3]
           83  +#define e qq[4]
   102     84   
   103         -    context->Intermediate_Hash[0]   = 0x67452301;
   104         -    context->Intermediate_Hash[1]   = 0xEFCDAB89;
   105         -    context->Intermediate_Hash[2]   = 0x98BADCFE;
   106         -    context->Intermediate_Hash[3]   = 0x10325476;
   107         -    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
   108         -
   109         -    context->Computed   = 0;
   110         -    context->Corrupted  = 0;
   111         -
   112         -    return shaSuccess;
   113         -}
   114         -
   115         -/*
   116         - *  SHA1Result
   117         - *
   118         - *  Description:
   119         - *      This function will return the 160-bit message digest into the
   120         - *      Message_Digest array  provided by the caller.
   121         - *      NOTE: The first octet of hash is stored in the 0th element,
   122         - *            the last octet of hash in the 19th element.
   123         - *
   124         - *  Parameters:
   125         - *      context: [in/out]
   126         - *          The context to use to calculate the SHA-1 hash.
   127         - *      Message_Digest: [out]
   128         - *          Where the digest is returned.
   129         - *
   130         - *  Returns:
   131         - *      sha Error Code.
   132         - *
   133         - */
   134         -static int SHA1Result( SHA1Context *context,
   135         -                uint8_t Message_Digest[SHA1HashSize])
           85  +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
   136     86   {
   137         -    int i;
   138         -
   139         -    if (context->Corrupted)
   140         -    {
   141         -        return context->Corrupted;
   142         -    }
   143         -
   144         -    if (!context->Computed)
   145         -    {
   146         -        SHA1PadMessage(context);
   147         -        for(i=0; i<64; ++i)
   148         -        {
   149         -            /* message may be sensitive, clear it out */
   150         -            context->Message_Block[i] = 0;
   151         -        }
   152         -        context->Length_Low = 0;    /* and clear length */
   153         -        context->Length_High = 0;
   154         -        context->Computed = 1;
   155         -
   156         -    }
   157         -
   158         -    for(i = 0; i < SHA1HashSize; ++i)
   159         -    {
   160         -        Message_Digest[i] = context->Intermediate_Hash[i>>2]
   161         -                            >> 8 * ( 3 - ( i & 0x03 ) );
   162         -    }
   163         -
   164         -    return shaSuccess;
   165         -}
   166         -
   167         -/*
   168         - *  SHA1Input
   169         - *
   170         - *  Description:
   171         - *      This function accepts an array of octets as the next portion
   172         - *      of the message.
   173         - *
   174         - *  Parameters:
   175         - *      context: [in/out]
   176         - *          The SHA context to update
   177         - *      message_array: [in]
   178         - *          An array of characters representing the next portion of
   179         - *          the message.
   180         - *      length: [in]
   181         - *          The length of the message in message_array
   182         - *
   183         - *  Returns:
   184         - *      sha Error Code.
   185         - *
           87  +  unsigned int qq[5]; /* a, b, c, d, e; */
           88  +  static int one = 1;
           89  +  unsigned int block[16];
           90  +  memcpy(block, buffer, 64);
           91  +  memcpy(qq,state,5*sizeof(unsigned int));
           92  +
           93  +  /* Copy context->state[] to working vars */
           94  +  /*
           95  +  a = state[0];
           96  +  b = state[1];
           97  +  c = state[2];
           98  +  d = state[3];
           99  +  e = state[4];
          100  +  */
          101  +
          102  +  /* 4 rounds of 20 operations each. Loop unrolled. */
          103  +  if( 1 == *(unsigned char*)&one ){
          104  +    Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
          105  +    Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
          106  +    Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
          107  +    Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
          108  +  }else{
          109  +    Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
          110  +    Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
          111  +    Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
          112  +    Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
          113  +  }
          114  +  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
          115  +  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
          116  +  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
          117  +  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
          118  +  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
          119  +  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
          120  +  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
          121  +  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
          122  +  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
          123  +  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
          124  +  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
          125  +  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
          126  +  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
          127  +  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
          128  +  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
          129  +  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
          130  +
          131  +  /* Add the working vars back into context.state[] */
          132  +  state[0] += a;
          133  +  state[1] += b;
          134  +  state[2] += c;
          135  +  state[3] += d;
          136  +  state[4] += e;
          137  +}
          138  +
          139  +
          140  +/*
          141  + * SHA1Init - Initialize new context
          142  + */
          143  +static void SHA1Init(SHA1Context *context){
          144  +    /* SHA1 initialization constants */
          145  +    context->state[0] = 0x67452301;
          146  +    context->state[1] = 0xEFCDAB89;
          147  +    context->state[2] = 0x98BADCFE;
          148  +    context->state[3] = 0x10325476;
          149  +    context->state[4] = 0xC3D2E1F0;
          150  +    context->count[0] = context->count[1] = 0;
          151  +}
          152  +
          153  +
          154  +/*
          155  + * Run your data through this.
   186    156    */
   187         -static
   188         -int SHA1Input(    SHA1Context    *context,
   189         -                  const uint8_t  *message_array,
   190         -                  unsigned       length)
   191         -{
   192         -    if (!length)
   193         -    {
   194         -        return shaSuccess;
   195         -    }
          157  +static void SHA1Update(
          158  +  SHA1Context *context,
          159  +  const unsigned char *data,
          160  +  unsigned int len
          161  +){
          162  +    unsigned int i, j;
   196    163   
   197         -    if (context->Computed)
   198         -    {
   199         -        context->Corrupted = shaStateError;
   200         -
   201         -        return shaStateError;
   202         -    }
   203         -
   204         -    if (context->Corrupted)
   205         -    {
   206         -         return context->Corrupted;
          164  +    j = context->count[0];
          165  +    if ((context->count[0] += len << 3) < j)
          166  +	context->count[1] += (len>>29)+1;
          167  +    j = (j >> 3) & 63;
          168  +    if ((j + len) > 63) {
          169  +	(void)memcpy(&context->buffer[j], data, (i = 64-j));
          170  +	SHA1Transform(context->state, context->buffer);
          171  +	for ( ; i + 63 < len; i += 64)
          172  +	    SHA1Transform(context->state, &data[i]);
          173  +	j = 0;
          174  +    } else {
          175  +	i = 0;
   207    176       }
   208         -    while(length-- && !context->Corrupted)
   209         -    {
   210         -    context->Message_Block[context->Message_Block_Index++] =
   211         -                    (*message_array & 0xFF);
   212         -
   213         -    context->Length_Low += 8;
   214         -    if (context->Length_Low == 0)
   215         -    {
   216         -        context->Length_High++;
   217         -        if (context->Length_High == 0)
   218         -        {
   219         -            /* Message is too long */
   220         -            context->Corrupted = 1;
   221         -        }
   222         -    }
   223         -
   224         -    if (context->Message_Block_Index == 64)
   225         -    {
   226         -        SHA1ProcessMessageBlock(context);
   227         -    }
   228         -
   229         -    message_array++;
   230         -    }
   231         -
   232         -    return shaSuccess;
          177  +    (void)memcpy(&context->buffer[j], &data[i], len - i);
   233    178   }
   234    179   
   235         -/*
   236         - *  SHA1ProcessMessageBlock
   237         - *
   238         - *  Description:
   239         - *      This function will process the next 512 bits of the message
   240         - *      stored in the Message_Block array.
   241         - *
   242         - *  Parameters:
   243         - *      None.
   244         - *
   245         - *  Returns:
   246         - *      Nothing.
   247         - *
   248         - *  Comments:
   249         - *      Many of the variable names in this code, especially the
   250         - *      single character names, were used because those were the
   251         - *      names used in the publication.
   252         - *
   253         - *
   254         - */
   255         -static void SHA1ProcessMessageBlock(SHA1Context *context)
   256         -{
   257         -    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
   258         -                            0x5A827999,
   259         -                            0x6ED9EBA1,
   260         -                            0x8F1BBCDC,
   261         -                            0xCA62C1D6
   262         -                            };
   263         -    int           t;                 /* Loop counter                */
   264         -    uint32_t      temp;              /* Temporary word value        */
   265         -    uint32_t      W[80];             /* Word sequence               */
   266         -    uint32_t      A, B, C, D, E;     /* Word buffers                */
   267         -
   268         -    /*
   269         -     *  Initialize the first 16 words in the array W
   270         -     */
   271         -    for(t = 0; t < 16; t++)
   272         -    {
   273         -        W[t] = context->Message_Block[t * 4] << 24;
   274         -        W[t] |= context->Message_Block[t * 4 + 1] << 16;
   275         -        W[t] |= context->Message_Block[t * 4 + 2] << 8;
   276         -        W[t] |= context->Message_Block[t * 4 + 3];
   277         -    }
   278         -
   279         -    for(t = 16; t < 80; t++)
   280         -    {
   281         -       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
   282         -    }
   283         -
   284         -    A = context->Intermediate_Hash[0];
   285         -    B = context->Intermediate_Hash[1];
   286         -    C = context->Intermediate_Hash[2];
   287         -    D = context->Intermediate_Hash[3];
   288         -    E = context->Intermediate_Hash[4];
   289         -
   290         -    for(t = 0; t < 20; t++)
   291         -    {
   292         -        temp =  SHA1CircularShift(5,A) +
   293         -                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
   294         -        E = D;
   295         -        D = C;
   296         -        C = SHA1CircularShift(30,B);
   297         -
   298         -        B = A;
   299         -        A = temp;
   300         -    }
   301         -
   302         -    for(t = 20; t < 40; t++)
   303         -    {
   304         -        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
   305         -        E = D;
   306         -        D = C;
   307         -        C = SHA1CircularShift(30,B);
   308         -        B = A;
   309         -        A = temp;
   310         -    }
   311         -
   312         -    for(t = 40; t < 60; t++)
   313         -    {
   314         -        temp = SHA1CircularShift(5,A) +
   315         -               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
   316         -        E = D;
   317         -        D = C;
   318         -        C = SHA1CircularShift(30,B);
   319         -        B = A;
   320         -        A = temp;
   321         -    }
   322         -
   323         -    for(t = 60; t < 80; t++)
   324         -    {
   325         -        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
   326         -        E = D;
   327         -        D = C;
   328         -        C = SHA1CircularShift(30,B);
   329         -        B = A;
   330         -        A = temp;
   331         -    }
   332         -
   333         -    context->Intermediate_Hash[0] += A;
   334         -    context->Intermediate_Hash[1] += B;
   335         -    context->Intermediate_Hash[2] += C;
   336         -    context->Intermediate_Hash[3] += D;
   337         -    context->Intermediate_Hash[4] += E;
   338         -
   339         -    context->Message_Block_Index = 0;
   340         -}
   341    180   
   342    181   /*
   343         - *  SHA1PadMessage
   344         - *
   345         -
   346         - *  Description:
   347         - *      According to the standard, the message must be padded to an even
   348         - *      512 bits.  The first padding bit must be a '1'.  The last 64
   349         - *      bits represent the length of the original message.  All bits in
   350         - *      between should be 0.  This function will pad the message
   351         - *      according to those rules by filling the Message_Block array
   352         - *      accordingly.  It will also call the ProcessMessageBlock function
   353         - *      provided appropriately.  When it returns, it can be assumed that
   354         - *      the message digest has been computed.
   355         - *
   356         - *  Parameters:
   357         - *      context: [in/out]
   358         - *          The context to pad
   359         - *      ProcessMessageBlock: [in]
   360         - *          The appropriate SHA*ProcessMessageBlock function
   361         - *  Returns:
   362         - *      Nothing.
   363         - *
          182  + * Add padding and return the message digest.
   364    183    */
   365         -static void SHA1PadMessage(SHA1Context *context)
   366         -{
   367         -    /*
   368         -     *  Check to see if the current message block is too small to hold
   369         -     *  the initial padding bits and length.  If so, we will pad the
   370         -     *  block, process it, and then continue padding into a second
   371         -     *  block.
   372         -     */
   373         -    if (context->Message_Block_Index > 55)
   374         -    {
   375         -        context->Message_Block[context->Message_Block_Index++] = 0x80;
   376         -        while(context->Message_Block_Index < 64)
   377         -        {
   378         -            context->Message_Block[context->Message_Block_Index++] = 0;
   379         -        }
          184  +static void SHA1Final(SHA1Context *context, unsigned char digest[20]){
          185  +    unsigned int i;
          186  +    unsigned char finalcount[8];
   380    187   
   381         -        SHA1ProcessMessageBlock(context);
   382         -
   383         -        while(context->Message_Block_Index < 56)
   384         -        {
   385         -            context->Message_Block[context->Message_Block_Index++] = 0;
   386         -        }
          188  +    for (i = 0; i < 8; i++) {
          189  +	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
          190  +	 >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
   387    191       }
   388         -    else
   389         -    {
   390         -        context->Message_Block[context->Message_Block_Index++] = 0x80;
   391         -        while(context->Message_Block_Index < 56)
   392         -        {
          192  +    SHA1Update(context, (const unsigned char *)"\200", 1);
          193  +    while ((context->count[0] & 504) != 448)
          194  +	SHA1Update(context, (const unsigned char *)"\0", 1);
          195  +    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
   393    196   
   394         -            context->Message_Block[context->Message_Block_Index++] = 0;
   395         -        }
          197  +    if (digest) {
          198  +	for (i = 0; i < 20; i++)
          199  +	    digest[i] = (unsigned char)
          200  +		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
   396    201       }
   397         -
   398         -    /*
   399         -     *  Store the message length as the last 8 octets
   400         -     */
   401         -    context->Message_Block[56] = context->Length_High >> 24;
   402         -    context->Message_Block[57] = context->Length_High >> 16;
   403         -    context->Message_Block[58] = context->Length_High >> 8;
   404         -    context->Message_Block[59] = context->Length_High;
   405         -    context->Message_Block[60] = context->Length_Low >> 24;
   406         -    context->Message_Block[61] = context->Length_Low >> 16;
   407         -    context->Message_Block[62] = context->Length_Low >> 8;
   408         -    context->Message_Block[63] = context->Length_Low;
   409         -
   410         -    SHA1ProcessMessageBlock(context);
   411    202   }
   412    203   
   413    204   
   414    205   /*
   415    206   ** Convert a digest into base-16.  digest should be declared as
   416    207   ** "unsigned char digest[20]" in the calling function.  The SHA1
   417    208   ** digest is stored in the first 20 bytes.  zBuf should
   418    209   ** be "char zBuf[41]".
   419    210   */
   420    211   static void DigestToBase16(unsigned char *digest, char *zBuf){
   421    212     static char const zEncode[] = "0123456789abcdef";
   422         -  int i, j;
          213  +  int ix;
   423    214   
   424         -  for(j=i=0; i<20; i++){
   425         -    int a = digest[i];
   426         -    zBuf[j++] = zEncode[(a>>4)&0xf];
   427         -    zBuf[j++] = zEncode[a & 0xf];
          215  +  for(ix=0; ix<20; ix++){
          216  +    *zBuf++ = zEncode[(*digest>>4)&0xf];
          217  +    *zBuf++ = zEncode[*digest++ & 0xf];
   428    218     }
   429         -  zBuf[j] = 0;
          219  +  *zBuf = '\0';
   430    220   }
   431    221   
   432    222   /*
   433    223   ** The state of a incremental SHA1 checksum computation.  Only one
   434    224   ** such computation can be underway at a time, of course.
   435    225   */
   436    226   static SHA1Context incrCtx;
................................................................................
   437    227   static int incrInit = 0;
   438    228   
   439    229   /*
   440    230   ** Add more text to the incremental SHA1 checksum.
   441    231   */
   442    232   void sha1sum_step_text(const char *zText, int nBytes){
   443    233     if( !incrInit ){
   444         -    SHA1Reset(&incrCtx);
          234  +    SHA1Init(&incrCtx);
   445    235       incrInit = 1;
   446    236     }
   447    237     if( nBytes<=0 ){
   448    238       if( nBytes==0 ) return;
   449    239       nBytes = strlen(zText);
   450    240     }
   451         -  SHA1Input(&incrCtx, (unsigned char*)zText, nBytes);
          241  +  SHA1Update(&incrCtx, (unsigned char*)zText, nBytes);
   452    242   }
   453    243   
   454    244   /*
   455    245   ** Add the content of a blob to the incremental SHA1 checksum.
   456    246   */
   457    247   void sha1sum_step_blob(Blob *p){
   458    248     sha1sum_step_text(blob_buffer(p), blob_size(p));
................................................................................
   466    256   ** of computation.  The return pointer points to a static buffer that
   467    257   ** is overwritten by subsequent calls to this function.
   468    258   */
   469    259   char *sha1sum_finish(Blob *pOut){
   470    260     unsigned char zResult[20];
   471    261     static char zOut[41];
   472    262     sha1sum_step_text(0,0);
   473         -  SHA1Result(&incrCtx, zResult);
          263  +  SHA1Final(&incrCtx, zResult);
   474    264     incrInit = 0;
   475    265     DigestToBase16(zResult, zOut);
   476    266     if( pOut ){
   477    267       blob_zero(pOut);
   478    268       blob_append(pOut, zOut, 40);
   479    269     }
   480    270     return zOut;
   481    271   }
   482    272   
   483    273   
   484    274   /*
   485    275   ** Compute the SHA1 checksum of a file on disk.  Store the resulting
   486         -** checksum in the blob pCksum.  pCksum is assumed to be ininitialized.
          276  +** checksum in the blob pCksum.  pCksum is assumed to be initialized.
   487    277   **
   488    278   ** Return the number of errors.
   489    279   */
   490    280   int sha1sum_file(const char *zFilename, Blob *pCksum){
   491    281     FILE *in;
   492    282     SHA1Context ctx;
   493    283     unsigned char zResult[20];
   494    284     char zBuf[10240];
   495    285   
   496         -  in = fopen(zFilename,"rb");
          286  +  if( file_wd_islink(zFilename) ){
          287  +    /* Instead of file content, return sha1 of link destination path */
          288  +    Blob destinationPath;
          289  +    int rc;
          290  +    
          291  +    blob_read_link(&destinationPath, zFilename);
          292  +    rc = sha1sum_blob(&destinationPath, pCksum);
          293  +    blob_reset(&destinationPath);
          294  +    return rc;
          295  +  }
          296  +
          297  +  in = fossil_fopen(zFilename,"rb");
   497    298     if( in==0 ){
   498    299       return 1;
   499    300     }
   500         -  SHA1Reset(&ctx);
          301  +  SHA1Init(&ctx);
   501    302     for(;;){
   502    303       int n;
   503    304       n = fread(zBuf, 1, sizeof(zBuf), in);
   504    305       if( n<=0 ) break;
   505         -    SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n);
          306  +    SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
   506    307     }
   507    308     fclose(in);
   508    309     blob_zero(pCksum);
   509    310     blob_resize(pCksum, 40);
   510         -  SHA1Result(&ctx, zResult);
          311  +  SHA1Final(&ctx, zResult);
   511    312     DigestToBase16(zResult, blob_buffer(pCksum));
   512    313     return 0;
   513    314   }
   514    315   
   515    316   /*
   516    317   ** Compute the SHA1 checksum of a blob in memory.  Store the resulting
   517    318   ** checksum in the blob pCksum.  pCksum is assumed to be either
................................................................................
   519    320   **
   520    321   ** Return the number of errors.
   521    322   */
   522    323   int sha1sum_blob(const Blob *pIn, Blob *pCksum){
   523    324     SHA1Context ctx;
   524    325     unsigned char zResult[20];
   525    326   
   526         -  SHA1Reset(&ctx);
   527         -  SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
          327  +  SHA1Init(&ctx);
          328  +  SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
   528    329     if( pIn==pCksum ){
   529    330       blob_reset(pCksum);
   530    331     }else{
   531    332       blob_zero(pCksum);
   532    333     }
   533    334     blob_resize(pCksum, 40);
   534         -  SHA1Result(&ctx, zResult);
          335  +  SHA1Final(&ctx, zResult);
   535    336     DigestToBase16(zResult, blob_buffer(pCksum));
   536    337     return 0;
   537    338   }
   538    339   
   539    340   /*
   540    341   ** Compute the SHA1 checksum of a zero-terminated string.  The
   541    342   ** result is held in memory obtained from mprintf().
   542    343   */
   543    344   char *sha1sum(const char *zIn){
   544    345     SHA1Context ctx;
   545    346     unsigned char zResult[20];
   546    347     char zDigest[41];
   547    348   
   548         -  SHA1Reset(&ctx);
   549         -  SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
   550         -  SHA1Result(&ctx, zResult);
          349  +  SHA1Init(&ctx);
          350  +  SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
          351  +  SHA1Final(&ctx, zResult);
   551    352     DigestToBase16(zResult, zDigest);
   552    353     return mprintf("%s", zDigest);
   553    354   }
   554    355   
   555    356   /*
   556    357   ** Convert a cleartext password for a specific user into a SHA1 hash.
   557    358   ** 
................................................................................
   565    366   ** The result of this function is the shared secret used by a client
   566    367   ** to authenticate to a server for the sync protocol.  It is also the
   567    368   ** value stored in the USER.PW field of the database.  By mixing in the
   568    369   ** login name and the project id with the hash, different shared secrets
   569    370   ** are obtained even if two users select the same password, or if a 
   570    371   ** single user selects the same password for multiple projects.
   571    372   */
   572         -char *sha1_shared_secret(const char *zPw, const char *zLogin){
          373  +char *sha1_shared_secret(
          374  +  const char *zPw,        /* The password to encrypt */
          375  +  const char *zLogin,     /* Username */
          376  +  const char *zProjCode   /* Project-code.  Use built-in project code if NULL */
          377  +){
   573    378     static char *zProjectId = 0;
   574    379     SHA1Context ctx;
   575    380     unsigned char zResult[20];
   576    381     char zDigest[41];
   577    382   
   578         -  SHA1Reset(&ctx);
   579         -  if( zProjectId==0 ){
   580         -    zProjectId = db_get("project-code", 0);
          383  +  SHA1Init(&ctx);
          384  +  if( zProjCode==0 ){
          385  +    if( zProjectId==0 ){
          386  +      zProjectId = db_get("project-code", 0);
   581    387   
   582         -    /* On the first xfer request of a clone, the project-code is not yet
   583         -    ** known.  Use the cleartext password, since that is all we have.
   584         -    */
   585         -    if( zProjectId==0 ){
   586         -      return mprintf("%s", zPw);
          388  +      /* On the first xfer request of a clone, the project-code is not yet
          389  +      ** known.  Use the cleartext password, since that is all we have.
          390  +      */
          391  +      if( zProjectId==0 ){
          392  +        return mprintf("%s", zPw);
          393  +      }
   587    394       }
          395  +    zProjCode = zProjectId;
   588    396     }
   589         -  SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
   590         -  SHA1Input(&ctx, (unsigned char*)"/", 1);
   591         -  SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
   592         -  SHA1Input(&ctx, (unsigned char*)"/", 1);
   593         -  SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
   594         -  SHA1Result(&ctx, zResult);
          397  +  SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
          398  +  SHA1Update(&ctx, (unsigned char*)"/", 1);
          399  +  SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
          400  +  SHA1Update(&ctx, (unsigned char*)"/", 1);
          401  +  SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
          402  +  SHA1Final(&ctx, zResult);
   595    403     DigestToBase16(zResult, zDigest);
   596    404     return mprintf("%s", zDigest);
   597    405   }
   598    406   
   599    407   /*
   600         -** COMMAND: sha1sum
          408  +** Implement the shared_secret() SQL function.  shared_secret() takes two or
          409  +** three arguments; the third argument is optional.
          410  +**
          411  +** (1) The cleartext password
          412  +** (2) The login name
          413  +** (3) The project code
          414  +**
          415  +** Returns sha1($password/$login/$projcode).
          416  +*/
          417  +void sha1_shared_secret_sql_function(
          418  +  sqlite3_context *context,
          419  +  int argc,
          420  +  sqlite3_value **argv
          421  +){
          422  +  const char *zPw;
          423  +  const char *zLogin;
          424  +  const char *zProjid;
          425  +
          426  +  assert( argc==2 || argc==3 );
          427  +  zPw = (const char*)sqlite3_value_text(argv[0]);
          428  +  if( zPw==0 || zPw[0]==0 ) return;
          429  +  zLogin = (const char*)sqlite3_value_text(argv[1]);
          430  +  if( zLogin==0 ) return;
          431  +  if( argc==3 ){
          432  +    zProjid = (const char*)sqlite3_value_text(argv[2]);
          433  +    if( zProjid && zProjid[0]==0 ) zProjid = 0;
          434  +  }else{
          435  +    zProjid = 0;
          436  +  }
          437  +  sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
          438  +                      fossil_free);
          439  +}
          440  +
          441  +/*
          442  +** COMMAND: sha1sum*
   601    443   ** %fossil sha1sum FILE...
   602    444   **
   603    445   ** Compute an SHA1 checksum of all files named on the command-line.
   604    446   ** If an file is named "-" then take its content from standard input.
   605    447   */
   606    448   void sha1sum_test(void){
   607    449     int i;
................................................................................
   612    454       blob_init(&cksum, "************** not found ***************", -1);
   613    455       if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){
   614    456         blob_read_from_channel(&in, stdin, -1);
   615    457         sha1sum_blob(&in, &cksum);
   616    458       }else{
   617    459         sha1sum_file(g.argv[i], &cksum);
   618    460       }
   619         -    printf("%s  %s\n", blob_str(&cksum), g.argv[i]);
          461  +    fossil_print("%s  %s\n", blob_str(&cksum), g.argv[i]);
   620    462       blob_reset(&cksum);
   621    463     }
   622    464   }

Added src/shell.c.

            1  +/*
            2  +** 2001 September 15
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +** This file contains code to implement the "sqlite" command line
           13  +** utility for accessing SQLite databases.
           14  +*/
           15  +#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
           16  +/* This needs to come before any includes for MSVC compiler */
           17  +#define _CRT_SECURE_NO_WARNINGS
           18  +#endif
           19  +
           20  +/*
           21  +** Enable large-file support for fopen() and friends on unix.
           22  +*/
           23  +#ifndef SQLITE_DISABLE_LFS
           24  +# define _LARGE_FILE       1
           25  +# ifndef _FILE_OFFSET_BITS
           26  +#   define _FILE_OFFSET_BITS 64
           27  +# endif
           28  +# define _LARGEFILE_SOURCE 1
           29  +#endif
           30  +
           31  +#include <stdlib.h>
           32  +#include <string.h>
           33  +#include <stdio.h>
           34  +#include <assert.h>
           35  +#include "sqlite3.h"
           36  +#include <ctype.h>
           37  +#include <stdarg.h>
           38  +
           39  +#if !defined(_WIN32) && !defined(WIN32)
           40  +# include <signal.h>
           41  +# if !defined(__RTP__) && !defined(_WRS_KERNEL)
           42  +#  include <pwd.h>
           43  +# endif
           44  +# include <unistd.h>
           45  +# include <sys/types.h>
           46  +#endif
           47  +
           48  +#ifdef HAVE_EDITLINE
           49  +# include <editline/editline.h>
           50  +#endif
           51  +#if defined(HAVE_READLINE) && HAVE_READLINE==1
           52  +# include <readline/readline.h>
           53  +# include <readline/history.h>
           54  +#endif
           55  +#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
           56  +# define readline(p) local_getline(p,stdin,0)
           57  +# define add_history(X)
           58  +# define read_history(X)
           59  +# define write_history(X)
           60  +# define stifle_history(X)
           61  +#endif
           62  +
           63  +#if defined(_WIN32) || defined(WIN32)
           64  +# include <io.h>
           65  +#define isatty(h) _isatty(h)
           66  +#define access(f,m) _access((f),(m))
           67  +#undef popen
           68  +#define popen(a,b) _popen((a),(b))
           69  +#undef pclose
           70  +#define pclose(x) _pclose(x)
           71  +#else
           72  +/* Make sure isatty() has a prototype.
           73  +*/
           74  +extern int isatty(int);
           75  +#endif
           76  +
           77  +#if defined(_WIN32_WCE)
           78  +/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty()
           79  + * thus we always assume that we have a console. That can be
           80  + * overridden with the -batch command line option.
           81  + */
           82  +#define isatty(x) 1
           83  +#endif
           84  +
           85  +/* True if the timer is enabled */
           86  +static int enableTimer = 0;
           87  +
           88  +/* ctype macros that work with signed characters */
           89  +#define IsSpace(X)  isspace((unsigned char)X)
           90  +#define IsDigit(X)  isdigit((unsigned char)X)
           91  +#define ToLower(X)  (char)tolower((unsigned char)X)
           92  +
           93  +#if !defined(_WIN32) && !defined(WIN32) && !defined(_WRS_KERNEL)
           94  +#include <sys/time.h>
           95  +#include <sys/resource.h>
           96  +
           97  +/* Saved resource information for the beginning of an operation */
           98  +static struct rusage sBegin;
           99  +
          100  +/*
          101  +** Begin timing an operation
          102  +*/
          103  +static void beginTimer(void){
          104  +  if( enableTimer ){
          105  +    getrusage(RUSAGE_SELF, &sBegin);
          106  +  }
          107  +}
          108  +
          109  +/* Return the difference of two time_structs in seconds */
          110  +static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
          111  +  return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + 
          112  +         (double)(pEnd->tv_sec - pStart->tv_sec);
          113  +}
          114  +
          115  +/*
          116  +** Print the timing results.
          117  +*/
          118  +static void endTimer(void){
          119  +  if( enableTimer ){
          120  +    struct rusage sEnd;
          121  +    getrusage(RUSAGE_SELF, &sEnd);
          122  +    printf("CPU Time: user %f sys %f\n",
          123  +       timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
          124  +       timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
          125  +  }
          126  +}
          127  +
          128  +#define BEGIN_TIMER beginTimer()
          129  +#define END_TIMER endTimer()
          130  +#define HAS_TIMER 1
          131  +
          132  +#elif (defined(_WIN32) || defined(WIN32))
          133  +
          134  +#include <windows.h>
          135  +
          136  +/* Saved resource information for the beginning of an operation */
          137  +static HANDLE hProcess;
          138  +static FILETIME ftKernelBegin;
          139  +static FILETIME ftUserBegin;
          140  +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
          141  +static GETPROCTIMES getProcessTimesAddr = NULL;
          142  +
          143  +/*
          144  +** Check to see if we have timer support.  Return 1 if necessary
          145  +** support found (or found previously).
          146  +*/
          147  +static int hasTimer(void){
          148  +  if( getProcessTimesAddr ){
          149  +    return 1;
          150  +  } else {
          151  +    /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions.
          152  +    ** See if the version we are running on has it, and if it does, save off
          153  +    ** a pointer to it and the current process handle.
          154  +    */
          155  +    hProcess = GetCurrentProcess();
          156  +    if( hProcess ){
          157  +      HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
          158  +      if( NULL != hinstLib ){
          159  +        getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
          160  +        if( NULL != getProcessTimesAddr ){
          161  +          return 1;
          162  +        }
          163  +        FreeLibrary(hinstLib); 
          164  +      }
          165  +    }
          166  +  }
          167  +  return 0;
          168  +}
          169  +
          170  +/*
          171  +** Begin timing an operation
          172  +*/
          173  +static void beginTimer(void){
          174  +  if( enableTimer && getProcessTimesAddr ){
          175  +    FILETIME ftCreation, ftExit;
          176  +    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
          177  +  }
          178  +}
          179  +
          180  +/* Return the difference of two FILETIME structs in seconds */
          181  +static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
          182  +  sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
          183  +  sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
          184  +  return (double) ((i64End - i64Start) / 10000000.0);
          185  +}
          186  +
          187  +/*
          188  +** Print the timing results.
          189  +*/
          190  +static void endTimer(void){
          191  +  if( enableTimer && getProcessTimesAddr){
          192  +    FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
          193  +    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
          194  +    printf("CPU Time: user %f sys %f\n",
          195  +       timeDiff(&ftUserBegin, &ftUserEnd),
          196  +       timeDiff(&ftKernelBegin, &ftKernelEnd));
          197  +  }
          198  +}
          199  +
          200  +#define BEGIN_TIMER beginTimer()
          201  +#define END_TIMER endTimer()
          202  +#define HAS_TIMER hasTimer()
          203  +
          204  +#else
          205  +#define BEGIN_TIMER 
          206  +#define END_TIMER
          207  +#define HAS_TIMER 0
          208  +#endif
          209  +
          210  +/*
          211  +** Used to prevent warnings about unused parameters
          212  +*/
          213  +#define UNUSED_PARAMETER(x) (void)(x)
          214  +
          215  +/*
          216  +** If the following flag is set, then command execution stops
          217  +** at an error if we are not interactive.
          218  +*/
          219  +static int bail_on_error = 0;
          220  +
          221  +/*
          222  +** Threat stdin as an interactive input if the following variable
          223  +** is true.  Otherwise, assume stdin is connected to a file or pipe.
          224  +*/
          225  +static int stdin_is_interactive = 1;
          226  +
          227  +/*
          228  +** The following is the open SQLite database.  We make a pointer
          229  +** to this database a static variable so that it can be accessed
          230  +** by the SIGINT handler to interrupt database processing.
          231  +*/
          232  +static sqlite3 *db = 0;
          233  +
          234  +/*
          235  +** True if an interrupt (Control-C) has been received.
          236  +*/
          237  +static volatile int seenInterrupt = 0;
          238  +
          239  +/*
          240  +** This is the name of our program. It is set in main(), used
          241  +** in a number of other places, mostly for error messages.
          242  +*/
          243  +static char *Argv0;
          244  +
          245  +/*
          246  +** Prompt strings. Initialized in main. Settable with
          247  +**   .prompt main continue
          248  +*/
          249  +static char mainPrompt[20];     /* First line prompt. default: "sqlite> "*/
          250  +static char continuePrompt[20]; /* Continuation prompt. default: "   ...> " */
          251  +
          252  +/*
          253  +** Write I/O traces to the following stream.
          254  +*/
          255  +#ifdef SQLITE_ENABLE_IOTRACE
          256  +static FILE *iotrace = 0;
          257  +#endif
          258  +
          259  +/*
          260  +** This routine works like printf in that its first argument is a
          261  +** format string and subsequent arguments are values to be substituted
          262  +** in place of % fields.  The result of formatting this string
          263  +** is written to iotrace.
          264  +*/
          265  +#ifdef SQLITE_ENABLE_IOTRACE
          266  +static void iotracePrintf(const char *zFormat, ...){
          267  +  va_list ap;
          268  +  char *z;
          269  +  if( iotrace==0 ) return;
          270  +  va_start(ap, zFormat);
          271  +  z = sqlite3_vmprintf(zFormat, ap);
          272  +  va_end(ap);
          273  +  fprintf(iotrace, "%s", z);
          274  +  sqlite3_free(z);
          275  +}
          276  +#endif
          277  +
          278  +
          279  +/*
          280  +** Determines if a string is a number of not.
          281  +*/
          282  +static int isNumber(const char *z, int *realnum){
          283  +  if( *z=='-' || *z=='+' ) z++;
          284  +  if( !IsDigit(*z) ){
          285  +    return 0;
          286  +  }
          287  +  z++;
          288  +  if( realnum ) *realnum = 0;
          289  +  while( IsDigit(*z) ){ z++; }
          290  +  if( *z=='.' ){
          291  +    z++;
          292  +    if( !IsDigit(*z) ) return 0;
          293  +    while( IsDigit(*z) ){ z++; }
          294  +    if( realnum ) *realnum = 1;
          295  +  }
          296  +  if( *z=='e' || *z=='E' ){
          297  +    z++;
          298  +    if( *z=='+' || *z=='-' ) z++;
          299  +    if( !IsDigit(*z) ) return 0;
          300  +    while( IsDigit(*z) ){ z++; }
          301  +    if( realnum ) *realnum = 1;
          302  +  }
          303  +  return *z==0;
          304  +}
          305  +
          306  +/*
          307  +** A global char* and an SQL function to access its current value 
          308  +** from within an SQL statement. This program used to use the 
          309  +** sqlite_exec_printf() API to substitue a string into an SQL statement.
          310  +** The correct way to do this with sqlite3 is to use the bind API, but
          311  +** since the shell is built around the callback paradigm it would be a lot
          312  +** of work. Instead just use this hack, which is quite harmless.
          313  +*/
          314  +static const char *zShellStatic = 0;
          315  +static void shellstaticFunc(
          316  +  sqlite3_context *context,
          317  +  int argc,
          318  +  sqlite3_value **argv
          319  +){
          320  +  assert( 0==argc );
          321  +  assert( zShellStatic );
          322  +  UNUSED_PARAMETER(argc);
          323  +  UNUSED_PARAMETER(argv);
          324  +  sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
          325  +}
          326  +
          327  +
          328  +/*
          329  +** This routine reads a line of text from FILE in, stores
          330  +** the text in memory obtained from malloc() and returns a pointer
          331  +** to the text.  NULL is returned at end of file, or if malloc()
          332  +** fails.
          333  +**
          334  +** The interface is like "readline" but no command-line editing
          335  +** is done.
          336  +*/
          337  +static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
          338  +  char *zLine;
          339  +  int nLine;
          340  +  int n;
          341  +  int inQuote = 0;
          342  +
          343  +  if( zPrompt && *zPrompt ){
          344  +    printf("%s",zPrompt);
          345  +    fflush(stdout);
          346  +  }
          347  +  nLine = 100;
          348  +  zLine = malloc( nLine );
          349  +  if( zLine==0 ) return 0;
          350  +  n = 0;
          351  +  while( 1 ){
          352  +    if( n+100>nLine ){
          353  +      nLine = nLine*2 + 100;
          354  +      zLine = realloc(zLine, nLine);
          355  +      if( zLine==0 ) return 0;
          356  +    }
          357  +    if( fgets(&zLine[n], nLine - n, in)==0 ){
          358  +      if( n==0 ){
          359  +        free(zLine);
          360  +        return 0;
          361  +      }
          362  +      zLine[n] = 0;
          363  +      break;
          364  +    }
          365  +    while( zLine[n] ){
          366  +      if( zLine[n]=='"' ) inQuote = !inQuote;
          367  +      n++;
          368  +    }
          369  +    if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
          370  +      n--;
          371  +      if( n>0 && zLine[n-1]=='\r' ) n--;
          372  +      zLine[n] = 0;
          373  +      break;
          374  +    }
          375  +  }
          376  +  zLine = realloc( zLine, n+1 );
          377  +  return zLine;
          378  +}
          379  +
          380  +/*
          381  +** Retrieve a single line of input text.
          382  +**
          383  +** zPrior is a string of prior text retrieved.  If not the empty
          384  +** string, then issue a continuation prompt.
          385  +*/
          386  +static char *one_input_line(const char *zPrior, FILE *in){
          387  +  char *zPrompt;
          388  +  char *zResult;
          389  +  if( in!=0 ){
          390  +    return local_getline(0, in, 0);
          391  +  }
          392  +  if( zPrior && zPrior[0] ){
          393  +    zPrompt = continuePrompt;
          394  +  }else{
          395  +    zPrompt = mainPrompt;
          396  +  }
          397  +  zResult = readline(zPrompt);
          398  +#if defined(HAVE_READLINE) && HAVE_READLINE==1
          399  +  if( zResult && *zResult ) add_history(zResult);
          400  +#endif
          401  +  return zResult;
          402  +}
          403  +
          404  +struct previous_mode_data {
          405  +  int valid;        /* Is there legit data in here? */
          406  +  int mode;
          407  +  int showHeader;
          408  +  int colWidth[100];
          409  +};
          410  +
          411  +/*
          412  +** An pointer to an instance of this structure is passed from
          413  +** the main program to the callback.  This is used to communicate
          414  +** state and mode information.
          415  +*/
          416  +struct callback_data {
          417  +  sqlite3 *db;           /* The database */
          418  +  int echoOn;            /* True to echo input commands */
          419  +  int statsOn;           /* True to display memory stats before each finalize */
          420  +  int cnt;               /* Number of records displayed so far */
          421  +  FILE *out;             /* Write results here */
          422  +  FILE *traceOut;        /* Output for sqlite3_trace() */
          423  +  int nErr;              /* Number of errors seen */
          424  +  int mode;              /* An output mode setting */
          425  +  int writableSchema;    /* True if PRAGMA writable_schema=ON */
          426  +  int showHeader;        /* True to show column names in List or Column mode */
          427  +  char *zDestTable;      /* Name of destination table when MODE_Insert */
          428  +  char separator[20];    /* Separator character for MODE_List */
          429  +  int colWidth[100];     /* Requested width of each column when in column mode*/
          430  +  int actualWidth[100];  /* Actual width of each column */
          431  +  char nullvalue[20];    /* The text to print when a NULL comes back from
          432  +                         ** the database */
          433  +  struct previous_mode_data explainPrev;
          434  +                         /* Holds the mode information just before
          435  +                         ** .explain ON */
          436  +  char outfile[FILENAME_MAX]; /* Filename for *out */
          437  +  const char *zDbFilename;    /* name of the database file */
          438  +  const char *zVfs;           /* Name of VFS to use */
          439  +  sqlite3_stmt *pStmt;   /* Current statement if any. */
          440  +  FILE *pLog;            /* Write log output here */
          441  +};
          442  +
          443  +/*
          444  +** These are the allowed modes.
          445  +*/
          446  +#define MODE_Line     0  /* One column per line.  Blank line between records */
          447  +#define MODE_Column   1  /* One record per line in neat columns */
          448  +#define MODE_List     2  /* One record per line with a separator */
          449  +#define MODE_Semi     3  /* Same as MODE_List but append ";" to each line */
          450  +#define MODE_Html     4  /* Generate an XHTML table */
          451  +#define MODE_Insert   5  /* Generate SQL "insert" statements */
          452  +#define MODE_Tcl      6  /* Generate ANSI-C or TCL quoted elements */
          453  +#define MODE_Csv      7  /* Quote strings, numbers are plain */
          454  +#define MODE_Explain  8  /* Like MODE_Column, but do not truncate data */
          455  +
          456  +static const char *modeDescr[] = {
          457  +  "line",
          458  +  "column",
          459  +  "list",
          460  +  "semi",
          461  +  "html",
          462  +  "insert",
          463  +  "tcl",
          464  +  "csv",
          465  +  "explain",
          466  +};
          467  +
          468  +/*
          469  +** Number of elements in an array
          470  +*/
          471  +#define ArraySize(X)  (int)(sizeof(X)/sizeof(X[0]))
          472  +
          473  +/*
          474  +** Compute a string length that is limited to what can be stored in
          475  +** lower 30 bits of a 32-bit signed integer.
          476  +*/
          477  +static int strlen30(const char *z){
          478  +  const char *z2 = z;
          479  +  while( *z2 ){ z2++; }
          480  +  return 0x3fffffff & (int)(z2 - z);
          481  +}
          482  +
          483  +/*
          484  +** A callback for the sqlite3_log() interface.
          485  +*/
          486  +static void shellLog(void *pArg, int iErrCode, const char *zMsg){
          487  +  struct callback_data *p = (struct callback_data*)pArg;
          488  +  if( p->pLog==0 ) return;
          489  +  fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
          490  +  fflush(p->pLog);
          491  +}
          492  +
          493  +/*
          494  +** Output the given string as a hex-encoded blob (eg. X'1234' )
          495  +*/
          496  +static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
          497  +  int i;
          498  +  char *zBlob = (char *)pBlob;
          499  +  fprintf(out,"X'");
          500  +  for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]&0xff); }
          501  +  fprintf(out,"'");
          502  +}
          503  +
          504  +/*
          505  +** Output the given string as a quoted string using SQL quoting conventions.
          506  +*/
          507  +static void output_quoted_string(FILE *out, const char *z){
          508  +  int i;
          509  +  int nSingle = 0;
          510  +  for(i=0; z[i]; i++){
          511  +    if( z[i]=='\'' ) nSingle++;
          512  +  }
          513  +  if( nSingle==0 ){
          514  +    fprintf(out,"'%s'",z);
          515  +  }else{
          516  +    fprintf(out,"'");
          517  +    while( *z ){
          518  +      for(i=0; z[i] && z[i]!='\''; i++){}
          519  +      if( i==0 ){
          520  +        fprintf(out,"''");
          521  +        z++;
          522  +      }else if( z[i]=='\'' ){
          523  +        fprintf(out,"%.*s''",i,z);
          524  +        z += i+1;
          525  +      }else{
          526  +        fprintf(out,"%s",z);
          527  +        break;
          528  +      }
          529  +    }
          530  +    fprintf(out,"'");
          531  +  }
          532  +}
          533  +
          534  +/*
          535  +** Output the given string as a quoted according to C or TCL quoting rules.
          536  +*/
          537  +static void output_c_string(FILE *out, const char *z){
          538  +  unsigned int c;
          539  +  fputc('"', out);
          540  +  while( (c = *(z++))!=0 ){
          541  +    if( c=='\\' ){
          542  +      fputc(c, out);
          543  +      fputc(c, out);
          544  +    }else if( c=='"' ){
          545  +      fputc('\\', out);
          546  +      fputc('"', out);
          547  +    }else if( c=='\t' ){
          548  +      fputc('\\', out);
          549  +      fputc('t', out);
          550  +    }else if( c=='\n' ){
          551  +      fputc('\\', out);
          552  +      fputc('n', out);
          553  +    }else if( c=='\r' ){
          554  +      fputc('\\', out);
          555  +      fputc('r', out);
          556  +    }else if( !isprint(c) ){
          557  +      fprintf(out, "\\%03o", c&0xff);
          558  +    }else{
          559  +      fputc(c, out);
          560  +    }
          561  +  }
          562  +  fputc('"', out);
          563  +}
          564  +
          565  +/*
          566  +** Output the given string with characters that are special to
          567  +** HTML escaped.
          568  +*/
          569  +static void output_html_string(FILE *out, const char *z){
          570  +  int i;
          571  +  while( *z ){
          572  +    for(i=0;   z[i] 
          573  +            && z[i]!='<' 
          574  +            && z[i]!='&' 
          575  +            && z[i]!='>' 
          576  +            && z[i]!='\"' 
          577  +            && z[i]!='\'';
          578  +        i++){}
          579  +    if( i>0 ){
          580  +      fprintf(out,"%.*s",i,z);
          581  +    }
          582  +    if( z[i]=='<' ){
          583  +      fprintf(out,"&lt;");
          584  +    }else if( z[i]=='&' ){
          585  +      fprintf(out,"&amp;");
          586  +    }else if( z[i]=='>' ){
          587  +      fprintf(out,"&gt;");
          588  +    }else if( z[i]=='\"' ){
          589  +      fprintf(out,"&quot;");
          590  +    }else if( z[i]=='\'' ){
          591  +      fprintf(out,"&#39;");
          592  +    }else{
          593  +      break;
          594  +    }
          595  +    z += i + 1;
          596  +  }
          597  +}
          598  +
          599  +/*
          600  +** If a field contains any character identified by a 1 in the following
          601  +** array, then the string must be quoted for CSV.
          602  +*/
          603  +static const char needCsvQuote[] = {
          604  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          605  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          606  +  1, 0, 1, 0, 0, 0, 0, 1,   0, 0, 0, 0, 0, 0, 0, 0, 
          607  +  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 
          608  +  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 
          609  +  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 
          610  +  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 
          611  +  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 1, 
          612  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          613  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          614  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          615  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          616  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          617  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          618  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          619  +  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   
          620  +};
          621  +
          622  +/*
          623  +** Output a single term of CSV.  Actually, p->separator is used for
          624  +** the separator, which may or may not be a comma.  p->nullvalue is
          625  +** the null value.  Strings are quoted if necessary.
          626  +*/
          627  +static void output_csv(struct callback_data *p, const char *z, int bSep){
          628  +  FILE *out = p->out;
          629  +  if( z==0 ){
          630  +    fprintf(out,"%s",p->nullvalue);
          631  +  }else{
          632  +    int i;
          633  +    int nSep = strlen30(p->separator);
          634  +    for(i=0; z[i]; i++){
          635  +      if( needCsvQuote[((unsigned char*)z)[i]] 
          636  +         || (z[i]==p->separator[0] && 
          637  +             (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
          638  +        i = 0;
          639  +        break;
          640  +      }
          641  +    }
          642  +    if( i==0 ){
          643  +      putc('"', out);
          644  +      for(i=0; z[i]; i++){
          645  +        if( z[i]=='"' ) putc('"', out);
          646  +        putc(z[i], out);
          647  +      }
          648  +      putc('"', out);
          649  +    }else{
          650  +      fprintf(out, "%s", z);
          651  +    }
          652  +  }
          653  +  if( bSep ){
          654  +    fprintf(p->out, "%s", p->separator);
          655  +  }
          656  +}
          657  +
          658  +#ifdef SIGINT
          659  +/*
          660  +** This routine runs when the user presses Ctrl-C
          661  +*/
          662  +static void interrupt_handler(int NotUsed){
          663  +  UNUSED_PARAMETER(NotUsed);
          664  +  seenInterrupt = 1;
          665  +  if( db ) sqlite3_interrupt(db);
          666  +}
          667  +#endif
          668  +
          669  +/*
          670  +** This is the callback routine that the shell
          671  +** invokes for each row of a query result.
          672  +*/
          673  +static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){
          674  +  int i;
          675  +  struct callback_data *p = (struct callback_data*)pArg;
          676  +
          677  +  switch( p->mode ){
          678  +    case MODE_Line: {
          679  +      int w = 5;
          680  +      if( azArg==0 ) break;
          681  +      for(i=0; i<nArg; i++){
          682  +        int len = strlen30(azCol[i] ? azCol[i] : "");
          683  +        if( len>w ) w = len;
          684  +      }
          685  +      if( p->cnt++>0 ) fprintf(p->out,"\n");
          686  +      for(i=0; i<nArg; i++){
          687  +        fprintf(p->out,"%*s = %s\n", w, azCol[i],
          688  +                azArg[i] ? azArg[i] : p->nullvalue);
          689  +      }
          690  +      break;
          691  +    }
          692  +    case MODE_Explain:
          693  +    case MODE_Column: {
          694  +      if( p->cnt++==0 ){
          695  +        for(i=0; i<nArg; i++){
          696  +          int w, n;
          697  +          if( i<ArraySize(p->colWidth) ){
          698  +            w = p->colWidth[i];
          699  +          }else{
          700  +            w = 0;
          701  +          }
          702  +          if( w==0 ){
          703  +            w = strlen30(azCol[i] ? azCol[i] : "");
          704  +            if( w<10 ) w = 10;
          705  +            n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
          706  +            if( w<n ) w = n;
          707  +          }
          708  +          if( i<ArraySize(p->actualWidth) ){
          709  +            p->actualWidth[i] = w;
          710  +          }
          711  +          if( p->showHeader ){
          712  +            if( w<0 ){
          713  +              fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": "  ");
          714  +            }else{
          715  +              fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": "  ");
          716  +            }
          717  +          }
          718  +        }
          719  +        if( p->showHeader ){
          720  +          for(i=0; i<nArg; i++){
          721  +            int w;
          722  +            if( i<ArraySize(p->actualWidth) ){
          723  +               w = p->actualWidth[i];
          724  +               if( w<0 ) w = -w;
          725  +            }else{
          726  +               w = 10;
          727  +            }
          728  +            fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
          729  +                   "----------------------------------------------------------",
          730  +                    i==nArg-1 ? "\n": "  ");
          731  +          }
          732  +        }
          733  +      }
          734  +      if( azArg==0 ) break;
          735  +      for(i=0; i<nArg; i++){
          736  +        int w;
          737  +        if( i<ArraySize(p->actualWidth) ){
          738  +           w = p->actualWidth[i];
          739  +        }else{
          740  +           w = 10;
          741  +        }
          742  +        if( p->mode==MODE_Explain && azArg[i] && 
          743  +           strlen30(azArg[i])>w ){
          744  +          w = strlen30(azArg[i]);
          745  +        }
          746  +        if( w<0 ){
          747  +          fprintf(p->out,"%*.*s%s",-w,-w,
          748  +              azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": "  ");
          749  +        }else{
          750  +          fprintf(p->out,"%-*.*s%s",w,w,
          751  +              azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": "  ");
          752  +        }
          753  +      }
          754  +      break;
          755  +    }
          756  +    case MODE_Semi:
          757  +    case MODE_List: {
          758  +      if( p->cnt++==0 && p->showHeader ){
          759  +        for(i=0; i<nArg; i++){
          760  +          fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
          761  +        }
          762  +      }
          763  +      if( azArg==0 ) break;
          764  +      for(i=0; i<nArg; i++){
          765  +        char *z = azArg[i];
          766  +        if( z==0 ) z = p->nullvalue;
          767  +        fprintf(p->out, "%s", z);
          768  +        if( i<nArg-1 ){
          769  +          fprintf(p->out, "%s", p->separator);
          770  +        }else if( p->mode==MODE_Semi ){
          771  +          fprintf(p->out, ";\n");
          772  +        }else{
          773  +          fprintf(p->out, "\n");
          774  +        }
          775  +      }
          776  +      break;
          777  +    }
          778  +    case MODE_Html: {
          779  +      if( p->cnt++==0 && p->showHeader ){
          780  +        fprintf(p->out,"<TR>");
          781  +        for(i=0; i<nArg; i++){
          782  +          fprintf(p->out,"<TH>");
          783  +          output_html_string(p->out, azCol[i]);
          784  +          fprintf(p->out,"</TH>\n");
          785  +        }
          786  +        fprintf(p->out,"</TR>\n");
          787  +      }
          788  +      if( azArg==0 ) break;
          789  +      fprintf(p->out,"<TR>");
          790  +      for(i=0; i<nArg; i++){
          791  +        fprintf(p->out,"<TD>");
          792  +        output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
          793  +        fprintf(p->out,"</TD>\n");
          794  +      }
          795  +      fprintf(p->out,"</TR>\n");
          796  +      break;
          797  +    }
          798  +    case MODE_Tcl: {
          799  +      if( p->cnt++==0 && p->showHeader ){
          800  +        for(i=0; i<nArg; i++){
          801  +          output_c_string(p->out,azCol[i] ? azCol[i] : "");
          802  +          if(i<nArg-1) fprintf(p->out, "%s", p->separator);
          803  +        }
          804  +        fprintf(p->out,"\n");
          805  +      }
          806  +      if( azArg==0 ) break;
          807  +      for(i=0; i<nArg; i++){
          808  +        output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
          809  +        if(i<nArg-1) fprintf(p->out, "%s", p->separator);
          810  +      }
          811  +      fprintf(p->out,"\n");
          812  +      break;
          813  +    }
          814  +    case MODE_Csv: {
          815  +      if( p->cnt++==0 && p->showHeader ){
          816  +        for(i=0; i<nArg; i++){
          817  +          output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
          818  +        }
          819  +        fprintf(p->out,"\n");
          820  +      }
          821  +      if( azArg==0 ) break;
          822  +      for(i=0; i<nArg; i++){
          823  +        output_csv(p, azArg[i], i<nArg-1);
          824  +      }
          825  +      fprintf(p->out,"\n");
          826  +      break;
          827  +    }
          828  +    case MODE_Insert: {
          829  +      p->cnt++;
          830  +      if( azArg==0 ) break;
          831  +      fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
          832  +      for(i=0; i<nArg; i++){
          833  +        char *zSep = i>0 ? ",": "";
          834  +        if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
          835  +          fprintf(p->out,"%sNULL",zSep);
          836  +        }else if( aiType && aiType[i]==SQLITE_TEXT ){
          837  +          if( zSep[0] ) fprintf(p->out,"%s",zSep);
          838  +          output_quoted_string(p->out, azArg[i]);
          839  +        }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){
          840  +          fprintf(p->out,"%s%s",zSep, azArg[i]);
          841  +        }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
          842  +          const void *pBlob = sqlite3_column_blob(p->pStmt, i);
          843  +          int nBlob = sqlite3_column_bytes(p->pStmt, i);
          844  +          if( zSep[0] ) fprintf(p->out,"%s",zSep);
          845  +          output_hex_blob(p->out, pBlob, nBlob);
          846  +        }else if( isNumber(azArg[i], 0) ){
          847  +          fprintf(p->out,"%s%s",zSep, azArg[i]);
          848  +        }else{
          849  +          if( zSep[0] ) fprintf(p->out,"%s",zSep);
          850  +          output_quoted_string(p->out, azArg[i]);
          851  +        }
          852  +      }
          853  +      fprintf(p->out,");\n");
          854  +      break;
          855  +    }
          856  +  }
          857  +  return 0;
          858  +}
          859  +
          860  +/*
          861  +** This is the callback routine that the SQLite library
          862  +** invokes for each row of a query result.
          863  +*/
          864  +static int callback(void *pArg, int nArg, char **azArg, char **azCol){
          865  +  /* since we don't have type info, call the shell_callback with a NULL value */
          866  +  return shell_callback(pArg, nArg, azArg, azCol, NULL);
          867  +}
          868  +
          869  +/*
          870  +** Set the destination table field of the callback_data structure to
          871  +** the name of the table given.  Escape any quote characters in the
          872  +** table name.
          873  +*/
          874  +static void set_table_name(struct callback_data *p, const char *zName){
          875  +  int i, n;
          876  +  int needQuote;
          877  +  char *z;
          878  +
          879  +  if( p->zDestTable ){
          880  +    free(p->zDestTable);
          881  +    p->zDestTable = 0;
          882  +  }
          883  +  if( zName==0 ) return;
          884  +  needQuote = !isalpha((unsigned char)*zName) && *zName!='_';
          885  +  for(i=n=0; zName[i]; i++, n++){
          886  +    if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){
          887  +      needQuote = 1;
          888  +      if( zName[i]=='\'' ) n++;
          889  +    }
          890  +  }
          891  +  if( needQuote ) n += 2;
          892  +  z = p->zDestTable = malloc( n+1 );
          893  +  if( z==0 ){
          894  +    fprintf(stderr,"Error: out of memory\n");
          895  +    exit(1);
          896  +  }
          897  +  n = 0;
          898  +  if( needQuote ) z[n++] = '\'';
          899  +  for(i=0; zName[i]; i++){
          900  +    z[n++] = zName[i];
          901  +    if( zName[i]=='\'' ) z[n++] = '\'';
          902  +  }
          903  +  if( needQuote ) z[n++] = '\'';
          904  +  z[n] = 0;
          905  +}
          906  +
          907  +/* zIn is either a pointer to a NULL-terminated string in memory obtained
          908  +** from malloc(), or a NULL pointer. The string pointed to by zAppend is
          909  +** added to zIn, and the result returned in memory obtained from malloc().
          910  +** zIn, if it was not NULL, is freed.
          911  +**
          912  +** If the third argument, quote, is not '\0', then it is used as a 
          913  +** quote character for zAppend.
          914  +*/
          915  +static char *appendText(char *zIn, char const *zAppend, char quote){
          916  +  int len;
          917  +  int i;
          918  +  int nAppend = strlen30(zAppend);
          919  +  int nIn = (zIn?strlen30(zIn):0);
          920  +
          921  +  len = nAppend+nIn+1;
          922  +  if( quote ){
          923  +    len += 2;
          924  +    for(i=0; i<nAppend; i++){
          925  +      if( zAppend[i]==quote ) len++;
          926  +    }
          927  +  }
          928  +
          929  +  zIn = (char *)realloc(zIn, len);
          930  +  if( !zIn ){
          931  +    return 0;
          932  +  }
          933  +
          934  +  if( quote ){
          935  +    char *zCsr = &zIn[nIn];
          936  +    *zCsr++ = quote;
          937  +    for(i=0; i<nAppend; i++){
          938  +      *zCsr++ = zAppend[i];
          939  +      if( zAppend[i]==quote ) *zCsr++ = quote;
          940  +    }
          941  +    *zCsr++ = quote;
          942  +    *zCsr++ = '\0';
          943  +    assert( (zCsr-zIn)==len );
          944  +  }else{
          945  +    memcpy(&zIn[nIn], zAppend, nAppend);
          946  +    zIn[len-1] = '\0';
          947  +  }
          948  +
          949  +  return zIn;
          950  +}
          951  +
          952  +
          953  +/*
          954  +** Execute a query statement that will generate SQL output.  Print
          955  +** the result columns, comma-separated, on a line and then add a
          956  +** semicolon terminator to the end of that line.
          957  +**
          958  +** If the number of columns is 1 and that column contains text "--"
          959  +** then write the semicolon on a separate line.  That way, if a 
          960  +** "--" comment occurs at the end of the statement, the comment
          961  +** won't consume the semicolon terminator.
          962  +*/
          963  +static int run_table_dump_query(
          964  +  struct callback_data *p, /* Query context */
          965  +  const char *zSelect,     /* SELECT statement to extract content */
          966  +  const char *zFirstRow    /* Print before first row, if not NULL */
          967  +){
          968  +  sqlite3_stmt *pSelect;
          969  +  int rc;
          970  +  int nResult;
          971  +  int i;
          972  +  const char *z;
          973  +  rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
          974  +  if( rc!=SQLITE_OK || !pSelect ){
          975  +    fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
          976  +    p->nErr++;
          977  +    return rc;
          978  +  }
          979  +  rc = sqlite3_step(pSelect);
          980  +  nResult = sqlite3_column_count(pSelect);
          981  +  while( rc==SQLITE_ROW ){
          982  +    if( zFirstRow ){
          983  +      fprintf(p->out, "%s", zFirstRow);
          984  +      zFirstRow = 0;
          985  +    }
          986  +    z = (const char*)sqlite3_column_text(pSelect, 0);
          987  +    fprintf(p->out, "%s", z);
          988  +    for(i=1; i<nResult; i++){ 
          989  +      fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i));
          990  +    }
          991  +    if( z==0 ) z = "";
          992  +    while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
          993  +    if( z[0] ){
          994  +      fprintf(p->out, "\n;\n");
          995  +    }else{
          996  +      fprintf(p->out, ";\n");
          997  +    }    
          998  +    rc = sqlite3_step(pSelect);
          999  +  }
         1000  +  rc = sqlite3_finalize(pSelect);
         1001  +  if( rc!=SQLITE_OK ){
         1002  +    fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
         1003  +    p->nErr++;
         1004  +  }
         1005  +  return rc;
         1006  +}
         1007  +
         1008  +/*
         1009  +** Allocate space and save off current error string.
         1010  +*/
         1011  +static char *save_err_msg(
         1012  +  sqlite3 *db            /* Database to query */
         1013  +){
         1014  +  int nErrMsg = 1+strlen30(sqlite3_errmsg(db));
         1015  +  char *zErrMsg = sqlite3_malloc(nErrMsg);
         1016  +  if( zErrMsg ){
         1017  +    memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
         1018  +  }
         1019  +  return zErrMsg;
         1020  +}
         1021  +
         1022  +/*
         1023  +** Display memory stats.
         1024  +*/
         1025  +static int display_stats(
         1026  +  sqlite3 *db,                /* Database to query */
         1027  +  struct callback_data *pArg, /* Pointer to struct callback_data */
         1028  +  int bReset                  /* True to reset the stats */
         1029  +){
         1030  +  int iCur;
         1031  +  int iHiwtr;
         1032  +
         1033  +  if( pArg && pArg->out ){
         1034  +    
         1035  +    iHiwtr = iCur = -1;
         1036  +    sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
         1037  +    fprintf(pArg->out, "Memory Used:                         %d (max %d) bytes\n", iCur, iHiwtr);
         1038  +    iHiwtr = iCur = -1;
         1039  +    sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
         1040  +    fprintf(pArg->out, "Number of Outstanding Allocations:   %d (max %d)\n", iCur, iHiwtr);
         1041  +/*
         1042  +** Not currently used by the CLI.
         1043  +**    iHiwtr = iCur = -1;
         1044  +**    sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
         1045  +**    fprintf(pArg->out, "Number of Pcache Pages Used:         %d (max %d) pages\n", iCur, iHiwtr);
         1046  +*/
         1047  +    iHiwtr = iCur = -1;
         1048  +    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
         1049  +    fprintf(pArg->out, "Number of Pcache Overflow Bytes:     %d (max %d) bytes\n", iCur, iHiwtr);
         1050  +/*
         1051  +** Not currently used by the CLI.
         1052  +**    iHiwtr = iCur = -1;
         1053  +**    sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset);
         1054  +**    fprintf(pArg->out, "Number of Scratch Allocations Used:  %d (max %d)\n", iCur, iHiwtr);
         1055  +*/
         1056  +    iHiwtr = iCur = -1;
         1057  +    sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset);
         1058  +    fprintf(pArg->out, "Number of Scratch Overflow Bytes:    %d (max %d) bytes\n", iCur, iHiwtr);
         1059  +    iHiwtr = iCur = -1;
         1060  +    sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
         1061  +    fprintf(pArg->out, "Largest Allocation:                  %d bytes\n", iHiwtr);
         1062  +    iHiwtr = iCur = -1;
         1063  +    sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
         1064  +    fprintf(pArg->out, "Largest Pcache Allocation:           %d bytes\n", iHiwtr);
         1065  +    iHiwtr = iCur = -1;
         1066  +    sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset);
         1067  +    fprintf(pArg->out, "Largest Scratch Allocation:          %d bytes\n", iHiwtr);
         1068  +#ifdef YYTRACKMAXSTACKDEPTH
         1069  +    iHiwtr = iCur = -1;
         1070  +    sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset);
         1071  +    fprintf(pArg->out, "Deepest Parser Stack:                %d (max %d)\n", iCur, iHiwtr);
         1072  +#endif
         1073  +  }
         1074  +
         1075  +  if( pArg && pArg->out && db ){
         1076  +    iHiwtr = iCur = -1;
         1077  +    sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset);
         1078  +    fprintf(pArg->out, "Lookaside Slots Used:                %d (max %d)\n", iCur, iHiwtr);
         1079  +    sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset);
         1080  +    fprintf(pArg->out, "Successful lookaside attempts:       %d\n", iHiwtr);
         1081  +    sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset);
         1082  +    fprintf(pArg->out, "Lookaside failures due to size:      %d\n", iHiwtr);
         1083  +    sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset);
         1084  +    fprintf(pArg->out, "Lookaside failures due to OOM:       %d\n", iHiwtr);
         1085  +    iHiwtr = iCur = -1;
         1086  +    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
         1087  +    fprintf(pArg->out, "Pager Heap Usage:                    %d bytes\n", iCur);    iHiwtr = iCur = -1;
         1088  +    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
         1089  +    fprintf(pArg->out, "Page cache hits:                     %d\n", iCur);
         1090  +    iHiwtr = iCur = -1;
         1091  +    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
         1092  +    fprintf(pArg->out, "Page cache misses:                   %d\n", iCur); 
         1093  +    iHiwtr = iCur = -1;
         1094  +    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
         1095  +    fprintf(pArg->out, "Page cache writes:                   %d\n", iCur); 
         1096  +    iHiwtr = iCur = -1;
         1097  +    sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
         1098  +    fprintf(pArg->out, "Schema Heap Usage:                   %d bytes\n", iCur); 
         1099  +    iHiwtr = iCur = -1;
         1100  +    sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
         1101  +    fprintf(pArg->out, "Statement Heap/Lookaside Usage:      %d bytes\n", iCur); 
         1102  +  }
         1103  +
         1104  +  if( pArg && pArg->out && db && pArg->pStmt ){
         1105  +    iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset);
         1106  +    fprintf(pArg->out, "Fullscan Steps:                      %d\n", iCur);
         1107  +    iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
         1108  +    fprintf(pArg->out, "Sort Operations:                     %d\n", iCur);
         1109  +    iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset);
         1110  +    fprintf(pArg->out, "Autoindex Inserts:                   %d\n", iCur);
         1111  +  }
         1112  +
         1113  +  return 0;
         1114  +}
         1115  +
         1116  +/*
         1117  +** Execute a statement or set of statements.  Print 
         1118  +** any result rows/columns depending on the current mode 
         1119  +** set via the supplied callback.
         1120  +**
         1121  +** This is very similar to SQLite's built-in sqlite3_exec() 
         1122  +** function except it takes a slightly different callback 
         1123  +** and callback data argument.
         1124  +*/
         1125  +static int shell_exec(
         1126  +  sqlite3 *db,                                /* An open database */
         1127  +  const char *zSql,                           /* SQL to be evaluated */
         1128  +  int (*xCallback)(void*,int,char**,char**,int*),   /* Callback function */
         1129  +                                              /* (not the same as sqlite3_exec) */
         1130  +  struct callback_data *pArg,                 /* Pointer to struct callback_data */
         1131  +  char **pzErrMsg                             /* Error msg written here */
         1132  +){
         1133  +  sqlite3_stmt *pStmt = NULL;     /* Statement to execute. */
         1134  +  int rc = SQLITE_OK;             /* Return Code */
         1135  +  int rc2;
         1136  +  const char *zLeftover;          /* Tail of unprocessed SQL */
         1137  +
         1138  +  if( pzErrMsg ){
         1139  +    *pzErrMsg = NULL;
         1140  +  }
         1141  +
         1142  +  while( zSql[0] && (SQLITE_OK == rc) ){
         1143  +    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
         1144  +    if( SQLITE_OK != rc ){
         1145  +      if( pzErrMsg ){
         1146  +        *pzErrMsg = save_err_msg(db);
         1147  +      }
         1148  +    }else{
         1149  +      if( !pStmt ){
         1150  +        /* this happens for a comment or white-space */
         1151  +        zSql = zLeftover;
         1152  +        while( IsSpace(zSql[0]) ) zSql++;
         1153  +        continue;
         1154  +      }
         1155  +
         1156  +      /* save off the prepared statment handle and reset row count */
         1157  +      if( pArg ){
         1158  +        pArg->pStmt = pStmt;
         1159  +        pArg->cnt = 0;
         1160  +      }
         1161  +
         1162  +      /* echo the sql statement if echo on */
         1163  +      if( pArg && pArg->echoOn ){
         1164  +        const char *zStmtSql = sqlite3_sql(pStmt);
         1165  +        fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
         1166  +      }
         1167  +
         1168  +      /* Output TESTCTRL_EXPLAIN text of requested */
         1169  +      if( pArg && pArg->mode==MODE_Explain ){
         1170  +        const char *zExplain = 0;
         1171  +        sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
         1172  +        if( zExplain && zExplain[0] ){
         1173  +          fprintf(pArg->out, "%s", zExplain);
         1174  +        }
         1175  +      }
         1176  +
         1177  +      /* perform the first step.  this will tell us if we
         1178  +      ** have a result set or not and how wide it is.
         1179  +      */
         1180  +      rc = sqlite3_step(pStmt);
         1181  +      /* if we have a result set... */
         1182  +      if( SQLITE_ROW == rc ){
         1183  +        /* if we have a callback... */
         1184  +        if( xCallback ){
         1185  +          /* allocate space for col name ptr, value ptr, and type */
         1186  +          int nCol = sqlite3_column_count(pStmt);
         1187  +          void *pData = sqlite3_malloc(3*nCol*sizeof(const char*) + 1);
         1188  +          if( !pData ){
         1189  +            rc = SQLITE_NOMEM;
         1190  +          }else{
         1191  +            char **azCols = (char **)pData;      /* Names of result columns */
         1192  +            char **azVals = &azCols[nCol];       /* Results */
         1193  +            int *aiTypes = (int *)&azVals[nCol]; /* Result types */
         1194  +            int i;
         1195  +            assert(sizeof(int) <= sizeof(char *)); 
         1196  +            /* save off ptrs to column names */
         1197  +            for(i=0; i<nCol; i++){
         1198  +              azCols[i] = (char *)sqlite3_column_name(pStmt, i);
         1199  +            }
         1200  +            do{
         1201  +              /* extract the data and data types */
         1202  +              for(i=0; i<nCol; i++){
         1203  +                azVals[i] = (char *)sqlite3_column_text(pStmt, i);
         1204  +                aiTypes[i] = sqlite3_column_type(pStmt, i);
         1205  +                if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
         1206  +                  rc = SQLITE_NOMEM;
         1207  +                  break; /* from for */
         1208  +                }
         1209  +              } /* end for */
         1210  +
         1211  +              /* if data and types extracted successfully... */
         1212  +              if( SQLITE_ROW == rc ){ 
         1213  +                /* call the supplied callback with the result row data */
         1214  +                if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){
         1215  +                  rc = SQLITE_ABORT;
         1216  +                }else{
         1217  +                  rc = sqlite3_step(pStmt);
         1218  +                }
         1219  +              }
         1220  +            } while( SQLITE_ROW == rc );
         1221  +            sqlite3_free(pData);
         1222  +          }
         1223  +        }else{
         1224  +          do{
         1225  +            rc = sqlite3_step(pStmt);
         1226  +          } while( rc == SQLITE_ROW );
         1227  +        }
         1228  +      }
         1229  +
         1230  +      /* print usage stats if stats on */
         1231  +      if( pArg && pArg->statsOn ){
         1232  +        display_stats(db, pArg, 0);
         1233  +      }
         1234  +
         1235  +      /* Finalize the statement just executed. If this fails, save a 
         1236  +      ** copy of the error message. Otherwise, set zSql to point to the
         1237  +      ** next statement to execute. */
         1238  +      rc2 = sqlite3_finalize(pStmt);
         1239  +      if( rc!=SQLITE_NOMEM ) rc = rc2;
         1240  +      if( rc==SQLITE_OK ){
         1241  +        zSql = zLeftover;
         1242  +        while( IsSpace(zSql[0]) ) zSql++;
         1243  +      }else if( pzErrMsg ){
         1244  +        *pzErrMsg = save_err_msg(db);
         1245  +      }
         1246  +
         1247  +      /* clear saved stmt handle */
         1248  +      if( pArg ){
         1249  +        pArg->pStmt = NULL;
         1250  +      }
         1251  +    }
         1252  +  } /* end while */
         1253  +
         1254  +  return rc;
         1255  +}
         1256  +
         1257  +
         1258  +/*
         1259  +** This is a different callback routine used for dumping the database.
         1260  +** Each row received by this callback consists of a table name,
         1261  +** the table type ("index" or "table") and SQL to create the table.
         1262  +** This routine should print text sufficient to recreate the table.
         1263  +*/
         1264  +static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
         1265  +  int rc;
         1266  +  const char *zTable;
         1267  +  const char *zType;
         1268  +  const char *zSql;
         1269  +  const char *zPrepStmt = 0;
         1270  +  struct callback_data *p = (struct callback_data *)pArg;
         1271  +
         1272  +  UNUSED_PARAMETER(azCol);
         1273  +  if( nArg!=3 ) return 1;
         1274  +  zTable = azArg[0];
         1275  +  zType = azArg[1];
         1276  +  zSql = azArg[2];
         1277  +  
         1278  +  if( strcmp(zTable, "sqlite_sequence")==0 ){
         1279  +    zPrepStmt = "DELETE FROM sqlite_sequence;\n";
         1280  +  }else if( strcmp(zTable, "sqlite_stat1")==0 ){
         1281  +    fprintf(p->out, "ANALYZE sqlite_master;\n");
         1282  +  }else if( strncmp(zTable, "sqlite_", 7)==0 ){
         1283  +    return 0;
         1284  +  }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
         1285  +    char *zIns;
         1286  +    if( !p->writableSchema ){
         1287  +      fprintf(p->out, "PRAGMA writable_schema=ON;\n");
         1288  +      p->writableSchema = 1;
         1289  +    }
         1290  +    zIns = sqlite3_mprintf(
         1291  +       "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
         1292  +       "VALUES('table','%q','%q',0,'%q');",
         1293  +       zTable, zTable, zSql);
         1294  +    fprintf(p->out, "%s\n", zIns);
         1295  +    sqlite3_free(zIns);
         1296  +    return 0;
         1297  +  }else{
         1298  +    fprintf(p->out, "%s;\n", zSql);
         1299  +  }
         1300  +
         1301  +  if( strcmp(zType, "table")==0 ){
         1302  +    sqlite3_stmt *pTableInfo = 0;
         1303  +    char *zSelect = 0;
         1304  +    char *zTableInfo = 0;
         1305  +    char *zTmp = 0;
         1306  +    int nRow = 0;
         1307  +   
         1308  +    zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
         1309  +    zTableInfo = appendText(zTableInfo, zTable, '"');
         1310  +    zTableInfo = appendText(zTableInfo, ");", 0);
         1311  +
         1312  +    rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
         1313  +    free(zTableInfo);
         1314  +    if( rc!=SQLITE_OK || !pTableInfo ){
         1315  +      return 1;
         1316  +    }
         1317  +
         1318  +    zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
         1319  +    /* Always quote the table name, even if it appears to be pure ascii,
         1320  +    ** in case it is a keyword. Ex:  INSERT INTO "table" ... */
         1321  +    zTmp = appendText(zTmp, zTable, '"');
         1322  +    if( zTmp ){
         1323  +      zSelect = appendText(zSelect, zTmp, '\'');
         1324  +      free(zTmp);
         1325  +    }
         1326  +    zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
         1327  +    rc = sqlite3_step(pTableInfo);
         1328  +    while( rc==SQLITE_ROW ){
         1329  +      const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1);
         1330  +      zSelect = appendText(zSelect, "quote(", 0);
         1331  +      zSelect = appendText(zSelect, zText, '"');
         1332  +      rc = sqlite3_step(pTableInfo);
         1333  +      if( rc==SQLITE_ROW ){
         1334  +        zSelect = appendText(zSelect, "), ", 0);
         1335  +      }else{
         1336  +        zSelect = appendText(zSelect, ") ", 0);
         1337  +      }
         1338  +      nRow++;
         1339  +    }
         1340  +    rc = sqlite3_finalize(pTableInfo);
         1341  +    if( rc!=SQLITE_OK || nRow==0 ){
         1342  +      free(zSelect);
         1343  +      return 1;
         1344  +    }
         1345  +    zSelect = appendText(zSelect, "|| ')' FROM  ", 0);
         1346  +    zSelect = appendText(zSelect, zTable, '"');
         1347  +
         1348  +    rc = run_table_dump_query(p, zSelect, zPrepStmt);
         1349  +    if( rc==SQLITE_CORRUPT ){
         1350  +      zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
         1351  +      run_table_dump_query(p, zSelect, 0);
         1352  +    }
         1353  +    free(zSelect);
         1354  +  }
         1355  +  return 0;
         1356  +}
         1357  +
         1358  +/*
         1359  +** Run zQuery.  Use dump_callback() as the callback routine so that
         1360  +** the contents of the query are output as SQL statements.
         1361  +**
         1362  +** If we get a SQLITE_CORRUPT error, rerun the query after appending
         1363  +** "ORDER BY rowid DESC" to the end.
         1364  +*/
         1365  +static int run_schema_dump_query(
         1366  +  struct callback_data *p, 
         1367  +  const char *zQuery
         1368  +){
         1369  +  int rc;
         1370  +  char *zErr = 0;
         1371  +  rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr);
         1372  +  if( rc==SQLITE_CORRUPT ){
         1373  +    char *zQ2;
         1374  +    int len = strlen30(zQuery);
         1375  +    fprintf(p->out, "/****** CORRUPTION ERROR *******/\n");
         1376  +    if( zErr ){
         1377  +      fprintf(p->out, "/****** %s ******/\n", zErr);
         1378  +      sqlite3_free(zErr);
         1379  +      zErr = 0;
         1380  +    }
         1381  +    zQ2 = malloc( len+100 );
         1382  +    if( zQ2==0 ) return rc;
         1383  +    sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
         1384  +    rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
         1385  +    if( rc ){
         1386  +      fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
         1387  +    }else{
         1388  +      rc = SQLITE_CORRUPT;
         1389  +    }
         1390  +    sqlite3_free(zErr);
         1391  +    free(zQ2);
         1392  +  }
         1393  +  return rc;
         1394  +}
         1395  +
         1396  +/*
         1397  +** Text of a help message
         1398  +*/
         1399  +static char zHelp[] =
         1400  +  ".backup ?DB? FILE      Backup DB (default \"main\") to FILE\n"
         1401  +  ".bail ON|OFF           Stop after hitting an error.  Default OFF\n"
         1402  +  ".databases             List names and files of attached databases\n"
         1403  +  ".dump ?TABLE? ...      Dump the database in an SQL text format\n"
         1404  +  "                         If TABLE specified, only dump tables matching\n"
         1405  +  "                         LIKE pattern TABLE.\n"
         1406  +  ".echo ON|OFF           Turn command echo on or off\n"
         1407  +  ".exit                  Exit this program\n"
         1408  +  ".explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.\n"
         1409  +  "                         With no args, it turns EXPLAIN on.\n"
         1410  +  ".header(s) ON|OFF      Turn display of headers on or off\n"
         1411  +  ".help                  Show this message\n"
         1412  +  ".import FILE TABLE     Import data from FILE into TABLE\n"
         1413  +  ".indices ?TABLE?       Show names of all indices\n"
         1414  +  "                         If TABLE specified, only show indices for tables\n"
         1415  +  "                         matching LIKE pattern TABLE.\n"
         1416  +#ifdef SQLITE_ENABLE_IOTRACE
         1417  +  ".iotrace FILE          Enable I/O diagnostic logging to FILE\n"
         1418  +#endif
         1419  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
         1420  +  ".load FILE ?ENTRY?     Load an extension library\n"
         1421  +#endif
         1422  +  ".log FILE|off          Turn logging on or off.  FILE can be stderr/stdout\n"
         1423  +  ".mode MODE ?TABLE?     Set output mode where MODE is one of:\n"
         1424  +  "                         csv      Comma-separated values\n"
         1425  +  "                         column   Left-aligned columns.  (See .width)\n"
         1426  +  "                         html     HTML <table> code\n"
         1427  +  "                         insert   SQL insert statements for TABLE\n"
         1428  +  "                         line     One value per line\n"
         1429  +  "                         list     Values delimited by .separator string\n"
         1430  +  "                         tabs     Tab-separated values\n"
         1431  +  "                         tcl      TCL list elements\n"
         1432  +  ".nullvalue STRING      Use STRING in place of NULL values\n"
         1433  +  ".output FILENAME       Send output to FILENAME\n"
         1434  +  ".output stdout         Send output to the screen\n"
         1435  +  ".print STRING...       Print literal STRING\n"
         1436  +  ".prompt MAIN CONTINUE  Replace the standard prompts\n"
         1437  +  ".quit                  Exit this program\n"
         1438  +  ".read FILENAME         Execute SQL in FILENAME\n"
         1439  +  ".restore ?DB? FILE     Restore content of DB (default \"main\") from FILE\n"
         1440  +  ".schema ?TABLE?        Show the CREATE statements\n"
         1441  +  "                         If TABLE specified, only show tables matching\n"
         1442  +  "                         LIKE pattern TABLE.\n"
         1443  +  ".separator STRING      Change separator used by output mode and .import\n"
         1444  +  ".show                  Show the current values for various settings\n"
         1445  +  ".stats ON|OFF          Turn stats on or off\n"
         1446  +  ".tables ?TABLE?        List names of tables\n"
         1447  +  "                         If TABLE specified, only list tables matching\n"
         1448  +  "                         LIKE pattern TABLE.\n"
         1449  +  ".timeout MS            Try opening locked tables for MS milliseconds\n"
         1450  +  ".trace FILE|off        Output each SQL statement as it is run\n"
         1451  +  ".vfsname ?AUX?         Print the name of the VFS stack\n"
         1452  +  ".width NUM1 NUM2 ...   Set column widths for \"column\" mode\n"
         1453  +;
         1454  +
         1455  +static char zTimerHelp[] =
         1456  +  ".timer ON|OFF          Turn the CPU timer measurement on or off\n"
         1457  +;
         1458  +
         1459  +/* Forward reference */
         1460  +static int process_input(struct callback_data *p, FILE *in);
         1461  +
         1462  +/*
         1463  +** Make sure the database is open.  If it is not, then open it.  If
         1464  +** the database fails to open, print an error message and exit.
         1465  +*/
         1466  +static void open_db(struct callback_data *p){
         1467  +  if( p->db==0 ){
         1468  +    sqlite3_initialize();
         1469  +    sqlite3_open(p->zDbFilename, &p->db);
         1470  +    db = p->db;
         1471  +    if( db && sqlite3_errcode(db)==SQLITE_OK ){
         1472  +      sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
         1473  +          shellstaticFunc, 0, 0);
         1474  +    }
         1475  +    if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){
         1476  +      fprintf(stderr,"Error: unable to open database \"%s\": %s\n", 
         1477  +          p->zDbFilename, sqlite3_errmsg(db));
         1478  +      exit(1);
         1479  +    }
         1480  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
         1481  +    sqlite3_enable_load_extension(p->db, 1);
         1482  +#endif
         1483  +#ifdef SQLITE_ENABLE_REGEXP
         1484  +    {
         1485  +      extern int sqlite3_add_regexp_func(sqlite3*);
         1486  +      sqlite3_add_regexp_func(db);
         1487  +    }
         1488  +#endif
         1489  +#ifdef SQLITE_ENABLE_SPELLFIX
         1490  +    {
         1491  +      extern int sqlite3_spellfix1_register(sqlite3*);
         1492  +      sqlite3_spellfix1_register(db);
         1493  +    }
         1494  +#endif
         1495  +  }
         1496  +}
         1497  +
         1498  +/*
         1499  +** Do C-language style dequoting.
         1500  +**
         1501  +**    \t    -> tab
         1502  +**    \n    -> newline
         1503  +**    \r    -> carriage return
         1504  +**    \NNN  -> ascii character NNN in octal
         1505  +**    \\    -> backslash
         1506  +*/
         1507  +static void resolve_backslashes(char *z){
         1508  +  int i, j;
         1509  +  char c;
         1510  +  for(i=j=0; (c = z[i])!=0; i++, j++){
         1511  +    if( c=='\\' ){
         1512  +      c = z[++i];
         1513  +      if( c=='n' ){
         1514  +        c = '\n';
         1515  +      }else if( c=='t' ){
         1516  +        c = '\t';
         1517  +      }else if( c=='r' ){
         1518  +        c = '\r';
         1519  +      }else if( c>='0' && c<='7' ){
         1520  +        c -= '0';
         1521  +        if( z[i+1]>='0' && z[i+1]<='7' ){
         1522  +          i++;
         1523  +          c = (c<<3) + z[i] - '0';
         1524  +          if( z[i+1]>='0' && z[i+1]<='7' ){
         1525  +            i++;
         1526  +            c = (c<<3) + z[i] - '0';
         1527  +          }
         1528  +        }
         1529  +      }
         1530  +    }
         1531  +    z[j] = c;
         1532  +  }
         1533  +  z[j] = 0;
         1534  +}
         1535  +
         1536  +/*
         1537  +** Interpret zArg as a boolean value.  Return either 0 or 1.
         1538  +*/
         1539  +static int booleanValue(char *zArg){
         1540  +  int i;
         1541  +  for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
         1542  +  if( i>0 && zArg[i]==0 ) return atoi(zArg);
         1543  +  if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
         1544  +    return 1;
         1545  +  }
         1546  +  if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
         1547  +    return 0;
         1548  +  }
         1549  +  fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
         1550  +          zArg);
         1551  +  return 0;
         1552  +}
         1553  +
         1554  +/*
         1555  +** Close an output file, assuming it is not stderr or stdout
         1556  +*/
         1557  +static void output_file_close(FILE *f){
         1558  +  if( f && f!=stdout && f!=stderr ) fclose(f);
         1559  +}
         1560  +
         1561  +/*
         1562  +** Try to open an output file.   The names "stdout" and "stderr" are
         1563  +** recognized and do the right thing.  NULL is returned if the output 
         1564  +** filename is "off".
         1565  +*/
         1566  +static FILE *output_file_open(const char *zFile){
         1567  +  FILE *f;
         1568  +  if( strcmp(zFile,"stdout")==0 ){
         1569  +    f = stdout;
         1570  +  }else if( strcmp(zFile, "stderr")==0 ){
         1571  +    f = stderr;
         1572  +  }else if( strcmp(zFile, "off")==0 ){
         1573  +    f = 0;
         1574  +  }else{
         1575  +    f = fopen(zFile, "wb");
         1576  +    if( f==0 ){
         1577  +      fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
         1578  +    }
         1579  +  }
         1580  +  return f;
         1581  +}
         1582  +
         1583  +/*
         1584  +** A routine for handling output from sqlite3_trace().
         1585  +*/
         1586  +static void sql_trace_callback(void *pArg, const char *z){
         1587  +  FILE *f = (FILE*)pArg;
         1588  +  if( f ) fprintf(f, "%s\n", z);
         1589  +}
         1590  +
         1591  +/*
         1592  +** A no-op routine that runs with the ".breakpoint" doc-command.  This is
         1593  +** a useful spot to set a debugger breakpoint.
         1594  +*/
         1595  +static void test_breakpoint(void){
         1596  +  static int nCall = 0;
         1597  +  nCall++;
         1598  +}
         1599  +
         1600  +/*
         1601  +** If an input line begins with "." then invoke this routine to
         1602  +** process that line.
         1603  +**
         1604  +** Return 1 on error, 2 to exit, and 0 otherwise.
         1605  +*/
         1606  +static int do_meta_command(char *zLine, struct callback_data *p){
         1607  +  int i = 1;
         1608  +  int nArg = 0;
         1609  +  int n, c;
         1610  +  int rc = 0;
         1611  +  char *azArg[50];
         1612  +
         1613  +  /* Parse the input line into tokens.
         1614  +  */
         1615  +  while( zLine[i] && nArg<ArraySize(azArg) ){
         1616  +    while( IsSpace(zLine[i]) ){ i++; }
         1617  +    if( zLine[i]==0 ) break;
         1618  +    if( zLine[i]=='\'' || zLine[i]=='"' ){
         1619  +      int delim = zLine[i++];
         1620  +      azArg[nArg++] = &zLine[i];
         1621  +      while( zLine[i] && zLine[i]!=delim ){ i++; }
         1622  +      if( zLine[i]==delim ){
         1623  +        zLine[i++] = 0;
         1624  +      }
         1625  +      if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
         1626  +    }else{
         1627  +      azArg[nArg++] = &zLine[i];
         1628  +      while( zLine[i] && !IsSpace(zLine[i]) ){ i++; }
         1629  +      if( zLine[i] ) zLine[i++] = 0;
         1630  +      resolve_backslashes(azArg[nArg-1]);
         1631  +    }
         1632  +  }
         1633  +
         1634  +  /* Process the input line.
         1635  +  */
         1636  +  if( nArg==0 ) return 0; /* no tokens, no error */
         1637  +  n = strlen30(azArg[0]);
         1638  +  c = azArg[0][0];
         1639  +  if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){
         1640  +    const char *zDestFile = 0;
         1641  +    const char *zDb = 0;
         1642  +    const char *zKey = 0;
         1643  +    sqlite3 *pDest;
         1644  +    sqlite3_backup *pBackup;
         1645  +    int j;
         1646  +    for(j=1; j<nArg; j++){
         1647  +      const char *z = azArg[j];
         1648  +      if( z[0]=='-' ){
         1649  +        while( z[0]=='-' ) z++;
         1650  +        if( strcmp(z,"key")==0 && j<nArg-1 ){
         1651  +          zKey = azArg[++j];
         1652  +        }else
         1653  +        {
         1654  +          fprintf(stderr, "unknown option: %s\n", azArg[j]);
         1655  +          return 1;
         1656  +        }
         1657  +      }else if( zDestFile==0 ){
         1658  +        zDestFile = azArg[j];
         1659  +      }else if( zDb==0 ){
         1660  +        zDb = zDestFile;
         1661  +        zDestFile = azArg[j];
         1662  +      }else{
         1663  +        fprintf(stderr, "too many arguments to .backup\n");
         1664  +        return 1;
         1665  +      }
         1666  +    }
         1667  +    if( zDestFile==0 ){
         1668  +      fprintf(stderr, "missing FILENAME argument on .backup\n");
         1669  +      return 1;
         1670  +    }
         1671  +    if( zDb==0 ) zDb = "main";
         1672  +    rc = sqlite3_open(zDestFile, &pDest);
         1673  +    if( rc!=SQLITE_OK ){
         1674  +      fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
         1675  +      sqlite3_close(pDest);
         1676  +      return 1;
         1677  +    }
         1678  +#ifdef SQLITE_HAS_CODEC
         1679  +    sqlite3_key(pDest, zKey, (int)strlen(zKey));
         1680  +#else
         1681  +    (void)zKey;
         1682  +#endif
         1683  +    open_db(p);
         1684  +    pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
         1685  +    if( pBackup==0 ){
         1686  +      fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
         1687  +      sqlite3_close(pDest);
         1688  +      return 1;
         1689  +    }
         1690  +    while(  (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
         1691  +    sqlite3_backup_finish(pBackup);
         1692  +    if( rc==SQLITE_DONE ){
         1693  +      rc = 0;
         1694  +    }else{
         1695  +      fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
         1696  +      rc = 1;
         1697  +    }
         1698  +    sqlite3_close(pDest);
         1699  +  }else
         1700  +
         1701  +  if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){
         1702  +    bail_on_error = booleanValue(azArg[1]);
         1703  +  }else
         1704  +
         1705  +  /* The undocumented ".breakpoint" command causes a call to the no-op
         1706  +  ** routine named test_breakpoint().
         1707  +  */
         1708  +  if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
         1709  +    test_breakpoint();
         1710  +  }else
         1711  +
         1712  +  if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
         1713  +    struct callback_data data;
         1714  +    char *zErrMsg = 0;
         1715  +    open_db(p);
         1716  +    memcpy(&data, p, sizeof(data));
         1717  +    data.showHeader = 1;
         1718  +    data.mode = MODE_Column;
         1719  +    data.colWidth[0] = 3;
         1720  +    data.colWidth[1] = 15;
         1721  +    data.colWidth[2] = 58;
         1722  +    data.cnt = 0;
         1723  +    sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
         1724  +    if( zErrMsg ){
         1725  +      fprintf(stderr,"Error: %s\n", zErrMsg);
         1726  +      sqlite3_free(zErrMsg);
         1727  +      rc = 1;
         1728  +    }
         1729  +  }else
         1730  +
         1731  +  if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
         1732  +    open_db(p);
         1733  +    /* When playing back a "dump", the content might appear in an order
         1734  +    ** which causes immediate foreign key constraints to be violated.
         1735  +    ** So disable foreign-key constraint enforcement to prevent problems. */
         1736  +    fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
         1737  +    fprintf(p->out, "BEGIN TRANSACTION;\n");
         1738  +    p->writableSchema = 0;
         1739  +    sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
         1740  +    p->nErr = 0;
         1741  +    if( nArg==1 ){
         1742  +      run_schema_dump_query(p, 
         1743  +        "SELECT name, type, sql FROM sqlite_master "
         1744  +        "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
         1745  +      );
         1746  +      run_schema_dump_query(p, 
         1747  +        "SELECT name, type, sql FROM sqlite_master "
         1748  +        "WHERE name=='sqlite_sequence'"
         1749  +      );
         1750  +      run_table_dump_query(p,
         1751  +        "SELECT sql FROM sqlite_master "
         1752  +        "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
         1753  +      );
         1754  +    }else{
         1755  +      int i;
         1756  +      for(i=1; i<nArg; i++){
         1757  +        zShellStatic = azArg[i];
         1758  +        run_schema_dump_query(p,
         1759  +          "SELECT name, type, sql FROM sqlite_master "
         1760  +          "WHERE tbl_name LIKE shellstatic() AND type=='table'"
         1761  +          "  AND sql NOT NULL");
         1762  +        run_table_dump_query(p,
         1763  +          "SELECT sql FROM sqlite_master "
         1764  +          "WHERE sql NOT NULL"
         1765  +          "  AND type IN ('index','trigger','view')"
         1766  +          "  AND tbl_name LIKE shellstatic()", 0
         1767  +        );
         1768  +        zShellStatic = 0;
         1769  +      }
         1770  +    }
         1771  +    if( p->writableSchema ){
         1772  +      fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
         1773  +      p->writableSchema = 0;
         1774  +    }
         1775  +    sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
         1776  +    sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
         1777  +    fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
         1778  +  }else
         1779  +
         1780  +  if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
         1781  +    p->echoOn = booleanValue(azArg[1]);
         1782  +  }else
         1783  +
         1784  +  if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
         1785  +    if( nArg>1 && (rc = atoi(azArg[1]))!=0 ) exit(rc);
         1786  +    rc = 2;
         1787  +  }else
         1788  +
         1789  +  if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){
         1790  +    int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
         1791  +    if(val == 1) {
         1792  +      if(!p->explainPrev.valid) {
         1793  +        p->explainPrev.valid = 1;
         1794  +        p->explainPrev.mode = p->mode;
         1795  +        p->explainPrev.showHeader = p->showHeader;
         1796  +        memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
         1797  +      }
         1798  +      /* We could put this code under the !p->explainValid
         1799  +      ** condition so that it does not execute if we are already in
         1800  +      ** explain mode. However, always executing it allows us an easy
         1801  +      ** was to reset to explain mode in case the user previously
         1802  +      ** did an .explain followed by a .width, .mode or .header
         1803  +      ** command.
         1804  +      */
         1805  +      p->mode = MODE_Explain;
         1806  +      p->showHeader = 1;
         1807  +      memset(p->colWidth,0,ArraySize(p->colWidth));
         1808  +      p->colWidth[0] = 4;                  /* addr */
         1809  +      p->colWidth[1] = 13;                 /* opcode */
         1810  +      p->colWidth[2] = 4;                  /* P1 */
         1811  +      p->colWidth[3] = 4;                  /* P2 */
         1812  +      p->colWidth[4] = 4;                  /* P3 */
         1813  +      p->colWidth[5] = 13;                 /* P4 */
         1814  +      p->colWidth[6] = 2;                  /* P5 */
         1815  +      p->colWidth[7] = 13;                  /* Comment */
         1816  +    }else if (p->explainPrev.valid) {
         1817  +      p->explainPrev.valid = 0;
         1818  +      p->mode = p->explainPrev.mode;
         1819  +      p->showHeader = p->explainPrev.showHeader;
         1820  +      memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
         1821  +    }
         1822  +  }else
         1823  +
         1824  +  if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
         1825  +                 strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){
         1826  +    p->showHeader = booleanValue(azArg[1]);
         1827  +  }else
         1828  +
         1829  +  if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
         1830  +    fprintf(stderr,"%s",zHelp);
         1831  +    if( HAS_TIMER ){
         1832  +      fprintf(stderr,"%s",zTimerHelp);
         1833  +    }
         1834  +  }else
         1835  +
         1836  +  if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
         1837  +    char *zTable = azArg[2];    /* Insert data into this table */
         1838  +    char *zFile = azArg[1];     /* The file from which to extract data */
         1839  +    sqlite3_stmt *pStmt = NULL; /* A statement */
         1840  +    int nCol;                   /* Number of columns in the table */
         1841  +    int nByte;                  /* Number of bytes in an SQL string */
         1842  +    int i, j;                   /* Loop counters */
         1843  +    int nSep;                   /* Number of bytes in p->separator[] */
         1844  +    char *zSql;                 /* An SQL statement */
         1845  +    char *zLine;                /* A single line of input from the file */
         1846  +    char **azCol;               /* zLine[] broken up into columns */
         1847  +    char *zCommit;              /* How to commit changes */   
         1848  +    FILE *in;                   /* The input file */
         1849  +    int lineno = 0;             /* Line number of input file */
         1850  +
         1851  +    open_db(p);
         1852  +    nSep = strlen30(p->separator);
         1853  +    if( nSep==0 ){
         1854  +      fprintf(stderr, "Error: non-null separator required for import\n");
         1855  +      return 1;
         1856  +    }
         1857  +    zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
         1858  +    if( zSql==0 ){
         1859  +      fprintf(stderr, "Error: out of memory\n");
         1860  +      return 1;
         1861  +    }
         1862  +    nByte = strlen30(zSql);
         1863  +    rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
         1864  +    sqlite3_free(zSql);
         1865  +    if( rc ){
         1866  +      if (pStmt) sqlite3_finalize(pStmt);
         1867  +      fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
         1868  +      return 1;
         1869  +    }
         1870  +    nCol = sqlite3_column_count(pStmt);
         1871  +    sqlite3_finalize(pStmt);
         1872  +    pStmt = 0;
         1873  +    if( nCol==0 ) return 0; /* no columns, no error */
         1874  +    zSql = malloc( nByte + 20 + nCol*2 );
         1875  +    if( zSql==0 ){
         1876  +      fprintf(stderr, "Error: out of memory\n");
         1877  +      return 1;
         1878  +    }
         1879  +    sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
         1880  +    j = strlen30(zSql);
         1881  +    for(i=1; i<nCol; i++){
         1882  +      zSql[j++] = ',';
         1883  +      zSql[j++] = '?';
         1884  +    }
         1885  +    zSql[j++] = ')';
         1886  +    zSql[j] = 0;
         1887  +    rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
         1888  +    free(zSql);
         1889  +    if( rc ){
         1890  +      fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
         1891  +      if (pStmt) sqlite3_finalize(pStmt);
         1892  +      return 1;
         1893  +    }
         1894  +    in = fopen(zFile, "rb");
         1895  +    if( in==0 ){
         1896  +      fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
         1897  +      sqlite3_finalize(pStmt);
         1898  +      return 1;
         1899  +    }
         1900  +    azCol = malloc( sizeof(azCol[0])*(nCol+1) );
         1901  +    if( azCol==0 ){
         1902  +      fprintf(stderr, "Error: out of memory\n");
         1903  +      fclose(in);
         1904  +      sqlite3_finalize(pStmt);
         1905  +      return 1;
         1906  +    }
         1907  +    sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
         1908  +    zCommit = "COMMIT";
         1909  +    while( (zLine = local_getline(0, in, 1))!=0 ){
         1910  +      char *z, c;
         1911  +      int inQuote = 0;
         1912  +      lineno++;
         1913  +      azCol[0] = zLine;
         1914  +      for(i=0, z=zLine; (c = *z)!=0; z++){
         1915  +        if( c=='"' ) inQuote = !inQuote;
         1916  +        if( c=='\n' ) lineno++;
         1917  +        if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
         1918  +          *z = 0;
         1919  +          i++;
         1920  +          if( i<nCol ){
         1921  +            azCol[i] = &z[nSep];
         1922  +            z += nSep-1;
         1923  +          }
         1924  +        }
         1925  +      } /* end for */
         1926  +      *z = 0;
         1927  +      if( i+1!=nCol ){
         1928  +        fprintf(stderr,
         1929  +                "Error: %s line %d: expected %d columns of data but found %d\n",
         1930  +                zFile, lineno, nCol, i+1);
         1931  +        zCommit = "ROLLBACK";
         1932  +        free(zLine);
         1933  +        rc = 1;
         1934  +        break; /* from while */
         1935  +      }
         1936  +      for(i=0; i<nCol; i++){
         1937  +        if( azCol[i][0]=='"' ){
         1938  +          int k;
         1939  +          for(z=azCol[i], j=1, k=0; z[j]; j++){
         1940  +            if( z[j]=='"' ){ j++; if( z[j]==0 ) break; }
         1941  +            z[k++] = z[j];
         1942  +          }
         1943  +          z[k] = 0;
         1944  +        }
         1945  +        sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
         1946  +      }
         1947  +      sqlite3_step(pStmt);
         1948  +      rc = sqlite3_reset(pStmt);
         1949  +      free(zLine);
         1950  +      if( rc!=SQLITE_OK ){
         1951  +        fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
         1952  +        zCommit = "ROLLBACK";
         1953  +        rc = 1;
         1954  +        break; /* from while */
         1955  +      }
         1956  +    } /* end while */
         1957  +    free(azCol);
         1958  +    fclose(in);
         1959  +    sqlite3_finalize(pStmt);
         1960  +    sqlite3_exec(p->db, zCommit, 0, 0, 0);
         1961  +  }else
         1962  +
         1963  +  if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
         1964  +    struct callback_data data;
         1965  +    char *zErrMsg = 0;
         1966  +    open_db(p);
         1967  +    memcpy(&data, p, sizeof(data));
         1968  +    data.showHeader = 0;
         1969  +    data.mode = MODE_List;
         1970  +    if( nArg==1 ){
         1971  +      rc = sqlite3_exec(p->db,
         1972  +        "SELECT name FROM sqlite_master "
         1973  +        "WHERE type='index' AND name NOT LIKE 'sqlite_%' "
         1974  +        "UNION ALL "
         1975  +        "SELECT name FROM sqlite_temp_master "
         1976  +        "WHERE type='index' "
         1977  +        "ORDER BY 1",
         1978  +        callback, &data, &zErrMsg
         1979  +      );
         1980  +    }else{
         1981  +      zShellStatic = azArg[1];
         1982  +      rc = sqlite3_exec(p->db,
         1983  +        "SELECT name FROM sqlite_master "
         1984  +        "WHERE type='index' AND tbl_name LIKE shellstatic() "
         1985  +        "UNION ALL "
         1986  +        "SELECT name FROM sqlite_temp_master "
         1987  +        "WHERE type='index' AND tbl_name LIKE shellstatic() "
         1988  +        "ORDER BY 1",
         1989  +        callback, &data, &zErrMsg
         1990  +      );
         1991  +      zShellStatic = 0;
         1992  +    }
         1993  +    if( zErrMsg ){
         1994  +      fprintf(stderr,"Error: %s\n", zErrMsg);
         1995  +      sqlite3_free(zErrMsg);
         1996  +      rc = 1;
         1997  +    }else if( rc != SQLITE_OK ){
         1998  +      fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n");
         1999  +      rc = 1;
         2000  +    }
         2001  +  }else
         2002  +
         2003  +#ifdef SQLITE_ENABLE_IOTRACE
         2004  +  if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){
         2005  +    extern void (*sqlite3IoTrace)(const char*, ...);
         2006  +    if( iotrace && iotrace!=stdout ) fclose(iotrace);
         2007  +    iotrace = 0;
         2008  +    if( nArg<2 ){
         2009  +      sqlite3IoTrace = 0;
         2010  +    }else if( strcmp(azArg[1], "-")==0 ){
         2011  +      sqlite3IoTrace = iotracePrintf;
         2012  +      iotrace = stdout;
         2013  +    }else{
         2014  +      iotrace = fopen(azArg[1], "w");
         2015  +      if( iotrace==0 ){
         2016  +        fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
         2017  +        sqlite3IoTrace = 0;
         2018  +        rc = 1;
         2019  +      }else{
         2020  +        sqlite3IoTrace = iotracePrintf;
         2021  +      }
         2022  +    }
         2023  +  }else
         2024  +#endif
         2025  +
         2026  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
         2027  +  if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
         2028  +    const char *zFile, *zProc;
         2029  +    char *zErrMsg = 0;
         2030  +    zFile = azArg[1];
         2031  +    zProc = nArg>=3 ? azArg[2] : 0;
         2032  +    open_db(p);
         2033  +    rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
         2034  +    if( rc!=SQLITE_OK ){
         2035  +      fprintf(stderr, "Error: %s\n", zErrMsg);
         2036  +      sqlite3_free(zErrMsg);
         2037  +      rc = 1;
         2038  +    }
         2039  +  }else
         2040  +#endif
         2041  +
         2042  +  if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
         2043  +    const char *zFile = azArg[1];
         2044  +    output_file_close(p->pLog);
         2045  +    p->pLog = output_file_open(zFile);
         2046  +  }else
         2047  +
         2048  +  if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
         2049  +    int n2 = strlen30(azArg[1]);
         2050  +    if( (n2==4 && strncmp(azArg[1],"line",n2)==0)
         2051  +        ||
         2052  +        (n2==5 && strncmp(azArg[1],"lines",n2)==0) ){
         2053  +      p->mode = MODE_Line;
         2054  +    }else if( (n2==6 && strncmp(azArg[1],"column",n2)==0)
         2055  +              ||
         2056  +              (n2==7 && strncmp(azArg[1],"columns",n2)==0) ){
         2057  +      p->mode = MODE_Column;
         2058  +    }else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){
         2059  +      p->mode = MODE_List;
         2060  +    }else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){
         2061  +      p->mode = MODE_Html;
         2062  +    }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){
         2063  +      p->mode = MODE_Tcl;
         2064  +      sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
         2065  +    }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){
         2066  +      p->mode = MODE_Csv;
         2067  +      sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
         2068  +    }else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){
         2069  +      p->mode = MODE_List;
         2070  +      sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
         2071  +    }else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
         2072  +      p->mode = MODE_Insert;
         2073  +      set_table_name(p, "table");
         2074  +    }else {
         2075  +      fprintf(stderr,"Error: mode should be one of: "
         2076  +         "column csv html insert line list tabs tcl\n");
         2077  +      rc = 1;
         2078  +    }
         2079  +  }else
         2080  +
         2081  +  if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){
         2082  +    int n2 = strlen30(azArg[1]);
         2083  +    if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
         2084  +      p->mode = MODE_Insert;
         2085  +      set_table_name(p, azArg[2]);
         2086  +    }else {
         2087  +      fprintf(stderr, "Error: invalid arguments: "
         2088  +        " \"%s\". Enter \".help\" for help\n", azArg[2]);
         2089  +      rc = 1;
         2090  +    }
         2091  +  }else
         2092  +
         2093  +  if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
         2094  +    sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
         2095  +                     "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
         2096  +  }else
         2097  +
         2098  +  if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
         2099  +    if( p->outfile[0]=='|' ){
         2100  +      pclose(p->out);
         2101  +    }else{
         2102  +      output_file_close(p->out);
         2103  +    }
         2104  +    p->outfile[0] = 0;
         2105  +    if( azArg[1][0]=='|' ){
         2106  +      p->out = popen(&azArg[1][1], "w");
         2107  +      if( p->out==0 ){
         2108  +        fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
         2109  +        p->out = stdout;
         2110  +        rc = 1;
         2111  +      }else{
         2112  +        sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
         2113  +      }
         2114  +    }else{
         2115  +      p->out = output_file_open(azArg[1]);
         2116  +      if( p->out==0 ){
         2117  +        if( strcmp(azArg[1],"off")!=0 ){
         2118  +          fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
         2119  +        }
         2120  +        p->out = stdout;
         2121  +        rc = 1;
         2122  +      } else {
         2123  +        sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
         2124  +      }
         2125  +    }
         2126  +  }else
         2127  +
         2128  +  if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){
         2129  +    int i;
         2130  +    for(i=1; i<nArg; i++){
         2131  +      if( i>1 ) fprintf(p->out, " ");
         2132  +      fprintf(p->out, "%s", azArg[i]);
         2133  +    }
         2134  +    fprintf(p->out, "\n");
         2135  +  }else
         2136  +
         2137  +  if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
         2138  +    if( nArg >= 2) {
         2139  +      strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
         2140  +    }
         2141  +    if( nArg >= 3) {
         2142  +      strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
         2143  +    }
         2144  +  }else
         2145  +
         2146  +  if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
         2147  +    rc = 2;
         2148  +  }else
         2149  +
         2150  +  if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
         2151  +    FILE *alt = fopen(azArg[1], "rb");
         2152  +    if( alt==0 ){
         2153  +      fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
         2154  +      rc = 1;
         2155  +    }else{
         2156  +      rc = process_input(p, alt);
         2157  +      fclose(alt);
         2158  +    }
         2159  +  }else
         2160  +
         2161  +  if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){
         2162  +    const char *zSrcFile;
         2163  +    const char *zDb;
         2164  +    sqlite3 *pSrc;
         2165  +    sqlite3_backup *pBackup;
         2166  +    int nTimeout = 0;
         2167  +
         2168  +    if( nArg==2 ){
         2169  +      zSrcFile = azArg[1];
         2170  +      zDb = "main";
         2171  +    }else{
         2172  +      zSrcFile = azArg[2];
         2173  +      zDb = azArg[1];
         2174  +    }
         2175  +    rc = sqlite3_open(zSrcFile, &pSrc);
         2176  +    if( rc!=SQLITE_OK ){
         2177  +      fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
         2178  +      sqlite3_close(pSrc);
         2179  +      return 1;
         2180  +    }
         2181  +    open_db(p);
         2182  +    pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
         2183  +    if( pBackup==0 ){
         2184  +      fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
         2185  +      sqlite3_close(pSrc);
         2186  +      return 1;
         2187  +    }
         2188  +    while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
         2189  +          || rc==SQLITE_BUSY  ){
         2190  +      if( rc==SQLITE_BUSY ){
         2191  +        if( nTimeout++ >= 3 ) break;
         2192  +        sqlite3_sleep(100);
         2193  +      }
         2194  +    }
         2195  +    sqlite3_backup_finish(pBackup);
         2196  +    if( rc==SQLITE_DONE ){
         2197  +      rc = 0;
         2198  +    }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
         2199  +      fprintf(stderr, "Error: source database is busy\n");
         2200  +      rc = 1;
         2201  +    }else{
         2202  +      fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
         2203  +      rc = 1;
         2204  +    }
         2205  +    sqlite3_close(pSrc);
         2206  +  }else
         2207  +
         2208  +  if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){
         2209  +    struct callback_data data;
         2210  +    char *zErrMsg = 0;
         2211  +    open_db(p);
         2212  +    memcpy(&data, p, sizeof(data));
         2213  +    data.showHeader = 0;
         2214  +    data.mode = MODE_Semi;
         2215  +    if( nArg>1 ){
         2216  +      int i;
         2217  +      for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
         2218  +      if( strcmp(azArg[1],"sqlite_master")==0 ){
         2219  +        char *new_argv[2], *new_colv[2];
         2220  +        new_argv[0] = "CREATE TABLE sqlite_master (\n"
         2221  +                      "  type text,\n"
         2222  +                      "  name text,\n"
         2223  +                      "  tbl_name text,\n"
         2224  +                      "  rootpage integer,\n"
         2225  +                      "  sql text\n"
         2226  +                      ")";
         2227  +        new_argv[1] = 0;
         2228  +        new_colv[0] = "sql";
         2229  +        new_colv[1] = 0;
         2230  +        callback(&data, 1, new_argv, new_colv);
         2231  +        rc = SQLITE_OK;
         2232  +      }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){
         2233  +        char *new_argv[2], *new_colv[2];
         2234  +        new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
         2235  +                      "  type text,\n"
         2236  +                      "  name text,\n"
         2237  +                      "  tbl_name text,\n"
         2238  +                      "  rootpage integer,\n"
         2239  +                      "  sql text\n"
         2240  +                      ")";
         2241  +        new_argv[1] = 0;
         2242  +        new_colv[0] = "sql";
         2243  +        new_colv[1] = 0;
         2244  +        callback(&data, 1, new_argv, new_colv);
         2245  +        rc = SQLITE_OK;
         2246  +      }else{
         2247  +        zShellStatic = azArg[1];
         2248  +        rc = sqlite3_exec(p->db,
         2249  +          "SELECT sql FROM "
         2250  +          "  (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
         2251  +          "     FROM sqlite_master UNION ALL"
         2252  +          "   SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
         2253  +          "WHERE lower(tbl_name) LIKE shellstatic()"
         2254  +          "  AND type!='meta' AND sql NOTNULL "
         2255  +          "ORDER BY substr(type,2,1), "
         2256  +                  " CASE type WHEN 'view' THEN rowid ELSE name END",
         2257  +          callback, &data, &zErrMsg);
         2258  +        zShellStatic = 0;
         2259  +      }
         2260  +    }else{
         2261  +      rc = sqlite3_exec(p->db,
         2262  +         "SELECT sql FROM "
         2263  +         "  (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
         2264  +         "     FROM sqlite_master UNION ALL"
         2265  +         "   SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
         2266  +         "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
         2267  +         "ORDER BY substr(type,2,1),"
         2268  +                  " CASE type WHEN 'view' THEN rowid ELSE name END",
         2269  +         callback, &data, &zErrMsg
         2270  +      );
         2271  +    }
         2272  +    if( zErrMsg ){
         2273  +      fprintf(stderr,"Error: %s\n", zErrMsg);
         2274  +      sqlite3_free(zErrMsg);
         2275  +      rc = 1;
         2276  +    }else if( rc != SQLITE_OK ){
         2277  +      fprintf(stderr,"Error: querying schema information\n");
         2278  +      rc = 1;
         2279  +    }else{
         2280  +      rc = 0;
         2281  +    }
         2282  +  }else
         2283  +
         2284  +  if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
         2285  +    sqlite3_snprintf(sizeof(p->separator), p->separator,
         2286  +                     "%.*s", (int)sizeof(p->separator)-1, azArg[1]);
         2287  +  }else
         2288  +
         2289  +  if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){
         2290  +    int i;
         2291  +    fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
         2292  +    fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
         2293  +    fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
         2294  +    fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
         2295  +    fprintf(p->out,"%9.9s: ", "nullvalue");
         2296  +      output_c_string(p->out, p->nullvalue);
         2297  +      fprintf(p->out, "\n");
         2298  +    fprintf(p->out,"%9.9s: %s\n","output",
         2299  +            strlen30(p->outfile) ? p->outfile : "stdout");
         2300  +    fprintf(p->out,"%9.9s: ", "separator");
         2301  +      output_c_string(p->out, p->separator);
         2302  +      fprintf(p->out, "\n");
         2303  +    fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
         2304  +    fprintf(p->out,"%9.9s: ","width");
         2305  +    for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
         2306  +      fprintf(p->out,"%d ",p->colWidth[i]);
         2307  +    }
         2308  +    fprintf(p->out,"\n");
         2309  +  }else
         2310  +
         2311  +  if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){
         2312  +    p->statsOn = booleanValue(azArg[1]);
         2313  +  }else
         2314  +
         2315  +  if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
         2316  +    sqlite3_stmt *pStmt;
         2317  +    char **azResult;
         2318  +    int nRow, nAlloc;
         2319  +    char *zSql = 0;
         2320  +    int ii;
         2321  +    open_db(p);
         2322  +    rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
         2323  +    if( rc ) return rc;
         2324  +    zSql = sqlite3_mprintf(
         2325  +        "SELECT name FROM sqlite_master"
         2326  +        " WHERE type IN ('table','view')"
         2327  +        "   AND name NOT LIKE 'sqlite_%%'"
         2328  +        "   AND name LIKE ?1");
         2329  +    while( sqlite3_step(pStmt)==SQLITE_ROW ){
         2330  +      const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
         2331  +      if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue;
         2332  +      if( strcmp(zDbName,"temp")==0 ){
         2333  +        zSql = sqlite3_mprintf(
         2334  +                 "%z UNION ALL "
         2335  +                 "SELECT 'temp.' || name FROM sqlite_temp_master"
         2336  +                 " WHERE type IN ('table','view')"
         2337  +                 "   AND name NOT LIKE 'sqlite_%%'"
         2338  +                 "   AND name LIKE ?1", zSql);
         2339  +      }else{
         2340  +        zSql = sqlite3_mprintf(
         2341  +                 "%z UNION ALL "
         2342  +                 "SELECT '%q.' || name FROM \"%w\".sqlite_master"
         2343  +                 " WHERE type IN ('table','view')"
         2344  +                 "   AND name NOT LIKE 'sqlite_%%'"
         2345  +                 "   AND name LIKE ?1", zSql, zDbName, zDbName);
         2346  +      }
         2347  +    }
         2348  +    sqlite3_finalize(pStmt);
         2349  +    zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
         2350  +    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
         2351  +    sqlite3_free(zSql);
         2352  +    if( rc ) return rc;
         2353  +    nRow = nAlloc = 0;
         2354  +    azResult = 0;
         2355  +    if( nArg>1 ){
         2356  +      sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
         2357  +    }else{
         2358  +      sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
         2359  +    }
         2360  +    while( sqlite3_step(pStmt)==SQLITE_ROW ){
         2361  +      if( nRow>=nAlloc ){
         2362  +        char **azNew;
         2363  +        int n = nAlloc*2 + 10;
         2364  +        azNew = sqlite3_realloc(azResult, sizeof(azResult[0])*n);
         2365  +        if( azNew==0 ){
         2366  +          fprintf(stderr, "Error: out of memory\n");
         2367  +          break;
         2368  +        }
         2369  +        nAlloc = n;
         2370  +        azResult = azNew;
         2371  +      }
         2372  +      azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
         2373  +      if( azResult[nRow] ) nRow++;
         2374  +    }
         2375  +    sqlite3_finalize(pStmt);        
         2376  +    if( nRow>0 ){
         2377  +      int len, maxlen = 0;
         2378  +      int i, j;
         2379  +      int nPrintCol, nPrintRow;
         2380  +      for(i=0; i<nRow; i++){
         2381  +        len = strlen30(azResult[i]);
         2382  +        if( len>maxlen ) maxlen = len;
         2383  +      }
         2384  +      nPrintCol = 80/(maxlen+2);
         2385  +      if( nPrintCol<1 ) nPrintCol = 1;
         2386  +      nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
         2387  +      for(i=0; i<nPrintRow; i++){
         2388  +        for(j=i; j<nRow; j+=nPrintRow){
         2389  +          char *zSp = j<nPrintRow ? "" : "  ";
         2390  +          printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
         2391  +        }
         2392  +        printf("\n");
         2393  +      }
         2394  +    }
         2395  +    for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
         2396  +    sqlite3_free(azResult);
         2397  +  }else
         2398  +
         2399  +  if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
         2400  +    static const struct {
         2401  +       const char *zCtrlName;   /* Name of a test-control option */
         2402  +       int ctrlCode;            /* Integer code for that option */
         2403  +    } aCtrl[] = {
         2404  +      { "prng_save",             SQLITE_TESTCTRL_PRNG_SAVE              },
         2405  +      { "prng_restore",          SQLITE_TESTCTRL_PRNG_RESTORE           },
         2406  +      { "prng_reset",            SQLITE_TESTCTRL_PRNG_RESET             },
         2407  +      { "bitvec_test",           SQLITE_TESTCTRL_BITVEC_TEST            },
         2408  +      { "fault_install",         SQLITE_TESTCTRL_FAULT_INSTALL          },
         2409  +      { "benign_malloc_hooks",   SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS    },
         2410  +      { "pending_byte",          SQLITE_TESTCTRL_PENDING_BYTE           },
         2411  +      { "assert",                SQLITE_TESTCTRL_ASSERT                 },
         2412  +      { "always",                SQLITE_TESTCTRL_ALWAYS                 },
         2413  +      { "reserve",               SQLITE_TESTCTRL_RESERVE                },
         2414  +      { "optimizations",         SQLITE_TESTCTRL_OPTIMIZATIONS          },
         2415  +      { "iskeyword",             SQLITE_TESTCTRL_ISKEYWORD              },
         2416  +      { "scratchmalloc",         SQLITE_TESTCTRL_SCRATCHMALLOC          },
         2417  +    };
         2418  +    int testctrl = -1;
         2419  +    int rc = 0;
         2420  +    int i, n;
         2421  +    open_db(p);
         2422  +
         2423  +    /* convert testctrl text option to value. allow any unique prefix
         2424  +    ** of the option name, or a numerical value. */
         2425  +    n = strlen30(azArg[1]);
         2426  +    for(i=0; i<(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++){
         2427  +      if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){
         2428  +        if( testctrl<0 ){
         2429  +          testctrl = aCtrl[i].ctrlCode;
         2430  +        }else{
         2431  +          fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[1]);
         2432  +          testctrl = -1;
         2433  +          break;
         2434  +        }
         2435  +      }
         2436  +    }
         2437  +    if( testctrl<0 ) testctrl = atoi(azArg[1]);
         2438  +    if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){
         2439  +      fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]);
         2440  +    }else{
         2441  +      switch(testctrl){
         2442  +
         2443  +        /* sqlite3_test_control(int, db, int) */
         2444  +        case SQLITE_TESTCTRL_OPTIMIZATIONS:
         2445  +        case SQLITE_TESTCTRL_RESERVE:             
         2446  +          if( nArg==3 ){
         2447  +            int opt = (int)strtol(azArg[2], 0, 0);        
         2448  +            rc = sqlite3_test_control(testctrl, p->db, opt);
         2449  +            printf("%d (0x%08x)\n", rc, rc);
         2450  +          } else {
         2451  +            fprintf(stderr,"Error: testctrl %s takes a single int option\n",
         2452  +                    azArg[1]);
         2453  +          }
         2454  +          break;
         2455  +
         2456  +        /* sqlite3_test_control(int) */
         2457  +        case SQLITE_TESTCTRL_PRNG_SAVE:           
         2458  +        case SQLITE_TESTCTRL_PRNG_RESTORE:        
         2459  +        case SQLITE_TESTCTRL_PRNG_RESET:
         2460  +          if( nArg==2 ){
         2461  +            rc = sqlite3_test_control(testctrl);
         2462  +            printf("%d (0x%08x)\n", rc, rc);
         2463  +          } else {
         2464  +            fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]);
         2465  +          }
         2466  +          break;
         2467  +
         2468  +        /* sqlite3_test_control(int, uint) */
         2469  +        case SQLITE_TESTCTRL_PENDING_BYTE:        
         2470  +          if( nArg==3 ){
         2471  +            unsigned int opt = (unsigned int)atoi(azArg[2]);        
         2472  +            rc = sqlite3_test_control(testctrl, opt);
         2473  +            printf("%d (0x%08x)\n", rc, rc);
         2474  +          } else {
         2475  +            fprintf(stderr,"Error: testctrl %s takes a single unsigned"
         2476  +                           " int option\n", azArg[1]);
         2477  +          }
         2478  +          break;
         2479  +          
         2480  +        /* sqlite3_test_control(int, int) */
         2481  +        case SQLITE_TESTCTRL_ASSERT:              
         2482  +        case SQLITE_TESTCTRL_ALWAYS:              
         2483  +          if( nArg==3 ){
         2484  +            int opt = atoi(azArg[2]);        
         2485  +            rc = sqlite3_test_control(testctrl, opt);
         2486  +            printf("%d (0x%08x)\n", rc, rc);
         2487  +          } else {
         2488  +            fprintf(stderr,"Error: testctrl %s takes a single int option\n",
         2489  +                            azArg[1]);
         2490  +          }
         2491  +          break;
         2492  +
         2493  +        /* sqlite3_test_control(int, char *) */
         2494  +#ifdef SQLITE_N_KEYWORD
         2495  +        case SQLITE_TESTCTRL_ISKEYWORD:           
         2496  +          if( nArg==3 ){
         2497  +            const char *opt = azArg[2];        
         2498  +            rc = sqlite3_test_control(testctrl, opt);
         2499  +            printf("%d (0x%08x)\n", rc, rc);
         2500  +          } else {
         2501  +            fprintf(stderr,"Error: testctrl %s takes a single char * option\n",
         2502  +                            azArg[1]);
         2503  +          }
         2504  +          break;
         2505  +#endif
         2506  +
         2507  +        case SQLITE_TESTCTRL_BITVEC_TEST:         
         2508  +        case SQLITE_TESTCTRL_FAULT_INSTALL:       
         2509  +        case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: 
         2510  +        case SQLITE_TESTCTRL_SCRATCHMALLOC:       
         2511  +        default:
         2512  +          fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n",
         2513  +                  azArg[1]);
         2514  +          break;
         2515  +      }
         2516  +    }
         2517  +  }else
         2518  +
         2519  +  if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
         2520  +    open_db(p);
         2521  +    sqlite3_busy_timeout(p->db, atoi(azArg[1]));
         2522  +  }else
         2523  +    
         2524  +  if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
         2525  +   && nArg==2
         2526  +  ){
         2527  +    enableTimer = booleanValue(azArg[1]);
         2528  +  }else
         2529  +  
         2530  +  if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
         2531  +    open_db(p);
         2532  +    output_file_close(p->traceOut);
         2533  +    p->traceOut = output_file_open(azArg[1]);
         2534  +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
         2535  +    if( p->traceOut==0 ){
         2536  +      sqlite3_trace(p->db, 0, 0);
         2537  +    }else{
         2538  +      sqlite3_trace(p->db, sql_trace_callback, p->traceOut);
         2539  +    }
         2540  +#endif
         2541  +  }else
         2542  +
         2543  +  if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
         2544  +    printf("SQLite %s %s\n" /*extra-version-info*/,
         2545  +        sqlite3_libversion(), sqlite3_sourceid());
         2546  +  }else
         2547  +
         2548  +  if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){
         2549  +    const char *zDbName = nArg==2 ? azArg[1] : "main";
         2550  +    char *zVfsName = 0;
         2551  +    if( p->db ){
         2552  +      sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
         2553  +      if( zVfsName ){
         2554  +        printf("%s\n", zVfsName);
         2555  +        sqlite3_free(zVfsName);
         2556  +      }
         2557  +    }
         2558  +  }else
         2559  +
         2560  +#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
         2561  +  if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
         2562  +    extern int sqlite3WhereTrace;
         2563  +    sqlite3WhereTrace = atoi(azArg[1]);
         2564  +  }else
         2565  +#endif
         2566  +
         2567  +  if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
         2568  +    int j;
         2569  +    assert( nArg<=ArraySize(azArg) );
         2570  +    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
         2571  +      p->colWidth[j-1] = atoi(azArg[j]);
         2572  +    }
         2573  +  }else
         2574  +
         2575  +  {
         2576  +    fprintf(stderr, "Error: unknown command or invalid arguments: "
         2577  +      " \"%s\". Enter \".help\" for help\n", azArg[0]);
         2578  +    rc = 1;
         2579  +  }
         2580  +
         2581  +  return rc;
         2582  +}
         2583  +
         2584  +/*
         2585  +** Return TRUE if a semicolon occurs anywhere in the first N characters
         2586  +** of string z[].
         2587  +*/
         2588  +static int _contains_semicolon(const char *z, int N){
         2589  +  int i;
         2590  +  for(i=0; i<N; i++){  if( z[i]==';' ) return 1; }
         2591  +  return 0;
         2592  +}
         2593  +
         2594  +/*
         2595  +** Test to see if a line consists entirely of whitespace.
         2596  +*/
         2597  +static int _all_whitespace(const char *z){
         2598  +  for(; *z; z++){
         2599  +    if( IsSpace(z[0]) ) continue;
         2600  +    if( *z=='/' && z[1]=='*' ){
         2601  +      z += 2;
         2602  +      while( *z && (*z!='*' || z[1]!='/') ){ z++; }
         2603  +      if( *z==0 ) return 0;
         2604  +      z++;
         2605  +      continue;
         2606  +    }
         2607  +    if( *z=='-' && z[1]=='-' ){
         2608  +      z += 2;
         2609  +      while( *z && *z!='\n' ){ z++; }
         2610  +      if( *z==0 ) return 1;
         2611  +      continue;
         2612  +    }
         2613  +    return 0;
         2614  +  }
         2615  +  return 1;
         2616  +}
         2617  +
         2618  +/*
         2619  +** Return TRUE if the line typed in is an SQL command terminator other
         2620  +** than a semi-colon.  The SQL Server style "go" command is understood
         2621  +** as is the Oracle "/".
         2622  +*/
         2623  +static int _is_command_terminator(const char *zLine){
         2624  +  while( IsSpace(zLine[0]) ){ zLine++; };
         2625  +  if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
         2626  +    return 1;  /* Oracle */
         2627  +  }
         2628  +  if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o'
         2629  +         && _all_whitespace(&zLine[2]) ){
         2630  +    return 1;  /* SQL Server */
         2631  +  }
         2632  +  return 0;
         2633  +}
         2634  +
         2635  +/*
         2636  +** Return true if zSql is a complete SQL statement.  Return false if it
         2637  +** ends in the middle of a string literal or C-style comment.
         2638  +*/
         2639  +static int _is_complete(char *zSql, int nSql){
         2640  +  int rc;
         2641  +  if( zSql==0 ) return 1;
         2642  +  zSql[nSql] = ';';
         2643  +  zSql[nSql+1] = 0;
         2644  +  rc = sqlite3_complete(zSql);
         2645  +  zSql[nSql] = 0;
         2646  +  return rc;
         2647  +}
         2648  +
         2649  +/*
         2650  +** Read input from *in and process it.  If *in==0 then input
         2651  +** is interactive - the user is typing it it.  Otherwise, input
         2652  +** is coming from a file or device.  A prompt is issued and history
         2653  +** is saved only if input is interactive.  An interrupt signal will
         2654  +** cause this routine to exit immediately, unless input is interactive.
         2655  +**
         2656  +** Return the number of errors.
         2657  +*/
         2658  +static int process_input(struct callback_data *p, FILE *in){
         2659  +  char *zLine = 0;
         2660  +  char *zSql = 0;
         2661  +  int nSql = 0;
         2662  +  int nSqlPrior = 0;
         2663  +  char *zErrMsg;
         2664  +  int rc;
         2665  +  int errCnt = 0;
         2666  +  int lineno = 0;
         2667  +  int startline = 0;
         2668  +
         2669  +  while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
         2670  +    fflush(p->out);
         2671  +    free(zLine);
         2672  +    zLine = one_input_line(zSql, in);
         2673  +    if( zLine==0 ){
         2674  +      /* End of input */
         2675  +      if( stdin_is_interactive ) printf("\n");
         2676  +      break;
         2677  +    }
         2678  +    if( seenInterrupt ){
         2679  +      if( in!=0 ) break;
         2680  +      seenInterrupt = 0;
         2681  +    }
         2682  +    lineno++;
         2683  +    if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
         2684  +    if( zLine && zLine[0]=='.' && nSql==0 ){
         2685  +      if( p->echoOn ) printf("%s\n", zLine);
         2686  +      rc = do_meta_command(zLine, p);
         2687  +      if( rc==2 ){ /* exit requested */
         2688  +        break;
         2689  +      }else if( rc ){
         2690  +        errCnt++;
         2691  +      }
         2692  +      continue;
         2693  +    }
         2694  +    if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
         2695  +      memcpy(zLine,";",2);
         2696  +    }
         2697  +    nSqlPrior = nSql;
         2698  +    if( zSql==0 ){
         2699  +      int i;
         2700  +      for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
         2701  +      if( zLine[i]!=0 ){
         2702  +        nSql = strlen30(zLine);
         2703  +        zSql = malloc( nSql+3 );
         2704  +        if( zSql==0 ){
         2705  +          fprintf(stderr, "Error: out of memory\n");
         2706  +          exit(1);
         2707  +        }
         2708  +        memcpy(zSql, zLine, nSql+1);
         2709  +        startline = lineno;
         2710  +      }
         2711  +    }else{
         2712  +      int len = strlen30(zLine);
         2713  +      zSql = realloc( zSql, nSql + len + 4 );
         2714  +      if( zSql==0 ){
         2715  +        fprintf(stderr,"Error: out of memory\n");
         2716  +        exit(1);
         2717  +      }
         2718  +      zSql[nSql++] = '\n';
         2719  +      memcpy(&zSql[nSql], zLine, len+1);
         2720  +      nSql += len;
         2721  +    }
         2722  +    if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
         2723  +                && sqlite3_complete(zSql) ){
         2724  +      p->cnt = 0;
         2725  +      open_db(p);
         2726  +      BEGIN_TIMER;
         2727  +      rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
         2728  +      END_TIMER;
         2729  +      if( rc || zErrMsg ){
         2730  +        char zPrefix[100];
         2731  +        if( in!=0 || !stdin_is_interactive ){
         2732  +          sqlite3_snprintf(sizeof(zPrefix), zPrefix, 
         2733  +                           "Error: near line %d:", startline);
         2734  +        }else{
         2735  +          sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
         2736  +        }
         2737  +        if( zErrMsg!=0 ){
         2738  +          fprintf(stderr, "%s %s\n", zPrefix, zErrMsg);
         2739  +          sqlite3_free(zErrMsg);
         2740  +          zErrMsg = 0;
         2741  +        }else{
         2742  +          fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
         2743  +        }
         2744  +        errCnt++;
         2745  +      }
         2746  +      free(zSql);
         2747  +      zSql = 0;
         2748  +      nSql = 0;
         2749  +    }
         2750  +  }
         2751  +  if( zSql ){
         2752  +    if( !_all_whitespace(zSql) ){
         2753  +      fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
         2754  +    }
         2755  +    free(zSql);
         2756  +  }
         2757  +  free(zLine);
         2758  +  return errCnt>0;
         2759  +}
         2760  +
         2761  +/*
         2762  +** Return a pathname which is the user's home directory.  A
         2763  +** 0 return indicates an error of some kind.
         2764  +*/
         2765  +static char *find_home_dir(void){
         2766  +  static char *home_dir = NULL;
         2767  +  if( home_dir ) return home_dir;
         2768  +
         2769  +#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
         2770  +  {
         2771  +    struct passwd *pwent;
         2772  +    uid_t uid = getuid();
         2773  +    if( (pwent=getpwuid(uid)) != NULL) {
         2774  +      home_dir = pwent->pw_dir;
         2775  +    }
         2776  +  }
         2777  +#endif
         2778  +
         2779  +#if defined(_WIN32_WCE)
         2780  +  /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
         2781  +   */
         2782  +  home_dir = "/";
         2783  +#else
         2784  +
         2785  +#if defined(_WIN32) || defined(WIN32)
         2786  +  if (!home_dir) {
         2787  +    home_dir = getenv("USERPROFILE");
         2788  +  }
         2789  +#endif
         2790  +
         2791  +  if (!home_dir) {
         2792  +    home_dir = getenv("HOME");
         2793  +  }
         2794  +
         2795  +#if defined(_WIN32) || defined(WIN32)
         2796  +  if (!home_dir) {
         2797  +    char *zDrive, *zPath;
         2798  +    int n;
         2799  +    zDrive = getenv("HOMEDRIVE");
         2800  +    zPath = getenv("HOMEPATH");
         2801  +    if( zDrive && zPath ){
         2802  +      n = strlen30(zDrive) + strlen30(zPath) + 1;
         2803  +      home_dir = malloc( n );
         2804  +      if( home_dir==0 ) return 0;
         2805  +      sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath);
         2806  +      return home_dir;
         2807  +    }
         2808  +    home_dir = "c:\\";
         2809  +  }
         2810  +#endif
         2811  +
         2812  +#endif /* !_WIN32_WCE */
         2813  +
         2814  +  if( home_dir ){
         2815  +    int n = strlen30(home_dir) + 1;
         2816  +    char *z = malloc( n );
         2817  +    if( z ) memcpy(z, home_dir, n);
         2818  +    home_dir = z;
         2819  +  }
         2820  +
         2821  +  return home_dir;
         2822  +}
         2823  +
         2824  +/*
         2825  +** Read input from the file given by sqliterc_override.  Or if that
         2826  +** parameter is NULL, take input from ~/.sqliterc
         2827  +**
         2828  +** Returns the number of errors.
         2829  +*/
         2830  +static int process_sqliterc(
         2831  +  struct callback_data *p,        /* Configuration data */
         2832  +  const char *sqliterc_override   /* Name of config file. NULL to use default */
         2833  +){
         2834  +  char *home_dir = NULL;
         2835  +  const char *sqliterc = sqliterc_override;
         2836  +  char *zBuf = 0;
         2837  +  FILE *in = NULL;
         2838  +  int rc = 0;
         2839  +
         2840  +  if (sqliterc == NULL) {
         2841  +    home_dir = find_home_dir();
         2842  +    if( home_dir==0 ){
         2843  +#if !defined(__RTP__) && !defined(_WRS_KERNEL)
         2844  +      fprintf(stderr,"%s: Error: cannot locate your home directory\n", Argv0);
         2845  +#endif
         2846  +      return 1;
         2847  +    }
         2848  +    sqlite3_initialize();
         2849  +    zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
         2850  +    sqliterc = zBuf;
         2851  +  }
         2852  +  in = fopen(sqliterc,"rb");
         2853  +  if( in ){
         2854  +    if( stdin_is_interactive ){
         2855  +      fprintf(stderr,"-- Loading resources from %s\n",sqliterc);
         2856  +    }
         2857  +    rc = process_input(p,in);
         2858  +    fclose(in);
         2859  +  }
         2860  +  sqlite3_free(zBuf);
         2861  +  return rc;
         2862  +}
         2863  +
         2864  +/*
         2865  +** Show available command line options
         2866  +*/
         2867  +static const char zOptions[] = 
         2868  +  "   -bail                stop after hitting an error\n"
         2869  +  "   -batch               force batch I/O\n"
         2870  +  "   -column              set output mode to 'column'\n"
         2871  +  "   -cmd COMMAND         run \"COMMAND\" before reading stdin\n"
         2872  +  "   -csv                 set output mode to 'csv'\n"
         2873  +  "   -echo                print commands before execution\n"
         2874  +  "   -init FILENAME       read/process named file\n"
         2875  +  "   -[no]header          turn headers on or off\n"
         2876  +#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
         2877  +  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
         2878  +#endif
         2879  +  "   -help                show this message\n"
         2880  +  "   -html                set output mode to HTML\n"
         2881  +  "   -interactive         force interactive I/O\n"
         2882  +  "   -line                set output mode to 'line'\n"
         2883  +  "   -list                set output mode to 'list'\n"
         2884  +#ifdef SQLITE_ENABLE_MULTIPLEX
         2885  +  "   -multiplex           enable the multiplexor VFS\n"
         2886  +#endif
         2887  +  "   -nullvalue TEXT      set text string for NULL values. Default ''\n"
         2888  +  "   -separator SEP       set output field separator. Default: '|'\n"
         2889  +  "   -stats               print memory stats before each finalize\n"
         2890  +  "   -version             show SQLite version\n"
         2891  +  "   -vfs NAME            use NAME as the default VFS\n"
         2892  +#ifdef SQLITE_ENABLE_VFSTRACE
         2893  +  "   -vfstrace            enable tracing of all VFS calls\n"
         2894  +#endif
         2895  +;
         2896  +static void usage(int showDetail){
         2897  +  fprintf(stderr,
         2898  +      "Usage: %s [OPTIONS] FILENAME [SQL]\n"  
         2899  +      "FILENAME is the name of an SQLite database. A new database is created\n"
         2900  +      "if the file does not previously exist.\n", Argv0);
         2901  +  if( showDetail ){
         2902  +    fprintf(stderr, "OPTIONS include:\n%s", zOptions);
         2903  +  }else{
         2904  +    fprintf(stderr, "Use the -help option for additional information\n");
         2905  +  }
         2906  +  exit(1);
         2907  +}
         2908  +
         2909  +/*
         2910  +** Initialize the state information in data
         2911  +*/
         2912  +static void main_init(struct callback_data *data) {
         2913  +  memset(data, 0, sizeof(*data));
         2914  +  data->mode = MODE_List;
         2915  +  memcpy(data->separator,"|", 2);
         2916  +  data->showHeader = 0;
         2917  +  sqlite3_config(SQLITE_CONFIG_URI, 1);
         2918  +  sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
         2919  +  sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> ");
         2920  +  sqlite3_snprintf(sizeof(continuePrompt), continuePrompt,"   ...> ");
         2921  +  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
         2922  +}
         2923  +
         2924  +/*
         2925  +** Get the argument to an --option.  Throw an error and die if no argument
         2926  +** is available.
         2927  +*/
         2928  +static char *cmdline_option_value(int argc, char **argv, int i){
         2929  +  if( i==argc ){
         2930  +    fprintf(stderr, "%s: Error: missing argument to %s\n",
         2931  +            argv[0], argv[argc-1]);
         2932  +    exit(1);
         2933  +  }
         2934  +  return argv[i];
         2935  +}
         2936  +
         2937  +int main(int argc, char **argv){
         2938  +  char *zErrMsg = 0;
         2939  +  struct callback_data data;
         2940  +  const char *zInitFile = 0;
         2941  +  char *zFirstCmd = 0;
         2942  +  int i;
         2943  +  int rc = 0;
         2944  +
         2945  +  if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
         2946  +    fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
         2947  +            sqlite3_sourceid(), SQLITE_SOURCE_ID);
         2948  +    exit(1);
         2949  +  }
         2950  +  Argv0 = argv[0];
         2951  +  main_init(&data);
         2952  +  stdin_is_interactive = isatty(0);
         2953  +
         2954  +  /* Make sure we have a valid signal handler early, before anything
         2955  +  ** else is done.
         2956  +  */
         2957  +#ifdef SIGINT
         2958  +  signal(SIGINT, interrupt_handler);
         2959  +#endif
         2960  +
         2961  +  /* Do an initial pass through the command-line argument to locate
         2962  +  ** the name of the database file, the name of the initialization file,
         2963  +  ** the size of the alternative malloc heap,
         2964  +  ** and the first command to execute.
         2965  +  */
         2966  +  for(i=1; i<argc; i++){
         2967  +    char *z;
         2968  +    z = argv[i];
         2969  +    if( z[0]!='-' ){
         2970  +      if( data.zDbFilename==0 ){
         2971  +        data.zDbFilename = z;
         2972  +        continue;
         2973  +      }
         2974  +      if( zFirstCmd==0 ){
         2975  +        zFirstCmd = z;
         2976  +        continue;
         2977  +      }
         2978  +      fprintf(stderr,"%s: Error: too many options: \"%s\"\n", Argv0, argv[i]);
         2979  +      fprintf(stderr,"Use -help for a list of options.\n");
         2980  +      return 1;
         2981  +    }
         2982  +    if( z[1]=='-' ) z++;
         2983  +    if( strcmp(z,"-separator")==0
         2984  +     || strcmp(z,"-nullvalue")==0
         2985  +     || strcmp(z,"-cmd")==0
         2986  +    ){
         2987  +      (void)cmdline_option_value(argc, argv, ++i);
         2988  +    }else if( strcmp(z,"-init")==0 ){
         2989  +      zInitFile = cmdline_option_value(argc, argv, ++i);
         2990  +    }else if( strcmp(z,"-batch")==0 ){
         2991  +      /* Need to check for batch mode here to so we can avoid printing
         2992  +      ** informational messages (like from process_sqliterc) before 
         2993  +      ** we do the actual processing of arguments later in a second pass.
         2994  +      */
         2995  +      stdin_is_interactive = 0;
         2996  +    }else if( strcmp(z,"-heap")==0 ){
         2997  +#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
         2998  +      int j, c;
         2999  +      const char *zSize;
         3000  +      sqlite3_int64 szHeap;
         3001  +
         3002  +      zSize = cmdline_option_value(argc, argv, ++i);
         3003  +      szHeap = atoi(zSize);
         3004  +      for(j=0; (c = zSize[j])!=0; j++){
         3005  +        if( c=='M' ){ szHeap *= 1000000; break; }
         3006  +        if( c=='K' ){ szHeap *= 1000; break; }
         3007  +        if( c=='G' ){ szHeap *= 1000000000; break; }
         3008  +      }
         3009  +      if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
         3010  +      sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
         3011  +#endif
         3012  +#ifdef SQLITE_ENABLE_VFSTRACE
         3013  +    }else if( strcmp(z,"-vfstrace")==0 ){
         3014  +      extern int vfstrace_register(
         3015  +         const char *zTraceName,
         3016  +         const char *zOldVfsName,
         3017  +         int (*xOut)(const char*,void*),
         3018  +         void *pOutArg,
         3019  +         int makeDefault
         3020  +      );
         3021  +      vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
         3022  +#endif
         3023  +#ifdef SQLITE_ENABLE_MULTIPLEX
         3024  +    }else if( strcmp(z,"-multiplex")==0 ){
         3025  +      extern int sqlite3_multiple_initialize(const char*,int);
         3026  +      sqlite3_multiplex_initialize(0, 1);
         3027  +#endif
         3028  +    }else if( strcmp(z,"-vfs")==0 ){
         3029  +      sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
         3030  +      if( pVfs ){
         3031  +        sqlite3_vfs_register(pVfs, 1);
         3032  +      }else{
         3033  +        fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]);
         3034  +        exit(1);
         3035  +      }
         3036  +    }
         3037  +  }
         3038  +  if( data.zDbFilename==0 ){
         3039  +#ifndef SQLITE_OMIT_MEMORYDB
         3040  +    data.zDbFilename = ":memory:";
         3041  +#else
         3042  +    fprintf(stderr,"%s: Error: no database filename specified\n", Argv0);
         3043  +    return 1;
         3044  +#endif
         3045  +    /***** Begin Fossil Patch *****/
         3046  +    {
         3047  +      extern void fossil_open(const char **);
         3048  +      fossil_open(&data.zDbFilename);
         3049  +    }
         3050  +    /***** End Fossil Patch *****/
         3051  +  }
         3052  +  data.out = stdout;
         3053  +
         3054  +  /* Go ahead and open the database file if it already exists.  If the
         3055  +  ** file does not exist, delay opening it.  This prevents empty database
         3056  +  ** files from being created if a user mistypes the database name argument
         3057  +  ** to the sqlite command-line tool.
         3058  +  */
         3059  +  if( access(data.zDbFilename, 0)==0 ){
         3060  +    open_db(&data);
         3061  +  }
         3062  +
         3063  +  /* Process the initialization file if there is one.  If no -init option
         3064  +  ** is given on the command line, look for a file named ~/.sqliterc and
         3065  +  ** try to process it.
         3066  +  */
         3067  +  rc = process_sqliterc(&data,zInitFile);
         3068  +  if( rc>0 ){
         3069  +    return rc;
         3070  +  }
         3071  +
         3072  +  /* Make a second pass through the command-line argument and set
         3073  +  ** options.  This second pass is delayed until after the initialization
         3074  +  ** file is processed so that the command-line arguments will override
         3075  +  ** settings in the initialization file.
         3076  +  */
         3077  +  for(i=1; i<argc; i++){
         3078  +    char *z = argv[i];
         3079  +    if( z[0]!='-' ) continue;
         3080  +    if( z[1]=='-' ){ z++; }
         3081  +    if( strcmp(z,"-init")==0 ){
         3082  +      i++;
         3083  +    }else if( strcmp(z,"-html")==0 ){
         3084  +      data.mode = MODE_Html;
         3085  +    }else if( strcmp(z,"-list")==0 ){
         3086  +      data.mode = MODE_List;
         3087  +    }else if( strcmp(z,"-line")==0 ){
         3088  +      data.mode = MODE_Line;
         3089  +    }else if( strcmp(z,"-column")==0 ){
         3090  +      data.mode = MODE_Column;
         3091  +    }else if( strcmp(z,"-csv")==0 ){
         3092  +      data.mode = MODE_Csv;
         3093  +      memcpy(data.separator,",",2);
         3094  +    }else if( strcmp(z,"-separator")==0 ){
         3095  +      sqlite3_snprintf(sizeof(data.separator), data.separator,
         3096  +                       "%s",cmdline_option_value(argc,argv,++i));
         3097  +    }else if( strcmp(z,"-nullvalue")==0 ){
         3098  +      sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
         3099  +                       "%s",cmdline_option_value(argc,argv,++i));
         3100  +    }else if( strcmp(z,"-header")==0 ){
         3101  +      data.showHeader = 1;
         3102  +    }else if( strcmp(z,"-noheader")==0 ){
         3103  +      data.showHeader = 0;
         3104  +    }else if( strcmp(z,"-echo")==0 ){
         3105  +      data.echoOn = 1;
         3106  +    }else if( strcmp(z,"-stats")==0 ){
         3107  +      data.statsOn = 1;
         3108  +    }else if( strcmp(z,"-bail")==0 ){
         3109  +      bail_on_error = 1;
         3110  +    }else if( strcmp(z,"-version")==0 ){
         3111  +      printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
         3112  +      return 0;
         3113  +    }else if( strcmp(z,"-interactive")==0 ){
         3114  +      stdin_is_interactive = 1;
         3115  +    }else if( strcmp(z,"-batch")==0 ){
         3116  +      stdin_is_interactive = 0;
         3117  +    }else if( strcmp(z,"-heap")==0 ){
         3118  +      i++;
         3119  +    }else if( strcmp(z,"-vfs")==0 ){
         3120  +      i++;
         3121  +#ifdef SQLITE_ENABLE_VFSTRACE
         3122  +    }else if( strcmp(z,"-vfstrace")==0 ){
         3123  +      i++;
         3124  +#endif
         3125  +#ifdef SQLITE_ENABLE_MULTIPLEX
         3126  +    }else if( strcmp(z,"-multiplex")==0 ){
         3127  +      i++;
         3128  +#endif
         3129  +    }else if( strcmp(z,"-help")==0 ){
         3130  +      usage(1);
         3131  +    }else if( strcmp(z,"-cmd")==0 ){
         3132  +      if( i==argc-1 ) break;
         3133  +      z = cmdline_option_value(argc,argv,++i);
         3134  +      if( z[0]=='.' ){
         3135  +        rc = do_meta_command(z, &data);
         3136  +        if( rc && bail_on_error ) return rc;
         3137  +      }else{
         3138  +        open_db(&data);
         3139  +        rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg);
         3140  +        if( zErrMsg!=0 ){
         3141  +          fprintf(stderr,"Error: %s\n", zErrMsg);
         3142  +          if( bail_on_error ) return rc!=0 ? rc : 1;
         3143  +        }else if( rc!=0 ){
         3144  +          fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z);
         3145  +          if( bail_on_error ) return rc;
         3146  +        }
         3147  +      }
         3148  +    }else{
         3149  +      fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
         3150  +      fprintf(stderr,"Use -help for a list of options.\n");
         3151  +      return 1;
         3152  +    }
         3153  +  }
         3154  +
         3155  +  if( zFirstCmd ){
         3156  +    /* Run just the command that follows the database name
         3157  +    */
         3158  +    if( zFirstCmd[0]=='.' ){
         3159  +      rc = do_meta_command(zFirstCmd, &data);
         3160  +    }else{
         3161  +      open_db(&data);
         3162  +      rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg);
         3163  +      if( zErrMsg!=0 ){
         3164  +        fprintf(stderr,"Error: %s\n", zErrMsg);
         3165  +        return rc!=0 ? rc : 1;
         3166  +      }else if( rc!=0 ){
         3167  +        fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd);
         3168  +        return rc;
         3169  +      }
         3170  +    }
         3171  +  }else{
         3172  +    /* Run commands received from standard input
         3173  +    */
         3174  +    if( stdin_is_interactive ){
         3175  +      char *zHome;
         3176  +      char *zHistory = 0;
         3177  +      int nHistory;
         3178  +      printf(
         3179  +        "SQLite version %s %.19s\n" /*extra-version-info*/
         3180  +        "Enter \".help\" for instructions\n"
         3181  +        "Enter SQL statements terminated with a \";\"\n",
         3182  +        sqlite3_libversion(), sqlite3_sourceid()
         3183  +      );
         3184  +      zHome = find_home_dir();
         3185  +      if( zHome ){
         3186  +        nHistory = strlen30(zHome) + 20;
         3187  +        if( (zHistory = malloc(nHistory))!=0 ){
         3188  +          sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
         3189  +        }
         3190  +      }
         3191  +#if defined(HAVE_READLINE) && HAVE_READLINE==1
         3192  +      if( zHistory ) read_history(zHistory);
         3193  +#endif
         3194  +      rc = process_input(&data, 0);
         3195  +      if( zHistory ){
         3196  +        stifle_history(100);
         3197  +        write_history(zHistory);
         3198  +        free(zHistory);
         3199  +      }
         3200  +    }else{
         3201  +      rc = process_input(&data, stdin);
         3202  +    }
         3203  +  }
         3204  +  set_table_name(&data, 0);
         3205  +  if( data.db ){
         3206  +    sqlite3_close(data.db);
         3207  +  }
         3208  +  return rc;
         3209  +}

Changes to src/shun.c.

    42     42     Stmt q;
    43     43     int cnt = 0;
    44     44     const char *zUuid = P("uuid");
    45     45     int nUuid;
    46     46     char zCanonical[UUID_SIZE+1];
    47     47   
    48     48     login_check_credentials();
    49         -  if( !g.okAdmin ){
           49  +  if( !g.perm.Admin ){
    50     50       login_needed();
    51     51     }
    52     52     if( P("rebuild") ){
    53         -    db_close();
           53  +    db_close(1);
    54     54       db_open_repository(g.zRepositoryName);
    55     55       db_begin_transaction();
    56         -    rebuild_db(0,0);
           56  +    rebuild_db(0, 0, 0);
    57     57       db_end_transaction(0);
    58     58     }
    59     59     if( zUuid ){
    60     60       nUuid = strlen(zUuid);
    61     61       if( nUuid!=40 || !validate16(zUuid, nUuid) ){
    62     62         zUuid = 0;
    63     63       }else{
................................................................................
    67     67       }
    68     68     }
    69     69     style_header("Shunned Artifacts");
    70     70     if( zUuid && P("sub") ){
    71     71       login_verify_csrf_secret();
    72     72       db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid);
    73     73       if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){
    74         -      @ <p><font color="blue">Artifact 
    75         -      @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no
    76         -      @ longer being shunned.</font></p>
           74  +      @ <p class="noMoreShun">Artifact 
           75  +      @ <a href="%s(g.zTop)/artifact/%s(zUuid)">%s(zUuid)</a> is no
           76  +      @ longer being shunned.</p>
    77     77       }else{
    78         -      @ <p><font color="blue">Artifact %s(zUuid)</a> will no longer
           78  +      @ <p class="noMoreShun">Artifact %s(zUuid) will no longer
    79     79         @ be shunned.  But it does not exist in the repository.  It
    80     80         @ may be necessary to rebuild the repository using the
    81     81         @ <b>fossil rebuild</b> command-line before the artifact content
    82         -      @ can pulled in from other respositories.</font></p>
           82  +      @ can pulled in from other repositories.</p>
    83     83       }
    84     84     }
    85     85     if( zUuid && P("add") ){
           86  +    int rid, tagid;
    86     87       login_verify_csrf_secret();
    87         -    db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
    88         -    @ <p><font color="blue">Artifact
    89         -    @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been
           88  +    db_multi_exec(
           89  +      "INSERT OR IGNORE INTO shun(uuid,mtime)"
           90  +      " VALUES('%s', now())", zUuid);
           91  +    @ <p class="shunned">Artifact
           92  +    @ <a href="%s(g.zTop)/artifact/%s(zUuid)">%s(zUuid)</a> has been
    90     93       @ shunned.  It will no longer be pushed.
    91         -    @ It will be removed from the repository the next time the respository
    92         -    @ is rebuilt using the <b>fossil rebuild</b> command-line</font></p>
           94  +    @ It will be removed from the repository the next time the repository
           95  +    @ is rebuilt using the <b>fossil rebuild</b> command-line</p>
           96  +    db_multi_exec("DELETE FROM attachment WHERE src=%Q", zUuid);
           97  +    rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid);
           98  +    if( rid ){
           99  +      db_multi_exec("DELETE FROM event WHERE objid=%d", rid);
          100  +    }
          101  +    tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", zUuid);
          102  +    if( tagid ){
          103  +      db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", zUuid);
          104  +      db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid);
          105  +      db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
          106  +    }
    93    107     }
    94    108     @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
    95    109     @ artifact content will be purged from the repository the next time the
    96    110     @ repository is rebuilt.  A list of shunned artifacts can be seen at the
    97    111     @ bottom of this page.</p>
    98    112     @ 
    99    113     @ <a name="addshun"></a>
................................................................................
   110    124     @ from the repository.  Inappropriate content includes such things as
   111    125     @ spam added to Wiki, files that violate copyright or patent agreements,
   112    126     @ or artifacts that by design or accident interfere with the processing
   113    127     @ of the repository.  Do not shun artifacts merely to remove them from
   114    128     @ sight - set the "hidden" tag on such artifacts instead.</p>
   115    129     @ 
   116    130     @ <blockquote>
   117         -  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
          131  +  @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
   118    132     login_insert_csrf_secret();
   119         -  @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
   120         -  @ <input type="submit" name="add" value="Shun">
   121         -  @ </form>
          133  +  @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" />
          134  +  @ <input type="submit" name="add" value="Shun" />
          135  +  @ </div></form>
   122    136     @ </blockquote>
   123    137     @
   124    138     @ <p>Enter the UUID of a previous shunned artifact to cause it to be
   125    139     @ accepted again in the repository.  The artifact content is not
   126    140     @ restored because the content is unknown.  The only change is that
   127    141     @ the formerly shunned artifact will be accepted on subsequent sync
   128    142     @ operations.</p>
   129    143     @
   130    144     @ <blockquote>
   131         -  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
          145  +  @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
   132    146     login_insert_csrf_secret();
   133         -  @ <input type="text" name="uuid" size="50">
   134         -  @ <input type="submit" name="sub" value="Accept">
   135         -  @ </form>
          147  +  @ <input type="text" name="uuid" size="50" />
          148  +  @ <input type="submit" name="sub" value="Accept" />
          149  +  @ </div></form>
   136    150     @ </blockquote>
   137    151     @
   138         -  @ <p>Press the Rebuild button below to rebuild the respository.  The
          152  +  @ <p>Press the Rebuild button below to rebuild the repository.  The
   139    153     @ content of newly shunned artifacts is not purged until the repository
   140    154     @ is rebuilt.  On larger repositories, the rebuild may take minute or
   141    155     @ two, so be patient after pressing the button.</p>
   142    156     @
   143    157     @ <blockquote>
   144         -  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
          158  +  @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
   145    159     login_insert_csrf_secret();
   146         -  @ <input type="submit" name="rebuild" value="Rebuild">
   147         -  @ </form>
          160  +  @ <input type="submit" name="rebuild" value="Rebuild" />
          161  +  @ </div></form>
   148    162     @ </blockquote>
   149    163     @ 
   150         -  @ <hr><p>Shunned Artifacts:</p>
   151         -  @ <blockquote>
          164  +  @ <hr /><p>Shunned Artifacts:</p>
          165  +  @ <blockquote><p>
   152    166     db_prepare(&q, 
   153    167        "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
   154    168        "  FROM shun ORDER BY uuid");
   155    169     while( db_step(&q)==SQLITE_ROW ){
   156    170       const char *zUuid = db_column_text(&q, 0);
   157    171       int stillExists = db_column_int(&q, 1);
   158    172       cnt++;
   159    173       if( stillExists ){
   160         -      @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
          174  +      @ <b><a href="%s(g.zTop)/artifact/%s(zUuid)">%s(zUuid)</a></b><br />
   161    175       }else{
   162         -      @ <b>%s(zUuid)</b><br>
          176  +      @ <b>%s(zUuid)</b><br />
   163    177       }
   164    178     }
   165    179     if( cnt==0 ){
   166    180       @ <i>no artifacts are shunned on this server</i>
   167    181     }
   168    182     db_finalize(&q);
   169         -  @ </blockquote>
          183  +  @ </p></blockquote>
   170    184     style_footer();
   171    185   }
   172    186   
   173    187   /*
   174    188   ** Remove from the BLOB table all artifacts that are in the SHUN table.
   175    189   */
   176    190   void shun_artifacts(void){
................................................................................
   203    217   */
   204    218   void rcvfromlist_page(void){
   205    219     int ofst = atoi(PD("ofst","0"));
   206    220     int cnt;
   207    221     Stmt q;
   208    222   
   209    223     login_check_credentials();
   210         -  if( !g.okAdmin ){
          224  +  if( !g.perm.Admin ){
   211    225       login_needed();
   212    226     }
   213    227     style_header("Content Sources");
   214    228     if( ofst>0 ){
   215    229       style_submenu_element("Newer", "Newer", "rcvfromlist?ofst=%d",
   216    230                              ofst>30 ? ofst-30 : 0);
   217    231     }
................................................................................
   227    241     @ finding and fixing attempts to inject illicit content into the
   228    242     @ repository.</p>
   229    243     @
   230    244     @ <p>Click on the "rcvid" to show a list of specific artifacts received
   231    245     @ by a transaction.  After identifying illicit artifacts, remove them
   232    246     @ using the "Shun" feature.</p>
   233    247     @
   234         -  @ <table cellpadding=0 cellspacing=0 border=0>
   235         -  @ <tr><th>rcvid</th><th width=15>
   236         -  @     <th>Date</th><th width=15><th>User</th>
   237         -  @     <th width=15><th>IP&nbsp;Address</th></tr>
          248  +  @ <table cellpadding="0" cellspacing="0" border="0">
          249  +  @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th>
          250  +  @     <th style="padding-right: 15px;text-align: left;">Date</th>
          251  +  @     <th style="padding-right: 15px;text-align: left;">User</th>
          252  +  @     <th style="text-align: left;">IP&nbsp;Address</th></tr>
   238    253     cnt = 0;
   239    254     while( db_step(&q)==SQLITE_ROW ){
   240    255       int rcvid = db_column_int(&q, 0);
   241    256       const char *zUser = db_column_text(&q, 1);
   242    257       const char *zDate = db_column_text(&q, 2);
   243    258       const char *zIpAddr = db_column_text(&q, 3);
   244    259       if( cnt==30 ){
   245    260         style_submenu_element("Older", "Older",
   246    261            "rcvfromlist?ofst=%d", ofst+30);
   247    262       }else{
   248    263         cnt++;
   249    264         @ <tr>
   250         -      @ <td><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td><td>
   251         -      @ <td>%s(zDate)</td><td>
   252         -      @ <td>%h(zUser)</td><td>
   253         -      @ <td>&nbsp;%s(zIpAddr)&nbsp</td>
          265  +      @ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td>
          266  +      @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td>
          267  +      @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td>
          268  +      @ <td style="text-align: left;">%s(zIpAddr)</td>
   254    269         @ </tr>
   255    270       }
   256    271     }
   257    272     db_finalize(&q);
   258    273     @ </table>
   259    274     style_footer();
   260    275   }
................................................................................
   265    280   ** Show a single RCVFROM table entry.
   266    281   */
   267    282   void rcvfrom_page(void){
   268    283     int rcvid = atoi(PD("rcvid","0"));
   269    284     Stmt q;
   270    285   
   271    286     login_check_credentials();
   272         -  if( !g.okAdmin ){
          287  +  if( !g.perm.Admin ){
   273    288       login_needed();
   274    289     }
   275    290     style_header("Content Source %d", rcvid);
   276    291     db_prepare(&q, 
   277    292       "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
   278    293       "  FROM rcvfrom LEFT JOIN user USING(uid)"
   279    294       " WHERE rcvid=%d",
   280    295       rcvid
   281    296     );
   282         -  @ <table cellspacing=15 cellpadding=0 border=0>
          297  +  @ <table cellspacing="15" cellpadding="0" border="0">
   283    298     @ <tr><td valign="top" align="right"><b>rcvid:</b></td>
   284    299     @ <td valign="top">%d(rcvid)</td></tr>
   285    300     if( db_step(&q)==SQLITE_ROW ){
   286    301       const char *zUser = db_column_text(&q, 0);
   287    302       const char *zDate = db_column_text(&q, 1);
   288    303       const char *zIpAddr = db_column_text(&q, 2);
   289    304       @ <tr><td valign="top" align="right"><b>User:</b></td>
................................................................................
   299    314     );
   300    315     @ <tr><td valign="top" align="right"><b>Artifacts:</b></td>
   301    316     @ <td valign="top">
   302    317     while( db_step(&q)==SQLITE_ROW ){
   303    318       int rid = db_column_int(&q, 0);
   304    319       const char *zUuid = db_column_text(&q, 1);
   305    320       int size = db_column_int(&q, 2);
   306         -    @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
   307         -    @ (rid: %d(rid), size: %d(size))<br>
          321  +    @ <a href="%s(g.zTop)/info/%s(zUuid)">%s(zUuid)</a>
          322  +    @ (rid: %d(rid), size: %d(size))<br />
   308    323     }
   309    324     @ </td></tr>
   310    325     @ </table>
          326  +  db_finalize(&q);
          327  +  style_footer();
   311    328   }

Changes to src/skins.c.

    17     17   **
    18     18   ** Implementation of the Setup page for "skins".
    19     19   */
    20     20   #include <assert.h>
    21     21   #include "config.h"
    22     22   #include "skins.h"
    23     23   
    24         -/* @-comment: // */
           24  +/* @-comment: ## */
    25     25   /*
    26     26   ** A black-and-white theme with the project title in a bar across the top
    27     27   ** and no logo image.
    28     28   */
    29         -static const char zBuiltinSkin1[] = 
    30         -@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
           29  +static const char zBuiltinSkin1[] =
           30  +@ REPLACE INTO config(name,mtime,value)
           31  +@ VALUES('css',now(),'/* General settings for the entire page */
    31     32   @ body {
    32     33   @   margin: 0ex 1ex;
    33     34   @   padding: 0px;
    34     35   @   background-color: white;
    35         -@   font-family: "sans serif";
           36  +@   font-family: sans-serif;
    36     37   @ }
    37         -@ 
           38  +@
    38     39   @ /* The project logo in the upper left-hand corner of each page */
    39     40   @ div.logo {
    40     41   @   display: table-row;
    41     42   @   text-align: center;
    42     43   @   /* vertical-align: bottom;*/
    43     44   @   font-size: 2em;
    44     45   @   font-weight: bold;
    45     46   @   background-color: #707070;
    46     47   @   color: #ffffff;
           48  +@   min-width: 200px;
           49  +@   white-space: nowrap;
    47     50   @ }
    48         -@ 
           51  +@
    49     52   @ /* The page title centered at the top of each page */
    50     53   @ div.title {
    51     54   @   display: table-cell;
    52     55   @   font-size: 1.5em;
    53     56   @   font-weight: bold;
    54         -@   text-align: left;
           57  +@   text-align: center;
    55     58   @   padding: 0 0 0 10px;
    56     59   @   color: #404040;
    57     60   @   vertical-align: bottom;
    58     61   @   width: 100%;
    59     62   @ }
    60         -@ 
           63  +@
    61     64   @ /* The login status message in the top right-hand corner */
    62     65   @ div.status {
    63     66   @   display: table-cell;
    64     67   @   text-align: right;
    65     68   @   vertical-align: bottom;
    66     69   @   color: #404040;
    67     70   @   font-size: 0.8em;
    68     71   @   font-weight: bold;
           72  +@   min-width: 200px;
           73  +@   white-space: nowrap;
    69     74   @ }
    70         -@ 
           75  +@
    71     76   @ /* The header across the top of the page */
    72     77   @ div.header {
    73     78   @   display: table;
    74     79   @   width: 100%;
    75     80   @ }
    76         -@ 
           81  +@
    77     82   @ /* The main menu bar that appears at the top of the page beneath
    78     83   @ ** the header */
    79     84   @ div.mainmenu {
    80     85   @   padding: 5px 10px 5px 10px;
    81     86   @   font-size: 0.9em;
    82     87   @   font-weight: bold;
    83     88   @   text-align: center;
    84     89   @   letter-spacing: 1px;
    85     90   @   background-color: #404040;
    86     91   @   color: white;
    87     92   @ }
    88         -@ 
           93  +@
    89     94   @ /* The submenu bar that *sometimes* appears below the main menu */
    90         -@ div.submenu {
           95  +@ div.submenu, div.sectionmenu {
    91     96   @   padding: 3px 10px 3px 0px;
    92     97   @   font-size: 0.9em;
    93     98   @   text-align: center;
    94     99   @   background-color: #606060;
    95    100   @   color: white;
    96    101   @ }
    97         -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
          102  +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
          103  +@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
    98    104   @   padding: 3px 10px 3px 10px;
    99    105   @   color: white;
   100    106   @   text-decoration: none;
   101    107   @ }
   102         -@ div.mainmenu a:hover, div.submenu a:hover {
          108  +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
   103    109   @   color: #404040;
   104    110   @   background-color: white;
   105    111   @ }
   106         -@ 
          112  +@
   107    113   @ /* All page content from the bottom of the menu or submenu down to
   108    114   @ ** the footer */
   109    115   @ div.content {
   110    116   @   padding: 0ex 0ex 0ex 0ex;
   111    117   @ }
   112    118   @ /* Hyperlink colors */
   113    119   @ div.content a { color: #604000; }
   114    120   @ div.content a:link { color: #604000;}
   115    121   @ div.content a:visited { color: #600000; }
   116         -@ 
          122  +@
   117    123   @ /* Some pages have section dividers */
   118    124   @ div.section {
   119    125   @   margin-bottom: 0px;
   120    126   @   margin-top: 1em;
   121    127   @   padding: 1px 1px 1px 1px;
   122    128   @   font-size: 1.2em;
   123    129   @   font-weight: bold;
   124    130   @   background-color: #404040;
   125    131   @   color: white;
          132  +@   white-space: nowrap;
   126    133   @ }
   127         -@ 
          134  +@
   128    135   @ /* The "Date" that occurs on the left hand side of timelines */
   129    136   @ div.divider {
   130    137   @   background: #a0a0a0;
   131    138   @   border: 2px #505050 solid;
   132    139   @   font-size: 1em; font-weight: normal;
   133    140   @   padding: .25em;
   134    141   @   margin: .2em 0 .2em 0;
   135    142   @   float: left;
   136    143   @   clear: left;
          144  +@   white-space: nowrap;
   137    145   @ }
   138         -@ 
          146  +@
   139    147   @ /* The footer at the very bottom of the page */
   140    148   @ div.footer {
   141    149   @   font-size: 0.8em;
   142    150   @   margin-top: 12px;
   143    151   @   padding: 5px 10px 5px 10px;
   144    152   @   text-align: right;
   145    153   @   background-color: #404040;
   146    154   @   color: white;
   147    155   @ }
   148         -@ 
          156  +@
   149    157   @ /* The label/value pairs on (for example) the vinfo page */
   150    158   @ table.label-value th {
   151    159   @   vertical-align: top;
   152    160   @   text-align: right;
   153    161   @   padding: 0.2ex 2ex;
   154    162   @ }');
   155         -@ REPLACE INTO config VALUES('header','<html>
          163  +@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   156    164   @ <head>
          165  +@ <base href="$baseurl/$current_page" />
   157    166   @ <title>$<project_name>: $<title></title>
   158    167   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   159         -@       href="$baseurl/timeline.rss">
   160         -@ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css"
          168  +@       href="$home/timeline.rss">
          169  +@ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
   161    170   @       media="screen">
   162    171   @ </head>
   163    172   @ <body>
   164    173   @ <div class="header">
   165         -@   <div class="logo">
   166         -@     <nobr>$<project_name></nobr>
   167         -@   </div>
   168         -@ </div>
   169         -@ <div class="header">
   170         -@   <div class="title">$<title></div>
   171         -@   <div class="status"><nobr><th1>
          174  +@   <div class="title"><small>$<project_name></small><br />$<title></div>
          175  +@   <div class="status"><th1>
   172    176   @      if {[info exists login]} {
   173    177   @        puts "Logged in as $login"
   174    178   @      } else {
   175    179   @        puts "Not logged in"
   176    180   @      }
   177         -@   </th1></nobr></div>
          181  +@   </th1></div>
   178    182   @ </div>
   179         -@ <div class="mainmenu"><th1>
   180         -@ html "<a href=''$baseurl$index_page''>Home</a> "
          183  +@ <div class="mainmenu">
          184  +@ <th1>
          185  +@ html "<a href=''$home$index_page''>Home</a>\n"
   181    186   @ if {[anycap jor]} {
   182         -@   html "<a href=''$baseurl/timeline''>Timeline</a> "
          187  +@   html "<a href=''$home/timeline''>Timeline</a>\n"
   183    188   @ }
   184    189   @ if {[hascap oh]} {
   185         -@   html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
          190  +@   html "<a href=''$home/dir?ci=tip''>Files</a>\n"
   186    191   @ }
   187    192   @ if {[hascap o]} {
   188         -@   html "<a href=''$baseurl/leaves''>Leaves</a> "
   189         -@   html "<a href=''$baseurl/brlist''>Branches</a> "
   190         -@   html "<a href=''$baseurl/taglist''>Tags</a> "
          193  +@   html "<a href=''$home/brlist''>Branches</a>\n"
          194  +@   html "<a href=''$home/taglist''>Tags</a>\n"
   191    195   @ }
   192    196   @ if {[hascap r]} {
   193         -@   html "<a href=''$baseurl/reportlist''>Tickets</a> "
          197  +@   html "<a href=''$home/reportlist''>Tickets</a>\n"
   194    198   @ }
   195    199   @ if {[hascap j]} {
   196         -@   html "<a href=''$baseurl/wiki''>Wiki</a> "
          200  +@   html "<a href=''$home/wiki''>Wiki</a>\n"
   197    201   @ }
   198    202   @ if {[hascap s]} {
   199         -@   html "<a href=''$baseurl/setup''>Admin</a> "
          203  +@   html "<a href=''$home/setup''>Admin</a>\n"
   200    204   @ } elseif {[hascap a]} {
   201         -@   html "<a href=''$baseurl/setup_ulist''>Users</a> "
          205  +@   html "<a href=''$home/setup_ulist''>Users</a>\n"
   202    206   @ }
   203    207   @ if {[info exists login]} {
   204         -@   html "<a href=''$baseurl/login''>Logout</a> "
          208  +@   html "<a href=''$home/login''>Logout</a>\n"
   205    209   @ } else {
   206         -@   html "<a href=''$baseurl/login''>Login</a> "
          210  +@   html "<a href=''$home/login''>Login</a>\n"
   207    211   @ }
   208    212   @ </th1></div>
   209    213   @ ');
   210         -@ REPLACE INTO config VALUES('footer','<div class="footer">
   211         -@ Fossil version $manifest_version $manifest_date 
          214  +@ REPLACE INTO config(name,mtime,value)
          215  +@ VALUES('footer',now(),'<div class="footer">
          216  +@ Fossil version $manifest_version $manifest_date
   212    217   @ </div>
   213    218   @ </body></html>
   214    219   @ ');
   215    220   ;
   216    221   
   217    222   /*
   218    223   ** A tan theme with the project title above the user identification
   219    224   ** and no logo image.
   220    225   */
   221         -static const char zBuiltinSkin2[] = 
   222         -@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
          226  +static const char zBuiltinSkin2[] =
          227  +@ REPLACE INTO config(name,mtime,value)
          228  +@ VALUES('css',now(),'/* General settings for the entire page */
   223    229   @ body {
   224    230   @   margin: 0ex 0ex;
   225    231   @   padding: 0px;
   226    232   @   background-color: #fef3bc;
   227    233   @   font-family: sans-serif;
   228    234   @ }
   229         -@ 
          235  +@
   230    236   @ /* The project logo in the upper left-hand corner of each page */
   231    237   @ div.logo {
   232    238   @   display: inline;
   233    239   @   text-align: center;
   234    240   @   vertical-align: bottom;
   235    241   @   font-weight: bold;
   236    242   @   font-size: 2.5em;
   237    243   @   color: #a09048;
          244  +@   white-space: nowrap;
   238    245   @ }
   239         -@ 
          246  +@
   240    247   @ /* The page title centered at the top of each page */
   241    248   @ div.title {
   242    249   @   display: table-cell;
   243    250   @   font-size: 2em;
   244    251   @   font-weight: bold;
   245    252   @   text-align: left;
   246    253   @   padding: 0 0 0 5px;
   247    254   @   color: #a09048;
   248    255   @   vertical-align: bottom;
   249    256   @   width: 100%;
   250    257   @ }
   251         -@ 
          258  +@
   252    259   @ /* The login status message in the top right-hand corner */
   253    260   @ div.status {
   254    261   @   display: table-cell;
   255    262   @   text-align: right;
   256    263   @   vertical-align: bottom;
   257    264   @   color: #a09048;
   258    265   @   padding: 5px 5px 0 0;
   259    266   @   font-size: 0.8em;
   260    267   @   font-weight: bold;
          268  +@   white-space: nowrap;
   261    269   @ }
   262         -@ 
          270  +@
   263    271   @ /* The header across the top of the page */
   264    272   @ div.header {
   265    273   @   display: table;
   266    274   @   width: 100%;
   267    275   @ }
   268         -@ 
          276  +@
   269    277   @ /* The main menu bar that appears at the top of the page beneath
   270    278   @ ** the header */
   271    279   @ div.mainmenu {
   272    280   @   padding: 5px 10px 5px 10px;
   273    281   @   font-size: 0.9em;
   274    282   @   font-weight: bold;
   275    283   @   text-align: center;
   276    284   @   letter-spacing: 1px;
   277    285   @   background-color: #a09048;
   278    286   @   color: black;
   279    287   @ }
   280         -@ 
          288  +@
   281    289   @ /* The submenu bar that *sometimes* appears below the main menu */
   282         -@ div.submenu {
          290  +@ div.submenu, div.sectionmenu {
   283    291   @   padding: 3px 10px 3px 0px;
   284    292   @   font-size: 0.9em;
   285    293   @   text-align: center;
   286    294   @   background-color: #c0af58;
   287    295   @   color: white;
   288    296   @ }
   289         -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
          297  +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
          298  +@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
   290    299   @   padding: 3px 10px 3px 10px;
   291    300   @   color: white;
   292    301   @   text-decoration: none;
   293    302   @ }
   294         -@ div.mainmenu a:hover, div.submenu a:hover {
          303  +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
   295    304   @   color: #a09048;
   296    305   @   background-color: white;
   297    306   @ }
   298         -@ 
          307  +@
   299    308   @ /* All page content from the bottom of the menu or submenu down to
   300    309   @ ** the footer */
   301    310   @ div.content {
   302    311   @   padding: 1ex 5px;
   303    312   @ }
   304    313   @ div.content a { color: #706532; }
   305    314   @ div.content a:link { color: #706532; }
   306    315   @ div.content a:visited { color: #704032; }
   307    316   @ div.content a:hover { background-color: white; color: #706532; }
   308         -@ 
          317  +@
   309    318   @ /* Some pages have section dividers */
   310    319   @ div.section {
   311    320   @   margin-bottom: 0px;
   312    321   @   margin-top: 1em;
   313    322   @   padding: 3px 3px 0 3px;
   314    323   @   font-size: 1.2em;
   315    324   @   font-weight: bold;
   316    325   @   background-color: #a09048;
   317    326   @   color: white;
          327  +@   white-space: nowrap;
   318    328   @ }
   319         -@ 
          329  +@
   320    330   @ /* The "Date" that occurs on the left hand side of timelines */
   321    331   @ div.divider {
   322    332   @   background: #e1d498;
   323    333   @   border: 2px #a09048 solid;
   324    334   @   font-size: 1em; font-weight: normal;
   325    335   @   padding: .25em;
   326    336   @   margin: .2em 0 .2em 0;
   327    337   @   float: left;
   328    338   @   clear: left;
          339  +@   white-space: nowrap;
   329    340   @ }
   330         -@ 
          341  +@
   331    342   @ /* The footer at the very bottom of the page */
   332    343   @ div.footer {
   333    344   @   font-size: 0.8em;
   334    345   @   margin-top: 12px;
   335    346   @   padding: 5px 10px 5px 10px;
   336    347   @   text-align: right;
   337    348   @   background-color: #a09048;
   338    349   @   color: white;
   339    350   @ }
   340         -@ 
          351  +@
   341    352   @ /* Hyperlink colors */
   342    353   @ div.footer a { color: white; }
   343    354   @ div.footer a:link { color: white; }
   344    355   @ div.footer a:visited { color: white; }
   345    356   @ div.footer a:hover { background-color: white; color: #558195; }
   346         -@ 
          357  +@
   347    358   @ /* <verbatim> blocks */
   348    359   @ pre.verbatim {
   349         -@    background-color: #f5f5f5;
   350         -@    padding: 0.5em;
          360  +@   background-color: #f5f5f5;
          361  +@   padding: 0.5em;
          362  +@   white-space: pre-wrap;
   351    363   @ }
   352         -@ 
          364  +@
   353    365   @ /* The label/value pairs on (for example) the ci page */
   354    366   @ table.label-value th {
   355    367   @   vertical-align: top;
   356    368   @   text-align: right;
   357    369   @   padding: 0.2ex 2ex;
   358         -@ }
   359         -@ ');
   360         -@ REPLACE INTO config VALUES('header','<html>
          370  +@ }');
          371  +@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   361    372   @ <head>
          373  +@ <base href="$baseurl/$current_page" />
   362    374   @ <title>$<project_name>: $<title></title>
   363    375   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   364         -@       href="$baseurl/timeline.rss">
   365         -@ <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css"
          376  +@       href="$home/timeline.rss">
          377  +@ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
   366    378   @       media="screen">
   367    379   @ </head>
   368    380   @ <body>
   369    381   @ <div class="header">
   370    382   @   <div class="title">$<title></div>
   371    383   @   <div class="status">
   372         -@     <div class="logo"><nobr>$<project_name></nobr></div><br/>
   373         -@     <nobr><th1>
          384  +@     <div class="logo">$<project_name></div><br/>
          385  +@     <th1>
   374    386   @      if {[info exists login]} {
   375    387   @        puts "Logged in as $login"
   376    388   @      } else {
   377    389   @        puts "Not logged in"
   378    390   @      }
   379         -@   </th1></nobr></div>
          391  +@   </th1></div>
   380    392   @ </div>
   381         -@ <div class="mainmenu"><th1>
   382         -@ html "<a href=''$baseurl$index_page''>Home</a> "
          393  +@ <div class="mainmenu">
          394  +@ <th1>
          395  +@ html "<a href=''$home$index_page''>Home</a>\n"
   383    396   @ if {[anycap jor]} {
   384         -@   html "<a href=''$baseurl/timeline''>Timeline</a> "
          397  +@   html "<a href=''$home/timeline''>Timeline</a>\n"
   385    398   @ }
   386    399   @ if {[hascap oh]} {
   387         -@   html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
          400  +@   html "<a href=''$home/dir?ci=tip''>Files</a>\n"
   388    401   @ }
   389    402   @ if {[hascap o]} {
   390         -@   html "<a href=''$baseurl/leaves''>Leaves</a> "
   391         -@   html "<a href=''$baseurl/brlist''>Branches</a> "
   392         -@   html "<a href=''$baseurl/taglist''>Tags</a> "
          403  +@   html "<a href=''$home/brlist''>Branches</a>\n"
          404  +@   html "<a href=''$home/taglist''>Tags</a>\n"
   393    405   @ }
   394    406   @ if {[hascap r]} {
   395         -@   html "<a href=''$baseurl/reportlist''>Tickets</a> "
          407  +@   html "<a href=''$home/reportlist''>Tickets</a>\n"
   396    408   @ }
   397    409   @ if {[hascap j]} {
   398         -@   html "<a href=''$baseurl/wiki''>Wiki</a> "
          410  +@   html "<a href=''$home/wiki''>Wiki</a>\n"
   399    411   @ }
   400    412   @ if {[hascap s]} {
   401         -@   html "<a href=''$baseurl/setup''>Admin</a> "
          413  +@   html "<a href=''$home/setup''>Admin</a>\n"
   402    414   @ } elseif {[hascap a]} {
   403         -@   html "<a href=''$baseurl/setup_ulist''>Users</a> "
          415  +@   html "<a href=''$home/setup_ulist''>Users</a>\n"
   404    416   @ }
   405    417   @ if {[info exists login]} {
   406         -@   html "<a href=''$baseurl/login''>Logout</a> "
          418  +@   html "<a href=''$home/login''>Logout</a>\n"
   407    419   @ } else {
   408         -@   html "<a href=''$baseurl/login''>Login</a> "
          420  +@   html "<a href=''$home/login''>Login</a>\n"
   409    421   @ }
   410    422   @ </th1></div>
   411    423   @ ');
   412         -@ REPLACE INTO config VALUES('footer','<div class="footer">
          424  +@ REPLACE INTO config(name,mtime,value)
          425  +@ VALUES('footer',now(),'<div class="footer">
   413    426   @ Fossil version $manifest_version $manifest_date
   414    427   @ </div>
   415    428   @ </body></html>
   416    429   @ ');
   417    430   ;
   418    431   
   419    432   /*
   420    433   ** Black letters on a white or cream background with the main menu
   421    434   ** stuck on the left-hand side.
   422    435   */
   423         -static const char zBuiltinSkin3[] = 
   424         -@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
          436  +static const char zBuiltinSkin3[] =
          437  +@ REPLACE INTO config(name,mtime,value)
          438  +@ VALUES('css',now(),'/* General settings for the entire page */
   425    439   @ body {
   426    440   @     margin:0px 0px 0px 0px;
   427    441   @     padding:0px;
   428    442   @     font-family:verdana, arial, helvetica, "sans serif";
   429    443   @     color:#333;
   430    444   @     background-color:white;
   431    445   @ }
   432         -@ 
          446  +@
   433    447   @ /* consistent colours */
   434    448   @ h2 {
   435    449   @   color: #333;
   436    450   @ }
   437    451   @ h3 {
   438    452   @   color: #333;
   439    453   @ }
   440         -@ 
          454  +@
   441    455   @ /* The project logo in the upper left-hand corner of each page */
   442    456   @ div.logo {
   443    457   @   display: table-cell;
   444    458   @   text-align: left;
   445    459   @   vertical-align: bottom;
   446    460   @   font-weight: bold;
   447    461   @   color: #333;
          462  +@   white-space: nowrap;
   448    463   @ }
   449         -@ 
          464  +@
   450    465   @ /* The page title centered at the top of each page */
   451    466   @ div.title {
   452    467   @   display: table-cell;
   453    468   @   font-size: 2em;
   454    469   @   font-weight: bold;
   455    470   @   text-align: center;
   456    471   @   color: #333;
   457    472   @   vertical-align: bottom;
   458    473   @   width: 100%;
   459    474   @ }
   460         -@ 
          475  +@
   461    476   @ /* The login status message in the top right-hand corner */
   462    477   @ div.status {
   463    478   @   display: table-cell;
   464    479   @   padding-right: 10px;
   465    480   @   text-align: right;
   466    481   @   vertical-align: bottom;
   467    482   @   padding-bottom: 5px;
   468    483   @   color: #333;
   469    484   @   font-size: 0.8em;
   470    485   @   font-weight: bold;
          486  +@   white-space: nowrap;
   471    487   @ }
   472         -@ 
          488  +@
   473    489   @ /* The header across the top of the page */
   474    490   @ div.header {
   475    491   @     margin:10px 0px 10px 0px;
   476    492   @     padding:1px 0px 0px 20px;
   477    493   @     border-style:solid;
   478    494   @     border-color:black;
   479    495   @     border-width:1px 0px;
   480    496   @     background-color:#eee;
   481    497   @ }
   482         -@ 
          498  +@
   483    499   @ /* The main menu bar that appears at the top left of the page beneath
   484    500   @ ** the header. Width must be co-ordinated with the container below */
   485    501   @ div.mainmenu {
   486    502   @   float: left;
   487    503   @   margin-left: 10px;
   488    504   @   margin-right: 10px;
   489    505   @   font-size: 0.9em;
   490    506   @   font-weight: bold;
   491    507   @   padding:5px;
   492    508   @   background-color:#eee;
   493    509   @   border:1px solid #999;
   494    510   @   width:8em;
   495    511   @ }
   496         -@ 
          512  +@
   497    513   @ /* Main menu is now a list */
   498    514   @ div.mainmenu ul {
   499    515   @   padding: 0;
   500    516   @   list-style:none;
   501    517   @ }
   502    518   @ div.mainmenu a, div.mainmenu a:visited{
   503    519   @   padding: 1px 10px 1px 10px;
................................................................................
   504    520   @   color: #333;
   505    521   @   text-decoration: none;
   506    522   @ }
   507    523   @ div.mainmenu a:hover {
   508    524   @   color: #eee;
   509    525   @   background-color: #333;
   510    526   @ }
   511         -@ 
          527  +@
   512    528   @ /* Container for the sub-menu and content so they don''t spread
   513    529   @ ** out underneath the main menu */
   514    530   @ #container {
   515    531   @   padding-left: 9em;
   516    532   @ }
   517         -@ 
          533  +@
   518    534   @ /* The submenu bar that *sometimes* appears below the main menu */
   519         -@ div.submenu {
          535  +@ div.submenu, div.sectionmenu {
   520    536   @   padding: 3px 10px 3px 10px;
   521    537   @   font-size: 0.9em;
   522    538   @   text-align: center;
   523    539   @   border:1px solid #999;
   524    540   @   border-width:1px 0px;
   525    541   @   background-color: #eee;
   526    542   @   color: #333;
   527    543   @ }
   528         -@ div.submenu a, div.submenu a:visited {
          544  +@ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link,
          545  +@ div.sectionmenu>a.button:visited {
   529    546   @   padding: 3px 10px 3px 10px;
   530    547   @   color: #333;
   531    548   @   text-decoration: none;
   532    549   @ }
   533         -@ div.submenu a:hover {
          550  +@ div.submenu a:hover, div.sectionmenu>a.button:hover {
   534    551   @   color: #eee;
   535    552   @   background-color: #333;
   536    553   @ }
   537         -@ 
          554  +@
   538    555   @ /* All page content from the bottom of the menu or submenu down to
   539    556   @ ** the footer */
   540    557   @ div.content {
   541         -@   float right;
   542    558   @   padding: 2ex 1ex 0ex 2ex;
   543    559   @ }
   544         -@ 
          560  +@
   545    561   @ /* Some pages have section dividers */
   546    562   @ div.section {
   547    563   @   margin-bottom: 0px;
   548    564   @   margin-top: 1em;
   549    565   @   padding: 1px 1px 1px 1px;
   550    566   @   font-size: 1.2em;
   551    567   @   font-weight: bold;
   552    568   @   border-style:solid;
   553    569   @   border-color:#999;
   554    570   @   border-width:1px 0px;
   555    571   @   background-color: #eee;
   556    572   @   color: #333;
          573  +@   white-space: nowrap;
   557    574   @ }
   558         -@ 
          575  +@
   559    576   @ /* The "Date" that occurs on the left hand side of timelines */
   560    577   @ div.divider {
   561    578   @   background: #eee;
   562    579   @   border: 2px #999 solid;
   563    580   @   font-size: 1em; font-weight: normal;
   564    581   @   padding: .25em;
   565    582   @   margin: .2em 0 .2em 0;
   566    583   @   float: left;
   567    584   @   clear: left;
   568         -@   color: #333
          585  +@   color: #333;
          586  +@   white-space: nowrap;
   569    587   @ }
   570         -@ 
          588  +@
   571    589   @ /* The footer at the very bottom of the page */
   572    590   @ div.footer {
   573    591   @   font-size: 0.8em;
   574    592   @   margin-top: 12px;
   575    593   @   padding: 5px 10px 5px 10px;
   576    594   @   text-align: right;
   577    595   @   background-color: #eee;
   578    596   @   color: #555;
   579    597   @ }
   580         -@ 
          598  +@
   581    599   @ /* <verbatim> blocks */
   582    600   @ pre.verbatim {
   583         -@    background-color: #f5f5f5;
   584         -@    padding: 0.5em;
   585         -@ }
   586         -@ 
          601  +@   background-color: #f5f5f5;
          602  +@   padding: 0.5em;
          603  +@   white-space: pre-wrap;
          604  +@ }
          605  +@
          606  +@ /* The label/value pairs on (for example) the ci page */
          607  +@ table.label-value th {
          608  +@   vertical-align: top;
          609  +@   text-align: right;
          610  +@   padding: 0.2ex 2ex;
          611  +@ }');
          612  +@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
          613  +@ <head>
          614  +@ <base href="$baseurl/$current_page" />
          615  +@ <title>$<project_name>: $<title></title>
          616  +@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
          617  +@       href="$home/timeline.rss">
          618  +@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
          619  +@       media="screen">
          620  +@ </head>
          621  +@ <body>
          622  +@ <div class="header">
          623  +@   <div class="logo">
          624  +@     <img src="$home/logo" alt="logo">
          625  +@     <br />$<project_name>
          626  +@   </div>
          627  +@   <div class="title">$<title></div>
          628  +@   <div class="status"><th1>
          629  +@      if {[info exists login]} {
          630  +@        puts "Logged in as $login"
          631  +@      } else {
          632  +@        puts "Not logged in"
          633  +@      }
          634  +@   </th1></div>
          635  +@ </div>
          636  +@ <div class="mainmenu">
          637  +@ <th1>
          638  +@ html "<a href=''$home$index_page''>Home</a>\n"
          639  +@ if {[anycap jor]} {
          640  +@   html "<a href=''$home/timeline''>Timeline</a>\n"
          641  +@ }
          642  +@ if {[hascap oh]} {
          643  +@   html "<a href=''$home/dir?ci=tip''>Files</a>\n"
          644  +@ }
          645  +@ if {[hascap o]} {
          646  +@   html "<a href=''$home/brlist''>Branches</a>\n"
          647  +@   html "<a href=''$home/taglist''>Tags</a>\n"
          648  +@ }
          649  +@ if {[hascap r]} {
          650  +@   html "<a href=''$home/reportlist''>Tickets</a>\n"
          651  +@ }
          652  +@ if {[hascap j]} {
          653  +@   html "<a href=''$home/wiki''>Wiki</a>\n"
          654  +@ }
          655  +@ if {[hascap s]} {
          656  +@   html "<a href=''$home/setup''>Admin</a>\n"
          657  +@ } elseif {[hascap a]} {
          658  +@   html "<a href=''$home/setup_ulist''>Users</a>\n"
          659  +@ }
          660  +@ if {[info exists login]} {
          661  +@   html "<a href=''$home/login''>Logout</a>\n"
          662  +@ } else {
          663  +@   html "<a href=''$home/login''>Login</a>\n"
          664  +@ }
          665  +@ </th1></ul></div>
          666  +@ <div id="container">
          667  +@ ');
          668  +@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
          669  +@ <div class="footer">
          670  +@ Fossil version $manifest_version $manifest_date
          671  +@ </div>
          672  +@ </body></html>
          673  +@ ');
          674  +;
          675  +
          676  +
          677  +/*
          678  +** Shadow boxes and rounded corners.
          679  +*/
          680  +static const char zBuiltinSkin4[] =
          681  +@ REPLACE INTO config(name,mtime,value)
          682  +@ VALUES('css',now(),'/* General settings for the entire page */
          683  +@ html {
          684  +@   min-height: 100%;
          685  +@ }
          686  +@ body {
          687  +@   margin: 0ex 1ex;
          688  +@   padding: 0px;
          689  +@   background-color: white;
          690  +@   color: #333;
          691  +@   font-family: Verdana, sans-serif;
          692  +@   font-size: 0.8em;
          693  +@ }
          694  +@
          695  +@ /* The project logo in the upper left-hand corner of each page */
          696  +@ div.logo {
          697  +@   display: table-cell;
          698  +@   text-align: right;
          699  +@   vertical-align: bottom;
          700  +@   font-weight: normal;
          701  +@   white-space: nowrap;
          702  +@ }
          703  +@
          704  +@ /* Widths */
          705  +@ div.header, div.mainmenu, div.submenu, div.content, div.footer {
          706  +@   max-width: 900px;
          707  +@   margin: auto;
          708  +@   padding: 3px 20px 3px 20px;
          709  +@   clear: both;
          710  +@ }
          711  +@
          712  +@ /* The page title at the top of each page */
          713  +@ div.title {
          714  +@   display: table-cell;
          715  +@   padding-left: 10px;
          716  +@   font-size: 2em;
          717  +@   margin: 10px 0 10px -20px;
          718  +@   vertical-align: bottom;
          719  +@   text-align: left;
          720  +@   width: 80%;
          721  +@   font-family: Verdana, sans-serif;
          722  +@   font-weight: bold;
          723  +@   color: #558195;
          724  +@   text-shadow: 0px 2px 2px #999999;
          725  +@ }
          726  +@
          727  +@ /* The login status message in the top right-hand corner */
          728  +@ div.status {
          729  +@   display: table-cell;
          730  +@   text-align: right;
          731  +@   vertical-align: bottom;
          732  +@   color: #333;
          733  +@   margin-right: -20px;
          734  +@   white-space: nowrap;
          735  +@ }
          736  +@
          737  +@ /* The main menu bar that appears at the top of the page beneath
          738  +@  ** the header */
          739  +@ div.mainmenu {
          740  +@   text-align: center;
          741  +@   color: white;
          742  +@   border-top-left-radius: 5px;
          743  +@   border-top-right-radius: 5px;
          744  +@   vertical-align: middle;
          745  +@   padding-top: 8px;
          746  +@   padding-bottom: 8px;
          747  +@   background-color: #446979;
          748  +@   box-shadow: 0px 3px 4px #333333;
          749  +@ }
          750  +@
          751  +@ /* The submenu bar that *sometimes* appears below the main menu */
          752  +@ div.submenu {
          753  +@   padding-top:10px;
          754  +@   padding-bottom:0;
          755  +@   text-align: right;
          756  +@   color: #000;
          757  +@   background-color: #fff;
          758  +@   height: 1.5em;
          759  +@   vertical-align:middle;
          760  +@   box-shadow: 0px 3px 4px #999;
          761  +@ }
          762  +@ div.mainmenu a, div.mainmenu a:visited {
          763  +@   padding: 3px 10px 3px 10px;
          764  +@   color: white;
          765  +@   text-decoration: none;
          766  +@ }
          767  +@ div.submenu a, div.submenu a:visited, a.button,
          768  +@ div.sectionmenu>a.button:link, div.sectinmenu>a.button:visited {
          769  +@   padding: 2px 8px;
          770  +@   color: #000;
          771  +@   font-family: Arial;
          772  +@   text-decoration: none;
          773  +@   margin:auto;
          774  +@   border-radius: 5px;
          775  +@   background-color: #e0e0e0;
          776  +@   text-shadow: 0px -1px 0px #eee;
          777  +@   border: 1px solid #000;
          778  +@ }
          779  +@
          780  +@ div.mainmenu a:hover {
          781  +@   color: #000;
          782  +@   background-color: white;
          783  +@ }
          784  +@
          785  +@ div.submenu a:hover, div.sectionmenu>a.button:hover {
          786  +@   background-color: #c0c0c0;
          787  +@ }
          788  +@
          789  +@ /* All page content from the bottom of the menu or submenu down to
          790  +@  ** the footer */
          791  +@ div.content {
          792  +@   background-color: #fff;
          793  +@   box-shadow: 0px 3px 4px #999;
          794  +@   border-bottom-right-radius: 5px;
          795  +@   border-bottom-left-radius: 5px;
          796  +@   padding-bottom: 1em;
          797  +@   min-height:40%;
          798  +@ }
          799  +@
          800  +@
          801  +@ /* Some pages have section dividers */
          802  +@ div.section {
          803  +@   margin-bottom: 0.5em;
          804  +@   margin-top: 1em;
          805  +@   margin-right: auto;
          806  +@   padding: 1px 1px 1px 1px;
          807  +@   font-size: 1.2em;
          808  +@   font-weight: bold;
          809  +@   text-align: center;
          810  +@   color: white;
          811  +@   border-radius: 5px;
          812  +@   background-color: #446979;
          813  +@   box-shadow: 0px 3px 4px #333333;
          814  +@   white-space: nowrap;
          815  +@ }
          816  +@
          817  +@ /* The "Date" that occurs on the left hand side of timelines */
          818  +@ div.divider {
          819  +@   font-size: 1.2em;
          820  +@   font-family: Georgia, serif;
          821  +@   font-weight: bold;
          822  +@   margin-top: 1em;
          823  +@   white-space: nowrap;
          824  +@ }
          825  +@
          826  +@ /* The footer at the very bottom of the page */
          827  +@ div.footer {
          828  +@   font-size: 0.9em;
          829  +@   text-align: right;
          830  +@   margin-bottom: 1em;
          831  +@   color: #666;
          832  +@ }
          833  +@
          834  +@ /* Hyperlink colors in the footer */
          835  +@ div.footer a { color: white; }
          836  +@ div.footer a:link { color: white; }
          837  +@ div.footer a:visited { color: white; }
          838  +@ div.footer a:hover { background-color: white; color: #558195; }
          839  +@
          840  +@ /* <verbatim> blocks */
          841  +@ pre.verbatim, blockquote pre {
          842  +@   font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
          843  +@   background-color: #f3f3f3;
          844  +@   padding: 0.5em;
          845  +@   white-space: pre-wrap;
          846  +@ }
          847  +@
          848  +@ blockquote pre {
          849  +@   border: 1px #000 dashed;
          850  +@ }
          851  +@
   587    852   @ /* The label/value pairs on (for example) the ci page */
   588    853   @ table.label-value th {
   589    854   @   vertical-align: top;
   590    855   @   text-align: right;
   591    856   @   padding: 0.2ex 2ex;
          857  +@ }
          858  +@
          859  +@ table.report tr th {
          860  +@   padding: 3px 5px;
          861  +@   text-transform: capitalize;
          862  +@   cursor: pointer;
          863  +@ }
          864  +@
          865  +@ table.report tr td {
          866  +@   padding: 3px 5px;
          867  +@   cursor: pointer;
          868  +@ }
          869  +@
          870  +@ textarea {
          871  +@   font-size: 1em;
   592    872   @ }');
   593         -@ REPLACE INTO config VALUES('header','<html>
          873  +@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   594    874   @ <head>
          875  +@ <base href="$baseurl/$current_page" />
   595    876   @ <title>$<project_name>: $<title></title>
   596    877   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   597         -@       href="$baseurl/timeline.rss">
   598         -@ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
          878  +@       href="$home/timeline.rss">
          879  +@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
   599    880   @       media="screen">
   600    881   @ </head>
   601    882   @ <body>
   602    883   @ <div class="header">
   603    884   @   <div class="logo">
   604         -@     <!-- <img src="$baseurl/logo" alt="logo"> -->
   605         -@     <br><nobr>$<project_name></nobr>
          885  +@     <img src="$home/logo" alt="logo">
          886  +@     <br />$<project_name>
   606    887   @   </div>
   607    888   @   <div class="title">$<title></div>
   608         -@   <div class="status"><nobr><th1>
          889  +@   <div class="status"><th1>
   609    890   @      if {[info exists login]} {
   610    891   @        puts "Logged in as $login"
   611    892   @      } else {
   612    893   @        puts "Not logged in"
   613    894   @      }
   614         -@   </th1></nobr></div>
          895  +@   </th1></div>
   615    896   @ </div>
   616         -@ <div class="mainmenu"><ul><th1>
   617         -@ html "<li><a href=''$baseurl$index_page''>Home</a></li>"
          897  +@ <div class="mainmenu">
          898  +@ <th1>
          899  +@ html "<a href=''$home$index_page''>Home</a>\n"
   618    900   @ if {[anycap jor]} {
   619         -@   html "<li><a href=''$baseurl/timeline''>Timeline</a></li>"
          901  +@   html "<a href=''$home/timeline''>Timeline</a>\n"
   620    902   @ }
   621    903   @ if {[hascap oh]} {
   622         -@   html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>"
          904  +@   html "<a href=''$home/dir?ci=tip''>Files</a>\n"
   623    905   @ }
   624    906   @ if {[hascap o]} {
   625         -@   html "<li><a href=''$baseurl/leaves''>Leaves</a></li>"
   626         -@   html "<li><a href=''$baseurl/brlist''>Branches</a></li>"
   627         -@   html "<li><a href=''$baseurl/taglist''>Tags</a></li>"
          907  +@   html "<a href=''$home/brlist''>Branches</a>\n"
          908  +@   html "<a href=''$home/taglist''>Tags</a>\n"
   628    909   @ }
   629    910   @ if {[hascap r]} {
   630         -@   html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>"
          911  +@   html "<a href=''$home/reportlist''>Tickets</a>\n"
   631    912   @ }
   632    913   @ if {[hascap j]} {
   633         -@   html "<li><a href=''$baseurl/wiki''>Wiki</a></li>"
          914  +@   html "<a href=''$home/wiki''>Wiki</a>\n"
   634    915   @ }
   635    916   @ if {[hascap s]} {
   636         -@   html "<li><a href=''$baseurl/setup''>Admin</a></li>"
          917  +@   html "<a href=''$home/setup''>Admin</a>\n"
   637    918   @ } elseif {[hascap a]} {
   638         -@   html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>"
          919  +@   html "<a href=''$home/setup_ulist''>Users</a>\n"
   639    920   @ }
   640    921   @ if {[info exists login]} {
   641         -@   html "<li><a href=''$baseurl/login''>Logout</a></li>"
          922  +@   html "<a href=''$home/login''>Logout</a>\n"
   642    923   @ } else {
   643         -@   html "<li><a href=''$baseurl/login''>Login</a></li>"
          924  +@   html "<a href=''$home/login''>Login</a>\n"
   644    925   @ }
   645         -@ </th1></ul></div>
          926  +@ </th1></div>
   646    927   @ <div id="container">
   647    928   @ ');
   648         -@ REPLACE INTO config VALUES('footer','</div>
          929  +@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
   649    930   @ <div class="footer">
   650    931   @ Fossil version $manifest_version $manifest_date
   651    932   @ </div>
   652    933   @ </body></html>
   653    934   @ ');
   654    935   ;
          936  +
          937  +
          938  +/*
          939  +** This skin is intended to be almost identical to the default one, with the
          940  +** following changes to the header and footer:
          941  +**
          942  +** 1. The logo image in the header has been modified to be a hyperlink to the
          943  +**    root of the web site containing the repository using the same scheme
          944  +**    (i.e. HTTP or HTTPS) as the base URL for the repository.  The header
          945  +**    contains a TH1 script block to help accomplish these tasks.
          946  +**
          947  +** 2. The Fossil version information in the footer has been augmented with
          948  +**    hyperlinks to the corresponding points on the timeline in the official
          949  +**    Fossil repository.  Additionally, if the Tcl integration feature is
          950  +**    enabled, the loaded version of Tcl is included, with a hyperlink to the
          951  +**    official Tcl/Tk web site.  The footer also contains a TH1 script block
          952  +**    to help accomplish these tasks.
          953  +*/
          954  +static const char zBuiltinSkin5[] =
          955  +@ REPLACE INTO config(name,mtime,value)
          956  +@ VALUES('css',now(),'/* General settings for the entire page */
          957  +@ body {
          958  +@   margin: 0ex 1ex;
          959  +@   padding: 0px;
          960  +@   background-color: white;
          961  +@   font-family: sans-serif;
          962  +@ }
          963  +@
          964  +@ /* The project logo in the upper left-hand corner of each page */
          965  +@ div.logo {
          966  +@   display: table-cell;
          967  +@   text-align: center;
          968  +@   vertical-align: bottom;
          969  +@   font-weight: bold;
          970  +@   color: #558195;
          971  +@   min-width: 200px;
          972  +@   white-space: nowrap;
          973  +@ }
          974  +@
          975  +@ /* The page title centered at the top of each page */
          976  +@ div.title {
          977  +@   display: table-cell;
          978  +@   font-size: 2em;
          979  +@   font-weight: bold;
          980  +@   text-align: center;
          981  +@   padding: 0 0 0 1em;
          982  +@   color: #558195;
          983  +@   vertical-align: bottom;
          984  +@   width: 100%;
          985  +@ }
          986  +@
          987  +@ /* The login status message in the top right-hand corner */
          988  +@ div.status {
          989  +@   display: table-cell;
          990  +@   text-align: right;
          991  +@   vertical-align: bottom;
          992  +@   color: #558195;
          993  +@   font-size: 0.8em;
          994  +@   font-weight: bold;
          995  +@   min-width: 200px;
          996  +@   white-space: nowrap;
          997  +@ }
          998  +@
          999  +@ /* The header across the top of the page */
         1000  +@ div.header {
         1001  +@   display: table;
         1002  +@   width: 100%;
         1003  +@ }
         1004  +@
         1005  +@ /* The main menu bar that appears at the top of the page beneath
         1006  +@ ** the header */
         1007  +@ div.mainmenu {
         1008  +@   padding: 5px 10px 5px 10px;
         1009  +@   font-size: 0.9em;
         1010  +@   font-weight: bold;
         1011  +@   text-align: center;
         1012  +@   letter-spacing: 1px;
         1013  +@   background-color: #558195;
         1014  +@   border-top-left-radius: 8px;
         1015  +@   border-top-right-radius: 8px;
         1016  +@   color: white;
         1017  +@ }
         1018  +@
         1019  +@ /* The submenu bar that *sometimes* appears below the main menu */
         1020  +@ div.submenu, div.sectionmenu {
         1021  +@   padding: 3px 10px 3px 0px;
         1022  +@   font-size: 0.9em;
         1023  +@   text-align: center;
         1024  +@   background-color: #456878;
         1025  +@   color: white;
         1026  +@ }
         1027  +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
         1028  +@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
         1029  +@   padding: 3px 10px 3px 10px;
         1030  +@   color: white;
         1031  +@   text-decoration: none;
         1032  +@ }
         1033  +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
         1034  +@   color: #558195;
         1035  +@   background-color: white;
         1036  +@ }
         1037  +@
         1038  +@ /* All page content from the bottom of the menu or submenu down to
         1039  +@ ** the footer */
         1040  +@ div.content {
         1041  +@   padding: 0ex 1ex 1ex 1ex;
         1042  +@   border: solid #aaa;
         1043  +@   border-width: 1px;
         1044  +@ }
         1045  +@
         1046  +@ /* Some pages have section dividers */
         1047  +@ div.section {
         1048  +@   margin-bottom: 0px;
         1049  +@   margin-top: 1em;
         1050  +@   padding: 1px 1px 1px 1px;
         1051  +@   font-size: 1.2em;
         1052  +@   font-weight: bold;
         1053  +@   background-color: #558195;
         1054  +@   color: white;
         1055  +@   white-space: nowrap;
         1056  +@ }
         1057  +@
         1058  +@ /* The "Date" that occurs on the left hand side of timelines */
         1059  +@ div.divider {
         1060  +@   background: #a1c4d4;
         1061  +@   border: 2px #558195 solid;
         1062  +@   font-size: 1em; font-weight: normal;
         1063  +@   padding: .25em;
         1064  +@   margin: .2em 0 .2em 0;
         1065  +@   float: left;
         1066  +@   clear: left;
         1067  +@   white-space: nowrap;
         1068  +@ }
         1069  +@
         1070  +@ /* The footer at the very bottom of the page */
         1071  +@ div.footer {
         1072  +@   clear: both;
         1073  +@   font-size: 0.8em;
         1074  +@   padding: 5px 10px 5px 10px;
         1075  +@   text-align: right;
         1076  +@   background-color: #558195;
         1077  +@   border-bottom-left-radius: 8px;
         1078  +@   border-bottom-right-radius: 8px;
         1079  +@   color: white;
         1080  +@ }
         1081  +@
         1082  +@ /* Hyperlink colors in the footer */
         1083  +@ div.footer a { color: white; }
         1084  +@ div.footer a:link { color: white; }
         1085  +@ div.footer a:visited { color: white; }
         1086  +@ div.footer a:hover { background-color: white; color: #558195; }
         1087  +@
         1088  +@ /* verbatim blocks */
         1089  +@ pre.verbatim {
         1090  +@   background-color: #f5f5f5;
         1091  +@   padding: 0.5em;
         1092  +@   white-space: pre-wrap;
         1093  +@ }
         1094  +@
         1095  +@ /* The label/value pairs on (for example) the ci page */
         1096  +@ table.label-value th {
         1097  +@   vertical-align: top;
         1098  +@   text-align: right;
         1099  +@   padding: 0.2ex 2ex;
         1100  +@ }');
         1101  +@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
         1102  +@ <head>
         1103  +@ <base href="$baseurl/$current_page" />
         1104  +@ <title>$<project_name>: $<title></title>
         1105  +@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
         1106  +@       href="$home/timeline.rss" />
         1107  +@ <link rel="stylesheet" href="$home/style.css?enhanced" type="text/css"
         1108  +@       media="screen" />
         1109  +@ </head>
         1110  +@ <body>
         1111  +@ <div class="header">
         1112  +@   <div class="logo">
         1113  +@     <th1>
         1114  +@     ##
         1115  +@     ## NOTE: The purpose of this procedure is to take the base URL of the
         1116  +@     ##       Fossil project and return the root of the entire web site using
         1117  +@     ##       the same URI scheme as the base URL (e.g. http or https).
         1118  +@     ##
         1119  +@     proc getLogoUrl { baseurl } {
         1120  +@       set idx(first) [string first // $baseurl]
         1121  +@       if {$idx(first) != -1} {
         1122  +@         ##
         1123  +@         ## NOTE: Skip second slash.
         1124  +@         ##
         1125  +@         set idx(first+1) [expr {$idx(first) + 2}]
         1126  +@         ##
         1127  +@         ## NOTE: (part 1) The [string first] command does NOT actually
         1128  +@         ##       the optional startIndex argument as specified in the
         1129  +@         ##       TH1 support manual; therefore, we fake it by using the
         1130  +@         ##       [string range] command and then adding the necessary
         1131  +@         ##       offset to the resulting index manually (below).  In Tcl,
         1132  +@         ##       we could use the following instead:
         1133  +@         ##
         1134  +@         ##       set idx(next) [string first / $baseurl $idx(first+1)]
         1135  +@         ##
         1136  +@         set idx(nextRange) [string range $baseurl $idx(first+1) end]
         1137  +@         set idx(next) [string first / $idx(nextRange)]
         1138  +@         if {$idx(next) != -1} {
         1139  +@           ##
         1140  +@           ## NOTE: (part 2) Add the necessary offset to the result of the
         1141  +@           ##       search for the next slash (i.e. the one after the initial
         1142  +@           ##       search for the two slashes).
         1143  +@           ##
         1144  +@           set idx(next) [expr {$idx(next) + $idx(first+1)}]
         1145  +@           ##
         1146  +@           ## NOTE: Back up one character from the next slash.
         1147  +@           ##
         1148  +@           set idx(next-1) [expr {$idx(next) - 1}]
         1149  +@           ##
         1150  +@           ## NOTE: Extract the URI scheme and host from the base URL.
         1151  +@           ##
         1152  +@           set scheme [string range $baseurl 0 $idx(first)]
         1153  +@           set host [string range $baseurl $idx(first+1) $idx(next-1)]
         1154  +@           ##
         1155  +@           ## NOTE: Try to stay in SSL mode if we are there now.
         1156  +@           ##
         1157  +@           if {[string compare $scheme http:/] == 0} {
         1158  +@             set scheme http://
         1159  +@           } else {
         1160  +@             set scheme https://
         1161  +@           }
         1162  +@           set logourl $scheme$host/
         1163  +@         } else {
         1164  +@           set logourl $baseurl
         1165  +@         }
         1166  +@       } else {
         1167  +@         set logourl $baseurl
         1168  +@       }
         1169  +@       return $logourl
         1170  +@     }
         1171  +@     set logourl [getLogoUrl $baseurl]
         1172  +@     </th1>
         1173  +@     <a href="$logourl">
         1174  +@       <img src="$baseurl/logo" border="0" alt="$project_name">
         1175  +@     </a>
         1176  +@   </div>
         1177  +@   <div class="title"><small>$<project_name></small><br />$<title></div>
         1178  +@   <div class="status"><th1>
         1179  +@      if {[info exists login]} {
         1180  +@        puts "Logged in as $login"
         1181  +@      } else {
         1182  +@        puts "Not logged in"
         1183  +@      }
         1184  +@   </th1></div>
         1185  +@ </div>
         1186  +@ <div class="mainmenu">
         1187  +@ <th1>
         1188  +@ html "<a href=''$home$index_page''>Home</a>\n"
         1189  +@ if {[anycap jor]} {
         1190  +@   html "<a href=''$home/timeline''>Timeline</a>\n"
         1191  +@ }
         1192  +@ if {[hascap oh]} {
         1193  +@   html "<a href=''$home/dir?ci=tip''>Files</a>\n"
         1194  +@ }
         1195  +@ if {[hascap o]} {
         1196  +@   html "<a href=''$home/brlist''>Branches</a>\n"
         1197  +@   html "<a href=''$home/taglist''>Tags</a>\n"
         1198  +@ }
         1199  +@ if {[hascap r]} {
         1200  +@   html "<a href=''$home/reportlist''>Tickets</a>\n"
         1201  +@ }
         1202  +@ if {[hascap j]} {
         1203  +@   html "<a href=''$home/wiki''>Wiki</a>\n"
         1204  +@ }
         1205  +@ if {[hascap s]} {
         1206  +@   html "<a href=''$home/setup''>Admin</a>\n"
         1207  +@ } elseif {[hascap a]} {
         1208  +@   html "<a href=''$home/setup_ulist''>Users</a>\n"
         1209  +@ }
         1210  +@ if {[info exists login]} {
         1211  +@   html "<a href=''$home/login''>Logout</a>\n"
         1212  +@ } else {
         1213  +@   html "<a href=''$home/login''>Login</a>\n"
         1214  +@ }
         1215  +@ </th1></div>
         1216  +@ ');
         1217  +@ REPLACE INTO config(name,mtime,value)
         1218  +@ VALUES('footer',now(),'<div class="footer">
         1219  +@   <th1>
         1220  +@   proc getTclVersion {} {
         1221  +@     if {[catch {tclEval info patchlevel} tclVersion] == 0} {
         1222  +@       return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion"
         1223  +@     }
         1224  +@     return ""
         1225  +@   }
         1226  +@   proc getVersion { version } {
         1227  +@     set length [string length $version]
         1228  +@     return [string range $version 1 [expr {$length - 2}]]
         1229  +@   }
         1230  +@   set version [getVersion $manifest_version]
         1231  +@   set tclVersion [getTclVersion]
         1232  +@   set fossilUrl http://www.fossil-scm.org
         1233  +@   </th1>
         1234  +@   This page was generated in about
         1235  +@   <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
         1236  +@   <a href="$fossilUrl/">Fossil</a>
         1237  +@   version $release_version $tclVersion
         1238  +@   <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
         1239  +@   <a href="$fossilUrl/fossil/timeline?c=$manifest_date&amp;y=ci">$manifest_date</a>
         1240  +@ </div>
         1241  +@ </body></html>
         1242  +@ ');
         1243  +;
         1244  +
   655   1245   /*
   656   1246   ** An array of available built-in skins.
   657   1247   */
   658   1248   static struct BuiltinSkin {
   659   1249     const char *zName;
   660   1250     const char *zValue;
   661   1251   } aBuiltinSkin[] = {
   662   1252     { "Default",                     0 /* Filled in at runtime */ },
   663   1253     { "Plain Gray, No Logo",         zBuiltinSkin1                },
   664   1254     { "Khaki, No Logo",              zBuiltinSkin2                },
   665   1255     { "Black & White, Menu on Left", zBuiltinSkin3                },
         1256  +  { "Shadow boxes & Rounded Corners", zBuiltinSkin4             },
         1257  +  { "Enhanced Default",            zBuiltinSkin5                },
   666   1258   };
   667   1259   
   668   1260   /*
   669   1261   ** For a skin named zSkinName, compute the name of the CONFIG table
   670   1262   ** entry where that skin is stored and return it.
   671   1263   **
   672   1264   ** Return NULL if zSkinName is NULL or an empty string.
................................................................................
   689   1281   ** useDefault==0 or a string for the default skin if useDefault==1.
   690   1282   **
   691   1283   ** Memory to hold the returned string is obtained from malloc.
   692   1284   */
   693   1285   static char *getSkin(int useDefault){
   694   1286     Blob val;
   695   1287     blob_zero(&val);
   696         -  blob_appendf(&val, "REPLACE INTO config VALUES('css',%Q);\n",
         1288  +  blob_appendf(&val,
         1289  +     "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n",
   697   1290        useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS)
   698   1291     );
   699         -  blob_appendf(&val, "REPLACE INTO config VALUES('header',%Q);\n",
         1292  +  blob_appendf(&val,
         1293  +     "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n",
   700   1294        useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader)
   701   1295     );
   702         -  blob_appendf(&val, "REPLACE INTO config VALUES('footer',%Q);\n",
         1296  +  blob_appendf(&val,
         1297  +     "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n",
   703   1298        useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter)
   704   1299     );
   705   1300     return blob_str(&val);
   706   1301   }
   707   1302   
   708   1303   /*
   709   1304   ** Construct the default skin string and fill in the corresponding
................................................................................
   721   1316     char *zName;
   722   1317     char *zErr = 0;
   723   1318     const char *zCurrent;  /* Current skin */
   724   1319     int i;                 /* Loop counter */
   725   1320     Stmt q;
   726   1321   
   727   1322     login_check_credentials();
   728         -  if( !g.okSetup ){
         1323  +  if( !g.perm.Setup ){
   729   1324       login_needed();
   730   1325     }
   731   1326     db_begin_transaction();
   732   1327   
   733   1328     /* Process requests to delete a user-defined skin */
   734   1329     if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
   735   1330       style_header("Confirm Custom Skin Delete");
   736         -    @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
         1331  +    @ <form action="%s(g.zTop)/setup_skin" method="post"><div>
   737   1332       @ <p>Deletion of a custom skin is a permanent action that cannot
   738   1333       @ be undone.  Please confirm that this is what you want to do:</p>
   739         -    @ <input type="hidden" name="sn" value="%h(P("sn"))">
   740         -    @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
   741         -    @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
         1334  +    @ <input type="hidden" name="sn" value="%h(P("sn"))" />
         1335  +    @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
         1336  +    @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
   742   1337       login_insert_csrf_secret();
   743         -    @ </form>
         1338  +    @ </div></form>
   744   1339       style_footer();
   745   1340       return;
   746   1341     }
   747   1342     if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
   748   1343       db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   749   1344     }
   750   1345   
   751   1346     setDefaultSkin();
   752   1347     zCurrent = getSkin(0);
   753   1348   
   754   1349     if( P("save")!=0 && (zName = skinVarName(P("save"),0))!=0 ){
   755   1350       if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName)
   756         -          || strcmp(zName, "Default")==0 ){
         1351  +          || fossil_strcmp(zName, "Default")==0 ){
   757   1352         zErr = mprintf("Skin name \"%h\" already exists. "
   758   1353                        "Choose a different name.", P("sn"));
   759   1354       }else{
   760         -      db_multi_exec("INSERT INTO config VALUES(%Q,%Q)",
         1355  +      db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
   761   1356            zName, zCurrent
   762   1357         );
   763   1358       }
   764   1359     }
   765   1360   
   766   1361     /* The user pressed the "Use This Skin" button. */
   767   1362     if( P("load") && (z = P("sn"))!=0 && z[0] ){
   768   1363       int seen = 0;
   769   1364       for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
   770         -      if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
         1365  +      if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
   771   1366           seen = 1;
   772   1367           break;
   773   1368         }
   774   1369       }
   775   1370       if( !seen ){
   776   1371         seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
   777   1372                          " AND value=%Q", zCurrent);
   778   1373       }
   779   1374       if( !seen ){
   780   1375         db_multi_exec(
   781         -        "INSERT INTO config VALUES("
         1376  +        "INSERT INTO config(name,value,mtime) VALUES("
   782   1377           "  strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
   783         -        "  %Q)", zCurrent
         1378  +        "  %Q,now())", zCurrent
   784   1379         );
   785   1380       }
   786   1381       seen = 0;
   787   1382       for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
   788         -      if( strcmp(aBuiltinSkin[i].zName, z)==0 ){
         1383  +      if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){
   789   1384           seen = 1;
   790   1385           zCurrent = aBuiltinSkin[i].zValue;
   791   1386           db_multi_exec("%s", zCurrent);
   792   1387           break;
   793   1388         }
   794   1389       }
   795   1390       if( !seen ){
................................................................................
   796   1391         zName = skinVarName(z,0);
   797   1392         zCurrent = db_get(zName, 0);
   798   1393         db_multi_exec("%s", zCurrent);
   799   1394       }
   800   1395     }
   801   1396   
   802   1397     style_header("Skins");
         1398  +  if( zErr ){
         1399  +    @ <p><font color="red">%h(zErr)</font></p>
         1400  +  }
   803   1401     @ <p>A "skin" is a combination of
   804         -  @ <a href="setup_editcss">CSS</a>, 
   805         -  @ <a href="setup_header">Header</a>, and 
   806         -  @ <a href="setup_footer">Footer</a> that determines the look and feel
         1402  +  @ <a href="setup_editcss">CSS</a>,
         1403  +  @ <a href="setup_header">Header</a>,
         1404  +  @ <a href="setup_footer">Footer</a>, and
         1405  +  @ <a href="setup_logo">Logo</a> that determines the look and feel
   807   1406     @ of the web interface.</p>
   808   1407     @
   809   1408     @ <h2>Available Skins:</h2>
   810   1409     @ <ol>
   811   1410     for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
   812   1411       z = aBuiltinSkin[i].zName;
   813         -    if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
         1412  +    if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
   814   1413         @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
   815   1414       }else{
   816         -      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
   817         -      @ %h(z).&nbsp;&nbsp; 
   818         -      @ <input type="hidden" name="sn" value="%h(z)">
   819         -      @ <input type="submit" name="load" value="Use This Skin">
   820         -      @ </form></li>
         1415  +      @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div>
         1416  +      @ %h(z).&nbsp;&nbsp;
         1417  +      @ <input type="hidden" name="sn" value="%h(z)" />
         1418  +      @ <input type="submit" name="load" value="Use This Skin" />
         1419  +      @ </div></form></li>
   821   1420       }
   822   1421     }
   823   1422     db_prepare(&q,
   824   1423        "SELECT substr(name, 6), value FROM config"
   825   1424        " WHERE name GLOB 'skin:*'"
   826   1425        " ORDER BY name"
   827   1426     );
   828   1427     while( db_step(&q)==SQLITE_ROW ){
   829   1428       const char *zN = db_column_text(&q, 0);
   830   1429       const char *zV = db_column_text(&q, 1);
   831         -    if( strcmp(zV, zCurrent)==0 ){
         1430  +    if( fossil_strcmp(zV, zCurrent)==0 ){
   832   1431         @ <li><p>%h(zN).&nbsp;&nbsp;  <b>Currently In Use</b></p>
   833   1432       }else{
   834         -      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
   835         -      @ %h(zN).&nbsp;&nbsp; 
         1433  +      @ <li><form action="%s(g.zTop)/setup_skin" method="post">
         1434  +      @ %h(zN).&nbsp;&nbsp;
   836   1435         @ <input type="hidden" name="sn" value="%h(zN)">
   837   1436         @ <input type="submit" name="load" value="Use This Skin">
   838   1437         @ <input type="submit" name="del1" value="Delete This Skin">
   839   1438         @ </form></li>
   840   1439       }
   841   1440     }
   842   1441     db_finalize(&q);
   843   1442     @ </ol>
   844   1443     style_footer();
   845   1444     db_end_transaction(0);
   846   1445   }

Added src/sqlcmd.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This module contains the code that initializes the "sqlite3" command-line
           19  +** shell against the repository database.  The command-line shell itself
           20  +** is a copy of the "shell.c" code from SQLite.  This file contains logic
           21  +** to initialize the code in shell.c.
           22  +*/
           23  +#include "config.h"
           24  +#include "sqlcmd.h"
           25  +#include <zlib.h>
           26  +
           27  +/*
           28  +** Implementation of the "content(X)" SQL function.  Return the complete
           29  +** content of artifact identified by X as a blob.
           30  +*/
           31  +static void sqlcmd_content(
           32  +  sqlite3_context *context,
           33  +  int argc,
           34  +  sqlite3_value **argv
           35  +){
           36  +  int rid;
           37  +  Blob cx;
           38  +  const char *zName;
           39  +  assert( argc==1 );
           40  +  zName = (const char*)sqlite3_value_text(argv[0]);
           41  +  if( zName==0 ) return;
           42  +  g.db = sqlite3_context_db_handle(context);
           43  +  g.repositoryOpen = 1;
           44  +  rid = name_to_rid(zName);
           45  +  if( rid==0 ) return;
           46  +  if( content_get(rid, &cx) ){
           47  +    sqlite3_result_blob(context, blob_buffer(&cx), blob_size(&cx), 
           48  +                                 SQLITE_TRANSIENT);
           49  +    blob_reset(&cx);
           50  +  }
           51  +}
           52  +
           53  +/*
           54  +** Implementation of the "compress(X)" SQL function.  The input X is
           55  +** compressed using zLib and the output is returned.
           56  +*/
           57  +static void sqlcmd_compress(
           58  +  sqlite3_context *context,
           59  +  int argc,
           60  +  sqlite3_value **argv
           61  +){
           62  +  const unsigned char *pIn;
           63  +  unsigned char *pOut;
           64  +  unsigned int nIn;
           65  +  unsigned long int nOut;
           66  +
           67  +  pIn = sqlite3_value_blob(argv[0]);
           68  +  nIn = sqlite3_value_bytes(argv[0]);
           69  +  nOut = 13 + nIn + (nIn+999)/1000;
           70  +  pOut = sqlite3_malloc( nOut+4 );
           71  +  pOut[0] = nIn>>24 & 0xff;
           72  +  pOut[1] = nIn>>16 & 0xff;
           73  +  pOut[2] = nIn>>8 & 0xff;
           74  +  pOut[3] = nIn & 0xff;
           75  +  compress(&pOut[4], &nOut, pIn, nIn);
           76  +  sqlite3_result_blob(context, pOut, nOut+4, sqlite3_free);
           77  +}
           78  +
           79  +/*
           80  +** Implementation of the "decompress(X)" SQL function.  The argument X
           81  +** is a blob which was obtained from compress(Y).  The output will be
           82  +** the value Y.
           83  +*/
           84  +static void sqlcmd_decompress(
           85  +  sqlite3_context *context,
           86  +  int argc,
           87  +  sqlite3_value **argv
           88  +){
           89  +  const unsigned char *pIn;
           90  +  unsigned char *pOut;
           91  +  unsigned int nIn;
           92  +  unsigned long int nOut;
           93  +  int rc;
           94  +
           95  +  pIn = sqlite3_value_blob(argv[0]);
           96  +  nIn = sqlite3_value_bytes(argv[0]);
           97  +  nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3];
           98  +  pOut = sqlite3_malloc( nOut+1 );
           99  +  rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
          100  +  if( rc==Z_OK ){
          101  +    sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
          102  +  }else{
          103  +    sqlite3_result_error(context, "input is not zlib compressed", -1);
          104  +  }
          105  +}
          106  +
          107  +/*
          108  +** This is the "automatic extension" initializer that runs right after
          109  +** the connection to the repository database is opened.  Set up the
          110  +** database connection to be more useful to the human operator.
          111  +*/
          112  +static int sqlcmd_autoinit(
          113  +  sqlite3 *db,
          114  +  const char **pzErrMsg,
          115  +  const void *notUsed
          116  +){
          117  +  sqlite3_create_function(db, "content", 1, SQLITE_ANY, 0,
          118  +                          sqlcmd_content, 0, 0);
          119  +  sqlite3_create_function(db, "compress", 1, SQLITE_ANY, 0,
          120  +                          sqlcmd_compress, 0, 0);
          121  +  sqlite3_create_function(db, "decompress", 1, SQLITE_ANY, 0,
          122  +                          sqlcmd_decompress, 0, 0);
          123  +  re_add_sql_func(db);
          124  +  g.repositoryOpen = 1;
          125  +  g.db = db;
          126  +  return SQLITE_OK;
          127  +}
          128  +
          129  +
          130  +/*
          131  +** COMMAND: sqlite3
          132  +**
          133  +** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS?
          134  +**
          135  +** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS.
          136  +** If DATABASE is omitted, then the repository that serves the working
          137  +** directory is opened.
          138  +**
          139  +** WARNING:  Careless use of this command can corrupt a Fossil repository
          140  +** in ways that are unrecoverable.  Be sure you know what you are doing before
          141  +** running any SQL commands that modifies the repository database.
          142  +*/
          143  +void sqlite3_cmd(void){
          144  +  extern int sqlite3_shell(int, char**);
          145  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
          146  +  db_close(1);
          147  +  sqlite3_shutdown();
          148  +  sqlite3_shell(g.argc-1, g.argv+1);
          149  +  g.db = 0;
          150  +}
          151  +
          152  +/*
          153  +** This routine is called by the patched sqlite3 command-line shell in order
          154  +** to load the name and database connection for the open Fossil database.
          155  +*/
          156  +void fossil_open(const char **pzRepoName){
          157  +  sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit);
          158  +  *pzRepoName = g.zRepositoryName;
          159  +}

Changes to src/sqlite3.c.

more than 10,000 changes

Changes to src/sqlite3.h.

    93     93   ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also
    94     94   ** be larger than the release from which it is derived.  Either Y will
    95     95   ** be held constant and Z will be incremented or else Y will be incremented
    96     96   ** and Z will be reset to zero.
    97     97   **
    98     98   ** Since version 3.6.18, SQLite source code has been stored in the
    99     99   ** <a href="http://www.fossil-scm.org/">Fossil configuration management
   100         -** system</a>.  ^The SQLITE_SOURCE_ID macro evalutes to
          100  +** system</a>.  ^The SQLITE_SOURCE_ID macro evaluates to
   101    101   ** a string which identifies a particular check-in of SQLite
   102    102   ** within its configuration management system.  ^The SQLITE_SOURCE_ID
   103    103   ** string contains the date and time of the check-in (UTC) and an SHA1
   104    104   ** hash of the entire source tree.
   105    105   **
   106    106   ** See also: [sqlite3_libversion()],
   107    107   ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
   108    108   ** [sqlite_version()] and [sqlite_source_id()].
   109    109   */
   110         -#define SQLITE_VERSION        "3.6.23"
   111         -#define SQLITE_VERSION_NUMBER 3006023
   112         -#define SQLITE_SOURCE_ID      "2010-04-15 23:24:29 f96782b389b5b97b488dc5814f7082e0393f64cd"
          110  +#define SQLITE_VERSION        "3.7.16"
          111  +#define SQLITE_VERSION_NUMBER 3007016
          112  +#define SQLITE_SOURCE_ID      "2013-02-13 14:04:28 7e10a62d0eb1cb2bdafb6752b78a9d368e9f21f5"
   113    113   
   114    114   /*
   115    115   ** CAPI3REF: Run-Time Library Version Numbers
   116    116   ** KEYWORDS: sqlite3_version, sqlite3_sourceid
   117    117   **
   118    118   ** These interfaces provide the same information as the [SQLITE_VERSION],
   119    119   ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
................................................................................
   150    150   ** CAPI3REF: Run-Time Library Compilation Options Diagnostics
   151    151   **
   152    152   ** ^The sqlite3_compileoption_used() function returns 0 or 1 
   153    153   ** indicating whether the specified option was defined at 
   154    154   ** compile time.  ^The SQLITE_ prefix may be omitted from the 
   155    155   ** option name passed to sqlite3_compileoption_used().  
   156    156   **
   157         -** ^The sqlite3_compileoption_get() function allows interating
          157  +** ^The sqlite3_compileoption_get() function allows iterating
   158    158   ** over the list of options that were defined at compile time by
   159    159   ** returning the N-th compile time option string.  ^If N is out of range,
   160    160   ** sqlite3_compileoption_get() returns a NULL pointer.  ^The SQLITE_ 
   161    161   ** prefix is omitted from any strings returned by 
   162    162   ** sqlite3_compileoption_get().
   163    163   **
   164    164   ** ^Support for the diagnostic functions sqlite3_compileoption_used()
   165         -** and sqlite3_compileoption_get() may be omitted by specifing the 
          165  +** and sqlite3_compileoption_get() may be omitted by specifying the 
   166    166   ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
   167    167   **
   168    168   ** See also: SQL functions [sqlite_compileoption_used()] and
   169    169   ** [sqlite_compileoption_get()] and the [compile_options pragma].
   170    170   */
   171    171   #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
   172    172   SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
................................................................................
   173    173   SQLITE_API const char *sqlite3_compileoption_get(int N);
   174    174   #endif
   175    175   
   176    176   /*
   177    177   ** CAPI3REF: Test To See If The Library Is Threadsafe
   178    178   **
   179    179   ** ^The sqlite3_threadsafe() function returns zero if and only if
   180         -** SQLite was compiled mutexing code omitted due to the
          180  +** SQLite was compiled with mutexing code omitted due to the
   181    181   ** [SQLITE_THREADSAFE] compile-time option being set to 0.
   182    182   **
   183    183   ** SQLite can be compiled with or without mutexes.  When
   184    184   ** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes
   185    185   ** are enabled and SQLite is threadsafe.  When the
   186    186   ** [SQLITE_THREADSAFE] macro is 0, 
   187    187   ** the mutexes are omitted.  Without the mutexes, it is not safe
................................................................................
   215    215   ** CAPI3REF: Database Connection Handle
   216    216   ** KEYWORDS: {database connection} {database connections}
   217    217   **
   218    218   ** Each open SQLite database is represented by a pointer to an instance of
   219    219   ** the opaque structure named "sqlite3".  It is useful to think of an sqlite3
   220    220   ** pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and
   221    221   ** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
   222         -** is its destructor.  There are many other interfaces (such as
          222  +** and [sqlite3_close_v2()] are its destructors.  There are many other
          223  +** interfaces (such as
   223    224   ** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
   224    225   ** [sqlite3_busy_timeout()] to name but three) that are methods on an
   225    226   ** sqlite3 object.
   226    227   */
   227    228   typedef struct sqlite3 sqlite3;
   228    229   
   229    230   /*
................................................................................
   262    263   #ifdef SQLITE_OMIT_FLOATING_POINT
   263    264   # define double sqlite3_int64
   264    265   #endif
   265    266   
   266    267   /*
   267    268   ** CAPI3REF: Closing A Database Connection
   268    269   **
   269         -** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
   270         -** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
   271         -** successfullly destroyed and all associated resources are deallocated.
          270  +** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
          271  +** for the [sqlite3] object.
          272  +** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
          273  +** the [sqlite3] object is successfully destroyed and all associated
          274  +** resources are deallocated.
   272    275   **
   273         -** Applications must [sqlite3_finalize | finalize] all [prepared statements]
   274         -** and [sqlite3_blob_close | close] all [BLOB handles] associated with
   275         -** the [sqlite3] object prior to attempting to close the object.  ^If
          276  +** ^If the database connection is associated with unfinalized prepared
          277  +** statements or unfinished sqlite3_backup objects then sqlite3_close()
          278  +** will leave the database connection open and return [SQLITE_BUSY].
          279  +** ^If sqlite3_close_v2() is called with unfinalized prepared statements
          280  +** and unfinished sqlite3_backups, then the database connection becomes
          281  +** an unusable "zombie" which will automatically be deallocated when the
          282  +** last prepared statement is finalized or the last sqlite3_backup is
          283  +** finished.  The sqlite3_close_v2() interface is intended for use with
          284  +** host languages that are garbage collected, and where the order in which
          285  +** destructors are called is arbitrary.
          286  +**
          287  +** Applications should [sqlite3_finalize | finalize] all [prepared statements],
          288  +** [sqlite3_blob_close | close] all [BLOB handles], and 
          289  +** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
          290  +** with the [sqlite3] object prior to attempting to close the object.  ^If
   276    291   ** sqlite3_close() is called on a [database connection] that still has
   277         -** outstanding [prepared statements] or [BLOB handles], then it returns
   278         -** SQLITE_BUSY.
          292  +** outstanding [prepared statements], [BLOB handles], and/or
          293  +** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
          294  +** of resources is deferred until all [prepared statements], [BLOB handles],
          295  +** and [sqlite3_backup] objects are also destroyed.
   279    296   **
   280         -** ^If [sqlite3_close()] is invoked while a transaction is open,
          297  +** ^If an [sqlite3] object is destroyed while a transaction is open,
   281    298   ** the transaction is automatically rolled back.
   282    299   **
   283         -** The C parameter to [sqlite3_close(C)] must be either a NULL
          300  +** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
          301  +** must be either a NULL
   284    302   ** pointer or an [sqlite3] object pointer obtained
   285    303   ** from [sqlite3_open()], [sqlite3_open16()], or
   286    304   ** [sqlite3_open_v2()], and not previously closed.
   287         -** ^Calling sqlite3_close() with a NULL pointer argument is a 
   288         -** harmless no-op.
          305  +** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
          306  +** argument is a harmless no-op.
   289    307   */
   290         -SQLITE_API int sqlite3_close(sqlite3 *);
          308  +SQLITE_API int sqlite3_close(sqlite3*);
          309  +SQLITE_API int sqlite3_close_v2(sqlite3*);
   291    310   
   292    311   /*
   293    312   ** The type for a callback function.
   294    313   ** This is legacy and deprecated.  It is included for historical
   295    314   ** compatibility and is not documented.
   296    315   */
   297    316   typedef int (*sqlite3_callback)(void*,int,char**, char**);
................................................................................
   306    325   **
   307    326   ** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
   308    327   ** semicolon-separate SQL statements passed into its 2nd argument,
   309    328   ** in the context of the [database connection] passed in as its 1st
   310    329   ** argument.  ^If the callback function of the 3rd argument to
   311    330   ** sqlite3_exec() is not NULL, then it is invoked for each result row
   312    331   ** coming out of the evaluated SQL statements.  ^The 4th argument to
   313         -** to sqlite3_exec() is relayed through to the 1st argument of each
          332  +** sqlite3_exec() is relayed through to the 1st argument of each
   314    333   ** callback invocation.  ^If the callback pointer to sqlite3_exec()
   315    334   ** is NULL, then no callback is ever invoked and result rows are
   316    335   ** ignored.
   317    336   **
   318    337   ** ^If an error occurs while evaluating the SQL statements passed into
   319    338   ** sqlite3_exec(), then execution of the current statement stops and
   320    339   ** subsequent statements are skipped.  ^If the 5th parameter to sqlite3_exec()
................................................................................
   367    386   
   368    387   /*
   369    388   ** CAPI3REF: Result Codes
   370    389   ** KEYWORDS: SQLITE_OK {error code} {error codes}
   371    390   ** KEYWORDS: {result code} {result codes}
   372    391   **
   373    392   ** Many SQLite functions return an integer result code from the set shown
   374         -** here in order to indicates success or failure.
          393  +** here in order to indicate success or failure.
   375    394   **
   376    395   ** New error codes may be added in future versions of SQLite.
   377    396   **
   378         -** See also: [SQLITE_IOERR_READ | extended result codes]
          397  +** See also: [SQLITE_IOERR_READ | extended result codes],
          398  +** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
   379    399   */
   380    400   #define SQLITE_OK           0   /* Successful result */
   381    401   /* beginning-of-error-codes */
   382    402   #define SQLITE_ERROR        1   /* SQL error or missing database */
   383    403   #define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */
   384    404   #define SQLITE_PERM         3   /* Access permission denied */
   385    405   #define SQLITE_ABORT        4   /* Callback routine requested an abort */
................................................................................
   386    406   #define SQLITE_BUSY         5   /* The database file is locked */
   387    407   #define SQLITE_LOCKED       6   /* A table in the database is locked */
   388    408   #define SQLITE_NOMEM        7   /* A malloc() failed */
   389    409   #define SQLITE_READONLY     8   /* Attempt to write a readonly database */
   390    410   #define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/
   391    411   #define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
   392    412   #define SQLITE_CORRUPT     11   /* The database disk image is malformed */
   393         -#define SQLITE_NOTFOUND    12   /* NOT USED. Table or record not found */
          413  +#define SQLITE_NOTFOUND    12   /* Unknown opcode in sqlite3_file_control() */
   394    414   #define SQLITE_FULL        13   /* Insertion failed because database is full */
   395    415   #define SQLITE_CANTOPEN    14   /* Unable to open the database file */
   396         -#define SQLITE_PROTOCOL    15   /* NOT USED. Database lock protocol error */
          416  +#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
   397    417   #define SQLITE_EMPTY       16   /* Database is empty */
   398    418   #define SQLITE_SCHEMA      17   /* The database schema changed */
   399    419   #define SQLITE_TOOBIG      18   /* String or BLOB exceeds size limit */
   400    420   #define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */
   401    421   #define SQLITE_MISMATCH    20   /* Data type mismatch */
   402    422   #define SQLITE_MISUSE      21   /* Library used incorrectly */
   403    423   #define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
................................................................................
   445    465   #define SQLITE_IOERR_BLOCKED           (SQLITE_IOERR | (11<<8))
   446    466   #define SQLITE_IOERR_NOMEM             (SQLITE_IOERR | (12<<8))
   447    467   #define SQLITE_IOERR_ACCESS            (SQLITE_IOERR | (13<<8))
   448    468   #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
   449    469   #define SQLITE_IOERR_LOCK              (SQLITE_IOERR | (15<<8))
   450    470   #define SQLITE_IOERR_CLOSE             (SQLITE_IOERR | (16<<8))
   451    471   #define SQLITE_IOERR_DIR_CLOSE         (SQLITE_IOERR | (17<<8))
   452         -#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED | (1<<8) )
          472  +#define SQLITE_IOERR_SHMOPEN           (SQLITE_IOERR | (18<<8))
          473  +#define SQLITE_IOERR_SHMSIZE           (SQLITE_IOERR | (19<<8))
          474  +#define SQLITE_IOERR_SHMLOCK           (SQLITE_IOERR | (20<<8))
          475  +#define SQLITE_IOERR_SHMMAP            (SQLITE_IOERR | (21<<8))
          476  +#define SQLITE_IOERR_SEEK              (SQLITE_IOERR | (22<<8))
          477  +#define SQLITE_IOERR_DELETE_NOENT      (SQLITE_IOERR | (23<<8))
          478  +#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
          479  +#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
          480  +#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
          481  +#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
          482  +#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
          483  +#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
          484  +#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
          485  +#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
          486  +#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
          487  +#define SQLITE_CONSTRAINT_CHECK        (SQLITE_CONSTRAINT | (1<<8))
          488  +#define SQLITE_CONSTRAINT_COMMITHOOK   (SQLITE_CONSTRAINT | (2<<8))
          489  +#define SQLITE_CONSTRAINT_FOREIGNKEY   (SQLITE_CONSTRAINT | (3<<8))
          490  +#define SQLITE_CONSTRAINT_FUNCTION     (SQLITE_CONSTRAINT | (4<<8))
          491  +#define SQLITE_CONSTRAINT_NOTNULL      (SQLITE_CONSTRAINT | (5<<8))
          492  +#define SQLITE_CONSTRAINT_PRIMARYKEY   (SQLITE_CONSTRAINT | (6<<8))
          493  +#define SQLITE_CONSTRAINT_TRIGGER      (SQLITE_CONSTRAINT | (7<<8))
          494  +#define SQLITE_CONSTRAINT_UNIQUE       (SQLITE_CONSTRAINT | (8<<8))
          495  +#define SQLITE_CONSTRAINT_VTAB         (SQLITE_CONSTRAINT | (9<<8))
   453    496   
   454    497   /*
   455    498   ** CAPI3REF: Flags For File Open Operations
   456    499   **
   457    500   ** These bit values are intended for use in the
   458    501   ** 3rd parameter to the [sqlite3_open_v2()] interface and
   459         -** in the 4th parameter to the xOpen method of the
   460         -** [sqlite3_vfs] object.
          502  +** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
   461    503   */
   462    504   #define SQLITE_OPEN_READONLY         0x00000001  /* Ok for sqlite3_open_v2() */
   463    505   #define SQLITE_OPEN_READWRITE        0x00000002  /* Ok for sqlite3_open_v2() */
   464    506   #define SQLITE_OPEN_CREATE           0x00000004  /* Ok for sqlite3_open_v2() */
   465    507   #define SQLITE_OPEN_DELETEONCLOSE    0x00000008  /* VFS only */
   466    508   #define SQLITE_OPEN_EXCLUSIVE        0x00000010  /* VFS only */
   467    509   #define SQLITE_OPEN_AUTOPROXY        0x00000020  /* VFS only */
          510  +#define SQLITE_OPEN_URI              0x00000040  /* Ok for sqlite3_open_v2() */
          511  +#define SQLITE_OPEN_MEMORY           0x00000080  /* Ok for sqlite3_open_v2() */
   468    512   #define SQLITE_OPEN_MAIN_DB          0x00000100  /* VFS only */
   469    513   #define SQLITE_OPEN_TEMP_DB          0x00000200  /* VFS only */
   470    514   #define SQLITE_OPEN_TRANSIENT_DB     0x00000400  /* VFS only */
   471    515   #define SQLITE_OPEN_MAIN_JOURNAL     0x00000800  /* VFS only */
   472    516   #define SQLITE_OPEN_TEMP_JOURNAL     0x00001000  /* VFS only */
   473    517   #define SQLITE_OPEN_SUBJOURNAL       0x00002000  /* VFS only */
   474    518   #define SQLITE_OPEN_MASTER_JOURNAL   0x00004000  /* VFS only */
   475    519   #define SQLITE_OPEN_NOMUTEX          0x00008000  /* Ok for sqlite3_open_v2() */
   476    520   #define SQLITE_OPEN_FULLMUTEX        0x00010000  /* Ok for sqlite3_open_v2() */
   477    521   #define SQLITE_OPEN_SHAREDCACHE      0x00020000  /* Ok for sqlite3_open_v2() */
   478    522   #define SQLITE_OPEN_PRIVATECACHE     0x00040000  /* Ok for sqlite3_open_v2() */
          523  +#define SQLITE_OPEN_WAL              0x00080000  /* VFS only */
          524  +
          525  +/* Reserved:                         0x00F00000 */
   479    526   
   480    527   /*
   481    528   ** CAPI3REF: Device Characteristics
   482    529   **
   483         -** The xDeviceCapabilities method of the [sqlite3_io_methods]
   484         -** object returns an integer which is a vector of the these
          530  +** The xDeviceCharacteristics method of the [sqlite3_io_methods]
          531  +** object returns an integer which is a vector of these
   485    532   ** bit values expressing I/O characteristics of the mass storage
   486    533   ** device that holds the file that the [sqlite3_io_methods]
   487    534   ** refers to.
   488    535   **
   489    536   ** The SQLITE_IOCAP_ATOMIC property means that all writes of
   490    537   ** any size are atomic.  The SQLITE_IOCAP_ATOMICnnn values
   491    538   ** mean that writes of blocks that are nnn bytes in size and
   492    539   ** are aligned to an address which is an integer multiple of
   493    540   ** nnn are atomic.  The SQLITE_IOCAP_SAFE_APPEND value means
   494    541   ** that when data is appended to a file, the data is appended
   495    542   ** first then the size of the file is extended, never the other
   496    543   ** way around.  The SQLITE_IOCAP_SEQUENTIAL property means that
   497    544   ** information is written to disk in the same order as calls
   498         -** to xWrite().
          545  +** to xWrite().  The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
          546  +** after reboot following a crash or power loss, the only bytes in a
          547  +** file that were written at the application level might have changed
          548  +** and that adjacent bytes, even bytes within the same sector are
          549  +** guaranteed to be unchanged.
   499    550   */
   500         -#define SQLITE_IOCAP_ATOMIC          0x00000001
   501         -#define SQLITE_IOCAP_ATOMIC512       0x00000002
   502         -#define SQLITE_IOCAP_ATOMIC1K        0x00000004
   503         -#define SQLITE_IOCAP_ATOMIC2K        0x00000008
   504         -#define SQLITE_IOCAP_ATOMIC4K        0x00000010
   505         -#define SQLITE_IOCAP_ATOMIC8K        0x00000020
   506         -#define SQLITE_IOCAP_ATOMIC16K       0x00000040
   507         -#define SQLITE_IOCAP_ATOMIC32K       0x00000080
   508         -#define SQLITE_IOCAP_ATOMIC64K       0x00000100
   509         -#define SQLITE_IOCAP_SAFE_APPEND     0x00000200
   510         -#define SQLITE_IOCAP_SEQUENTIAL      0x00000400
          551  +#define SQLITE_IOCAP_ATOMIC                 0x00000001
          552  +#define SQLITE_IOCAP_ATOMIC512              0x00000002
          553  +#define SQLITE_IOCAP_ATOMIC1K               0x00000004
          554  +#define SQLITE_IOCAP_ATOMIC2K               0x00000008
          555  +#define SQLITE_IOCAP_ATOMIC4K               0x00000010
          556  +#define SQLITE_IOCAP_ATOMIC8K               0x00000020
          557  +#define SQLITE_IOCAP_ATOMIC16K              0x00000040
          558  +#define SQLITE_IOCAP_ATOMIC32K              0x00000080
          559  +#define SQLITE_IOCAP_ATOMIC64K              0x00000100
          560  +#define SQLITE_IOCAP_SAFE_APPEND            0x00000200
          561  +#define SQLITE_IOCAP_SEQUENTIAL             0x00000400
          562  +#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN  0x00000800
          563  +#define SQLITE_IOCAP_POWERSAFE_OVERWRITE    0x00001000
   511    564   
   512    565   /*
   513    566   ** CAPI3REF: File Locking Levels
   514    567   **
   515    568   ** SQLite uses one of these integer values as the second
   516    569   ** argument to calls it makes to the xLock() and xUnlock() methods
   517    570   ** of an [sqlite3_io_methods] object.
................................................................................
   531    584   **
   532    585   ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the
   533    586   ** sync operation only needs to flush data to mass storage.  Inode
   534    587   ** information need not be flushed. If the lower four bits of the flag
   535    588   ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics.
   536    589   ** If the lower four bits equal SQLITE_SYNC_FULL, that means
   537    590   ** to use Mac OS X style fullsync instead of fsync().
          591  +**
          592  +** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags
          593  +** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL
          594  +** settings.  The [synchronous pragma] determines when calls to the
          595  +** xSync VFS method occur and applies uniformly across all platforms.
          596  +** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how
          597  +** energetic or rigorous or forceful the sync operations are and
          598  +** only make a difference on Mac OSX for the default SQLite code.
          599  +** (Third-party VFS implementations might also make the distinction
          600  +** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the
          601  +** operating systems natively supported by SQLite, only Mac OSX
          602  +** cares about the difference.)
   538    603   */
   539    604   #define SQLITE_SYNC_NORMAL        0x00002
   540    605   #define SQLITE_SYNC_FULL          0x00003
   541    606   #define SQLITE_SYNC_DATAONLY      0x00010
   542    607   
   543    608   /*
   544    609   ** CAPI3REF: OS Interface Open File Handle
................................................................................
   555    620   struct sqlite3_file {
   556    621     const struct sqlite3_io_methods *pMethods;  /* Methods for an open file */
   557    622   };
   558    623   
   559    624   /*
   560    625   ** CAPI3REF: OS Interface File Virtual Methods Object
   561    626   **
   562         -** Every file opened by the [sqlite3_vfs] xOpen method populates an
          627  +** Every file opened by the [sqlite3_vfs.xOpen] method populates an
   563    628   ** [sqlite3_file] object (or, more commonly, a subclass of the
   564    629   ** [sqlite3_file] object) with a pointer to an instance of this object.
   565    630   ** This object defines the methods used to perform various operations
   566    631   ** against the open file represented by the [sqlite3_file] object.
   567    632   **
   568         -** If the xOpen method sets the sqlite3_file.pMethods element 
          633  +** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element 
   569    634   ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method
   570         -** may be invoked even if the xOpen reported that it failed.  The
   571         -** only way to prevent a call to xClose following a failed xOpen
   572         -** is for the xOpen to set the sqlite3_file.pMethods element to NULL.
          635  +** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed.  The
          636  +** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]
          637  +** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element
          638  +** to NULL.
   573    639   **
   574    640   ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
   575    641   ** [SQLITE_SYNC_FULL].  The first choice is the normal fsync().
   576    642   ** The second choice is a Mac OS X style fullsync.  The [SQLITE_SYNC_DATAONLY]
   577    643   ** flag may be ORed in to indicate that only the data of the file
   578    644   ** and not its inode needs to be synced.
   579    645   **
................................................................................
   599    665   ** write return values.  Potential uses for xFileControl() might be
   600    666   ** functions to enable blocking locks with timeouts, to change the
   601    667   ** locking strategy (for example to use dot-file locks), to inquire
   602    668   ** about the status of a lock, or to break stale locks.  The SQLite
   603    669   ** core reserves all opcodes less than 100 for its own use.
   604    670   ** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
   605    671   ** Applications that define a custom xFileControl method should use opcodes
   606         -** greater than 100 to avoid conflicts.
          672  +** greater than 100 to avoid conflicts.  VFS implementations should
          673  +** return [SQLITE_NOTFOUND] for file control opcodes that they do not
          674  +** recognize.
   607    675   **
   608    676   ** The xSectorSize() method returns the sector size of the
   609    677   ** device that underlies the file.  The sector size is the
   610    678   ** minimum write that can be performed without disturbing
   611    679   ** other bytes in the file.  The xDeviceCharacteristics()
   612    680   ** method returns a bit vector describing behaviors of the
   613    681   ** underlying device:
................................................................................
   654    722     int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
   655    723     int (*xLock)(sqlite3_file*, int);
   656    724     int (*xUnlock)(sqlite3_file*, int);
   657    725     int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);
   658    726     int (*xFileControl)(sqlite3_file*, int op, void *pArg);
   659    727     int (*xSectorSize)(sqlite3_file*);
   660    728     int (*xDeviceCharacteristics)(sqlite3_file*);
          729  +  /* Methods above are valid for version 1 */
          730  +  int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
          731  +  int (*xShmLock)(sqlite3_file*, int offset, int n, int flags);
          732  +  void (*xShmBarrier)(sqlite3_file*);
          733  +  int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
          734  +  /* Methods above are valid for version 2 */
   661    735     /* Additional methods may be added in future releases */
   662    736   };
   663    737   
   664    738   /*
   665    739   ** CAPI3REF: Standard File Control Opcodes
   666    740   **
   667    741   ** These integer constants are opcodes for the xFileControl method
................................................................................
   671    745   ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging.  This
   672    746   ** opcode causes the xFileControl method to write the current state of
   673    747   ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
   674    748   ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
   675    749   ** into an integer that the pArg argument points to. This capability
   676    750   ** is used during testing and only needs to be supported when SQLITE_TEST
   677    751   ** is defined.
   678         -*/
   679         -#define SQLITE_FCNTL_LOCKSTATE        1
   680         -#define SQLITE_GET_LOCKPROXYFILE      2
   681         -#define SQLITE_SET_LOCKPROXYFILE      3
   682         -#define SQLITE_LAST_ERRNO             4
          752  +** <ul>
          753  +** <li>[[SQLITE_FCNTL_SIZE_HINT]]
          754  +** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
          755  +** layer a hint of how large the database file will grow to be during the
          756  +** current transaction.  This hint is not guaranteed to be accurate but it
          757  +** is often close.  The underlying VFS might choose to preallocate database
          758  +** file space based on this hint in order to help writes to the database
          759  +** file run faster.
          760  +**
          761  +** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
          762  +** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
          763  +** extends and truncates the database file in chunks of a size specified
          764  +** by the user. The fourth argument to [sqlite3_file_control()] should 
          765  +** point to an integer (type int) containing the new chunk-size to use
          766  +** for the nominated database. Allocating database file space in large
          767  +** chunks (say 1MB at a time), may reduce file-system fragmentation and
          768  +** improve performance on some systems.
          769  +**
          770  +** <li>[[SQLITE_FCNTL_FILE_POINTER]]
          771  +** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
          772  +** to the [sqlite3_file] object associated with a particular database
          773  +** connection.  See the [sqlite3_file_control()] documentation for
          774  +** additional information.
          775  +**
          776  +** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
          777  +** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
          778  +** SQLite and sent to all VFSes in place of a call to the xSync method
          779  +** when the database connection has [PRAGMA synchronous] set to OFF.)^
          780  +** Some specialized VFSes need this signal in order to operate correctly
          781  +** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most 
          782  +** VFSes do not need this signal and should silently ignore this opcode.
          783  +** Applications should not call [sqlite3_file_control()] with this
          784  +** opcode as doing so may disrupt the operation of the specialized VFSes
          785  +** that do require it.  
          786  +**
          787  +** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
          788  +** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
          789  +** retry counts and intervals for certain disk I/O operations for the
          790  +** windows [VFS] in order to provide robustness in the presence of
          791  +** anti-virus programs.  By default, the windows VFS will retry file read,
          792  +** file write, and file delete operations up to 10 times, with a delay
          793  +** of 25 milliseconds before the first retry and with the delay increasing
          794  +** by an additional 25 milliseconds with each subsequent retry.  This
          795  +** opcode allows these two values (10 retries and 25 milliseconds of delay)
          796  +** to be adjusted.  The values are changed for all database connections
          797  +** within the same process.  The argument is a pointer to an array of two
          798  +** integers where the first integer i the new retry count and the second
          799  +** integer is the delay.  If either integer is negative, then the setting
          800  +** is not changed but instead the prior value of that setting is written
          801  +** into the array entry, allowing the current retry settings to be
          802  +** interrogated.  The zDbName parameter is ignored.
          803  +**
          804  +** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
          805  +** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
          806  +** persistent [WAL | Write Ahead Log] setting.  By default, the auxiliary
          807  +** write ahead log and shared memory files used for transaction control
          808  +** are automatically deleted when the latest connection to the database
          809  +** closes.  Setting persistent WAL mode causes those files to persist after
          810  +** close.  Persisting the files is useful when other processes that do not
          811  +** have write permission on the directory containing the database file want
          812  +** to read the database file, as the WAL and shared memory files must exist
          813  +** in order for the database to be readable.  The fourth parameter to
          814  +** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
          815  +** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
          816  +** WAL mode.  If the integer is -1, then it is overwritten with the current
          817  +** WAL persistence setting.
          818  +**
          819  +** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]
          820  +** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
          821  +** persistent "powersafe-overwrite" or "PSOW" setting.  The PSOW setting
          822  +** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
          823  +** xDeviceCharacteristics methods. The fourth parameter to
          824  +** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
          825  +** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
          826  +** mode.  If the integer is -1, then it is overwritten with the current
          827  +** zero-damage mode setting.
          828  +**
          829  +** <li>[[SQLITE_FCNTL_OVERWRITE]]
          830  +** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
          831  +** a write transaction to indicate that, unless it is rolled back for some
          832  +** reason, the entire database file will be overwritten by the current 
          833  +** transaction. This is used by VACUUM operations.
          834  +**
          835  +** <li>[[SQLITE_FCNTL_VFSNAME]]
          836  +** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
          837  +** all [VFSes] in the VFS stack.  The names are of all VFS shims and the
          838  +** final bottom-level VFS are written into memory obtained from 
          839  +** [sqlite3_malloc()] and the result is stored in the char* variable
          840  +** that the fourth parameter of [sqlite3_file_control()] points to.
          841  +** The caller is responsible for freeing the memory when done.  As with
          842  +** all file-control actions, there is no guarantee that this will actually
          843  +** do anything.  Callers should initialize the char* variable to a NULL
          844  +** pointer in case this file-control is not implemented.  This file-control
          845  +** is intended for diagnostic use only.
          846  +**
          847  +** <li>[[SQLITE_FCNTL_PRAGMA]]
          848  +** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] 
          849  +** file control is sent to the open [sqlite3_file] object corresponding
          850  +** to the database file to which the pragma statement refers. ^The argument
          851  +** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
          852  +** pointers to strings (char**) in which the second element of the array
          853  +** is the name of the pragma and the third element is the argument to the
          854  +** pragma or NULL if the pragma has no argument.  ^The handler for an
          855  +** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element
          856  +** of the char** argument point to a string obtained from [sqlite3_mprintf()]
          857  +** or the equivalent and that string will become the result of the pragma or
          858  +** the error message if the pragma fails. ^If the
          859  +** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal 
          860  +** [PRAGMA] processing continues.  ^If the [SQLITE_FCNTL_PRAGMA]
          861  +** file control returns [SQLITE_OK], then the parser assumes that the
          862  +** VFS has handled the PRAGMA itself and the parser generates a no-op
          863  +** prepared statement.  ^If the [SQLITE_FCNTL_PRAGMA] file control returns
          864  +** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means
          865  +** that the VFS encountered an error while handling the [PRAGMA] and the
          866  +** compilation of the PRAGMA fails with an error.  ^The [SQLITE_FCNTL_PRAGMA]
          867  +** file control occurs at the beginning of pragma statement analysis and so
          868  +** it is able to override built-in [PRAGMA] statements.
          869  +**
          870  +** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
          871  +** ^This file-control may be invoked by SQLite on the database file handle
          872  +** shortly after it is opened in order to provide a custom VFS with access
          873  +** to the connections busy-handler callback. The argument is of type (void **)
          874  +** - an array of two (void *) values. The first (void *) actually points
          875  +** to a function of type (int (*)(void *)). In order to invoke the connections
          876  +** busy-handler, this function should be invoked with the second (void *) in
          877  +** the array as the only argument. If it returns non-zero, then the operation
          878  +** should be retried. If it returns zero, the custom VFS should abandon the
          879  +** current operation.
          880  +**
          881  +** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
          882  +** ^Application can invoke this file-control to have SQLite generate a
          883  +** temporary filename using the same algorithm that is followed to generate
          884  +** temporary filenames for TEMP tables and other internal uses.  The
          885  +** argument should be a char** which will be filled with the filename
          886  +** written into memory obtained from [sqlite3_malloc()].  The caller should
          887  +** invoke [sqlite3_free()] on the result to avoid a memory leak.
          888  +**
          889  +** </ul>
          890  +*/
          891  +#define SQLITE_FCNTL_LOCKSTATE               1
          892  +#define SQLITE_GET_LOCKPROXYFILE             2
          893  +#define SQLITE_SET_LOCKPROXYFILE             3
          894  +#define SQLITE_LAST_ERRNO                    4
          895  +#define SQLITE_FCNTL_SIZE_HINT               5
          896  +#define SQLITE_FCNTL_CHUNK_SIZE              6
          897  +#define SQLITE_FCNTL_FILE_POINTER            7
          898  +#define SQLITE_FCNTL_SYNC_OMITTED            8
          899  +#define SQLITE_FCNTL_WIN32_AV_RETRY          9
          900  +#define SQLITE_FCNTL_PERSIST_WAL            10
          901  +#define SQLITE_FCNTL_OVERWRITE              11
          902  +#define SQLITE_FCNTL_VFSNAME                12
          903  +#define SQLITE_FCNTL_POWERSAFE_OVERWRITE    13
          904  +#define SQLITE_FCNTL_PRAGMA                 14
          905  +#define SQLITE_FCNTL_BUSYHANDLER            15
          906  +#define SQLITE_FCNTL_TEMPFILENAME           16
   683    907   
   684    908   /*
   685    909   ** CAPI3REF: Mutex Handle
   686    910   **
   687    911   ** The mutex module within SQLite defines [sqlite3_mutex] to be an
   688    912   ** abstract type for a mutex object.  The SQLite core never looks
   689    913   ** at the internal representation of an [sqlite3_mutex].  It only
................................................................................
   694    918   typedef struct sqlite3_mutex sqlite3_mutex;
   695    919   
   696    920   /*
   697    921   ** CAPI3REF: OS Interface Object
   698    922   **
   699    923   ** An instance of the sqlite3_vfs object defines the interface between
   700    924   ** the SQLite core and the underlying operating system.  The "vfs"
   701         -** in the name of the object stands for "virtual file system".
          925  +** in the name of the object stands for "virtual file system".  See
          926  +** the [VFS | VFS documentation] for further information.
   702    927   **
   703    928   ** The value of the iVersion field is initially 1 but may be larger in
   704    929   ** future versions of SQLite.  Additional fields may be appended to this
   705    930   ** object when the iVersion value is increased.  Note that the structure
   706    931   ** of the sqlite3_vfs object changes in the transaction between
   707    932   ** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
   708    933   ** modified.
................................................................................
   723    948   ** or modify this field while holding a particular static mutex.
   724    949   ** The application should never modify anything within the sqlite3_vfs
   725    950   ** object once the object has been registered.
   726    951   **
   727    952   ** The zName field holds the name of the VFS module.  The name must
   728    953   ** be unique across all VFS modules.
   729    954   **
   730         -** SQLite will guarantee that the zFilename parameter to xOpen
          955  +** [[sqlite3_vfs.xOpen]]
          956  +** ^SQLite guarantees that the zFilename parameter to xOpen
   731    957   ** is either a NULL pointer or string obtained
   732         -** from xFullPathname().  SQLite further guarantees that
          958  +** from xFullPathname() with an optional suffix added.
          959  +** ^If a suffix is added to the zFilename parameter, it will
          960  +** consist of a single "-" character followed by no more than
          961  +** 11 alphanumeric and/or "-" characters.
          962  +** ^SQLite further guarantees that
   733    963   ** the string will be valid and unchanged until xClose() is
   734    964   ** called. Because of the previous sentence,
   735    965   ** the [sqlite3_file] can safely store a pointer to the
   736    966   ** filename if it needs to remember the filename for some reason.
   737         -** If the zFilename parameter is xOpen is a NULL pointer then xOpen
   738         -** must invent its own temporary name for the file.  Whenever the 
          967  +** If the zFilename parameter to xOpen is a NULL pointer then xOpen
          968  +** must invent its own temporary name for the file.  ^Whenever the 
   739    969   ** xFilename parameter is NULL it will also be the case that the
   740    970   ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].
   741    971   **
   742    972   ** The flags argument to xOpen() includes all bits set in
   743    973   ** the flags argument to [sqlite3_open_v2()].  Or if [sqlite3_open()]
   744    974   ** or [sqlite3_open16()] is used, then flags includes at least
   745    975   ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. 
   746    976   ** If xOpen() opens a file read-only then it sets *pOutFlags to
   747    977   ** include [SQLITE_OPEN_READONLY].  Other bits in *pOutFlags may be set.
   748    978   **
   749         -** SQLite will also add one of the following flags to the xOpen()
          979  +** ^(SQLite will also add one of the following flags to the xOpen()
   750    980   ** call, depending on the object being opened:
   751    981   **
   752    982   ** <ul>
   753    983   ** <li>  [SQLITE_OPEN_MAIN_DB]
   754    984   ** <li>  [SQLITE_OPEN_MAIN_JOURNAL]
   755    985   ** <li>  [SQLITE_OPEN_TEMP_DB]
   756    986   ** <li>  [SQLITE_OPEN_TEMP_JOURNAL]
   757    987   ** <li>  [SQLITE_OPEN_TRANSIENT_DB]
   758    988   ** <li>  [SQLITE_OPEN_SUBJOURNAL]
   759    989   ** <li>  [SQLITE_OPEN_MASTER_JOURNAL]
   760         -** </ul>
          990  +** <li>  [SQLITE_OPEN_WAL]
          991  +** </ul>)^
   761    992   **
   762    993   ** The file I/O implementation can use the object type flags to
   763    994   ** change the way it deals with files.  For example, an application
   764    995   ** that does not care about crash recovery or rollback might make
   765    996   ** the open of a journal file a no-op.  Writes to this journal would
   766    997   ** also be no-ops, and any attempt to read the journal would return
   767    998   ** SQLITE_IOERR.  Or the implementation might recognize that a database
................................................................................
   772   1003   **
   773   1004   ** <ul>
   774   1005   ** <li> [SQLITE_OPEN_DELETEONCLOSE]
   775   1006   ** <li> [SQLITE_OPEN_EXCLUSIVE]
   776   1007   ** </ul>
   777   1008   **
   778   1009   ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be
   779         -** deleted when it is closed.  The [SQLITE_OPEN_DELETEONCLOSE]
   780         -** will be set for TEMP  databases, journals and for subjournals.
         1010  +** deleted when it is closed.  ^The [SQLITE_OPEN_DELETEONCLOSE]
         1011  +** will be set for TEMP databases and their journals, transient
         1012  +** databases, and subjournals.
   781   1013   **
   782         -** The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
         1014  +** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
   783   1015   ** with the [SQLITE_OPEN_CREATE] flag, which are both directly
   784   1016   ** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
   785   1017   ** API.  The SQLITE_OPEN_EXCLUSIVE flag, when paired with the 
   786   1018   ** SQLITE_OPEN_CREATE, is used to indicate that file should always
   787   1019   ** be created, and that it is an error if it already exists.
   788   1020   ** It is <i>not</i> used to indicate the file should be opened 
   789   1021   ** for exclusive access.
   790   1022   **
   791         -** At least szOsFile bytes of memory are allocated by SQLite
         1023  +** ^At least szOsFile bytes of memory are allocated by SQLite
   792   1024   ** to hold the  [sqlite3_file] structure passed as the third
   793   1025   ** argument to xOpen.  The xOpen method does not have to
   794   1026   ** allocate the structure; it should just fill it in.  Note that
   795   1027   ** the xOpen method must set the sqlite3_file.pMethods to either
   796   1028   ** a valid [sqlite3_io_methods] object or to NULL.  xOpen must do
   797   1029   ** this even if the open fails.  SQLite expects that the sqlite3_file.pMethods
   798   1030   ** element will be valid after xOpen returns regardless of the success
   799   1031   ** or failure of the xOpen call.
   800   1032   **
   801         -** The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
         1033  +** [[sqlite3_vfs.xAccess]]
         1034  +** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
   802   1035   ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
   803   1036   ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
   804   1037   ** to test whether a file is at least readable.   The file can be a
   805   1038   ** directory.
   806   1039   **
   807         -** SQLite will always allocate at least mxPathname+1 bytes for the
         1040  +** ^SQLite will always allocate at least mxPathname+1 bytes for the
   808   1041   ** output buffer xFullPathname.  The exact size of the output buffer
   809   1042   ** is also passed as a parameter to both  methods. If the output buffer
   810   1043   ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is
   811   1044   ** handled as a fatal error by SQLite, vfs implementations should endeavor
   812   1045   ** to prevent this by setting mxPathname to a sufficiently large value.
   813   1046   **
   814         -** The xRandomness(), xSleep(), and xCurrentTime() interfaces
   815         -** are not strictly a part of the filesystem, but they are
         1047  +** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64()
         1048  +** interfaces are not strictly a part of the filesystem, but they are
   816   1049   ** included in the VFS structure for completeness.
   817   1050   ** The xRandomness() function attempts to return nBytes bytes
   818   1051   ** of good-quality randomness into zOut.  The return value is
   819   1052   ** the actual number of bytes of randomness obtained.
   820   1053   ** The xSleep() method causes the calling thread to sleep for at
   821         -** least the number of microseconds given.  The xCurrentTime()
   822         -** method returns a Julian Day Number for the current date and time.
         1054  +** least the number of microseconds given.  ^The xCurrentTime()
         1055  +** method returns a Julian Day Number for the current date and time as
         1056  +** a floating point value.
         1057  +** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
         1058  +** Day Number multiplied by 86400000 (the number of milliseconds in 
         1059  +** a 24-hour day).  
         1060  +** ^SQLite will use the xCurrentTimeInt64() method to get the current
         1061  +** date and time if that method is available (if iVersion is 2 or 
         1062  +** greater and the function pointer is not NULL) and will fall back
         1063  +** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
   823   1064   **
         1065  +** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
         1066  +** are not used by the SQLite core.  These optional interfaces are provided
         1067  +** by some VFSes to facilitate testing of the VFS code. By overriding 
         1068  +** system calls with functions under its control, a test program can
         1069  +** simulate faults and error conditions that would otherwise be difficult
         1070  +** or impossible to induce.  The set of system calls that can be overridden
         1071  +** varies from one VFS to another, and from one version of the same VFS to the
         1072  +** next.  Applications that use these interfaces must be prepared for any
         1073  +** or all of these interfaces to be NULL or for their behavior to change
         1074  +** from one release to the next.  Applications must not attempt to access
         1075  +** any of these methods if the iVersion of the VFS is less than 3.
   824   1076   */
   825   1077   typedef struct sqlite3_vfs sqlite3_vfs;
         1078  +typedef void (*sqlite3_syscall_ptr)(void);
   826   1079   struct sqlite3_vfs {
   827         -  int iVersion;            /* Structure version number */
         1080  +  int iVersion;            /* Structure version number (currently 3) */
   828   1081     int szOsFile;            /* Size of subclassed sqlite3_file */
   829   1082     int mxPathname;          /* Maximum file pathname length */
   830   1083     sqlite3_vfs *pNext;      /* Next registered VFS */
   831   1084     const char *zName;       /* Name of this virtual file system */
   832   1085     void *pAppData;          /* Pointer to application-specific data */
   833   1086     int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
   834   1087                  int flags, int *pOutFlags);
................................................................................
   839   1092     void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
   840   1093     void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);
   841   1094     void (*xDlClose)(sqlite3_vfs*, void*);
   842   1095     int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
   843   1096     int (*xSleep)(sqlite3_vfs*, int microseconds);
   844   1097     int (*xCurrentTime)(sqlite3_vfs*, double*);
   845   1098     int (*xGetLastError)(sqlite3_vfs*, int, char *);
   846         -  /* New fields may be appended in figure versions.  The iVersion
   847         -  ** value will increment whenever this happens. */
         1099  +  /*
         1100  +  ** The methods above are in version 1 of the sqlite_vfs object
         1101  +  ** definition.  Those that follow are added in version 2 or later
         1102  +  */
         1103  +  int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
         1104  +  /*
         1105  +  ** The methods above are in versions 1 and 2 of the sqlite_vfs object.
         1106  +  ** Those below are for version 3 and greater.
         1107  +  */
         1108  +  int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
         1109  +  sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);
         1110  +  const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
         1111  +  /*
         1112  +  ** The methods above are in versions 1 through 3 of the sqlite_vfs object.
         1113  +  ** New fields may be appended in figure versions.  The iVersion
         1114  +  ** value will increment whenever this happens. 
         1115  +  */
   848   1116   };
   849   1117   
   850   1118   /*
   851   1119   ** CAPI3REF: Flags for the xAccess VFS method
   852   1120   **
   853   1121   ** These integer constants can be used as the third parameter to
   854   1122   ** the xAccess method of an [sqlite3_vfs] object.  They determine
   855   1123   ** what kind of permissions the xAccess method is looking for.
   856   1124   ** With SQLITE_ACCESS_EXISTS, the xAccess method
   857   1125   ** simply checks whether the file exists.
   858   1126   ** With SQLITE_ACCESS_READWRITE, the xAccess method
   859         -** checks whether the file is both readable and writable.
         1127  +** checks whether the named directory is both readable and writable
         1128  +** (in other words, if files can be added, removed, and renamed within
         1129  +** the directory).
         1130  +** The SQLITE_ACCESS_READWRITE constant is currently used only by the
         1131  +** [temp_store_directory pragma], though this could change in a future
         1132  +** release of SQLite.
   860   1133   ** With SQLITE_ACCESS_READ, the xAccess method
   861         -** checks whether the file is readable.
         1134  +** checks whether the file is readable.  The SQLITE_ACCESS_READ constant is
         1135  +** currently unused, though it might be used in a future release of
         1136  +** SQLite.
   862   1137   */
   863   1138   #define SQLITE_ACCESS_EXISTS    0
   864         -#define SQLITE_ACCESS_READWRITE 1
   865         -#define SQLITE_ACCESS_READ      2
         1139  +#define SQLITE_ACCESS_READWRITE 1   /* Used by PRAGMA temp_store_directory */
         1140  +#define SQLITE_ACCESS_READ      2   /* Unused */
         1141  +
         1142  +/*
         1143  +** CAPI3REF: Flags for the xShmLock VFS method
         1144  +**
         1145  +** These integer constants define the various locking operations
         1146  +** allowed by the xShmLock method of [sqlite3_io_methods].  The
         1147  +** following are the only legal combinations of flags to the
         1148  +** xShmLock method:
         1149  +**
         1150  +** <ul>
         1151  +** <li>  SQLITE_SHM_LOCK | SQLITE_SHM_SHARED
         1152  +** <li>  SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE
         1153  +** <li>  SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED
         1154  +** <li>  SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE
         1155  +** </ul>
         1156  +**
         1157  +** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
         1158  +** was given no the corresponding lock.  
         1159  +**
         1160  +** The xShmLock method can transition between unlocked and SHARED or
         1161  +** between unlocked and EXCLUSIVE.  It cannot transition between SHARED
         1162  +** and EXCLUSIVE.
         1163  +*/
         1164  +#define SQLITE_SHM_UNLOCK       1
         1165  +#define SQLITE_SHM_LOCK         2
         1166  +#define SQLITE_SHM_SHARED       4
         1167  +#define SQLITE_SHM_EXCLUSIVE    8
         1168  +
         1169  +/*
         1170  +** CAPI3REF: Maximum xShmLock index
         1171  +**
         1172  +** The xShmLock method on [sqlite3_io_methods] may use values
         1173  +** between 0 and this upper bound as its "offset" argument.
         1174  +** The SQLite core will never attempt to acquire or release a
         1175  +** lock outside of this range
         1176  +*/
         1177  +#define SQLITE_SHM_NLOCK        8
         1178  +
   866   1179   
   867   1180   /*
   868   1181   ** CAPI3REF: Initialize The SQLite Library
   869   1182   **
   870   1183   ** ^The sqlite3_initialize() routine initializes the
   871   1184   ** SQLite library.  ^The sqlite3_shutdown() routine
   872   1185   ** deallocates any resources that were allocated by sqlite3_initialize().
................................................................................
   960   1273   ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
   961   1274   ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
   962   1275   ** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.
   963   1276   ** Note, however, that ^sqlite3_config() can be called as part of the
   964   1277   ** implementation of an application-defined [sqlite3_os_init()].
   965   1278   **
   966   1279   ** The first argument to sqlite3_config() is an integer
   967         -** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines
         1280  +** [configuration option] that determines
   968   1281   ** what property of SQLite is to be configured.  Subsequent arguments
   969         -** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option]
         1282  +** vary depending on the [configuration option]
   970   1283   ** in the first argument.
   971   1284   **
   972   1285   ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
   973   1286   ** ^If the option is unknown or SQLite is unable to set the option
   974   1287   ** then this routine returns a non-zero [error code].
   975   1288   */
   976   1289   SQLITE_API int sqlite3_config(int, ...);
................................................................................
   977   1290   
   978   1291   /*
   979   1292   ** CAPI3REF: Configure database connections
   980   1293   **
   981   1294   ** The sqlite3_db_config() interface is used to make configuration
   982   1295   ** changes to a [database connection].  The interface is similar to
   983   1296   ** [sqlite3_config()] except that the changes apply to a single
   984         -** [database connection] (specified in the first argument).  The
   985         -** sqlite3_db_config() interface should only be used immediately after
   986         -** the database connection is created using [sqlite3_open()],
   987         -** [sqlite3_open16()], or [sqlite3_open_v2()].  
         1297  +** [database connection] (specified in the first argument).
   988   1298   **
   989   1299   ** The second argument to sqlite3_db_config(D,V,...)  is the
   990         -** configuration verb - an integer code that indicates what
   991         -** aspect of the [database connection] is being configured.
   992         -** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE].
   993         -** New verbs are likely to be added in future releases of SQLite.
   994         -** Additional arguments depend on the verb.
         1300  +** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code 
         1301  +** that indicates what aspect of the [database connection] is being configured.
         1302  +** Subsequent arguments vary depending on the configuration verb.
   995   1303   **
   996   1304   ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if
   997   1305   ** the call is considered successful.
   998   1306   */
   999   1307   SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
  1000   1308   
  1001   1309   /*
................................................................................
  1019   1327   ** and that this object is only useful to a tiny minority of applications
  1020   1328   ** with specialized memory allocation requirements.  This object is
  1021   1329   ** also used during testing of SQLite in order to specify an alternative
  1022   1330   ** memory allocator that simulates memory out-of-memory conditions in
  1023   1331   ** order to verify that SQLite recovers gracefully from such
  1024   1332   ** conditions.
  1025   1333   **
  1026         -** The xMalloc and xFree methods must work like the
  1027         -** malloc() and free() functions from the standard C library.
  1028         -** The xRealloc method must work like realloc() from the standard C library
  1029         -** with the exception that if the second argument to xRealloc is zero,
  1030         -** xRealloc must be a no-op - it must not perform any allocation or
  1031         -** deallocation.  ^SQLite guarantees that the second argument to
         1334  +** The xMalloc, xRealloc, and xFree methods must work like the
         1335  +** malloc(), realloc() and free() functions from the standard C library.
         1336  +** ^SQLite guarantees that the second argument to
  1032   1337   ** xRealloc is always a value returned by a prior call to xRoundup.
  1033         -** And so in cases where xRoundup always returns a positive number,
  1034         -** xRealloc can perform exactly as the standard library realloc() and
  1035         -** still be in compliance with this specification.
  1036   1338   **
  1037   1339   ** xSize should return the allocated size of a memory allocation
  1038   1340   ** previously obtained from xMalloc or xRealloc.  The allocated size
  1039   1341   ** is always at least as big as the requested size but may be larger.
  1040   1342   **
  1041   1343   ** The xRoundup method returns what would be the allocated size of
  1042   1344   ** a memory allocation given a particular requested size.  Most memory
................................................................................
  1077   1379     int (*xInit)(void*);           /* Initialize the memory allocator */
  1078   1380     void (*xShutdown)(void*);      /* Deinitialize the memory allocator */
  1079   1381     void *pAppData;                /* Argument to xInit() and xShutdown() */
  1080   1382   };
  1081   1383   
  1082   1384   /*
  1083   1385   ** CAPI3REF: Configuration Options
         1386  +** KEYWORDS: {configuration option}
  1084   1387   **
  1085   1388   ** These constants are the available integer configuration options that
  1086   1389   ** can be passed as the first argument to the [sqlite3_config()] interface.
  1087   1390   **
  1088   1391   ** New configuration options may be added in future releases of SQLite.
  1089   1392   ** Existing configuration options might be discontinued.  Applications
  1090   1393   ** should check the return code from [sqlite3_config()] to make sure that
  1091   1394   ** the call worked.  The [sqlite3_config()] interface will return a
  1092   1395   ** non-zero [error code] if a discontinued or unsupported configuration option
  1093   1396   ** is invoked.
  1094   1397   **
  1095   1398   ** <dl>
  1096         -** <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
         1399  +** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
  1097   1400   ** <dd>There are no arguments to this option.  ^This option sets the
  1098   1401   ** [threading mode] to Single-thread.  In other words, it disables
  1099   1402   ** all mutexing and puts SQLite into a mode where it can only be used
  1100   1403   ** by a single thread.   ^If SQLite is compiled with
  1101   1404   ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
  1102   1405   ** it is not possible to change the [threading mode] from its default
  1103   1406   ** value of Single-thread and so [sqlite3_config()] will return 
  1104   1407   ** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD
  1105   1408   ** configuration option.</dd>
  1106   1409   **
  1107         -** <dt>SQLITE_CONFIG_MULTITHREAD</dt>
         1410  +** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt>
  1108   1411   ** <dd>There are no arguments to this option.  ^This option sets the
  1109   1412   ** [threading mode] to Multi-thread.  In other words, it disables
  1110   1413   ** mutexing on [database connection] and [prepared statement] objects.
  1111   1414   ** The application is responsible for serializing access to
  1112   1415   ** [database connections] and [prepared statements].  But other mutexes
  1113   1416   ** are enabled so that SQLite will be safe to use in a multi-threaded
  1114   1417   ** environment as long as no two threads attempt to use the same
  1115   1418   ** [database connection] at the same time.  ^If SQLite is compiled with
  1116   1419   ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
  1117   1420   ** it is not possible to set the Multi-thread [threading mode] and
  1118   1421   ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
  1119   1422   ** SQLITE_CONFIG_MULTITHREAD configuration option.</dd>
  1120   1423   **
  1121         -** <dt>SQLITE_CONFIG_SERIALIZED</dt>
         1424  +** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt>
  1122   1425   ** <dd>There are no arguments to this option.  ^This option sets the
  1123   1426   ** [threading mode] to Serialized. In other words, this option enables
  1124   1427   ** all mutexes including the recursive
  1125   1428   ** mutexes on [database connection] and [prepared statement] objects.
  1126   1429   ** In this mode (which is the default when SQLite is compiled with
  1127   1430   ** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access
  1128   1431   ** to [database connections] and [prepared statements] so that the
................................................................................
  1130   1433   ** same [prepared statement] in different threads at the same time.
  1131   1434   ** ^If SQLite is compiled with
  1132   1435   ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
  1133   1436   ** it is not possible to set the Serialized [threading mode] and
  1134   1437   ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
  1135   1438   ** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
  1136   1439   **
  1137         -** <dt>SQLITE_CONFIG_MALLOC</dt>
         1440  +** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
  1138   1441   ** <dd> ^(This option takes a single argument which is a pointer to an
  1139   1442   ** instance of the [sqlite3_mem_methods] structure.  The argument specifies
  1140   1443   ** alternative low-level memory allocation routines to be used in place of
  1141   1444   ** the memory allocation routines built into SQLite.)^ ^SQLite makes
  1142   1445   ** its own private copy of the content of the [sqlite3_mem_methods] structure
  1143   1446   ** before the [sqlite3_config()] call returns.</dd>
  1144   1447   **
  1145         -** <dt>SQLITE_CONFIG_GETMALLOC</dt>
         1448  +** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>
  1146   1449   ** <dd> ^(This option takes a single argument which is a pointer to an
  1147   1450   ** instance of the [sqlite3_mem_methods] structure.  The [sqlite3_mem_methods]
  1148   1451   ** structure is filled with the currently defined memory allocation routines.)^
  1149   1452   ** This option can be used to overload the default memory allocation
  1150   1453   ** routines with a wrapper that simulations memory allocation failure or
  1151   1454   ** tracks memory usage, for example. </dd>
  1152   1455   **
  1153         -** <dt>SQLITE_CONFIG_MEMSTATUS</dt>
         1456  +** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
  1154   1457   ** <dd> ^This option takes single argument of type int, interpreted as a 
  1155   1458   ** boolean, which enables or disables the collection of memory allocation 
  1156   1459   ** statistics. ^(When memory allocation statistics are disabled, the 
  1157   1460   ** following SQLite interfaces become non-operational:
  1158   1461   **   <ul>
  1159   1462   **   <li> [sqlite3_memory_used()]
  1160   1463   **   <li> [sqlite3_memory_highwater()]
  1161         -**   <li> [sqlite3_soft_heap_limit()]
         1464  +**   <li> [sqlite3_soft_heap_limit64()]
  1162   1465   **   <li> [sqlite3_status()]
  1163   1466   **   </ul>)^
  1164   1467   ** ^Memory allocation statistics are enabled by default unless SQLite is
  1165   1468   ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory
  1166   1469   ** allocation statistics are disabled by default.
  1167   1470   ** </dd>
  1168   1471   **
  1169         -** <dt>SQLITE_CONFIG_SCRATCH</dt>
         1472  +** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
  1170   1473   ** <dd> ^This option specifies a static memory buffer that SQLite can use for
  1171   1474   ** scratch memory.  There are three arguments:  A pointer an 8-byte
  1172         -** aligned memory buffer from which the scrach allocations will be
         1475  +** aligned memory buffer from which the scratch allocations will be
  1173   1476   ** drawn, the size of each scratch allocation (sz),
  1174   1477   ** and the maximum number of scratch allocations (N).  The sz
  1175         -** argument must be a multiple of 16. The sz parameter should be a few bytes
  1176         -** larger than the actual scratch space required due to internal overhead.
         1478  +** argument must be a multiple of 16.
  1177   1479   ** The first argument must be a pointer to an 8-byte aligned buffer
  1178   1480   ** of at least sz*N bytes of memory.
  1179         -** ^SQLite will use no more than one scratch buffer per thread.  So
  1180         -** N should be set to the expected maximum number of threads.  ^SQLite will
  1181         -** never require a scratch buffer that is more than 6 times the database
  1182         -** page size. ^If SQLite needs needs additional scratch memory beyond 
  1183         -** what is provided by this configuration option, then 
         1481  +** ^SQLite will use no more than two scratch buffers per thread.  So
         1482  +** N should be set to twice the expected maximum number of threads.
         1483  +** ^SQLite will never require a scratch buffer that is more than 6
         1484  +** times the database page size. ^If SQLite needs needs additional
         1485  +** scratch memory beyond what is provided by this configuration option, then 
  1184   1486   ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>
  1185   1487   **
  1186         -** <dt>SQLITE_CONFIG_PAGECACHE</dt>
         1488  +** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
  1187   1489   ** <dd> ^This option specifies a static memory buffer that SQLite can use for
  1188         -** the database page cache with the default page cache implemenation.  
         1490  +** the database page cache with the default page cache implementation.  
  1189   1491   ** This configuration should not be used if an application-define page
  1190         -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
         1492  +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option.
  1191   1493   ** There are three arguments to this option: A pointer to 8-byte aligned
  1192   1494   ** memory, the size of each page buffer (sz), and the number of pages (N).
  1193   1495   ** The sz argument should be the size of the largest database page
  1194   1496   ** (a power of two between 512 and 32768) plus a little extra for each
  1195   1497   ** page header.  ^The page header size is 20 to 40 bytes depending on
  1196   1498   ** the host architecture.  ^It is harmless, apart from the wasted memory,
  1197   1499   ** to make sz a little too large.  The first
  1198   1500   ** argument should point to an allocation of at least sz*N bytes of memory.
  1199   1501   ** ^SQLite will use the memory provided by the first argument to satisfy its
  1200   1502   ** memory needs for the first N pages that it adds to cache.  ^If additional
  1201   1503   ** page cache memory is needed beyond what is provided by this option, then
  1202   1504   ** SQLite goes to [sqlite3_malloc()] for the additional storage space.
  1203         -** ^The implementation might use one or more of the N buffers to hold 
  1204         -** memory accounting information. The pointer in the first argument must
         1505  +** The pointer in the first argument must
  1205   1506   ** be aligned to an 8-byte boundary or subsequent behavior of SQLite
  1206   1507   ** will be undefined.</dd>
  1207   1508   **
  1208         -** <dt>SQLITE_CONFIG_HEAP</dt>
         1509  +** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
  1209   1510   ** <dd> ^This option specifies a static memory buffer that SQLite will use
  1210   1511   ** for all of its dynamic memory allocation needs beyond those provided
  1211   1512   ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
  1212   1513   ** There are three arguments: An 8-byte aligned pointer to the memory,
  1213   1514   ** the number of bytes in the memory buffer, and the minimum allocation size.
  1214   1515   ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts
  1215   1516   ** to using its default memory allocator (the system malloc() implementation),
  1216   1517   ** undoing any prior invocation of [SQLITE_CONFIG_MALLOC].  ^If the
  1217   1518   ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
  1218   1519   ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
  1219   1520   ** allocator is engaged to handle all of SQLites memory allocation needs.
  1220   1521   ** The first pointer (the memory pointer) must be aligned to an 8-byte
  1221         -** boundary or subsequent behavior of SQLite will be undefined.</dd>
         1522  +** boundary or subsequent behavior of SQLite will be undefined.
         1523  +** The minimum allocation size is capped at 2**12. Reasonable values
         1524  +** for the minimum allocation size are 2**5 through 2**8.</dd>
  1222   1525   **
  1223         -** <dt>SQLITE_CONFIG_MUTEX</dt>
         1526  +** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
  1224   1527   ** <dd> ^(This option takes a single argument which is a pointer to an
  1225   1528   ** instance of the [sqlite3_mutex_methods] structure.  The argument specifies
  1226   1529   ** alternative low-level mutex routines to be used in place
  1227   1530   ** the mutex routines built into SQLite.)^  ^SQLite makes a copy of the
  1228   1531   ** content of the [sqlite3_mutex_methods] structure before the call to
  1229   1532   ** [sqlite3_config()] returns. ^If SQLite is compiled with
  1230   1533   ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
  1231   1534   ** the entire mutexing subsystem is omitted from the build and hence calls to
  1232   1535   ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will
  1233   1536   ** return [SQLITE_ERROR].</dd>
  1234   1537   **
  1235         -** <dt>SQLITE_CONFIG_GETMUTEX</dt>
         1538  +** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>
  1236   1539   ** <dd> ^(This option takes a single argument which is a pointer to an
  1237   1540   ** instance of the [sqlite3_mutex_methods] structure.  The
  1238   1541   ** [sqlite3_mutex_methods]
  1239   1542   ** structure is filled with the currently defined mutex routines.)^
  1240   1543   ** This option can be used to overload the default mutex allocation
  1241   1544   ** routines with a wrapper used to track mutex usage for performance
  1242   1545   ** profiling or testing, for example.   ^If SQLite is compiled with
  1243   1546   ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
  1244   1547   ** the entire mutexing subsystem is omitted from the build and hence calls to
  1245   1548   ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will
  1246   1549   ** return [SQLITE_ERROR].</dd>
  1247   1550   **
  1248         -** <dt>SQLITE_CONFIG_LOOKASIDE</dt>
         1551  +** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
  1249   1552   ** <dd> ^(This option takes two arguments that determine the default
  1250   1553   ** memory allocation for the lookaside memory allocator on each
  1251   1554   ** [database connection].  The first argument is the
  1252   1555   ** size of each lookaside buffer slot and the second is the number of
  1253   1556   ** slots allocated to each database connection.)^  ^(This option sets the
  1254   1557   ** <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
  1255   1558   ** verb to [sqlite3_db_config()] can be used to change the lookaside
  1256   1559   ** configuration on individual connections.)^ </dd>
  1257   1560   **
  1258         -** <dt>SQLITE_CONFIG_PCACHE</dt>
         1561  +** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
  1259   1562   ** <dd> ^(This option takes a single argument which is a pointer to
  1260         -** an [sqlite3_pcache_methods] object.  This object specifies the interface
         1563  +** an [sqlite3_pcache_methods2] object.  This object specifies the interface
  1261   1564   ** to a custom page cache implementation.)^  ^SQLite makes a copy of the
  1262   1565   ** object and uses it for page cache memory allocations.</dd>
  1263   1566   **
  1264         -** <dt>SQLITE_CONFIG_GETPCACHE</dt>
         1567  +** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
  1265   1568   ** <dd> ^(This option takes a single argument which is a pointer to an
  1266         -** [sqlite3_pcache_methods] object.  SQLite copies of the current
         1569  +** [sqlite3_pcache_methods2] object.  SQLite copies of the current
  1267   1570   ** page cache implementation into that object.)^ </dd>
  1268   1571   **
  1269         -** <dt>SQLITE_CONFIG_LOG</dt>
         1572  +** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
  1270   1573   ** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
  1271   1574   ** function with a call signature of void(*)(void*,int,const char*), 
  1272   1575   ** and a pointer to void. ^If the function pointer is not NULL, it is
  1273   1576   ** invoked by [sqlite3_log()] to process each logging event.  ^If the
  1274   1577   ** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op.
  1275   1578   ** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is
  1276   1579   ** passed through as the first parameter to the application-defined logger
................................................................................
  1280   1583   ** [extended result code].  ^The third parameter passed to the logger is
  1281   1584   ** log message after formatting via [sqlite3_snprintf()].
  1282   1585   ** The SQLite logging interface is not reentrant; the logger function
  1283   1586   ** supplied by the application must not invoke any SQLite interface.
  1284   1587   ** In a multi-threaded application, the application-defined logger
  1285   1588   ** function must be threadsafe. </dd>
  1286   1589   **
         1590  +** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
         1591  +** <dd> This option takes a single argument of type int. If non-zero, then
         1592  +** URI handling is globally enabled. If the parameter is zero, then URI handling
         1593  +** is globally disabled. If URI handling is globally enabled, all filenames
         1594  +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
         1595  +** specified as part of [ATTACH] commands are interpreted as URIs, regardless
         1596  +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
         1597  +** connection is opened. If it is globally disabled, filenames are
         1598  +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
         1599  +** database connection is opened. By default, URI handling is globally
         1600  +** disabled. The default value may be changed by compiling with the
         1601  +** [SQLITE_USE_URI] symbol defined.
         1602  +**
         1603  +** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
         1604  +** <dd> This option takes a single integer argument which is interpreted as
         1605  +** a boolean in order to enable or disable the use of covering indices for
         1606  +** full table scans in the query optimizer.  The default setting is determined
         1607  +** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
         1608  +** if that compile-time option is omitted.
         1609  +** The ability to disable the use of covering indices for full table scans
         1610  +** is because some incorrectly coded legacy applications might malfunction
         1611  +** malfunction when the optimization is enabled.  Providing the ability to
         1612  +** disable the optimization allows the older, buggy application code to work
         1613  +** without change even with newer versions of SQLite.
         1614  +**
         1615  +** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]
         1616  +** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
         1617  +** <dd> These options are obsolete and should not be used by new code.
         1618  +** They are retained for backwards compatibility but are now no-ops.
         1619  +** </dl>
         1620  +**
         1621  +** [[SQLITE_CONFIG_SQLLOG]]
         1622  +** <dt>SQLITE_CONFIG_SQLLOG
         1623  +** <dd>This option is only available if sqlite is compiled with the
         1624  +** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should
         1625  +** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int).
         1626  +** The second should be of type (void*). The callback is invoked by the library
         1627  +** in three separate circumstances, identified by the value passed as the
         1628  +** fourth parameter. If the fourth parameter is 0, then the database connection
         1629  +** passed as the second argument has just been opened. The third argument
         1630  +** points to a buffer containing the name of the main database file. If the
         1631  +** fourth parameter is 1, then the SQL statement that the third parameter
         1632  +** points to has just been executed. Or, if the fourth parameter is 2, then
         1633  +** the connection being passed as the second parameter is being closed. The
         1634  +** third parameter is passed NULL In this case.
  1287   1635   ** </dl>
  1288   1636   */
  1289   1637   #define SQLITE_CONFIG_SINGLETHREAD  1  /* nil */
  1290   1638   #define SQLITE_CONFIG_MULTITHREAD   2  /* nil */
  1291   1639   #define SQLITE_CONFIG_SERIALIZED    3  /* nil */
  1292   1640   #define SQLITE_CONFIG_MALLOC        4  /* sqlite3_mem_methods* */
  1293   1641   #define SQLITE_CONFIG_GETMALLOC     5  /* sqlite3_mem_methods* */
................................................................................
  1295   1643   #define SQLITE_CONFIG_PAGECACHE     7  /* void*, int sz, int N */
  1296   1644   #define SQLITE_CONFIG_HEAP          8  /* void*, int nByte, int min */
  1297   1645   #define SQLITE_CONFIG_MEMSTATUS     9  /* boolean */
  1298   1646   #define SQLITE_CONFIG_MUTEX        10  /* sqlite3_mutex_methods* */
  1299   1647   #define SQLITE_CONFIG_GETMUTEX     11  /* sqlite3_mutex_methods* */
  1300   1648   /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ 
  1301   1649   #define SQLITE_CONFIG_LOOKASIDE    13  /* int int */
  1302         -#define SQLITE_CONFIG_PCACHE       14  /* sqlite3_pcache_methods* */
  1303         -#define SQLITE_CONFIG_GETPCACHE    15  /* sqlite3_pcache_methods* */
         1650  +#define SQLITE_CONFIG_PCACHE       14  /* no-op */
         1651  +#define SQLITE_CONFIG_GETPCACHE    15  /* no-op */
  1304   1652   #define SQLITE_CONFIG_LOG          16  /* xFunc, void* */
         1653  +#define SQLITE_CONFIG_URI          17  /* int */
         1654  +#define SQLITE_CONFIG_PCACHE2      18  /* sqlite3_pcache_methods2* */
         1655  +#define SQLITE_CONFIG_GETPCACHE2   19  /* sqlite3_pcache_methods2* */
         1656  +#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20  /* int */
         1657  +#define SQLITE_CONFIG_SQLLOG       21  /* xSqllog, void* */
  1305   1658   
  1306   1659   /*
  1307   1660   ** CAPI3REF: Database Connection Configuration Options
  1308   1661   **
  1309   1662   ** These constants are the available integer configuration options that
  1310   1663   ** can be passed as the second argument to the [sqlite3_db_config()] interface.
  1311   1664   **
................................................................................
  1317   1670   ** is invoked.
  1318   1671   **
  1319   1672   ** <dl>
  1320   1673   ** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
  1321   1674   ** <dd> ^This option takes three additional arguments that determine the 
  1322   1675   ** [lookaside memory allocator] configuration for the [database connection].
  1323   1676   ** ^The first argument (the third parameter to [sqlite3_db_config()] is a
  1324         -** pointer to an memory buffer to use for lookaside memory.
         1677  +** pointer to a memory buffer to use for lookaside memory.
  1325   1678   ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
  1326   1679   ** may be NULL in which case SQLite will allocate the
  1327   1680   ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
  1328   1681   ** size of each lookaside buffer slot.  ^The third argument is the number of
  1329   1682   ** slots.  The size of the buffer in the first argument must be greater than
  1330   1683   ** or equal to the product of the second and third arguments.  The buffer
  1331   1684   ** must be aligned to an 8-byte boundary.  ^If the second argument to
  1332   1685   ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
  1333         -** rounded down to the next smaller
  1334         -** multiple of 8.  See also: [SQLITE_CONFIG_LOOKASIDE]</dd>
         1686  +** rounded down to the next smaller multiple of 8.  ^(The lookaside memory
         1687  +** configuration for a database connection can only be changed when that
         1688  +** connection is not currently using lookaside memory, or in other words
         1689  +** when the "current value" returned by
         1690  +** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
         1691  +** Any attempt to change the lookaside memory configuration when lookaside
         1692  +** memory is in use leaves the configuration unchanged and returns 
         1693  +** [SQLITE_BUSY].)^</dd>
         1694  +**
         1695  +** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
         1696  +** <dd> ^This option is used to enable or disable the enforcement of
         1697  +** [foreign key constraints].  There should be two additional arguments.
         1698  +** The first argument is an integer which is 0 to disable FK enforcement,
         1699  +** positive to enable FK enforcement or negative to leave FK enforcement
         1700  +** unchanged.  The second parameter is a pointer to an integer into which
         1701  +** is written 0 or 1 to indicate whether FK enforcement is off or on
         1702  +** following this call.  The second parameter may be a NULL pointer, in
         1703  +** which case the FK enforcement setting is not reported back. </dd>
         1704  +**
         1705  +** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
         1706  +** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
         1707  +** There should be two additional arguments.
         1708  +** The first argument is an integer which is 0 to disable triggers,
         1709  +** positive to enable triggers or negative to leave the setting unchanged.
         1710  +** The second parameter is a pointer to an integer into which
         1711  +** is written 0 or 1 to indicate whether triggers are disabled or enabled
         1712  +** following this call.  The second parameter may be a NULL pointer, in
         1713  +** which case the trigger setting is not reported back. </dd>
  1335   1714   **
  1336   1715   ** </dl>
  1337   1716   */
  1338         -#define SQLITE_DBCONFIG_LOOKASIDE    1001  /* void* int int */
         1717  +#define SQLITE_DBCONFIG_LOOKASIDE       1001  /* void* int int */
         1718  +#define SQLITE_DBCONFIG_ENABLE_FKEY     1002  /* int int* */
         1719  +#define SQLITE_DBCONFIG_ENABLE_TRIGGER  1003  /* int int* */
  1339   1720   
  1340   1721   
  1341   1722   /*
  1342   1723   ** CAPI3REF: Enable Or Disable Extended Result Codes
  1343   1724   **
  1344   1725   ** ^The sqlite3_extended_result_codes() routine enables or disables the
  1345   1726   ** [extended result codes] feature of SQLite. ^The extended result
................................................................................
  1355   1736   ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
  1356   1737   ** names are not also used by explicitly declared columns. ^If
  1357   1738   ** the table has a column of type [INTEGER PRIMARY KEY] then that column
  1358   1739   ** is another alias for the rowid.
  1359   1740   **
  1360   1741   ** ^This routine returns the [rowid] of the most recent
  1361   1742   ** successful [INSERT] into the database from the [database connection]
  1362         -** in the first argument.  ^If no successful [INSERT]s
         1743  +** in the first argument.  ^As of SQLite version 3.7.7, this routines
         1744  +** records the last insert rowid of both ordinary tables and [virtual tables].
         1745  +** ^If no successful [INSERT]s
  1363   1746   ** have ever occurred on that database connection, zero is returned.
  1364   1747   **
  1365         -** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted
  1366         -** row is returned by this routine as long as the trigger is running.
  1367         -** But once the trigger terminates, the value returned by this routine
  1368         -** reverts to the last value inserted before the trigger fired.)^
         1748  +** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
         1749  +** method, then this routine will return the [rowid] of the inserted
         1750  +** row as long as the trigger or virtual table method is running.
         1751  +** But once the trigger or virtual table method ends, the value returned 
         1752  +** by this routine reverts to what it was before the trigger or virtual
         1753  +** table method began.)^
  1369   1754   **
  1370   1755   ** ^An [INSERT] that fails due to a constraint violation is not a
  1371   1756   ** successful [INSERT] and does not change the value returned by this
  1372   1757   ** routine.  ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK,
  1373   1758   ** and INSERT OR ABORT make no changes to the return value of this
  1374   1759   ** routine when their insertion fails.  ^(When INSERT OR REPLACE
  1375   1760   ** encounters a constraint violation, it does not fail.  The
................................................................................
  1631   2016   ** was defined  (using [sqlite3_busy_handler()]) prior to calling
  1632   2017   ** this routine, that other busy handler is cleared.)^
  1633   2018   */
  1634   2019   SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
  1635   2020   
  1636   2021   /*
  1637   2022   ** CAPI3REF: Convenience Routines For Running Queries
         2023  +**
         2024  +** This is a legacy interface that is preserved for backwards compatibility.
         2025  +** Use of this interface is not recommended.
  1638   2026   **
  1639   2027   ** Definition: A <b>result table</b> is memory data structure created by the
  1640   2028   ** [sqlite3_get_table()] interface.  A result table records the
  1641   2029   ** complete query results from one or more queries.
  1642   2030   **
  1643   2031   ** The table conceptually has a number of rows and columns.  But
  1644   2032   ** these numbers are not part of the result table itself.  These
................................................................................
  1652   2040   ** in NULL pointers.  All other values are in their UTF-8 zero-terminated
  1653   2041   ** string representation as returned by [sqlite3_column_text()].
  1654   2042   **
  1655   2043   ** A result table might consist of one or more memory allocations.
  1656   2044   ** It is not safe to pass a result table directly to [sqlite3_free()].
  1657   2045   ** A result table should be deallocated using [sqlite3_free_table()].
  1658   2046   **
  1659         -** As an example of the result table format, suppose a query result
         2047  +** ^(As an example of the result table format, suppose a query result
  1660   2048   ** is as follows:
  1661   2049   **
  1662   2050   ** <blockquote><pre>
  1663   2051   **        Name        | Age
  1664   2052   **        -----------------------
  1665   2053   **        Alice       | 43
  1666   2054   **        Bob         | 28
................................................................................
  1676   2064   **        azResult&#91;1] = "Age";
  1677   2065   **        azResult&#91;2] = "Alice";
  1678   2066   **        azResult&#91;3] = "43";
  1679   2067   **        azResult&#91;4] = "Bob";
  1680   2068   **        azResult&#91;5] = "28";
  1681   2069   **        azResult&#91;6] = "Cindy";
  1682   2070   **        azResult&#91;7] = "21";
  1683         -** </pre></blockquote>
         2071  +** </pre></blockquote>)^
  1684   2072   **
  1685   2073   ** ^The sqlite3_get_table() function evaluates one or more
  1686   2074   ** semicolon-separated SQL statements in the zero-terminated UTF-8
  1687   2075   ** string of its 2nd parameter and returns a result table to the
  1688   2076   ** pointer given in its 3rd parameter.
  1689   2077   **
  1690   2078   ** After the application has finished with the result from sqlite3_get_table(),
  1691         -** it should pass the result table pointer to sqlite3_free_table() in order to
         2079  +** it must pass the result table pointer to sqlite3_free_table() in order to
  1692   2080   ** release the memory that was malloced.  Because of the way the
  1693   2081   ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling
  1694   2082   ** function must not try to call [sqlite3_free()] directly.  Only
  1695   2083   ** [sqlite3_free_table()] is able to release the memory properly and safely.
  1696   2084   **
  1697         -** ^(The sqlite3_get_table() interface is implemented as a wrapper around
         2085  +** The sqlite3_get_table() interface is implemented as a wrapper around
  1698   2086   ** [sqlite3_exec()].  The sqlite3_get_table() routine does not have access
  1699   2087   ** to any internal data structures of SQLite.  It uses only the public
  1700   2088   ** interface defined here.  As a consequence, errors that occur in the
  1701   2089   ** wrapper layer outside of the internal [sqlite3_exec()] call are not
  1702   2090   ** reflected in subsequent calls to [sqlite3_errcode()] or
  1703         -** [sqlite3_errmsg()].)^
         2091  +** [sqlite3_errmsg()].
  1704   2092   */
  1705   2093   SQLITE_API int sqlite3_get_table(
  1706   2094     sqlite3 *db,          /* An open database */
  1707   2095     const char *zSql,     /* SQL to be evaluated */
  1708   2096     char ***pazResult,    /* Results of the query */
  1709   2097     int *pnRow,           /* Number of result rows written here */
  1710   2098     int *pnColumn,        /* Number of result columns written here */
................................................................................
  1721   2109   ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
  1722   2110   ** results into memory obtained from [sqlite3_malloc()].
  1723   2111   ** The strings returned by these two routines should be
  1724   2112   ** released by [sqlite3_free()].  ^Both routines return a
  1725   2113   ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
  1726   2114   ** memory to hold the resulting string.
  1727   2115   **
  1728         -** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from
         2116  +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from
  1729   2117   ** the standard C library.  The result is written into the
  1730   2118   ** buffer supplied as the second parameter whose size is given by
  1731   2119   ** the first parameter. Note that the order of the
  1732   2120   ** first two parameters is reversed from snprintf().)^  This is an
  1733   2121   ** historical accident that cannot be fixed without breaking
  1734   2122   ** backwards compatibility.  ^(Note also that sqlite3_snprintf()
  1735   2123   ** returns a pointer to its buffer instead of the number of
................................................................................
  1740   2128   **
  1741   2129   ** ^As long as the buffer size is greater than zero, sqlite3_snprintf()
  1742   2130   ** guarantees that the buffer is always zero-terminated.  ^The first
  1743   2131   ** parameter "n" is the total size of the buffer, including space for
  1744   2132   ** the zero terminator.  So the longest string that can be completely
  1745   2133   ** written will be n-1 characters.
  1746   2134   **
         2135  +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf().
         2136  +**
  1747   2137   ** These routines all implement some additional formatting
  1748   2138   ** options that are useful for constructing SQL statements.
  1749   2139   ** All of the usual printf() formatting options apply.  In addition, there
  1750   2140   ** is are "%q", "%Q", and "%z" options.
  1751   2141   **
  1752         -** ^(The %q option works like %s in that it substitutes a null-terminated
         2142  +** ^(The %q option works like %s in that it substitutes a nul-terminated
  1753   2143   ** string from the argument list.  But %q also doubles every '\'' character.
  1754   2144   ** %q is designed for use inside a string literal.)^  By doubling each '\''
  1755   2145   ** character it escapes that character and allows it to be inserted into
  1756   2146   ** the string.
  1757   2147   **
  1758   2148   ** For example, assume the string variable zText contains text as follows:
  1759   2149   **
................................................................................
  1803   2193   ** ^(The "%z" formatting option works like "%s" but with the
  1804   2194   ** addition that after the string has been read and copied into
  1805   2195   ** the result, [sqlite3_free()] is called on the input string.)^
  1806   2196   */
  1807   2197   SQLITE_API char *sqlite3_mprintf(const char*,...);
  1808   2198   SQLITE_API char *sqlite3_vmprintf(const char*, va_list);
  1809   2199   SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...);
         2200  +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
  1810   2201   
  1811   2202   /*
  1812   2203   ** CAPI3REF: Memory Allocation Subsystem
  1813   2204   **
  1814   2205   ** The SQLite core uses these three routines for all of its own
  1815   2206   ** internal memory allocation needs. "Core" in the previous sentence
  1816   2207   ** does not include operating-system specific VFS implementation.  The
................................................................................
  1848   2239   ** ^If M is the size of the prior allocation, then min(N,M) bytes
  1849   2240   ** of the prior allocation are copied into the beginning of buffer returned
  1850   2241   ** by sqlite3_realloc() and the prior allocation is freed.
  1851   2242   ** ^If sqlite3_realloc() returns NULL, then the prior allocation
  1852   2243   ** is not freed.
  1853   2244   **
  1854   2245   ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
  1855         -** is always aligned to at least an 8 byte boundary.
         2246  +** is always aligned to at least an 8 byte boundary, or to a
         2247  +** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
         2248  +** option is used.
  1856   2249   **
  1857   2250   ** In SQLite version 3.5.0 and 3.5.1, it was possible to define
  1858   2251   ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
  1859   2252   ** implementation of these routines to be omitted.  That capability
  1860   2253   ** is no longer provided.  Only built-in memory allocators can be used.
  1861   2254   **
  1862         -** The Windows OS interface layer calls
         2255  +** Prior to SQLite version 3.7.10, the Windows OS interface layer called
  1863   2256   ** the system malloc() and free() directly when converting
  1864   2257   ** filenames between the UTF-8 encoding used by SQLite
  1865   2258   ** and whatever filename encoding is used by the particular Windows
  1866         -** installation.  Memory allocation errors are detected, but
  1867         -** they are reported back as [SQLITE_CANTOPEN] or
         2259  +** installation.  Memory allocation errors were detected, but
         2260  +** they were reported back as [SQLITE_CANTOPEN] or
  1868   2261   ** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
  1869   2262   **
  1870   2263   ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
  1871   2264   ** must be either NULL or else pointers obtained from a prior
  1872   2265   ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
  1873   2266   ** not yet been released.
  1874   2267   **
................................................................................
  1925   2318   ** method.
  1926   2319   */
  1927   2320   SQLITE_API void sqlite3_randomness(int N, void *P);
  1928   2321   
  1929   2322   /*
  1930   2323   ** CAPI3REF: Compile-Time Authorization Callbacks
  1931   2324   **
  1932         -** ^This routine registers a authorizer callback with a particular
         2325  +** ^This routine registers an authorizer callback with a particular
  1933   2326   ** [database connection], supplied in the first argument.
  1934   2327   ** ^The authorizer callback is invoked as SQL statements are being compiled
  1935   2328   ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
  1936   2329   ** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  ^At various
  1937   2330   ** points during the compilation process, as logic is being created
  1938   2331   ** to perform various actions, the authorizer callback is invoked to
  1939   2332   ** see if those actions are allowed.  ^The authorizer callback should
................................................................................
  2016   2409   ** CAPI3REF: Authorizer Return Codes
  2017   2410   **
  2018   2411   ** The [sqlite3_set_authorizer | authorizer callback function] must
  2019   2412   ** return either [SQLITE_OK] or one of these two constants in order
  2020   2413   ** to signal SQLite whether or not the action is permitted.  See the
  2021   2414   ** [sqlite3_set_authorizer | authorizer documentation] for additional
  2022   2415   ** information.
         2416  +**
         2417  +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
         2418  +** from the [sqlite3_vtab_on_conflict()] interface.
  2023   2419   */
  2024   2420   #define SQLITE_DENY   1   /* Abort the SQL statement with an error */
  2025   2421   #define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */
  2026   2422   
  2027   2423   /*
  2028   2424   ** CAPI3REF: Authorizer Action Codes
  2029   2425   **
................................................................................
  2091   2487   ** ^(Additional sqlite3_trace() callbacks might occur
  2092   2488   ** as each triggered subprogram is entered.  The callbacks for triggers
  2093   2489   ** contain a UTF-8 SQL comment that identifies the trigger.)^
  2094   2490   **
  2095   2491   ** ^The callback function registered by sqlite3_profile() is invoked
  2096   2492   ** as each SQL statement finishes.  ^The profile callback contains
  2097   2493   ** the original statement text and an estimate of wall-clock time
  2098         -** of how long that statement took to run.
         2494  +** of how long that statement took to run.  ^The profile callback
         2495  +** time is in units of nanoseconds, however the current implementation
         2496  +** is only capable of millisecond resolution so the six least significant
         2497  +** digits in the time are meaningless.  Future versions of SQLite
         2498  +** might provide greater resolution on the profiler callback.  The
         2499  +** sqlite3_profile() function is considered experimental and is
         2500  +** subject to change in future versions of SQLite.
  2099   2501   */
  2100   2502   SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
  2101   2503   SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
  2102   2504      void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
  2103   2505   
  2104   2506   /*
  2105   2507   ** CAPI3REF: Query Progress Callbacks
  2106   2508   **
  2107         -** ^This routine configures a callback function - the
  2108         -** progress callback - that is invoked periodically during long
  2109         -** running calls to [sqlite3_exec()], [sqlite3_step()] and
  2110         -** [sqlite3_get_table()].  An example use for this
         2509  +** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
         2510  +** function X to be invoked periodically during long running calls to
         2511  +** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
         2512  +** database connection D.  An example use for this
  2111   2513   ** interface is to keep a GUI updated during a large query.
         2514  +**
         2515  +** ^The parameter P is passed through as the only parameter to the 
         2516  +** callback function X.  ^The parameter N is the number of 
         2517  +** [virtual machine instructions] that are evaluated between successive
         2518  +** invocations of the callback X.
         2519  +**
         2520  +** ^Only a single progress handler may be defined at one time per
         2521  +** [database connection]; setting a new progress handler cancels the
         2522  +** old one.  ^Setting parameter X to NULL disables the progress handler.
         2523  +** ^The progress handler is also disabled by setting N to a value less
         2524  +** than 1.
  2112   2525   **
  2113   2526   ** ^If the progress callback returns non-zero, the operation is
  2114   2527   ** interrupted.  This feature can be used to implement a
  2115   2528   ** "Cancel" button on a GUI progress dialog box.
  2116   2529   **
  2117         -** The progress handler must not do anything that will modify
         2530  +** The progress handler callback must not do anything that will modify
  2118   2531   ** the database connection that invoked the progress handler.
  2119   2532   ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
  2120   2533   ** database connections for the meaning of "modify" in this paragraph.
  2121   2534   **
  2122   2535   */
  2123   2536   SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
  2124   2537   
  2125   2538   /*
  2126   2539   ** CAPI3REF: Opening A New Database Connection
  2127   2540   **
  2128         -** ^These routines open an SQLite database file whose name is given by the
         2541  +** ^These routines open an SQLite database file as specified by the 
  2129   2542   ** filename argument. ^The filename argument is interpreted as UTF-8 for
  2130   2543   ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
  2131   2544   ** order for sqlite3_open16(). ^(A [database connection] handle is usually
  2132   2545   ** returned in *ppDb, even if an error occurs.  The only exception is that
  2133   2546   ** if SQLite is unable to allocate memory to hold the [sqlite3] object,
  2134   2547   ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]
  2135   2548   ** object.)^ ^(If the database is opened (and/or created) successfully, then
................................................................................
  2148   2561   **
  2149   2562   ** The sqlite3_open_v2() interface works like sqlite3_open()
  2150   2563   ** except that it accepts two additional parameters for additional control
  2151   2564   ** over the new database connection.  ^(The flags parameter to
  2152   2565   ** sqlite3_open_v2() can take one of
  2153   2566   ** the following three values, optionally combined with the 
  2154   2567   ** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
  2155         -** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^
         2568  +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^
  2156   2569   **
  2157   2570   ** <dl>
  2158   2571   ** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
  2159   2572   ** <dd>The database is opened in read-only mode.  If the database does not
  2160   2573   ** already exist, an error is returned.</dd>)^
  2161   2574   **
  2162   2575   ** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
  2163   2576   ** <dd>The database is opened for reading and writing if possible, or reading
  2164   2577   ** only if the file is write protected by the operating system.  In either
  2165   2578   ** case the database must already exist, otherwise an error is returned.</dd>)^
  2166   2579   **
  2167   2580   ** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
  2168         -** <dd>The database is opened for reading and writing, and is creates it if
         2581  +** <dd>The database is opened for reading and writing, and is created if
  2169   2582   ** it does not already exist. This is the behavior that is always used for
  2170   2583   ** sqlite3_open() and sqlite3_open16().</dd>)^
  2171   2584   ** </dl>
  2172   2585   **
  2173   2586   ** If the 3rd parameter to sqlite3_open_v2() is not one of the
  2174         -** combinations shown above or one of the combinations shown above combined
  2175         -** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX],
  2176         -** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags,
         2587  +** combinations shown above optionally combined with other
         2588  +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
  2177   2589   ** then the behavior is undefined.
  2178   2590   **
  2179   2591   ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection
  2180   2592   ** opens in the multi-thread [threading mode] as long as the single-thread
  2181   2593   ** mode has not been set at compile-time or start-time.  ^If the
  2182   2594   ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens
  2183   2595   ** in the serialized [threading mode] unless single-thread was
  2184   2596   ** previously selected at compile-time or start-time.
  2185   2597   ** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be
  2186   2598   ** eligible to use [shared cache mode], regardless of whether or not shared
  2187   2599   ** cache is enabled using [sqlite3_enable_shared_cache()].  ^The
  2188   2600   ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not
  2189   2601   ** participate in [shared cache mode] even if it is enabled.
         2602  +**
         2603  +** ^The fourth parameter to sqlite3_open_v2() is the name of the
         2604  +** [sqlite3_vfs] object that defines the operating system interface that
         2605  +** the new database connection should use.  ^If the fourth parameter is
         2606  +** a NULL pointer then the default [sqlite3_vfs] object is used.
  2190   2607   **
  2191   2608   ** ^If the filename is ":memory:", then a private, temporary in-memory database
  2192   2609   ** is created for the connection.  ^This in-memory database will vanish when
  2193   2610   ** the database connection is closed.  Future versions of SQLite might
  2194   2611   ** make use of additional special filenames that begin with the ":" character.
  2195   2612   ** It is recommended that when a database filename actually does begin with
  2196   2613   ** a ":" character you should prefix the filename with a pathname such as
  2197   2614   ** "./" to avoid ambiguity.
  2198   2615   **
  2199   2616   ** ^If the filename is an empty string, then a private, temporary
  2200   2617   ** on-disk database will be created.  ^This private database will be
  2201   2618   ** automatically deleted as soon as the database connection is closed.
  2202   2619   **
  2203         -** ^The fourth parameter to sqlite3_open_v2() is the name of the
  2204         -** [sqlite3_vfs] object that defines the operating system interface that
  2205         -** the new database connection should use.  ^If the fourth parameter is
  2206         -** a NULL pointer then the default [sqlite3_vfs] object is used.
         2620  +** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3>
         2621  +**
         2622  +** ^If [URI filename] interpretation is enabled, and the filename argument
         2623  +** begins with "file:", then the filename is interpreted as a URI. ^URI
         2624  +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is
         2625  +** set in the fourth argument to sqlite3_open_v2(), or if it has
         2626  +** been enabled globally using the [SQLITE_CONFIG_URI] option with the
         2627  +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.
         2628  +** As of SQLite version 3.7.7, URI filename interpretation is turned off
         2629  +** by default, but future releases of SQLite might enable URI filename
         2630  +** interpretation by default.  See "[URI filenames]" for additional
         2631  +** information.
         2632  +**
         2633  +** URI filenames are parsed according to RFC 3986. ^If the URI contains an
         2634  +** authority, then it must be either an empty string or the string 
         2635  +** "localhost". ^If the authority is not an empty string or "localhost", an 
         2636  +** error is returned to the caller. ^The fragment component of a URI, if 
         2637  +** present, is ignored.
         2638  +**
         2639  +** ^SQLite uses the path component of the URI as the name of the disk file
         2640  +** which contains the database. ^If the path begins with a '/' character, 
         2641  +** then it is interpreted as an absolute path. ^If the path does not begin 
         2642  +** with a '/' (meaning that the authority section is omitted from the URI)
         2643  +** then the path is interpreted as a relative path. 
         2644  +** ^On windows, the first component of an absolute path 
         2645  +** is a drive specification (e.g. "C:").
         2646  +**
         2647  +** [[core URI query parameters]]
         2648  +** The query component of a URI may contain parameters that are interpreted
         2649  +** either by SQLite itself, or by a [VFS | custom VFS implementation].
         2650  +** SQLite interprets the following three query parameters:
         2651  +**
         2652  +** <ul>
         2653  +**   <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
         2654  +**     a VFS object that provides the operating system interface that should
         2655  +**     be used to access the database file on disk. ^If this option is set to
         2656  +**     an empty string the default VFS object is used. ^Specifying an unknown
         2657  +**     VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is
         2658  +**     present, then the VFS specified by the option takes precedence over
         2659  +**     the value passed as the fourth parameter to sqlite3_open_v2().
         2660  +**
         2661  +**   <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
         2662  +**     "rwc", or "memory". Attempting to set it to any other value is
         2663  +**     an error)^. 
         2664  +**     ^If "ro" is specified, then the database is opened for read-only 
         2665  +**     access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the 
         2666  +**     third argument to sqlite3_open_v2(). ^If the mode option is set to 
         2667  +**     "rw", then the database is opened for read-write (but not create) 
         2668  +**     access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had 
         2669  +**     been set. ^Value "rwc" is equivalent to setting both 
         2670  +**     SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE.  ^If the mode option is
         2671  +**     set to "memory" then a pure [in-memory database] that never reads
         2672  +**     or writes from disk is used. ^It is an error to specify a value for
         2673  +**     the mode parameter that is less restrictive than that specified by
         2674  +**     the flags passed in the third parameter to sqlite3_open_v2().
         2675  +**
         2676  +**   <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
         2677  +**     "private". ^Setting it to "shared" is equivalent to setting the
         2678  +**     SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to
         2679  +**     sqlite3_open_v2(). ^Setting the cache parameter to "private" is 
         2680  +**     equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
         2681  +**     ^If sqlite3_open_v2() is used and the "cache" parameter is present in
         2682  +**     a URI filename, its value overrides any behaviour requested by setting
         2683  +**     SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
         2684  +** </ul>
         2685  +**
         2686  +** ^Specifying an unknown parameter in the query component of a URI is not an
         2687  +** error.  Future versions of SQLite might understand additional query
         2688  +** parameters.  See "[query parameters with special meaning to SQLite]" for
         2689  +** additional information.
         2690  +**
         2691  +** [[URI filename examples]] <h3>URI filename examples</h3>
         2692  +**
         2693  +** <table border="1" align=center cellpadding=5>
         2694  +** <tr><th> URI filenames <th> Results
         2695  +** <tr><td> file:data.db <td> 
         2696  +**          Open the file "data.db" in the current directory.
         2697  +** <tr><td> file:/home/fred/data.db<br>
         2698  +**          file:///home/fred/data.db <br> 
         2699  +**          file://localhost/home/fred/data.db <br> <td> 
         2700  +**          Open the database file "/home/fred/data.db".
         2701  +** <tr><td> file://darkstar/home/fred/data.db <td> 
         2702  +**          An error. "darkstar" is not a recognized authority.
         2703  +** <tr><td style="white-space:nowrap"> 
         2704  +**          file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
         2705  +**     <td> Windows only: Open the file "data.db" on fred's desktop on drive
         2706  +**          C:. Note that the %20 escaping in this example is not strictly 
         2707  +**          necessary - space characters can be used literally
         2708  +**          in URI filenames.
         2709  +** <tr><td> file:data.db?mode=ro&cache=private <td> 
         2710  +**          Open file "data.db" in the current directory for read-only access.
         2711  +**          Regardless of whether or not shared-cache mode is enabled by
         2712  +**          default, use a private cache.
         2713  +** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
         2714  +**          Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
         2715  +** <tr><td> file:data.db?mode=readonly <td> 
         2716  +**          An error. "readonly" is not a valid option for the "mode" parameter.
         2717  +** </table>
         2718  +**
         2719  +** ^URI hexadecimal escape sequences (%HH) are supported within the path and
         2720  +** query components of a URI. A hexadecimal escape sequence consists of a
         2721  +** percent sign - "%" - followed by exactly two hexadecimal digits 
         2722  +** specifying an octet value. ^Before the path or query components of a
         2723  +** URI filename are interpreted, they are encoded using UTF-8 and all 
         2724  +** hexadecimal escape sequences replaced by a single byte containing the
         2725  +** corresponding octet. If this process generates an invalid UTF-8 encoding,
         2726  +** the results are undefined.
  2207   2727   **
  2208   2728   ** <b>Note to Windows users:</b>  The encoding used for the filename argument
  2209   2729   ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever
  2210   2730   ** codepage is currently defined.  Filenames containing international
  2211   2731   ** characters must be converted to UTF-8 prior to passing them into
  2212   2732   ** sqlite3_open() or sqlite3_open_v2().
         2733  +**
         2734  +** <b>Note to Windows Runtime users:</b>  The temporary directory must be set
         2735  +** prior to calling sqlite3_open() or sqlite3_open_v2().  Otherwise, various
         2736  +** features that require the use of temporary files may fail.
         2737  +**
         2738  +** See also: [sqlite3_temp_directory]
  2213   2739   */
  2214   2740   SQLITE_API int sqlite3_open(
  2215   2741     const char *filename,   /* Database filename (UTF-8) */
  2216   2742     sqlite3 **ppDb          /* OUT: SQLite db handle */
  2217   2743   );
  2218   2744   SQLITE_API int sqlite3_open16(
  2219   2745     const void *filename,   /* Database filename (UTF-16) */
................................................................................
  2221   2747   );
  2222   2748   SQLITE_API int sqlite3_open_v2(
  2223   2749     const char *filename,   /* Database filename (UTF-8) */
  2224   2750     sqlite3 **ppDb,         /* OUT: SQLite db handle */
  2225   2751     int flags,              /* Flags */
  2226   2752     const char *zVfs        /* Name of VFS module to use */
  2227   2753   );
         2754  +
         2755  +/*
         2756  +** CAPI3REF: Obtain Values For URI Parameters
         2757  +**
         2758  +** These are utility routines, useful to VFS implementations, that check
         2759  +** to see if a database file was a URI that contained a specific query 
         2760  +** parameter, and if so obtains the value of that query parameter.
         2761  +**
         2762  +** If F is the database filename pointer passed into the xOpen() method of 
         2763  +** a VFS implementation when the flags parameter to xOpen() has one or 
         2764  +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
         2765  +** P is the name of the query parameter, then
         2766  +** sqlite3_uri_parameter(F,P) returns the value of the P
         2767  +** parameter if it exists or a NULL pointer if P does not appear as a 
         2768  +** query parameter on F.  If P is a query parameter of F
         2769  +** has no explicit value, then sqlite3_uri_parameter(F,P) returns
         2770  +** a pointer to an empty string.
         2771  +**
         2772  +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
         2773  +** parameter and returns true (1) or false (0) according to the value
         2774  +** of P.  The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
         2775  +** value of query parameter P is one of "yes", "true", or "on" in any
         2776  +** case or if the value begins with a non-zero number.  The 
         2777  +** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
         2778  +** query parameter P is one of "no", "false", or "off" in any case or
         2779  +** if the value begins with a numeric zero.  If P is not a query
         2780  +** parameter on F or if the value of P is does not match any of the
         2781  +** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
         2782  +**
         2783  +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
         2784  +** 64-bit signed integer and returns that integer, or D if P does not
         2785  +** exist.  If the value of P is something other than an integer, then
         2786  +** zero is returned.
         2787  +** 
         2788  +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
         2789  +** sqlite3_uri_boolean(F,P,B) returns B.  If F is not a NULL pointer and
         2790  +** is not a database file pathname pointer that SQLite passed into the xOpen
         2791  +** VFS method, then the behavior of this routine is undefined and probably
         2792  +** undesirable.
         2793  +*/
         2794  +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
         2795  +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
         2796  +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
         2797  +
  2228   2798   
  2229   2799   /*
  2230   2800   ** CAPI3REF: Error Codes And Messages
  2231   2801   **
  2232   2802   ** ^The sqlite3_errcode() interface returns the numeric [result code] or
  2233   2803   ** [extended result code] for the most recent failed sqlite3_* API call
  2234   2804   ** associated with a [database connection]. If a prior API call failed
................................................................................
  2240   2810   **
  2241   2811   ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
  2242   2812   ** text that describes the error, as either UTF-8 or UTF-16 respectively.
  2243   2813   ** ^(Memory to hold the error message string is managed internally.
  2244   2814   ** The application does not need to worry about freeing the result.
  2245   2815   ** However, the error string might be overwritten or deallocated by
  2246   2816   ** subsequent calls to other SQLite interface functions.)^
         2817  +**
         2818  +** ^The sqlite3_errstr() interface returns the English-language text
         2819  +** that describes the [result code], as UTF-8.
         2820  +** ^(Memory to hold the error message string is managed internally
         2821  +** and must not be freed by the application)^.
  2247   2822   **
  2248   2823   ** When the serialized [threading mode] is in use, it might be the
  2249   2824   ** case that a second error occurs on a separate thread in between
  2250   2825   ** the time of the first error and the call to these interfaces.
  2251   2826   ** When that happens, the second error will be reported since these
  2252   2827   ** interfaces always report the most recent result.  To avoid
  2253   2828   ** this, each thread can obtain exclusive use of the [database connection] D
................................................................................
  2259   2834   ** was invoked incorrectly by the application.  In that case, the
  2260   2835   ** error code and message may or may not be set.
  2261   2836   */
  2262   2837   SQLITE_API int sqlite3_errcode(sqlite3 *db);
  2263   2838   SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
  2264   2839   SQLITE_API const char *sqlite3_errmsg(sqlite3*);
  2265   2840   SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
         2841  +SQLITE_API const char *sqlite3_errstr(int);
  2266   2842   
  2267   2843   /*
  2268   2844   ** CAPI3REF: SQL Statement Object
  2269   2845   ** KEYWORDS: {prepared statement} {prepared statements}
  2270   2846   **
  2271   2847   ** An instance of this object represents a single SQL statement.
  2272   2848   ** This object is variously known as a "prepared statement" or a
................................................................................
  2294   2870   ** CAPI3REF: Run-time Limits
  2295   2871   **
  2296   2872   ** ^(This interface allows the size of various constructs to be limited
  2297   2873   ** on a connection by connection basis.  The first parameter is the
  2298   2874   ** [database connection] whose limit is to be set or queried.  The
  2299   2875   ** second parameter is one of the [limit categories] that define a
  2300   2876   ** class of constructs to be size limited.  The third parameter is the
  2301         -** new limit for that construct.  The function returns the old limit.)^
         2877  +** new limit for that construct.)^
  2302   2878   **
  2303   2879   ** ^If the new limit is a negative number, the limit is unchanged.
  2304         -** ^(For the limit category of SQLITE_LIMIT_XYZ there is a 
         2880  +** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a 
  2305   2881   ** [limits | hard upper bound]
  2306         -** set by a compile-time C preprocessor macro named 
  2307         -** [limits | SQLITE_MAX_XYZ].
         2882  +** set at compile-time by a C preprocessor macro called
         2883  +** [limits | SQLITE_MAX_<i>NAME</i>].
  2308   2884   ** (The "_LIMIT_" in the name is changed to "_MAX_".))^
  2309   2885   ** ^Attempts to increase a limit above its hard upper bound are
  2310   2886   ** silently truncated to the hard upper bound.
  2311   2887   **
         2888  +** ^Regardless of whether or not the limit was changed, the 
         2889  +** [sqlite3_limit()] interface returns the prior value of the limit.
         2890  +** ^Hence, to find the current value of a limit without changing it,
         2891  +** simply invoke this interface with the third parameter set to -1.
         2892  +**
  2312   2893   ** Run-time limits are intended for use in applications that manage
  2313   2894   ** both their own internal database and also databases that are controlled
  2314   2895   ** by untrusted external sources.  An example application might be a
  2315   2896   ** web browser that has its own databases for storing history and
  2316   2897   ** separate databases controlled by JavaScript applications downloaded
  2317   2898   ** off the Internet.  The internal databases can be given the
  2318   2899   ** large, default limits.  Databases managed by external sources can
................................................................................
  2332   2913   **
  2333   2914   ** These constants define various performance limits
  2334   2915   ** that can be lowered at run-time using [sqlite3_limit()].
  2335   2916   ** The synopsis of the meanings of the various limits is shown below.
  2336   2917   ** Additional information is available at [limits | Limits in SQLite].
  2337   2918   **
  2338   2919   ** <dl>
  2339         -** ^(<dt>SQLITE_LIMIT_LENGTH</dt>
  2340         -** <dd>The maximum size of any string or BLOB or table row.<dd>)^
         2920  +** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
         2921  +** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^
  2341   2922   **
  2342         -** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
         2923  +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
  2343   2924   ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^
  2344   2925   **
  2345         -** ^(<dt>SQLITE_LIMIT_COLUMN</dt>
         2926  +** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt>
  2346   2927   ** <dd>The maximum number of columns in a table definition or in the
  2347   2928   ** result set of a [SELECT] or the maximum number of columns in an index
  2348   2929   ** or in an ORDER BY or GROUP BY clause.</dd>)^
  2349   2930   **
  2350         -** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
         2931  +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
  2351   2932   ** <dd>The maximum depth of the parse tree on any expression.</dd>)^
  2352   2933   **
  2353         -** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
         2934  +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
  2354   2935   ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
  2355   2936   **
  2356         -** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
         2937  +** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
  2357   2938   ** <dd>The maximum number of instructions in a virtual machine program
  2358         -** used to implement an SQL statement.</dd>)^
         2939  +** used to implement an SQL statement.  This limit is not currently
         2940  +** enforced, though that might be added in some future release of
         2941  +** SQLite.</dd>)^
  2359   2942   **
  2360         -** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
         2943  +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
  2361   2944   ** <dd>The maximum number of arguments on a function.</dd>)^
  2362   2945   **
  2363         -** ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
         2946  +** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
  2364   2947   ** <dd>The maximum number of [ATTACH | attached databases].)^</dd>
  2365   2948   **
         2949  +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]]
  2366   2950   ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>
  2367   2951   ** <dd>The maximum length of the pattern argument to the [LIKE] or
  2368   2952   ** [GLOB] operators.</dd>)^
  2369   2953   **
         2954  +** [[SQLITE_LIMIT_VARIABLE_NUMBER]]
  2370   2955   ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>
  2371         -** <dd>The maximum number of variables in an SQL statement that can
  2372         -** be bound.</dd>)^
         2956  +** <dd>The maximum index number of any [parameter] in an SQL statement.)^
  2373   2957   **
  2374         -** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
         2958  +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
  2375   2959   ** <dd>The maximum depth of recursion for triggers.</dd>)^
  2376   2960   ** </dl>
  2377   2961   */
  2378   2962   #define SQLITE_LIMIT_LENGTH                    0
  2379   2963   #define SQLITE_LIMIT_SQL_LENGTH                1
  2380   2964   #define SQLITE_LIMIT_COLUMN                    2
  2381   2965   #define SQLITE_LIMIT_EXPR_DEPTH                3
................................................................................
  2407   2991   ** first zero terminator. ^If nByte is non-negative, then it is the maximum
  2408   2992   ** number of  bytes read from zSql.  ^When nByte is non-negative, the
  2409   2993   ** zSql string ends at either the first '\000' or '\u0000' character or
  2410   2994   ** the nByte-th byte, whichever comes first. If the caller knows
  2411   2995   ** that the supplied string is nul-terminated, then there is a small
  2412   2996   ** performance advantage to be gained by passing an nByte parameter that
  2413   2997   ** is equal to the number of bytes in the input string <i>including</i>
  2414         -** the nul-terminator bytes.
         2998  +** the nul-terminator bytes as this saves SQLite from having to
         2999  +** make a copy of the input string.
  2415   3000   **
  2416   3001   ** ^If pzTail is not NULL then *pzTail is made to point to the first byte
  2417   3002   ** past the end of the first SQL statement in zSql.  These routines only
  2418   3003   ** compile the first statement in zSql, so *pzTail is left pointing to
  2419   3004   ** what remains uncompiled.
  2420   3005   **
  2421   3006   ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be
................................................................................
  2437   3022   ** original SQL text. This causes the [sqlite3_step()] interface to
  2438   3023   ** behave differently in three ways:
  2439   3024   **
  2440   3025   ** <ol>
  2441   3026   ** <li>
  2442   3027   ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
  2443   3028   ** always used to do, [sqlite3_step()] will automatically recompile the SQL
  2444         -** statement and try to run it again.  ^If the schema has changed in
  2445         -** a way that makes the statement no longer valid, [sqlite3_step()] will still
  2446         -** return [SQLITE_SCHEMA].  But unlike the legacy behavior, [SQLITE_SCHEMA] is
  2447         -** now a fatal error.  Calling [sqlite3_prepare_v2()] again will not make the
  2448         -** error go away.  Note: use [sqlite3_errmsg()] to find the text
  2449         -** of the parsing error that results in an [SQLITE_SCHEMA] return.
         3029  +** statement and try to run it again.
  2450   3030   ** </li>
  2451   3031   **
  2452   3032   ** <li>
  2453   3033   ** ^When an error occurs, [sqlite3_step()] will return one of the detailed
  2454   3034   ** [error codes] or [extended error codes].  ^The legacy behavior was that
  2455   3035   ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code
  2456   3036   ** and the application would have to make a second call to [sqlite3_reset()]
  2457   3037   ** in order to find the underlying cause of the problem. With the "v2" prepare
  2458   3038   ** interfaces, the underlying reason for the error is returned immediately.
  2459   3039   ** </li>
  2460   3040   **
  2461   3041   ** <li>
  2462         -** ^If the value of a [parameter | host parameter] in the WHERE clause might
  2463         -** change the query plan for a statement, then the statement may be
  2464         -** automatically recompiled (as if there had been a schema change) on the first 
  2465         -** [sqlite3_step()] call following any change to the 
  2466         -** [sqlite3_bind_text | bindings] of the [parameter]. 
         3042  +** ^If the specific value bound to [parameter | host parameter] in the 
         3043  +** WHERE clause might influence the choice of query plan for a statement,
         3044  +** then the statement will be automatically recompiled, as if there had been 
         3045  +** a schema change, on the first  [sqlite3_step()] call following any change
         3046  +** to the [sqlite3_bind_text | bindings] of that [parameter]. 
         3047  +** ^The specific value of WHERE-clause [parameter] might influence the 
         3048  +** choice of query plan if the parameter is the left-hand side of a [LIKE]
         3049  +** or [GLOB] operator or if the parameter is compared to an indexed column
         3050  +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
         3051  +** the 
  2467   3052   ** </li>
  2468   3053   ** </ol>
  2469   3054   */
  2470   3055   SQLITE_API int sqlite3_prepare(
  2471   3056     sqlite3 *db,            /* Database handle */
  2472   3057     const char *zSql,       /* SQL statement, UTF-8 encoded */
  2473   3058     int nByte,              /* Maximum length of zSql in bytes. */
................................................................................
  2501   3086   **
  2502   3087   ** ^This interface can be used to retrieve a saved copy of the original
  2503   3088   ** SQL text used to create a [prepared statement] if that statement was
  2504   3089   ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
  2505   3090   */
  2506   3091   SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
  2507   3092   
         3093  +/*
         3094  +** CAPI3REF: Determine If An SQL Statement Writes The Database
         3095  +**
         3096  +** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
         3097  +** and only if the [prepared statement] X makes no direct changes to
         3098  +** the content of the database file.
         3099  +**
         3100  +** Note that [application-defined SQL functions] or
         3101  +** [virtual tables] might change the database indirectly as a side effect.  
         3102  +** ^(For example, if an application defines a function "eval()" that 
         3103  +** calls [sqlite3_exec()], then the following SQL statement would
         3104  +** change the database file through side-effects:
         3105  +**
         3106  +** <blockquote><pre>
         3107  +**    SELECT eval('DELETE FROM t1') FROM t2;
         3108  +** </pre></blockquote>
         3109  +**
         3110  +** But because the [SELECT] statement does not change the database file
         3111  +** directly, sqlite3_stmt_readonly() would still return true.)^
         3112  +**
         3113  +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK],
         3114  +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true,
         3115  +** since the statements themselves do not actually modify the database but
         3116  +** rather they control the timing of when other statements modify the 
         3117  +** database.  ^The [ATTACH] and [DETACH] statements also cause
         3118  +** sqlite3_stmt_readonly() to return true since, while those statements
         3119  +** change the configuration of a database connection, they do not make 
         3120  +** changes to the content of the database files on disk.
         3121  +*/
         3122  +SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
         3123  +
         3124  +/*
         3125  +** CAPI3REF: Determine If A Prepared Statement Has Been Reset
         3126  +**
         3127  +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
         3128  +** [prepared statement] S has been stepped at least once using 
         3129  +** [sqlite3_step(S)] but has not run to completion and/or has not 
         3130  +** been reset using [sqlite3_reset(S)].  ^The sqlite3_stmt_busy(S)
         3131  +** interface returns false if S is a NULL pointer.  If S is not a 
         3132  +** NULL pointer and is not a pointer to a valid [prepared statement]
         3133  +** object, then the behavior is undefined and probably undesirable.
         3134  +**
         3135  +** This interface can be used in combination [sqlite3_next_stmt()]
         3136  +** to locate all prepared statements associated with a database 
         3137  +** connection that are in need of being reset.  This can be used,
         3138  +** for example, in diagnostic routines to search for prepared 
         3139  +** statements that are holding a transaction open.
         3140  +*/
         3141  +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
         3142  +
  2508   3143   /*
  2509   3144   ** CAPI3REF: Dynamically Typed Value Object
  2510   3145   ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
  2511   3146   **
  2512   3147   ** SQLite uses the sqlite3_value object to represent all values
  2513   3148   ** that can be stored in a database table. SQLite uses dynamic typing
  2514   3149   ** for the values it stores.  ^Values stored in sqlite3_value objects
................................................................................
  2517   3152   ** An sqlite3_value object may be either "protected" or "unprotected".
  2518   3153   ** Some interfaces require a protected sqlite3_value.  Other interfaces
  2519   3154   ** will accept either a protected or an unprotected sqlite3_value.
  2520   3155   ** Every interface that accepts sqlite3_value arguments specifies
  2521   3156   ** whether or not it requires a protected sqlite3_value.
  2522   3157   **
  2523   3158   ** The terms "protected" and "unprotected" refer to whether or not
  2524         -** a mutex is held.  A internal mutex is held for a protected
         3159  +** a mutex is held.  An internal mutex is held for a protected
  2525   3160   ** sqlite3_value object but no mutex is held for an unprotected
  2526   3161   ** sqlite3_value object.  If SQLite is compiled to be single-threaded
  2527   3162   ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
  2528   3163   ** or if SQLite is run in one of reduced mutex modes 
  2529   3164   ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
  2530   3165   ** then there is no distinction between protected and unprotected
  2531   3166   ** sqlite3_value objects and they can be used interchangeably.  However,
  2532   3167   ** for maximum code portability it is recommended that applications
  2533         -** still make the distinction between between protected and unprotected
         3168  +** still make the distinction between protected and unprotected
  2534   3169   ** sqlite3_value objects even when not strictly required.
  2535   3170   **
  2536   3171   ** ^The sqlite3_value objects that are passed as parameters into the
  2537   3172   ** implementation of [application-defined SQL functions] are protected.
  2538   3173   ** ^The sqlite3_value object returned by
  2539   3174   ** [sqlite3_column_value()] is unprotected.
  2540   3175   ** Unprotected sqlite3_value objects may only be used with
................................................................................
  2572   3207   ** <li>  ?NNN
  2573   3208   ** <li>  :VVV
  2574   3209   ** <li>  @VVV
  2575   3210   ** <li>  $VVV
  2576   3211   ** </ul>
  2577   3212   **
  2578   3213   ** In the templates above, NNN represents an integer literal,
  2579         -** and VVV represents an alphanumeric identifer.)^  ^The values of these
         3214  +** and VVV represents an alphanumeric identifier.)^  ^The values of these
  2580   3215   ** parameters (also called "host parameter names" or "SQL parameters")
  2581   3216   ** can be set using the sqlite3_bind_*() routines defined here.
  2582   3217   **
  2583   3218   ** ^The first argument to the sqlite3_bind_*() routines is always
  2584   3219   ** a pointer to the [sqlite3_stmt] object returned from
  2585   3220   ** [sqlite3_prepare_v2()] or its variants.
  2586   3221   **
................................................................................
  2595   3230   ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
  2596   3231   **
  2597   3232   ** ^The third argument is the value to bind to the parameter.
  2598   3233   **
  2599   3234   ** ^(In those routines that have a fourth argument, its value is the
  2600   3235   ** number of bytes in the parameter.  To be clear: the value is the
  2601   3236   ** number of <u>bytes</u> in the value, not the number of characters.)^
  2602         -** ^If the fourth parameter is negative, the length of the string is
         3237  +** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
         3238  +** is negative, then the length of the string is
  2603   3239   ** the number of bytes up to the first zero terminator.
         3240  +** If the fourth parameter to sqlite3_bind_blob() is negative, then
         3241  +** the behavior is undefined.
         3242  +** If a non-negative fourth parameter is provided to sqlite3_bind_text()
         3243  +** or sqlite3_bind_text16() then that parameter must be the byte offset
         3244  +** where the NUL terminator would occur assuming the string were NUL
         3245  +** terminated.  If any NUL characters occur at byte offsets less than 
         3246  +** the value of the fourth parameter then the resulting string value will
         3247  +** contain embedded NULs.  The result of expressions involving strings
         3248  +** with embedded NULs is undefined.
  2604   3249   **
  2605   3250   ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
  2606   3251   ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
  2607         -** string after SQLite has finished with it. ^If the fifth argument is
         3252  +** string after SQLite has finished with it.  ^The destructor is called
         3253  +** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(),
         3254  +** sqlite3_bind_text(), or sqlite3_bind_text16() fails.  
         3255  +** ^If the fifth argument is
  2608   3256   ** the special value [SQLITE_STATIC], then SQLite assumes that the
  2609   3257   ** information is in static, unmanaged space and does not need to be freed.
  2610   3258   ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then
  2611   3259   ** SQLite makes its own private copy of the data immediately, before
  2612   3260   ** the sqlite3_bind_*() routine returns.
  2613   3261   **
  2614   3262   ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
................................................................................
  2721   3369   
  2722   3370   /*
  2723   3371   ** CAPI3REF: Number Of Columns In A Result Set
  2724   3372   **
  2725   3373   ** ^Return the number of columns in the result set returned by the
  2726   3374   ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL
  2727   3375   ** statement that does not return data (for example an [UPDATE]).
         3376  +**
         3377  +** See also: [sqlite3_data_count()]
  2728   3378   */
  2729   3379   SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt);
  2730   3380   
  2731   3381   /*
  2732   3382   ** CAPI3REF: Column Names In A Result Set
  2733   3383   **
  2734   3384   ** ^These routines return the name assigned to a particular column
................................................................................
  2736   3386   ** interface returns a pointer to a zero-terminated UTF-8 string
  2737   3387   ** and sqlite3_column_name16() returns a pointer to a zero-terminated
  2738   3388   ** UTF-16 string.  ^The first parameter is the [prepared statement]
  2739   3389   ** that implements the [SELECT] statement. ^The second parameter is the
  2740   3390   ** column number.  ^The leftmost column is number 0.
  2741   3391   **
  2742   3392   ** ^The returned string pointer is valid until either the [prepared statement]
  2743         -** is destroyed by [sqlite3_finalize()] or until the next call to
         3393  +** is destroyed by [sqlite3_finalize()] or until the statement is automatically
         3394  +** reprepared by the first call to [sqlite3_step()] for a particular run
         3395  +** or until the next call to
  2744   3396   ** sqlite3_column_name() or sqlite3_column_name16() on the same column.
  2745   3397   **
  2746   3398   ** ^If sqlite3_malloc() fails during the processing of either routine
  2747   3399   ** (for example during a conversion from UTF-8 to UTF-16) then a
  2748   3400   ** NULL pointer is returned.
  2749   3401   **
  2750   3402   ** ^The name of a result column is the value of the "AS" clause for
................................................................................
  2762   3414   ** table column that is the origin of a particular result column in
  2763   3415   ** [SELECT] statement.
  2764   3416   ** ^The name of the database or table or column can be returned as
  2765   3417   ** either a UTF-8 or UTF-16 string.  ^The _database_ routines return
  2766   3418   ** the database name, the _table_ routines return the table name, and
  2767   3419   ** the origin_ routines return the column name.
  2768   3420   ** ^The returned string is valid until the [prepared statement] is destroyed
  2769         -** using [sqlite3_finalize()] or until the same information is requested
         3421  +** using [sqlite3_finalize()] or until the statement is automatically
         3422  +** reprepared by the first call to [sqlite3_step()] for a particular run
         3423  +** or until the same information is requested
  2770   3424   ** again in a different encoding.
  2771   3425   **
  2772   3426   ** ^The names returned are the original un-aliased names of the
  2773   3427   ** database, table, and column.
  2774   3428   **
  2775   3429   ** ^The first argument to these interfaces is a [prepared statement].
  2776   3430   ** ^These functions return information about the Nth result column returned by
................................................................................
  2856   3510   ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
  2857   3511   ** ^With the "v2" interface, any of the other [result codes] or
  2858   3512   ** [extended result codes] might be returned as well.
  2859   3513   **
  2860   3514   ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the
  2861   3515   ** database locks it needs to do its job.  ^If the statement is a [COMMIT]
  2862   3516   ** or occurs outside of an explicit transaction, then you can retry the
  2863         -** statement.  If the statement is not a [COMMIT] and occurs within a
         3517  +** statement.  If the statement is not a [COMMIT] and occurs within an
  2864   3518   ** explicit transaction then you should rollback the transaction before
  2865   3519   ** continuing.
  2866   3520   **
  2867   3521   ** ^[SQLITE_DONE] means that the statement has finished executing
  2868   3522   ** successfully.  sqlite3_step() should not be called again on this virtual
  2869   3523   ** machine without first calling [sqlite3_reset()] to reset the virtual
  2870   3524   ** machine back to its initial state.
................................................................................
  2885   3539   **
  2886   3540   ** [SQLITE_MISUSE] means that the this routine was called inappropriately.
  2887   3541   ** Perhaps it was called on a [prepared statement] that has
  2888   3542   ** already been [sqlite3_finalize | finalized] or on one that had
  2889   3543   ** previously returned [SQLITE_ERROR] or [SQLITE_DONE].  Or it could
  2890   3544   ** be the case that the same database connection is being used by two or
  2891   3545   ** more threads at the same moment in time.
         3546  +**
         3547  +** For all versions of SQLite up to and including 3.6.23.1, a call to
         3548  +** [sqlite3_reset()] was required after sqlite3_step() returned anything
         3549  +** other than [SQLITE_ROW] before any subsequent invocation of
         3550  +** sqlite3_step().  Failure to reset the prepared statement using 
         3551  +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
         3552  +** sqlite3_step().  But after version 3.6.23.1, sqlite3_step() began
         3553  +** calling [sqlite3_reset()] automatically in this circumstance rather
         3554  +** than returning [SQLITE_MISUSE].  This is not considered a compatibility
         3555  +** break because any application that ever receives an SQLITE_MISUSE error
         3556  +** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option
         3557  +** can be used to restore the legacy behavior.
  2892   3558   **
  2893   3559   ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()
  2894   3560   ** API always returns a generic error code, [SQLITE_ERROR], following any
  2895   3561   ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE].  You must call
  2896   3562   ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the
  2897   3563   ** specific [error codes] that better describes the error.
  2898   3564   ** We admit that this is a goofy design.  The problem has been fixed
................................................................................
  2903   3569   ** by sqlite3_step().  The use of the "v2" interface is recommended.
  2904   3570   */
  2905   3571   SQLITE_API int sqlite3_step(sqlite3_stmt*);
  2906   3572   
  2907   3573   /*
  2908   3574   ** CAPI3REF: Number of columns in a result set
  2909   3575   **
  2910         -** ^The sqlite3_data_count(P) the number of columns in the
  2911         -** of the result set of [prepared statement] P.
         3576  +** ^The sqlite3_data_count(P) interface returns the number of columns in the
         3577  +** current row of the result set of [prepared statement] P.
         3578  +** ^If prepared statement P does not have results ready to return
         3579  +** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
         3580  +** interfaces) then sqlite3_data_count(P) returns 0.
         3581  +** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
         3582  +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
         3583  +** [sqlite3_step](P) returned [SQLITE_DONE].  ^The sqlite3_data_count(P)
         3584  +** will return non-zero if previous call to [sqlite3_step](P) returned
         3585  +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
         3586  +** where it always returns zero since each step of that multi-step
         3587  +** pragma returns 0 columns of data.
         3588  +**
         3589  +** See also: [sqlite3_column_count()]
  2912   3590   */
  2913   3591   SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
  2914   3592   
  2915   3593   /*
  2916   3594   ** CAPI3REF: Fundamental Datatypes
  2917   3595   ** KEYWORDS: SQLITE_TEXT
  2918   3596   **
................................................................................
  2984   3662   ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()
  2985   3663   ** routine returns the number of bytes in that BLOB or string.
  2986   3664   ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts
  2987   3665   ** the string to UTF-8 and then returns the number of bytes.
  2988   3666   ** ^If the result is a numeric value then sqlite3_column_bytes() uses
  2989   3667   ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns
  2990   3668   ** the number of bytes in that string.
  2991         -** ^The value returned does not include the zero terminator at the end
  2992         -** of the string.  ^For clarity: the value returned is the number of
         3669  +** ^If the result is NULL, then sqlite3_column_bytes() returns zero.
         3670  +**
         3671  +** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16()
         3672  +** routine returns the number of bytes in that BLOB or string.
         3673  +** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts
         3674  +** the string to UTF-16 and then returns the number of bytes.
         3675  +** ^If the result is a numeric value then sqlite3_column_bytes16() uses
         3676  +** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns
         3677  +** the number of bytes in that string.
         3678  +** ^If the result is NULL, then sqlite3_column_bytes16() returns zero.
         3679  +**
         3680  +** ^The values returned by [sqlite3_column_bytes()] and 
         3681  +** [sqlite3_column_bytes16()] do not include the zero terminators at the end
         3682  +** of the string.  ^For clarity: the values returned by
         3683  +** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of
  2993   3684   ** bytes in the string, not the number of characters.
  2994   3685   **
  2995   3686   ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
  2996         -** even empty strings, are always zero terminated.  ^The return
  2997         -** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary
  2998         -** pointer, possibly even a NULL pointer.
  2999         -**
  3000         -** ^The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes()
  3001         -** but leaves the result in UTF-16 in native byte order instead of UTF-8.
  3002         -** ^The zero terminator is not included in this count.
         3687  +** even empty strings, are always zero-terminated.  ^The return
         3688  +** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
  3003   3689   **
  3004   3690   ** ^The object returned by [sqlite3_column_value()] is an
  3005   3691   ** [unprotected sqlite3_value] object.  An unprotected sqlite3_value object
  3006   3692   ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
  3007   3693   ** If the [unprotected sqlite3_value] object returned by
  3008   3694   ** [sqlite3_column_value()] is used in any other way, including calls
  3009   3695   ** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
................................................................................
  3040   3726   **
  3041   3727   ** The table above makes reference to standard C library functions atoi()
  3042   3728   ** and atof().  SQLite does not really use these functions.  It has its
  3043   3729   ** own equivalent internal routines.  The atoi() and atof() names are
  3044   3730   ** used in the table for brevity and because they are familiar to most
  3045   3731   ** C programmers.
  3046   3732   **
  3047         -** ^Note that when type conversions occur, pointers returned by prior
         3733  +** Note that when type conversions occur, pointers returned by prior
  3048   3734   ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
  3049   3735   ** sqlite3_column_text16() may be invalidated.
  3050         -** ^(Type conversions and pointer invalidations might occur
         3736  +** Type conversions and pointer invalidations might occur
  3051   3737   ** in the following cases:
  3052   3738   **
  3053   3739   ** <ul>
  3054   3740   ** <li> The initial content is a BLOB and sqlite3_column_text() or
  3055   3741   **      sqlite3_column_text16() is called.  A zero-terminator might
  3056   3742   **      need to be added to the string.</li>
  3057   3743   ** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or
  3058   3744   **      sqlite3_column_text16() is called.  The content must be converted
  3059   3745   **      to UTF-16.</li>
  3060   3746   ** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or
  3061   3747   **      sqlite3_column_text() is called.  The content must be converted
  3062   3748   **      to UTF-8.</li>
  3063         -** </ul>)^
         3749  +** </ul>
  3064   3750   **
  3065   3751   ** ^Conversions between UTF-16be and UTF-16le are always done in place and do
  3066   3752   ** not invalidate a prior pointer, though of course the content of the buffer
  3067         -** that the prior pointer points to will have been modified.  Other kinds
         3753  +** that the prior pointer references will have been modified.  Other kinds
  3068   3754   ** of conversion are done in place when it is possible, but sometimes they
  3069   3755   ** are not possible and in those cases prior pointers are invalidated.
  3070   3756   **
  3071         -** ^(The safest and easiest to remember policy is to invoke these routines
         3757  +** The safest and easiest to remember policy is to invoke these routines
  3072   3758   ** in one of the following ways:
  3073   3759   **
  3074   3760   ** <ul>
  3075   3761   **  <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>
  3076   3762   **  <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>
  3077   3763   **  <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>
  3078         -** </ul>)^
         3764  +** </ul>
  3079   3765   **
  3080   3766   ** In other words, you should call sqlite3_column_text(),
  3081   3767   ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result
  3082   3768   ** into the desired format, then invoke sqlite3_column_bytes() or
  3083   3769   ** sqlite3_column_bytes16() to find the size of the result.  Do not mix calls
  3084   3770   ** to sqlite3_column_text() or sqlite3_column_blob() with calls to
  3085   3771   ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16()
................................................................................
  3109   3795   SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
  3110   3796   SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
  3111   3797   
  3112   3798   /*
  3113   3799   ** CAPI3REF: Destroy A Prepared Statement Object
  3114   3800   **
  3115   3801   ** ^The sqlite3_finalize() function is called to delete a [prepared statement].
  3116         -** ^If the statement was executed successfully or not executed at all, then
  3117         -** SQLITE_OK is returned. ^If execution of the statement failed then an
  3118         -** [error code] or [extended error code] is returned.
         3802  +** ^If the most recent evaluation of the statement encountered no errors
         3803  +** or if the statement is never been evaluated, then sqlite3_finalize() returns
         3804  +** SQLITE_OK.  ^If the most recent evaluation of statement S failed, then
         3805  +** sqlite3_finalize(S) returns the appropriate [error code] or
         3806  +** [extended error code].
  3119   3807   **
  3120         -** ^This routine can be called at any point during the execution of the
  3121         -** [prepared statement].  ^If the virtual machine has not
  3122         -** completed execution when this routine is called, that is like
  3123         -** encountering an error or an [sqlite3_interrupt | interrupt].
  3124         -** ^Incomplete updates may be rolled back and transactions canceled,
  3125         -** depending on the circumstances, and the
  3126         -** [error code] returned will be [SQLITE_ABORT].
         3808  +** ^The sqlite3_finalize(S) routine can be called at any point during
         3809  +** the life cycle of [prepared statement] S:
         3810  +** before statement S is ever evaluated, after
         3811  +** one or more calls to [sqlite3_reset()], or after any call
         3812  +** to [sqlite3_step()] regardless of whether or not the statement has
         3813  +** completed execution.
         3814  +**
         3815  +** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op.
         3816  +**
         3817  +** The application must finalize every [prepared statement] in order to avoid
         3818  +** resource leaks.  It is a grievous error for the application to try to use
         3819  +** a prepared statement after it has been finalized.  Any use of a prepared
         3820  +** statement after it has been finalized can result in undefined and
         3821  +** undesirable behavior such as segfaults and heap corruption.
  3127   3822   */
  3128   3823   SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
  3129   3824   
  3130   3825   /*
  3131   3826   ** CAPI3REF: Reset A Prepared Statement Object
  3132   3827   **
  3133   3828   ** The sqlite3_reset() function is called to reset a [prepared statement]
................................................................................
  3155   3850   
  3156   3851   /*
  3157   3852   ** CAPI3REF: Create Or Redefine SQL Functions
  3158   3853   ** KEYWORDS: {function creation routines}
  3159   3854   ** KEYWORDS: {application-defined SQL function}
  3160   3855   ** KEYWORDS: {application-defined SQL functions}
  3161   3856   **
  3162         -** ^These two functions (collectively known as "function creation routines")
         3857  +** ^These functions (collectively known as "function creation routines")
  3163   3858   ** are used to add SQL functions or aggregates or to redefine the behavior
  3164         -** of existing SQL functions or aggregates.  The only difference between the
  3165         -** two is that the second parameter, the name of the (scalar) function or
  3166         -** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16
  3167         -** for sqlite3_create_function16().
         3859  +** of existing SQL functions or aggregates.  The only differences between
         3860  +** these routines are the text encoding expected for
         3861  +** the second parameter (the name of the function being created)
         3862  +** and the presence or absence of a destructor callback for
         3863  +** the application data pointer.
  3168   3864   **
  3169   3865   ** ^The first parameter is the [database connection] to which the SQL
  3170   3866   ** function is to be added.  ^If an application uses more than one database
  3171   3867   ** connection then application-defined SQL functions must be added
  3172   3868   ** to each database connection separately.
  3173   3869   **
  3174         -** The second parameter is the name of the SQL function to be created or
  3175         -** redefined.  ^The length of the name is limited to 255 bytes, exclusive of
  3176         -** the zero-terminator.  Note that the name length limit is in bytes, not
  3177         -** characters.  ^Any attempt to create a function with a longer name
  3178         -** will result in [SQLITE_ERROR] being returned.
         3870  +** ^The second parameter is the name of the SQL function to be created or
         3871  +** redefined.  ^The length of the name is limited to 255 bytes in a UTF-8
         3872  +** representation, exclusive of the zero-terminator.  ^Note that the name
         3873  +** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.  
         3874  +** ^Any attempt to create a function with a longer name
         3875  +** will result in [SQLITE_MISUSE] being returned.
  3179   3876   **
  3180   3877   ** ^The third parameter (nArg)
  3181   3878   ** is the number of arguments that the SQL function or
  3182   3879   ** aggregate takes. ^If this parameter is -1, then the SQL function or
  3183   3880   ** aggregate may take any number of arguments between 0 and the limit
  3184   3881   ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]).  If the third
  3185   3882   ** parameter is less than -1 or greater than 127 then the behavior is
  3186   3883   ** undefined.
  3187   3884   **
  3188         -** The fourth parameter, eTextRep, specifies what
         3885  +** ^The fourth parameter, eTextRep, specifies what
  3189   3886   ** [SQLITE_UTF8 | text encoding] this SQL function prefers for
  3190         -** its parameters.  Any SQL function implementation should be able to work
  3191         -** work with UTF-8, UTF-16le, or UTF-16be.  But some implementations may be
         3887  +** its parameters.  Every SQL function implementation must be able to work
         3888  +** with UTF-8, UTF-16le, or UTF-16be.  But some implementations may be
  3192   3889   ** more efficient with one encoding than another.  ^An application may
  3193   3890   ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
  3194   3891   ** times with the same function but with different values of eTextRep.
  3195   3892   ** ^When multiple implementations of the same function are available, SQLite
  3196   3893   ** will pick the one that involves the least amount of data conversion.
  3197   3894   ** If there is only a single implementation which does not care what text
  3198   3895   ** encoding is used, then the fourth argument should be [SQLITE_ANY].
  3199   3896   **
  3200   3897   ** ^(The fifth parameter is an arbitrary pointer.  The implementation of the
  3201   3898   ** function can gain access to this pointer using [sqlite3_user_data()].)^
  3202   3899   **
  3203         -** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
         3900  +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are
  3204   3901   ** pointers to C-language functions that implement the SQL function or
  3205   3902   ** aggregate. ^A scalar SQL function requires an implementation of the xFunc
  3206         -** callback only; NULL pointers should be passed as the xStep and xFinal
         3903  +** callback only; NULL pointers must be passed as the xStep and xFinal
  3207   3904   ** parameters. ^An aggregate SQL function requires an implementation of xStep
  3208         -** and xFinal and NULL should be passed for xFunc. ^To delete an existing
  3209         -** SQL function or aggregate, pass NULL for all three function callbacks.
         3905  +** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
         3906  +** SQL function or aggregate, pass NULL pointers for all three function
         3907  +** callbacks.
         3908  +**
         3909  +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL,
         3910  +** then it is destructor for the application data pointer. 
         3911  +** The destructor is invoked when the function is deleted, either by being
         3912  +** overloaded or when the database connection closes.)^
         3913  +** ^The destructor is also invoked if the call to
         3914  +** sqlite3_create_function_v2() fails.
         3915  +** ^When the destructor callback of the tenth parameter is invoked, it
         3916  +** is passed a single argument which is a copy of the application data 
         3917  +** pointer which was the fifth parameter to sqlite3_create_function_v2().
  3210   3918   **
  3211   3919   ** ^It is permitted to register multiple implementations of the same
  3212   3920   ** functions with the same name but with either differing numbers of
  3213   3921   ** arguments or differing preferred text encodings.  ^SQLite will use
  3214   3922   ** the implementation that most closely matches the way in which the
  3215   3923   ** SQL function is used.  ^A function implementation with a non-negative
  3216   3924   ** nArg parameter is a better match than a function implementation with
................................................................................
  3218   3926   ** matches the database encoding is a better
  3219   3927   ** match than a function where the encoding is different.  
  3220   3928   ** ^A function where the encoding difference is between UTF16le and UTF16be
  3221   3929   ** is a closer match than a function where the encoding difference is
  3222   3930   ** between UTF8 and UTF16.
  3223   3931   **
  3224   3932   ** ^Built-in functions may be overloaded by new application-defined functions.
  3225         -** ^The first application-defined function with a given name overrides all
  3226         -** built-in functions in the same [database connection] with the same name.
  3227         -** ^Subsequent application-defined functions of the same name only override 
  3228         -** prior application-defined functions that are an exact match for the
  3229         -** number of parameters and preferred encoding.
  3230   3933   **
  3231   3934   ** ^An application-defined function is permitted to call other
  3232   3935   ** SQLite interfaces.  However, such calls must not
  3233   3936   ** close the database connection nor finalize or reset the prepared
  3234   3937   ** statement in which the function is running.
  3235   3938   */
  3236   3939   SQLITE_API int sqlite3_create_function(
................................................................................
  3248   3951     const void *zFunctionName,
  3249   3952     int nArg,
  3250   3953     int eTextRep,
  3251   3954     void *pApp,
  3252   3955     void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
  3253   3956     void (*xStep)(sqlite3_context*,int,sqlite3_value**),
  3254   3957     void (*xFinal)(sqlite3_context*)
         3958  +);
         3959  +SQLITE_API int sqlite3_create_function_v2(
         3960  +  sqlite3 *db,
         3961  +  const char *zFunctionName,
         3962  +  int nArg,
         3963  +  int eTextRep,
         3964  +  void *pApp,
         3965  +  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
         3966  +  void (*xStep)(sqlite3_context*,int,sqlite3_value**),
         3967  +  void (*xFinal)(sqlite3_context*),
         3968  +  void(*xDestroy)(void*)
  3255   3969   );
  3256   3970   
  3257   3971   /*
  3258   3972   ** CAPI3REF: Text Encodings
  3259   3973   **
  3260   3974   ** These constant define integer codes that represent the various
  3261   3975   ** text encodings supported by SQLite.
................................................................................
  3292   4006   ** The C-language implementation of SQL functions and aggregates uses
  3293   4007   ** this set of interface routines to access the parameter values on
  3294   4008   ** the function or aggregate.
  3295   4009   **
  3296   4010   ** The xFunc (for scalar functions) or xStep (for aggregates) parameters
  3297   4011   ** to [sqlite3_create_function()] and [sqlite3_create_function16()]
  3298   4012   ** define callbacks that implement the SQL functions and aggregates.
  3299         -** The 4th parameter to these callbacks is an array of pointers to
         4013  +** The 3rd parameter to these callbacks is an array of pointers to
  3300   4014   ** [protected sqlite3_value] objects.  There is one [sqlite3_value] object for
  3301   4015   ** each parameter to the SQL function.  These routines are used to
  3302   4016   ** extract values from the [sqlite3_value] objects.
  3303   4017   **
  3304   4018   ** These routines work only with [protected sqlite3_value] objects.
  3305   4019   ** Any attempt to use these routines on an [unprotected sqlite3_value]
  3306   4020   ** object results in undefined behavior.
................................................................................
  3343   4057   SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);
  3344   4058   SQLITE_API int sqlite3_value_type(sqlite3_value*);
  3345   4059   SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
  3346   4060   
  3347   4061   /*
  3348   4062   ** CAPI3REF: Obtain Aggregate Function Context
  3349   4063   **
  3350         -** Implementions of aggregate SQL functions use this
         4064  +** Implementations of aggregate SQL functions use this
  3351   4065   ** routine to allocate memory for storing their state.
  3352   4066   **
  3353   4067   ** ^The first time the sqlite3_aggregate_context(C,N) routine is called 
  3354   4068   ** for a particular aggregate function, SQLite
  3355   4069   ** allocates N of memory, zeroes out that memory, and returns a pointer
  3356   4070   ** to the new memory. ^On second and subsequent calls to
  3357   4071   ** sqlite3_aggregate_context() for the same aggregate function instance,
................................................................................
  3517   4231   ** they return.  Hence, the calling function can deallocate or
  3518   4232   ** modify the text after they return without harm.
  3519   4233   ** ^The sqlite3_result_error_code() function changes the error code
  3520   4234   ** returned by SQLite as a result of an error in a function.  ^By default,
  3521   4235   ** the error code is SQLITE_ERROR.  ^A subsequent call to sqlite3_result_error()
  3522   4236   ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
  3523   4237   **
  3524         -** ^The sqlite3_result_toobig() interface causes SQLite to throw an error
  3525         -** indicating that a string or BLOB is too long to represent.
         4238  +** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an
         4239  +** error indicating that a string or BLOB is too long to represent.
  3526   4240   **
  3527         -** ^The sqlite3_result_nomem() interface causes SQLite to throw an error
  3528         -** indicating that a memory allocation failed.
         4241  +** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an
         4242  +** error indicating that a memory allocation failed.
  3529   4243   **
  3530   4244   ** ^The sqlite3_result_int() interface sets the return value
  3531   4245   ** of the application-defined function to be the 32-bit signed integer
  3532   4246   ** value given in the 2nd argument.
  3533   4247   ** ^The sqlite3_result_int64() interface sets the return value
  3534   4248   ** of the application-defined function to be the 64-bit signed integer
  3535   4249   ** value given in the 2nd argument.
................................................................................
  3546   4260   ** the 2nd parameter of the sqlite3_result_text* interfaces.
  3547   4261   ** ^If the 3rd parameter to the sqlite3_result_text* interfaces
  3548   4262   ** is negative, then SQLite takes result text from the 2nd parameter
  3549   4263   ** through the first zero character.
  3550   4264   ** ^If the 3rd parameter to the sqlite3_result_text* interfaces
  3551   4265   ** is non-negative, then as many bytes (not characters) of the text
  3552   4266   ** pointed to by the 2nd parameter are taken as the application-defined
  3553         -** function result.
         4267  +** function result.  If the 3rd parameter is non-negative, then it
         4268  +** must be the byte offset into the string where the NUL terminator would
         4269  +** appear if the string where NUL terminated.  If any NUL characters occur
         4270  +** in the string at a byte offset that is less than the value of the 3rd
         4271  +** parameter, then the resulting string will contain embedded NULs and the
         4272  +** result of expressions operating on strings with embedded NULs is undefined.
  3554   4273   ** ^If the 4th parameter to the sqlite3_result_text* interfaces
  3555   4274   ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
  3556   4275   ** function as the destructor on the text or BLOB result when it has
  3557   4276   ** finished using that result.
  3558   4277   ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to
  3559   4278   ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite
  3560   4279   ** assumes that the text or BLOB result is in constant space and does not
................................................................................
  3595   4314   SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
  3596   4315   SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
  3597   4316   SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);
  3598   4317   
  3599   4318   /*
  3600   4319   ** CAPI3REF: Define New Collating Sequences
  3601   4320   **
  3602         -** These functions are used to add new collation sequences to the
  3603         -** [database connection] specified as the first argument.
         4321  +** ^These functions add, remove, or modify a [collation] associated
         4322  +** with the [database connection] specified as the first argument.
  3604   4323   **
  3605         -** ^The name of the new collation sequence is specified as a UTF-8 string
         4324  +** ^The name of the collation is a UTF-8 string
  3606   4325   ** for sqlite3_create_collation() and sqlite3_create_collation_v2()
  3607         -** and a UTF-16 string for sqlite3_create_collation16(). ^In all cases
  3608         -** the name is passed as the second function argument.
  3609         -**
  3610         -** ^The third argument may be one of the constants [SQLITE_UTF8],
  3611         -** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied
  3612         -** routine expects to be passed pointers to strings encoded using UTF-8,
  3613         -** UTF-16 little-endian, or UTF-16 big-endian, respectively. ^The
  3614         -** third argument might also be [SQLITE_UTF16] to indicate that the routine
  3615         -** expects pointers to be UTF-16 strings in the native byte order, or the
  3616         -** argument can be [SQLITE_UTF16_ALIGNED] if the
  3617         -** the routine expects pointers to 16-bit word aligned strings
  3618         -** of UTF-16 in the native byte order.
  3619         -**
  3620         -** A pointer to the user supplied routine must be passed as the fifth
  3621         -** argument.  ^If it is NULL, this is the same as deleting the collation
  3622         -** sequence (so that SQLite cannot call it anymore).
  3623         -** ^Each time the application supplied function is invoked, it is passed
  3624         -** as its first parameter a copy of the void* passed as the fourth argument
  3625         -** to sqlite3_create_collation() or sqlite3_create_collation16().
  3626         -**
  3627         -** ^The remaining arguments to the application-supplied routine are two strings,
  3628         -** each represented by a (length, data) pair and encoded in the encoding
  3629         -** that was passed as the third argument when the collation sequence was
  3630         -** registered.  The application defined collation routine should
  3631         -** return negative, zero or positive if the first string is less than,
  3632         -** equal to, or greater than the second string. i.e. (STRING1 - STRING2).
         4326  +** and a UTF-16 string in native byte order for sqlite3_create_collation16().
         4327  +** ^Collation names that compare equal according to [sqlite3_strnicmp()] are
         4328  +** considered to be the same name.
         4329  +**
         4330  +** ^(The third argument (eTextRep) must be one of the constants:
         4331  +** <ul>
         4332  +** <li> [SQLITE_UTF8],
         4333  +** <li> [SQLITE_UTF16LE],
         4334  +** <li> [SQLITE_UTF16BE],
         4335  +** <li> [SQLITE_UTF16], or
         4336  +** <li> [SQLITE_UTF16_ALIGNED].
         4337  +** </ul>)^
         4338  +** ^The eTextRep argument determines the encoding of strings passed
         4339  +** to the collating function callback, xCallback.
         4340  +** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep
         4341  +** force strings to be UTF16 with native byte order.
         4342  +** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin
         4343  +** on an even byte address.
         4344  +**
         4345  +** ^The fourth argument, pArg, is an application data pointer that is passed
         4346  +** through as the first argument to the collating function callback.
         4347  +**
         4348  +** ^The fifth argument, xCallback, is a pointer to the collating function.
         4349  +** ^Multiple collating functions can be registered using the same name but
         4350  +** with different eTextRep parameters and SQLite will use whichever
         4351  +** function requires the least amount of data transformation.
         4352  +** ^If the xCallback argument is NULL then the collating function is
         4353  +** deleted.  ^When all collating functions having the same name are deleted,
         4354  +** that collation is no longer usable.
         4355  +**
         4356  +** ^The collating function callback is invoked with a copy of the pArg 
         4357  +** application data pointer and with two strings in the encoding specified
         4358  +** by the eTextRep argument.  The collating function must return an
         4359  +** integer that is negative, zero, or positive
         4360  +** if the first string is less than, equal to, or greater than the second,
         4361  +** respectively.  A collating function must always return the same answer
         4362  +** given the same inputs.  If two or more collating functions are registered
         4363  +** to the same collation name (using different eTextRep values) then all
         4364  +** must give an equivalent answer when invoked with equivalent strings.
         4365  +** The collating function must obey the following properties for all
         4366  +** strings A, B, and C:
         4367  +**
         4368  +** <ol>
         4369  +** <li> If A==B then B==A.
         4370  +** <li> If A==B and B==C then A==C.
         4371  +** <li> If A&lt;B THEN B&gt;A.
         4372  +** <li> If A&lt;B and B&lt;C then A&lt;C.
         4373  +** </ol>
         4374  +**
         4375  +** If a collating function fails any of the above constraints and that
         4376  +** collating function is  registered and used, then the behavior of SQLite
         4377  +** is undefined.
  3633   4378   **
  3634   4379   ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation()
  3635         -** except that it takes an extra argument which is a destructor for
  3636         -** the collation.  ^The destructor is called when the collation is
  3637         -** destroyed and is passed a copy of the fourth parameter void* pointer
  3638         -** of the sqlite3_create_collation_v2().
  3639         -** ^Collations are destroyed when they are overridden by later calls to the
  3640         -** collation creation functions or when the [database connection] is closed
  3641         -** using [sqlite3_close()].
         4380  +** with the addition that the xDestroy callback is invoked on pArg when
         4381  +** the collating function is deleted.
         4382  +** ^Collating functions are deleted when they are overridden by later
         4383  +** calls to the collation creation functions or when the
         4384  +** [database connection] is closed using [sqlite3_close()].
         4385  +**
         4386  +** ^The xDestroy callback is <u>not</u> called if the 
         4387  +** sqlite3_create_collation_v2() function fails.  Applications that invoke
         4388  +** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should 
         4389  +** check the return code and dispose of the application data pointer
         4390  +** themselves rather than expecting SQLite to deal with it for them.
         4391  +** This is different from every other SQLite interface.  The inconsistency 
         4392  +** is unfortunate but cannot be changed without breaking backwards 
         4393  +** compatibility.
  3642   4394   **
  3643   4395   ** See also:  [sqlite3_collation_needed()] and [sqlite3_collation_needed16()].
  3644   4396   */
  3645   4397   SQLITE_API int sqlite3_create_collation(
  3646   4398     sqlite3*, 
  3647   4399     const char *zName, 
  3648   4400     int eTextRep, 
  3649         -  void*,
         4401  +  void *pArg,
  3650   4402     int(*xCompare)(void*,int,const void*,int,const void*)
  3651   4403   );
  3652   4404   SQLITE_API int sqlite3_create_collation_v2(
  3653   4405     sqlite3*, 
  3654   4406     const char *zName, 
  3655   4407     int eTextRep, 
  3656         -  void*,
         4408  +  void *pArg,
  3657   4409     int(*xCompare)(void*,int,const void*,int,const void*),
  3658   4410     void(*xDestroy)(void*)
  3659   4411   );
  3660   4412   SQLITE_API int sqlite3_create_collation16(
  3661   4413     sqlite3*, 
  3662   4414     const void *zName,
  3663   4415     int eTextRep, 
  3664         -  void*,
         4416  +  void *pArg,
  3665   4417     int(*xCompare)(void*,int,const void*,int,const void*)
  3666   4418   );
  3667   4419   
  3668   4420   /*
  3669   4421   ** CAPI3REF: Collation Needed Callbacks
  3670   4422   **
  3671   4423   ** ^To avoid having to register all collation sequences before a database
................................................................................
  3746   4498     const char *zPassPhrase        /* Activation phrase */
  3747   4499   );
  3748   4500   #endif
  3749   4501   
  3750   4502   /*
  3751   4503   ** CAPI3REF: Suspend Execution For A Short Time
  3752   4504   **
  3753         -** ^The sqlite3_sleep() function causes the current thread to suspend execution
         4505  +** The sqlite3_sleep() function causes the current thread to suspend execution
  3754   4506   ** for at least a number of milliseconds specified in its parameter.
  3755   4507   **
  3756         -** ^If the operating system does not support sleep requests with
         4508  +** If the operating system does not support sleep requests with
  3757   4509   ** millisecond time resolution, then the time will be rounded up to
  3758         -** the nearest second. ^The number of milliseconds of sleep actually
         4510  +** the nearest second. The number of milliseconds of sleep actually
  3759   4511   ** requested from the operating system is returned.
  3760   4512   **
  3761   4513   ** ^SQLite implements this interface by calling the xSleep()
  3762         -** method of the default [sqlite3_vfs] object.
         4514  +** method of the default [sqlite3_vfs] object.  If the xSleep() method
         4515  +** of the default VFS is not implemented correctly, or not implemented at
         4516  +** all, then the behavior of sqlite3_sleep() may deviate from the description
         4517  +** in the previous paragraphs.
  3763   4518   */
  3764   4519   SQLITE_API int sqlite3_sleep(int);
  3765   4520   
  3766   4521   /*
  3767   4522   ** CAPI3REF: Name Of The Folder Holding Temporary Files
  3768   4523   **
  3769   4524   ** ^(If this global variable is made to point to a string which is
................................................................................
  3787   4542   ** the [temp_store_directory pragma] always assumes that any string
  3788   4543   ** that this variable points to is held in memory obtained from 
  3789   4544   ** [sqlite3_malloc] and the pragma may attempt to free that memory
  3790   4545   ** using [sqlite3_free].
  3791   4546   ** Hence, if this variable is modified directly, either it should be
  3792   4547   ** made NULL or made to point to memory obtained from [sqlite3_malloc]
  3793   4548   ** or else the use of the [temp_store_directory pragma] should be avoided.
         4549  +**
         4550  +** <b>Note to Windows Runtime users:</b>  The temporary directory must be set
         4551  +** prior to calling [sqlite3_open] or [sqlite3_open_v2].  Otherwise, various
         4552  +** features that require the use of temporary files may fail.  Here is an
         4553  +** example of how to do this using C++ with the Windows Runtime:
         4554  +**
         4555  +** <blockquote><pre>
         4556  +** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
         4557  +** &nbsp;     TemporaryFolder->Path->Data();
         4558  +** char zPathBuf&#91;MAX_PATH + 1&#93;;
         4559  +** memset(zPathBuf, 0, sizeof(zPathBuf));
         4560  +** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
         4561  +** &nbsp;     NULL, NULL);
         4562  +** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
         4563  +** </pre></blockquote>
  3794   4564   */
  3795   4565   SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;
  3796   4566   
         4567  +/*
         4568  +** CAPI3REF: Name Of The Folder Holding Database Files
         4569  +**
         4570  +** ^(If this global variable is made to point to a string which is
         4571  +** the name of a folder (a.k.a. directory), then all database files
         4572  +** specified with a relative pathname and created or accessed by
         4573  +** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
         4574  +** to be relative to that directory.)^ ^If this variable is a NULL
         4575  +** pointer, then SQLite assumes that all database files specified
         4576  +** with a relative pathname are relative to the current directory
         4577  +** for the process.  Only the windows VFS makes use of this global
         4578  +** variable; it is ignored by the unix VFS.
         4579  +**
         4580  +** Changing the value of this variable while a database connection is
         4581  +** open can result in a corrupt database.
         4582  +**
         4583  +** It is not safe to read or modify this variable in more than one
         4584  +** thread at a time.  It is not safe to read or modify this variable
         4585  +** if a [database connection] is being used at the same time in a separate
         4586  +** thread.
         4587  +** It is intended that this variable be set once
         4588  +** as part of process initialization and before any SQLite interface
         4589  +** routines have been called and that this variable remain unchanged
         4590  +** thereafter.
         4591  +**
         4592  +** ^The [data_store_directory pragma] may modify this variable and cause
         4593  +** it to point to memory obtained from [sqlite3_malloc].  ^Furthermore,
         4594  +** the [data_store_directory pragma] always assumes that any string
         4595  +** that this variable points to is held in memory obtained from 
         4596  +** [sqlite3_malloc] and the pragma may attempt to free that memory
         4597  +** using [sqlite3_free].
         4598  +** Hence, if this variable is modified directly, either it should be
         4599  +** made NULL or made to point to memory obtained from [sqlite3_malloc]
         4600  +** or else the use of the [data_store_directory pragma] should be avoided.
         4601  +*/
         4602  +SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;
         4603  +
  3797   4604   /*
  3798   4605   ** CAPI3REF: Test For Auto-Commit Mode
  3799   4606   ** KEYWORDS: {autocommit mode}
  3800   4607   **
  3801   4608   ** ^The sqlite3_get_autocommit() interface returns non-zero or
  3802   4609   ** zero if the given database connection is or is not in autocommit mode,
  3803   4610   ** respectively.  ^Autocommit mode is on by default.
................................................................................
  3825   4632   ** returned by sqlite3_db_handle is the same [database connection]
  3826   4633   ** that was the first argument
  3827   4634   ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to
  3828   4635   ** create the statement in the first place.
  3829   4636   */
  3830   4637   SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
  3831   4638   
         4639  +/*
         4640  +** CAPI3REF: Return The Filename For A Database Connection
         4641  +**
         4642  +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
         4643  +** associated with database N of connection D.  ^The main database file
         4644  +** has the name "main".  If there is no attached database N on the database
         4645  +** connection D, or if database N is a temporary or in-memory database, then
         4646  +** a NULL pointer is returned.
         4647  +**
         4648  +** ^The filename returned by this function is the output of the
         4649  +** xFullPathname method of the [VFS].  ^In other words, the filename
         4650  +** will be an absolute pathname, even if the filename used
         4651  +** to open the database originally was a URI or relative pathname.
         4652  +*/
         4653  +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
         4654  +
         4655  +/*
         4656  +** CAPI3REF: Determine if a database is read-only
         4657  +**
         4658  +** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N
         4659  +** of connection D is read-only, 0 if it is read/write, or -1 if N is not
         4660  +** the name of a database on connection D.
         4661  +*/
         4662  +SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
         4663  +
  3832   4664   /*
  3833   4665   ** CAPI3REF: Find the next prepared statement
  3834   4666   **
  3835   4667   ** ^This interface returns a pointer to the next [prepared statement] after
  3836   4668   ** pStmt associated with the [database connection] pDb.  ^If pStmt is NULL
  3837   4669   ** then this interface returns a pointer to the first prepared statement
  3838   4670   ** associated with the database connection pDb.  ^If no prepared statement
................................................................................
  3860   4692   ** then the commit is converted into a rollback.
  3861   4693   **
  3862   4694   ** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions
  3863   4695   ** return the P argument from the previous call of the same function
  3864   4696   ** on the same [database connection] D, or NULL for
  3865   4697   ** the first call for each function on D.
  3866   4698   **
         4699  +** The commit and rollback hook callbacks are not reentrant.
  3867   4700   ** The callback implementation must not do anything that will modify
  3868   4701   ** the database connection that invoked the callback.  Any actions
  3869   4702   ** to modify the database connection must be deferred until after the
  3870   4703   ** completion of the [sqlite3_step()] call that triggered the commit
  3871   4704   ** or rollback hook in the first place.
  3872         -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
  3873         -** database connections for the meaning of "modify" in this paragraph.
         4705  +** Note that running any other SQL statements, including SELECT statements,
         4706  +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify
         4707  +** the database connections for the meaning of "modify" in this paragraph.
  3874   4708   **
  3875   4709   ** ^Registering a NULL function disables the callback.
  3876   4710   **
  3877   4711   ** ^When the commit hook callback routine returns zero, the [COMMIT]
  3878   4712   ** operation is allowed to continue normally.  ^If the commit hook
  3879   4713   ** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK].
  3880   4714   ** ^The rollback hook is invoked on a rollback that results from a commit
................................................................................
  3941   4775     sqlite3*, 
  3942   4776     void(*)(void *,int ,char const *,char const *,sqlite3_int64),
  3943   4777     void*
  3944   4778   );
  3945   4779   
  3946   4780   /*
  3947   4781   ** CAPI3REF: Enable Or Disable Shared Pager Cache
  3948         -** KEYWORDS: {shared cache}
  3949   4782   **
  3950   4783   ** ^(This routine enables or disables the sharing of the database cache
  3951   4784   ** and schema data structures between [database connection | connections]
  3952   4785   ** to the same database. Sharing is enabled if the argument is true
  3953   4786   ** and disabled if the argument is false.)^
  3954   4787   **
  3955   4788   ** ^Cache sharing is enabled and disabled for an entire process.
................................................................................
  3963   4796   **
  3964   4797   ** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled
  3965   4798   ** successfully.  An [error code] is returned otherwise.)^
  3966   4799   **
  3967   4800   ** ^Shared cache is disabled by default. But this might change in
  3968   4801   ** future releases of SQLite.  Applications that care about shared
  3969   4802   ** cache setting should set it explicitly.
         4803  +**
         4804  +** This interface is threadsafe on processors where writing a
         4805  +** 32-bit integer is atomic.
  3970   4806   **
  3971   4807   ** See Also:  [SQLite Shared-Cache Mode]
  3972   4808   */
  3973   4809   SQLITE_API int sqlite3_enable_shared_cache(int);
  3974   4810   
  3975   4811   /*
  3976   4812   ** CAPI3REF: Attempt To Free Heap Memory
................................................................................
  3977   4813   **
  3978   4814   ** ^The sqlite3_release_memory() interface attempts to free N bytes
  3979   4815   ** of heap memory by deallocating non-essential memory allocations
  3980   4816   ** held by the database library.   Memory used to cache database
  3981   4817   ** pages to improve performance is an example of non-essential memory.
  3982   4818   ** ^sqlite3_release_memory() returns the number of bytes actually freed,
  3983   4819   ** which might be more or less than the amount requested.
         4820  +** ^The sqlite3_release_memory() routine is a no-op returning zero
         4821  +** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT].
         4822  +**
         4823  +** See also: [sqlite3_db_release_memory()]
  3984   4824   */
  3985   4825   SQLITE_API int sqlite3_release_memory(int);
  3986   4826   
         4827  +/*
         4828  +** CAPI3REF: Free Memory Used By A Database Connection
         4829  +**
         4830  +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
         4831  +** memory as possible from database connection D. Unlike the
         4832  +** [sqlite3_release_memory()] interface, this interface is effect even
         4833  +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
         4834  +** omitted.
         4835  +**
         4836  +** See also: [sqlite3_release_memory()]
         4837  +*/
         4838  +SQLITE_API int sqlite3_db_release_memory(sqlite3*);
         4839  +
  3987   4840   /*
  3988   4841   ** CAPI3REF: Impose A Limit On Heap Size
  3989   4842   **
  3990         -** ^The sqlite3_soft_heap_limit() interface places a "soft" limit
  3991         -** on the amount of heap memory that may be allocated by SQLite.
  3992         -** ^If an internal allocation is requested that would exceed the
  3993         -** soft heap limit, [sqlite3_release_memory()] is invoked one or
  3994         -** more times to free up some space before the allocation is performed.
  3995         -**
  3996         -** ^The limit is called "soft" because if [sqlite3_release_memory()]
  3997         -** cannot free sufficient memory to prevent the limit from being exceeded,
  3998         -** the memory is allocated anyway and the current operation proceeds.
  3999         -**
  4000         -** ^A negative or zero value for N means that there is no soft heap limit and
  4001         -** [sqlite3_release_memory()] will only be called when memory is exhausted.
  4002         -** ^The default value for the soft heap limit is zero.
  4003         -**
  4004         -** ^(SQLite makes a best effort to honor the soft heap limit.
  4005         -** But if the soft heap limit cannot be honored, execution will
  4006         -** continue without error or notification.)^  This is why the limit is
  4007         -** called a "soft" limit.  It is advisory only.
  4008         -**
  4009         -** Prior to SQLite version 3.5.0, this routine only constrained the memory
  4010         -** allocated by a single thread - the same thread in which this routine
  4011         -** runs.  Beginning with SQLite version 3.5.0, the soft heap limit is
  4012         -** applied to all threads. The value specified for the soft heap limit
  4013         -** is an upper bound on the total memory allocation for all threads. In
  4014         -** version 3.5.0 there is no mechanism for limiting the heap usage for
  4015         -** individual threads.
  4016         -*/
  4017         -SQLITE_API void sqlite3_soft_heap_limit(int);
         4843  +** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
         4844  +** soft limit on the amount of heap memory that may be allocated by SQLite.
         4845  +** ^SQLite strives to keep heap memory utilization below the soft heap
         4846  +** limit by reducing the number of pages held in the page cache
         4847  +** as heap memory usages approaches the limit.
         4848  +** ^The soft heap limit is "soft" because even though SQLite strives to stay
         4849  +** below the limit, it will exceed the limit rather than generate
         4850  +** an [SQLITE_NOMEM] error.  In other words, the soft heap limit 
         4851  +** is advisory only.
         4852  +**
         4853  +** ^The return value from sqlite3_soft_heap_limit64() is the size of
         4854  +** the soft heap limit prior to the call, or negative in the case of an
         4855  +** error.  ^If the argument N is negative
         4856  +** then no change is made to the soft heap limit.  Hence, the current
         4857  +** size of the soft heap limit can be determined by invoking
         4858  +** sqlite3_soft_heap_limit64() with a negative argument.
         4859  +**
         4860  +** ^If the argument N is zero then the soft heap limit is disabled.
         4861  +**
         4862  +** ^(The soft heap limit is not enforced in the current implementation
         4863  +** if one or more of following conditions are true:
         4864  +**
         4865  +** <ul>
         4866  +** <li> The soft heap limit is set to zero.
         4867  +** <li> Memory accounting is disabled using a combination of the
         4868  +**      [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
         4869  +**      the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
         4870  +** <li> An alternative page cache implementation is specified using
         4871  +**      [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...).
         4872  +** <li> The page cache allocates from its own memory pool supplied
         4873  +**      by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than
         4874  +**      from the heap.
         4875  +** </ul>)^
         4876  +**
         4877  +** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
         4878  +** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
         4879  +** compile-time option is invoked.  With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
         4880  +** the soft heap limit is enforced on every memory allocation.  Without
         4881  +** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced
         4882  +** when memory is allocated by the page cache.  Testing suggests that because
         4883  +** the page cache is the predominate memory user in SQLite, most
         4884  +** applications will achieve adequate soft heap limit enforcement without
         4885  +** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT].
         4886  +**
         4887  +** The circumstances under which SQLite will enforce the soft heap limit may
         4888  +** changes in future releases of SQLite.
         4889  +*/
         4890  +SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
         4891  +
         4892  +/*
         4893  +** CAPI3REF: Deprecated Soft Heap Limit Interface
         4894  +** DEPRECATED
         4895  +**
         4896  +** This is a deprecated version of the [sqlite3_soft_heap_limit64()]
         4897  +** interface.  This routine is provided for historical compatibility
         4898  +** only.  All new applications should use the
         4899  +** [sqlite3_soft_heap_limit64()] interface rather than this one.
         4900  +*/
         4901  +SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
         4902  +
  4018   4903   
  4019   4904   /*
  4020   4905   ** CAPI3REF: Extract Metadata About A Column Of A Table
  4021   4906   **
  4022   4907   ** ^This routine returns metadata about a specific column of a specific
  4023   4908   ** database table accessible using the [database connection] handle
  4024   4909   ** passed as the first function argument.
................................................................................
  4134   5019   ** ^Call the sqlite3_enable_load_extension() routine with onoff==1
  4135   5020   ** to turn extension loading on and call it with onoff==0 to turn
  4136   5021   ** it back off again.
  4137   5022   */
  4138   5023   SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
  4139   5024   
  4140   5025   /*
  4141         -** CAPI3REF: Automatically Load An Extensions
  4142         -**
  4143         -** ^This API can be invoked at program startup in order to register
  4144         -** one or more statically linked extensions that will be available
  4145         -** to all new [database connections].
  4146         -**
  4147         -** ^(This routine stores a pointer to the extension entry point
  4148         -** in an array that is obtained from [sqlite3_malloc()].  That memory
  4149         -** is deallocated by [sqlite3_reset_auto_extension()].)^
  4150         -**
  4151         -** ^This function registers an extension entry point that is
  4152         -** automatically invoked whenever a new [database connection]
  4153         -** is opened using [sqlite3_open()], [sqlite3_open16()],
  4154         -** or [sqlite3_open_v2()].
  4155         -** ^Duplicate extensions are detected so calling this routine
  4156         -** multiple times with the same extension is harmless.
  4157         -** ^Automatic extensions apply across all threads.
         5026  +** CAPI3REF: Automatically Load Statically Linked Extensions
         5027  +**
         5028  +** ^This interface causes the xEntryPoint() function to be invoked for
         5029  +** each new [database connection] that is created.  The idea here is that
         5030  +** xEntryPoint() is the entry point for a statically linked SQLite extension
         5031  +** that is to be automatically loaded into all new database connections.
         5032  +**
         5033  +** ^(Even though the function prototype shows that xEntryPoint() takes
         5034  +** no arguments and returns void, SQLite invokes xEntryPoint() with three
         5035  +** arguments and expects and integer result as if the signature of the
         5036  +** entry point where as follows:
         5037  +**
         5038  +** <blockquote><pre>
         5039  +** &nbsp;  int xEntryPoint(
         5040  +** &nbsp;    sqlite3 *db,
         5041  +** &nbsp;    const char **pzErrMsg,
         5042  +** &nbsp;    const struct sqlite3_api_routines *pThunk
         5043  +** &nbsp;  );
         5044  +** </pre></blockquote>)^
         5045  +**
         5046  +** If the xEntryPoint routine encounters an error, it should make *pzErrMsg
         5047  +** point to an appropriate error message (obtained from [sqlite3_mprintf()])
         5048  +** and return an appropriate [error code].  ^SQLite ensures that *pzErrMsg
         5049  +** is NULL before calling the xEntryPoint().  ^SQLite will invoke
         5050  +** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns.  ^If any
         5051  +** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()],
         5052  +** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail.
         5053  +**
         5054  +** ^Calling sqlite3_auto_extension(X) with an entry point X that is already
         5055  +** on the list of automatic extensions is a harmless no-op. ^No entry point
         5056  +** will be called more than once for each database connection that is opened.
         5057  +**
         5058  +** See also: [sqlite3_reset_auto_extension()].
  4158   5059   */
  4159   5060   SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));
  4160   5061   
  4161   5062   /*
  4162   5063   ** CAPI3REF: Reset Automatic Extension Loading
  4163   5064   **
  4164         -** ^(This function disables all previously registered automatic
  4165         -** extensions. It undoes the effect of all prior
  4166         -** [sqlite3_auto_extension()] calls.)^
  4167         -**
  4168         -** ^This function disables automatic extensions in all threads.
         5065  +** ^This interface disables all automatic extensions previously
         5066  +** registered using [sqlite3_auto_extension()].
  4169   5067   */
  4170   5068   SQLITE_API void sqlite3_reset_auto_extension(void);
  4171   5069   
  4172   5070   /*
  4173   5071   ** The interface to the virtual-table mechanism is currently considered
  4174   5072   ** to be experimental.  The interface might change in incompatible ways.
  4175   5073   ** If this is a problem for you, do not use the interface at this time.
................................................................................
  4186   5084   typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
  4187   5085   typedef struct sqlite3_module sqlite3_module;
  4188   5086   
  4189   5087   /*
  4190   5088   ** CAPI3REF: Virtual Table Object
  4191   5089   ** KEYWORDS: sqlite3_module {virtual table module}
  4192   5090   **
  4193         -** This structure, sometimes called a a "virtual table module", 
         5091  +** This structure, sometimes called a "virtual table module", 
  4194   5092   ** defines the implementation of a [virtual tables].  
  4195   5093   ** This structure consists mostly of methods for the module.
  4196   5094   **
  4197   5095   ** ^A virtual table module is created by filling in a persistent
  4198   5096   ** instance of this structure and passing a pointer to that instance
  4199   5097   ** to [sqlite3_create_module()] or [sqlite3_create_module_v2()].
  4200   5098   ** ^The registration remains valid until it is replaced by a different
................................................................................
  4226   5124     int (*xSync)(sqlite3_vtab *pVTab);
  4227   5125     int (*xCommit)(sqlite3_vtab *pVTab);
  4228   5126     int (*xRollback)(sqlite3_vtab *pVTab);
  4229   5127     int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
  4230   5128                          void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
  4231   5129                          void **ppArg);
  4232   5130     int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
         5131  +  /* The methods above are in version 1 of the sqlite_module object. Those 
         5132  +  ** below are for version 2 and greater. */
         5133  +  int (*xSavepoint)(sqlite3_vtab *pVTab, int);
         5134  +  int (*xRelease)(sqlite3_vtab *pVTab, int);
         5135  +  int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
  4233   5136   };
  4234   5137   
  4235   5138   /*
  4236   5139   ** CAPI3REF: Virtual Table Indexing Information
  4237   5140   ** KEYWORDS: sqlite3_index_info
  4238   5141   **
  4239         -** The sqlite3_index_info structure and its substructures is used to
         5142  +** The sqlite3_index_info structure and its substructures is used as part
         5143  +** of the [virtual table] interface to
  4240   5144   ** pass information into and receive the reply from the [xBestIndex]
  4241   5145   ** method of a [virtual table module].  The fields under **Inputs** are the
  4242   5146   ** inputs to xBestIndex and are read-only.  xBestIndex inserts its
  4243   5147   ** results into the **Outputs** fields.
  4244   5148   **
  4245   5149   ** ^(The aConstraint[] array records WHERE clause constraints of the form:
  4246   5150   **
  4247         -** <pre>column OP expr</pre>
         5151  +** <blockquote>column OP expr</blockquote>
  4248   5152   **
  4249   5153   ** where OP is =, &lt;, &lt;=, &gt;, or &gt;=.)^  ^(The particular operator is
  4250         -** stored in aConstraint[].op.)^  ^(The index of the column is stored in
         5154  +** stored in aConstraint[].op using one of the
         5155  +** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^
         5156  +** ^(The index of the column is stored in
  4251   5157   ** aConstraint[].iColumn.)^  ^(aConstraint[].usable is TRUE if the
  4252   5158   ** expr on the right-hand side can be evaluated (and thus the constraint
  4253   5159   ** is usable) and false if it cannot.)^
  4254   5160   **
  4255   5161   ** ^The optimizer automatically inverts terms of the form "expr OP column"
  4256   5162   ** and makes other simplifications to the WHERE clause in an attempt to
  4257   5163   ** get as many WHERE clause terms into the form shown above as possible.
................................................................................
  4303   5209     } *aConstraintUsage;
  4304   5210     int idxNum;                /* Number used to identify the index */
  4305   5211     char *idxStr;              /* String, possibly obtained from sqlite3_malloc */
  4306   5212     int needToFreeIdxStr;      /* Free idxStr using sqlite3_free() if true */
  4307   5213     int orderByConsumed;       /* True if output is already ordered */
  4308   5214     double estimatedCost;      /* Estimated cost of using this index */
  4309   5215   };
         5216  +
         5217  +/*
         5218  +** CAPI3REF: Virtual Table Constraint Operator Codes
         5219  +**
         5220  +** These macros defined the allowed values for the
         5221  +** [sqlite3_index_info].aConstraint[].op field.  Each value represents
         5222  +** an operator that is part of a constraint term in the wHERE clause of
         5223  +** a query that uses a [virtual table].
         5224  +*/
  4310   5225   #define SQLITE_INDEX_CONSTRAINT_EQ    2
  4311   5226   #define SQLITE_INDEX_CONSTRAINT_GT    4
  4312   5227   #define SQLITE_INDEX_CONSTRAINT_LE    8
  4313   5228   #define SQLITE_INDEX_CONSTRAINT_LT    16
  4314   5229   #define SQLITE_INDEX_CONSTRAINT_GE    32
  4315   5230   #define SQLITE_INDEX_CONSTRAINT_MATCH 64
  4316   5231   
................................................................................
  4329   5244   ** parameter is an arbitrary client data pointer that is passed through
  4330   5245   ** into the [xCreate] and [xConnect] methods of the virtual table module
  4331   5246   ** when a new virtual table is be being created or reinitialized.
  4332   5247   **
  4333   5248   ** ^The sqlite3_create_module_v2() interface has a fifth parameter which
  4334   5249   ** is a pointer to a destructor for the pClientData.  ^SQLite will
  4335   5250   ** invoke the destructor function (if it is not NULL) when SQLite
  4336         -** no longer needs the pClientData pointer.  ^The sqlite3_create_module()
         5251  +** no longer needs the pClientData pointer.  ^The destructor will also
         5252  +** be invoked if the call to sqlite3_create_module_v2() fails.
         5253  +** ^The sqlite3_create_module()
  4337   5254   ** interface is equivalent to sqlite3_create_module_v2() with a NULL
  4338   5255   ** destructor.
  4339   5256   */
  4340   5257   SQLITE_API int sqlite3_create_module(
  4341   5258     sqlite3 *db,               /* SQLite connection to register module with */
  4342   5259     const char *zName,         /* Name of the module */
  4343   5260     const sqlite3_module *p,   /* Methods for the module */
................................................................................
  4484   5401   **
  4485   5402   ** ^(If the row that a BLOB handle points to is modified by an
  4486   5403   ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
  4487   5404   ** then the BLOB handle is marked as "expired".
  4488   5405   ** This is true if any column of the row is changed, even a column
  4489   5406   ** other than the one the BLOB handle is open on.)^
  4490   5407   ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for
  4491         -** a expired BLOB handle fail with an return code of [SQLITE_ABORT].
         5408  +** an expired BLOB handle fail with a return code of [SQLITE_ABORT].
  4492   5409   ** ^(Changes written into a BLOB prior to the BLOB expiring are not
  4493   5410   ** rolled back by the expiration of the BLOB.  Such changes will eventually
  4494   5411   ** commit if the transaction continues to completion.)^
  4495   5412   **
  4496   5413   ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of
  4497   5414   ** the opened blob.  ^The size of a blob may not be changed by this
  4498   5415   ** interface.  Use the [UPDATE] SQL command to change the size of a
................................................................................
  4512   5429     const char *zTable,
  4513   5430     const char *zColumn,
  4514   5431     sqlite3_int64 iRow,
  4515   5432     int flags,
  4516   5433     sqlite3_blob **ppBlob
  4517   5434   );
  4518   5435   
         5436  +/*
         5437  +** CAPI3REF: Move a BLOB Handle to a New Row
         5438  +**
         5439  +** ^This function is used to move an existing blob handle so that it points
         5440  +** to a different row of the same database table. ^The new row is identified
         5441  +** by the rowid value passed as the second argument. Only the row can be
         5442  +** changed. ^The database, table and column on which the blob handle is open
         5443  +** remain the same. Moving an existing blob handle to a new row can be
         5444  +** faster than closing the existing handle and opening a new one.
         5445  +**
         5446  +** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] -
         5447  +** it must exist and there must be either a blob or text value stored in
         5448  +** the nominated column.)^ ^If the new row is not present in the table, or if
         5449  +** it does not contain a blob or text value, or if another error occurs, an
         5450  +** SQLite error code is returned and the blob handle is considered aborted.
         5451  +** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or
         5452  +** [sqlite3_blob_reopen()] on an aborted blob handle immediately return
         5453  +** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle
         5454  +** always returns zero.
         5455  +**
         5456  +** ^This function sets the database handle error code and message.
         5457  +*/
         5458  +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
         5459  +
  4519   5460   /*
  4520   5461   ** CAPI3REF: Close A BLOB Handle
  4521   5462   **
  4522   5463   ** ^Closes an open [BLOB handle].
  4523   5464   **
  4524   5465   ** ^Closing a BLOB shall cause the current transaction to commit
  4525   5466   ** if there are no other BLOBs, no pending prepared statements, and the
................................................................................
  4664   5605   **
  4665   5606   ** The SQLite source code contains multiple implementations
  4666   5607   ** of these mutex routines.  An appropriate implementation
  4667   5608   ** is selected automatically at compile-time.  ^(The following
  4668   5609   ** implementations are available in the SQLite core:
  4669   5610   **
  4670   5611   ** <ul>
  4671         -** <li>   SQLITE_MUTEX_OS2
  4672         -** <li>   SQLITE_MUTEX_PTHREAD
         5612  +** <li>   SQLITE_MUTEX_PTHREADS
  4673   5613   ** <li>   SQLITE_MUTEX_W32
  4674   5614   ** <li>   SQLITE_MUTEX_NOOP
  4675   5615   ** </ul>)^
  4676   5616   **
  4677   5617   ** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
  4678   5618   ** that does no real locking and is appropriate for use in
  4679         -** a single-threaded application.  ^The SQLITE_MUTEX_OS2,
  4680         -** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
  4681         -** are appropriate for use on OS/2, Unix, and Windows.
         5619  +** a single-threaded application.  ^The SQLITE_MUTEX_PTHREADS and
         5620  +** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
         5621  +** and Windows.
  4682   5622   **
  4683   5623   ** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
  4684   5624   ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
  4685   5625   ** implementation is included with the library. In this case the
  4686   5626   ** application must supply a custom mutex implementation using the
  4687   5627   ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
  4688   5628   ** before calling sqlite3_initialize() or any other public sqlite3_
................................................................................
  4788   5728   ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.
  4789   5729   ** Additionally, an instance of this structure can be used as an
  4790   5730   ** output variable when querying the system for the current mutex
  4791   5731   ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option.
  4792   5732   **
  4793   5733   ** ^The xMutexInit method defined by this structure is invoked as
  4794   5734   ** part of system initialization by the sqlite3_initialize() function.
  4795         -** ^The xMutexInit routine is calle by SQLite exactly once for each
         5735  +** ^The xMutexInit routine is called by SQLite exactly once for each
  4796   5736   ** effective call to [sqlite3_initialize()].
  4797   5737   **
  4798   5738   ** ^The xMutexEnd method defined by this structure is invoked as
  4799   5739   ** part of system shutdown by the sqlite3_shutdown() function. The
  4800   5740   ** implementation of this method is expected to release all outstanding
  4801   5741   ** resources obtained by the mutex methods implementation, especially
  4802   5742   ** those obtained by the xMutexInit method.  ^The xMutexEnd()
................................................................................
  4821   5761   ** of a valid mutex handle. The implementations of the methods defined
  4822   5762   ** by this structure are not required to handle this case, the results
  4823   5763   ** of passing a NULL pointer instead of a valid mutex handle are undefined
  4824   5764   ** (i.e. it is acceptable to provide an implementation that segfaults if
  4825   5765   ** it is passed a NULL pointer).
  4826   5766   **
  4827   5767   ** The xMutexInit() method must be threadsafe.  ^It must be harmless to
  4828         -** invoke xMutexInit() mutiple times within the same process and without
         5768  +** invoke xMutexInit() multiple times within the same process and without
  4829   5769   ** intervening calls to xMutexEnd().  Second and subsequent calls to
  4830   5770   ** xMutexInit() must be no-ops.
  4831   5771   **
  4832   5772   ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
  4833   5773   ** and its associates).  ^Similarly, xMutexAlloc() must not use SQLite memory
  4834   5774   ** allocation for a static mutex.  ^However xMutexAlloc() may use SQLite
  4835   5775   ** memory allocation for a fast or recursive mutex.
................................................................................
  4863   5803   ** with the SQLITE_DEBUG flag.  ^External mutex implementations
  4864   5804   ** are only required to provide these routines if SQLITE_DEBUG is
  4865   5805   ** defined and if NDEBUG is not defined.
  4866   5806   **
  4867   5807   ** ^These routines should return true if the mutex in their argument
  4868   5808   ** is held or not held, respectively, by the calling thread.
  4869   5809   **
  4870         -** ^The implementation is not required to provided versions of these
         5810  +** ^The implementation is not required to provide versions of these
  4871   5811   ** routines that actually work. If the implementation does not provide working
  4872   5812   ** versions of these routines, it should at least provide stubs that always
  4873   5813   ** return true so that one does not get spurious assertion failures.
  4874   5814   **
  4875   5815   ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then
  4876   5816   ** the routine should return 1.   This seems counter-intuitive since
  4877         -** clearly the mutex cannot be held if it does not exist.  But the
         5817  +** clearly the mutex cannot be held if it does not exist.  But
  4878   5818   ** the reason the mutex does not exist is because the build is not
  4879   5819   ** using mutexes.  And we do not want the assert() containing the
  4880   5820   ** call to sqlite3_mutex_held() to fail, so a non-zero return is
  4881   5821   ** the appropriate thing to do.  ^The sqlite3_mutex_notheld()
  4882   5822   ** interface should also return 1 when given a NULL pointer.
  4883   5823   */
  4884   5824   #ifndef NDEBUG
................................................................................
  4900   5840   #define SQLITE_MUTEX_RECURSIVE        1
  4901   5841   #define SQLITE_MUTEX_STATIC_MASTER    2
  4902   5842   #define SQLITE_MUTEX_STATIC_MEM       3  /* sqlite3_malloc() */
  4903   5843   #define SQLITE_MUTEX_STATIC_MEM2      4  /* NOT USED */
  4904   5844   #define SQLITE_MUTEX_STATIC_OPEN      4  /* sqlite3BtreeOpen() */
  4905   5845   #define SQLITE_MUTEX_STATIC_PRNG      5  /* sqlite3_random() */
  4906   5846   #define SQLITE_MUTEX_STATIC_LRU       6  /* lru page list */
  4907         -#define SQLITE_MUTEX_STATIC_LRU2      7  /* lru page list */
         5847  +#define SQLITE_MUTEX_STATIC_LRU2      7  /* NOT USED */
         5848  +#define SQLITE_MUTEX_STATIC_PMEM      7  /* sqlite3PageMalloc() */
  4908   5849   
  4909   5850   /*
  4910   5851   ** CAPI3REF: Retrieve the mutex for a database connection
  4911   5852   **
  4912   5853   ** ^This interface returns a pointer the [sqlite3_mutex] object that 
  4913   5854   ** serializes access to the [database connection] given in the argument
  4914   5855   ** when the [threading mode] is Serialized.
................................................................................
  4919   5860   
  4920   5861   /*
  4921   5862   ** CAPI3REF: Low-Level Control Of Database Files
  4922   5863   **
  4923   5864   ** ^The [sqlite3_file_control()] interface makes a direct call to the
  4924   5865   ** xFileControl method for the [sqlite3_io_methods] object associated
  4925   5866   ** with a particular database identified by the second argument. ^The
  4926         -** name of the database "main" for the main database or "temp" for the
         5867  +** name of the database is "main" for the main database or "temp" for the
  4927   5868   ** TEMP database, or the name that appears after the AS keyword for
  4928   5869   ** databases that are added using the [ATTACH] SQL command.
  4929   5870   ** ^A NULL pointer can be used in place of "main" to refer to the
  4930   5871   ** main database file.
  4931   5872   ** ^The third and fourth parameters to this routine
  4932   5873   ** are passed directly through to the second and third parameters of
  4933   5874   ** the xFileControl method.  ^The return value of the xFileControl
  4934   5875   ** method becomes the return value of this routine.
         5876  +**
         5877  +** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes
         5878  +** a pointer to the underlying [sqlite3_file] object to be written into
         5879  +** the space pointed to by the 4th parameter.  ^The SQLITE_FCNTL_FILE_POINTER
         5880  +** case is a short-circuit path which does not actually invoke the
         5881  +** underlying sqlite3_io_methods.xFileControl method.
  4935   5882   **
  4936   5883   ** ^If the second parameter (zDbName) does not match the name of any
  4937   5884   ** open database file, then SQLITE_ERROR is returned.  ^This error
  4938   5885   ** code is not remembered and will not be recalled by [sqlite3_errcode()]
  4939   5886   ** or [sqlite3_errmsg()].  The underlying xFileControl method might
  4940   5887   ** also return SQLITE_ERROR.  There is no way to distinguish between
  4941   5888   ** an incorrect zDbName and an SQLITE_ERROR return from the underlying
................................................................................
  4984   5931   #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10
  4985   5932   #define SQLITE_TESTCTRL_PENDING_BYTE            11
  4986   5933   #define SQLITE_TESTCTRL_ASSERT                  12
  4987   5934   #define SQLITE_TESTCTRL_ALWAYS                  13
  4988   5935   #define SQLITE_TESTCTRL_RESERVE                 14
  4989   5936   #define SQLITE_TESTCTRL_OPTIMIZATIONS           15
  4990   5937   #define SQLITE_TESTCTRL_ISKEYWORD               16
  4991         -#define SQLITE_TESTCTRL_LAST                    16
         5938  +#define SQLITE_TESTCTRL_SCRATCHMALLOC           17
         5939  +#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
         5940  +#define SQLITE_TESTCTRL_EXPLAIN_STMT            19
         5941  +#define SQLITE_TESTCTRL_LAST                    19
  4992   5942   
  4993   5943   /*
  4994   5944   ** CAPI3REF: SQLite Runtime Status
  4995   5945   **
  4996   5946   ** ^This interface is used to retrieve runtime status information
  4997         -** about the preformance of SQLite, and optionally to reset various
         5947  +** about the performance of SQLite, and optionally to reset various
  4998   5948   ** highwater marks.  ^The first argument is an integer code for
  4999   5949   ** the specific parameter to measure.  ^(Recognized integer codes
  5000         -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
         5950  +** are of the form [status parameters | SQLITE_STATUS_...].)^
  5001   5951   ** ^The current value of the parameter is returned into *pCurrent.
  5002   5952   ** ^The highest recorded value is returned in *pHighwater.  ^If the
  5003   5953   ** resetFlag is true, then the highest record value is reset after
  5004   5954   ** *pHighwater is written.  ^(Some parameters do not record the highest
  5005   5955   ** value.  For those parameters
  5006   5956   ** nothing is written into *pHighwater and the resetFlag is ignored.)^
  5007   5957   ** ^(Other parameters record only the highwater mark and not the current
  5008   5958   ** value.  For these latter parameters nothing is written into *pCurrent.)^
  5009   5959   **
  5010         -** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
         5960  +** ^The sqlite3_status() routine returns SQLITE_OK on success and a
  5011   5961   ** non-zero [error code] on failure.
  5012   5962   **
  5013   5963   ** This routine is threadsafe but is not atomic.  This routine can be
  5014   5964   ** called while other threads are running the same or different SQLite
  5015   5965   ** interfaces.  However the values returned in *pCurrent and
  5016   5966   ** *pHighwater reflect the status of SQLite at different points in time
  5017   5967   ** and it is possible that another thread might change the parameter
................................................................................
  5020   5970   ** See also: [sqlite3_db_status()]
  5021   5971   */
  5022   5972   SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
  5023   5973   
  5024   5974   
  5025   5975   /*
  5026   5976   ** CAPI3REF: Status Parameters
         5977  +** KEYWORDS: {status parameters}
  5027   5978   **
  5028   5979   ** These integer constants designate various run-time status parameters
  5029   5980   ** that can be returned by [sqlite3_status()].
  5030   5981   **
  5031   5982   ** <dl>
  5032         -** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
         5983  +** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
  5033   5984   ** <dd>This parameter is the current amount of memory checked out
  5034   5985   ** using [sqlite3_malloc()], either directly or indirectly.  The
  5035   5986   ** figure includes calls made to [sqlite3_malloc()] by the application
  5036   5987   ** and internal memory usage by the SQLite library.  Scratch memory
  5037   5988   ** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
  5038   5989   ** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
  5039   5990   ** this parameter.  The amount returned is the sum of the allocation
  5040   5991   ** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^
  5041   5992   **
  5042         -** ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
         5993  +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
  5043   5994   ** <dd>This parameter records the largest memory allocation request
  5044   5995   ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
  5045   5996   ** internal equivalents).  Only the value returned in the
  5046   5997   ** *pHighwater parameter to [sqlite3_status()] is of interest.  
  5047   5998   ** The value written into the *pCurrent parameter is undefined.</dd>)^
  5048   5999   **
  5049         -** ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
         6000  +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
         6001  +** <dd>This parameter records the number of separate memory allocations
         6002  +** currently checked out.</dd>)^
         6003  +**
         6004  +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
  5050   6005   ** <dd>This parameter returns the number of pages used out of the
  5051   6006   ** [pagecache memory allocator] that was configured using 
  5052   6007   ** [SQLITE_CONFIG_PAGECACHE].  The
  5053   6008   ** value returned is in pages, not in bytes.</dd>)^
  5054   6009   **
         6010  +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] 
  5055   6011   ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
  5056   6012   ** <dd>This parameter returns the number of bytes of page cache
  5057         -** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE]
         6013  +** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
  5058   6014   ** buffer and where forced to overflow to [sqlite3_malloc()].  The
  5059   6015   ** returned value includes allocations that overflowed because they
  5060   6016   ** where too large (they were larger than the "sz" parameter to
  5061   6017   ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
  5062   6018   ** no space was left in the page cache.</dd>)^
  5063   6019   **
  5064         -** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
         6020  +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
  5065   6021   ** <dd>This parameter records the largest memory allocation request
  5066   6022   ** handed to [pagecache memory allocator].  Only the value returned in the
  5067   6023   ** *pHighwater parameter to [sqlite3_status()] is of interest.  
  5068   6024   ** The value written into the *pCurrent parameter is undefined.</dd>)^
  5069   6025   **
  5070         -** ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
         6026  +** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
  5071   6027   ** <dd>This parameter returns the number of allocations used out of the
  5072   6028   ** [scratch memory allocator] configured using
  5073   6029   ** [SQLITE_CONFIG_SCRATCH].  The value returned is in allocations, not
  5074   6030   ** in bytes.  Since a single thread may only have one scratch allocation
  5075   6031   ** outstanding at time, this parameter also reports the number of threads
  5076   6032   ** using scratch memory at the same time.</dd>)^
  5077   6033   **
  5078         -** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
         6034  +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
  5079   6035   ** <dd>This parameter returns the number of bytes of scratch memory
  5080         -** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH]
         6036  +** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]
  5081   6037   ** buffer and where forced to overflow to [sqlite3_malloc()].  The values
  5082   6038   ** returned include overflows because the requested allocation was too
  5083   6039   ** larger (that is, because the requested allocation was larger than the
  5084   6040   ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer
  5085   6041   ** slots were available.
  5086   6042   ** </dd>)^
  5087   6043   **
  5088         -** ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
         6044  +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
  5089   6045   ** <dd>This parameter records the largest memory allocation request
  5090   6046   ** handed to [scratch memory allocator].  Only the value returned in the
  5091   6047   ** *pHighwater parameter to [sqlite3_status()] is of interest.  
  5092   6048   ** The value written into the *pCurrent parameter is undefined.</dd>)^
  5093   6049   **
  5094         -** ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
         6050  +** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
  5095   6051   ** <dd>This parameter records the deepest parser stack.  It is only
  5096   6052   ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
  5097   6053   ** </dl>
  5098   6054   **
  5099   6055   ** New status parameters may be added from time to time.
  5100   6056   */
  5101   6057   #define SQLITE_STATUS_MEMORY_USED          0
................................................................................
  5103   6059   #define SQLITE_STATUS_PAGECACHE_OVERFLOW   2
  5104   6060   #define SQLITE_STATUS_SCRATCH_USED         3
  5105   6061   #define SQLITE_STATUS_SCRATCH_OVERFLOW     4
  5106   6062   #define SQLITE_STATUS_MALLOC_SIZE          5
  5107   6063   #define SQLITE_STATUS_PARSER_STACK         6
  5108   6064   #define SQLITE_STATUS_PAGECACHE_SIZE       7
  5109   6065   #define SQLITE_STATUS_SCRATCH_SIZE         8
         6066  +#define SQLITE_STATUS_MALLOC_COUNT         9
  5110   6067   
  5111   6068   /*
  5112   6069   ** CAPI3REF: Database Connection Status
  5113   6070   **
  5114   6071   ** ^This interface is used to retrieve runtime status information 
  5115   6072   ** about a single [database connection].  ^The first argument is the
  5116   6073   ** database connection object to be interrogated.  ^The second argument
  5117   6074   ** is an integer constant, taken from the set of
  5118         -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
  5119         -** determiness the parameter to interrogate.  The set of 
  5120         -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
         6075  +** [SQLITE_DBSTATUS options], that
         6076  +** determines the parameter to interrogate.  The set of 
         6077  +** [SQLITE_DBSTATUS options] is likely
  5121   6078   ** to grow in future releases of SQLite.
  5122   6079   **
  5123   6080   ** ^The current value of the requested parameter is written into *pCur
  5124   6081   ** and the highest instantaneous value is written into *pHiwtr.  ^If
  5125   6082   ** the resetFlg is true, then the highest instantaneous value is
  5126   6083   ** reset back down to the current value.
         6084  +**
         6085  +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
         6086  +** non-zero [error code] on failure.
  5127   6087   **
  5128   6088   ** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
  5129   6089   */
  5130   6090   SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
  5131   6091   
  5132   6092   /*
  5133   6093   ** CAPI3REF: Status Parameters for database connections
         6094  +** KEYWORDS: {SQLITE_DBSTATUS options}
  5134   6095   **
  5135   6096   ** These constants are the available integer "verbs" that can be passed as
  5136   6097   ** the second argument to the [sqlite3_db_status()] interface.
  5137   6098   **
  5138   6099   ** New verbs may be added in future releases of SQLite. Existing verbs
  5139   6100   ** might be discontinued. Applications should check the return code from
  5140   6101   ** [sqlite3_db_status()] to make sure that the call worked.
  5141   6102   ** The [sqlite3_db_status()] interface will return a non-zero error code
  5142   6103   ** if a discontinued or unsupported verb is invoked.
  5143   6104   **
  5144   6105   ** <dl>
  5145         -** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
         6106  +** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
  5146   6107   ** <dd>This parameter returns the number of lookaside memory slots currently
  5147   6108   ** checked out.</dd>)^
  5148   6109   **
  5149         -** <dt>SQLITE_DBSTATUS_CACHE_USED</dt>
  5150         -** <dd>^This parameter returns the approximate number of of bytes of heap
  5151         -** memory used by all pager caches associated with the database connection.
         6110  +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
         6111  +** <dd>This parameter returns the number malloc attempts that were 
         6112  +** satisfied using lookaside memory. Only the high-water value is meaningful;
         6113  +** the current value is always zero.)^
         6114  +**
         6115  +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]
         6116  +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
         6117  +** <dd>This parameter returns the number malloc attempts that might have
         6118  +** been satisfied using lookaside memory but failed due to the amount of
         6119  +** memory requested being larger than the lookaside slot size.
         6120  +** Only the high-water value is meaningful;
         6121  +** the current value is always zero.)^
         6122  +**
         6123  +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]
         6124  +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
         6125  +** <dd>This parameter returns the number malloc attempts that might have
         6126  +** been satisfied using lookaside memory but failed due to all lookaside
         6127  +** memory already being in use.
         6128  +** Only the high-water value is meaningful;
         6129  +** the current value is always zero.)^
         6130  +**
         6131  +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
         6132  +** <dd>This parameter returns the approximate number of of bytes of heap
         6133  +** memory used by all pager caches associated with the database connection.)^
  5152   6134   ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
  5153         -** checked out.</dd>)^
         6135  +**
         6136  +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
         6137  +** <dd>This parameter returns the approximate number of of bytes of heap
         6138  +** memory used to store the schema for all databases associated
         6139  +** with the connection - main, temp, and any [ATTACH]-ed databases.)^ 
         6140  +** ^The full amount of memory used by the schemas is reported, even if the
         6141  +** schema memory is shared with other database connections due to
         6142  +** [shared cache mode] being enabled.
         6143  +** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
         6144  +**
         6145  +** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
         6146  +** <dd>This parameter returns the approximate number of of bytes of heap
         6147  +** and lookaside memory used by all prepared statements associated with
         6148  +** the database connection.)^
         6149  +** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
         6150  +** </dd>
         6151  +**
         6152  +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
         6153  +** <dd>This parameter returns the number of pager cache hits that have
         6154  +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT 
         6155  +** is always 0.
         6156  +** </dd>
         6157  +**
         6158  +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
         6159  +** <dd>This parameter returns the number of pager cache misses that have
         6160  +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS 
         6161  +** is always 0.
         6162  +** </dd>
         6163  +**
         6164  +** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>
         6165  +** <dd>This parameter returns the number of dirty cache entries that have
         6166  +** been written to disk. Specifically, the number of pages written to the
         6167  +** wal file in wal mode databases, or the number of pages written to the
         6168  +** database file in rollback mode databases. Any pages written as part of
         6169  +** transaction rollback or database recovery operations are not included.
         6170  +** If an IO or other error occurs while writing a page to disk, the effect
         6171  +** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
         6172  +** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
         6173  +** </dd>
  5154   6174   ** </dl>
  5155   6175   */
  5156         -#define SQLITE_DBSTATUS_LOOKASIDE_USED     0
  5157         -#define SQLITE_DBSTATUS_CACHE_USED         1
  5158         -#define SQLITE_DBSTATUS_MAX                1   /* Largest defined DBSTATUS */
         6176  +#define SQLITE_DBSTATUS_LOOKASIDE_USED       0
         6177  +#define SQLITE_DBSTATUS_CACHE_USED           1
         6178  +#define SQLITE_DBSTATUS_SCHEMA_USED          2
         6179  +#define SQLITE_DBSTATUS_STMT_USED            3
         6180  +#define SQLITE_DBSTATUS_LOOKASIDE_HIT        4
         6181  +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE  5
         6182  +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL  6
         6183  +#define SQLITE_DBSTATUS_CACHE_HIT            7
         6184  +#define SQLITE_DBSTATUS_CACHE_MISS           8
         6185  +#define SQLITE_DBSTATUS_CACHE_WRITE          9
         6186  +#define SQLITE_DBSTATUS_MAX                  9   /* Largest defined DBSTATUS */
  5159   6187   
  5160   6188   
  5161   6189   /*
  5162   6190   ** CAPI3REF: Prepared Statement Status
  5163   6191   **
  5164   6192   ** ^(Each prepared statement maintains various
  5165         -** [SQLITE_STMTSTATUS_SORT | counters] that measure the number
         6193  +** [SQLITE_STMTSTATUS counters] that measure the number
  5166   6194   ** of times it has performed specific operations.)^  These counters can
  5167   6195   ** be used to monitor the performance characteristics of the prepared
  5168   6196   ** statements.  For example, if the number of table steps greatly exceeds
  5169   6197   ** the number of table searches or result rows, that would tend to indicate
  5170   6198   ** that the prepared statement is using a full table scan rather than
  5171   6199   ** an index.  
  5172   6200   **
  5173   6201   ** ^(This interface is used to retrieve and reset counter values from
  5174   6202   ** a [prepared statement].  The first argument is the prepared statement
  5175   6203   ** object to be interrogated.  The second argument
  5176         -** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter]
         6204  +** is an integer code for a specific [SQLITE_STMTSTATUS counter]
  5177   6205   ** to be interrogated.)^
  5178   6206   ** ^The current value of the requested counter is returned.
  5179   6207   ** ^If the resetFlg is true, then the counter is reset to zero after this
  5180   6208   ** interface call returns.
  5181   6209   **
  5182   6210   ** See also: [sqlite3_status()] and [sqlite3_db_status()].
  5183   6211   */
  5184   6212   SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
  5185   6213   
  5186   6214   /*
  5187   6215   ** CAPI3REF: Status Parameters for prepared statements
         6216  +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters}
  5188   6217   **
  5189   6218   ** These preprocessor macros define integer codes that name counter
  5190   6219   ** values associated with the [sqlite3_stmt_status()] interface.
  5191   6220   ** The meanings of the various counters are as follows:
  5192   6221   **
  5193   6222   ** <dl>
  5194         -** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
         6223  +** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
  5195   6224   ** <dd>^This is the number of times that SQLite has stepped forward in
  5196   6225   ** a table as part of a full table scan.  Large numbers for this counter
  5197   6226   ** may indicate opportunities for performance improvement through 
  5198   6227   ** careful use of indices.</dd>
  5199   6228   **
  5200         -** <dt>SQLITE_STMTSTATUS_SORT</dt>
         6229  +** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>
  5201   6230   ** <dd>^This is the number of sort operations that have occurred.
  5202   6231   ** A non-zero value in this counter may indicate an opportunity to
  5203   6232   ** improvement performance through careful use of indices.</dd>
  5204   6233   **
  5205         -** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
         6234  +** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
  5206   6235   ** <dd>^This is the number of rows inserted into transient indices that
  5207   6236   ** were created automatically in order to help joins run faster.
  5208   6237   ** A non-zero value in this counter may indicate an opportunity to
  5209   6238   ** improvement performance by adding permanent indices that do not
  5210   6239   ** need to be reinitialized each time the statement is run.</dd>
  5211         -**
  5212   6240   ** </dl>
  5213   6241   */
  5214   6242   #define SQLITE_STMTSTATUS_FULLSCAN_STEP     1
  5215   6243   #define SQLITE_STMTSTATUS_SORT              2
  5216   6244   #define SQLITE_STMTSTATUS_AUTOINDEX         3
  5217   6245   
  5218   6246   /*
................................................................................
  5220   6248   **
  5221   6249   ** The sqlite3_pcache type is opaque.  It is implemented by
  5222   6250   ** the pluggable module.  The SQLite core has no knowledge of
  5223   6251   ** its size or internal structure and never deals with the
  5224   6252   ** sqlite3_pcache object except by holding and passing pointers
  5225   6253   ** to the object.
  5226   6254   **
  5227         -** See [sqlite3_pcache_methods] for additional information.
         6255  +** See [sqlite3_pcache_methods2] for additional information.
  5228   6256   */
  5229   6257   typedef struct sqlite3_pcache sqlite3_pcache;
         6258  +
         6259  +/*
         6260  +** CAPI3REF: Custom Page Cache Object
         6261  +**
         6262  +** The sqlite3_pcache_page object represents a single page in the
         6263  +** page cache.  The page cache will allocate instances of this
         6264  +** object.  Various methods of the page cache use pointers to instances
         6265  +** of this object as parameters or as their return value.
         6266  +**
         6267  +** See [sqlite3_pcache_methods2] for additional information.
         6268  +*/
         6269  +typedef struct sqlite3_pcache_page sqlite3_pcache_page;
         6270  +struct sqlite3_pcache_page {
         6271  +  void *pBuf;        /* The content of the page */
         6272  +  void *pExtra;      /* Extra information associated with the page */
         6273  +};
  5230   6274   
  5231   6275   /*
  5232   6276   ** CAPI3REF: Application Defined Page Cache.
  5233   6277   ** KEYWORDS: {page cache}
  5234   6278   **
  5235         -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can
         6279  +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
  5236   6280   ** register an alternative page cache implementation by passing in an 
  5237         -** instance of the sqlite3_pcache_methods structure.)^ The majority of the 
  5238         -** heap memory used by SQLite is used by the page cache to cache data read 
  5239         -** from, or ready to be written to, the database file. By implementing a 
  5240         -** custom page cache using this API, an application can control more 
  5241         -** precisely the amount of memory consumed by SQLite, the way in which 
         6281  +** instance of the sqlite3_pcache_methods2 structure.)^
         6282  +** In many applications, most of the heap memory allocated by 
         6283  +** SQLite is used for the page cache.
         6284  +** By implementing a 
         6285  +** custom page cache using this API, an application can better control
         6286  +** the amount of memory consumed by SQLite, the way in which 
  5242   6287   ** that memory is allocated and released, and the policies used to 
  5243   6288   ** determine exactly which parts of a database file are cached and for 
  5244   6289   ** how long.
  5245   6290   **
  5246         -** ^(The contents of the sqlite3_pcache_methods structure are copied to an
         6291  +** The alternative page cache mechanism is an
         6292  +** extreme measure that is only needed by the most demanding applications.
         6293  +** The built-in page cache is recommended for most uses.
         6294  +**
         6295  +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an
  5247   6296   ** internal buffer by SQLite within the call to [sqlite3_config].  Hence
  5248   6297   ** the application may discard the parameter after the call to
  5249   6298   ** [sqlite3_config()] returns.)^
  5250   6299   **
  5251         -** ^The xInit() method is called once for each call to [sqlite3_initialize()]
         6300  +** [[the xInit() page cache method]]
         6301  +** ^(The xInit() method is called once for each effective 
         6302  +** call to [sqlite3_initialize()])^
  5252   6303   ** (usually only once during the lifetime of the process). ^(The xInit()
  5253         -** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^
  5254         -** ^The xInit() method can set up up global structures and/or any mutexes
         6304  +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
         6305  +** The intent of the xInit() method is to set up global data structures 
  5255   6306   ** required by the custom page cache implementation. 
         6307  +** ^(If the xInit() method is NULL, then the 
         6308  +** built-in default page cache is used instead of the application defined
         6309  +** page cache.)^
  5256   6310   **
  5257         -** ^The xShutdown() method is called from within [sqlite3_shutdown()], 
  5258         -** if the application invokes this API. It can be used to clean up 
         6311  +** [[the xShutdown() page cache method]]
         6312  +** ^The xShutdown() method is called by [sqlite3_shutdown()].
         6313  +** It can be used to clean up 
  5259   6314   ** any outstanding resources before process shutdown, if required.
         6315  +** ^The xShutdown() method may be NULL.
  5260   6316   **
  5261         -** ^SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes
  5262         -** the xInit method, so the xInit method need not be threadsafe.  ^The
         6317  +** ^SQLite automatically serializes calls to the xInit method,
         6318  +** so the xInit method need not be threadsafe.  ^The
  5263   6319   ** xShutdown method is only called from [sqlite3_shutdown()] so it does
  5264   6320   ** not need to be threadsafe either.  All other methods must be threadsafe
  5265   6321   ** in multithreaded applications.
  5266   6322   **
  5267   6323   ** ^SQLite will never invoke xInit() more than once without an intervening
  5268   6324   ** call to xShutdown().
  5269   6325   **
  5270         -** ^The xCreate() method is used to construct a new cache instance.  SQLite
  5271         -** will typically create one cache instance for each open database file,
         6326  +** [[the xCreate() page cache methods]]
         6327  +** ^SQLite invokes the xCreate() method to construct a new cache instance.
         6328  +** SQLite will typically create one cache instance for each open database file,
  5272   6329   ** though this is not guaranteed. ^The
  5273   6330   ** first parameter, szPage, is the size in bytes of the pages that must
  5274         -** be allocated by the cache.  ^szPage will not be a power of two.  ^szPage
  5275         -** will the page size of the database file that is to be cached plus an
  5276         -** increment (here called "R") of about 100 or 200.  ^SQLite will use the
  5277         -** extra R bytes on each page to store metadata about the underlying
  5278         -** database page on disk.  The value of R depends
         6331  +** be allocated by the cache.  ^szPage will always a power of two.  ^The
         6332  +** second parameter szExtra is a number of bytes of extra storage 
         6333  +** associated with each page cache entry.  ^The szExtra parameter will
         6334  +** a number less than 250.  SQLite will use the
         6335  +** extra szExtra bytes on each page to store metadata about the underlying
         6336  +** database page on disk.  The value passed into szExtra depends
  5279   6337   ** on the SQLite version, the target platform, and how SQLite was compiled.
  5280         -** ^R is constant for a particular build of SQLite.  ^The second argument to
  5281         -** xCreate(), bPurgeable, is true if the cache being created will
  5282         -** be used to cache database pages of a file stored on disk, or
  5283         -** false if it is used for an in-memory database. ^The cache implementation
         6338  +** ^The third argument to xCreate(), bPurgeable, is true if the cache being
         6339  +** created will be used to cache database pages of a file stored on disk, or
         6340  +** false if it is used for an in-memory database. The cache implementation
  5284   6341   ** does not have to do anything special based with the value of bPurgeable;
  5285   6342   ** it is purely advisory.  ^On a cache where bPurgeable is false, SQLite will
  5286   6343   ** never invoke xUnpin() except to deliberately delete a page.
  5287         -** ^In other words, a cache created with bPurgeable set to false will
         6344  +** ^In other words, calls to xUnpin() on a cache with bPurgeable set to
         6345  +** false will always have the "discard" flag set to true.  
         6346  +** ^Hence, a cache created with bPurgeable false will
  5288   6347   ** never contain any unpinned pages.
  5289   6348   **
         6349  +** [[the xCachesize() page cache method]]
  5290   6350   ** ^(The xCachesize() method may be called at any time by SQLite to set the
  5291   6351   ** suggested maximum cache-size (number of pages stored by) the cache
  5292   6352   ** instance passed as the first argument. This is the value configured using
  5293         -** the SQLite "[PRAGMA cache_size]" command.)^  ^As with the bPurgeable
         6353  +** the SQLite "[PRAGMA cache_size]" command.)^  As with the bPurgeable
  5294   6354   ** parameter, the implementation is not required to do anything with this
  5295   6355   ** value; it is advisory only.
  5296   6356   **
  5297         -** ^The xPagecount() method should return the number of pages currently
  5298         -** stored in the cache.
         6357  +** [[the xPagecount() page cache methods]]
         6358  +** The xPagecount() method must return the number of pages currently
         6359  +** stored in the cache, both pinned and unpinned.
  5299   6360   ** 
  5300         -** ^The xFetch() method is used to fetch a page and return a pointer to it. 
  5301         -** ^A 'page', in this context, is a buffer of szPage bytes aligned at an
  5302         -** 8-byte boundary. ^The page to be fetched is determined by the key. ^The
  5303         -** mimimum key value is 1. After it has been retrieved using xFetch, the page 
  5304         -** is considered to be "pinned".
         6361  +** [[the xFetch() page cache methods]]
         6362  +** The xFetch() method locates a page in the cache and returns a pointer to 
         6363  +** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
         6364  +** The pBuf element of the returned sqlite3_pcache_page object will be a
         6365  +** pointer to a buffer of szPage bytes used to store the content of a 
         6366  +** single database page.  The pExtra element of sqlite3_pcache_page will be
         6367  +** a pointer to the szExtra bytes of extra storage that SQLite has requested
         6368  +** for each entry in the page cache.
  5305   6369   **
  5306         -** ^If the requested page is already in the page cache, then the page cache
         6370  +** The page to be fetched is determined by the key. ^The minimum key value
         6371  +** is 1.  After it has been retrieved using xFetch, the page is considered
         6372  +** to be "pinned".
         6373  +**
         6374  +** If the requested page is already in the page cache, then the page cache
  5307   6375   ** implementation must return a pointer to the page buffer with its content
  5308         -** intact.  ^(If the requested page is not already in the cache, then the
  5309         -** behavior of the cache implementation is determined by the value of the
  5310         -** createFlag parameter passed to xFetch, according to the following table:
         6376  +** intact.  If the requested page is not already in the cache, then the
         6377  +** cache implementation should use the value of the createFlag
         6378  +** parameter to help it determined what action to take:
  5311   6379   **
  5312   6380   ** <table border=1 width=85% align=center>
  5313   6381   ** <tr><th> createFlag <th> Behaviour when page is not already in cache
  5314   6382   ** <tr><td> 0 <td> Do not allocate a new page.  Return NULL.
  5315   6383   ** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
  5316   6384   **                 Otherwise return NULL.
  5317   6385   ** <tr><td> 2 <td> Make every effort to allocate a new page.  Only return
  5318   6386   **                 NULL if allocating a new page is effectively impossible.
  5319         -** </table>)^
         6387  +** </table>
  5320   6388   **
  5321         -** SQLite will normally invoke xFetch() with a createFlag of 0 or 1.  If
  5322         -** a call to xFetch() with createFlag==1 returns NULL, then SQLite will
         6389  +** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1.  SQLite
         6390  +** will only use a createFlag of 2 after a prior call with a createFlag of 1
         6391  +** failed.)^  In between the to xFetch() calls, SQLite may
  5323   6392   ** attempt to unpin one or more cache pages by spilling the content of
  5324         -** pinned pages to disk and synching the operating system disk cache. After
  5325         -** attempting to unpin pages, the xFetch() method will be invoked again with
  5326         -** a createFlag of 2.
         6393  +** pinned pages to disk and synching the operating system disk cache.
  5327   6394   **
         6395  +** [[the xUnpin() page cache method]]
  5328   6396   ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page
  5329         -** as its second argument. ^(If the third parameter, discard, is non-zero,
  5330         -** then the page should be evicted from the cache. In this case SQLite 
  5331         -** assumes that the next time the page is retrieved from the cache using
  5332         -** the xFetch() method, it will be zeroed.)^ ^If the discard parameter is
  5333         -** zero, then the page is considered to be unpinned. ^The cache implementation
         6397  +** as its second argument.  If the third parameter, discard, is non-zero,
         6398  +** then the page must be evicted from the cache.
         6399  +** ^If the discard parameter is
         6400  +** zero, then the page may be discarded or retained at the discretion of
         6401  +** page cache implementation. ^The page cache implementation
  5334   6402   ** may choose to evict unpinned pages at any time.
  5335   6403   **
  5336         -** ^(The cache is not required to perform any reference counting. A single 
         6404  +** The cache must not perform any reference counting. A single 
  5337   6405   ** call to xUnpin() unpins the page regardless of the number of prior calls 
  5338         -** to xFetch().)^
         6406  +** to xFetch().
  5339   6407   **
  5340         -** ^The xRekey() method is used to change the key value associated with the
  5341         -** page passed as the second argument from oldKey to newKey. ^If the cache
  5342         -** previously contains an entry associated with newKey, it should be
         6408  +** [[the xRekey() page cache methods]]
         6409  +** The xRekey() method is used to change the key value associated with the
         6410  +** page passed as the second argument. If the cache
         6411  +** previously contains an entry associated with newKey, it must be
  5343   6412   ** discarded. ^Any prior cache entry associated with newKey is guaranteed not
  5344   6413   ** to be pinned.
  5345   6414   **
  5346         -** ^When SQLite calls the xTruncate() method, the cache must discard all
         6415  +** When SQLite calls the xTruncate() method, the cache must discard all
  5347   6416   ** existing cache entries with page numbers (keys) greater than or equal
  5348         -** to the value of the iLimit parameter passed to xTruncate(). ^If any
         6417  +** to the value of the iLimit parameter passed to xTruncate(). If any
  5349   6418   ** of these pages are pinned, they are implicitly unpinned, meaning that
  5350   6419   ** they can be safely discarded.
  5351   6420   **
         6421  +** [[the xDestroy() page cache method]]
  5352   6422   ** ^The xDestroy() method is used to delete a cache allocated by xCreate().
  5353   6423   ** All resources associated with the specified cache should be freed. ^After
  5354   6424   ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]
  5355         -** handle invalid, and will not use it with any other sqlite3_pcache_methods
         6425  +** handle invalid, and will not use it with any other sqlite3_pcache_methods2
  5356   6426   ** functions.
         6427  +**
         6428  +** [[the xShrink() page cache method]]
         6429  +** ^SQLite invokes the xShrink() method when it wants the page cache to
         6430  +** free up as much of heap memory as possible.  The page cache implementation
         6431  +** is not obligated to free any memory, but well-behaved implementations should
         6432  +** do their best.
         6433  +*/
         6434  +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
         6435  +struct sqlite3_pcache_methods2 {
         6436  +  int iVersion;
         6437  +  void *pArg;
         6438  +  int (*xInit)(void*);
         6439  +  void (*xShutdown)(void*);
         6440  +  sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
         6441  +  void (*xCachesize)(sqlite3_pcache*, int nCachesize);
         6442  +  int (*xPagecount)(sqlite3_pcache*);
         6443  +  sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
         6444  +  void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
         6445  +  void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, 
         6446  +      unsigned oldKey, unsigned newKey);
         6447  +  void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
         6448  +  void (*xDestroy)(sqlite3_pcache*);
         6449  +  void (*xShrink)(sqlite3_pcache*);
         6450  +};
         6451  +
         6452  +/*
         6453  +** This is the obsolete pcache_methods object that has now been replaced
         6454  +** by sqlite3_pcache_methods2.  This object is not used by SQLite.  It is
         6455  +** retained in the header file for backwards compatibility only.
  5357   6456   */
  5358   6457   typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
  5359   6458   struct sqlite3_pcache_methods {
  5360   6459     void *pArg;
  5361   6460     int (*xInit)(void*);
  5362   6461     void (*xShutdown)(void*);
  5363   6462     sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable);
................................................................................
  5365   6464     int (*xPagecount)(sqlite3_pcache*);
  5366   6465     void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
  5367   6466     void (*xUnpin)(sqlite3_pcache*, void*, int discard);
  5368   6467     void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey);
  5369   6468     void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
  5370   6469     void (*xDestroy)(sqlite3_pcache*);
  5371   6470   };
         6471  +
  5372   6472   
  5373   6473   /*
  5374   6474   ** CAPI3REF: Online Backup Object
  5375   6475   **
  5376   6476   ** The sqlite3_backup object records state information about an ongoing
  5377   6477   ** online backup operation.  ^The sqlite3_backup object is created by
  5378   6478   ** a call to [sqlite3_backup_init()] and is destroyed by a call to
................................................................................
  5387   6487   **
  5388   6488   ** The backup API copies the content of one database into another.
  5389   6489   ** It is useful either for creating backups of databases or
  5390   6490   ** for copying in-memory databases to or from persistent files. 
  5391   6491   **
  5392   6492   ** See Also: [Using the SQLite Online Backup API]
  5393   6493   **
  5394         -** ^Exclusive access is required to the destination database for the 
  5395         -** duration of the operation. ^However the source database is only
  5396         -** read-locked while it is actually being read; it is not locked
  5397         -** continuously for the entire backup operation. ^Thus, the backup may be
  5398         -** performed on a live source database without preventing other users from
         6494  +** ^SQLite holds a write transaction open on the destination database file
         6495  +** for the duration of the backup operation.
         6496  +** ^The source database is read-locked only while it is being read;
         6497  +** it is not locked continuously for the entire backup operation.
         6498  +** ^Thus, the backup may be performed on a live source database without
         6499  +** preventing other database connections from
  5399   6500   ** reading or writing to the source database while the backup is underway.
  5400   6501   ** 
  5401   6502   ** ^(To perform a backup operation: 
  5402   6503   **   <ol>
  5403   6504   **     <li><b>sqlite3_backup_init()</b> is called once to initialize the
  5404   6505   **         backup, 
  5405   6506   **     <li><b>sqlite3_backup_step()</b> is called one or more times to transfer 
................................................................................
  5406   6507   **         the data between the two databases, and finally
  5407   6508   **     <li><b>sqlite3_backup_finish()</b> is called to release all resources 
  5408   6509   **         associated with the backup operation. 
  5409   6510   **   </ol>)^
  5410   6511   ** There should be exactly one call to sqlite3_backup_finish() for each
  5411   6512   ** successful call to sqlite3_backup_init().
  5412   6513   **
  5413         -** <b>sqlite3_backup_init()</b>
         6514  +** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>
  5414   6515   **
  5415   6516   ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the 
  5416   6517   ** [database connection] associated with the destination database 
  5417   6518   ** and the database name, respectively.
  5418   6519   ** ^The database name is "main" for the main database, "temp" for the
  5419   6520   ** temporary database, or the name specified after the AS keyword in
  5420   6521   ** an [ATTACH] statement for an attached database.
  5421   6522   ** ^The S and M arguments passed to 
  5422   6523   ** sqlite3_backup_init(D,N,S,M) identify the [database connection]
  5423   6524   ** and database name of the source database, respectively.
  5424   6525   ** ^The source and destination [database connections] (parameters S and D)
  5425         -** must be different or else sqlite3_backup_init(D,N,S,M) will file with
         6526  +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
  5426   6527   ** an error.
  5427   6528   **
  5428   6529   ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is
  5429         -** returned and an error code and error message are store3d in the
         6530  +** returned and an error code and error message are stored in the
  5430   6531   ** destination [database connection] D.
  5431   6532   ** ^The error code and message for the failed call to sqlite3_backup_init()
  5432   6533   ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or
  5433   6534   ** [sqlite3_errmsg16()] functions.
  5434   6535   ** ^A successful call to sqlite3_backup_init() returns a pointer to an
  5435   6536   ** [sqlite3_backup] object.
  5436   6537   ** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and
  5437   6538   ** sqlite3_backup_finish() functions to perform the specified backup 
  5438   6539   ** operation.
  5439   6540   **
  5440         -** <b>sqlite3_backup_step()</b>
         6541  +** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>
  5441   6542   **
  5442   6543   ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between 
  5443   6544   ** the source and destination databases specified by [sqlite3_backup] object B.
  5444   6545   ** ^If N is negative, all remaining source pages are copied. 
  5445   6546   ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there
  5446         -** are still more pages to be copied, then the function resturns [SQLITE_OK].
         6547  +** are still more pages to be copied, then the function returns [SQLITE_OK].
  5447   6548   ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages
  5448   6549   ** from source to destination, then it returns [SQLITE_DONE].
  5449   6550   ** ^If an error occurs while running sqlite3_backup_step(B,N),
  5450   6551   ** then an [error code] is returned. ^As well as [SQLITE_OK] and
  5451   6552   ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY],
  5452   6553   ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an
  5453   6554   ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code.
  5454   6555   **
  5455         -** ^The sqlite3_backup_step() might return [SQLITE_READONLY] if the destination
  5456         -** database was opened read-only or if
  5457         -** the destination is an in-memory database with a different page size
  5458         -** from the source database.
         6556  +** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if
         6557  +** <ol>
         6558  +** <li> the destination database was opened read-only, or
         6559  +** <li> the destination database is using write-ahead-log journaling
         6560  +** and the destination and source page sizes differ, or
         6561  +** <li> the destination database is an in-memory database and the
         6562  +** destination and source page sizes differ.
         6563  +** </ol>)^
  5459   6564   **
  5460   6565   ** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then
  5461   6566   ** the [sqlite3_busy_handler | busy-handler function]
  5462   6567   ** is invoked (if one is specified). ^If the 
  5463   6568   ** busy-handler returns non-zero before the lock is available, then 
  5464   6569   ** [SQLITE_BUSY] is returned to the caller. ^In this case the call to
  5465   6570   ** sqlite3_backup_step() can be retried later. ^If the source
................................................................................
  5486   6591   ** external process or via a database connection other than the one being
  5487   6592   ** used by the backup operation, then the backup will be automatically
  5488   6593   ** restarted by the next call to sqlite3_backup_step(). ^If the source 
  5489   6594   ** database is modified by the using the same database connection as is used
  5490   6595   ** by the backup operation, then the backup database is automatically
  5491   6596   ** updated at the same time.
  5492   6597   **
  5493         -** <b>sqlite3_backup_finish()</b>
         6598  +** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>
  5494   6599   **
  5495   6600   ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the 
  5496   6601   ** application wishes to abandon the backup operation, the application
  5497   6602   ** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish().
  5498   6603   ** ^The sqlite3_backup_finish() interfaces releases all
  5499   6604   ** resources associated with the [sqlite3_backup] object. 
  5500   6605   ** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any
................................................................................
  5509   6614   ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then
  5510   6615   ** sqlite3_backup_finish() returns the corresponding [error code].
  5511   6616   **
  5512   6617   ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step()
  5513   6618   ** is not a permanent error and does not affect the return value of
  5514   6619   ** sqlite3_backup_finish().
  5515   6620   **
  5516         -** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
         6621  +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]
         6622  +** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>
  5517   6623   **
  5518   6624   ** ^Each call to sqlite3_backup_step() sets two values inside
  5519   6625   ** the [sqlite3_backup] object: the number of pages still to be backed
  5520         -** up and the total number of pages in the source databae file.
         6626  +** up and the total number of pages in the source database file.
  5521   6627   ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
  5522   6628   ** retrieve these two values, respectively.
  5523   6629   **
  5524   6630   ** ^The values returned by these functions are only updated by
  5525   6631   ** sqlite3_backup_step(). ^If the source database is modified during a backup
  5526   6632   ** operation, then the values are not updated to account for any extra
  5527   6633   ** pages that need to be updated or the size of the source database file
................................................................................
  5609   6715   ** the other connections to use as the blocking connection.
  5610   6716   **
  5611   6717   ** ^(There may be at most one unlock-notify callback registered by a 
  5612   6718   ** blocked connection. If sqlite3_unlock_notify() is called when the
  5613   6719   ** blocked connection already has a registered unlock-notify callback,
  5614   6720   ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
  5615   6721   ** called with a NULL pointer as its second argument, then any existing
  5616         -** unlock-notify callback is cancelled. ^The blocked connections 
         6722  +** unlock-notify callback is canceled. ^The blocked connections 
  5617   6723   ** unlock-notify callback may also be canceled by closing the blocked
  5618   6724   ** connection using [sqlite3_close()].
  5619   6725   **
  5620   6726   ** The unlock-notify callback is not reentrant. If an application invokes
  5621   6727   ** any sqlite3_xxx API functions from within an unlock-notify callback, a
  5622   6728   ** crash or deadlock may be the result.
  5623   6729   **
................................................................................
  5689   6795     void *pNotifyArg                            /* Argument to pass to xNotify */
  5690   6796   );
  5691   6797   
  5692   6798   
  5693   6799   /*
  5694   6800   ** CAPI3REF: String Comparison
  5695   6801   **
  5696         -** ^The [sqlite3_strnicmp()] API allows applications and extensions to
  5697         -** compare the contents of two buffers containing UTF-8 strings in a
  5698         -** case-indendent fashion, using the same definition of case independence 
  5699         -** that SQLite uses internally when comparing identifiers.
         6802  +** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications
         6803  +** and extensions to compare the contents of two buffers containing UTF-8
         6804  +** strings in a case-independent fashion, using the same definition of "case
         6805  +** independence" that SQLite uses internally when comparing identifiers.
  5700   6806   */
         6807  +SQLITE_API int sqlite3_stricmp(const char *, const char *);
  5701   6808   SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
  5702   6809   
  5703   6810   /*
  5704   6811   ** CAPI3REF: Error Logging Interface
  5705   6812   **
  5706   6813   ** ^The [sqlite3_log()] interface writes a message into the error log
  5707   6814   ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
................................................................................
  5718   6825   ** To avoid deadlocks and other threading problems, the sqlite3_log() routine
  5719   6826   ** will not use dynamically allocated memory.  The log message is stored in
  5720   6827   ** a fixed-length buffer on the stack.  If the log message is longer than
  5721   6828   ** a few hundred characters, it will be truncated to the length of the
  5722   6829   ** buffer.
  5723   6830   */
  5724   6831   SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
         6832  +
         6833  +/*
         6834  +** CAPI3REF: Write-Ahead Log Commit Hook
         6835  +**
         6836  +** ^The [sqlite3_wal_hook()] function is used to register a callback that
         6837  +** will be invoked each time a database connection commits data to a
         6838  +** [write-ahead log] (i.e. whenever a transaction is committed in
         6839  +** [journal_mode | journal_mode=WAL mode]). 
         6840  +**
         6841  +** ^The callback is invoked by SQLite after the commit has taken place and 
         6842  +** the associated write-lock on the database released, so the implementation 
         6843  +** may read, write or [checkpoint] the database as required.
         6844  +**
         6845  +** ^The first parameter passed to the callback function when it is invoked
         6846  +** is a copy of the third parameter passed to sqlite3_wal_hook() when
         6847  +** registering the callback. ^The second is a copy of the database handle.
         6848  +** ^The third parameter is the name of the database that was written to -
         6849  +** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
         6850  +** is the number of pages currently in the write-ahead log file,
         6851  +** including those that were just committed.
         6852  +**
         6853  +** The callback function should normally return [SQLITE_OK].  ^If an error
         6854  +** code is returned, that error will propagate back up through the
         6855  +** SQLite code base to cause the statement that provoked the callback
         6856  +** to report an error, though the commit will have still occurred. If the
         6857  +** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value
         6858  +** that does not correspond to any valid SQLite error code, the results
         6859  +** are undefined.
         6860  +**
         6861  +** A single database handle may have at most a single write-ahead log callback 
         6862  +** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
         6863  +** previously registered write-ahead log callback. ^Note that the
         6864  +** [sqlite3_wal_autocheckpoint()] interface and the
         6865  +** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
         6866  +** those overwrite any prior [sqlite3_wal_hook()] settings.
         6867  +*/
         6868  +SQLITE_API void *sqlite3_wal_hook(
         6869  +  sqlite3*, 
         6870  +  int(*)(void *,sqlite3*,const char*,int),
         6871  +  void*
         6872  +);
         6873  +
         6874  +/*
         6875  +** CAPI3REF: Configure an auto-checkpoint
         6876  +**
         6877  +** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
         6878  +** [sqlite3_wal_hook()] that causes any database on [database connection] D
         6879  +** to automatically [checkpoint]
         6880  +** after committing a transaction if there are N or
         6881  +** more frames in the [write-ahead log] file.  ^Passing zero or 
         6882  +** a negative value as the nFrame parameter disables automatic
         6883  +** checkpoints entirely.
         6884  +**
         6885  +** ^The callback registered by this function replaces any existing callback
         6886  +** registered using [sqlite3_wal_hook()].  ^Likewise, registering a callback
         6887  +** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism
         6888  +** configured by this function.
         6889  +**
         6890  +** ^The [wal_autocheckpoint pragma] can be used to invoke this interface
         6891  +** from SQL.
         6892  +**
         6893  +** ^Every new [database connection] defaults to having the auto-checkpoint
         6894  +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
         6895  +** pages.  The use of this interface
         6896  +** is only necessary if the default setting is found to be suboptimal
         6897  +** for a particular application.
         6898  +*/
         6899  +SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
         6900  +
         6901  +/*
         6902  +** CAPI3REF: Checkpoint a database
         6903  +**
         6904  +** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X
         6905  +** on [database connection] D to be [checkpointed].  ^If X is NULL or an
         6906  +** empty string, then a checkpoint is run on all databases of
         6907  +** connection D.  ^If the database connection D is not in
         6908  +** [WAL | write-ahead log mode] then this interface is a harmless no-op.
         6909  +**
         6910  +** ^The [wal_checkpoint pragma] can be used to invoke this interface
         6911  +** from SQL.  ^The [sqlite3_wal_autocheckpoint()] interface and the
         6912  +** [wal_autocheckpoint pragma] can be used to cause this interface to be
         6913  +** run whenever the WAL reaches a certain size threshold.
         6914  +**
         6915  +** See also: [sqlite3_wal_checkpoint_v2()]
         6916  +*/
         6917  +SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
         6918  +
         6919  +/*
         6920  +** CAPI3REF: Checkpoint a database
         6921  +**
         6922  +** Run a checkpoint operation on WAL database zDb attached to database 
         6923  +** handle db. The specific operation is determined by the value of the 
         6924  +** eMode parameter:
         6925  +**
         6926  +** <dl>
         6927  +** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
         6928  +**   Checkpoint as many frames as possible without waiting for any database 
         6929  +**   readers or writers to finish. Sync the db file if all frames in the log
         6930  +**   are checkpointed. This mode is the same as calling 
         6931  +**   sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
         6932  +**
         6933  +** <dt>SQLITE_CHECKPOINT_FULL<dd>
         6934  +**   This mode blocks (calls the busy-handler callback) until there is no
         6935  +**   database writer and all readers are reading from the most recent database
         6936  +**   snapshot. It then checkpoints all frames in the log file and syncs the
         6937  +**   database file. This call blocks database writers while it is running,
         6938  +**   but not database readers.
         6939  +**
         6940  +** <dt>SQLITE_CHECKPOINT_RESTART<dd>
         6941  +**   This mode works the same way as SQLITE_CHECKPOINT_FULL, except after 
         6942  +**   checkpointing the log file it blocks (calls the busy-handler callback)
         6943  +**   until all readers are reading from the database file only. This ensures 
         6944  +**   that the next client to write to the database file restarts the log file 
         6945  +**   from the beginning. This call blocks database writers while it is running,
         6946  +**   but not database readers.
         6947  +** </dl>
         6948  +**
         6949  +** If pnLog is not NULL, then *pnLog is set to the total number of frames in
         6950  +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to
         6951  +** the total number of checkpointed frames (including any that were already
         6952  +** checkpointed when this function is called). *pnLog and *pnCkpt may be
         6953  +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.
         6954  +** If no values are available because of an error, they are both set to -1
         6955  +** before returning to communicate this to the caller.
         6956  +**
         6957  +** All calls obtain an exclusive "checkpoint" lock on the database file. If
         6958  +** any other process is running a checkpoint operation at the same time, the 
         6959  +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a 
         6960  +** busy-handler configured, it will not be invoked in this case.
         6961  +**
         6962  +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive 
         6963  +** "writer" lock on the database file. If the writer lock cannot be obtained
         6964  +** immediately, and a busy-handler is configured, it is invoked and the writer
         6965  +** lock retried until either the busy-handler returns 0 or the lock is
         6966  +** successfully obtained. The busy-handler is also invoked while waiting for
         6967  +** database readers as described above. If the busy-handler returns 0 before
         6968  +** the writer lock is obtained or while waiting for database readers, the
         6969  +** checkpoint operation proceeds from that point in the same way as 
         6970  +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible 
         6971  +** without blocking any further. SQLITE_BUSY is returned in this case.
         6972  +**
         6973  +** If parameter zDb is NULL or points to a zero length string, then the
         6974  +** specified operation is attempted on all WAL databases. In this case the
         6975  +** values written to output parameters *pnLog and *pnCkpt are undefined. If 
         6976  +** an SQLITE_BUSY error is encountered when processing one or more of the 
         6977  +** attached WAL databases, the operation is still attempted on any remaining 
         6978  +** attached databases and SQLITE_BUSY is returned to the caller. If any other 
         6979  +** error occurs while processing an attached database, processing is abandoned 
         6980  +** and the error code returned to the caller immediately. If no error 
         6981  +** (SQLITE_BUSY or otherwise) is encountered while processing the attached 
         6982  +** databases, SQLITE_OK is returned.
         6983  +**
         6984  +** If database zDb is the name of an attached database that is not in WAL
         6985  +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If
         6986  +** zDb is not NULL (or a zero length string) and is not the name of any
         6987  +** attached database, SQLITE_ERROR is returned to the caller.
         6988  +*/
         6989  +SQLITE_API int sqlite3_wal_checkpoint_v2(
         6990  +  sqlite3 *db,                    /* Database handle */
         6991  +  const char *zDb,                /* Name of attached database (or NULL) */
         6992  +  int eMode,                      /* SQLITE_CHECKPOINT_* value */
         6993  +  int *pnLog,                     /* OUT: Size of WAL log in frames */
         6994  +  int *pnCkpt                     /* OUT: Total number of frames checkpointed */
         6995  +);
         6996  +
         6997  +/*
         6998  +** CAPI3REF: Checkpoint operation parameters
         6999  +**
         7000  +** These constants can be used as the 3rd parameter to
         7001  +** [sqlite3_wal_checkpoint_v2()].  See the [sqlite3_wal_checkpoint_v2()]
         7002  +** documentation for additional information about the meaning and use of
         7003  +** each of these values.
         7004  +*/
         7005  +#define SQLITE_CHECKPOINT_PASSIVE 0
         7006  +#define SQLITE_CHECKPOINT_FULL    1
         7007  +#define SQLITE_CHECKPOINT_RESTART 2
         7008  +
         7009  +/*
         7010  +** CAPI3REF: Virtual Table Interface Configuration
         7011  +**
         7012  +** This function may be called by either the [xConnect] or [xCreate] method
         7013  +** of a [virtual table] implementation to configure
         7014  +** various facets of the virtual table interface.
         7015  +**
         7016  +** If this interface is invoked outside the context of an xConnect or
         7017  +** xCreate virtual table method then the behavior is undefined.
         7018  +**
         7019  +** At present, there is only one option that may be configured using
         7020  +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].)  Further options
         7021  +** may be added in the future.
         7022  +*/
         7023  +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
         7024  +
         7025  +/*
         7026  +** CAPI3REF: Virtual Table Configuration Options
         7027  +**
         7028  +** These macros define the various options to the
         7029  +** [sqlite3_vtab_config()] interface that [virtual table] implementations
         7030  +** can use to customize and optimize their behavior.
         7031  +**
         7032  +** <dl>
         7033  +** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
         7034  +** <dd>Calls of the form
         7035  +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
         7036  +** where X is an integer.  If X is zero, then the [virtual table] whose
         7037  +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not
         7038  +** support constraints.  In this configuration (which is the default) if
         7039  +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire
         7040  +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been
         7041  +** specified as part of the users SQL statement, regardless of the actual
         7042  +** ON CONFLICT mode specified.
         7043  +**
         7044  +** If X is non-zero, then the virtual table implementation guarantees
         7045  +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before
         7046  +** any modifications to internal or persistent data structures have been made.
         7047  +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite 
         7048  +** is able to roll back a statement or database transaction, and abandon
         7049  +** or continue processing the current SQL statement as appropriate. 
         7050  +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
         7051  +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
         7052  +** had been ABORT.
         7053  +**
         7054  +** Virtual table implementations that are required to handle OR REPLACE
         7055  +** must do so within the [xUpdate] method. If a call to the 
         7056  +** [sqlite3_vtab_on_conflict()] function indicates that the current ON 
         7057  +** CONFLICT policy is REPLACE, the virtual table implementation should 
         7058  +** silently replace the appropriate rows within the xUpdate callback and
         7059  +** return SQLITE_OK. Or, if this is not possible, it may return
         7060  +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT 
         7061  +** constraint handling.
         7062  +** </dl>
         7063  +*/
         7064  +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
         7065  +
         7066  +/*
         7067  +** CAPI3REF: Determine The Virtual Table Conflict Policy
         7068  +**
         7069  +** This function may only be called from within a call to the [xUpdate] method
         7070  +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
         7071  +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
         7072  +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode
         7073  +** of the SQL statement that triggered the call to the [xUpdate] method of the
         7074  +** [virtual table].
         7075  +*/
         7076  +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
         7077  +
         7078  +/*
         7079  +** CAPI3REF: Conflict resolution modes
         7080  +**
         7081  +** These constants are returned by [sqlite3_vtab_on_conflict()] to
         7082  +** inform a [virtual table] implementation what the [ON CONFLICT] mode
         7083  +** is for the SQL statement being evaluated.
         7084  +**
         7085  +** Note that the [SQLITE_IGNORE] constant is also used as a potential
         7086  +** return value from the [sqlite3_set_authorizer()] callback and that
         7087  +** [SQLITE_ABORT] is also a [result code].
         7088  +*/
         7089  +#define SQLITE_ROLLBACK 1
         7090  +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
         7091  +#define SQLITE_FAIL     3
         7092  +/* #define SQLITE_ABORT 4  // Also an error code */
         7093  +#define SQLITE_REPLACE  5
         7094  +
         7095  +
  5725   7096   
  5726   7097   /*
  5727   7098   ** Undo the hack that converts floating point types to integer for
  5728   7099   ** builds on processors without floating point support.
  5729   7100   */
  5730   7101   #ifdef SQLITE_OMIT_FLOATING_POINT
  5731   7102   # undef double
................................................................................
  5732   7103   #endif
  5733   7104   
  5734   7105   #ifdef __cplusplus
  5735   7106   }  /* End of the 'extern "C"' block */
  5736   7107   #endif
  5737   7108   #endif
  5738   7109   
         7110  +/*
         7111  +** 2010 August 30
         7112  +**
         7113  +** The author disclaims copyright to this source code.  In place of
         7114  +** a legal notice, here is a blessing:
         7115  +**
         7116  +**    May you do good and not evil.
         7117  +**    May you find forgiveness for yourself and forgive others.
         7118  +**    May you share freely, never taking more than you give.
         7119  +**
         7120  +*************************************************************************
         7121  +*/
         7122  +
         7123  +#ifndef _SQLITE3RTREE_H_
         7124  +#define _SQLITE3RTREE_H_
         7125  +
         7126  +
         7127  +#ifdef __cplusplus
         7128  +extern "C" {
         7129  +#endif
         7130  +
         7131  +typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
         7132  +
         7133  +/*
         7134  +** Register a geometry callback named zGeom that can be used as part of an
         7135  +** R-Tree geometry query as follows:
         7136  +**
         7137  +**   SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...)
         7138  +*/
         7139  +SQLITE_API int sqlite3_rtree_geometry_callback(
         7140  +  sqlite3 *db,
         7141  +  const char *zGeom,
         7142  +#ifdef SQLITE_RTREE_INT_ONLY
         7143  +  int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
         7144  +#else
         7145  +  int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
         7146  +#endif
         7147  +  void *pContext
         7148  +);
         7149  +
         7150  +
         7151  +/*
         7152  +** A pointer to a structure of the following type is passed as the first
         7153  +** argument to callbacks registered using rtree_geometry_callback().
         7154  +*/
         7155  +struct sqlite3_rtree_geometry {
         7156  +  void *pContext;                 /* Copy of pContext passed to s_r_g_c() */
         7157  +  int nParam;                     /* Size of array aParam[] */
         7158  +  double *aParam;                 /* Parameters passed to SQL geom function */
         7159  +  void *pUser;                    /* Callback implementation user data */
         7160  +  void (*xDelUser)(void *);       /* Called by SQLite to clean up pUser */
         7161  +};
         7162  +
         7163  +
         7164  +#ifdef __cplusplus
         7165  +}  /* end of the 'extern "C"' block */
         7166  +#endif
         7167  +
         7168  +#endif  /* ifndef _SQLITE3RTREE_H_ */
         7169  +

Added src/stash.c.

            1  +/*
            2  +** Copyright (c) 2010 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@sqlite.org
           14  +**
           15  +*******************************************************************************
           16  +**
           17  +** This file contains code used to implement the "stash" command.
           18  +*/
           19  +#include "config.h"
           20  +#include "stash.h"
           21  +#include <assert.h>
           22  +
           23  +
           24  +/*
           25  +** SQL code to implement the tables needed by the stash.
           26  +*/
           27  +static const char zStashInit[] =
           28  +@ CREATE TABLE IF NOT EXISTS %s.stash(
           29  +@   stashid INTEGER PRIMARY KEY,     -- Unique stash identifier
           30  +@   vid INTEGER,                     -- The baseline check-out for this stash
           31  +@   comment TEXT,                    -- Comment for this stash.  Or NULL
           32  +@   ctime TIMESTAMP                  -- When the stash was created
           33  +@ );
           34  +@ CREATE TABLE IF NOT EXISTS %s.stashfile(
           35  +@   stashid INTEGER REFERENCES stash,  -- Stash that contains this file
           36  +@   rid INTEGER,                       -- Baseline content in BLOB table or 0.
           37  +@   isAdded BOOLEAN,                   -- True if this is an added file
           38  +@   isRemoved BOOLEAN,                 -- True if this file is deleted
           39  +@   isExec BOOLEAN,                    -- True if file is executable
           40  +@   isLink BOOLEAN,                    -- True if file is a symlink
           41  +@   origname TEXT,                     -- Original filename
           42  +@   newname TEXT,                      -- New name for file at next check-in
           43  +@   delta BLOB,                        -- Delta from baseline. Content if rid=0
           44  +@   PRIMARY KEY(origname, stashid)
           45  +@ );
           46  +@ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
           47  +;
           48  +
           49  +/*
           50  +** Add zFName to the stash given by stashid.  zFName might be the name of a
           51  +** file or a directory.  If a directory, add all changed files contained
           52  +** within that directory.
           53  +*/
           54  +static void stash_add_file_or_dir(int stashid, int vid, const char *zFName){
           55  +  char *zFile;          /* Normalized filename */
           56  +  char *zTreename;      /* Name of the file in the tree */
           57  +  Blob fname;           /* Filename relative to root */
           58  +  Blob sql;             /* Query statement text */
           59  +  Stmt q;               /* Query against the vfile table */
           60  +  Stmt ins;             /* Insert statement */
           61  +
           62  +  zFile = mprintf("%/", zFName);
           63  +  file_tree_name(zFile, &fname, 1);
           64  +  zTreename = blob_str(&fname);
           65  +  blob_zero(&sql);
           66  +  blob_appendf(&sql,
           67  +    "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)"
           68  +    "  FROM vfile"
           69  +    " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
           70  +    vid
           71  +  );
           72  +  if( fossil_strcmp(zTreename,".")!=0 ){
           73  +    blob_appendf(&sql,
           74  +      "   AND (pathname GLOB '%q/*' OR origname GLOB '%q/*'"
           75  +            "  OR pathname=%Q OR origname=%Q)",
           76  +      zTreename, zTreename, zTreename, zTreename
           77  +    );
           78  +  }
           79  +  db_prepare(&q, blob_str(&sql));
           80  +  blob_reset(&sql);
           81  +  db_prepare(&ins,
           82  +     "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
           83  +                           "origname, newname, delta)"
           84  +     "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
           85  +     stashid
           86  +  );
           87  +  while( db_step(&q)==SQLITE_ROW ){
           88  +    int deleted = db_column_int(&q, 0);
           89  +    int rid = db_column_int(&q, 3);
           90  +    const char *zName = db_column_text(&q, 4);
           91  +    const char *zOrig = db_column_text(&q, 5);
           92  +    char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
           93  +    Blob content;
           94  +    int isNewLink = file_wd_islink(zPath);
           95  +
           96  +    db_bind_int(&ins, ":rid", rid);
           97  +    db_bind_int(&ins, ":isadd", rid==0);
           98  +    db_bind_int(&ins, ":isrm", deleted);
           99  +    db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
          100  +    db_bind_int(&ins, ":islink", db_column_int(&q, 2));
          101  +    db_bind_text(&ins, ":orig", zOrig);
          102  +    db_bind_text(&ins, ":new", zName);
          103  +
          104  +    if( rid==0 ){
          105  +      /* A new file */
          106  +      if( isNewLink ){
          107  +        blob_read_link(&content, zPath);
          108  +      }else{
          109  +        blob_read_from_file(&content, zPath);
          110  +      }
          111  +      db_bind_blob(&ins, ":content", &content);
          112  +    }else if( deleted ){
          113  +      blob_zero(&content);
          114  +      db_bind_null(&ins, ":content");
          115  +    }else{
          116  +      /* A modified file */
          117  +      Blob orig;
          118  +      Blob disk;
          119  +
          120  +      if( isNewLink ){
          121  +        blob_read_link(&disk, zPath);
          122  +      }else{
          123  +        blob_read_from_file(&disk, zPath);
          124  +      }
          125  +      content_get(rid, &orig);
          126  +      blob_delta_create(&orig, &disk, &content);
          127  +      blob_reset(&orig);
          128  +      blob_reset(&disk);
          129  +      db_bind_blob(&ins, ":content", &content);
          130  +    }
          131  +    db_bind_int(&ins, ":islink", isNewLink);
          132  +    db_step(&ins);
          133  +    db_reset(&ins);
          134  +    fossil_free(zPath);
          135  +    blob_reset(&content);
          136  +  }
          137  +  db_finalize(&ins);
          138  +  db_finalize(&q);
          139  +  fossil_free(zFile);
          140  +  blob_reset(&fname);
          141  +}
          142  +
          143  +/*
          144  +** Create a new stash based on the uncommitted changes currently in
          145  +** the working directory.
          146  +**
          147  +** If the "-m" or "--comment" command-line option is present, gather
          148  +** its argument as the stash comment.
          149  +**
          150  +** If files are named on the command-line, then only stash the named
          151  +** files.
          152  +*/
          153  +static int stash_create(void){
          154  +  const char *zComment;              /* Comment to add to the stash */
          155  +  int stashid;                       /* ID of the new stash */
          156  +  int vid;                           /* Current checkout */
          157  +
          158  +  zComment = find_option("comment", "m", 1);
          159  +  verify_all_options();
          160  +  if( zComment==0 ){
          161  +    Blob prompt;                       /* Prompt for stash comment */
          162  +    Blob comment;                      /* User comment reply */
          163  +#ifdef _WIN32
          164  +    int bomSize;
          165  +    const unsigned char *bom = get_utf8_bom(&bomSize);
          166  +    blob_init(&prompt, (const char *) bom, bomSize);
          167  +#else
          168  +    blob_zero(&prompt);
          169  +#endif
          170  +    blob_append(&prompt,
          171  +       "\n"
          172  +       "# Enter a description of what is being stashed.  Lines beginning\n"
          173  +       "# with \"#\" are ignored.  Stash comments are plain text except.\n"
          174  +       "# newlines are not preserved.\n",
          175  +       -1);
          176  +    prompt_for_user_comment(&comment, &prompt);
          177  +    blob_reset(&prompt);
          178  +    zComment = blob_str(&comment);
          179  +  }
          180  +  stashid = db_lget_int("stash-next", 1);
          181  +  db_lset_int("stash-next", stashid+1);
          182  +  vid = db_lget_int("checkout", 0);
          183  +  vfile_check_signature(vid, 0);
          184  +  db_multi_exec(
          185  +    "INSERT INTO stash(stashid,vid,comment,ctime)"
          186  +    "VALUES(%d,%d,%Q,julianday('now'))",
          187  +    stashid, vid, zComment
          188  +  );
          189  +  if( g.argc>3 ){
          190  +    int i;
          191  +    for(i=3; i<g.argc; i++){
          192  +      stash_add_file_or_dir(stashid, vid, g.argv[i]);
          193  +    }
          194  +  }else{
          195  +    stash_add_file_or_dir(stashid, vid, g.zLocalRoot);
          196  +  }
          197  +  return stashid;
          198  +}
          199  +
          200  +/*
          201  +** Apply a stash to the current check-out.
          202  +*/
          203  +static void stash_apply(int stashid, int nConflict){
          204  +  Stmt q;
          205  +  db_prepare(&q,
          206  +     "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
          207  +     "  FROM stashfile WHERE stashid=%d",
          208  +     stashid
          209  +  );
          210  +  while( db_step(&q)==SQLITE_ROW ){
          211  +    int rid = db_column_int(&q, 0);
          212  +    int isRemoved = db_column_int(&q, 1);
          213  +    int isExec = db_column_int(&q, 2);
          214  +    int isLink = db_column_int(&q, 3);
          215  +    const char *zOrig = db_column_text(&q, 4);
          216  +    const char *zNew = db_column_text(&q, 5);
          217  +    char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
          218  +    char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
          219  +    Blob delta;
          220  +    undo_save(zNew);
          221  +    blob_zero(&delta);
          222  +    if( rid==0 ){
          223  +      db_ephemeral_blob(&q, 6, &delta);
          224  +      blob_write_to_file(&delta, zNPath);
          225  +      file_wd_setexe(zNPath, isExec);
          226  +      fossil_print("ADD %s\n", zNew);
          227  +    }else if( isRemoved ){
          228  +      fossil_print("DELETE %s\n", zOrig);
          229  +      file_delete(zOPath);
          230  +    }else{
          231  +      Blob a, b, out, disk;
          232  +      int isNewLink = file_wd_islink(zOPath);
          233  +      db_ephemeral_blob(&q, 6, &delta);
          234  +      if( isNewLink ){
          235  +        blob_read_link(&disk, zOPath);
          236  +      }else{
          237  +        blob_read_from_file(&disk, zOPath);
          238  +      }
          239  +      content_get(rid, &a);
          240  +      blob_delta_apply(&a, &delta, &b);
          241  +      if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){
          242  +        if( isLink || isNewLink ){
          243  +          file_delete(zNPath);
          244  +        }
          245  +        if( isLink ){
          246  +          symlink_create(blob_str(&b), zNPath);
          247  +        }else{
          248  +          blob_write_to_file(&b, zNPath);
          249  +        }
          250  +        file_wd_setexe(zNPath, isExec);
          251  +        fossil_print("UPDATE %s\n", zNew);
          252  +      }else{
          253  +        int rc;
          254  +        if( isLink || isNewLink ){
          255  +          rc = -1;
          256  +          blob_zero(&b); /* because we reset it later */
          257  +          fossil_print("***** Cannot merge symlink %s\n", zNew);
          258  +        }else{
          259  +          rc = merge_3way(&a, zOPath, &b, &out, 0);
          260  +          blob_write_to_file(&out, zNPath);
          261  +          blob_reset(&out);
          262  +          file_wd_setexe(zNPath, isExec);
          263  +        }
          264  +        if( rc ){
          265  +          fossil_print("CONFLICT %s\n", zNew);
          266  +          nConflict++;
          267  +        }else{
          268  +          fossil_print("MERGE %s\n", zNew);
          269  +        }
          270  +      }
          271  +      blob_reset(&a);
          272  +      blob_reset(&b);
          273  +      blob_reset(&disk);
          274  +    }
          275  +    blob_reset(&delta);
          276  +    if( fossil_strcmp(zOrig,zNew)!=0 ){
          277  +      undo_save(zOrig);
          278  +      file_delete(zOPath);
          279  +    }
          280  +  }
          281  +  db_finalize(&q);
          282  +  if( nConflict ){
          283  +    fossil_print(
          284  +      "WARNING: %d merge conflicts - see messages above for details.\n",
          285  +      nConflict);
          286  +  }
          287  +}
          288  +
          289  +/*
          290  +** Show the diffs associate with a single stash.
          291  +*/
          292  +static void stash_diff(
          293  +  int stashid,             /* The stash entry to diff */
          294  +  const char *zDiffCmd,    /* Command used for diffing */
          295  +  const char *zBinGlob,    /* GLOB pattern to determine binary files */
          296  +  int fBaseline,           /* Diff against original baseline check-in if true */
          297  +  int fIncludeBinary,      /* Do diffs against binary files */
          298  +  u64 diffFlags            /* Other diff flags */
          299  +){
          300  +  Stmt q;
          301  +  Blob empty;
          302  +  blob_zero(&empty);
          303  +  db_prepare(&q,
          304  +     "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
          305  +     "  FROM stashfile WHERE stashid=%d",
          306  +     stashid
          307  +  );
          308  +  while( db_step(&q)==SQLITE_ROW ){
          309  +    int rid = db_column_int(&q, 0);
          310  +    int isRemoved = db_column_int(&q, 1);
          311  +    int isLink = db_column_int(&q, 3);
          312  +    int isBin1, isBin2;
          313  +    const char *zOrig = db_column_text(&q, 4);
          314  +    const char *zNew = db_column_text(&q, 5);
          315  +    char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
          316  +    Blob delta, a, b, disk;
          317  +    if( rid==0 ){
          318  +      db_ephemeral_blob(&q, 6, &a);
          319  +      fossil_print("ADDED %s\n", zNew);
          320  +      diff_print_index(zNew, diffFlags);
          321  +      isBin1 = 0;
          322  +      isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
          323  +      diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
          324  +                    zBinGlob, fIncludeBinary, diffFlags);
          325  +    }else if( isRemoved ){
          326  +      fossil_print("DELETE %s\n", zOrig);
          327  +      if( fBaseline==0 ){
          328  +        if( file_wd_islink(zOPath) ){
          329  +          blob_read_link(&a, zOPath);
          330  +        }else{
          331  +          blob_read_from_file(&a, zOPath);
          332  +        }
          333  +      }else{
          334  +        content_get(rid, &a);
          335  +      }
          336  +      diff_print_index(zNew, diffFlags);
          337  +      isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
          338  +      isBin2 = 0;
          339  +      diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
          340  +                    zBinGlob, fIncludeBinary, diffFlags);
          341  +    }else{
          342  +      int isOrigLink = file_wd_islink(zOPath);
          343  +      db_ephemeral_blob(&q, 6, &delta);
          344  +      if( fBaseline==0 ){
          345  +        if( isOrigLink ){
          346  +          blob_read_link(&disk, zOPath);
          347  +        }else{
          348  +          blob_read_from_file(&disk, zOPath);
          349  +        }
          350  +      }
          351  +      fossil_print("CHANGED %s\n", zNew);
          352  +      if( !isOrigLink != !isLink ){
          353  +        diff_print_index(zNew, diffFlags);
          354  +        diff_print_filenames(zOrig, zNew, diffFlags);
          355  +        printf(DIFF_CANNOT_COMPUTE_SYMLINK);
          356  +      }else{
          357  +        Blob *pBase = fBaseline ? &a : &disk;
          358  +        content_get(rid, &a);
          359  +        blob_delta_apply(&a, &delta, &b);
          360  +        isBin1 = fIncludeBinary ? 0 : looks_like_binary(pBase);
          361  +        isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
          362  +        diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew,
          363  +                      zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
          364  +        blob_reset(&a);
          365  +        blob_reset(&b);
          366  +      }
          367  +      if( !fBaseline ) blob_reset(&disk);
          368  +    }
          369  +    blob_reset(&delta);
          370  + }
          371  +  db_finalize(&q);
          372  +}
          373  +
          374  +/*
          375  +** Drop the indicated stash
          376  +*/
          377  +static void stash_drop(int stashid){
          378  +  db_multi_exec(
          379  +    "DELETE FROM stash WHERE stashid=%d;"
          380  +    "DELETE FROM stashfile WHERE stashid=%d;",
          381  +    stashid, stashid
          382  +  );
          383  +}
          384  +
          385  +/*
          386  +** If zStashId is non-NULL then interpret is as a stash number and
          387  +** return that number.  Or throw a fatal error if it is not a valid
          388  +** stash number.  If it is NULL, return the most recent stash or
          389  +** throw an error if the stash is empty.
          390  +*/
          391  +static int stash_get_id(const char *zStashId){
          392  +  int stashid = 0;
          393  +  if( zStashId==0 ){
          394  +    stashid = db_int(0, "SELECT max(stashid) FROM stash");
          395  +    if( stashid==0 ) fossil_fatal("empty stash");
          396  +  }else{
          397  +    stashid = atoi(zStashId);
          398  +    if( !db_exists("SELECT 1 FROM stash WHERE stashid=%d", stashid) ){
          399  +      fossil_fatal("no such stash: %d\n", stashid);
          400  +    }
          401  +  }
          402  +  return stashid;
          403  +}
          404  +
          405  +/*
          406  +** COMMAND: stash
          407  +**
          408  +** Usage: %fossil stash SUBCOMMAND ARGS...
          409  +**
          410  +**  fossil stash
          411  +**  fossil stash save ?-m COMMENT? ?FILES...?
          412  +**  fossil stash snapshot ?-m COMMENT? ?FILES...?
          413  +**
          414  +**     Save the current changes in the working tree as a new stash.
          415  +**     Then revert the changes back to the last check-in.  If FILES
          416  +**     are listed, then only stash and revert the named files.  The
          417  +**     "save" verb can be omitted if and only if there are no other
          418  +**     arguments.  The "snapshot" verb works the same as "save" but
          419  +**     omits the revert, keeping the check-out unchanged.
          420  +**
          421  +**  fossil stash list ?--detail?
          422  +**  fossil stash ls ?-l?
          423  +**
          424  +**     List all changes sets currently stashed.  Show information about
          425  +**     individual files in each changeset if --detail or -l is used.
          426  +**
          427  +**  fossil stash show ?STASHID? ?DIFF-FLAGS?
          428  +**
          429  +**     Show the content of a stash
          430  +**
          431  +**  fossil stash pop
          432  +**  fossil stash apply ?STASHID?
          433  +**
          434  +**     Apply STASHID or the most recently create stash to the current
          435  +**     working check-out.  The "pop" command deletes that changeset from
          436  +**     the stash after applying it but the "apply" command retains the
          437  +**     changeset.
          438  +**
          439  +**  fossil stash goto ?STASHID?
          440  +**
          441  +**     Update to the baseline checkout for STASHID then apply the
          442  +**     changes of STASHID.  Keep STASHID so that it can be reused
          443  +**     This command is undoable.
          444  +**
          445  +**  fossil stash drop ?STASHID? ?--all?
          446  +**  fossil stash rm   ?STASHID? ?--all?
          447  +**
          448  +**     Forget everything about STASHID.  Forget the whole stash if the
          449  +**     --all flag is used.  Individual drops are undoable but --all is not.
          450  +**
          451  +**  fossil stash diff ?STASHID?
          452  +**  fossil stash gdiff ?STASHID?
          453  +**
          454  +**     Show diffs of the current working directory and what that
          455  +**     directory would be if STASHID were applied.
          456  +**
          457  +** SUMMARY:
          458  +**  fossil stash
          459  +**  fossil stash save ?-m COMMENT? ?FILES...?
          460  +**  fossil stash snapshot ?-m COMMENT? ?FILES...?
          461  +**  fossil stash list|ls  ?-l? ?--detail?
          462  +**  fossil stash show ?STASHID? ?DIFF-OPTIONS?
          463  +**  fossil stash pop
          464  +**  fossil stash apply ?STASHID?
          465  +**  fossil stash goto ?STASHID?
          466  +**  fossil stash rm|drop ?STASHID? ?--all?
          467  +**  fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
          468  +*/
          469  +void stash_cmd(void){
          470  +  const char *zDb;
          471  +  const char *zCmd;
          472  +  int nCmd;
          473  +  int stashid = 0;
          474  +
          475  +  undo_capture_command_line();
          476  +  db_must_be_within_tree();
          477  +  db_open_config(0);
          478  +  db_begin_transaction();
          479  +  zDb = db_name("localdb");
          480  +  db_multi_exec(zStashInit, zDb, zDb);
          481  +  if( g.argc<=2 ){
          482  +    zCmd = "save";
          483  +  }else{
          484  +    zCmd = g.argv[2];
          485  +  }
          486  +  nCmd = strlen(zCmd);
          487  +  if( memcmp(zCmd, "save", nCmd)==0 ){
          488  +    stashid = stash_create();
          489  +    undo_disable();
          490  +    if( g.argc>=2 ){
          491  +      int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",
          492  +                         stashid);
          493  +      char **newArgv = fossil_malloc( sizeof(char*)*(nFile+2) );
          494  +      int i = 2;
          495  +      Stmt q;
          496  +      db_prepare(&q,"SELECT origname FROM stashfile WHERE stashid=%d", stashid);
          497  +      while( db_step(&q)==SQLITE_ROW ){
          498  +        newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0));
          499  +      }
          500  +      db_finalize(&q);
          501  +      newArgv[0] = g.argv[0];
          502  +      g.argv = newArgv;
          503  +      g.argc = nFile+2;
          504  +      if( nFile==0 ) return;
          505  +    }
          506  +    g.argv[1] = "revert";
          507  +    revert_cmd();
          508  +  }else
          509  +  if( memcmp(zCmd, "snapshot", nCmd)==0 ){
          510  +    stash_create();
          511  +  }else
          512  +  if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
          513  +    Stmt q, q2;
          514  +    int n = 0;
          515  +    int fDetail = find_option("detail","l",0)!=0;
          516  +    verify_all_options();
          517  +    db_prepare(&q,
          518  +       "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
          519  +       "       comment, datetime(ctime) FROM stash"
          520  +       " ORDER BY ctime DESC"
          521  +    );
          522  +    if( fDetail ){
          523  +      db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
          524  +                      "  FROM stashfile WHERE stashid=$id");
          525  +    }
          526  +    while( db_step(&q)==SQLITE_ROW ){
          527  +      int stashid = db_column_int(&q, 0);
          528  +      const char *zCom;
          529  +      n++;
          530  +      fossil_print("%5d: [%.14s] on %s\n",
          531  +        stashid,
          532  +        db_column_text(&q, 1),
          533  +        db_column_text(&q, 3)
          534  +      );
          535  +      zCom = db_column_text(&q, 2);
          536  +      if( zCom && zCom[0] ){
          537  +        fossil_print("       ");
          538  +        comment_print(zCom, 7, 79);
          539  +      }
          540  +      if( fDetail ){
          541  +        db_bind_int(&q2, "$id", stashid);
          542  +        while( db_step(&q2)==SQLITE_ROW ){
          543  +          int isAdded = db_column_int(&q2, 0);
          544  +          int isRemoved = db_column_int(&q2, 1);
          545  +          const char *zOrig = db_column_text(&q2, 2);
          546  +          const char *zNew = db_column_text(&q2, 3);
          547  +          if( isAdded ){
          548  +             fossil_print("          ADD %s\n", zNew);
          549  +          }else if( isRemoved ){
          550  +             fossil_print("          REMOVE %s\n", zOrig);
          551  +          }else if( fossil_strcmp(zOrig,zNew)!=0 ){
          552  +             fossil_print("          RENAME %s -> %s\n", zOrig, zNew);
          553  +          }else{
          554  +             fossil_print("          EDIT %s\n", zOrig);
          555  +          }
          556  +        }
          557  +        db_reset(&q2);
          558  +      }
          559  +    }
          560  +    db_finalize(&q);
          561  +    if( fDetail ) db_finalize(&q2);
          562  +    if( n==0 ) fossil_print("empty stash\n");
          563  +  }else
          564  +  if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
          565  +    int allFlag = find_option("all", 0, 0)!=0;
          566  +    if( allFlag ){
          567  +      Blob ans;
          568  +      char cReply;
          569  +      blob_zero(&ans);
          570  +      prompt_user("This action is not undoable.  Continue (y/N)? ", &ans);
          571  +      cReply = blob_str(&ans)[0];
          572  +      if( cReply=='y' || cReply=='Y' ){
          573  +        db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;");
          574  +      }
          575  +    }else if( g.argc>=4 ){
          576  +      int i;
          577  +      undo_begin();
          578  +      for(i=3; i<g.argc; i++){
          579  +        stashid = stash_get_id(g.argv[i]);
          580  +        undo_save_stash(stashid);
          581  +        stash_drop(stashid);
          582  +      }
          583  +      undo_finish();
          584  +    }else{
          585  +      undo_begin();
          586  +      undo_save_stash(0);
          587  +      stash_drop(stashid);
          588  +      undo_finish();
          589  +    }
          590  +  }else
          591  +  if( memcmp(zCmd, "pop", nCmd)==0 ){
          592  +    if( g.argc>3 ) usage("pop");
          593  +    stashid = stash_get_id(0);
          594  +    undo_begin();
          595  +    stash_apply(stashid, 0);
          596  +    undo_save_stash(stashid);
          597  +    undo_finish();
          598  +    stash_drop(stashid);
          599  +  }else
          600  +  if( memcmp(zCmd, "apply", nCmd)==0 ){
          601  +    if( g.argc>4 ) usage("apply STASHID");
          602  +    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
          603  +    undo_begin();
          604  +    stash_apply(stashid, 0);
          605  +    undo_finish();
          606  +  }else
          607  +  if( memcmp(zCmd, "goto", nCmd)==0 ){
          608  +    int nConflict;
          609  +    int vid;
          610  +
          611  +    if( g.argc>4 ) usage("apply STASHID");
          612  +    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
          613  +    undo_begin();
          614  +    vid = db_int(0, "SELECT vid FROM stash WHERE stashid=%d", stashid);
          615  +    nConflict = update_to(vid);
          616  +    stash_apply(stashid, nConflict);
          617  +    db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
          618  +                  "(SELECT origname FROM stashfile WHERE stashid=%d)",
          619  +                  stashid);
          620  +    undo_finish();
          621  +  }else
          622  +  if( memcmp(zCmd, "diff", nCmd)==0
          623  +   || memcmp(zCmd, "gdiff", nCmd)==0
          624  +   || memcmp(zCmd, "show", nCmd)==0
          625  +  ){
          626  +    const char *zDiffCmd = 0;
          627  +    const char *zBinGlob = 0;
          628  +    int fIncludeBinary = 0;
          629  +    u64 diffFlags;
          630  +
          631  +    if( find_option("tk",0,0)!=0 ){
          632  +      db_close(0);
          633  +      diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3);
          634  +      return;
          635  +    }
          636  +    if( find_option("internal","i",0)==0 ){
          637  +      zDiffCmd = diff_command_external(0);
          638  +    }
          639  +    diffFlags = diff_options();
          640  +    if( g.argc>4 ) usage(mprintf("%s STASHID", zCmd));
          641  +    if( zDiffCmd ){
          642  +      zBinGlob = diff_get_binary_glob();
          643  +      fIncludeBinary = diff_include_binary_files();
          644  +    }
          645  +    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
          646  +    stash_diff(stashid, zDiffCmd, zBinGlob, zCmd[0]=='s', fIncludeBinary,
          647  +               diffFlags);
          648  +  }else
          649  +  if( memcmp(zCmd, "help", nCmd)==0 ){
          650  +    g.argv[1] = "help";
          651  +    g.argv[2] = "stash";
          652  +    g.argc = 3;
          653  +    help_cmd();
          654  +  }else
          655  +  {
          656  +    usage("SUBCOMMAND ARGS...");
          657  +  }
          658  +  db_end_transaction(0);
          659  +}

Changes to src/stat.c.

    17     17   **
    18     18   ** This file contains code to implement the stat web page
    19     19   **
    20     20   */
    21     21   #include <string.h>
    22     22   #include "config.h"
    23     23   #include "stat.h"
           24  +
           25  +/*
           26  +** For a sufficiently large integer, provide an alternative
           27  +** representation as MB or GB or TB.
           28  +*/
           29  +static void bigSizeName(int nOut, char *zOut, sqlite3_int64 v){
           30  +  if( v<100000 ){
           31  +    sqlite3_snprintf(nOut, zOut, "%lld bytes", v);
           32  +  }else if( v<1000000000 ){
           33  +    sqlite3_snprintf(nOut, zOut, "%lld bytes (%.1fMB)",
           34  +                    v, (double)v/1000000.0);
           35  +  }else{
           36  +    sqlite3_snprintf(nOut, zOut, "%lld bytes (%.1fGB)",
           37  +                    v, (double)v/1000000000.0);
           38  +  }
           39  +}
    24     40   
    25     41   /*
    26     42   ** WEBPAGE: stat
    27     43   **
    28     44   ** Show statistics and global information about the repository.
    29     45   */
    30     46   void stat_page(void){
    31         -  i64 t;
    32         -  int n, m, fsize;
           47  +  i64 t, fsize;
           48  +  int n, m;
           49  +  int szMax, szAvg;
           50  +  const char *zDb;
           51  +  int brief;
    33     52     char zBuf[100];
           53  +
    34     54     login_check_credentials();
    35         -  if( !g.okRead ){ login_needed(); return; }
           55  +  if( !g.perm.Read ){ login_needed(); return; }
           56  +  brief = P("brief")!=0;
    36     57     style_header("Repository Statistics");
    37         -  @ <p><table class="label-value">
           58  +  if( g.perm.Admin ){
           59  +    style_submenu_element("URLs", "URLs and Checkouts", "urllist");
           60  +  }
           61  +  @ <table class="label-value">
    38     62     @ <tr><th>Repository&nbsp;Size:</th><td>
    39     63     fsize = file_size(g.zRepositoryName);
    40         -  @ %d(fsize) bytes
    41         -  @ </td></tr>
    42         -  @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
    43         -  n = db_int(0, "SELECT count(*) FROM blob");
    44         -  m = db_int(0, "SELECT count(*) FROM delta");
    45         -  @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
           64  +  bigSizeName(sizeof(zBuf), zBuf, fsize);
           65  +  @ %s(zBuf)
    46     66     @ </td></tr>
    47         -  if( n>0 ){
    48         -    int a, b;
    49         -    @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
    50         -    t = db_int64(0, "SELECT total(size) FROM blob WHERE size>0");
    51         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
    52         -    @ %d((int)(((double)t)/(double)n)) bytes average, %s(zBuf) bytes total
           67  +  if( !brief ){
           68  +    @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
           69  +    n = db_int(0, "SELECT count(*) FROM blob");
           70  +    m = db_int(0, "SELECT count(*) FROM delta");
           71  +    @ %d(n) (%d(n-m) fulltext and %d(m) deltas)
    53     72       @ </td></tr>
    54         -    @ <tr><th>Compression&nbsp;Ratio:</th><td>
    55         -    if( t/fsize < 5 ){
    56         -      b = 10;
    57         -      fsize /= 10;
    58         -    }else{
    59         -      b = 1;
           73  +    if( n>0 ){
           74  +      int a, b;
           75  +      Stmt q;
           76  +      @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
           77  +      db_prepare(&q, "SELECT total(size), avg(size), max(size)"
           78  +                     " FROM blob WHERE size>0");
           79  +      db_step(&q);
           80  +      t = db_column_int64(&q, 0);
           81  +      szAvg = db_column_int(&q, 1);
           82  +      szMax = db_column_int(&q, 2);
           83  +      db_finalize(&q);
           84  +      bigSizeName(sizeof(zBuf), zBuf, t);
           85  +      @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) total
           86  +      @ </td></tr>
           87  +      @ <tr><th>Compression&nbsp;Ratio:</th><td>
           88  +      if( t/fsize < 5 ){
           89  +        b = 10;
           90  +        fsize /= 10;
           91  +      }else{
           92  +        b = 1;
           93  +      }
           94  +      a = t/fsize;
           95  +      @ %d(a):%d(b)
           96  +      @ </td></tr>
    60     97       }
    61         -    a = t/fsize;
    62         -    @ %d(a):%d(b)
           98  +    @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
           99  +    n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
          100  +    @ %d(n)
          101  +    @ </td></tr>
          102  +    @ <tr><th>Number&nbsp;Of&nbsp;Files:</th><td>
          103  +    n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
          104  +    @ %d(n)
          105  +    @ </td></tr>
          106  +    @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
          107  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
          108  +                  " WHERE +tagname GLOB 'wiki-*'");
          109  +    @ %d(n)
          110  +    @ </td></tr>
          111  +    @ <tr><th>Number&nbsp;Of&nbsp;Tickets:</th><td>
          112  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
          113  +                  " WHERE +tagname GLOB 'tkt-*'");
          114  +    @ %d(n)
    63    115       @ </td></tr>
    64    116     }
    65         -  @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
    66         -  n = db_int(0, "SELECT count(distinct mid) FROM mlink");
    67         -  @ %d(n)
          117  +  @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
          118  +  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
          119  +                " + 0.99");
          120  +  @ %d(n) days or approximately %.2f(n/365.2425) years.
    68    121     @ </td></tr>
    69         -  @ <tr><th>Number&nbsp;Of&nbsp;Files:</th><td>
    70         -  n = db_int(0, "SELECT count(*) FROM filename");
    71         -  @ %d(n)
          122  +  @ <tr><th>Project&nbsp;ID:</th><td>%h(db_get("project-code",""))</td></tr>
          123  +  @ <tr><th>Fossil&nbsp;Version:</th><td>
          124  +  @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
          125  +  @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)]
    72    126     @ </td></tr>
    73         -  @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
    74         -  n = db_int(0, "SELECT count(*) FROM tag WHERE +tagname GLOB 'wiki-*'");
    75         -  @ %d(n)
    76         -  @ </td></tr>
    77         -  @ <tr><th>Number&nbsp;Of&nbsp;Tickets:</th><td>
    78         -  n = db_int(0, "SELECT count(*) FROM tag WHERE +tagname GLOB 'tkt-*'");
    79         -  @ %d(n)
          127  +  @ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(SQLITE_SOURCE_ID)
          128  +  @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr>
          129  +  @ <tr><th>Database&nbsp;Stats:</th><td>
          130  +  zDb = db_name("repository");
          131  +  @ %d(db_int(0, "PRAGMA %s.page_count", zDb)) pages,
          132  +  @ %d(db_int(0, "PRAGMA %s.page_size", zDb)) bytes/page,
          133  +  @ %d(db_int(0, "PRAGMA %s.freelist_count", zDb)) free pages,
          134  +  @ %s(db_text(0, "PRAGMA %s.encoding", zDb)),
          135  +  @ %s(db_text(0, "PRAGMA %s.journal_mode", zDb)) mode
    80    136     @ </td></tr>
    81         -  @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
    82         -  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event) + 0.99");
    83         -  @ %d(n) days
    84         -  @ </td></tr>
    85         -  @ <tr><th>Project&nbsp;ID:</th><td>
    86         -  @ %h(db_get("project-code",""))
    87         -  @ </td></tr>
    88         -  @ <tr><th>Server&nbsp;ID:</th><td>
    89         -  @ %h(db_get("server-code",""))
    90         -  @ </td></tr>
    91         -  @ </table></p>
          137  +
          138  +  @ </table>
          139  +  style_footer();
          140  +}
          141  +
          142  +/*
          143  +** COMMAND: dbstat*
          144  +**
          145  +** Usage: %fossil dbstat ?-brief | -b?
          146  +**
          147  +** Shows statistics and global information about the repository.
          148  +**
          149  +** The (-brief|-b) option removes any "long-running" statistics, namely
          150  +** those whose calculations are known to slow down as the repository
          151  +** grows.
          152  +**
          153  +*/
          154  +void dbstat_cmd(void){
          155  +  i64 t, fsize;
          156  +  int n, m;
          157  +  int szMax, szAvg;
          158  +  const char *zDb;
          159  +  int brief;
          160  +  char zBuf[100];
          161  +  const int colWidth = -19 /* printf alignment/width for left column */;
          162  +  brief = find_option("brief", "b",0)!=0;
          163  +  db_find_and_open_repository(0,0);
          164  +  fsize = file_size(g.zRepositoryName);
          165  +  bigSizeName(sizeof(zBuf), zBuf, fsize);
          166  +  fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf );
          167  +  if( !brief ){
          168  +    n = db_int(0, "SELECT count(*) FROM blob");
          169  +    m = db_int(0, "SELECT count(*) FROM delta");
          170  +    fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n",
          171  +                 colWidth, "artifact-count:",
          172  +                 n, n-m, m);
          173  +    if( n>0 ){
          174  +      int a, b;
          175  +      Stmt q;
          176  +      db_prepare(&q, "SELECT total(size), avg(size), max(size)"
          177  +                     " FROM blob WHERE size>0");
          178  +      db_step(&q);
          179  +      t = db_column_int64(&q, 0);
          180  +      szAvg = db_column_int(&q, 1);
          181  +      szMax = db_column_int(&q, 2);
          182  +      db_finalize(&q);
          183  +      bigSizeName(sizeof(zBuf), zBuf, t);
          184  +      fossil_print( "%*s%d average, "
          185  +                    "%d max, %s total\n",
          186  +                    colWidth, "artifact-sizes:",
          187  +                    szAvg, szMax, zBuf);
          188  +      if( t/fsize < 5 ){
          189  +        b = 10;
          190  +        fsize /= 10;
          191  +      }else{
          192  +        b = 1;
          193  +      }
          194  +      a = t/fsize;
          195  +      fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
          196  +    }
          197  +    n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
          198  +    fossil_print("%*s%d\n", colWidth, "checkins:", n);
          199  +    n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
          200  +    fossil_print("%*s%d across all branches\n", colWidth, "files:", n);
          201  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
          202  +                  " WHERE tagname GLOB 'wiki-*'");
          203  +    m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'");
          204  +    fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m);
          205  +    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
          206  +                  " WHERE tagname GLOB 'tkt-*'");
          207  +    m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'");
          208  +    fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m);
          209  +    n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'");
          210  +    fossil_print("%*s%d\n", colWidth, "events:", n);
          211  +    n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'");
          212  +    fossil_print("%*s%d\n", colWidth, "tagchanges:", n);
          213  +  }
          214  +  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
          215  +                " + 0.99");
          216  +  fossil_print("%*s%d days or approximately %.2f years.\n",
          217  +               colWidth, "project-age:", n, n/365.2425);
          218  +  fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code",""));
          219  +  fossil_print("%*s%s %s [%s] (%s)\n",
          220  +               colWidth, "fossil-version:",
          221  +               MANIFEST_DATE, MANIFEST_VERSION, RELEASE_VERSION,
          222  +               COMPILER_NAME);
          223  +  fossil_print("%*s%.19s [%.10s] (%s)\n",
          224  +               colWidth, "sqlite-version:",
          225  +               SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20],
          226  +               SQLITE_VERSION);
          227  +  zDb = db_name("repository");
          228  +  fossil_print("%*s%d pages, %d bytes/pg, %d free pages, "
          229  +               "%s, %s mode\n",
          230  +               colWidth, "database-stats:",
          231  +               db_int(0, "PRAGMA %s.page_count", zDb),
          232  +               db_int(0, "PRAGMA %s.page_size", zDb),
          233  +               db_int(0, "PRAGMA %s.freelist_count", zDb),
          234  +               db_text(0, "PRAGMA %s.encoding", zDb),
          235  +               db_text(0, "PRAGMA %s.journal_mode", zDb));
          236  +
          237  +}
          238  +
          239  +
          240  +
          241  +/*
          242  +** WEBPAGE: urllist
          243  +**
          244  +** Show ways in which this repository has been accessed
          245  +*/
          246  +void urllist_page(void){
          247  +  Stmt q;
          248  +  int cnt;
          249  +  login_check_credentials();
          250  +  if( !g.perm.Admin ){ login_needed(); return; }
          251  +
          252  +  style_header("URLs and Checkouts");
          253  +  style_submenu_element("Stat", "Repository Stats", "stat");
          254  +  @ <div class="section">URLs</div>
          255  +  @ <table border="0" width='100%%'>
          256  +  db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')"
          257  +                 "  FROM config WHERE name GLOB 'baseurl:*' ORDER BY 2");
          258  +  cnt = 0;
          259  +  while( db_step(&q)==SQLITE_ROW ){
          260  +    @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
          261  +    @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
          262  +    cnt++;
          263  +  }
          264  +  db_finalize(&q);
          265  +  if( cnt==0 ){
          266  +    @ <tr><td>(none)</td>
          267  +  }
          268  +  @ </table>
          269  +  @ <div class="section">Checkouts</div>
          270  +  @ <table border="0" width='100%%'>
          271  +  db_prepare(&q, "SELECT substr(name,7), datetime(mtime,'unixepoch')"
          272  +                 "  FROM config WHERE name GLOB 'ckout:*' ORDER BY 2");
          273  +  cnt = 0;
          274  +  while( db_step(&q)==SQLITE_ROW ){
          275  +    @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
          276  +    @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
          277  +    cnt++;
          278  +  }
          279  +  db_finalize(&q);
          280  +  if( cnt==0 ){
          281  +    @ <tr><td>(none)</td>
          282  +  }
          283  +  @ </table>
    92    284     style_footer();
    93    285   }

Changes to src/style.c.

    37     37   static int nSubmenu = 0;
    38     38   
    39     39   /*
    40     40   ** Remember that the header has been generated.  The footer is omitted
    41     41   ** if an error occurs before the header.
    42     42   */
    43     43   static int headerHasBeenGenerated = 0;
           44  +
           45  +/*
           46  +** remember, if a sidebox was used
           47  +*/
           48  +static int sideboxUsed = 0;
           49  +
           50  +
           51  +/*
           52  +** List of hyperlinks and forms that need to be resolved by javascript in
           53  +** the footer.
           54  +*/
           55  +char **aHref = 0;
           56  +int nHref = 0;
           57  +int nHrefAlloc = 0;
           58  +char **aFormAction = 0;
           59  +int nFormAction = 0;
           60  +
           61  +/*
           62  +** Generate and return a anchor tag like this:
           63  +**
           64  +**        <a href="URL">
           65  +**  or    <a id="ID">
           66  +**
           67  +** The form of the anchor tag is determined by the g.javascriptHyperlink
           68  +** variable.  The href="URL" form is used if g.javascriptHyperlink is false.
           69  +** If g.javascriptHyperlink is true then the
           70  +** id="ID" form is used and javascript is generated in the footer to cause
           71  +** href values to be inserted after the page has loaded.  If
           72  +** g.perm.History is false, then the <a id="ID"> form is still
           73  +** generated but the javascript is not generated so the links never
           74  +** activate. 
           75  +**
           76  +** If the user lacks the Hyperlink (h) property and the "auto-hyperlink"
           77  +** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and
           78  +** g.javascriptHyperlink is set to 1.  The g.javascriptHyperlink defaults
           79  +** to 0 and only changes to one if the user lacks the Hyperlink (h) property
           80  +** and the "auto-hyperlink" setting is enabled.
           81  +**
           82  +** Filling in the href="URL" using javascript is a defense against bots.
           83  +**
           84  +** The name of this routine is deliberately kept short so that can be
           85  +** easily used within @-lines.  Example:
           86  +**
           87  +**      @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
           88  +**
           89  +** Note %z format.  The string returned by this function is always
           90  +** obtained from fossil_malloc() so rendering it with %z will reclaim
           91  +** that memory space.
           92  +**
           93  +** There are two versions of this routine: href() does a plain hyperlink
           94  +** and xhref() adds extra attribute text.
           95  +**
           96  +** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
           97  +** Most logged in users should have this property, since we can assume
           98  +** that a logged in user is not a bot.  Only "nobody" lacks g.perm.Hyperlink,
           99  +** typically.
          100  +*/
          101  +char *xhref(const char *zExtra, const char *zFormat, ...){
          102  +  char *zUrl;
          103  +  va_list ap;
          104  +  va_start(ap, zFormat);
          105  +  zUrl = vmprintf(zFormat, ap);
          106  +  va_end(ap);
          107  +  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
          108  +    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
          109  +    fossil_free(zUrl);
          110  +    return zHUrl;
          111  +  }
          112  +  if( nHref>=nHrefAlloc ){
          113  +    nHrefAlloc = nHrefAlloc*2 + 10;
          114  +    aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
          115  +  }
          116  +  aHref[nHref++] = zUrl;
          117  +  return mprintf("<a %s id='a%d'>", zExtra, nHref);
          118  +}
          119  +char *href(const char *zFormat, ...){
          120  +  char *zUrl;
          121  +  va_list ap;
          122  +  va_start(ap, zFormat);
          123  +  zUrl = vmprintf(zFormat, ap);
          124  +  va_end(ap);
          125  +  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
          126  +    char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
          127  +    fossil_free(zUrl);
          128  +    return zHUrl;
          129  +  }
          130  +  if( nHref>=nHrefAlloc ){
          131  +    nHrefAlloc = nHrefAlloc*2 + 10;
          132  +    aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
          133  +  }
          134  +  aHref[nHref++] = zUrl;
          135  +  return mprintf("<a id='a%d'>", nHref);
          136  +}
          137  +
          138  +/*
          139  +** Generate <form method="post" action=ARG>.  The ARG value is inserted
          140  +** by javascript.
          141  +*/
          142  +void form_begin(const char *zOtherArgs, const char *zAction, ...){
          143  +  char *zLink;
          144  +  va_list ap;
          145  +  if( zOtherArgs==0 ) zOtherArgs = "";
          146  +  va_start(ap, zAction);
          147  +  zLink = vmprintf(zAction, ap);
          148  +  va_end(ap);
          149  +  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
          150  +    @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
          151  +  }else{
          152  +    int n;
          153  +    aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
          154  +    aFormAction[nFormAction++] = zLink;
          155  +    n = nFormAction;
          156  +    @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
          157  +  }
          158  +}
          159  +
          160  +/*
          161  +** Generate javascript that will set the href= attribute on all anchors.
          162  +*/
          163  +void style_resolve_href(void){
          164  +  int i;
          165  +  if( !g.perm.Hyperlink ) return;
          166  +  if( nHref==0 && nFormAction==0 ) return;
          167  +  @ <script type="text/JavaScript">
          168  +  @ /* <![CDATA[ */
          169  +  if( g.javascriptHyperlink ){
          170  +    for(i=0; i<nHref; i++){
          171  +      @ gebi("a%d(i+1)").href="%s(aHref[i])";
          172  +    }
          173  +  }
          174  +  for(i=0; i<nFormAction; i++){
          175  +    @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
          176  +  }
          177  +  @ /* ]]> */
          178  +  @ </script>
          179  +}
    44    180   
    45    181   /*
    46    182   ** Add a new element to the submenu
    47    183   */
    48    184   void style_submenu_element(
    49    185     const char *zLabel,
    50    186     const char *zTitle,
................................................................................
    63    199   
    64    200   /*
    65    201   ** Compare two submenu items for sorting purposes
    66    202   */
    67    203   static int submenuCompare(const void *a, const void *b){
    68    204     const struct Submenu *A = (const struct Submenu*)a;
    69    205     const struct Submenu *B = (const struct Submenu*)b;
    70         -  return strcmp(A->zLabel, B->zLabel);
          206  +  return fossil_strcmp(A->zLabel, B->zLabel);
          207  +}
          208  +
          209  +/* Use this for the $current_page variable if it is not NULL.  If it is
          210  +** NULL then use g.zPath.
          211  +*/
          212  +static char *local_zCurrentPage = 0;
          213  +
          214  +/*
          215  +** Set the desired $current_page to something other than g.zPath
          216  +*/
          217  +void style_set_current_page(const char *zFormat, ...){
          218  +  fossil_free(local_zCurrentPage);
          219  +  if( zFormat==0 ){
          220  +    local_zCurrentPage = 0;
          221  +  }else{
          222  +    va_list ap;
          223  +    va_start(ap, zFormat);
          224  +    local_zCurrentPage = vmprintf(zFormat, ap);
          225  +    va_end(ap);
          226  +  }
    71    227   }
    72    228   
    73    229   /*
    74    230   ** Draw the header.
    75    231   */
    76    232   void style_header(const char *zTitleFormat, ...){
    77    233     va_list ap;
    78    234     char *zTitle;
    79         -  const char *zHeader = db_get("header", (char*)zDefaultHeader);  
          235  +  const char *zHeader = db_get("header", (char*)zDefaultHeader);
    80    236     login_check_credentials();
    81    237   
    82    238     va_start(ap, zTitleFormat);
    83    239     zTitle = vmprintf(zTitleFormat, ap);
    84    240     va_end(ap);
    85         -  
          241  +
    86    242     cgi_destination(CGI_HEADER);
    87         -  cgi_printf("%s",
    88         -     "<!DOCTYPE html PUBLIC \"-//W3C/DTD XHTML 1.0 Strict//EN\""
    89         -     " \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
    90         -  
          243  +
          244  +  @ <!DOCTYPE html>
          245  +
    91    246     if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
    92    247   
    93    248     /* Generate the header up through the main menu */
    94    249     Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
    95    250     Th_Store("title", zTitle);
    96    251     Th_Store("baseurl", g.zBaseURL);
          252  +  Th_Store("home", g.zTop);
    97    253     Th_Store("index_page", db_get("index-page","/home"));
    98         -  Th_Store("current_page", g.zPath);
          254  +  Th_Store("current_page", local_zCurrentPage ? local_zCurrentPage : g.zPath);
          255  +  Th_Store("release_version", RELEASE_VERSION);
    99    256     Th_Store("manifest_version", MANIFEST_VERSION);
   100    257     Th_Store("manifest_date", MANIFEST_DATE);
          258  +  Th_Store("compiler_name", COMPILER_NAME);
   101    259     if( g.zLogin ){
   102    260       Th_Store("login", g.zLogin);
   103    261     }
   104    262     if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
   105    263     Th_Render(zHeader);
   106    264     if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
   107    265     Th_Unstore("title");   /* Avoid collisions with ticket field names */
   108    266     cgi_destination(CGI_BODY);
   109    267     g.cgiOutput = 1;
   110    268     headerHasBeenGenerated = 1;
          269  +  sideboxUsed = 0;
          270  +
          271  +  /* Make the gebi(x) function available as an almost-alias for
          272  +  ** document.getElementById(x) (except that it throws an error
          273  +  ** if the element is not found).
          274  +  **
          275  +  ** Maintenance note: this function must of course be available
          276  +  ** before it is called. It "should" go in the HEAD so that client
          277  +  ** HEAD code can make use of it, but because the client can replace
          278  +  ** the HEAD, and some fossil pages rely on gebi(), we put it here.
          279  +  */
          280  +  @ <script>
          281  +  @ function gebi(x){
          282  +  @ if(/^#/.test(x)) x = x.substr(1);
          283  +  @ var e = document.getElementById(x);
          284  +  @ if(!e) throw new Error("Expecting element with ID "+x);
          285  +  @ else return e;}
          286  +  @ </script>
          287  +}
          288  +
          289  +/*
          290  +** Append ad unit text if appropriate.
          291  +*/
          292  +static void style_ad_unit(void){
          293  +  const char *zAd;
          294  +  if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){
          295  +    return;
          296  +  }
          297  +  if( g.zLogin && strcmp(g.zLogin,"anonymous")!=0
          298  +      && db_get_boolean("adunit-omit-if-user",0) ){
          299  +    return;
          300  +  }
          301  +  zAd = db_get("adunit", 0);
          302  +  if( zAd ) cgi_append_content(zAd, -1);
   111    303   }
   112    304   
   113    305   /*
   114    306   ** Draw the footer at the bottom of the page.
   115    307   */
   116    308   void style_footer(void){
   117    309     const char *zFooter;
   118    310   
   119    311     if( !headerHasBeenGenerated ) return;
   120         -  
          312  +
   121    313     /* Go back and put the submenu at the top of the page.  We delay the
   122    314     ** creation of the submenu until the end so that we can add elements
   123    315     ** to the submenu while generating page text.
   124    316     */
   125    317     cgi_destination(CGI_HEADER);
   126    318     if( nSubmenu>0 ){
   127    319       int i;
................................................................................
   128    320       @ <div class="submenu">
   129    321       qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
   130    322       for(i=0; i<nSubmenu; i++){
   131    323         struct Submenu *p = &aSubmenu[i];
   132    324         if( p->zLink==0 ){
   133    325           @ <span class="label">%h(p->zLabel)</span>
   134    326         }else{
   135         -        @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a>
          327  +        @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
   136    328         }
   137    329       }
   138    330       @ </div>
   139    331     }
          332  +  style_ad_unit();
   140    333     @ <div class="content">
   141    334     cgi_destination(CGI_BODY);
   142    335   
   143         -  /* Put the footer at the bottom of the page.
   144         -  */
   145         -  @ </div><br clear="both"/>
          336  +  if (sideboxUsed) {
          337  +    /* Put the footer at the bottom of the page.
          338  +    ** the additional clear/both is needed to extend the content
          339  +    ** part to the end of an optional sidebox.
          340  +    */
          341  +    @ <div class="endContent"></div>
          342  +  }
          343  +  @ </div>
          344  +
          345  +  /* Set the href= field on hyperlinks.  Do this before the footer since
          346  +  ** the footer will be generating </html> */
          347  +  style_resolve_href();
          348  +
   146    349     zFooter = db_get("footer", (char*)zDefaultFooter);
   147    350     if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
   148    351     Th_Render(zFooter);
   149    352     if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
   150         -  
          353  +
   151    354     /* Render trace log if TH1 tracing is enabled. */
   152    355     if( g.thTrace ){
   153         -    cgi_append_content("<font color=\"red\"><hr>\n", -1);
          356  +    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
   154    357       cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
   155         -    cgi_append_content("</font>\n", -1);
          358  +    cgi_append_content("</span>\n", -1);
   156    359     }
   157    360   }
   158    361   
   159    362   /*
   160    363   ** Begin a side-box on the right-hand side of a page.  The title and
   161    364   ** the width of the box are given as arguments.  The width is usually
   162    365   ** a percentage of total screen width.
   163    366   */
   164    367   void style_sidebox_begin(const char *zTitle, const char *zWidth){
   165         -  @ <table width="%s(zWidth)" align="right" border="1" cellpadding=5
   166         -  @  vspace=5 hspace=5>
   167         -  @ <tr><td>
   168         -  @ <b>%h(zTitle)</b>
          368  +  sideboxUsed = 1;
          369  +  @ <div class="sidebox" style="width:%s(zWidth)">
          370  +  @ <div class="sideboxTitle">%h(zTitle)</div>
   169    371   }
   170    372   
   171    373   /* End the side-box
   172    374   */
   173    375   void style_sidebox_end(void){
   174         -  @ </td></tr></table>
          376  +  @ </div>
   175    377   }
   176    378   
   177    379   /* @-comment: // */
   178    380   /*
   179    381   ** The default page header.
   180    382   */
   181         -const char zDefaultHeader[] = 
          383  +const char zDefaultHeader[] =
   182    384   @ <html>
   183    385   @ <head>
          386  +@ <base href="$baseurl/$current_page" />
   184    387   @ <title>$<project_name>: $<title></title>
   185    388   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   186         -@       href="$baseurl/timeline.rss">
   187         -@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
   188         -@       media="screen">
          389  +@       href="$home/timeline.rss" />
          390  +@ <link rel="stylesheet" href="$home/style.css?default" type="text/css"
          391  +@       media="screen" />
   189    392   @ </head>
   190    393   @ <body>
   191    394   @ <div class="header">
   192    395   @   <div class="logo">
   193         -@     <img src="$baseurl/logo" alt="logo">
   194         -@     <br><nobr>$<project_name></nobr>
          396  +@     <img src="$home/logo" alt="logo" />
   195    397   @   </div>
   196         -@   <div class="title">$<title></div>
   197         -@   <div class="status"><nobr><th1>
          398  +@   <div class="title"><small>$<project_name></small><br />$<title></div>
          399  +@   <div class="status"><th1>
   198    400   @      if {[info exists login]} {
   199    401   @        puts "Logged in as $login"
   200    402   @      } else {
   201    403   @        puts "Not logged in"
   202    404   @      }
   203         -@   </th1></nobr></div>
          405  +@   </th1></div>
   204    406   @ </div>
   205         -@ <div class="mainmenu"><th1>
   206         -@ html "<a href='$baseurl$index_page'>Home</a> "
          407  +@ <div class="mainmenu">
          408  +@ <th1>
          409  +@ html "<a href='$home$index_page'>Home</a>\n"
   207    410   @ if {[anycap jor]} {
   208         -@   html "<a href='$baseurl/timeline'>Timeline</a> "
          411  +@   html "<a href='$home/timeline'>Timeline</a>\n"
   209    412   @ }
   210    413   @ if {[hascap oh]} {
   211         -@   html "<a href='$baseurl/dir?ci=tip'>Files</a> "
          414  +@   html "<a href='$home/dir?ci=tip'>Files</a>\n"
   212    415   @ }
   213    416   @ if {[hascap o]} {
   214         -@   html "<a href='$baseurl/leaves'>Leaves</a> "
   215         -@   html "<a href='$baseurl/brlist'>Branches</a> "
   216         -@   html "<a href='$baseurl/taglist'>Tags</a> "
          417  +@   html "<a href='$home/brlist'>Branches</a>\n"
          418  +@   html "<a href='$home/taglist'>Tags</a>\n"
   217    419   @ }
   218    420   @ if {[hascap r]} {
   219         -@   html "<a href='$baseurl/reportlist'>Tickets</a> "
          421  +@   html "<a href='$home/reportlist'>Tickets</a>\n"
   220    422   @ }
   221    423   @ if {[hascap j]} {
   222         -@   html "<a href='$baseurl/wiki'>Wiki</a> "
          424  +@   html "<a href='$home/wiki'>Wiki</a>\n"
   223    425   @ }
   224    426   @ if {[hascap s]} {
   225         -@   html "<a href='$baseurl/setup'>Admin</a> "
          427  +@   html "<a href='$home/setup'>Admin</a>\n"
   226    428   @ } elseif {[hascap a]} {
   227         -@   html "<a href='$baseurl/setup_ulist'>Users</a> "
          429  +@   html "<a href='$home/setup_ulist'>Users</a>\n"
   228    430   @ }
   229    431   @ if {[info exists login]} {
   230         -@   html "<a href='$baseurl/login'>Logout</a> "
          432  +@   html "<a href='$home/login'>Logout</a>\n"
   231    433   @ } else {
   232         -@   html "<a href='$baseurl/login'>Login</a> "
          434  +@   html "<a href='$home/login'>Login</a>\n"
   233    435   @ }
   234    436   @ </th1></div>
   235    437   ;
   236    438   
   237    439   /*
   238    440   ** The default page footer
   239    441   */
   240         -const char zDefaultFooter[] = 
          442  +const char zDefaultFooter[] =
   241    443   @ <div class="footer">
          444  +@ This page was generated in about
          445  +@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
   242    446   @ Fossil version $manifest_version $manifest_date
   243    447   @ </div>
   244    448   @ </body></html>
   245    449   ;
   246    450   
   247    451   /*
   248    452   ** The default Cascading Style Sheet.
          453  +** It's assembled by different strings for each class.
          454  +** The default css conatains all definitions.
          455  +** The style sheet, send to the client only contains the ones,
          456  +** not defined in the user defined css.
   249    457   */
   250         -const char zDefaultCSS[] = 
          458  +const char zDefaultCSS[] =
   251    459   @ /* General settings for the entire page */
   252    460   @ body {
   253    461   @   margin: 0ex 1ex;
   254    462   @   padding: 0px;
   255    463   @   background-color: white;
   256    464   @   font-family: sans-serif;
   257    465   @ }
................................................................................
   259    467   @ /* The project logo in the upper left-hand corner of each page */
   260    468   @ div.logo {
   261    469   @   display: table-cell;
   262    470   @   text-align: center;
   263    471   @   vertical-align: bottom;
   264    472   @   font-weight: bold;
   265    473   @   color: #558195;
          474  +@   min-width: 200px;
          475  +@   white-space: nowrap;
   266    476   @ }
   267    477   @
   268    478   @ /* The page title centered at the top of each page */
   269    479   @ div.title {
   270    480   @   display: table-cell;
   271    481   @   font-size: 2em;
   272    482   @   font-weight: bold;
   273         -@   text-align: left;
          483  +@   text-align: center;
   274    484   @   padding: 0 0 0 1em;
   275    485   @   color: #558195;
   276    486   @   vertical-align: bottom;
   277    487   @   width: 100%;
   278    488   @ }
   279    489   @
   280    490   @ /* The login status message in the top right-hand corner */
................................................................................
   281    491   @ div.status {
   282    492   @   display: table-cell;
   283    493   @   text-align: right;
   284    494   @   vertical-align: bottom;
   285    495   @   color: #558195;
   286    496   @   font-size: 0.8em;
   287    497   @   font-weight: bold;
          498  +@   min-width: 200px;
          499  +@   white-space: nowrap;
   288    500   @ }
   289    501   @
   290    502   @ /* The header across the top of the page */
   291    503   @ div.header {
   292    504   @   display: table;
   293    505   @   width: 100%;
   294    506   @ }
................................................................................
   298    510   @ div.mainmenu {
   299    511   @   padding: 5px 10px 5px 10px;
   300    512   @   font-size: 0.9em;
   301    513   @   font-weight: bold;
   302    514   @   text-align: center;
   303    515   @   letter-spacing: 1px;
   304    516   @   background-color: #558195;
          517  +@   border-top-left-radius: 8px;
          518  +@   border-top-right-radius: 8px;
   305    519   @   color: white;
   306    520   @ }
   307    521   @
   308    522   @ /* The submenu bar that *sometimes* appears below the main menu */
   309         -@ div.submenu {
          523  +@ div.submenu, div.sectionmenu {
   310    524   @   padding: 3px 10px 3px 0px;
   311    525   @   font-size: 0.9em;
   312    526   @   text-align: center;
   313    527   @   background-color: #456878;
   314    528   @   color: white;
   315    529   @ }
   316         -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
          530  +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
          531  +@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
   317    532   @   padding: 3px 10px 3px 10px;
   318    533   @   color: white;
   319    534   @   text-decoration: none;
   320    535   @ }
   321         -@ div.mainmenu a:hover, div.submenu a:hover {
          536  +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
   322    537   @   color: #558195;
   323    538   @   background-color: white;
   324    539   @ }
   325    540   @
   326    541   @ /* All page content from the bottom of the menu or submenu down to
   327    542   @ ** the footer */
   328    543   @ div.content {
   329         -@   padding: 0ex 1ex 0ex 2ex;
          544  +@   padding: 0ex 1ex 1ex 1ex;
          545  +@   border: solid #aaa;
          546  +@   border-width: 1px;
   330    547   @ }
   331    548   @
   332    549   @ /* Some pages have section dividers */
   333    550   @ div.section {
   334    551   @   margin-bottom: 0px;
   335    552   @   margin-top: 1em;
   336    553   @   padding: 1px 1px 1px 1px;
   337    554   @   font-size: 1.2em;
   338    555   @   font-weight: bold;
   339    556   @   background-color: #558195;
   340    557   @   color: white;
          558  +@   white-space: nowrap;
   341    559   @ }
   342    560   @
   343    561   @ /* The "Date" that occurs on the left hand side of timelines */
   344    562   @ div.divider {
   345    563   @   background: #a1c4d4;
   346    564   @   border: 2px #558195 solid;
   347    565   @   font-size: 1em; font-weight: normal;
   348    566   @   padding: .25em;
   349    567   @   margin: .2em 0 .2em 0;
   350    568   @   float: left;
   351    569   @   clear: left;
          570  +@   white-space: nowrap;
   352    571   @ }
   353    572   @
   354    573   @ /* The footer at the very bottom of the page */
   355    574   @ div.footer {
          575  +@   clear: both;
   356    576   @   font-size: 0.8em;
   357         -@   margin-top: 12px;
   358    577   @   padding: 5px 10px 5px 10px;
   359    578   @   text-align: right;
   360    579   @   background-color: #558195;
          580  +@   border-bottom-left-radius: 8px;
          581  +@   border-bottom-right-radius: 8px;
   361    582   @   color: white;
   362    583   @ }
   363    584   @
   364    585   @ /* Hyperlink colors in the footer */
   365    586   @ div.footer a { color: white; }
   366    587   @ div.footer a:link { color: white; }
   367    588   @ div.footer a:visited { color: white; }
   368    589   @ div.footer a:hover { background-color: white; color: #558195; }
   369         -@ 
   370         -@ /* <verbatim> blocks */
          590  +@
          591  +@ /* verbatim blocks */
   371    592   @ pre.verbatim {
   372         -@    background-color: #f5f5f5;
   373         -@    padding: 0.5em;
          593  +@   background-color: #f5f5f5;
          594  +@   padding: 0.5em;
          595  +@   white-space: pre-wrap;
   374    596   @}
   375    597   @
   376    598   @ /* The label/value pairs on (for example) the ci page */
   377    599   @ table.label-value th {
   378    600   @   vertical-align: top;
   379    601   @   text-align: right;
   380    602   @   padding: 0.2ex 2ex;
   381    603   @ }
   382    604   ;
   383    605   
          606  +
          607  +/* The following table contains bits of default CSS that must
          608  +** be included if they are not found in the application-defined
          609  +** CSS.
          610  +*/
          611  +const struct strctCssDefaults {
          612  +  char const * const elementClass;  /* Name of element needed */
          613  +  char const * const comment;       /* Comment text */
          614  +  char const * const value;         /* CSS text */
          615  +} cssDefaultList[] = {
          616  +  { "",
          617  +    "",
          618  +    zDefaultCSS
          619  +  },
          620  +  { "div.sidebox",
          621  +    "The nomenclature sidebox for branches,..",
          622  +    @   float: right;
          623  +    @   background-color: white;
          624  +    @   border-width: medium;
          625  +    @   border-style: double;
          626  +    @   margin: 10px;
          627  +  },
          628  +  { "div.sideboxTitle",
          629  +    "The nomenclature title in sideboxes for branches,..",
          630  +    @   display: inline;
          631  +    @   font-weight: bold;
          632  +  },
          633  +  { "div.sideboxDescribed",
          634  +    "The defined element in sideboxes for branches,..",
          635  +    @   display: inline;
          636  +    @   font-weight: bold;
          637  +  },
          638  +  { "span.disabled",
          639  +    "The defined element in sideboxes for branches,..",
          640  +    @   color: red;
          641  +  },
          642  +  { "span.timelineDisabled",
          643  +    "The suppressed duplicates lines in timeline, ..",
          644  +    @   font-style: italic;
          645  +    @   font-size: small;
          646  +  },
          647  +  { "table.timelineTable",
          648  +    "the format for the timeline data table",
          649  +    @   border: 0;
          650  +  },
          651  +  { "td.timelineTableCell",
          652  +    "the format for the timeline data cells",
          653  +    @   vertical-align: top;
          654  +    @   text-align: left;
          655  +  },
          656  +  { "span.timelineLeaf",
          657  +    "the format for the timeline leaf marks",
          658  +    @   font-weight: bold;
          659  +  },
          660  +  { "a.timelineHistLink",
          661  +    "the format for the timeline version links",
          662  +    @
          663  +  },
          664  +  { "span.timelineHistDsp",
          665  +    "the format for the timeline version display(no history permission!)",
          666  +    @   font-weight: bold;
          667  +  },
          668  +  { "td.timelineTime",
          669  +    "the format for the timeline time display",
          670  +    @   vertical-align: top;
          671  +    @   text-align: right;
          672  +  },
          673  +  { "td.timelineGraph",
          674  +    "the format for the grap placeholder cells in timelines",
          675  +    @ width: 20px;
          676  +    @ text-align: left;
          677  +    @ vertical-align: top;
          678  +  },
          679  +  { "a.tagLink",
          680  +    "the format for the tag links",
          681  +    @
          682  +  },
          683  +  { "span.tagDsp",
          684  +    "the format for the tag display(no history permission!)",
          685  +    @   font-weight: bold;
          686  +  },
          687  +  { "span.wikiError",
          688  +    "the format for wiki errors",
          689  +    @   font-weight: bold;
          690  +    @   color: red;
          691  +  },
          692  +  { "span.infoTagCancelled",
          693  +    "the format for fixed/canceled tags,..",
          694  +    @   font-weight: bold;
          695  +    @   text-decoration: line-through;
          696  +  },
          697  +  { "span.infoTag",
          698  +    "the format for tags,..",
          699  +    @   font-weight: bold;
          700  +  },
          701  +  { "span.wikiTagCancelled",
          702  +    "the format for fixed/cancelled tags,.. on wiki pages",
          703  +    @   text-decoration: line-through;
          704  +  },
          705  +  { "table.browser",
          706  +    "format for the file display table",
          707  +    @ /* the format for wiki errors */
          708  +    @   width: 100%;
          709  +    @   border: 0;
          710  +  },
          711  +  { "td.browser",
          712  +    "format for cells in the file browser",
          713  +    @   width: 24%;
          714  +    @   vertical-align: top;
          715  +  },
          716  +  { "ul.browser",
          717  +    "format for the list in the file browser",
          718  +    @   margin-left: 0.5em;
          719  +    @   padding-left: 0.5em;
          720  +    @   white-space: nowrap;
          721  +  },
          722  +  { "table.login_out",
          723  +    "table format for login/out label/input table",
          724  +    @   text-align: left;
          725  +    @   margin-right: 10px;
          726  +    @   margin-left: 10px;
          727  +    @   margin-top: 10px;
          728  +  },
          729  +  { "div.captcha",
          730  +    "captcha display options",
          731  +    @   text-align: center;
          732  +    @   padding: 1ex;
          733  +  },
          734  +  { "table.captcha",
          735  +    "format for the layout table, used for the captcha display",
          736  +    @   margin: auto;
          737  +    @   padding: 10px;
          738  +    @   border-width: 4px;
          739  +    @   border-style: double;
          740  +    @   border-color: black;
          741  +  },
          742  +  { "td.login_out_label",
          743  +    "format for the label cells in the login/out table",
          744  +    @   text-align: center;
          745  +  },
          746  +  { "span.loginError",
          747  +    "format for login error messages",
          748  +    @   color: red;
          749  +  },
          750  +  { "span.note",
          751  +    "format for leading text for notes",
          752  +    @   font-weight: bold;
          753  +  },
          754  +  { "span.textareaLabel",
          755  +    "format for textarea labels",
          756  +    @   font-weight: bold;
          757  +  },
          758  +  { "table.usetupLayoutTable",
          759  +    "format for the user setup layout table",
          760  +    @   outline-style: none;
          761  +    @   padding: 0;
          762  +    @   margin: 25px;
          763  +  },
          764  +  { "td.usetupColumnLayout",
          765  +    "format of the columns on the user setup list page",
          766  +    @   vertical-align: top
          767  +  },
          768  +  { "table.usetupUserList",
          769  +    "format for the user list table on the user setup page",
          770  +    @   outline-style: double;
          771  +    @   outline-width: 1px;
          772  +    @   padding: 10px;
          773  +  },
          774  +  { "th.usetupListUser",
          775  +    "format for table header user in user list on user setup page",
          776  +    @   text-align: right;
          777  +    @   padding-right: 20px;
          778  +  },
          779  +  { "th.usetupListCap",
          780  +    "format for table header capabilities in user list on user setup page",
          781  +    @   text-align: center;
          782  +    @   padding-right: 15px;
          783  +  },
          784  +  { "th.usetupListCon",
          785  +    "format for table header contact info in user list on user setup page",
          786  +    @   text-align: left;
          787  +  },
          788  +  { "td.usetupListUser",
          789  +    "format for table cell user in user list on user setup page",
          790  +    @   text-align: right;
          791  +    @   padding-right: 20px;
          792  +    @   white-space:nowrap;
          793  +  },
          794  +  { "td.usetupListCap",
          795  +    "format for table cell capabilities in user list on user setup page",
          796  +    @   text-align: center;
          797  +    @   padding-right: 15px;
          798  +  },
          799  +  { "td.usetupListCon",
          800  +    "format for table cell contact info in user list on user setup page",
          801  +    @   text-align: left
          802  +  },
          803  +  { "div.ueditCapBox",
          804  +    "layout definition for the capabilities box on the user edit detail page",
          805  +    @   float: left;
          806  +    @   margin-right: 20px;
          807  +    @   margin-bottom: 20px;
          808  +  },
          809  +  { "td.usetupEditLabel",
          810  +    "format of the label cells in the detailed user edit page",
          811  +    @   text-align: right;
          812  +    @   vertical-align: top;
          813  +    @   white-space: nowrap;
          814  +  },
          815  +  { "span.ueditInheritNobody",
          816  +    "color for capabilities, inherited by nobody",
          817  +    @   color: green;
          818  +  },
          819  +  { "span.ueditInheritDeveloper",
          820  +    "color for capabilities, inherited by developer",
          821  +    @   color: red;
          822  +  },
          823  +  { "span.ueditInheritReader",
          824  +    "color for capabilities, inherited by reader",
          825  +    @   color: black;
          826  +  },
          827  +  { "span.ueditInheritAnonymous",
          828  +    "color for capabilities, inherited by anonymous",
          829  +    @   color: blue;
          830  +  },
          831  +  { "span.capability",
          832  +    "format for capabilities, mentioned on the user edit page",
          833  +    @   font-weight: bold;
          834  +  },
          835  +  { "span.usertype",
          836  +    "format for different user types, mentioned on the user edit page",
          837  +    @   font-weight: bold;
          838  +  },
          839  +  { "span.usertype:before",
          840  +    "leading text for user types, mentioned on the user edit page",
          841  +    @   content:"'";
          842  +  },
          843  +  { "span.usertype:after",
          844  +    "trailing text for user types, mentioned on the user edit page",
          845  +    @   content:"'";
          846  +  },
          847  +  { "div.selectedText",
          848  +    "selected lines of text within a linenumbered artifact display",
          849  +    @   font-weight: bold;
          850  +    @   color: blue;
          851  +    @   background-color: #d5d5ff;
          852  +    @   border: 1px blue solid;
          853  +  },
          854  +  { "p.missingPriv",
          855  +    "format for missing privileges note on user setup page",
          856  +    @  color: blue;
          857  +  },
          858  +  { "span.wikiruleHead",
          859  +    "format for leading text in wikirules definitions",
          860  +    @   font-weight: bold;
          861  +  },
          862  +  { "td.tktDspLabel",
          863  +    "format for labels on ticket display page",
          864  +    @   text-align: right;
          865  +  },
          866  +  { "td.tktDspValue",
          867  +    "format for values on ticket display page",
          868  +    @   text-align: left;
          869  +    @   vertical-align: top;
          870  +    @   background-color: #d0d0d0;
          871  +  },
          872  +  { "span.tktError",
          873  +    "format for ticket error messages",
          874  +    @   color: red;
          875  +    @   font-weight: bold;
          876  +  },
          877  +  { "table.rpteditex",
          878  +    "format for example tables on the report edit page",
          879  +    @   float: right;
          880  +    @   margin: 0;
          881  +    @   padding: 0;
          882  +    @   width: 125px;
          883  +    @   text-align: center;
          884  +    @   border-collapse: collapse;
          885  +    @   border-spacing: 0;
          886  +  },
          887  +  { "table.report",
          888  +    "Ticket report table formatting",
          889  +    @   border-collapse:collapse;
          890  +    @   border: 1px solid #999;
          891  +    @   margin: 1em 0 1em 0;
          892  +    @   cursor: pointer;
          893  +  },
          894  +  { "td.rpteditex",
          895  +    "format for example table cells on the report edit page",
          896  +    @   border-width: thin;
          897  +    @   border-color: #000000;
          898  +    @   border-style: solid;
          899  +  },
          900  +  { "input.checkinUserColor",
          901  +    "format for user color input on checkin edit page",
          902  +    @ /* no special definitions, class defined, to enable color pickers, f.e.:
          903  +    @ **  add the color picker found at http:jscolor.com  as java script include
          904  +    @ **  to the header and configure the java script file with
          905  +    @ **   1. use as bindClass :checkinUserColor
          906  +    @ **   2. change the default hash adding behaviour to ON
          907  +    @ ** or change the class defition of element identified by id="clrcust"
          908  +    @ ** to a standard jscolor definition with java script in the footer. */
          909  +  },
          910  +  { "div.endContent",
          911  +    "format for end of content area, to be used to clear page flow(sidebox on branch,..",
          912  +    @   clear: both;
          913  +  },
          914  +  { "p.generalError",
          915  +    "format for general errors",
          916  +    @   color: red;
          917  +  },
          918  +  { "p.tktsetupError",
          919  +    "format for tktsetup errors",
          920  +    @   color: red;
          921  +    @   font-weight: bold;
          922  +  },
          923  +  { "p.xfersetupError",
          924  +    "format for xfersetup errors",
          925  +    @   color: red;
          926  +    @   font-weight: bold;
          927  +  },
          928  +  { "p.thmainError",
          929  +    "format for th script errors",
          930  +    @   color: red;
          931  +    @   font-weight: bold;
          932  +  },
          933  +  { "span.thTrace",
          934  +    "format for th script trace messages",
          935  +    @   color: red;
          936  +  },
          937  +  { "p.reportError",
          938  +    "format for report configuration errors",
          939  +    @   color: red;
          940  +    @   font-weight: bold;
          941  +  },
          942  +  { "blockquote.reportError",
          943  +    "format for report configuration errors",
          944  +    @   color: red;
          945  +    @   font-weight: bold;
          946  +  },
          947  +  { "p.noMoreShun",
          948  +    "format for artifact lines, no longer shunned",
          949  +    @   color: blue;
          950  +  },
          951  +  { "p.shunned",
          952  +    "format for artifact lines beeing shunned",
          953  +    @   color: blue;
          954  +  },
          955  +  { "span.brokenlink",
          956  +    "a broken hyperlink",
          957  +    @   color: red;
          958  +  },
          959  +  { "ul.filelist",
          960  +    "List of files in a timeline",
          961  +    @   margin-top: 3px;
          962  +    @   line-height: 100%;
          963  +  },
          964  +  { "div.sbsdiff",
          965  +    "side-by-side diff display",
          966  +    @   font-family: monospace;
          967  +    @   font-size: xx-small;
          968  +    @   white-space: pre;
          969  +  },
          970  +  { "div.udiff",
          971  +    "context diff display",
          972  +    @   font-family: monospace;
          973  +    @   white-space: pre;
          974  +  },
          975  +  { "span.diffchng",
          976  +    "changes in a diff",
          977  +    @   background-color: #c0c0ff;
          978  +  },
          979  +  { "span.diffadd",
          980  +    "added code in a diff",
          981  +    @   background-color: #c0ffc0;
          982  +  },
          983  +  { "span.diffrm",
          984  +    "deleted in a diff",
          985  +    @   background-color: #ffc8c8;
          986  +  },
          987  +  { "span.diffhr",
          988  +    "suppressed lines in a diff",
          989  +    @   color: #0000ff;
          990  +  },
          991  +  { "span.diffln",
          992  +    "line numbers in a diff",
          993  +    @   color: #a0a0a0;
          994  +  },
          995  +  { "span.modpending",
          996  +    "Moderation Pending message on timeline",
          997  +    @   color: #b03800;
          998  +    @   font-style: italic;
          999  +  },
         1000  +  { "pre.th1result",
         1001  +    "format for th1 script results",
         1002  +    @   white-space: pre-wrap;
         1003  +    @   word-wrap: break-word;
         1004  +  },
         1005  +  { "pre.th1error",
         1006  +    "format for th1 script errors",
         1007  +    @   white-space: pre-wrap;
         1008  +    @   word-wrap: break-word;
         1009  +    @   color: red;
         1010  +  },
         1011  +  { 0,
         1012  +    0,
         1013  +    0
         1014  +  }
         1015  +};
         1016  +
         1017  +/*
         1018  +** Append all of the default CSS to the CGI output.
         1019  +*/
         1020  +void cgi_append_default_css(void) {
         1021  +  int i;
         1022  +
         1023  +  for (i=0;cssDefaultList[i].elementClass;i++){
         1024  +    if (cssDefaultList[i].elementClass[0]){
         1025  +      cgi_printf("/* %s */\n%s {\n%s\n}\n\n",
         1026  +		 cssDefaultList[i].comment,
         1027  +		 cssDefaultList[i].elementClass,
         1028  +		 cssDefaultList[i].value
         1029  +		);
         1030  +    }else{
         1031  +      cgi_printf("%s",
         1032  +		 cssDefaultList[i].value
         1033  +		);
         1034  +    }
         1035  +  }
         1036  +}
         1037  +
   384   1038   /*
   385   1039   ** WEBPAGE: style.css
   386   1040   */
   387   1041   void page_style_css(void){
   388         -  char *zCSS = 0;
         1042  +  Blob css;
         1043  +  int i;
   389   1044   
   390   1045     cgi_set_content_type("text/css");
   391         -  zCSS = db_get("css",(char*)zDefaultCSS);
   392         -  cgi_append_content(zCSS, -1);
         1046  +  blob_init(&css, db_get("css",(char*)zDefaultCSS), -1);
         1047  +
         1048  +  /* add special missing definitions */
         1049  +  for(i=1; cssDefaultList[i].elementClass; i++){
         1050  +    if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){
         1051  +      blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
         1052  +          cssDefaultList[i].comment,
         1053  +          cssDefaultList[i].elementClass,
         1054  +          cssDefaultList[i].value);
         1055  +    }
         1056  +  }
         1057  +
         1058  +  /* Process through TH1 in order to give an opportunity to substitute
         1059  +  ** variables such as $baseurl.
         1060  +  */
         1061  +  Th_Store("baseurl", g.zBaseURL);
         1062  +  Th_Store("home", g.zTop);
         1063  +  Th_Render(blob_str(&css));
         1064  +
         1065  +  /* Tell CGI that the content returned by this page is considered cacheable */
   393   1066     g.isConst = 1;
   394   1067   }
   395   1068   
   396   1069   /*
   397   1070   ** WEBPAGE: test_env
   398   1071   */
   399   1072   void page_test_env(void){
         1073  +  char c;
         1074  +  int i;
         1075  +  int showAll;
         1076  +  char zCap[30];
         1077  +  login_check_credentials();
         1078  +  if( !g.perm.Admin && !g.perm.Setup && !db_get_boolean("test_env_enable",0) ){
         1079  +    login_needed();
         1080  +    return;
         1081  +  }
   400   1082     style_header("Environment Test");
   401         -#if !defined(__MINGW32__)
   402         -  @ uid=%d(getuid()), gid=%d(getgid())<br>
         1083  +  showAll = atoi(PD("showall","0"));
         1084  +  if( !showAll ){
         1085  +    style_submenu_element("Show Cookies", "Show Cookies",
         1086  +                          "%s/test_env?showall=1", g.zTop);
         1087  +  }else{
         1088  +    style_submenu_element("Hide Cookies", "Hide Cookies",
         1089  +                          "%s/test_env", g.zTop);
         1090  +  }
         1091  +#if !defined(_WIN32)
         1092  +  @ uid=%d(getuid()), gid=%d(getgid())<br />
   403   1093   #endif
   404         -  @ g.zBaseURL = %h(g.zBaseURL)<br>
   405         -  @ g.zTop = %h(g.zTop)<br>
   406         -  @ g.zRepositoryName = %h(g.zRepositoryName)<br>
   407         -  cgi_print_all();
         1094  +  @ g.zBaseURL = %h(g.zBaseURL)<br />
         1095  +  @ g.zTop = %h(g.zTop)<br />
         1096  +  for(i=0, c='a'; c<='z'; c++){
         1097  +    if( login_has_capability(&c, 1) ) zCap[i++] = c;
         1098  +  }
         1099  +  zCap[i] = 0;
         1100  +  @ g.userUid = %d(g.userUid)<br />
         1101  +  @ g.zLogin = %h(g.zLogin)<br />
         1102  +  @ capabilities = %s(zCap)<br />
         1103  +  @ <hr>
         1104  +  P("HTTP_USER_AGENT");
         1105  +  cgi_print_all(atoi(PD("showall","0")));
         1106  +  if( g.perm.Setup ){
         1107  +    const char *zRedir = P("redirect");
         1108  +    if( zRedir ) cgi_redirect(zRedir);
         1109  +  }
   408   1110     style_footer();
   409   1111   }

Changes to src/sync.c.

    17     17   **
    18     18   ** This file contains code used to push, pull, and sync a repository
    19     19   */
    20     20   #include "config.h"
    21     21   #include "sync.h"
    22     22   #include <assert.h>
    23     23   
    24         -#if INTERFACE
    25     24   /*
    26         -** Flags used to determine which direction(s) an autosync goes in.
    27         -*/
    28         -#define AUTOSYNC_PUSH  1
    29         -#define AUTOSYNC_PULL  2
    30         -
    31         -#endif /* INTERFACE */
    32         -
    33         -/*
    34         -** If the respository is configured for autosyncing, then do an
           25  +** If the repository is configured for autosyncing, then do an
    35     26   ** autosync.  This will be a pull if the argument is true or a push
    36     27   ** if the argument is false.
           28  +**
           29  +** Return the number of errors.
    37     30   */
    38         -void autosync(int flags){
           31  +int autosync(int flags){
    39     32     const char *zUrl;
    40     33     const char *zAutosync;
    41     34     const char *zPw;
           35  +  int rc;
           36  +  int configSync = 0;       /* configuration changes transferred */
    42     37     if( g.fNoSync ){
    43         -    return;
           38  +    return 0;
           39  +  }
           40  +  if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
           41  +    return 0;
    44     42     }
    45     43     zAutosync = db_get("autosync", 0);
    46     44     if( zAutosync ){
    47         -    if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
    48         -      return;   /* Do not auto-push when autosync=pullonly */
           45  +    if( (flags & SYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
           46  +      return 0;   /* Do not auto-push when autosync=pullonly */
    49     47       }
    50     48       if( is_false(zAutosync) ){
    51         -      return;   /* Autosync is completely off */
           49  +      return 0;   /* Autosync is completely off */
    52     50       }
    53     51     }else{
    54     52       /* Autosync defaults on.  To make it default off, "return" here. */
    55     53     }
    56     54     zUrl = db_get("last-sync-url", 0);
    57     55     if( zUrl==0 ){
    58         -    return;  /* No default server */
           56  +    return 0;  /* No default server */
    59     57     }
    60         -  zPw = db_get("last-sync-pw", 0);
           58  +  zPw = unobscure(db_get("last-sync-pw", 0));
    61     59     url_parse(zUrl);
    62     60     if( g.urlUser!=0 && g.urlPasswd==0 ){
    63     61       g.urlPasswd = mprintf("%s", zPw);
    64     62     }
    65         -  printf("Autosync:  %s\n", g.urlCanonical);
           63  +#if 0 /* Disabled for now */
           64  +  if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
           65  +    /* When doing an automatic pull, also automatically pull shuns from
           66  +    ** the server if pull_shuns is enabled.
           67  +    **
           68  +    ** TODO:  What happens if the shun list gets really big? 
           69  +    ** Maybe the shunning list should only be pulled on every 10th
           70  +    ** autosync, or something?
           71  +    */
           72  +    configSync = CONFIGSET_SHUN;
           73  +  }
           74  +#endif
           75  +  if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
           76  +  fossil_print("Autosync:  %s\n", g.urlCanonical);
    66     77     url_enable_proxy("via proxy: ");
    67         -  client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, 0);
           78  +  rc = client_sync(flags, configSync, 0);
           79  +  if( rc ) fossil_warning("Autosync failed");
           80  +  return rc;
    68     81   }
    69     82   
    70     83   /*
    71     84   ** This routine processes the command-line argument for push, pull,
    72     85   ** and sync.  If a command-line argument is given, that is the URL
    73     86   ** of a server to sync against.  If no argument is given, use the
    74     87   ** most recently synced URL.  Remember the current URL for next time.
    75     88   */
    76         -void process_sync_args(void){
           89  +static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
    77     90     const char *zUrl = 0;
    78     91     const char *zPw = 0;
           92  +  unsigned configSync = 0;
    79     93     int urlOptional = find_option("autourl",0,0)!=0;
    80     94     g.dontKeepUrl = find_option("once",0,0)!=0;
           95  +  if( find_option("private",0,0)!=0 ){
           96  +    *pSyncFlags |= SYNC_PRIVATE;
           97  +  }
           98  +  if( find_option("verbose","v",0)!=0 ){
           99  +    *pSyncFlags |= SYNC_VERBOSE;
          100  +  }
    81    101     url_proxy_options();
    82         -  db_find_and_open_repository(1);
          102  +  db_find_and_open_repository(0, 0);
    83    103     db_open_config(0);
    84    104     if( g.argc==2 ){
    85    105       zUrl = db_get("last-sync-url", 0);
    86         -    zPw = db_get("last-sync-pw", 0);
          106  +    zPw = unobscure(db_get("last-sync-pw", 0));
          107  +    if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
    87    108     }else if( g.argc==3 ){
    88    109       zUrl = g.argv[2];
    89    110     }
    90    111     if( zUrl==0 ){
    91         -    if( urlOptional ) exit(0);
          112  +    if( urlOptional ) fossil_exit(0);
    92    113       usage("URL");
    93    114     }
    94    115     url_parse(zUrl);
    95         -  if( !g.dontKeepUrl ){
    96         -    db_set("last-sync-url", g.urlCanonical, 0);
    97         -    if( g.urlPasswd ) db_set("last-sync-pw", g.urlPasswd, 0);
    98         -  }
    99         -  if( g.urlUser!=0 && g.urlPasswd==0 ){
          116  +  if( g.urlUser!=0 && g.urlPasswd==0 && g.urlIsSsh==0 ){
   100    117       if( zPw==0 ){
   101    118         url_prompt_for_password();
   102    119       }else{
   103    120         g.urlPasswd = mprintf("%s", zPw);
   104    121       }
   105    122     }
          123  +  if( !g.dontKeepUrl ){
          124  +    db_set("last-sync-url", g.urlCanonical, 0);
          125  +    if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
          126  +  }
   106    127     user_select();
   107    128     if( g.argc==2 ){
   108         -    printf("Server:    %s\n", g.urlCanonical);
          129  +    if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
          130  +      fossil_print("Sync with %s\n", g.urlCanonical);
          131  +    }else if( (*pSyncFlags) & SYNC_PUSH ){
          132  +      fossil_print("Push to %s\n", g.urlCanonical);
          133  +    }else if( (*pSyncFlags) & SYNC_PULL ){
          134  +      fossil_print("Pull from %s\n", g.urlCanonical);
          135  +    }
   109    136     }
   110    137     url_enable_proxy("via proxy: ");
          138  +  *pConfigFlags |= configSync;
   111    139   }
   112    140   
   113    141   /*
   114    142   ** COMMAND: pull
   115    143   **
   116    144   ** Usage: %fossil pull ?URL? ?options?
   117    145   **
................................................................................
   122    150   ** If the URL is not specified, then the URL from the most recent
   123    151   ** clone, push, pull, remote-url, or sync command is used.
   124    152   **
   125    153   ** The URL specified normally becomes the new "remote-url" used for
   126    154   ** subsequent push, pull, and sync operations.  However, the "--once"
   127    155   ** command-line option makes the URL a one-time-use URL that is not
   128    156   ** saved.
          157  +**
          158  +** Use the --private option to pull private branches from the
          159  +** remote repository.
   129    160   **
   130    161   ** See also: clone, push, sync, remote-url
   131    162   */
   132    163   void pull_cmd(void){
   133         -  process_sync_args();
   134         -  client_sync(0,1,0,0,0);
          164  +  unsigned configFlags = 0;
          165  +  unsigned syncFlags = SYNC_PULL;
          166  +  process_sync_args(&configFlags, &syncFlags);
          167  +  client_sync(syncFlags, configFlags, 0);
   135    168   }
   136    169   
   137    170   /*
   138    171   ** COMMAND: push
   139    172   **
   140    173   ** Usage: %fossil push ?URL? ?options?
   141    174   **
................................................................................
   146    179   ** If the URL is not specified, then the URL from the most recent
   147    180   ** clone, push, pull, remote-url, or sync command is used.
   148    181   **
   149    182   ** The URL specified normally becomes the new "remote-url" used for
   150    183   ** subsequent push, pull, and sync operations.  However, the "--once"
   151    184   ** command-line option makes the URL a one-time-use URL that is not
   152    185   ** saved.
          186  +**
          187  +** Use the --private option to push private branches to the
          188  +** remote repository.
   153    189   **
   154    190   ** See also: clone, pull, sync, remote-url
   155    191   */
   156    192   void push_cmd(void){
   157         -  process_sync_args();
   158         -  client_sync(1,0,0,0,0);
          193  +  unsigned configFlags = 0;
          194  +  unsigned syncFlags = SYNC_PUSH;
          195  +  process_sync_args(&configFlags, &syncFlags);
          196  +  if( db_get_boolean("dont-push",0) ){
          197  +    fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
          198  +  }
          199  +  client_sync(syncFlags, 0, 0);
   159    200   }
   160    201   
   161    202   
   162    203   /*
   163    204   ** COMMAND: sync
   164    205   **
   165    206   ** Usage: %fossil sync ?URL? ?options?
................................................................................
   176    217   ** If the URL is not specified, then the URL from the most recent successful
   177    218   ** clone, push, pull, remote-url, or sync command is used.
   178    219   **
   179    220   ** The URL specified normally becomes the new "remote-url" used for
   180    221   ** subsequent push, pull, and sync operations.  However, the "--once"
   181    222   ** command-line option makes the URL a one-time-use URL that is not
   182    223   ** saved.
          224  +**
          225  +** Use the --private option to sync private branches with the
          226  +** remote repository.
   183    227   **
   184    228   ** See also:  clone, push, pull, remote-url
   185    229   */
   186    230   void sync_cmd(void){
   187         -  process_sync_args();
   188         -  client_sync(1,1,0,0,0);
          231  +  unsigned configFlags = 0;
          232  +  unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
          233  +  process_sync_args(&configFlags, &syncFlags);
          234  +  if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
          235  +  client_sync(syncFlags, configFlags, 0);
          236  +  if( (syncFlags & SYNC_PUSH)==0 ){
          237  +    fossil_warning("pull only: the 'dont-push' option is set");
          238  +  }
   189    239   }
   190    240   
   191    241   /*
   192    242   ** COMMAND: remote-url
   193    243   **
   194    244   ** Usage: %fossil remote-url ?URL|off?
   195    245   **
................................................................................
   201    251   ** The default remote-url is used by auto-syncing and by "sync", "push",
   202    252   ** "pull" that omit the server URL.
   203    253   **
   204    254   ** See also: clone, push, pull, sync
   205    255   */
   206    256   void remote_url_cmd(void){
   207    257     char *zUrl;
   208         -  db_find_and_open_repository(1);
          258  +  db_find_and_open_repository(0, 0);
   209    259     if( g.argc!=2 && g.argc!=3 ){
   210    260       usage("remote-url ?URL|off?");
   211    261     }
   212    262     if( g.argc==3 ){
   213         -    if( strcmp(g.argv[2],"off")==0 ){
          263  +    if( fossil_strcmp(g.argv[2],"off")==0 ){
   214    264         db_unset("last-sync-url", 0);
   215    265         db_unset("last-sync-pw", 0);
   216    266       }else{
   217    267         url_parse(g.argv[2]);
   218         -      if( g.urlUser && g.urlPasswd==0 ){
          268  +      if( g.urlUser && g.urlPasswd==0 && g.urlIsSsh==0 ){
   219    269           url_prompt_for_password();
   220    270         }
   221    271         db_set("last-sync-url", g.urlCanonical, 0);
   222    272         if( g.urlPasswd ){
   223         -        db_set("last-sync-pw", g.urlPasswd, 0);
          273  +        db_set("last-sync-pw", obscure(g.urlPasswd), 0);
   224    274         }else{
   225    275           db_unset("last-sync-pw", 0);
   226    276         }
   227    277       }
   228    278     }
   229    279     zUrl = db_get("last-sync-url", 0);
   230    280     if( zUrl==0 ){
   231         -    printf("off\n");
          281  +    fossil_print("off\n");
   232    282       return;
   233    283     }else{
   234    284       url_parse(zUrl);
   235         -    printf("%s\n", g.urlCanonical);
          285  +    fossil_print("%s\n", g.urlCanonical);
   236    286     }
   237    287   }

Changes to src/tag.c.

    24     24   /*
    25     25   ** Propagate the tag given by tagid to the children of pid.
    26     26   **
    27     27   ** This routine assumes that tagid is a tag that should be
    28     28   ** propagated and that the tag is already present in pid.
    29     29   **
    30     30   ** If tagtype is 2 then the tag is being propagated from an
    31         -** ancestor node.  If tagtype is 0 it means a branch tag is
    32         -** being cancelled.
           31  +** ancestor node.  If tagtype is 0 it means a propagating tag is
           32  +** being blocked.
    33     33   */
    34         -void tag_propagate(
           34  +static void tag_propagate(
    35     35     int pid,             /* Propagate the tag to children of this node */
    36     36     int tagid,           /* Tag to propagate */
    37     37     int tagType,         /* 2 for a propagating tag.  0 for an antitag */
    38     38     int origId,          /* Artifact of tag, when tagType==2 */
    39     39     const char *zValue,  /* Value of the tag.  Might be NULL */
    40     40     double mtime         /* Timestamp on the tag */
    41     41   ){
    42         -  PQueue queue;
    43         -  Stmt s, ins, eventupdate;
           42  +  PQueue queue;        /* Queue of check-ins to be tagged */
           43  +  Stmt s;              /* Query the children of :pid to which to propagate */
           44  +  Stmt ins;            /* INSERT INTO tagxref */
           45  +  Stmt eventupdate;    /* UPDATE event */
    44     46   
    45     47     assert( tagType==0 || tagType==2 );
    46         -  pqueue_init(&queue);
    47         -  pqueue_insert(&queue, pid, 0.0);
           48  +  pqueuex_init(&queue);
           49  +  pqueuex_insert(&queue, pid, 0.0, 0);
           50  +
           51  +  /* Query for children of :pid to which to propagate the tag.
           52  +  ** Three returns:  (1) rid of the child.  (2) timestamp of child.
           53  +  ** (3) True to propagate or false to block.
           54  +  */
    48     55     db_prepare(&s, 
    49     56        "SELECT cid, plink.mtime,"
    50     57        "       coalesce(srcid=0 AND tagxref.mtime<:mtime, %d) AS doit"
    51     58        "  FROM plink LEFT JOIN tagxref ON cid=rid AND tagid=%d"
    52     59        " WHERE pid=:pid AND isprim",
    53         -     tagType!=0, tagid
           60  +     tagType==2, tagid
    54     61     );
    55     62     db_bind_double(&s, ":mtime", mtime);
           63  +
    56     64     if( tagType==2 ){
           65  +    /* Set the propagated tag marker on checkin :rid */
    57     66       db_prepare(&ins,
    58     67          "REPLACE INTO tagxref(tagid, tagtype, srcid, origid, value, mtime, rid)"
    59     68          "VALUES(%d,2,0,%d,%Q,:mtime,:rid)",
    60     69          tagid, origId, zValue
    61     70       );
    62     71       db_bind_double(&ins, ":mtime", mtime);
    63     72     }else{
           73  +    /* Remove all references to the tag from checkin :rid */
    64     74       zValue = 0;
    65     75       db_prepare(&ins,
    66     76          "DELETE FROM tagxref WHERE tagid=%d AND rid=:rid", tagid
    67     77       );
    68     78     }
    69     79     if( tagid==TAG_BGCOLOR ){
    70     80       db_prepare(&eventupdate,
    71     81         "UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
    72     82       );
    73     83     }
    74         -  while( (pid = pqueue_extract(&queue))!=0 ){
           84  +  while( (pid = pqueuex_extract(&queue, 0))!=0 ){
    75     85       db_bind_int(&s, ":pid", pid);
    76     86       while( db_step(&s)==SQLITE_ROW ){
    77     87         int doit = db_column_int(&s, 2);
    78     88         if( doit ){
    79     89           int cid = db_column_int(&s, 0);
    80     90           double mtime = db_column_double(&s, 1);
    81         -        pqueue_insert(&queue, cid, mtime);
           91  +        pqueuex_insert(&queue, cid, mtime, 0);
    82     92           db_bind_int(&ins, ":rid", cid);
    83     93           db_step(&ins);
    84     94           db_reset(&ins);
    85     95           if( tagid==TAG_BGCOLOR ){
    86     96             db_bind_int(&eventupdate, ":rid", cid);
    87     97             db_step(&eventupdate);
    88     98             db_reset(&eventupdate);
    89     99           }
          100  +        if( tagid==TAG_BRANCH ){
          101  +          leaf_eventually_check(cid);
          102  +        }
    90    103         }
    91    104       }
    92    105       db_reset(&s);
    93    106     }
    94         -  pqueue_clear(&queue);
          107  +  pqueuex_clear(&queue);
    95    108     db_finalize(&ins);
    96    109     db_finalize(&s);
    97    110     if( tagid==TAG_BGCOLOR ){
    98    111       db_finalize(&eventupdate);
    99    112     }
   100    113   }
   101    114   
   102    115   /*
   103         -** Propagate all propagatable tags in pid to its children.
          116  +** Propagate all propagatable tags in pid to the children of pid.
   104    117   */
   105    118   void tag_propagate_all(int pid){
   106    119     Stmt q;
   107    120     db_prepare(&q,
   108    121        "SELECT tagid, tagtype, mtime, value, origid FROM tagxref"
   109         -     " WHERE rid=%d"
   110         -     "   AND (tagtype=0 OR tagtype=2)",
          122  +     " WHERE rid=%d",
   111    123        pid
   112    124     );
   113    125     while( db_step(&q)==SQLITE_ROW ){
   114    126       int tagid = db_column_int(&q, 0);
   115    127       int tagtype = db_column_int(&q, 1);
   116    128       double mtime = db_column_double(&q, 2);
   117    129       const char *zValue = db_column_text(&q, 3);
   118    130       int origid = db_column_int(&q, 4);
          131  +    if( tagtype==1 ) tagtype = 0;
   119    132       tag_propagate(pid, tagid, tagtype, origid, zValue, mtime);
   120    133     }
   121    134     db_finalize(&q);
   122    135   }
   123    136   
   124    137   /*
   125    138   ** Get a tagid for the given TAG.  Create a new tag if necessary
................................................................................
   172    185       "REPLACE INTO tagxref(tagid,tagtype,srcId,origid,value,mtime,rid)"
   173    186       " VALUES(%d,%d,%d,%d,%Q,:mtime,%d)",
   174    187       tagid, tagtype, srcId, rid, zValue, rid
   175    188     );
   176    189     db_bind_double(&s, ":mtime", mtime);
   177    190     db_step(&s);
   178    191     db_finalize(&s);
          192  +  if( tagid==TAG_BRANCH ) leaf_eventually_check(rid);
   179    193     if( tagtype==0 ){
   180    194       zValue = 0;
   181    195     }
   182    196     zCol = 0;
   183    197     switch( tagid ){
   184    198       case TAG_BGCOLOR: {
   185    199         zCol = "bgcolor";
................................................................................
   189    203         zCol = "ecomment";
   190    204         break;
   191    205       }
   192    206       case TAG_USER: {
   193    207         zCol = "euser";
   194    208         break;
   195    209       }
          210  +    case TAG_PRIVATE: {
          211  +      db_multi_exec(
          212  +        "INSERT OR IGNORE INTO private(rid) VALUES(%d);",
          213  +        rid
          214  +      );
          215  +    }
   196    216     }
   197    217     if( zCol ){
   198    218       db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
   199    219       if( tagid==TAG_COMMENT ){
   200    220         char *zCopy = mprintf("%s", zValue);
   201    221         wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
   202    222         free(zCopy);
   203    223       }
   204    224     }
   205    225     if( tagid==TAG_DATE ){
   206         -    db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
          226  +    db_multi_exec("UPDATE event "
          227  +                  "   SET mtime=julianday(%Q),"
          228  +                  "       omtime=coalesce(omtime,mtime)"
          229  +                  " WHERE objid=%d",
   207    230                     zValue, rid);
   208    231     }
   209         -  if( tagtype==0 || tagtype==2 ){
   210         -    tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
   211         -  }
          232  +  if( tagtype==1 ) tagtype = 0;
          233  +  tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
   212    234     return tagid;
   213    235   }
   214    236   
   215    237   
   216    238   /*
   217    239   ** COMMAND: test-tag
   218    240   ** %fossil test-tag (+|*|-)TAGNAME ARTIFACT-ID ?VALUE?
................................................................................
   256    278   ** or cancels a tag.
   257    279   */
   258    280   void tag_add_artifact(
   259    281     const char *zPrefix,        /* Prefix to prepend to tag name */
   260    282     const char *zTagname,       /* The tag to add or cancel */
   261    283     const char *zObjName,       /* Name of object attached to */
   262    284     const char *zValue,         /* Value for the tag.  Might be NULL */
   263         -  int tagtype                 /* 0:cancel 1:singleton 2:propagated */
          285  +  int tagtype,                /* 0:cancel 1:singleton 2:propagated */
          286  +  const char *zDateOvrd,      /* Override date string */
          287  +  const char *zUserOvrd       /* Override user name */
   264    288   ){
   265    289     int rid;
   266    290     int nrid;
   267    291     char *zDate;
   268    292     Blob uuid;
   269    293     Blob ctrl;
   270    294     Blob cksum;
   271    295     static const char zTagtype[] = { '-', '+', '*' };
   272    296   
   273    297     assert( tagtype>=0 && tagtype<=2 );
   274    298     user_select();
   275    299     blob_zero(&uuid);
   276    300     blob_append(&uuid, zObjName, -1);
   277         -  if( name_to_uuid(&uuid, 9) ){
          301  +  if( name_to_uuid(&uuid, 9, "*") ){
   278    302       fossil_fatal("%s", g.zErrMsg);
   279    303       return;
   280    304     }
   281    305     rid = name_to_rid(blob_str(&uuid));
   282    306     g.markPrivate = content_is_private(rid);
   283    307     blob_zero(&ctrl);
   284    308   
................................................................................
   287    311       fossil_fatal(
   288    312          "invalid tag name \"%s\" - might be confused with"
   289    313          " a hexadecimal artifact ID",
   290    314          zTagname
   291    315       );
   292    316     }
   293    317   #endif
   294         -  zDate = db_text(0, "SELECT datetime('now')");
   295         -  zDate[10] = 'T';
          318  +  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
   296    319     blob_appendf(&ctrl, "D %s\n", zDate);
   297    320     blob_appendf(&ctrl, "T %c%s%F %b",
   298    321                  zTagtype[tagtype], zPrefix, zTagname, &uuid);
   299    322     if( tagtype>0 && zValue && zValue[0] ){
   300    323       blob_appendf(&ctrl, " %F\n", zValue);
   301    324     }else{
   302    325       blob_appendf(&ctrl, "\n");
   303    326     }
   304         -  blob_appendf(&ctrl, "U %F\n", g.zLogin);
          327  +  blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
   305    328     md5sum_blob(&ctrl, &cksum);
   306    329     blob_appendf(&ctrl, "Z %b\n", &cksum);
   307         -  nrid = content_put(&ctrl, 0, 0);
          330  +  nrid = content_put(&ctrl);
   308    331     manifest_crosslink(nrid, &ctrl);
          332  +  assert( blob_is_reset(&ctrl) );
   309    333   }
   310    334   
   311    335   /*
   312    336   ** COMMAND: tag
   313    337   ** Usage: %fossil tag SUBCOMMAND ...
   314    338   **
   315    339   ** Run various subcommands to control tags and properties
   316    340   **
   317    341   **     %fossil tag add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?
   318    342   **
   319    343   **         Add a new tag or property to CHECK-IN. The tag will
   320    344   **         be usable instead of a CHECK-IN in commands such as
   321    345   **         update and merge.  If the --propagate flag is present,
   322         -**         the tag value propages to all descendants of CHECK-IN
          346  +**         the tag value propagates to all descendants of CHECK-IN
   323    347   **
   324    348   **     %fossil tag cancel ?--raw? TAGNAME CHECK-IN
   325    349   **
   326    350   **         Remove the tag TAGNAME from CHECK-IN, and also remove
   327    351   **         the propagation of the tag to any descendants.
   328    352   **
   329         -**     %fossil tag find ?--raw? TAGNAME
          353  +**     %fossil tag find ?--raw? ?--type TYPE? TAGNAME
   330    354   **
   331         -**         List all check-ins that use TAGNAME
          355  +**         List all objects that use TAGNAME.  TYPE can be "ci" for
          356  +**         checkins or "e" for events.
   332    357   **
   333    358   **     %fossil tag list ?--raw? ?CHECK-IN?
   334    359   **
   335    360   **         List all tags, or if CHECK-IN is supplied, list
   336    361   **         all tags and their values for CHECK-IN.
   337    362   **
   338    363   ** The option --raw allows the manipulation of all types of tags
................................................................................
   350    375   ** will be taken as an artifact or baseline ID and fossil will
   351    376   ** probably complain that no such revision was found. However
   352    377   **
   353    378   **   fossil update tag:decaf
   354    379   **
   355    380   ** will assume that "decaf" is a tag/branch name.
   356    381   **
          382  +** only allow --date-override and --user-override in 
          383  +**   %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\
          384  +**                   --user-override user 
          385  +** in order to import history from other scm systems
   357    386   */
   358    387   void tag_cmd(void){
   359    388     int n;
   360    389     int fRaw = find_option("raw","",0)!=0;
   361    390     int fPropagate = find_option("propagate","",0)!=0;
   362    391     const char *zPrefix = fRaw ? "" : "sym-";
   363    392   
   364         -  db_find_and_open_repository(1);
          393  +  db_find_and_open_repository(0, 0);
   365    394     if( g.argc<3 ){
   366    395       goto tag_cmd_usage;
   367    396     }
   368    397     n = strlen(g.argv[2]);
   369    398     if( n==0 ){
   370    399       goto tag_cmd_usage;
   371    400     }
   372    401   
   373    402     if( strncmp(g.argv[2],"add",n)==0 ){
   374    403       char *zValue;
          404  +    const char *zDateOvrd = find_option("date-override",0,1);
          405  +    const char *zUserOvrd = find_option("user-override",0,1);
   375    406       if( g.argc!=5 && g.argc!=6 ){
   376    407         usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?");
   377    408       }
   378    409       zValue = g.argc==6 ? g.argv[5] : 0;
   379    410       db_begin_transaction();
   380         -    tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, 1+fPropagate);
          411  +    tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue,
          412  +                     1+fPropagate,zDateOvrd,zUserOvrd);
   381    413       db_end_transaction(0);
   382    414     }else
   383    415   
   384    416     if( strncmp(g.argv[2],"branch",n)==0 ){
   385    417       fossil_fatal("the \"fossil tag branch\" command is discontinued\n"
   386    418                    "Use the \"fossil branch new\" command instead.");
   387    419     }else
   388    420   
   389    421     if( strncmp(g.argv[2],"cancel",n)==0 ){
   390    422       if( g.argc!=5 ){
   391    423         usage("cancel ?--raw? TAGNAME CHECK-IN");
   392    424       }
   393    425       db_begin_transaction();
   394         -    tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0);
          426  +    tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0, 0, 0);
   395    427       db_end_transaction(0);
   396    428     }else
   397    429   
   398    430     if( strncmp(g.argv[2],"find",n)==0 ){
   399    431       Stmt q;
          432  +    const char *zType = find_option("type","t",1);
          433  +    if( zType==0 || zType[0]==0 ) zType = "*";
   400    434       if( g.argc!=4 ){
   401    435         usage("find ?--raw? TAGNAME");
   402    436       }
   403    437       if( fRaw ){
   404    438         db_prepare(&q,
   405    439           "SELECT blob.uuid FROM tagxref, blob"
   406    440           " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
   407    441           "   AND tagxref.tagtype>0"
   408    442           "   AND blob.rid=tagxref.rid",
   409    443           g.argv[3]
   410    444         );
   411    445         while( db_step(&q)==SQLITE_ROW ){
   412         -        printf("%s\n", db_column_text(&q, 0));
          446  +        fossil_print("%s\n", db_column_text(&q, 0));
   413    447         }
   414    448         db_finalize(&q);
   415    449       }else{
   416    450         int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
   417    451                            g.argv[3]);
   418    452         if( tagid>0 ){
   419    453           db_prepare(&q,
   420    454             "%s"
          455  +          "  AND event.type GLOB '%q'"
   421    456             "  AND blob.rid IN ("
   422    457                       " SELECT rid FROM tagxref"
   423    458                       "  WHERE tagtype>0 AND tagid=%d"
   424    459                       ")"
   425    460             " ORDER BY event.mtime DESC",
   426         -          timeline_query_for_tty(), tagid
          461  +          timeline_query_for_tty(), zType, tagid
   427    462           );
   428         -        print_timeline(&q, 2000);
          463  +        print_timeline(&q, 2000, 0);
   429    464           db_finalize(&q);
   430    465         }
   431    466       }
   432    467     }else
   433    468   
   434    469     if( strncmp(g.argv[2],"list",n)==0 ){
   435    470       Stmt q;
................................................................................
   440    475           "               WHERE tagid=tag.tagid"
   441    476           "                 AND tagtype>0)"
   442    477           " ORDER BY tagname"
   443    478         );
   444    479         while( db_step(&q)==SQLITE_ROW ){
   445    480           const char *zName = db_column_text(&q, 0);
   446    481           if( fRaw ){
   447         -          printf("%s\n", zName);
          482  +          fossil_print("%s\n", zName);
   448    483           }else if( strncmp(zName, "sym-", 4)==0 ){
   449         -          printf("%s\n", &zName[4]);
          484  +          fossil_print("%s\n", &zName[4]);
   450    485           }
   451    486         }
   452    487         db_finalize(&q);
   453    488       }else if( g.argc==4 ){
   454    489         int rid = name_to_rid(g.argv[3]);
   455    490         db_prepare(&q,
   456    491           "SELECT tagname, value FROM tagxref, tag"
................................................................................
   464    499           const char *zName = db_column_text(&q, 0);
   465    500           const char *zValue = db_column_text(&q, 1);
   466    501           if( fRaw==0 ){
   467    502             if( strncmp(zName, "sym-", 4)!=0 ) continue;
   468    503             zName += 4;
   469    504           }
   470    505           if( zValue && zValue[0] ){
   471         -          printf("%s=%s\n", zName, zValue);
          506  +          fossil_print("%s=%s\n", zName, zValue);
   472    507           }else{
   473         -          printf("%s\n", zName);
          508  +          fossil_print("%s\n", zName);
   474    509           }
   475    510         }
   476    511         db_finalize(&q);
   477    512       }else{
   478    513         usage("tag list ?CHECK-IN?");
   479    514       }
   480    515     }else
................................................................................
   492    527   /*
   493    528   ** WEBPAGE: /taglist
   494    529   */
   495    530   void taglist_page(void){
   496    531     Stmt q;
   497    532   
   498    533     login_check_credentials();
   499         -  if( !g.okRead ){
          534  +  if( !g.perm.Read ){
   500    535       login_needed();
   501    536     }
   502    537     login_anonymous_available();
   503    538     style_header("Tags");
   504    539     style_submenu_element("Timeline", "Timeline", "tagtimeline");
   505    540     @ <h2>Non-propagating tags:</h2>
   506    541     db_prepare(&q,
................................................................................
   511    546       "                 AND tagtype=1)"
   512    547       " AND tagname GLOB 'sym-*'"
   513    548       " ORDER BY tagname"
   514    549     );
   515    550     @ <ul>
   516    551     while( db_step(&q)==SQLITE_ROW ){
   517    552       const char *zName = db_column_text(&q, 0);
   518         -    if( g.okHistory ){
   519         -      @ <li><a href=%s(g.zBaseURL)/timeline?t=%T(zName)>%h(zName)</a></li>
          553  +    if( g.perm.Hyperlink ){
          554  +      @ <li>%z(xhref("class='taglink'","%R/timeline?t=%T",zName))
          555  +      @ %h(zName)</a></li>
   520    556       }else{
   521         -      @ <li><strong>%h(zName)</strong></li>
          557  +      @ <li><span class="tagDsp">%h(zName)</span></li>
   522    558       }
   523    559     }
   524    560     @ </ul>
   525    561     db_finalize(&q);
   526    562     style_footer();
   527    563   }
   528    564   
   529         -/*
   530         -** Draw the names of all tags added to check-in rid.  Only tags
   531         -** that are directly applied to rid are named.  Propagated tags
   532         -** are omitted.
   533         -*/
   534         -static void tagtimeline_extra(int rid){
   535         -  Stmt q;
   536         -  db_prepare(&q, 
   537         -    "SELECT substr(tagname,5) FROM tagxref, tag"
   538         -    " WHERE tagxref.rid=%d"
   539         -    "   AND tagxref.tagid=tag.tagid"
   540         -    "   AND tagxref.tagtype>0 AND tagxref.srcid>0"
   541         -    "   AND tag.tagname GLOB 'sym-*'",
   542         -    rid
   543         -  );
   544         -  while( db_step(&q)==SQLITE_ROW ){
   545         -    const char *zTagName = db_column_text(&q, 0);
   546         -    if( g.okHistory ){
   547         -      @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[%h(zTagName)]</a>
   548         -    }else{
   549         -      @ <b>[%h(zTagName)]</b>
   550         -    }
   551         -  }
   552         -  db_finalize(&q);
   553         -}
   554         -
   555    565   /*
   556    566   ** WEBPAGE: /tagtimeline
   557    567   */
   558    568   void tagtimeline_page(void){
   559    569     Stmt q;
   560    570   
   561    571     login_check_credentials();
   562         -  if( !g.okRead ){ login_needed(); return; }
          572  +  if( !g.perm.Read ){ login_needed(); return; }
   563    573   
   564    574     style_header("Tagged Check-ins");
   565    575     style_submenu_element("List", "List", "taglist");
   566    576     login_anonymous_available();
   567    577     @ <h2>Check-ins with non-propagating tags:</h2>
   568    578     db_prepare(&q,
   569    579       "%s AND blob.rid IN (SELECT rid FROM tagxref"
   570    580       "                     WHERE tagtype=1 AND srcid>0"
   571    581       "                       AND tagid IN (SELECT tagid FROM tag "
   572    582       "                                      WHERE tagname GLOB 'sym-*'))"
   573    583       " ORDER BY event.mtime DESC",
   574    584       timeline_query_for_www()
   575    585     );
   576         -  www_print_timeline(&q, 0, tagtimeline_extra);
          586  +  www_print_timeline(&q, 0, 0, 0, 0);
   577    587     db_finalize(&q);
   578         -  @ <br clear="both">
   579         -  @ <script>
          588  +  @ <br />
          589  +  @ <script  type="text/JavaScript">
   580    590     @ function xin(id){
   581    591     @ }
   582    592     @ function xout(id){
   583    593     @ }
   584    594     @ </script>
   585    595     style_footer();
   586    596   }

Added src/tar.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to generate tarballs.
           19  +*/
           20  +#include <assert.h>
           21  +#include <zlib.h>
           22  +#include "config.h"
           23  +#include "tar.h"
           24  +
           25  +/*
           26  +** State information for the tarball builder.
           27  +*/
           28  +static struct tarball_t {
           29  +  unsigned char *aHdr;      /* Space for building headers */
           30  +  char *zSpaces;            /* Spaces for padding */
           31  +  char *zPrevDir;           /* Name of directory for previous entry */
           32  +  int nPrevDirAlloc;        /* size of zPrevDir */
           33  +  Blob pax;                 /* PAX data */
           34  +} tball;
           35  +
           36  +
           37  +/*
           38  +** field lengths of 'ustar' name and prefix fields.
           39  +*/
           40  +#define USTAR_NAME_LEN    100
           41  +#define USTAR_PREFIX_LEN  155
           42  +
           43  +
           44  +/*
           45  +** Begin the process of generating a tarball.
           46  +**
           47  +** Initialize the GZIP compressor and the table of directory names.
           48  +*/
           49  +static void tar_begin(sqlite3_int64 mTime){
           50  +  assert( tball.aHdr==0 );
           51  +  tball.aHdr = fossil_malloc(512+512);
           52  +  memset(tball.aHdr, 0, 512+512);
           53  +  tball.zSpaces = (char*)&tball.aHdr[512];
           54  +  /* zPrevDir init */
           55  +  tball.zPrevDir = NULL;
           56  +  tball.nPrevDirAlloc = 0;
           57  +  /* scratch buffer init */
           58  +  blob_zero(&tball.pax);
           59  +
           60  +  memcpy(&tball.aHdr[108], "0000000", 8);  /* Owner ID */
           61  +  memcpy(&tball.aHdr[116], "0000000", 8);  /* Group ID */
           62  +  memcpy(&tball.aHdr[257], "ustar\00000", 8);  /* POSIX.1 format */
           63  +  memcpy(&tball.aHdr[265], "nobody", 7);   /* Owner name */
           64  +  memcpy(&tball.aHdr[297], "nobody", 7);   /* Group name */
           65  +  gzip_begin(mTime);
           66  +  db_multi_exec(
           67  +    "CREATE TEMP TABLE dir(name UNIQUE);"
           68  +  );
           69  +}
           70  +
           71  +
           72  +/*
           73  +** verify that lla characters in 'zName' are in the
           74  +** ISO646 (=ASCII) character set.
           75  +*/
           76  +static int is_iso646_name(
           77  +  const char *zName,     /* file path */
           78  +  int nName              /* path length */
           79  +){
           80  +  int i;
           81  +  for(i = 0; i < nName; i++){
           82  +    unsigned char c = (unsigned char)zName[i];
           83  +    if( c>0x7e ) return 0;
           84  +  }
           85  +  return 1;
           86  +}
           87  +
           88  +
           89  +/*
           90  +**   copy string pSrc into pDst, truncating or padding with 0 if necessary
           91  +*/
           92  +static void padded_copy(
           93  +  char *pDest,
           94  +  int nDest,
           95  +  const char *pSrc,
           96  +  int nSrc
           97  +){
           98  +  if(nSrc >= nDest){
           99  +    memcpy(pDest, pSrc, nDest);
          100  +  }else{
          101  +    memcpy(pDest, pSrc, nSrc);
          102  +    memset(&pDest[nSrc], 0, nDest - nSrc);
          103  +  }
          104  +}
          105  +
          106  +
          107  +
          108  +/******************************************************************************
          109  +**
          110  +** The 'tar' format has evolved over time. Initially the name was stored
          111  +** in a 100 byte null-terminated field 'name'. File path names were
          112  +** limited to 99 bytes.
          113  +**
          114  +** The Posix.1 'ustar' format added a 155 byte field 'prefix', allowing
          115  +** for up to 255 characters to be stored. The full file path is formed by
          116  +** concatenating the field 'prefix', a slash, and the field 'name'. This
          117  +** gives some measure of compatibility with programs that only understand
          118  +** the oldest format.
          119  +**
          120  +** The latest Posix extension is called the 'pax Interchange Format'.
          121  +** It removes all the limitations of the previous two formats by allowing
          122  +** the storage of arbitrary-length attributes in a separate object that looks
          123  +** like a file to programs that do not understand this extension. So the
          124  +** contents of the 'name' and 'prefix' fields should contain values that allow
          125  +** versions of tar that do not understand this extension to still do
          126  +** something useful.
          127  +**
          128  +******************************************************************************/
          129  +
          130  +/*
          131  +** The position we use to split a file path into the 'name' and 'prefix'
          132  +** fields needs to meet the following criteria:
          133  +**
          134  +**   - not at the beginning or end of the string
          135  +**   - the position must contain a slash
          136  +**   - no more than 100 characters follow the slash
          137  +**   - no more than 155 characters precede it
          138  +**
          139  +** The routine 'find_split_pos' finds a split position. It will meet the
          140  +** criteria of listed above if such a position exists. If no such
          141  +** position exists it generates one that useful for generating the
          142  +** values used for backward compatibility.
          143  +*/
          144  +static int find_split_pos(
          145  +  const char *zName,     /* file path */
          146  +  int nName              /* path length */
          147  +){
          148  +  int i, split = 0;
          149  +  /* only search if the string needs splitting */
          150  +  if(nName > USTAR_NAME_LEN){
          151  +    for(i = 1; i+1 < nName; i++)
          152  +      if(zName[i] == '/'){
          153  +        split = i+1;
          154  +        /* if the split position is within USTAR_NAME_LEN bytes from
          155  +         * the end we can quit */
          156  +        if(nName - split <= USTAR_NAME_LEN) break;
          157  +      }
          158  +  }
          159  +  return split;
          160  +}
          161  +
          162  +
          163  +/*
          164  +** attempt to split the file name path to meet 'ustar' header
          165  +** criteria.
          166  +*/
          167  +static int tar_split_path(
          168  +  const char *zName,     /* path */
          169  +  int nName,             /* path length */
          170  +  char *pName,           /* name field */
          171  +  char *pPrefix          /* prefix field */
          172  +){
          173  +  int split = find_split_pos(zName, nName);
          174  +  /* check whether both pieces fit */
          175  +  if(nName - split > USTAR_NAME_LEN || split > USTAR_PREFIX_LEN+1){
          176  +    return 0; /* no */
          177  +  }
          178  +
          179  +  /* extract name */
          180  +  padded_copy(pName, USTAR_NAME_LEN, &zName[split], nName - split);
          181  +
          182  +  /* extract prefix */
          183  +  padded_copy(pPrefix, USTAR_PREFIX_LEN, zName, (split > 0 ? split - 1 : 0));
          184  +
          185  +  return 1; /* success */
          186  +}
          187  +
          188  +
          189  +/*
          190  +** When using an extension header we still need to put something
          191  +** reasonable in the name and prefix fields. This is probably as
          192  +** good as it gets.
          193  +*/
          194  +static void approximate_split_path(
          195  +  const char *zName,     /* path */
          196  +  int nName,             /* path length */
          197  +  char *pName,           /* name field */
          198  +  char *pPrefix,         /* prefix field */
          199  +  int bHeader            /* is this a 'x' type tar header? */
          200  +){
          201  +  int split;
          202  +
          203  +  /* if this is a Pax Interchange header prepend "PaxHeader/"
          204  +  ** so we can tell files apart from metadata */
          205  +  if( bHeader ){
          206  +    blob_reset(&tball.pax);
          207  +    blob_appendf(&tball.pax, "PaxHeader/%*.*s", nName, nName, zName);
          208  +    zName = blob_buffer(&tball.pax);
          209  +    nName = blob_size(&tball.pax);
          210  +  }
          211  +
          212  +  /* find the split position */
          213  +  split = find_split_pos(zName, nName);
          214  +
          215  +  /* extract a name, truncate if needed */
          216  +  padded_copy(pName, USTAR_NAME_LEN, &zName[split], nName - split);
          217  +
          218  +  /* extract a prefix field, truncate when needed */
          219  +  padded_copy(pPrefix, USTAR_PREFIX_LEN, zName, (split > 0 ? split-1 : 0));
          220  +}
          221  +
          222  +
          223  +/*
          224  +** add a Pax Interchange header to the scratch buffer
          225  +**
          226  +** format: <length> <key>=<value>\n
          227  +** the tricky part is that each header contains its own
          228  +** size in decimal, counting that length.
          229  +*/
          230  +static void add_pax_header(
          231  +  const char *zField,
          232  +  const char *zValue,
          233  +  int nValue
          234  +){
          235  +  /* calculate length without length field */
          236  +  int blen = strlen(zField) + nValue + 3;
          237  +  /* calculate the length of the length field */
          238  +  int next10 = 1;
          239  +  int n;
          240  +  for(n = blen; n > 0; ){
          241  +    blen++; next10 *= 10;
          242  +    n /= 10;
          243  +  }
          244  +  /* adding the length extended the length field? */
          245  +  if(blen > next10){
          246  +    blen++;
          247  +  }
          248  +  /* build the string */
          249  +  blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue);
          250  +  /* this _must_ be right */
          251  +  if(blob_size(&tball.pax) != blen){
          252  +    fossil_fatal("internal error: PAX tar header has bad length");
          253  +  }
          254  +}
          255  +
          256  +
          257  +/*
          258  +** set the header type, calculate the checksum and output
          259  +** the header
          260  +*/
          261  +static void cksum_and_write_header(
          262  +  char cType
          263  +){
          264  +  unsigned int cksum = 0;
          265  +  int i;
          266  +  memset(&tball.aHdr[148], ' ', 8);
          267  +  tball.aHdr[156] = cType;
          268  +  for(i=0; i<512; i++) cksum += tball.aHdr[i];
          269  +  sqlite3_snprintf(8, (char*)&tball.aHdr[148], "%07o", cksum);
          270  +  tball.aHdr[155] = 0;
          271  +  gzip_step((char*)tball.aHdr, 512);
          272  +}
          273  +
          274  +
          275  +/*
          276  +** Build a header for a file or directory and write that header
          277  +** into the growing tarball.
          278  +*/
          279  +static void tar_add_header(
          280  +  const char *zName,     /* Name of the object */
          281  +  int nName,             /* Number of characters in zName */
          282  +  int iMode,             /* Mode.  0644 or 0755 */
          283  +  unsigned int mTime,    /* File modification time */
          284  +  int iSize,             /* Size of the object in bytes */
          285  +  char cType             /* Type of object:  
          286  +                            '0'==file. '2'==symlink. '5'==directory */
          287  +){
          288  +  /* set mode and modification time */
          289  +  sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode);
          290  +  sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime);
          291  +
          292  +  /* see if we need to output a Pax Interchange Header */
          293  +  if( !is_iso646_name(zName, nName)
          294  +   || !tar_split_path(zName, nName, (char*)tball.aHdr, (char*)&tball.aHdr[345])
          295  +  ){
          296  +    int lastPage;
          297  +    /* add a file name for interoperability with older programs */
          298  +    approximate_split_path(zName, nName, (char*)tball.aHdr,
          299  +                           (char*)&tball.aHdr[345], 1);
          300  +
          301  +    /* generate the Pax Interchange path header */
          302  +    blob_reset(&tball.pax);
          303  +    add_pax_header("path", zName, nName);
          304  +
          305  +    /* set the header length, and write the header */
          306  +    sqlite3_snprintf(12, (char*)&tball.aHdr[124], "%011o",
          307  +                     blob_size(&tball.pax));
          308  +    cksum_and_write_header('x');
          309  +
          310  +    /* write the Pax Interchange data */
          311  +    gzip_step(blob_buffer(&tball.pax), blob_size(&tball.pax));
          312  +    lastPage = blob_size(&tball.pax) % 512;
          313  +    if( lastPage!=0 ){
          314  +      gzip_step(tball.zSpaces, 512 - lastPage);
          315  +    }
          316  +
          317  +    /* generate an approximate path for the regular header */
          318  +    approximate_split_path(zName, nName, (char*)tball.aHdr,
          319  +                           (char*)&tball.aHdr[345], 0);
          320  +  }
          321  +  /* set the size */
          322  +  sqlite3_snprintf(12, (char*)&tball.aHdr[124], "%011o", iSize);
          323  +
          324  +  /* write the regular header */
          325  +  cksum_and_write_header(cType);
          326  +}
          327  +
          328  +
          329  +/*
          330  +** Recursively add an directory entry for the given file if those
          331  +** directories have not previously been seen.
          332  +*/
          333  +static void tar_add_directory_of(
          334  +  const char *zName,      /* Name of directory including final "/" */
          335  +  int nName,              /* Characters in zName */
          336  +  unsigned int mTime      /* Modification time */
          337  +){
          338  +  int i;
          339  +  for(i=nName-1; i>0 && zName[i]!='/'; i--){}
          340  +  if( i<=0 ) return;
          341  +  if( i < tball.nPrevDirAlloc && tball.zPrevDir[i]==0 &&
          342  +        memcmp(tball.zPrevDir, zName, i)==0 ) return;
          343  +  db_multi_exec("INSERT OR IGNORE INTO dir VALUES('%#q')", i, zName);
          344  +  if( sqlite3_changes(g.db)==0 ) return;
          345  +  tar_add_directory_of(zName, i-1, mTime);
          346  +  tar_add_header(zName, i, 0755, mTime, 0, '5');
          347  +  if( i >= tball.nPrevDirAlloc ){
          348  +    int nsize = tball.nPrevDirAlloc * 2;
          349  +    if(i+1 > nsize)
          350  +      nsize = i+1;
          351  +    tball.zPrevDir = fossil_realloc(tball.zPrevDir, nsize);
          352  +    tball.nPrevDirAlloc = nsize;
          353  +  }
          354  +  memcpy(tball.zPrevDir, zName, i);
          355  +  tball.zPrevDir[i] = 0;
          356  +}
          357  +
          358  +
          359  +/*
          360  +** Add a single file to the growing tarball.
          361  +*/
          362  +static void tar_add_file(
          363  +  const char *zName,               /* Name of the file.  nul-terminated */
          364  +  Blob *pContent,                  /* Content of the file */
          365  +  int mPerm,                       /* 1: executable file, 2: symlink */
          366  +  unsigned int mTime               /* Last modification time of the file */
          367  +){
          368  +  int nName = strlen(zName);
          369  +  int n = blob_size(pContent);
          370  +  int lastPage;
          371  +  char cType = '0';
          372  +
          373  +  /* length check moved to tar_split_path */
          374  +  tar_add_directory_of(zName, nName, mTime);
          375  +
          376  +  /* 
          377  +   * If we have a symlink, write its destination path (which is stored in
          378  +   * pContent) into header, and set content length to 0 to avoid storing path
          379  +   * as file content in the next step.  Since 'linkname' header is limited to
          380  +   * 100 bytes (-1 byte for terminating zero), if path is greater than that,
          381  +   * store symlink as a plain-text file. (Not sure how TAR handles long links.)
          382  +   */
          383  +  if( mPerm == PERM_LNK && n <= 100 ){
          384  +    sqlite3_snprintf(100, (char*)&tball.aHdr[157], "%s", blob_str(pContent));
          385  +    cType = '2';
          386  +    n = 0;
          387  +  }
          388  +
          389  +  tar_add_header(zName, nName, ( mPerm==PERM_EXE ) ? 0755 : 0644, 
          390  +                 mTime, n, cType);
          391  +  if( n ){
          392  +    gzip_step(blob_buffer(pContent), n);
          393  +    lastPage = n % 512;
          394  +    if( lastPage!=0 ){
          395  +      gzip_step(tball.zSpaces, 512 - lastPage);
          396  +    }
          397  +  }
          398  +}
          399  +
          400  +/*
          401  +** Finish constructing the tarball.  Put the content of the tarball
          402  +** in Blob pOut.
          403  +*/
          404  +static void tar_finish(Blob *pOut){
          405  +  db_multi_exec("DROP TABLE dir");
          406  +  gzip_step(tball.zSpaces, 512);
          407  +  gzip_step(tball.zSpaces, 512);
          408  +  gzip_finish(pOut);
          409  +  fossil_free(tball.aHdr);
          410  +  tball.aHdr = 0;
          411  +  fossil_free(tball.zPrevDir);
          412  +  tball.zPrevDir = NULL;
          413  +  tball.nPrevDirAlloc = 0;
          414  +  blob_reset(&tball.pax);
          415  +}
          416  +
          417  +
          418  +/*
          419  +** COMMAND: test-tarball
          420  +**
          421  +** Generate a GZIP-compressed tarball in the file given by the first argument
          422  +** that contains files given in the second and subsequent arguments.
          423  +*/
          424  +void test_tarball_cmd(void){
          425  +  int i;
          426  +  Blob zip;
          427  +  Blob file;
          428  +  if( g.argc<3 ){
          429  +    usage("ARCHIVE FILE....");
          430  +  }
          431  +  sqlite3_open(":memory:", &g.db);
          432  +  tar_begin(0);
          433  +  for(i=3; i<g.argc; i++){
          434  +    blob_zero(&file);
          435  +    blob_read_from_file(&file, g.argv[i]);
          436  +    tar_add_file(g.argv[i], &file,
          437  +                 file_wd_perm(g.argv[i]), file_wd_mtime(g.argv[i]));
          438  +    blob_reset(&file);
          439  +  }
          440  +  tar_finish(&zip);
          441  +  blob_write_to_file(&zip, g.argv[2]);
          442  +}
          443  +
          444  +/*
          445  +** Given the RID for a checkin, construct a tarball containing
          446  +** all files in that checkin
          447  +**
          448  +** If RID is for an object that is not a real manifest, then the
          449  +** resulting tarball contains a single file which is the RID
          450  +** object.
          451  +**
          452  +** If the RID object does not exist in the repository, then
          453  +** pTar is zeroed.
          454  +**
          455  +** zDir is a "synthetic" subdirectory which all files get
          456  +** added to as part of the tarball. It may be 0 or an empty string, in
          457  +** which case it is ignored. The intention is to create a tarball which
          458  +** politely expands into a subdir instead of filling your current dir
          459  +** with source files. For example, pass a UUID or "ProjectName".
          460  +**
          461  +*/
          462  +void tarball_of_checkin(int rid, Blob *pTar, const char *zDir){
          463  +  Blob mfile, hash, file;
          464  +  Manifest *pManifest;
          465  +  ManifestFile *pFile;
          466  +  Blob filename;
          467  +  int nPrefix;
          468  +  char *zName;
          469  +  unsigned int mTime;
          470  +
          471  +  content_get(rid, &mfile);
          472  +  if( blob_size(&mfile)==0 ){
          473  +    blob_zero(pTar);
          474  +    return;
          475  +  }
          476  +  blob_zero(&hash);
          477  +  blob_zero(&filename);
          478  +
          479  +  if( zDir && zDir[0] ){
          480  +    blob_appendf(&filename, "%s/", zDir);
          481  +  }
          482  +  nPrefix = blob_size(&filename);
          483  +
          484  +  pManifest = manifest_get(rid, CFTYPE_MANIFEST);
          485  +  if( pManifest ){
          486  +    mTime = (pManifest->rDate - 2440587.5)*86400.0;
          487  +    tar_begin(mTime);
          488  +    if( db_get_boolean("manifest", 0) ){
          489  +      blob_append(&filename, "manifest", -1);
          490  +      zName = blob_str(&filename);
          491  +      tar_add_file(zName, &mfile, 0, mTime);
          492  +      sha1sum_blob(&mfile, &hash);
          493  +      blob_reset(&mfile);
          494  +      blob_append(&hash, "\n", 1);
          495  +      blob_resize(&filename, nPrefix);
          496  +      blob_append(&filename, "manifest.uuid", -1);
          497  +      zName = blob_str(&filename);
          498  +      tar_add_file(zName, &hash, 0, mTime);
          499  +      blob_reset(&hash);
          500  +    }
          501  +    manifest_file_rewind(pManifest);
          502  +    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
          503  +      int fid = uuid_to_rid(pFile->zUuid, 0);
          504  +      if( fid ){
          505  +        content_get(fid, &file);
          506  +        blob_resize(&filename, nPrefix);
          507  +        blob_append(&filename, pFile->zName, -1);
          508  +        zName = blob_str(&filename);
          509  +        tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime);
          510  +        blob_reset(&file);
          511  +      }
          512  +    }
          513  +  }else{
          514  +    sha1sum_blob(&mfile, &hash);
          515  +    blob_append(&filename, blob_str(&hash), 16);
          516  +    zName = blob_str(&filename);
          517  +    mTime = db_int64(0, "SELECT (julianday('now') -  2440587.5)*86400.0;");
          518  +    tar_begin(mTime);
          519  +    tar_add_file(zName, &mfile, 0, mTime);
          520  +  }
          521  +  manifest_destroy(pManifest);
          522  +  blob_reset(&mfile);
          523  +  blob_reset(&filename);
          524  +  tar_finish(pTar);
          525  +}
          526  +
          527  +/*
          528  +** COMMAND: tarball*
          529  +**
          530  +** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] [-R|--repository REPO]
          531  +**
          532  +** Generate a compressed tarball for a specified version.  If the --name
          533  +** option is used, its argument becomes the name of the top-level directory
          534  +** in the resulting tarball.  If --name is omitted, the top-level directory
          535  +** named is derived from the project name, the check-in date and time, and
          536  +** the artifact ID of the check-in.
          537  +*/
          538  +void tarball_cmd(void){
          539  +  int rid;
          540  +  Blob tarball;
          541  +  const char *zName;
          542  +  zName = find_option("name", 0, 1);
          543  +  db_find_and_open_repository(0, 0);
          544  +  if( g.argc!=4 ){
          545  +    usage("VERSION OUTPUTFILE");
          546  +  }
          547  +  rid = name_to_typed_rid(g.argv[2], "ci");
          548  +  if( rid==0 ){
          549  +    fossil_fatal("Checkin not found: %s", g.argv[2]);
          550  +    return;
          551  +  }
          552  +
          553  +  if( zName==0 ){
          554  +    zName = db_text("default-name",
          555  +       "SELECT replace(%Q,' ','_') "
          556  +          " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
          557  +          " || substr(blob.uuid, 1, 10)"
          558  +       "  FROM event, blob"
          559  +       " WHERE event.objid=%d"
          560  +       "   AND blob.rid=%d",
          561  +       db_get("project-name", "unnamed"), rid, rid
          562  +    );
          563  +  }
          564  +  tarball_of_checkin(rid, &tarball, zName);
          565  +  blob_write_to_file(&tarball, g.argv[3]);
          566  +  blob_reset(&tarball);
          567  +}
          568  +
          569  +/*
          570  +** WEBPAGE: tarball
          571  +** URL: /tarball/RID.tar.gz
          572  +**
          573  +** Generate a compressed tarball for a checkin.
          574  +** Return that tarball as the HTTP reply content.
          575  +*/
          576  +void tarball_page(void){
          577  +  int rid;
          578  +  char *zName, *zRid;
          579  +  int nName, nRid;
          580  +  Blob tarball;
          581  +
          582  +  login_check_credentials();
          583  +  if( !g.perm.Zip ){ login_needed(); return; }
          584  +  zName = mprintf("%s", PD("name",""));
          585  +  nName = strlen(zName);
          586  +  zRid = mprintf("%s", PD("uuid","trunk"));
          587  +  nRid = strlen(zRid);
          588  +  if( nName>7 && fossil_strcmp(&zName[nName-7], ".tar.gz")==0 ){
          589  +    /* Special case:  Remove the ".tar.gz" suffix.  */
          590  +    nName -= 7;
          591  +    zName[nName] = 0;
          592  +  }else{
          593  +    /* If the file suffix is not ".tar.gz" then just remove the
          594  +    ** suffix up to and including the last "." */
          595  +    for(nName=strlen(zName)-1; nName>5; nName--){
          596  +      if( zName[nName]=='.' ){
          597  +        zName[nName] = 0;
          598  +        break;
          599  +      }
          600  +    }
          601  +  }
          602  +  rid = name_to_typed_rid(nRid?zRid:zName, "ci");
          603  +  if( rid==0 ){
          604  +    @ Not found
          605  +    return;
          606  +  }
          607  +  if( nRid==0 && nName>10 ) zName[10] = 0;
          608  +  tarball_of_checkin(rid, &tarball, zName);
          609  +  free( zName );
          610  +  free( zRid );
          611  +  cgi_set_content(&tarball);
          612  +  cgi_set_content_type("application/x-compressed");
          613  +}

Changes to src/th.c.

    75     75   ** is stored in Th_Variable.nRef.
    76     76   **
    77     77   ** For scalar variables, Th_Variable.zData is never 0. Th_Variable.nData
    78     78   ** stores the number of bytes in the value pointed to by zData.
    79     79   **
    80     80   ** For an array variable, Th_Variable.zData is 0 and pHash points to
    81     81   ** a hash table mapping between array key name (a th1 string) and
    82         -** a the pointer to the Th_Variable structure holding the scalar
           82  +** a pointer to the Th_Variable structure holding the scalar
    83     83   ** value.
    84     84   */
    85     85   struct Th_Variable {
    86     86     int nRef;                   /* Number of references to this structure */
    87     87     int nData;                  /* Number of bytes at Th_Variable.zData */
    88     88     char *zData;               /* Data for scalar variables */
    89     89     Th_Hash *pHash;             /* Data for array variables */
................................................................................
   250    250       case 'f': case 'F': return 15;
   251    251     }
   252    252     return -1;
   253    253   }
   254    254   
   255    255   /*
   256    256   ** Argument pEntry points to an entry in a stack frame hash table
   257         -** (Th_Frame.paVar). Decrement the refrerence count of the Th_Variable
          257  +** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
   258    258   ** structure that the entry points to. Free the Th_Variable if its
   259    259   ** reference count reaches 0.
   260    260   **
   261    261   ** Argument pContext is a pointer to the interpreter structure.
   262    262   */
   263    263   static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
   264    264     Th_Variable *pValue = (Th_Variable *)pEntry->pData;
................................................................................
   844    844         continue;
   845    845       }
   846    846   
   847    847       /* Gobble up input a word at a time until the end of the command
   848    848       ** (a semi-colon or end of line).
   849    849       */
   850    850       while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
   851         -      int nWord;
          851  +      int nWord=0;
   852    852         thNextSpace(interp, zInput, nInput, &nSpace);
   853    853         rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1);
   854    854         zInput += (nSpace+nWord);
   855    855         nInput -= (nSpace+nWord);
   856    856       }
   857    857       if( rc!=TH_OK ) continue;
   858    858   
................................................................................
   874    874         /* Call the command procedure. */
   875    875         if( rc==TH_OK ){
   876    876           Th_Command *p = (Th_Command *)(pEntry->pData);
   877    877           const char **azArg = (const char **)argv;
   878    878           rc = p->xProc(interp, p->pContext, argc, azArg, argl);
   879    879         }
   880    880     
   881         -      /* If an error occured, add this command to the stack trace report. */
          881  +      /* If an error occurred, add this command to the stack trace report. */
   882    882         if( rc==TH_ERROR ){
   883    883           char *zRes;
   884    884           int nRes;
   885    885           char *zStack = 0;
   886    886           int nStack = 0;
   887    887     
   888    888           zRes = Th_TakeResult(interp, &nRes);
................................................................................
  1057   1057   ** arrayok is true an array name is Ok.
  1058   1058   */
  1059   1059   static Th_Variable *thFindValue(
  1060   1060     Th_Interp *interp,
  1061   1061     const char *zVar,     /* Pointer to variable name */
  1062   1062     int nVar,              /* Number of bytes at nVar */
  1063   1063     int create,            /* If true, create the variable if not found */
  1064         -  int arrayok            /* If true, an array is Ok. Othewise array==error */
         1064  +  int arrayok            /* If true, an array is Ok. Otherwise array==error */
  1065   1065   ){
  1066   1066     const char *zOuter;
  1067   1067     int nOuter;
  1068   1068     const char *zInner;
  1069   1069     int nInner;
  1070   1070     int isGlobal;
  1071   1071   
................................................................................
  1146   1146     if( !pValue->zData ){
  1147   1147       Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
  1148   1148       return TH_ERROR;
  1149   1149     }
  1150   1150   
  1151   1151     return Th_SetResult(interp, pValue->zData, pValue->nData);
  1152   1152   }
         1153  +
         1154  +/*
         1155  +** Return true if variable (zVar, nVar) exists.
         1156  +*/
         1157  +int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
         1158  +  return thFindValue(interp, zVar, nVar, 0, 0)!=0;
         1159  +}
  1153   1160   
  1154   1161   /*
  1155   1162   ** String (zVar, nVar) must contain the name of a scalar variable or
  1156   1163   ** array member. If the variable does not exist it is created. The
  1157   1164   ** variable is set to the value supplied in string (zValue, nValue).
  1158   1165   **
  1159   1166   ** If (zVar, nVar) refers to an existing array, TH_ERROR is returned
................................................................................
  1815   1822     if( pExpr->pOp==0 ){
  1816   1823       /* A literal */
  1817   1824       rc = thSubstWord(interp, pExpr->zValue, pExpr->nValue);
  1818   1825     }else{
  1819   1826       int eArgType = 0;           /* Actual type of arguments */
  1820   1827   
  1821   1828       /* Argument values */
  1822         -    int iLeft;
  1823         -    int iRight;
         1829  +    int iLeft = 0;
         1830  +    int iRight = 0;
  1824   1831       double fLeft;
  1825   1832       double fRight;
  1826   1833   
  1827   1834       /* Left and right arguments as strings */
  1828   1835       char *zLeft = 0; int nLeft = 0;
  1829   1836       char *zRight = 0; int nRight = 0;
  1830   1837   

Changes to src/th.h.

    47     47   */
    48     48   int Th_Expr(Th_Interp *interp, const char *, int);
    49     49   
    50     50   /* 
    51     51   ** Access TH variables in the current stack frame. If the variable name
    52     52   ** begins with "::", the lookup is in the top level (global) frame. 
    53     53   */
           54  +int Th_ExistsVar(Th_Interp *, const char *, int);
    54     55   int Th_GetVar(Th_Interp *, const char *, int);
    55     56   int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
    56     57   int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
    57     58   int Th_UnsetVar(Th_Interp *, const char *, int);
    58     59   
    59     60   typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
    60     61   
................................................................................
   152    153   /*
   153    154   ** Interfaces to register the language extensions.
   154    155   */
   155    156   int th_register_language(Th_Interp *interp);            /* th_lang.c */
   156    157   int th_register_sqlite(Th_Interp *interp);              /* th_sqlite.c */
   157    158   int th_register_vfs(Th_Interp *interp);                 /* th_vfs.c */
   158    159   int th_register_testvfs(Th_Interp *interp);             /* th_testvfs.c */
          160  +int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */
   159    161   
   160    162   /*
   161    163   ** General purpose hash table from th_lang.c.
   162    164   */
   163    165   typedef struct Th_Hash      Th_Hash;
   164    166   typedef struct Th_HashEntry Th_HashEntry;
   165    167   struct Th_HashEntry {

Changes to src/th_lang.c.

     5      5   **
     6      6   ** All built-in commands are implemented using the public interface 
     7      7   ** declared in th.h, so this file serves as both a part of the language 
     8      8   ** implementation and an example of how to extend the language with
     9      9   ** new commands.
    10     10   */
    11     11   
           12  +#include "config.h"
    12     13   #include "th.h"
    13     14   #include <string.h>
    14     15   #include <assert.h>
    15     16   
    16     17   int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){
    17     18     Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
    18     19     return TH_ERROR;
................................................................................
   355    356     if( p->hasArgs ){
   356    357       char *zArgs = 0;
   357    358       int nArgs = 0;
   358    359       for(i=p->nParam+1; i<pArgs->argc; i++){
   359    360         Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]);
   360    361       }
   361    362       Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs);
          363  +    if(zArgs){
          364  +      Th_Free(interp, zArgs);
          365  +    }
   362    366     }
   363    367   
   364    368     Th_SetResult(interp, 0, 0);
   365    369     return Th_Eval(interp, 0, p->zProgram, p->nProgram);
   366    370   }
   367    371   
   368    372   /*
................................................................................
   569    573   ){
   570    574     if( argc!=1 && argc!=2 ){
   571    575       return Th_WrongNumArgs(interp, "return ?value?");
   572    576     }
   573    577     if( argc==2 ){
   574    578       Th_SetResult(interp, argv[1], argl[1]);
   575    579     }
   576         -  return (int)ctx;
          580  +  return FOSSIL_PTR_TO_INT(ctx);
   577    581   }
   578    582   
   579    583   /*
   580    584   ** TH Syntax: 
   581    585   **
   582    586   **   return ?-code code? ?value?
   583    587   */
................................................................................
   811    815     Th_SetResult(interp, zByte, nByte);
   812    816     Th_Free(interp, zByte);
   813    817     return TH_OK;
   814    818   }
   815    819   
   816    820   /*
   817    821   ** TH Syntax:
          822  +**
          823  +**   string trim STRING
          824  +**   string trimleft STRING
          825  +**   string trimright STRING
          826  +*/
          827  +static int string_trim_command(
          828  +  Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
          829  +){
          830  +  int n;
          831  +  const char *z;
          832  +
          833  +  if( argc!=3 ){
          834  +    return Th_WrongNumArgs(interp, "string trim string");
          835  +  }
          836  +  z = argv[2];
          837  +  n = argl[2];
          838  +  if( argl[1]<5 || argv[1][4]=='l' ){
          839  +    while( n && th_isspace(z[0]) ){ z++; n--; }
          840  +  }
          841  +  if( argl[1]<5 || argv[1][4]=='r' ){
          842  +    while( n && th_isspace(z[n-1]) ){ n--; }
          843  +  }
          844  +  Th_SetResult(interp, z, n);
          845  +  return TH_OK;
          846  +}
          847  +
          848  +/*
          849  +** TH Syntax:
   818    850   **
   819    851   **   info exists VAR
   820    852   */
   821    853   static int info_exists_command(
   822    854     Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
   823    855   ){
   824    856     int rc;
   825    857   
   826    858     if( argc!=3 ){
   827    859       return Th_WrongNumArgs(interp, "info exists var");
   828    860     }
   829         -  rc = Th_GetVar(interp, argv[2], argl[2]);
   830         -  Th_SetResultInt(interp, rc?0:1);
          861  +  rc = Th_ExistsVar(interp, argv[2], argl[2]);
          862  +  Th_SetResultInt(interp, rc);
   831    863     return TH_OK;
   832    864   }
   833    865   
   834    866   /*
   835    867   ** TH Syntax:
   836    868   **
   837    869   **   unset VAR
................................................................................
   891    923       { "compare", string_compare_command },
   892    924       { "first",   string_first_command },
   893    925       { "is",      string_is_command },
   894    926       { "last",    string_last_command },
   895    927       { "length",  string_length_command },
   896    928       { "range",   string_range_command },
   897    929       { "repeat",  string_repeat_command },
          930  +    { "trim",      string_trim_command },
          931  +    { "trimleft",  string_trim_command },
          932  +    { "trimright", string_trim_command },
   898    933       { 0, 0 }
   899    934     };
   900    935     return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
   901    936   }
   902    937   
   903    938   /*
   904    939   ** TH Syntax:
................................................................................
  1051   1086       {"breakpoint", breakpoint_command, 0},
  1052   1087   
  1053   1088       {"return",   return_command, 0},
  1054   1089       {"break",    simple_command, (void *)TH_BREAK}, 
  1055   1090       {"continue", simple_command, (void *)TH_CONTINUE}, 
  1056   1091       {"error",    simple_command, (void *)TH_ERROR}, 
  1057   1092   
  1058         -    {0, 0}
         1093  +    {0, 0, 0}
  1059   1094     };
  1060   1095     int i;
  1061   1096   
  1062   1097     /* Add the language commands. */
  1063   1098     for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
  1064         -    void *ctx = aCommand[i].pContext;
         1099  +    void *ctx;
         1100  +    if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
         1101  +    ctx = aCommand[i].pContext;
  1065   1102       Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
  1066   1103     }
  1067   1104   
  1068   1105     return TH_OK;
  1069   1106   }

Changes to src/th_main.c.

    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains an interface between the TH scripting language
    19     19   ** (an independent project) and fossil.
    20     20   */
    21     21   #include "config.h"
    22     22   #include "th_main.h"
           23  +#include "sqlite3.h"
    23     24   
    24     25   /*
    25     26   ** Global variable counting the number of outstanding calls to malloc()
    26     27   ** made by the th1 implementation. This is used to catch memory leaks
    27     28   ** in the interpreter. Obviously, it also means th1 is not threadsafe.
    28     29   */
    29     30   static int nOutstandingMalloc = 0;
    30     31   
    31     32   /*
    32     33   ** Implementations of malloc() and free() to pass to the interpreter.
    33     34   */
    34     35   static void *xMalloc(unsigned int n){
    35         -  void *p = malloc(n);
           36  +  void *p = fossil_malloc(n);
    36     37     if( p ){
    37     38       nOutstandingMalloc++;
    38     39     }
    39     40     return p;
    40     41   }
    41     42   static void xFree(void *p){
    42     43     if( p ){
................................................................................
    70     71   static int enableOutputCmd(
    71     72     Th_Interp *interp, 
    72     73     void *p, 
    73     74     int argc, 
    74     75     const char **argv, 
    75     76     int *argl
    76     77   ){
    77         -  if( argc!=2 ){
    78         -    return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
           78  +  int rc;
           79  +  if( argc<2 || argc>3 ){
           80  +    return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
    79     81     }
    80         -  return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
           82  +  rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
           83  +  if( g.thTrace ){
           84  +    Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput);
           85  +  }
           86  +  return rc;
           87  +}
           88  +
           89  +/*
           90  +** Return a name for a TH1 return code.
           91  +*/
           92  +const char *Th_ReturnCodeName(int rc){
           93  +  static char zRc[32];
           94  +  switch( rc ){
           95  +    case TH_OK:       return "TH_OK";
           96  +    case TH_ERROR:    return "TH_ERROR";
           97  +    case TH_BREAK:    return "TH_BREAK";
           98  +    case TH_RETURN:   return "TH_RETURN";
           99  +    case TH_CONTINUE: return "TH_CONTINUE";
          100  +    default: {
          101  +      sqlite3_snprintf(sizeof(zRc),zRc,"return code %d",rc);
          102  +    }
          103  +  }
          104  +  return zRc;
    81    105   }
    82    106   
    83    107   /*
    84    108   ** Send text to the appropriate output:  Either to the console
    85         -** or to the CGI reply buffer.
          109  +** or to the CGI reply buffer.  Escape all characters with special
          110  +** meaning to HTML if the encode parameter is true.
    86    111   */
    87    112   static void sendText(const char *z, int n, int encode){
    88    113     if( enableOutput && n ){
    89    114       if( n<0 ) n = strlen(z);
    90    115       if( encode ){
    91    116         z = htmlize(z, n);
    92    117         n = strlen(z);
    93    118       }
    94    119       if( g.cgiOutput ){
    95    120         cgi_append_content(z, n);
    96    121       }else{
    97    122         fwrite(z, 1, n, stdout);
          123  +      fflush(stdout);
    98    124       }
    99    125       if( encode ) free((char*)z);
   100    126     }
   101    127   }
          128  +
          129  +static void sendError(const char *z, int n, int forceCgi){
          130  +  int savedEnable = enableOutput;
          131  +  enableOutput = 1;
          132  +  if( forceCgi || g.cgiOutput ){
          133  +    sendText("<hr><p class=\"thmainError\">", -1, 0);
          134  +  }
          135  +  sendText("ERROR: ", -1, 0);
          136  +  sendText((char*)z, n, 1);
          137  +  sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
          138  +  enableOutput = savedEnable;
          139  +}
   102    140   
   103    141   /*
   104    142   ** TH command:     puts STRING
   105    143   ** TH command:     html STRING
   106    144   **
   107         -** Output STRING as HTML (html) or unchanged (puts).  
          145  +** Output STRING escaped for HTML (html) or unchanged (puts).  
   108    146   */
   109    147   static int putsCmd(
   110    148     Th_Interp *interp, 
   111    149     void *pConvert, 
   112    150     int argc, 
   113    151     const char **argv, 
   114    152     int *argl
   115    153   ){
   116    154     if( argc!=2 ){
   117    155       return Th_WrongNumArgs(interp, "puts STRING");
   118    156     }
   119         -  sendText((char*)argv[1], argl[1], pConvert!=0);
          157  +  sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
   120    158     return TH_OK;
   121    159   }
   122    160   
   123    161   /*
   124    162   ** TH command:      wiki STRING
   125    163   **
   126    164   ** Render the input string as wiki.
................................................................................
   128    166   static int wikiCmd(
   129    167     Th_Interp *interp, 
   130    168     void *p, 
   131    169     int argc, 
   132    170     const char **argv, 
   133    171     int *argl
   134    172   ){
          173  +  int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
   135    174     if( argc!=2 ){
   136    175       return Th_WrongNumArgs(interp, "wiki STRING");
   137    176     }
   138    177     if( enableOutput ){
   139    178       Blob src;
   140    179       blob_init(&src, (char*)argv[1], argl[1]);
   141         -    wiki_convert(&src, 0, WIKI_INLINE);
          180  +    wiki_convert(&src, 0, flags);
   142    181       blob_reset(&src);
   143    182     }
   144    183     return TH_OK;
   145    184   }
   146    185   
   147    186   /*
   148    187   ** TH command:      htmlize STRING
................................................................................
   166    205     free(zOut);
   167    206     return TH_OK;
   168    207   }
   169    208   
   170    209   /*
   171    210   ** TH command:      date
   172    211   **
   173         -** Return a string which is the current time and date.
          212  +** Return a string which is the current time and date.  If the
          213  +** -local option is used, the date appears using localtime instead
          214  +** of UTC.
   174    215   */
   175    216   static int dateCmd(
   176    217     Th_Interp *interp, 
   177    218     void *p, 
   178    219     int argc, 
   179    220     const char **argv, 
   180    221     int *argl
   181    222   ){
   182         -  char *zOut = db_text("??", "SELECT datetime('now')");
          223  +  char *zOut;
          224  +  if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){
          225  +    zOut = db_text("??", "SELECT datetime('now','localtime')");
          226  +  }else{
          227  +    zOut = db_text("??", "SELECT datetime('now')");
          228  +  }
   183    229     Th_SetResult(interp, zOut, -1);
   184    230     free(zOut);
   185    231     return TH_OK;
   186    232   }
   187    233   
   188    234   /*
   189         -** TH command:     hascap STRING
          235  +** TH command:     hascap STRING...
   190    236   **
   191    237   ** Return true if the user has all of the capabilities listed in STRING.
   192    238   */
   193    239   static int hascapCmd(
   194    240     Th_Interp *interp, 
   195    241     void *p, 
   196    242     int argc, 
   197    243     const char **argv, 
   198    244     int *argl
   199    245   ){
   200         -  int rc;
          246  +  int rc = 0, i;
          247  +  if( argc<2 ){
          248  +    return Th_WrongNumArgs(interp, "hascap STRING ...");
          249  +  }
          250  +  for(i=1; i<argc && rc==0; i++){
          251  +    rc = login_has_capability((char*)argv[i],argl[i]);
          252  +  }
          253  +  if( g.thTrace ){
          254  +    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
          255  +  }
          256  +  Th_SetResultInt(interp, rc);
          257  +  return TH_OK;
          258  +}
          259  +
          260  +/*
          261  +** TH command:     hasfeature STRING
          262  +**
          263  +** Return true if the fossil binary has the given compile-time feature
          264  +** enabled. The set of features includes:
          265  +**
          266  +** "json"     = FOSSIL_ENABLE_JSON
          267  +** "ssl"      = FOSSIL_ENABLE_SSL
          268  +** "tcl"      = FOSSIL_ENABLE_TCL
          269  +** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS
          270  +** "markdown" = FOSSIL_ENABLE_MARKDOWN
          271  +**
          272  +*/
          273  +static int hasfeatureCmd(
          274  +  Th_Interp *interp, 
          275  +  void *p, 
          276  +  int argc, 
          277  +  const char **argv, 
          278  +  int *argl
          279  +){
          280  +  int rc = 0;
          281  +  char const * zArg;
   201    282     if( argc!=2 ){
   202         -    return Th_WrongNumArgs(interp, "hascap STRING");
          283  +    return Th_WrongNumArgs(interp, "hasfeature STRING");
   203    284     }
   204         -  rc = login_has_capability((char*)argv[1],argl[1]);
          285  +  zArg = (char const*)argv[1];
          286  +  if(NULL==zArg){
          287  +    /* placeholder for following ifdefs... */
          288  +  }
          289  +#if defined(FOSSIL_ENABLE_JSON)
          290  +  else if( 0 == fossil_strnicmp( zArg, "json", 4 ) ){
          291  +    rc = 1;
          292  +  }
          293  +#endif
          294  +#if defined(FOSSIL_ENABLE_SSL)
          295  +  else if( 0 == fossil_strnicmp( zArg, "ssl", 3 ) ){
          296  +    rc = 1;
          297  +  }
          298  +#endif
          299  +#if defined(FOSSIL_ENABLE_TCL)
          300  +  else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
          301  +    rc = 1;
          302  +  }
          303  +#endif
          304  +#if defined(FOSSIL_ENABLE_TCL_STUBS)
          305  +  else if( 0 == fossil_strnicmp( zArg, "tclStubs", 8 ) ){
          306  +    rc = 1;
          307  +  }
          308  +#endif
          309  +#if defined(FOSSIL_ENABLE_MARKDOWN)
          310  +  else if( 0 == fossil_strnicmp( zArg, "markdown", 8 ) ){
          311  +    rc = 1;
          312  +  }
          313  +#endif
   205    314     if( g.thTrace ){
   206         -    Th_Trace("[hascap %.*h] => %d<br />\n", argl[1], argv[1], rc);
          315  +    Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
   207    316     }
   208    317     Th_SetResultInt(interp, rc);
   209    318     return TH_OK;
   210    319   }
          320  +
   211    321   
   212    322   /*
   213    323   ** TH command:     anycap STRING
   214    324   **
   215    325   ** Return true if the user has any one of the capabilities listed in STRING.
   216    326   */
   217    327   static int anycapCmd(
................................................................................
   226    336     if( argc!=2 ){
   227    337       return Th_WrongNumArgs(interp, "anycap STRING");
   228    338     }
   229    339     for(i=0; rc==0 && i<argl[1]; i++){
   230    340       rc = login_has_capability((char*)&argv[1][i],1);
   231    341     }
   232    342     if( g.thTrace ){
   233         -    Th_Trace("[hascap %.*h] => %d<br />\n", argl[1], argv[1], rc);
          343  +    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
   234    344     }
   235    345     Th_SetResultInt(interp, rc);
   236    346     return TH_OK;
   237    347   }
   238    348   
   239    349   /*
   240    350   ** TH1 command:  combobox NAME TEXT-LIST NUMLINES
................................................................................
   276    386       sendText(z, -1, 0);
   277    387       free(z);
   278    388       blob_reset(&name);
   279    389       for(i=0; i<nElem; i++){
   280    390         zH = htmlize((char*)azElem[i], aszElem[i]);
   281    391         if( zValue && aszElem[i]==nValue 
   282    392                && memcmp(zValue, azElem[i], nValue)==0 ){
   283         -        z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH);
          393  +        z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
          394  +                     zH, zH);
   284    395         }else{
   285    396           z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
   286    397         }
   287    398         free(zH);
   288    399         sendText(z, -1, 0);
   289    400         free(z);
   290    401       }
................................................................................
   324    435       }
   325    436     }
   326    437     if( n<iMin ) n = iMin;
   327    438     if( n>iMax ) n = iMax;
   328    439     Th_SetResultInt(interp, n);
   329    440     return TH_OK;
   330    441   }
          442  +
          443  +/*
          444  +** TH1 command:     repository ?BOOLEAN?
          445  +**
          446  +** Return the fully qualified file name of the open repository or an empty
          447  +** string if one is not currently open.  Optionally, it will attempt to open
          448  +** the repository if the boolean argument is non-zero.
          449  +*/
          450  +static int repositoryCmd(
          451  +  Th_Interp *interp,
          452  +  void *p, 
          453  +  int argc, 
          454  +  const char **argv, 
          455  +  int *argl
          456  +){
          457  +  int openRepository;
          458  +
          459  +  if( argc!=1 && argc!=2 ){
          460  +    return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
          461  +  }
          462  +  if( argc==2 ){
          463  +    if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
          464  +      return TH_ERROR;
          465  +    }
          466  +    if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
          467  +  }
          468  +  Th_SetResult(interp, g.zRepositoryName, -1);
          469  +  return TH_OK;
          470  +}
          471  +
          472  +#ifdef _WIN32
          473  +# include <windows.h>
          474  +#else
          475  +# include <sys/time.h>
          476  +# include <sys/resource.h>
          477  +#endif
          478  +
          479  +/*
          480  +** Get user and kernel times in microseconds.
          481  +*/
          482  +static void getCpuTimes(sqlite3_uint64 *piUser, sqlite3_uint64 *piKernel){
          483  +#ifdef _WIN32
          484  +  FILETIME not_used;
          485  +  FILETIME kernel_time;
          486  +  FILETIME user_time;
          487  +  GetProcessTimes(GetCurrentProcess(), &not_used, &not_used,
          488  +                  &kernel_time, &user_time);
          489  +  if( piUser ){
          490  +     *piUser = ((((sqlite3_uint64)user_time.dwHighDateTime)<<32) +
          491  +                         (sqlite3_uint64)user_time.dwLowDateTime + 5)/10;
          492  +  }
          493  +  if( piKernel ){
          494  +     *piKernel = ((((sqlite3_uint64)kernel_time.dwHighDateTime)<<32) +
          495  +                         (sqlite3_uint64)kernel_time.dwLowDateTime + 5)/10;
          496  +  }
          497  +#else
          498  +  struct rusage s;
          499  +  getrusage(RUSAGE_SELF, &s);
          500  +  if( piUser ){
          501  +    *piUser = ((sqlite3_uint64)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec;
          502  +  }
          503  +  if( piKernel ){
          504  +    *piKernel = 
          505  +              ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
          506  +  }
          507  +#endif
          508  +}
          509  +
          510  +/*
          511  +** TH1 command:     utime
          512  +**
          513  +** Return the number of microseconds of CPU time consumed by the current
          514  +** process in user space.
          515  +*/
          516  +static int utimeCmd(
          517  +  Th_Interp *interp,
          518  +  void *p, 
          519  +  int argc, 
          520  +  const char **argv, 
          521  +  int *argl
          522  +){
          523  +  sqlite3_uint64 x;
          524  +  char zUTime[50];
          525  +  getCpuTimes(&x, 0);
          526  +  sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
          527  +  Th_SetResult(interp, zUTime, -1);
          528  +  return TH_OK;
          529  +}
          530  +
          531  +/*
          532  +** TH1 command:     stime
          533  +**
          534  +** Return the number of microseconds of CPU time consumed by the current
          535  +** process in system space.
          536  +*/
          537  +static int stimeCmd(
          538  +  Th_Interp *interp,
          539  +  void *p, 
          540  +  int argc, 
          541  +  const char **argv, 
          542  +  int *argl
          543  +){
          544  +  sqlite3_uint64 x;
          545  +  char zUTime[50];
          546  +  getCpuTimes(0, &x);
          547  +  sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
          548  +  Th_SetResult(interp, zUTime, -1);
          549  +  return TH_OK;
          550  +}
          551  +
          552  +
          553  +/*
          554  +** TH1 command:     randhex  N
          555  +**
          556  +** Return N*2 random hexadecimal digits with N<50.  If N is omitted, 
          557  +** use a value of 10.
          558  +*/
          559  +static int randhexCmd(
          560  +  Th_Interp *interp,
          561  +  void *p, 
          562  +  int argc, 
          563  +  const char **argv, 
          564  +  int *argl
          565  +){
          566  +  int n;
          567  +  unsigned char aRand[50];
          568  +  unsigned char zOut[100];
          569  +  if( argc!=1 && argc!=2 ){
          570  +    return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
          571  +  }
          572  +  if( argc==2 ){
          573  +    if( Th_ToInt(interp, argv[1], argl[1], &n) ){
          574  +      return TH_ERROR;
          575  +    }
          576  +    if( n<1 ) n = 1;
          577  +    if( n>sizeof(aRand) ) n = sizeof(aRand);
          578  +  }else{
          579  +    n = 10;
          580  +  }
          581  +  sqlite3_randomness(n, aRand);
          582  +  encode16(aRand, zOut, n);
          583  +  Th_SetResult(interp, (const char *)zOut, -1);
          584  +  return TH_OK;
          585  +}
          586  +
          587  +/*
          588  +** TH1 command:     query SQL CODE
          589  +**
          590  +** Run the SQL query given by the SQL argument.  For each row in the result
          591  +** set, run CODE.
          592  +**
          593  +** In SQL, parameters such as $var are filled in using the value of variable
          594  +** "var".  Result values are stored in variables with the column name prior
          595  +** to each invocation of CODE.
          596  +*/
          597  +static int queryCmd(
          598  +  Th_Interp *interp,
          599  +  void *p, 
          600  +  int argc, 
          601  +  const char **argv, 
          602  +  int *argl
          603  +){
          604  +  sqlite3_stmt *pStmt;
          605  +  int rc;
          606  +  const char *zSql;
          607  +  int nSql;
          608  +  const char *zTail;
          609  +  int n, i;
          610  +  int res = TH_OK;
          611  +  int nVar;
          612  +  char *zErr = 0;
          613  +
          614  +  if( argc!=3 ){
          615  +    return Th_WrongNumArgs(interp, "query SQL CODE");
          616  +  }
          617  +  if( g.db==0 ){
          618  +    Th_ErrorMessage(interp, "database is not open", 0, 0);
          619  +    return TH_ERROR;
          620  +  }
          621  +  zSql = argv[1];
          622  +  nSql = argl[1];
          623  +  while( res==TH_OK && nSql>0 ){
          624  +    zErr = 0;
          625  +    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr);
          626  +    rc = sqlite3_prepare_v2(g.db, argv[1], argl[1], &pStmt, &zTail);
          627  +    sqlite3_set_authorizer(g.db, 0, 0);
          628  +    if( rc!=0 || zErr!=0 ){
          629  +      Th_ErrorMessage(interp, "SQL error: ",
          630  +                      zErr ? zErr : sqlite3_errmsg(g.db), -1);
          631  +      return TH_ERROR;
          632  +    }
          633  +    n = (int)(zTail - zSql);
          634  +    zSql += n;
          635  +    nSql -= n;
          636  +    if( pStmt==0 ) continue;
          637  +    nVar = sqlite3_bind_parameter_count(pStmt);
          638  +    for(i=1; i<=nVar; i++){
          639  +      const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
          640  +      int szVar = zVar ? th_strlen(zVar) : 0;
          641  +      if( szVar>1 && zVar[0]=='$'
          642  +       && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){
          643  +        int nVal;
          644  +        const char *zVal = Th_GetResult(interp, &nVal);
          645  +        sqlite3_bind_text(pStmt, i, zVal, nVal, SQLITE_TRANSIENT);
          646  +      }
          647  +    }
          648  +    while( res==TH_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
          649  +      int nCol = sqlite3_column_count(pStmt);
          650  +      for(i=0; i<nCol; i++){
          651  +        const char *zCol = sqlite3_column_name(pStmt, i);
          652  +        int szCol = th_strlen(zCol);
          653  +        const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
          654  +        int szVal = sqlite3_column_bytes(pStmt, i);
          655  +        Th_SetVar(interp, zCol, szCol, zVal, szVal);
          656  +      }
          657  +      res = Th_Eval(interp, 0, argv[2], argl[2]);
          658  +      if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK;
          659  +    }
          660  +    rc = sqlite3_finalize(pStmt);
          661  +    if( rc!=SQLITE_OK ){
          662  +      Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1);
          663  +      return TH_ERROR;
          664  +    }
          665  +  } 
          666  +  return res;
          667  +}
   331    668   
   332    669   /*
   333    670   ** Make sure the interpreter has been initialized.  Initialize it if
   334    671   ** it has not been already.
   335    672   **
   336    673   ** The interpreter is stored in the g.interp global variable.
   337    674   */
   338         -void Th_FossilInit(void){
          675  +void Th_FossilInit(int needConfig, int forceSetup){
          676  +  int wasInit = 0;
          677  +  static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY };
   339    678     static struct _Command {
   340    679       const char *zName;
   341    680       Th_CommandProc xProc;
   342    681       void *pContext;
   343    682     } aCommand[] = {
   344    683       {"anycap",        anycapCmd,            0},
   345    684       {"combobox",      comboboxCmd,          0},
          685  +    {"date",          dateCmd,              0},
          686  +    {"decorate",      wikiCmd,              (void*)&aFlags[2]},
   346    687       {"enable_output", enableOutputCmd,      0},
   347         -    {"linecount",     linecntCmd,           0},
   348    688       {"hascap",        hascapCmd,            0},
          689  +    {"hasfeature",    hasfeatureCmd,        0},
          690  +    {"html",          putsCmd,              (void*)&aFlags[0]},
   349    691       {"htmlize",       htmlizeCmd,           0},
   350         -    {"date",          dateCmd,              0},
   351         -    {"html",          putsCmd,              0},
   352         -    {"puts",          putsCmd,       (void*)1},
   353         -    {"wiki",          wikiCmd,              0},
          692  +    {"linecount",     linecntCmd,           0},
          693  +    {"puts",          putsCmd,              (void*)&aFlags[1]},
          694  +    {"query",         queryCmd,             0},
          695  +    {"randhex",       randhexCmd,           0},
          696  +    {"repository",    repositoryCmd,        0},
          697  +    {"stime",         stimeCmd,             0},
          698  +    {"utime",         utimeCmd,             0},
          699  +    {"wiki",          wikiCmd,              (void*)&aFlags[0]},
          700  +    {0, 0, 0}
   354    701     };
          702  +  if( needConfig ){
          703  +    /*
          704  +    ** This function uses several settings which may be defined in the
          705  +    ** repository and/or the global configuration.  Since the caller
          706  +    ** passed a non-zero value for the needConfig parameter, make sure
          707  +    ** the necessary database connections are open prior to continuing.
          708  +    */
          709  +    db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
          710  +    db_open_config(0);
          711  +  }
   355    712     if( g.interp==0 ){
   356    713       int i;
   357    714       g.interp = Th_CreateInterp(&vtab);
   358    715       th_register_language(g.interp);       /* Basic scripting commands. */
          716  +#ifdef FOSSIL_ENABLE_TCL
          717  +    if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
          718  +      if( !g.tcl.setup ){
          719  +        g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
          720  +      }
          721  +      th_register_tcl(g.interp, &g.tcl);  /* Tcl integration commands. */
          722  +    }
          723  +#endif
   359    724       for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
          725  +      if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
   360    726         Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
   361    727                          aCommand[i].pContext, 0);
   362    728       }
          729  +  }else{
          730  +    wasInit = 1;
          731  +  }
          732  +  if( forceSetup || !wasInit ){
          733  +    int rc = TH_OK;
          734  +    if( !g.th1Setup ){
          735  +      g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
          736  +    }
          737  +    if( g.th1Setup ){
          738  +      rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
          739  +      if( rc==TH_ERROR ){
          740  +        int nResult = 0;
          741  +        char *zResult = (char*)Th_GetResult(g.interp, &nResult);
          742  +        sendError(zResult, nResult, 0);
          743  +      }
          744  +    }
          745  +    if( g.thTrace ){
          746  +      Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
          747  +               Th_ReturnCodeName(rc));
          748  +    }
   363    749     }
   364    750   }
   365    751   
   366    752   /*
   367    753   ** Store a string value in a variable in the interpreter.
   368    754   */
   369    755   void Th_Store(const char *zName, const char *zValue){
   370         -  Th_FossilInit();
          756  +  Th_FossilInit(0, 0);
   371    757     if( zValue ){
   372    758       if( g.thTrace ){
   373    759         Th_Trace("set %h {%h}<br />\n", zName, zValue);
   374    760       }
   375    761       Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
   376    762     }
   377    763   }
          764  +
          765  +/*
          766  +** Store an integer value in a variable in the interpreter.
          767  +*/
          768  +void Th_StoreInt(const char *zName, int iValue){
          769  +  Blob value;
          770  +  char *zValue;
          771  +  Th_FossilInit(0, 0);
          772  +  blob_zero(&value);
          773  +  blob_appendf(&value, "%d", iValue);
          774  +  zValue = blob_str(&value);
          775  +  if( g.thTrace ){
          776  +    Th_Trace("set %h {%h}<br />\n", zName, zValue);
          777  +  }
          778  +  Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
          779  +  blob_reset(&value);
          780  +}
   378    781   
   379    782   /*
   380    783   ** Unset a variable.
   381    784   */
   382    785   void Th_Unstore(const char *zName){
   383    786     if( g.interp ){
   384    787       Th_UnsetVar(g.interp, (char*)zName, -1);
................................................................................
   387    790   
   388    791   /*
   389    792   ** Retrieve a string value from the interpreter.  If no such
   390    793   ** variable exists, return NULL.
   391    794   */
   392    795   char *Th_Fetch(const char *zName, int *pSize){
   393    796     int rc;
   394         -  Th_FossilInit();
          797  +  Th_FossilInit(0, 0);
   395    798     rc = Th_GetVar(g.interp, (char*)zName, -1);
   396    799     if( rc==TH_OK ){
   397    800       return (char*)Th_GetResult(g.interp, pSize);
   398    801     }else{
   399    802       return 0;
   400    803     }
   401    804   }
................................................................................
   432    835   static int validVarName(const char *z){
   433    836     int i = 0;
   434    837     int inBracket = 0;
   435    838     if( z[0]=='<' ){
   436    839       inBracket = 1;
   437    840       z++;
   438    841     }
   439         -  if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
          842  +  if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){
   440    843       z += 3;
   441    844       i += 3;
   442         -  }else if( isalpha(z[0]) ){
          845  +  }else if( fossil_isalpha(z[0]) ){
   443    846       z ++;
   444    847       i += 1;
   445    848     }else{
   446    849       return 0;
   447    850     }
   448         -  while( isalnum(z[0]) || z[0]=='_' ){
          851  +  while( fossil_isalnum(z[0]) || z[0]=='_' ){
   449    852       z++;
   450    853       i++;
   451    854     }
   452    855     if( inBracket ){
   453    856       if( z[0]!='>' ) return 0;
   454    857       i += 2;
   455    858     }
................................................................................
   467    870   ** on either stdout or into CGI.
   468    871   */
   469    872   int Th_Render(const char *z){
   470    873     int i = 0;
   471    874     int n;
   472    875     int rc = TH_OK;
   473    876     char *zResult;
   474         -  Th_FossilInit();
          877  +  Th_FossilInit(0, 0);
   475    878     while( z[i] ){
   476    879       if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
   477    880         const char *zVar;
   478    881         int nVar;
          882  +      int encode = 1;
   479    883         sendText(z, i, 0);
   480    884         if( z[i+1]=='<' ){
   481         -        /* Variables of the form $<aaa> */
          885  +        /* Variables of the form $<aaa> are html escaped */
   482    886           zVar = &z[i+2];
   483    887           nVar = n-2;
   484    888         }else{
   485         -        /* Variables of the form $aaa */
          889  +        /* Variables of the form $aaa are output raw */
   486    890           zVar = &z[i+1];
   487    891           nVar = n;
          892  +        encode = 0;
   488    893         }
   489    894         rc = Th_GetVar(g.interp, (char*)zVar, nVar);
   490    895         z += i+1+n;
   491    896         i = 0;
   492    897         zResult = (char*)Th_GetResult(g.interp, &n);
   493         -      sendText((char*)zResult, n, n>nVar);
          898  +      sendText((char*)zResult, n, encode);
   494    899       }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
   495    900         sendText(z, i, 0);
   496    901         z += i+5;
   497    902         for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
          903  +      if( g.thTrace ){
          904  +        Th_Trace("eval {<pre>%#h</pre>}<br>", i, z);
          905  +      }
   498    906         rc = Th_Eval(g.interp, 0, (const char*)z, i);
   499    907         if( rc!=TH_OK ) break;
   500    908         z += i;
   501    909         if( z[0] ){ z += 6; }
   502    910         i = 0;
   503    911       }else{
   504    912         i++;
   505    913       }
   506    914     }
   507    915     if( rc==TH_ERROR ){
   508         -    sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0);
   509    916       zResult = (char*)Th_GetResult(g.interp, &n);
   510         -    sendText((char*)zResult, n, 1);
   511         -    sendText("</b></font></p>", -1, 0);
          917  +    sendError(zResult, n, 1);
   512    918     }else{
   513    919       sendText(z, i, 0);
   514    920     }
   515    921     return rc;
   516    922   }
   517    923   
   518    924   /*
................................................................................
   519    925   ** COMMAND: test-th-render
   520    926   */
   521    927   void test_th_render(void){
   522    928     Blob in;
   523    929     if( g.argc<3 ){
   524    930       usage("FILE");
   525    931     }
          932  +  db_open_config(0); /* Needed for global "tcl" setting. */
   526    933     blob_zero(&in);
   527    934     blob_read_from_file(&in, g.argv[2]);
   528    935     Th_Render(blob_str(&in));
   529    936   }

Added src/th_tcl.c.

            1  +/*
            2  +** Copyright (c) 2011 D. Richard Hipp
            3  +** Copyright (c) 2011 Joe Mistachkin
            4  +**
            5  +** This program is free software; you can redistribute it and/or
            6  +** modify it under the terms of the Simplified BSD License (also
            7  +** known as the "2-Clause License" or "FreeBSD License".)
            8  +
            9  +** This program is distributed in the hope that it will be useful,
           10  +** but without any warranty; without even the implied warranty of
           11  +** merchantability or fitness for a particular purpose.
           12  +**
           13  +** Author contact information:
           14  +**   drh@hwaci.com
           15  +**   http://www.hwaci.com/drh/
           16  +**
           17  +*******************************************************************************
           18  +**
           19  +** This file contains code used to bridge the TH1 and Tcl scripting languages.
           20  +*/
           21  +#include "config.h"
           22  +
           23  +#ifdef FOSSIL_ENABLE_TCL
           24  +
           25  +#include "th.h"
           26  +#include "tcl.h"
           27  +
           28  +/*
           29  +** Are we being compiled against Tcl 8.6 or higher?
           30  + */
           31  +#if (TCL_MAJOR_VERSION > 8) || \
           32  +    ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
           33  +/*
           34  +** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
           35  +** Tcl_EvalObjv instead of invoking the objProc directly.
           36  + */
           37  +#  define USE_TCL_EVALOBJV   1
           38  +#endif
           39  +
           40  +/*
           41  +** These macros are designed to reduce the redundant code required to marshal
           42  +** arguments from TH1 to Tcl.
           43  + */
           44  +#define USE_ARGV_TO_OBJV() \
           45  +  int objc;                \
           46  +  Tcl_Obj **objv;          \
           47  +  int i;
           48  +
           49  +#define COPY_ARGV_TO_OBJV()                                         \
           50  +  objc = argc-1;                                                    \
           51  +  objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \
           52  +  for(i=1; i<argc; i++){                                            \
           53  +    objv[i-1] = Tcl_NewStringObj(argv[i], argl[i]);                 \
           54  +    Tcl_IncrRefCount(objv[i-1]);                                    \
           55  +  }
           56  +
           57  +#define FREE_ARGV_TO_OBJV()      \
           58  +  for(i=1; i<argc; i++){         \
           59  +    Tcl_DecrRefCount(objv[i-1]); \
           60  +  }                              \
           61  +  ckfree((char *)objv);
           62  +
           63  +/*
           64  +** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl
           65  +** context.
           66  + */
           67  +#define GET_CTX_TCL_INTERP(ctx) \
           68  +  ((struct TclContext *)(ctx))->interp
           69  +
           70  +/*
           71  +** Define the Tcl shared library name, some exported function names, and some
           72  +** cross-platform macros for use with the Tcl stubs mechanism, when enabled.
           73  + */
           74  +#if defined(USE_TCL_STUBS)
           75  +#  if defined(_WIN32)
           76  +#    define WIN32_LEAN_AND_MEAN
           77  +#    include <windows.h>
           78  +#    ifndef TCL_LIBRARY_NAME
           79  +#      define TCL_LIBRARY_NAME "tcl86.dll\0"
           80  +#    endif
           81  +#    ifndef TCL_MINOR_OFFSET
           82  +#      define TCL_MINOR_OFFSET (4)
           83  +#    endif
           84  +#    ifndef dlopen
           85  +#      define dlopen(a,b) (void *)LoadLibrary((a))
           86  +#    endif
           87  +#    ifndef dlsym
           88  +#      define dlsym(a,b) GetProcAddress((HANDLE)(a),(b))
           89  +#    endif
           90  +#    ifndef dlclose
           91  +#      define dlclose(a) FreeLibrary((HANDLE)(a))
           92  +#    endif
           93  +#  else
           94  +#    include <dlfcn.h>
           95  +#    if defined(__CYGWIN__)
           96  +#      ifndef TCL_LIBRARY_NAME
           97  +#        define TCL_LIBRARY_NAME "libtcl8.6.dll\0"
           98  +#      endif
           99  +#      ifndef TCL_MINOR_OFFSET
          100  +#        define TCL_MINOR_OFFSET (8)
          101  +#      endif
          102  +#    elif defined(__APPLE__)
          103  +#      ifndef TCL_LIBRARY_NAME
          104  +#        define TCL_LIBRARY_NAME "libtcl8.6.dylib\0"
          105  +#      endif
          106  +#      ifndef TCL_MINOR_OFFSET
          107  +#        define TCL_MINOR_OFFSET (8)
          108  +#      endif
          109  +#    else
          110  +#      ifndef TCL_LIBRARY_NAME
          111  +#        define TCL_LIBRARY_NAME "libtcl8.6.so\0"
          112  +#      endif
          113  +#      ifndef TCL_MINOR_OFFSET
          114  +#        define TCL_MINOR_OFFSET (8)
          115  +#      endif
          116  +#    endif /* defined(__CYGWIN__) */
          117  +#  endif /* defined(_WIN32) */
          118  +#  ifndef TCL_FINDEXECUTABLE_NAME
          119  +#    define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable"
          120  +#  endif
          121  +#  ifndef TCL_CREATEINTERP_NAME
          122  +#    define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp"
          123  +#  endif
          124  +#endif /* defined(USE_TCL_STUBS) */
          125  +
          126  +/*
          127  +** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed
          128  +** when the Tcl library is being loaded dynamically by a stubs-enabled
          129  +** application (i.e. the inverse of using a stubs-enabled package).  These are
          130  +** the only Tcl API functions that MUST be called prior to being able to call
          131  +** Tcl_InitStubs (i.e. because it requires a Tcl interpreter).
          132  + */
          133  +typedef void (tcl_FindExecutableProc) (CONST char * argv0);
          134  +typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
          135  +
          136  +/*
          137  +** The function types for the "hook" functions to be called before and after a
          138  +** TH1 command makes a call to evaluate a Tcl script.  If the "pre" function
          139  +** returns anything but TH_OK, then evaluation of the Tcl script is skipped and
          140  +** that value is used as the return code.  If the "post" function returns
          141  +** anything other than its rc argument, that will become the new return code
          142  +** for the command.
          143  + */
          144  +typedef int (tcl_NotifyProc) (
          145  +  void *pContext,    /* The context for this notification. */
          146  +  Th_Interp *interp, /* The TH1 interpreter being used. */
          147  +  void *ctx,         /* The original TH1 command context. */
          148  +  int argc,          /* Number of arguments for the TH1 command. */
          149  +  const char **argv, /* Array of arguments for the TH1 command. */
          150  +  int *argl,         /* Array of lengths for the TH1 command arguments. */
          151  +  int rc             /* Recommended notification return value. */
          152  +);
          153  +
          154  +/*
          155  +** Creates and initializes a Tcl interpreter for use with the specified TH1
          156  +** interpreter.  Stores the created Tcl interpreter in the Tcl context supplied
          157  +** by the caller.  This must be declared here because quite a few functions in
          158  +** this file need to use it before it can be defined.
          159  + */
          160  +static int createTclInterp(Th_Interp *interp, void *pContext);
          161  +
          162  +/*
          163  +** Returns the Tcl interpreter result as a string with the associated length.
          164  +** If the Tcl interpreter or the Tcl result are NULL, the length will be 0.
          165  +** If the length pointer is NULL, the length will not be stored.
          166  + */
          167  +static char *getTclResult(
          168  +  Tcl_Interp *pInterp,
          169  +  int *pN
          170  +){
          171  +  Tcl_Obj *resultPtr;
          172  +  if( !pInterp ){ /* This should not happen. */
          173  +    if( pN ) *pN = 0;
          174  +    return 0;
          175  +  }
          176  +  resultPtr = Tcl_GetObjResult(pInterp);
          177  +  if( !resultPtr ){ /* This should not happen either? */
          178  +    if( pN ) *pN = 0;
          179  +    return 0;
          180  +  }
          181  +  return Tcl_GetStringFromObj(resultPtr, pN);
          182  +}
          183  +
          184  +/*
          185  +** Tcl context information used by TH1.  This structure definition has been
          186  +** copied from and should be kept in sync with the one in "main.c".
          187  +*/
          188  +struct TclContext {
          189  +  int argc;           /* Number of original arguments. */
          190  +  char **argv;        /* Full copy of the original arguments. */
          191  +  void *library;      /* The Tcl library module handle. */
          192  +  tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
          193  +  tcl_CreateInterpProc *xCreateInterp;     /* Tcl_CreateInterp() pointer. */
          194  +  Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
          195  +  char *setup;        /* The optional Tcl setup script. */
          196  +  tcl_NotifyProc *xPreEval;  /* Optional, called before Tcl_Eval*(). */
          197  +  void *pPreContext;         /* Optional, provided to xPreEval(). */
          198  +  tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */
          199  +  void *pPostContext;        /* Optional, provided to xPostEval(). */
          200  +};
          201  +
          202  +/*
          203  +** This function calls the configured xPreEval or xPostEval functions, if any.
          204  +** May have arbitrary side-effects.  This function returns the result of the
          205  +** called notification function or the value of the rc argument if there is no
          206  +** notification function configured.
          207  +*/
          208  +static int notifyPreOrPostEval(
          209  +  int bIsPost,
          210  +  Th_Interp *interp,
          211  +  void *ctx,
          212  +  int argc,
          213  +  const char **argv,
          214  +  int *argl,
          215  +  int rc
          216  +){
          217  +  struct TclContext *tclContext = (struct TclContext *)ctx;
          218  +  tcl_NotifyProc *xNotifyProc;
          219  +
          220  +  if ( !tclContext ){
          221  +    Th_ErrorMessage(interp,
          222  +        "invalid Tcl context", (const char *)"", 0);
          223  +    return TH_ERROR;
          224  +  }
          225  +  xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval;
          226  +  if ( xNotifyProc ){
          227  +    rc = xNotifyProc(bIsPost ?
          228  +        tclContext->pPostContext : tclContext->pPreContext,
          229  +        interp, ctx, argc, argv, argl, rc);
          230  +  }
          231  +  return rc;
          232  +}
          233  +
          234  +/*
          235  +** Syntax:
          236  +**
          237  +**   tclEval arg ?arg ...?
          238  +*/
          239  +static int tclEval_command(
          240  +  Th_Interp *interp,
          241  +  void *ctx,
          242  +  int argc,
          243  +  const char **argv,
          244  +  int *argl
          245  +){
          246  +  Tcl_Interp *tclInterp;
          247  +  Tcl_Obj *objPtr;
          248  +  int rc = TH_OK;
          249  +  int nResult;
          250  +  const char *zResult;
          251  +
          252  +  if( createTclInterp(interp, ctx)!=TH_OK ){
          253  +    return TH_ERROR;
          254  +  }
          255  +  if( argc<2 ){
          256  +    return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
          257  +  }
          258  +  tclInterp = GET_CTX_TCL_INTERP(ctx);
          259  +  if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
          260  +    Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
          261  +    return TH_ERROR;
          262  +  }
          263  +  rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
          264  +  if( rc!=TH_OK ){
          265  +    return rc;
          266  +  }
          267  +  Tcl_Preserve((ClientData)tclInterp);
          268  +  if( argc==2 ){
          269  +    objPtr = Tcl_NewStringObj(argv[1], argl[1]);
          270  +    Tcl_IncrRefCount(objPtr);
          271  +    rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
          272  +    Tcl_DecrRefCount(objPtr);
          273  +  }else{
          274  +    USE_ARGV_TO_OBJV();
          275  +    COPY_ARGV_TO_OBJV();
          276  +    objPtr = Tcl_ConcatObj(objc, objv);
          277  +    Tcl_IncrRefCount(objPtr);
          278  +    rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
          279  +    Tcl_DecrRefCount(objPtr);
          280  +    FREE_ARGV_TO_OBJV();
          281  +  }
          282  +  zResult = getTclResult(tclInterp, &nResult);
          283  +  Th_SetResult(interp, zResult, nResult);
          284  +  Tcl_Release((ClientData)tclInterp);
          285  +  rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
          286  +  return rc;
          287  +}
          288  +
          289  +/*
          290  +** Syntax:
          291  +**
          292  +**   tclExpr arg ?arg ...?
          293  +*/
          294  +static int tclExpr_command(
          295  +  Th_Interp *interp,
          296  +  void *ctx,
          297  +  int argc,
          298  +  const char **argv,
          299  +  int *argl
          300  +){
          301  +  Tcl_Interp *tclInterp;
          302  +  Tcl_Obj *objPtr;
          303  +  Tcl_Obj *resultObjPtr;
          304  +  int rc = TH_OK;
          305  +  int nResult;
          306  +  const char *zResult;
          307  +
          308  +  if( createTclInterp(interp, ctx)!=TH_OK ){
          309  +    return TH_ERROR;
          310  +  }
          311  +  if( argc<2 ){
          312  +    return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
          313  +  }
          314  +  tclInterp = GET_CTX_TCL_INTERP(ctx);
          315  +  if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
          316  +    Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
          317  +    return TH_ERROR;
          318  +  }
          319  +  rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
          320  +  if( rc!=TH_OK ){
          321  +    return rc;
          322  +  }
          323  +  Tcl_Preserve((ClientData)tclInterp);
          324  +  if( argc==2 ){
          325  +    objPtr = Tcl_NewStringObj(argv[1], argl[1]);
          326  +    Tcl_IncrRefCount(objPtr);
          327  +    rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr);
          328  +    Tcl_DecrRefCount(objPtr);
          329  +  }else{
          330  +    USE_ARGV_TO_OBJV();
          331  +    COPY_ARGV_TO_OBJV();
          332  +    objPtr = Tcl_ConcatObj(objc, objv);
          333  +    Tcl_IncrRefCount(objPtr);
          334  +    rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr);
          335  +    Tcl_DecrRefCount(objPtr);
          336  +    FREE_ARGV_TO_OBJV();
          337  +  }
          338  +  if( rc==TCL_OK ){
          339  +    zResult = Tcl_GetStringFromObj(resultObjPtr, &nResult);
          340  +  }else{
          341  +    zResult = getTclResult(tclInterp, &nResult);
          342  +  }
          343  +  Th_SetResult(interp, zResult, nResult);
          344  +  if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
          345  +  Tcl_Release((ClientData)tclInterp);
          346  +  rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
          347  +  return rc;
          348  +}
          349  +
          350  +/*
          351  +** Syntax:
          352  +**
          353  +**   tclInvoke command ?arg ...?
          354  +*/
          355  +static int tclInvoke_command(
          356  +  Th_Interp *interp,
          357  +  void *ctx,
          358  +  int argc,
          359  +  const char **argv,
          360  +  int *argl
          361  +){
          362  +  Tcl_Interp *tclInterp;
          363  +#if !defined(USE_TCL_EVALOBJV)
          364  +  Tcl_Command command;
          365  +  Tcl_CmdInfo cmdInfo;
          366  +#endif
          367  +  int rc = TH_OK;
          368  +  int nResult;
          369  +  const char *zResult;
          370  +#if !defined(USE_TCL_EVALOBJV)
          371  +  Tcl_Obj *objPtr;
          372  +#endif
          373  +  USE_ARGV_TO_OBJV();
          374  +
          375  +  if( createTclInterp(interp, ctx)!=TH_OK ){
          376  +    return TH_ERROR;
          377  +  }
          378  +  if( argc<2 ){
          379  +    return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
          380  +  }
          381  +  tclInterp = GET_CTX_TCL_INTERP(ctx);
          382  +  if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
          383  +    Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
          384  +    return TH_ERROR;
          385  +  }
          386  +  rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
          387  +  if( rc!=TH_OK ){
          388  +    return rc;
          389  +  }
          390  +  Tcl_Preserve((ClientData)tclInterp);
          391  +#if !defined(USE_TCL_EVALOBJV)
          392  +  objPtr = Tcl_NewStringObj(argv[1], argl[1]);
          393  +  Tcl_IncrRefCount(objPtr);
          394  +  command = Tcl_GetCommandFromObj(tclInterp, objPtr);
          395  +  if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){
          396  +    Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
          397  +    Tcl_DecrRefCount(objPtr);
          398  +    Tcl_Release((ClientData)tclInterp);
          399  +    return TH_ERROR;
          400  +  }
          401  +  if( !cmdInfo.objProc ){
          402  +    Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]);
          403  +    Tcl_DecrRefCount(objPtr);
          404  +    Tcl_Release((ClientData)tclInterp);
          405  +    return TH_ERROR;
          406  +  }
          407  +  Tcl_DecrRefCount(objPtr);
          408  +#endif
          409  +  COPY_ARGV_TO_OBJV();
          410  +#if defined(USE_TCL_EVALOBJV)
          411  +  rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
          412  +#else
          413  +  Tcl_ResetResult(tclInterp);
          414  +  rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv);
          415  +#endif
          416  +  FREE_ARGV_TO_OBJV();
          417  +  zResult = getTclResult(tclInterp, &nResult);
          418  +  Th_SetResult(interp, zResult, nResult);
          419  +  Tcl_Release((ClientData)tclInterp);
          420  +  rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
          421  +  return rc;
          422  +}
          423  +
          424  +/*
          425  +** Syntax:
          426  +**
          427  +**   th1Eval arg
          428  +*/
          429  +static int Th1EvalObjCmd(
          430  +  ClientData clientData,
          431  +  Tcl_Interp *interp,
          432  +  int objc,
          433  +  Tcl_Obj *CONST objv[]
          434  +){
          435  +  Th_Interp *th1Interp;
          436  +  int nArg;
          437  +  const char *arg;
          438  +  int rc;
          439  +
          440  +  if( objc!=2 ){
          441  +    Tcl_WrongNumArgs(interp, 1, objv, "arg");
          442  +    return TCL_ERROR;
          443  +  }
          444  +  th1Interp = (Th_Interp *)clientData;
          445  +  if( !th1Interp ){
          446  +    Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL);
          447  +    return TCL_ERROR;
          448  +  }
          449  +  arg = Tcl_GetStringFromObj(objv[1], &nArg);
          450  +  rc = Th_Eval(th1Interp, 0, arg, nArg);
          451  +  arg = Th_GetResult(th1Interp, &nArg);
          452  +  Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg));
          453  +  return rc;
          454  +}
          455  +
          456  +/*
          457  +** Syntax:
          458  +**
          459  +**   th1Expr arg
          460  +*/
          461  +static int Th1ExprObjCmd(
          462  +  ClientData clientData,
          463  +  Tcl_Interp *interp,
          464  +  int objc,
          465  +  Tcl_Obj *CONST objv[]
          466  +){
          467  +  Th_Interp *th1Interp;
          468  +  int nArg;
          469  +  const char *arg;
          470  +  int rc;
          471  +
          472  +  if( objc!=2 ){
          473  +    Tcl_WrongNumArgs(interp, 1, objv, "arg");
          474  +    return TCL_ERROR;
          475  +  }
          476  +  th1Interp = (Th_Interp *)clientData;
          477  +  if( !th1Interp ){
          478  +    Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL);
          479  +    return TCL_ERROR;
          480  +  }
          481  +  arg = Tcl_GetStringFromObj(objv[1], &nArg);
          482  +  rc = Th_Expr(th1Interp, arg, nArg);
          483  +  arg = Th_GetResult(th1Interp, &nArg);
          484  +  Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg));
          485  +  return rc;
          486  +}
          487  +
          488  +/*
          489  +** Array of Tcl integration commands.  Used when adding or removing the Tcl
          490  +** integration commands from TH1.
          491  +*/
          492  +static struct _Command {
          493  +  const char *zName;
          494  +  Th_CommandProc xProc;
          495  +  void *pContext;
          496  +} aCommand[] = {
          497  +  {"tclEval",   tclEval_command,   0},
          498  +  {"tclExpr",   tclExpr_command,   0},
          499  +  {"tclInvoke", tclInvoke_command, 0},
          500  +  {0, 0, 0}
          501  +};
          502  +
          503  +/*
          504  +** Called if the Tcl interpreter is deleted.  Removes the Tcl integration
          505  +** commands from the TH1 interpreter.
          506  + */
          507  +static void Th1DeleteProc(
          508  +  ClientData clientData,
          509  +  Tcl_Interp *interp
          510  +){
          511  +  int i;
          512  +  Th_Interp *th1Interp = (Th_Interp *)clientData;
          513  +  if( !th1Interp ) return;
          514  +  /* Remove the Tcl integration commands. */
          515  +  for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
          516  +    Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
          517  +  }
          518  +}
          519  +
          520  +/*
          521  +** When Tcl stubs support is enabled, attempts to dynamically load the Tcl
          522  +** shared library and fetch the function pointers necessary to create an
          523  +** interpreter and initialize the stubs mechanism; otherwise, simply setup
          524  +** the function pointers provided by the caller with the statically linked
          525  +** functions.
          526  + */
          527  +static int loadTcl(
          528  +  Th_Interp *interp,
          529  +  void **pLibrary,
          530  +  tcl_FindExecutableProc **pxFindExecutable,
          531  +  tcl_CreateInterpProc **pxCreateInterp
          532  +){
          533  +#if defined(USE_TCL_STUBS)
          534  +  char fileName[] = TCL_LIBRARY_NAME;
          535  +#endif
          536  +  if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){
          537  +    Th_ErrorMessage(interp,
          538  +        "invalid Tcl loader argument(s)", (const char *)"", 0);
          539  +    return TH_ERROR;
          540  +  }
          541  +#if defined(USE_TCL_STUBS)
          542  +  do {
          543  +    void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL);
          544  +    if( library ){
          545  +      tcl_FindExecutableProc *xFindExecutable;
          546  +      tcl_CreateInterpProc *xCreateInterp;
          547  +      const char *procName = TCL_FINDEXECUTABLE_NAME;
          548  +      xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1);
          549  +      if( !xFindExecutable ){
          550  +        xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName);
          551  +      }
          552  +      if( !xFindExecutable ){
          553  +        Th_ErrorMessage(interp,
          554  +            "could not locate Tcl_FindExecutable", (const char *)"", 0);
          555  +        dlclose(library);
          556  +        return TH_ERROR;
          557  +      }
          558  +      procName = TCL_CREATEINTERP_NAME;
          559  +      xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName + 1);
          560  +      if( !xCreateInterp ){
          561  +        xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName);
          562  +      }
          563  +      if( !xCreateInterp ){
          564  +        Th_ErrorMessage(interp,
          565  +            "could not locate Tcl_CreateInterp", (const char *)"", 0);
          566  +        dlclose(library);
          567  +        return TH_ERROR;
          568  +      }
          569  +      *pLibrary = library;
          570  +      *pxFindExecutable = xFindExecutable;
          571  +      *pxCreateInterp = xCreateInterp;
          572  +      return TH_OK;
          573  +    }
          574  +  } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
          575  +  Th_ErrorMessage(interp,
          576  +      "could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"",
          577  +      (const char *)"", 0);
          578  +  return TH_ERROR;
          579  +#else
          580  +  *pLibrary = 0;
          581  +  *pxFindExecutable = Tcl_FindExecutable;
          582  +  *pxCreateInterp = Tcl_CreateInterp;
          583  +  return TH_OK;
          584  +#endif
          585  +}
          586  +
          587  +/*
          588  +** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
          589  +** based on the supplied command line arguments.
          590  + */
          591  +static int setTclArguments(
          592  +  Tcl_Interp *pInterp,
          593  +  int argc,
          594  +  char **argv
          595  +){
          596  +  Tcl_Obj *objPtr;
          597  +  Tcl_Obj *resultObjPtr;
          598  +  Tcl_Obj *listPtr;
          599  +  int rc = TCL_OK;
          600  +  if( argc<=0 || !argv ){
          601  +    return TCL_OK;
          602  +  }
          603  +  objPtr = Tcl_NewStringObj(argv[0], -1);
          604  +  Tcl_IncrRefCount(objPtr);
          605  +  resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv0", NULL, objPtr,
          606  +      TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
          607  +  Tcl_DecrRefCount(objPtr);
          608  +  if( !resultObjPtr ){
          609  +    return TCL_ERROR;
          610  +  }
          611  +  objPtr = Tcl_NewIntObj(argc - 1);
          612  +  Tcl_IncrRefCount(objPtr);
          613  +  resultObjPtr = Tcl_SetVar2Ex(pInterp, "argc", NULL, objPtr,
          614  +      TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
          615  +  Tcl_DecrRefCount(objPtr);
          616  +  if( !resultObjPtr ){
          617  +    return TCL_ERROR;
          618  +  }
          619  +  listPtr = Tcl_NewListObj(0, NULL);
          620  +  Tcl_IncrRefCount(listPtr);
          621  +  if( argc>1 ){
          622  +    while( --argc ){
          623  +      objPtr = Tcl_NewStringObj(*++argv, -1);
          624  +      Tcl_IncrRefCount(objPtr);
          625  +      rc = Tcl_ListObjAppendElement(pInterp, listPtr, objPtr);
          626  +      Tcl_DecrRefCount(objPtr);
          627  +      if( rc!=TCL_OK ){
          628  +        break;
          629  +      }
          630  +    }
          631  +  }
          632  +  if( rc==TCL_OK ){
          633  +    resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv", NULL, listPtr,
          634  +        TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
          635  +    if( !resultObjPtr ){
          636  +      rc = TCL_ERROR;
          637  +    }
          638  +  }
          639  +  Tcl_DecrRefCount(listPtr);
          640  +  return rc;
          641  +}
          642  +
          643  +/*
          644  +** Creates and initializes a Tcl interpreter for use with the specified TH1
          645  +** interpreter.  Stores the created Tcl interpreter in the Tcl context supplied
          646  +** by the caller.
          647  + */
          648  +static int createTclInterp(
          649  +  Th_Interp *interp,
          650  +  void *pContext
          651  +){
          652  +  struct TclContext *tclContext = (struct TclContext *)pContext;
          653  +  int argc;
          654  +  char **argv;
          655  +  char *argv0 = 0;
          656  +  Tcl_Interp *tclInterp;
          657  +  char *setup;
          658  +
          659  +  if ( !tclContext ){
          660  +    Th_ErrorMessage(interp,
          661  +        "invalid Tcl context", (const char *)"", 0);
          662  +    return TH_ERROR;
          663  +  }
          664  +  if ( tclContext->interp ){
          665  +    return TH_OK;
          666  +  }
          667  +  if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable,
          668  +              &tclContext->xCreateInterp)!=TH_OK ){
          669  +    return TH_ERROR;
          670  +  }
          671  +  argc = tclContext->argc;
          672  +  argv = tclContext->argv;
          673  +  if( argc>0 && argv ){
          674  +    argv0 = argv[0];
          675  +  }
          676  +  tclContext->xFindExecutable(argv0);
          677  +  tclInterp = tclContext->xCreateInterp();
          678  +  if( !tclInterp ||
          679  +#if defined(USE_TCL_STUBS)
          680  +      !Tcl_InitStubs(tclInterp, "8.4", 0) ||
          681  +#endif
          682  +      Tcl_InterpDeleted(tclInterp) ){
          683  +    Th_ErrorMessage(interp,
          684  +        "could not create Tcl interpreter", (const char *)"", 0);
          685  +    return TH_ERROR;
          686  +  }
          687  +  tclContext->interp = tclInterp;
          688  +  if( Tcl_Init(tclInterp)!=TCL_OK ){
          689  +    Th_ErrorMessage(interp,
          690  +        "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1);
          691  +    Tcl_DeleteInterp(tclInterp);
          692  +    tclContext->interp = tclInterp = 0;
          693  +    return TH_ERROR;
          694  +  }
          695  +  if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
          696  +    Th_ErrorMessage(interp,
          697  +        "Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1);
          698  +    Tcl_DeleteInterp(tclInterp);
          699  +    tclContext->interp = tclInterp = 0;
          700  +    return TH_ERROR;
          701  +  }
          702  +  /* Add the TH1 integration commands to Tcl. */
          703  +  Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
          704  +  Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
          705  +  Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
          706  +  /* If necessary, evaluate the custom Tcl setup script. */
          707  +  setup = tclContext->setup;
          708  +  if( setup && Tcl_Eval(tclInterp, setup)!=TCL_OK ){
          709  +    Th_ErrorMessage(interp,
          710  +        "Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
          711  +    Tcl_DeleteInterp(tclInterp);
          712  +    tclContext->interp = tclInterp = 0;
          713  +    return TH_ERROR;
          714  +  }
          715  +  return TH_OK;
          716  +}
          717  +
          718  +/*
          719  +** Register the Tcl language commands with interpreter interp.
          720  +** Usually this is called soon after interpreter creation.
          721  +*/
          722  +int th_register_tcl(
          723  +  Th_Interp *interp,
          724  +  void *pContext
          725  +){
          726  +  int i;
          727  +  /* Add the Tcl integration commands to TH1. */
          728  +  for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
          729  +    void *ctx;
          730  +    if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
          731  +    ctx = aCommand[i].pContext;
          732  +    /* Use Tcl interpreter for context? */
          733  +    if( !ctx ) ctx = pContext;
          734  +    Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
          735  +  }
          736  +  return TH_OK;
          737  +}
          738  +
          739  +#endif /* FOSSIL_ENABLE_TCL */

Changes to src/timeline.c.

    43     43   }
    44     44   
    45     45   
    46     46   /*
    47     47   ** Generate a hyperlink to a version.
    48     48   */
    49     49   void hyperlink_to_uuid(const char *zUuid){
    50         -  char zShortUuid[UUID_SIZE+1];
    51         -  shorten_uuid(zShortUuid, zUuid);
    52         -  if( g.okHistory ){
    53         -    @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%s(zShortUuid)]</a>
           50  +  char z[UUID_SIZE+1];
           51  +  shorten_uuid(z, zUuid);
           52  +  if( g.perm.Hyperlink ){
           53  +    @ %z(xhref("class='timelineHistLink'","%R/info/%s",z))[%s(z)]</a>
    54     54     }else{
    55         -    @ <b>[%s(zShortUuid)]</b>
    56         -  }
    57         -}
    58         -
    59         -/*
    60         -** Generate a hyperlink that invokes javascript to highlight
    61         -** a version on mouseover.
    62         -*/
    63         -void hyperlink_to_uuid_with_mouseover(
    64         -  const char *zUuid,   /* The UUID to display */
    65         -  const char *zIn,     /* Javascript proc for mouseover */
    66         -  const char *zOut,    /* Javascript proc for mouseout */
    67         -  int id               /* Argument to javascript procs */
    68         -){
    69         -  char zShortUuid[UUID_SIZE+1];
    70         -  shorten_uuid(zShortUuid, zUuid);
    71         -  if( g.okHistory ){
    72         -    @ <a onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")'
    73         -    @    href="%s(g.zBaseURL)/vinfo/%s(zShortUuid)">[%s(zShortUuid)]</a>
    74         -  }else{
    75         -    @ <b onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")'>
    76         -    @ [%s(zShortUuid)]</b>
           55  +    @ <span class="timelineHistDsp">[%s(z)]</span>
    77     56     }
    78     57   }
    79     58   
    80     59   /*
    81     60   ** Generate a hyperlink to a diff between two versions.
    82     61   */
    83     62   void hyperlink_to_diff(const char *zV1, const char *zV2){
    84         -  if( g.okHistory ){
           63  +  if( g.perm.Hyperlink ){
    85     64       if( zV2==0 ){
    86         -      @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
           65  +      @ %z(href("%R/diff?v2=%s",zV1))[diff]</a>
    87     66       }else{
    88         -      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
           67  +      @ %z(href("%R/diff?v1=%s&v2=%s",zV1,zV2))[diff]</a>
    89     68       }
    90     69     }
    91     70   }
    92     71   
    93     72   /*
    94     73   ** Generate a hyperlink to a date & time.
    95     74   */
    96     75   void hyperlink_to_date(const char *zDate, const char *zSuffix){
    97     76     if( zSuffix==0 ) zSuffix = "";
    98         -  if( g.okHistory ){
    99         -    @ <a href="%s(g.zTop)/timeline?c=%T(zDate)">%s(zDate)</a>%s(zSuffix)
           77  +  if( g.perm.Hyperlink ){
           78  +    @ %z(href("%R/timeline?c=%T",zDate))%s(zDate)</a>%s(zSuffix)
   100     79     }else{
   101     80       @ %s(zDate)%s(zSuffix)
   102     81     }
   103     82   }
   104     83   
   105     84   /*
   106     85   ** Generate a hyperlink to a user.  This will link to a timeline showing
   107     86   ** events by that user.  If the date+time is specified, then the timeline
   108     87   ** is centered on that date+time.
   109     88   */
   110     89   void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
           90  +  if( zU==0 || zU[0]==0 ) zU = "anonymous";
   111     91     if( zSuf==0 ) zSuf = "";
   112         -  if( g.okHistory ){
           92  +  if( g.perm.Hyperlink ){
   113     93       if( zD && zD[0] ){
   114         -      @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf)
           94  +      @ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
   115     95       }else{
   116         -      @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
           96  +      @ %z(href("%R/timeline?u=%T",zU))%h(zU)</a>%s(zSuf)
   117     97       }
   118     98     }else{
   119     99       @ %s(zU)
   120    100     }
   121    101   }
   122    102   
   123         -/*
   124         -** Count the number of primary non-branch children for the given check-in.
   125         -**
   126         -** A primary child is one where the parent is the primary parent, not
   127         -** a merge parent.
   128         -**
   129         -** A non-branch child is one which is on the same branch as the parent.
   130         -*/
   131         -int count_nonbranch_children(int pid){
   132         -  int nNonBranch;
   133         -  static const char zSql[] = 
   134         -    @ SELECT count(*) FROM plink
   135         -    @  WHERE pid=%d AND isprim
   136         -    @    AND coalesce((SELECT value FROM tagxref
   137         -    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
   138         -    @       =coalesce((SELECT value FROM tagxref
   139         -    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
   140         -  ;
   141         -  nNonBranch = db_int(0, zSql, pid, TAG_BRANCH, TAG_BRANCH);
   142         -  return nNonBranch;
   143         -}
   144         -
   145    103   /*
   146    104   ** Allowed flags for the tmFlags argument to www_print_timeline
   147    105   */
   148    106   #if INTERFACE
   149    107   #define TIMELINE_ARTID    0x0001  /* Show artifact IDs on non-check-in lines */
   150    108   #define TIMELINE_LEAFONLY 0x0002  /* Show "Leaf", but not "Merge", "Fork" etc */
   151    109   #define TIMELINE_BRIEF    0x0004  /* Combine adjacent elements of same object */
   152    110   #define TIMELINE_GRAPH    0x0008  /* Compute a graph */
   153    111   #define TIMELINE_DISJOINT 0x0010  /* Elements are not contiguous */
          112  +#define TIMELINE_FCHANGES 0x0020  /* Detail file changes */
          113  +#define TIMELINE_BRCOLOR  0x0040  /* Background color by branch name */
          114  +#define TIMELINE_UCOLOR   0x0080  /* Background color by user */
   154    115   #endif
          116  +
          117  +/*
          118  +** Hash a string and use the hash to determine a background color.
          119  +*/
          120  +char *hash_color(const char *z){
          121  +  int i;                       /* Loop counter */
          122  +  unsigned int h = 0;          /* Hash on the branch name */
          123  +  int r, g, b;                 /* Values for red, green, and blue */
          124  +  int h1, h2, h3, h4;          /* Elements of the hash value */
          125  +  int mx, mn;                  /* Components of HSV */
          126  +  static char zColor[10];      /* The resulting color */
          127  +  static int ix[2] = {0,0};    /* Color chooser parameters */
          128  +
          129  +  if( ix[0]==0 ){
          130  +    if( db_get_boolean("white-foreground", 0) ){
          131  +      ix[0] = 140;
          132  +      ix[1] = 40;
          133  +    }else{
          134  +      ix[0] = 216;
          135  +      ix[1] = 16;
          136  +    }
          137  +  }
          138  +  for(i=0; z[i]; i++ ){
          139  +    h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i];
          140  +  }
          141  +  h1 = h % 6;  h /= 6;
          142  +  h3 = h % 30; h /= 30;
          143  +  h4 = h % 40; h /= 40;
          144  +  mx = ix[0] - h3;
          145  +  mn = mx - h4 - ix[1];
          146  +  h2 = (h%(mx - mn)) + mn;
          147  +  switch( h1 ){
          148  +    case 0:  r = mx; g = h2, b = mn;  break;
          149  +    case 1:  r = h2; g = mx, b = mn;  break;
          150  +    case 2:  r = mn; g = mx, b = h2;  break;
          151  +    case 3:  r = mn; g = h2, b = mx;  break;
          152  +    case 4:  r = h2; g = mn, b = mx;  break;
          153  +    default: r = mx; g = mn, b = h2;  break;
          154  +  }
          155  +  sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
          156  +  return zColor;
          157  +}
          158  +
          159  +/*
          160  +** COMMAND:  test-hash-color
          161  +**
          162  +** Usage: %fossil test-hash-color TAG ...
          163  +**
          164  +** Print out the color names associated with each tag.  Used for
          165  +** testing the hash_color() function.
          166  +*/
          167  +void test_hash_color(void){
          168  +  int i;
          169  +  for(i=2; i<g.argc; i++){
          170  +    fossil_print("%20s: %s\n", g.argv[i], hash_color(g.argv[i]));
          171  +  }
          172  +}
   155    173   
   156    174   /*
   157    175   ** Output a timeline in the web format given a query.  The query
   158    176   ** should return these columns:
   159    177   **
   160    178   **    0.  rid
   161    179   **    1.  UUID
   162    180   **    2.  Date/Time
   163    181   **    3.  Comment string
   164    182   **    4.  User
   165    183   **    5.  True if is a leaf
   166    184   **    6.  background color
   167         -**    7.  type ("ci", "w", "t")
          185  +**    7.  type ("ci", "w", "t", "e", "g", "div")
   168    186   **    8.  list of symbolic tags.
   169         -**    9.  tagid for ticket or wiki
          187  +**    9.  tagid for ticket or wiki or event
   170    188   **   10.  Short comment to user for repeated tickets and wiki
   171    189   */
   172    190   void www_print_timeline(
   173    191     Stmt *pQuery,          /* Query to implement the timeline */
   174    192     int tmFlags,           /* Flags controlling display behavior */
          193  +  const char *zThisUser, /* Suppress links to this user */
          194  +  const char *zThisTag,  /* Suppress links to this tag */
   175    195     void (*xExtra)(int)    /* Routine to call on each line of display */
   176    196   ){
   177         -  int wikiFlags;
   178    197     int mxWikiLen;
   179    198     Blob comment;
   180    199     int prevTagid = 0;
   181    200     int suppressCnt = 0;
   182    201     char zPrevDate[20];
   183    202     GraphContext *pGraph = 0;
          203  +  int prevWasDivider = 0;     /* True if previous output row was <hr> */
          204  +  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
          205  +  Stmt fchngQuery;            /* Query for file changes on check-ins */
          206  +  static Stmt qbranch;
          207  +  int pendingEndTr = 0;       /* True if a </td></tr> is needed */
   184    208   
   185    209     zPrevDate[0] = 0;
   186    210     mxWikiLen = db_get_int("timeline-max-comment", 0);
   187         -  if( db_get_boolean("timeline-block-markup", 0) ){
   188         -    wikiFlags = WIKI_INLINE;
   189         -  }else{
   190         -    wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
   191         -  }
   192    211     if( tmFlags & TIMELINE_GRAPH ){
   193    212       pGraph = graph_init();
   194         -    @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
          213  +    /* style is not moved to css, because this is
          214  +    ** a technical div for the timeline graph
          215  +    */
          216  +    @ <div id="canvas" style="position:relative;height:0px;width:0px;"
          217  +    @  onclick="clickOnGraph(event)"></div>
   195    218     }
          219  +  db_static_prepare(&qbranch,
          220  +    "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
          221  +    TAG_BRANCH
          222  +  );
   196    223   
   197         -  @ <table cellspacing=0 border=0 cellpadding=0>
          224  +  @ <table id="timelineTable" class="timelineTable"
          225  +  @  onclick="clickOnGraph(event)">
   198    226     blob_zero(&comment);
   199    227     while( db_step(pQuery)==SQLITE_ROW ){
   200    228       int rid = db_column_int(pQuery, 0);
   201    229       const char *zUuid = db_column_text(pQuery, 1);
   202    230       int isLeaf = db_column_int(pQuery, 5);
   203    231       const char *zBgClr = db_column_text(pQuery, 6);
   204    232       const char *zDate = db_column_text(pQuery, 2);
   205    233       const char *zType = db_column_text(pQuery, 7);
   206    234       const char *zUser = db_column_text(pQuery, 4);
   207    235       const char *zTagList = db_column_text(pQuery, 8);
   208    236       int tagid = db_column_int(pQuery, 9);
          237  +    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
          238  +    const char *zBr = 0;      /* Branch */
   209    239       int commentColumn = 3;    /* Column containing comment text */
          240  +    int modPending;           /* Pending moderation */
   210    241       char zTime[8];
          242  +
          243  +    modPending =  moderation_pending(rid);
   211    244       if( tagid ){
          245  +      if( modPending ) tagid = -tagid;
   212    246         if( tagid==prevTagid ){
   213    247           if( tmFlags & TIMELINE_BRIEF ){
   214    248             suppressCnt++;
   215    249             continue;
   216    250           }else{
   217    251             commentColumn = 10;
   218    252           }
   219    253         }
   220    254       }
   221    255       prevTagid = tagid;
   222    256       if( suppressCnt ){
   223         -      @ <tr><td><td><td>
   224         -      @ <small><i>... %d(suppressCnt) similar
   225         -      @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
          257  +      @ <span class="timelineDisabled">... %d(suppressCnt) similar
          258  +      @ event%s(suppressCnt>1?"s":"") omitted.</span>
   226    259         suppressCnt = 0;
   227    260       }
   228         -    if( strcmp(zType,"div")==0 ){
   229         -      @ <tr><td colspan=3><hr></td></tr>
          261  +    if( pendingEndTr ){
          262  +      @ </td></tr>
          263  +      pendingEndTr = 0;
          264  +    }
          265  +    if( fossil_strcmp(zType,"div")==0 ){
          266  +      if( !prevWasDivider ){
          267  +        @ <tr><td colspan="3"><hr /></td></tr>
          268  +      }
          269  +      prevWasDivider = 1;
   230    270         continue;
   231    271       }
          272  +    prevWasDivider = 0;
   232    273       if( memcmp(zDate, zPrevDate, 10) ){
   233         -      sprintf(zPrevDate, "%.10s", zDate);
          274  +      sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
   234    275         @ <tr><td>
   235         -      @   <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
   236         -      @ </td></tr>
          276  +      @   <div class="divider">%s(zPrevDate)</div>
          277  +      @ </td><td></td><td></td></tr>
   237    278       }
   238    279       memcpy(zTime, &zDate[11], 5);
   239    280       zTime[5] = 0;
   240    281       @ <tr>
   241         -    @ <td valign="top" align="right">%s(zTime)</td>
   242         -    @ <td width="20" align="left" valign="top">
   243         -    if( pGraph && zType[0]=='c' ){
   244         -      int nParent = 0;
   245         -      int aParent[32];
   246         -      const char *zBr;
   247         -      int gidx;
   248         -      static Stmt qparent;
   249         -      static Stmt qbranch;
   250         -      db_static_prepare(&qparent,
   251         -        "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC /*sort*/"
   252         -      );
   253         -      db_static_prepare(&qbranch,
   254         -        "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
   255         -        TAG_BRANCH
   256         -      );
   257         -      db_bind_int(&qparent, ":rid", rid);
   258         -      while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
   259         -        aParent[nParent++] = db_column_int(&qparent, 0);
   260         -      }
   261         -      db_reset(&qparent);
          282  +    @ <td class="timelineTime">%s(zTime)</td>
          283  +    @ <td class="timelineGraph">
          284  +    if( tmFlags & TIMELINE_UCOLOR )  zBgClr = zUser ? hash_color(zUser) : 0;
          285  +    if( zType[0]=='c'
          286  +    && (pGraph || zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0)
          287  +    ){
          288  +      db_reset(&qbranch);
   262    289         db_bind_int(&qbranch, ":rid", rid);
   263    290         if( db_step(&qbranch)==SQLITE_ROW ){
   264    291           zBr = db_column_text(&qbranch, 0);
   265    292         }else{
   266    293           zBr = "trunk";
   267    294         }
   268         -      gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
          295  +      if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
          296  +        if( zBr==0 || strcmp(zBr,"trunk")==0 ){
          297  +          zBgClr = 0;
          298  +        }else{
          299  +          zBgClr = hash_color(zBr);
          300  +        }
          301  +      }
          302  +    }
          303  +    if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){
          304  +      int nParent = 0;
          305  +      int aParent[32];
          306  +      int gidx;
          307  +      static Stmt qparent;
          308  +      db_static_prepare(&qparent,
          309  +        "SELECT pid FROM plink"
          310  +        " WHERE cid=:rid AND pid NOT IN phantom"
          311  +        " ORDER BY isprim DESC /*sort*/"
          312  +      );
          313  +      db_bind_int(&qparent, ":rid", rid);
          314  +      while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
          315  +        aParent[nParent++] = db_column_int(&qparent, 0);
          316  +      }
          317  +      db_reset(&qparent);
          318  +      gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
          319  +                           zUuid, isLeaf);
   269    320         db_reset(&qbranch);
   270    321         @ <div id="m%d(gidx)"></div>
   271    322       }
          323  +    @</td>
   272    324       if( zBgClr && zBgClr[0] ){
   273         -      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
          325  +      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
   274    326       }else{
   275         -      @ <td valign="top" align="left">
          327  +      @ <td class="timelineTableCell">
          328  +    }
          329  +    if( pGraph && zType[0]!='c' ){
          330  +      @ &bull;
          331  +    }
          332  +    if( modPending ){
          333  +      @ <span class="modpending">(Awaiting Moderator Approval)</span>
   276    334       }
   277    335       if( zType[0]=='c' ){
   278    336         hyperlink_to_uuid(zUuid);
   279    337         if( isLeaf ){
   280    338           if( db_exists("SELECT 1 FROM tagxref"
   281    339                         " WHERE rid=%d AND tagid=%d AND tagtype>0",
   282    340                         rid, TAG_CLOSED) ){
   283         -          @ <b>Closed-Leaf:</b>
          341  +          @ <span class="timelineLeaf">Closed-Leaf:</span>
   284    342           }else{
   285         -          @ <b>Leaf:</b>
          343  +          @ <span class="timelineLeaf">Leaf:</span>
   286    344           }
   287    345         }
          346  +    }else if( zType[0]=='e' && tagid ){
          347  +      hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
   288    348       }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
   289    349         hyperlink_to_uuid(zUuid);
   290    350       }
   291    351       db_column_blob(pQuery, commentColumn, &comment);
   292         -    if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
          352  +    if( zType[0]!='c' ){
          353  +      /* Comments for anything other than a check-in are generated by
          354  +      ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
          355  +      wiki_convert(&comment, 0, WIKI_INLINE);
          356  +    }else if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
   293    357         Blob truncated;
   294    358         blob_zero(&truncated);
   295    359         blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
   296    360         blob_append(&truncated, "...", 3);
   297         -      wiki_convert(&truncated, 0, wikiFlags);
          361  +      @ %w(blob_str(&truncated))
   298    362         blob_reset(&truncated);
   299    363       }else{
   300         -      wiki_convert(&comment, 0, wikiFlags);
          364  +      @ %w(blob_str(&comment))
   301    365       }
   302    366       blob_reset(&comment);
   303         -    if( zTagList && zTagList[0] ){
   304         -      @ (user: %h(zUser), tags: %h(zTagList))
          367  +
          368  +    /* Generate the "user: USERNAME" at the end of the comment, together
          369  +    ** with a hyperlink to another timeline for that user.
          370  +    */
          371  +    if( zTagList && zTagList[0]==0 ) zTagList = 0;
          372  +    if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
          373  +      char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
          374  +      @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
   305    375       }else{
   306         -      @ (user: %h(zUser))
          376  +      @ (user: %h(zDispUser)%s(zTagList?",":"\051")
   307    377       }
          378  +
          379  +    /* Generate a "detail" link for tags. */
          380  +    if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
          381  +      @ [%z(href("%R/info/%S",zUuid))details</a>]
          382  +    }
          383  +
          384  +    /* Generate the "tags: TAGLIST" at the end of the comment, together
          385  +    ** with hyperlinks to the tag list.
          386  +    */
          387  +    if( zTagList ){
          388  +      if( g.perm.Hyperlink ){
          389  +        int i;
          390  +        const char *z = zTagList;
          391  +        Blob links;
          392  +        blob_zero(&links);
          393  +        while( z && z[0] ){
          394  +          for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
          395  +          if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
          396  +            blob_appendf(&links,
          397  +                  "%z%#h</a>%.2s",
          398  +                  href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
          399  +            );
          400  +          }else{
          401  +            blob_appendf(&links, "%#h", i+2, z);
          402  +          }
          403  +          if( z[i]==0 ) break;
          404  +          z += i+2;
          405  +        }
          406  +        @ tags: %s(blob_str(&links)))
          407  +        blob_reset(&links);
          408  +      }else{
          409  +        @ tags: %h(zTagList))
          410  +      }
          411  +    }
          412  +
          413  +
          414  +    /* Generate extra hyperlinks at the end of the comment */
   308    415       if( xExtra ){
   309    416         xExtra(rid);
   310    417       }
   311         -    @ </td></tr>
          418  +
          419  +    /* Generate the file-change list if requested */
          420  +    if( (tmFlags & TIMELINE_FCHANGES)!=0 && zType[0]=='c' && g.perm.Hyperlink ){
          421  +      int inUl = 0;
          422  +      if( !fchngQueryInit ){
          423  +        db_prepare(&fchngQuery,
          424  +          "SELECT (pid==0) AS isnew,"
          425  +          "       (fid==0) AS isdel,"
          426  +          "       (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name,"
          427  +          "       (SELECT uuid FROM blob WHERE rid=fid),"
          428  +          "       (SELECT uuid FROM blob WHERE rid=pid),"
          429  +          "       (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm"
          430  +          "  FROM mlink"
          431  +          " WHERE mid=:mid AND (pid!=fid OR pfnid>0)"
          432  +          "   AND (fid>0 OR"
          433  +               "   fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=:mid))"
          434  +          " ORDER BY 3 /*sort*/"
          435  +        );
          436  +        fchngQueryInit = 1;
          437  +      }
          438  +      db_bind_int(&fchngQuery, ":mid", rid);
          439  +      while( db_step(&fchngQuery)==SQLITE_ROW ){
          440  +        const char *zFilename = db_column_text(&fchngQuery, 2);
          441  +        int isNew = db_column_int(&fchngQuery, 0);
          442  +        int isDel = db_column_int(&fchngQuery, 1);
          443  +        const char *zOldName = db_column_text(&fchngQuery, 5);
          444  +        const char *zOld = db_column_text(&fchngQuery, 4);
          445  +        const char *zNew = db_column_text(&fchngQuery, 3);
          446  +        if( !inUl ){
          447  +          @ <ul class="filelist">
          448  +          inUl = 1;
          449  +        }
          450  +        if( isNew ){
          451  +          @ <li> %h(zFilename) (new file) &nbsp;
          452  +          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
          453  +          @ [view]</a></li>
          454  +        }else if( isDel ){
          455  +          @ <li> %h(zFilename) (deleted)</li>
          456  +        }else if( fossil_strcmp(zOld,zNew)==0 && zOldName!=0 ){
          457  +          @ <li> %h(zOldName) &rarr; %h(zFilename)
          458  +          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
          459  +          @ [view]</a></li>
          460  +        }else{
          461  +          if( zOldName!=0 ){
          462  +            @ <li> %h(zOldName) &rarr; %h(zFilename)
          463  +          }else{
          464  +            @ <li> %h(zFilename) &nbsp;
          465  +          }
          466  +          @ %z(xhref("target='diffwindow'","%R/fdiff?v1=%S&v2=%S",zOld,zNew))
          467  +          @ [diff]</a></li>
          468  +        }
          469  +      }
          470  +      db_reset(&fchngQuery);
          471  +      if( inUl ){
          472  +        @ </ul>
          473  +      }
          474  +    }
          475  +    pendingEndTr = 1;
   312    476     }
   313    477     if( suppressCnt ){
   314         -    @ <tr><td><td><td>
   315         -    @ <small><i>... %d(suppressCnt) similar
   316         -    @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
          478  +    @ <span class="timelineDisabled">... %d(suppressCnt) similar
          479  +    @ event%s(suppressCnt>1?"s":"") omitted.</span>
   317    480       suppressCnt = 0;
          481  +  }
          482  +  if( pendingEndTr ){
          483  +    @ </td></tr>
   318    484     }
   319    485     if( pGraph ){
   320    486       graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
   321    487       if( pGraph->nErr ){
   322    488         graph_free(pGraph);
   323    489         pGraph = 0;
   324    490       }else{
   325         -      @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
          491  +      int w;
          492  +      /* style is not moved to css, because this is
          493  +      ** a technical div for the timeline graph
          494  +      */
          495  +      w = (pGraph->mxRail+1)*pGraph->iRailPitch + 10;
          496  +      @ <tr><td></td><td>
          497  +      @ <div id="grbtm" style="width:%d(w)px;"></div>
          498  +      @ </td><td></td></tr>
   326    499       }
   327    500     }
   328    501     @ </table>
   329         -  timeline_output_graph_javascript(pGraph);
          502  +  if( fchngQueryInit ) db_finalize(&fchngQuery);
          503  +  timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
   330    504   }
   331    505   
   332    506   /*
   333    507   ** Generate all of the necessary javascript to generate a timeline
   334    508   ** graph.
   335    509   */
   336         -void timeline_output_graph_javascript(GraphContext *pGraph){
   337         -  if( pGraph && pGraph->nErr==0 ){
          510  +void timeline_output_graph_javascript(
          511  +  GraphContext *pGraph,     /* The graph to be displayed */
          512  +  int omitDescenders,       /* True to omit descenders */
          513  +  int fileDiff              /* True for file diff.  False for check-in diff */
          514  +){
          515  +  if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
   338    516       GraphRow *pRow;
   339    517       int i;
   340    518       char cSep;
   341         -    @ <script type="text/JavaScript">
          519  +    @ <script  type="text/JavaScript">
          520  +    @ /* <![CDATA[ */
          521  +    @ var railPitch=%d(pGraph->iRailPitch);
          522  +
          523  +    /* the rowinfo[] array contains all the information needed to generate
          524  +    ** the graph.  Each entry contains information for a single row:
          525  +    **
          526  +    **   id:  The id of the <div> element for the row. This is an integer.
          527  +    **        to get an actual id, prepend "m" to the integer.  The top node
          528  +    **        is 1 and numbers increase moving down the timeline.
          529  +    **   bg:  The background color for this row
          530  +    **    r:  The "rail" that the node for this row sits on.  The left-most
          531  +    **        rail is 0 and the number increases to the right.
          532  +    **    d:  True if there is a "descender" - an arrow coming from the bottom
          533  +    **        of the page straight up to this node.
          534  +    **   mo:  "merge-out".  If non-zero, this is one more than the x-coordinate
          535  +    **        for the upward portion of a merge arrow.  The merge arrow goes up
          536  +    **        to the row identified by mu:.  If this value is zero then
          537  +    **        node has no merge children and no merge-out line is drawn.
          538  +    **   mu:  The id of the row which is the top of the merge-out arrow.
          539  +    **    u:  Draw a thick child-line out of the top of this node and up to
          540  +    **        the node with an id equal to this value.  0 if there is no
          541  +    **        thick-line riser.
          542  +    **    f:  0x01: a leaf node.
          543  +    **   au:  An array of integers that define thick-line risers for branches.
          544  +    **        The integers are in pairs.  For each pair, the first integer is
          545  +    **        is the rail on which the riser should run and the second integer
          546  +    **        is the id of the node upto which the riser should run.
          547  +    **   mi:  "merge-in".  An array of integer x-coordinates from which
          548  +    **        merge arrows should be drawn into this node.  If the value is
          549  +    **        negative, then the x-coordinate is the absolute value of mi[]
          550  +    **        and a thin merge-arrow descender is drawn to the bottom of
          551  +    **        the screen.
          552  +    **    h:  The SHA1 hash of the object being graphed
          553  +    */
   342    554       cgi_printf("var rowinfo = [\n");
   343    555       for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
   344         -      cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
   345         -        pRow->idx,
   346         -        pRow->zBgClr,
   347         -        pRow->iRail,
   348         -        pRow->bDescender,
   349         -        pRow->mergeOut,
   350         -        pRow->mergeUpto,
   351         -        pRow->aiRaiser[pRow->iRail]
          556  +      int mo = pRow->mergeOut;
          557  +      if( mo<0 ){
          558  +        mo = 0;
          559  +      }else{
          560  +        mo = (mo/4)*pGraph->iRailPitch - 3 + 4*(mo&3);
          561  +      }
          562  +      cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
          563  +        pRow->idx,                      /* id */
          564  +        pRow->zBgClr,                   /* bg */
          565  +        pRow->iRail,                    /* r */
          566  +        pRow->bDescender,               /* d */
          567  +        mo,                             /* mo */
          568  +        pRow->mergeUpto,                /* mu */
          569  +        pRow->aiRiser[pRow->iRail],     /* u */
          570  +        pRow->isLeaf ? 1 : 0            /* f */
   352    571         );
          572  +      /* u */
   353    573         cSep = '[';
   354    574         for(i=0; i<GR_MAX_RAIL; i++){
   355    575           if( i==pRow->iRail ) continue;
   356         -        if( pRow->aiRaiser[i]>0 ){
   357         -          cgi_printf("%c%d,%d", cSep, pGraph->railMap[i], pRow->aiRaiser[i]);
          576  +        if( pRow->aiRiser[i]>0 ){
          577  +          cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
   358    578             cSep = ',';
   359    579           }
   360    580         }
   361    581         if( cSep=='[' ) cgi_printf("[");
   362    582         cgi_printf("],mi:");
          583  +      /* mi */
   363    584         cSep = '[';
   364    585         for(i=0; i<GR_MAX_RAIL; i++){
   365         -        if( pRow->mergeIn & (1<<i) ){
   366         -          cgi_printf("%c%d", cSep, pGraph->railMap[i]);
          586  +        if( pRow->mergeIn[i] ){
          587  +          int mi = i*pGraph->iRailPitch - 8 + 4*pRow->mergeIn[i];
          588  +          if( pRow->mergeDown & (1<<i) ) mi = -mi;
          589  +          cgi_printf("%c%d", cSep, mi);
   367    590             cSep = ',';
   368    591           }
   369    592         }
   370    593         if( cSep=='[' ) cgi_printf("[");
   371         -      cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n");
          594  +      cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
   372    595       }
   373    596       cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
   374    597       graph_free(pGraph);
   375         -    @ var canvasDiv = document.getElementById("canvas");
   376         -    @ var realCanvas = null;
          598  +    @ var canvasDiv = gebi("canvas");
   377    599       @ function drawBox(color,x0,y0,x1,y1){
   378    600       @   var n = document.createElement("div");
   379    601       @   if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
   380    602       @   if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
   381    603       @   var w = x1-x0+1;
   382    604       @   var h = y1-y0+1;
   383    605       @   n.style.position = "absolute";
................................................................................
   384    606       @   n.style.overflow = "hidden";
   385    607       @   n.style.left = x0+"px";
   386    608       @   n.style.top = y0+"px";
   387    609       @   n.style.width = w+"px";
   388    610       @   n.style.height = h+"px";
   389    611       @   n.style.backgroundColor = color;
   390    612       @   canvasDiv.appendChild(n);
          613  +    @   return n;
   391    614       @ }
   392    615       @ function absoluteY(id){
   393         -    @   var obj = document.getElementById(id);
          616  +    @   var obj = gebi(id);
   394    617       @   if( !obj ) return;
   395    618       @   var top = 0;
   396    619       @   if( obj.offsetParent ){
   397    620       @     do{
   398    621       @       top += obj.offsetTop;
   399    622       @     }while( obj = obj.offsetParent );
   400    623       @   }
   401    624       @   return top;
   402    625       @ }
   403    626       @ function absoluteX(id){
   404         -    @   var obj = document.getElementById(id);
          627  +    @   var obj = gebi(id);
   405    628       @   if( !obj ) return;
   406    629       @   var left = 0;
   407    630       @   if( obj.offsetParent ){
   408    631       @     do{
   409    632       @       left += obj.offsetLeft;
   410    633       @     }while( obj = obj.offsetParent );
   411    634       @   }
   412    635       @   return left;
   413    636       @ }
   414    637       @ function drawUpArrow(x,y0,y1){
   415    638       @   drawBox("black",x,y0,x+1,y1);
   416         -    @   if( y0+8>=y1 ){
          639  +    @   if( y0+10>=y1 ){
   417    640       @     drawBox("black",x-1,y0+1,x+2,y0+2);
   418    641       @     drawBox("black",x-2,y0+3,x+3,y0+4);
   419    642       @   }else{
   420    643       @     drawBox("black",x-1,y0+2,x+2,y0+4);
   421    644       @     drawBox("black",x-2,y0+5,x+3,y0+7);
   422    645       @   }
   423    646       @ }
   424    647       @ function drawThinArrow(y,xFrom,xTo){
   425    648       @   if( xFrom<xTo ){
   426    649       @     drawBox("black",xFrom,y,xTo,y);
   427         -    @     drawBox("black",xTo-4,y-1,xTo-2,y+1);
   428         -    @     if( xTo>xFrom-8 ) drawBox("black",xTo-6,y-2,xTo-5,y+2);
          650  +    @     drawBox("black",xTo-3,y-1,xTo-2,y+1);
          651  +    @     drawBox("black",xTo-4,y-2,xTo-4,y+2);
   429    652       @   }else{
   430    653       @     drawBox("black",xTo,y,xFrom,y);
   431         -    @     drawBox("black",xTo+2,y-1,xTo+4,y+1);
   432         -    @     if( xTo+8<xFrom ) drawBox("black",xTo+5,y-2,xTo+6,y+2);
          654  +    @     drawBox("black",xTo+2,y-1,xTo+3,y+1);
          655  +    @     drawBox("black",xTo+4,y-2,xTo+4,y+2);
   433    656       @   }
   434    657       @ }
   435    658       @ function drawThinLine(x0,y0,x1,y1){
   436    659       @   drawBox("black",x0,y0,x1,y1);
   437    660       @ }
   438    661       @ function drawNode(p, left, btm){
   439    662       @   drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6);
   440    663       @   drawBox(p.bg,p.x-4,p.y-4,p.x+5,p.y+5);
   441         -    @   if( p.u>0 ){
   442         -    @     var u = rowinfo[p.u-1];
   443         -    @     drawUpArrow(p.x, u.y+6, p.y-5);
   444         -    @   }
   445         -    @   if( p.d ){
   446         -    @     drawUpArrow(p.x, p.y+6, btm);
   447         -    @   } 
   448         -    @   if( p.mo>=0 ){
   449         -    @     var x1 = p.mo*20 + left;
          664  +    @   if( p.u>0 ) drawUpArrow(p.x, rowinfo[p.u-1].y+6, p.y-5);
          665  +    @   if( p.f&1 ) drawBox("black",p.x-1,p.y-1,p.x+2,p.y+2);
          666  +    if( !omitDescenders ){
          667  +      @   if( p.u==0 ) drawUpArrow(p.x, 0, p.y-5);
          668  +      @   if( p.d ) drawUpArrow(p.x, p.y+6, btm);
          669  +    }
          670  +    @   if( p.mo>0 ){
          671  +    @     var x1 = p.mo + left - 1;
   450    672       @     var y1 = p.y-3;
   451    673       @     var x0 = x1>p.x ? p.x+7 : p.x-6;
   452    674       @     var u = rowinfo[p.mu-1];
   453    675       @     var y0 = u.y+5;
   454         -    @     drawThinLine(x0,y1,x1,y1);
          676  +    @     if( x1>=p.x-5 && x1<=p.x+5 ){
          677  +    @       y1 = p.y-5;
          678  +    @     }else{
          679  +    @       drawThinLine(x0,y1,x1,y1);
          680  +    @     }
   455    681       @     drawThinLine(x1,y0,x1,y1);
   456    682       @   }
   457    683       @   var n = p.au.length;
   458    684       @   for(var i=0; i<n; i+=2){
   459         -    @     var x1 = p.au[i]*20 + left;
          685  +    @     var x1 = p.au[i]*railPitch + left;
   460    686       @     var x0 = x1>p.x ? p.x+7 : p.x-6;
   461         -    @     drawBox("black",x0,p.y,x1,p.y+1);
   462    687       @     var u = rowinfo[p.au[i+1]-1];
   463         -    @     drawUpArrow(x1, u.y+6, p.y);
          688  +    @     if(u.id<p.id){
          689  +    @       drawBox("black",x0,p.y,x1,p.y+1);
          690  +    @       drawUpArrow(x1, u.y+6, p.y);
          691  +    @     }else{
          692  +    @       drawBox("#600000",x0,p.y,x1,p.y+1);
          693  +    @       drawBox("#600000",x1-1,p.y,x1,u.y+1);
          694  +    @       drawBox("#600000",x1,u.y,u.x-6,u.y+1);
          695  +    @       drawBox("#600000",u.x-9,u.y-1,u.x-8,u.y+2);
          696  +    @       drawBox("#600000",u.x-11,u.y-2,u.x-10,u.y+3);
          697  +    @     }
   464    698       @   }
   465    699       @   for(var j in p.mi){
   466    700       @     var y0 = p.y+5;
   467         -    @     var mx = p.mi[j]*20 + left;
          701  +    @     var mx = p.mi[j];
          702  +    @     if( mx<0 ){
          703  +    @       mx = left-mx;
          704  +    @       drawThinLine(mx,y0,mx,btm);
          705  +    @     }else{
          706  +    @       mx += left;
          707  +    @     }
   468    708       @     if( mx>p.x ){
   469         -    @       drawThinArrow(y0,mx,p.x+5);
          709  +    @       drawThinArrow(y0,mx,p.x+6);
   470    710       @     }else{
   471    711       @       drawThinArrow(y0,mx,p.x-5);
   472    712       @     }
   473    713       @   }
   474    714       @ }
          715  +    @ var selBox = null;
          716  +    @ var selRow = null;
   475    717       @ function renderGraph(){
   476         -    @   var canvasDiv = document.getElementById("canvas");
          718  +    @   var canvasDiv = gebi("canvas");
   477    719       @   while( canvasDiv.hasChildNodes() ){
   478    720       @     canvasDiv.removeChild(canvasDiv.firstChild);
   479    721       @   }
   480         -    @   var canvasY = absoluteY("canvas");
   481         -    @   var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15;
   482         -    @   var width = nrail*20;
          722  +    @   var canvasY = absoluteY("timelineTable");
          723  +    @   var left = absoluteX("m"+rowinfo[0].id) - absoluteX("canvas") + 15;
   483    724       @   for(var i in rowinfo){
   484         -    @     rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
   485         -    @     rowinfo[i].x = left + rowinfo[i].r*20;
          725  +    @     rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY;
          726  +    @     rowinfo[i].x = left + rowinfo[i].r*railPitch;
   486    727       @   }
   487         -    @   var btm = rowinfo[rowinfo.length-1].y + 20;
   488         -    @   if( btm<32768 ){
   489         -    @     canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
   490         -    @        'style="position:absolute;left:'+(left-5)+'px;"' +
   491         -    @        ' width="'+width+'" height="'+btm+'"></canvas>';
   492         -    @     realCanvas = document.getElementById('timeline-canvas');
   493         -    @   }else{
   494         -    @     realCanvas = 0;
   495         -    @   }
   496         -    @   var context;
   497         -    @   if( realCanvas && realCanvas.getContext
   498         -    @        && (context = realCanvas.getContext('2d'))) {
   499         -    @     drawBox = function(color,x0,y0,x1,y1) {
   500         -    @       if( y0>32767 || y1>32767 ) return;
   501         -    @       if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
   502         -    @       if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
   503         -    @       if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return;
   504         -    @       context.fillStyle = color;
   505         -    @       context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1);
   506         -    @     };
   507         -    @   }
          728  +    @   var btm = absoluteY("grbtm") + 10 - canvasY;
   508    729       @   for(var i in rowinfo){
   509    730       @     drawNode(rowinfo[i], left, btm);
          731  +    @   }
          732  +    @   if( selRow!=null ) clickOnRow(selRow);
          733  +    @ }
          734  +    @ function clickOnGraph(event){
          735  +    @   var x=event.clientX-absoluteX("canvas");
          736  +    @   var y=event.clientY-absoluteY("canvas");
          737  +    @   if(window.pageXOffset!=null){
          738  +    @     x += window.pageXOffset;
          739  +    @     y += window.pageYOffset;
          740  +    @   }else{
          741  +    @     var d = window.document.documentElement;
          742  +    @     if(document.compatMode!="CSS1Compat") d = d.body;
          743  +    @     x += d.scrollLeft;
          744  +    @     y += d.scrollTop;
          745  +    @   }
          746  +    if( P("clicktest")!=0 ){
          747  +      @ alert("click at "+x+","+y)
          748  +    }
          749  +    @   for(var i in rowinfo){
          750  +    @     p = rowinfo[i];
          751  +    @     if( p.y<y-11 ) continue;
          752  +    @     if( p.y>y+9 ) break;
          753  +    @     if( p.x>x-11 && p.x<x+9 ){
          754  +    @       clickOnRow(p);
          755  +    @       break;
          756  +    @     }
          757  +    @   }
          758  +    @ }
          759  +    @ function clickOnRow(p){
          760  +    @   if( selRow==null ){
          761  +    @     selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3);
          762  +    @     selRow = p;
          763  +    @   }else if( selRow==p ){
          764  +    @     var canvasDiv = gebi("canvas");
          765  +    @     canvasDiv.removeChild(selBox);
          766  +    @     selBox = null;
          767  +    @     selRow = null;
          768  +    @   }else{
          769  +    if( fileDiff ){
          770  +      @     location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h;
          771  +    }else{
          772  +      @     location.href="%R/vdiff?from="+selRow.h+"&to="+p.h;
          773  +    }
   510    774       @   }
   511    775       @ }
   512         -    @ var lastId = rowinfo[rowinfo.length-1].id;
          776  +    @ var lastId = "m"+rowinfo[rowinfo.length-1].id;
   513    777       @ var lastY = 0;
   514    778       @ function checkHeight(){
   515    779       @   var h = absoluteY(lastId);
   516    780       @   if( h!=lastY ){
   517    781       @     renderGraph();
   518    782       @     lastY = h;
   519    783       @   }
   520    784       @   setTimeout("checkHeight();", 1000);
   521    785       @ }
   522    786       @ checkHeight();
          787  +    @ /* ]]> */
   523    788       @ </script>
   524    789     }
   525    790   }
   526    791   
   527    792   /*
   528    793   ** Create a temporary table suitable for storing timeline data.
   529    794   */
   530    795   static void timeline_temp_table(void){
   531         -  static const char zSql[] = 
          796  +  static const char zSql[] =
   532    797       @ CREATE TEMP TABLE IF NOT EXISTS timeline(
   533    798       @   rid INTEGER PRIMARY KEY,
   534    799       @   uuid TEXT,
   535    800       @   timestamp TEXT,
   536    801       @   comment TEXT,
   537    802       @   user TEXT,
   538    803       @   isleaf BOOLEAN,
   539    804       @   bgcolor TEXT,
   540    805       @   etype TEXT,
   541    806       @   taglist TEXT,
   542    807       @   tagid INTEGER,
   543         -    @   short TEXT
          808  +    @   short TEXT,
          809  +    @   sortby REAL
   544    810       @ )
   545    811     ;
   546    812     db_multi_exec(zSql);
   547    813   }
   548    814   
   549    815   /*
   550    816   ** Return a pointer to a constant string that forms the basis
   551    817   ** for a timeline query for the WWW interface.
   552    818   */
   553    819   const char *timeline_query_for_www(void){
   554    820     static char *zBase = 0;
   555    821     static const char zBaseSql[] =
   556    822       @ SELECT
   557         -    @   blob.rid,
   558         -    @   uuid,
          823  +    @   blob.rid AS blobRid,
          824  +    @   uuid AS uuid,
   559    825       @   datetime(event.mtime,'localtime') AS timestamp,
   560         -    @   coalesce(ecomment, comment),
   561         -    @   coalesce(euser, user),
   562         -    @   NOT EXISTS(SELECT 1 FROM plink
   563         -    @               WHERE pid=blob.rid
   564         -    @                AND coalesce((SELECT value FROM tagxref
   565         -    @                              WHERE tagid=%d AND rid=plink.pid), 'trunk')
   566         -    @                  = coalesce((SELECT value FROM tagxref
   567         -    @                              WHERE tagid=%d AND rid=plink.cid), 'trunk')),
   568         -    @   bgcolor,
   569         -    @   event.type,
          826  +    @   coalesce(ecomment, comment) AS comment,
          827  +    @   coalesce(euser, user) AS user,
          828  +    @   blob.rid IN leaf AS leaf,
          829  +    @   bgcolor AS bgColor,
          830  +    @   event.type AS eventType,
   570    831       @   (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
   571    832       @     WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
   572         -    @       AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
   573         -    @   tagid,
   574         -    @   brief
   575         -    @  FROM event JOIN blob 
          833  +    @       AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
          834  +    @   tagid AS tagid,
          835  +    @   brief AS brief,
          836  +    @   event.mtime AS mtime
          837  +    @  FROM event CROSS JOIN blob
   576    838       @ WHERE blob.rid=event.objid
   577    839     ;
   578    840     if( zBase==0 ){
   579    841       zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
   580    842     }
   581    843     return zBase;
   582    844   }
................................................................................
   592    854     const char *zRemove      /* Parameter to omit */
   593    855   ){
   594    856     style_submenu_element(zMenuName, zMenuName, "%s",
   595    857                           url_render(pUrl, zParam, zValue, zRemove, 0));
   596    858   }
   597    859   
   598    860   
          861  +/*
          862  +** Convert a symbolic name used as an argument to the a=, b=, or c=
          863  +** query parameters of timeline into a julianday mtime value.
          864  +*/
          865  +double symbolic_name_to_mtime(const char *z){
          866  +  double mtime;
          867  +  int rid;
          868  +  if( z==0 ) return -1.0;
          869  +  if( fossil_isdate(z) ){
          870  +    mtime = db_double(0.0, "SELECT julianday(%Q,'utc')", z);
          871  +    if( mtime>0.0 ) return mtime;
          872  +  }
          873  +  rid = symbolic_name_to_rid(z, "ci");
          874  +  if( rid==0 ) return -1.0;
          875  +  mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
          876  +  return mtime;
          877  +}
          878  +
          879  +/*
          880  +** The value of one second in julianday notation
          881  +*/
          882  +#define ONE_SECOND (1.0/86400.0)
          883  +
   599    884   /*
   600    885   ** zDate is a localtime date.  Insert records into the
   601    886   ** "timeline" table to cause <hr> to be inserted before and after
   602         -** entries of that date.
          887  +** entries of that date.  If zDate==NULL then put dividers around
          888  +** the event identified by rid.
   603    889   */
   604         -static void timeline_add_dividers(const char *zDate){
          890  +static void timeline_add_dividers(double rDate, int rid){
          891  +  char *zToDel = 0;
          892  +  if( rDate==0 ){
          893  +    rDate = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
          894  +  }
   605    895     db_multi_exec(
   606         -    "INSERT INTO timeline(rid,timestamp,etype)"
   607         -    "VALUES(-1,datetime(%Q,'-1 second') || '.9','div')",
   608         -    zDate
          896  +    "INSERT INTO timeline(rid,sortby,etype)"
          897  +    "VALUES(-1,%.16g,'div')",
          898  +    rDate-ONE_SECOND
   609    899     );
   610    900     db_multi_exec(
   611         -    "INSERT INTO timeline(rid,timestamp,etype)"
   612         -    "VALUES(-2,datetime(%Q) || '.1','div')",
   613         -     zDate
          901  +    "INSERT INTO timeline(rid,sortby,etype)"
          902  +    "VALUES(-2,%.17g,'div')",
          903  +    rDate+ONE_SECOND
          904  +  );
          905  +  fossil_free(zToDel);
          906  +}
          907  +
          908  +/*
          909  +** Return all possible names for file zUuid.
          910  +*/
          911  +char *names_of_file(const char *zUuid){
          912  +  Stmt q;
          913  +  Blob out;
          914  +  const char *zSep = "";
          915  +  db_prepare(&q,
          916  +    "SELECT DISTINCT filename.name FROM mlink, filename"
          917  +    " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid='%s')"
          918  +    "   AND filename.fnid=mlink.fnid",
          919  +    zUuid
   614    920     );
          921  +  blob_zero(&out);
          922  +  while( db_step(&q)==SQLITE_ROW ){
          923  +    const char *zFN = db_column_text(&q, 0);
          924  +    blob_appendf(&out, "%s%z%h</a>", zSep,
          925  +          href("%R/finfo?name=%t", zFN), zFN);
          926  +    zSep = " or ";
          927  +  }
          928  +  db_finalize(&q);
          929  +  return blob_str(&out);
   615    930   }
   616    931   
   617    932   
   618    933   /*
   619    934   ** WEBPAGE: timeline
   620    935   **
   621    936   ** Query parameters:
   622    937   **
   623         -**    a=TIMESTAMP    after this date
   624         -**    b=TIMESTAMP    before this date.
   625         -**    c=TIMESTAMP    "circa" this date.
   626         -**    n=COUNT        number of events in output
   627         -**    p=RID          artifact RID and up to COUNT parents and ancestors
   628         -**    d=RID          artifact RID and up to COUNT descendants
          938  +**    a=TIMEORTAG    after this event
          939  +**    b=TIMEORTAG    before this event
          940  +**    c=TIMEORTAG    "circa" this event
          941  +**    n=COUNT        max number of events in output
          942  +**    p=UUID         artifact and up to COUNT parents and ancestors
          943  +**    d=UUID         artifact and up to COUNT descendants
          944  +**    dp=UUID        The same as d=UUID&p=UUID
   629    945   **    t=TAGID        show only check-ins with the given tagid
          946  +**    r=TAGID        show check-ins related to tagid
   630    947   **    u=USER         only if belonging to this user
   631         -**    y=TYPE         'ci', 'w', 't'
          948  +**    y=TYPE         'ci', 'w', 't', 'e'
   632    949   **    s=TEXT         string search (comment and brief)
   633    950   **    ng             Suppress the graph if present
          951  +**    nd             Suppress "divider" lines
          952  +**    fc             Show details of files changed
          953  +**    f=UUID         Show family (immediate parents and children) of UUID
          954  +**    from=UUID      Path from...
          955  +**    to=UUID          ... to this
          956  +**    nomerge          ... avoid merge links on the path
          957  +**    brbg           Background color from branch name
          958  +**    ubg            Background color from user
   634    959   **
   635    960   ** p= and d= can appear individually or together.  If either p= or d=
   636    961   ** appear, then u=, y=, a=, and b= are ignored.
   637    962   **
   638    963   ** If a= and b= appear, only a= is used.  If neither appear, the most
   639         -** recent events are choosen.
          964  +** recent events are chosen.
   640    965   **
   641    966   ** If n= is missing, the default count is 20.
   642    967   */
   643    968   void page_timeline(void){
   644    969     Stmt q;                            /* Query used to generate the timeline */
   645    970     Blob sql;                          /* text of SQL used to generate timeline */
   646    971     Blob desc;                         /* Description of the timeline */
   647    972     int nEntry = atoi(PD("n","20"));   /* Max number of entries on timeline */
   648         -  int p_rid = name_to_rid(P("p"));   /* artifact p and its parents */
   649         -  int d_rid = name_to_rid(P("d"));    /* artifact d and its descendants */
          973  +  int p_rid = name_to_typed_rid(P("p"),"ci");  /* artifact p and its parents */
          974  +  int d_rid = name_to_typed_rid(P("d"),"ci");  /* artifact d and descendants */
          975  +  int f_rid = name_to_typed_rid(P("f"),"ci");  /* artifact f and close family */
   650    976     const char *zUser = P("u");        /* All entries by this user if not NULL */
   651    977     const char *zType = PD("y","all"); /* Type of events.  All if NULL */
   652    978     const char *zAfter = P("a");       /* Events after this time */
   653    979     const char *zBefore = P("b");      /* Events before this time */
   654    980     const char *zCirca = P("c");       /* Events near this time */
   655    981     const char *zTagName = P("t");     /* Show events with this tag */
          982  +  const char *zBrName = P("r");      /* Show events related to this tag */
   656    983     const char *zSearch = P("s");      /* Search string */
   657         -  HQuery url;                        /* URL for various branch links */
          984  +  const char *zUses = P("uf");       /* Only show checkins hold this file */
          985  +  int useDividers = P("nd")==0;      /* Show dividers if "nd" is missing */
   658    986     int tagid;                         /* Tag ID */
   659    987     int tmFlags;                       /* Timeline flags */
          988  +  const char *zThisTag = 0;          /* Suppress links to this tag */
          989  +  const char *zThisUser = 0;         /* Suppress links to this user */
          990  +  HQuery url;                        /* URL for various branch links */
          991  +  int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
          992  +  int to_rid = name_to_typed_rid(P("to"),"ci");    /* to= for path timelines */
          993  +  int noMerge = P("shortest")==0;           /* Follow merge links if shorter */
          994  +  int me_rid = name_to_typed_rid(P("me"),"ci");  /* me= for common ancestory */
          995  +  int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
          996  +  int pd_rid;
          997  +  double rBefore, rAfter, rCirca;     /* Boundary times */
   660    998   
   661    999     /* To view the timeline, must have permission to read project data.
   662   1000     */
         1001  +  pd_rid = name_to_typed_rid(P("dp"),"ci");
         1002  +  if( pd_rid ){
         1003  +    p_rid = d_rid = pd_rid;
         1004  +  }
   663   1005     login_check_credentials();
   664         -  if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
   665         -  if( zTagName && g.okRead ){
         1006  +  if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
         1007  +    login_needed();
         1008  +    return;
         1009  +  }
         1010  +  url_initialize(&url, "timeline");
         1011  +  if( zTagName && g.perm.Read ){
   666   1012       tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName);
         1013  +    zThisTag = zTagName;
         1014  +  }else if( zBrName && g.perm.Read ){
         1015  +    tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName);
         1016  +    zThisTag = zBrName;
   667   1017     }else{
   668   1018       tagid = 0;
   669   1019     }
   670   1020     if( zType[0]=='a' ){
   671   1021       tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
   672   1022     }else{
   673   1023       tmFlags = TIMELINE_GRAPH;
   674   1024     }
   675   1025     if( P("ng")!=0 || zSearch!=0 ){
   676   1026       tmFlags &= ~TIMELINE_GRAPH;
         1027  +    url_add_parameter(&url, "ng", 0);
         1028  +  }
         1029  +  if( P("brbg")!=0 ){
         1030  +    tmFlags |= TIMELINE_BRCOLOR;
         1031  +    url_add_parameter(&url, "brbg", 0);
         1032  +  }
         1033  +  if( P("ubg")!=0 ){
         1034  +    tmFlags |= TIMELINE_UCOLOR;
         1035  +    url_add_parameter(&url, "ubg", 0);
         1036  +  }
         1037  +  if( zUses!=0 ){
         1038  +    int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
         1039  +    if( ufid ){
         1040  +      zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
         1041  +      url_add_parameter(&url, "uf", zUses);
         1042  +      db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
         1043  +      compute_uses_file("usesfile", ufid, 0);
         1044  +      zType = "ci";
         1045  +    }else{
         1046  +      zUses = 0;
         1047  +    }
   677   1048     }
   678   1049   
   679   1050     style_header("Timeline");
   680   1051     login_anonymous_available();
   681   1052     timeline_temp_table();
   682   1053     blob_zero(&sql);
   683   1054     blob_zero(&desc);
   684   1055     blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
   685   1056     blob_append(&sql, timeline_query_for_www(), -1);
   686         -  if( (p_rid || d_rid) && g.okRead ){
         1057  +  if( P("fc")!=0 || P("detail")!=0 ){
         1058  +    tmFlags |= TIMELINE_FCHANGES;
         1059  +    url_add_parameter(&url, "fc", 0);
         1060  +  }
         1061  +  if( !useDividers ) url_add_parameter(&url, "nd", 0);
         1062  +  if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
         1063  +    /* If from= and to= are present, display all nodes on a path connecting
         1064  +    ** the two */
         1065  +    PathNode *p = 0;
         1066  +    const char *zFrom = 0;
         1067  +    const char *zTo = 0;
         1068  +
         1069  +    if( from_rid && to_rid ){
         1070  +      p = path_shortest(from_rid, to_rid, noMerge, 0);
         1071  +      zFrom = P("from");
         1072  +      zTo = P("to");
         1073  +    }else{
         1074  +      if( path_common_ancestor(me_rid, you_rid) ){
         1075  +        p = path_first();
         1076  +      }
         1077  +      zFrom = P("me");
         1078  +      zTo = P("you");
         1079  +    }
         1080  +    blob_append(&sql, " AND event.objid IN (0", -1);
         1081  +    while( p ){
         1082  +      blob_appendf(&sql, ",%d", p->rid);
         1083  +      p = p->u.pTo;
         1084  +    }
         1085  +    blob_append(&sql, ")", -1);
         1086  +    path_reset();
         1087  +    blob_append(&desc, "All nodes on the path from ", -1);
         1088  +    blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);
         1089  +    blob_append(&desc, " and ", -1);
         1090  +    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
         1091  +    tmFlags |= TIMELINE_DISJOINT;
         1092  +    db_multi_exec("%s", blob_str(&sql));
         1093  +  }else if( (p_rid || d_rid) && g.perm.Read ){
   687   1094       /* If p= or d= is present, ignore all other parameters other than n= */
   688   1095       char *zUuid;
   689   1096       int np, nd;
   690   1097   
   691   1098       if( p_rid && d_rid ){
   692   1099         if( p_rid!=d_rid ) p_rid = d_rid;
   693   1100         if( P("n")==0 ) nEntry = 10;
................................................................................
   698   1105       zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
   699   1106                            p_rid ? p_rid : d_rid);
   700   1107       blob_appendf(&sql, " AND event.objid IN ok");
   701   1108       nd = 0;
   702   1109       if( d_rid ){
   703   1110         compute_descendants(d_rid, nEntry+1);
   704   1111         nd = db_int(0, "SELECT count(*)-1 FROM ok");
   705         -      if( nd>=0 ){
   706         -        db_multi_exec("%s", blob_str(&sql));
   707         -        blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
   708         -      }
   709         -      timeline_add_dividers(
   710         -        db_text("1","SELECT datetime(mtime,'localtime') FROM event"
   711         -                    " WHERE objid=%d", d_rid)
   712         -      );
         1112  +      if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
         1113  +      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
         1114  +      if( useDividers ) timeline_add_dividers(0, d_rid);
   713   1115         db_multi_exec("DELETE FROM ok");
   714   1116       }
   715   1117       if( p_rid ){
   716         -      compute_ancestors(p_rid, nEntry+1);
         1118  +      compute_ancestors(p_rid, nEntry+1, 0);
   717   1119         np = db_int(0, "SELECT count(*)-1 FROM ok");
   718   1120         if( np>0 ){
   719   1121           if( nd>0 ) blob_appendf(&desc, " and ");
   720   1122           blob_appendf(&desc, "%d ancestors", np);
   721   1123           db_multi_exec("%s", blob_str(&sql));
   722   1124         }
   723         -      if( d_rid==0 ){
   724         -        timeline_add_dividers(  
   725         -          db_text("1","SELECT datetime(mtime,'localtime') FROM event"
   726         -                      " WHERE objid=%d", p_rid)
   727         -        );
   728         -      }
         1125  +      if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid);
   729   1126       }
   730         -    if( g.okHistory ){
   731         -      blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
   732         -                   g.zBaseURL, zUuid, zUuid);
   733         -    }else{
   734         -      blob_appendf(&desc, " of check-in [%.10s]", zUuid);
   735         -    }
         1127  +    blob_appendf(&desc, " of %z[%.10s]</a>",
         1128  +                   href("%R/info/%s", zUuid), zUuid);
         1129  +  }else if( f_rid && g.perm.Read ){
         1130  +    /* If f= is present, ignore all other parameters other than n= */
         1131  +    char *zUuid;
         1132  +    db_multi_exec(
         1133  +       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
         1134  +       "INSERT INTO ok VALUES(%d);"
         1135  +       "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
         1136  +       "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
         1137  +       f_rid, f_rid, f_rid
         1138  +    );
         1139  +    blob_appendf(&sql, " AND event.objid IN ok");
         1140  +    db_multi_exec("%s", blob_str(&sql));
         1141  +    if( useDividers ) timeline_add_dividers(0, f_rid);
         1142  +    blob_appendf(&desc, "Parents and children of check-in ");
         1143  +    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
         1144  +    blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid);
         1145  +    tmFlags |= TIMELINE_DISJOINT;
   736   1146     }else{
         1147  +    /* Otherwise, a timeline based on a span of time */
   737   1148       int n;
   738         -    const char *zEType = "event";
         1149  +    const char *zEType = "timeline item";
   739   1150       char *zDate;
   740   1151       char *zNEntry = mprintf("%d", nEntry);
   741         -    url_initialize(&url, "timeline");
   742   1152       url_add_parameter(&url, "n", zNEntry);
         1153  +    if( zUses ){
         1154  +      blob_appendf(&sql, " AND event.objid IN usesfile ");
         1155  +    }
   743   1156       if( tagid>0 ){
   744         -      zType = "ci";
   745         -      url_add_parameter(&url, "t", zTagName);
   746         -      blob_appendf(&sql, " AND EXISTS (SELECT 1 FROM tagxref WHERE tagid=%d"
   747         -                                        " AND tagtype>0 AND rid=blob.rid)",
   748         -                   tagid);
         1157  +      blob_appendf(&sql,
         1158  +        "AND (EXISTS(SELECT 1 FROM tagxref"
         1159  +                    " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);
         1160  +
         1161  +      if( zBrName ){
         1162  +        url_add_parameter(&url, "r", zBrName);
         1163  +        /* The next two blob_appendf() calls add SQL that causes checkins that
         1164  +        ** are not part of the branch which are parents or children of the
         1165  +        ** branch to be included in the report.  This related check-ins are
         1166  +        ** useful in helping to visualize what has happened on a quiescent
         1167  +        ** branch that is infrequently merged with a much more activate branch.
         1168  +        */
         1169  +        blob_appendf(&sql,
         1170  +          " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
         1171  +                     " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
         1172  +           tagid
         1173  +        );
         1174  +        if( P("mionly")==0 ){
         1175  +          blob_appendf(&sql,
         1176  +            " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
         1177  +                       " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
         1178  +            tagid
         1179  +          );
         1180  +        }else{
         1181  +          url_add_parameter(&url, "mionly", "1");
         1182  +        }
         1183  +      }else{
         1184  +        url_add_parameter(&url, "t", zTagName);
         1185  +      }
         1186  +      blob_appendf(&sql, ")");
   749   1187       }
   750         -    if( (zType[0]=='w' && !g.okRdWiki)
   751         -     || (zType[0]=='t' && !g.okRdTkt)
   752         -     || (zType[0]=='c' && !g.okRead)
         1188  +    if( (zType[0]=='w' && !g.perm.RdWiki)
         1189  +     || (zType[0]=='t' && !g.perm.RdTkt)
         1190  +     || (zType[0]=='e' && !g.perm.RdWiki)
         1191  +     || (zType[0]=='c' && !g.perm.Read)
         1192  +     || (zType[0]=='g' && !g.perm.Read)
   753   1193       ){
   754   1194         zType = "all";
   755   1195       }
   756   1196       if( zType[0]=='a' ){
   757         -      if( !g.okRead || !g.okRdWiki || !g.okRdTkt ){
         1197  +      if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
   758   1198           char cSep = '(';
   759   1199           blob_appendf(&sql, " AND event.type IN ");
   760         -        if( g.okRead ){
   761         -          blob_appendf(&sql, "%c'ci'", cSep);
         1200  +        if( g.perm.Read ){
         1201  +          blob_appendf(&sql, "%c'ci','g'", cSep);
   762   1202             cSep = ',';
   763   1203           }
   764         -        if( g.okRdWiki ){
   765         -          blob_appendf(&sql, "%c'w'", cSep);
         1204  +        if( g.perm.RdWiki ){
         1205  +          blob_appendf(&sql, "%c'w','e'", cSep);
   766   1206             cSep = ',';
   767   1207           }
   768         -        if( g.okRdTkt ){
         1208  +        if( g.perm.RdTkt ){
   769   1209             blob_appendf(&sql, "%c't'", cSep);
   770   1210             cSep = ',';
   771   1211           }
   772   1212           blob_appendf(&sql, ")");
   773   1213         }
   774   1214       }else{ /* zType!="all" */
   775   1215         blob_appendf(&sql, " AND event.type=%Q", zType);
................................................................................
   776   1216         url_add_parameter(&url, "y", zType);
   777   1217         if( zType[0]=='c' ){
   778   1218           zEType = "checkin";
   779   1219         }else if( zType[0]=='w' ){
   780   1220           zEType = "wiki edit";
   781   1221         }else if( zType[0]=='t' ){
   782   1222           zEType = "ticket change";
         1223  +      }else if( zType[0]=='e' ){
         1224  +        zEType = "event";
         1225  +      }else if( zType[0]=='g' ){
         1226  +        zEType = "tag";
   783   1227         }
   784   1228       }
   785   1229       if( zUser ){
   786         -      blob_appendf(&sql, " AND event.user=%Q", zUser);
         1230  +      blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)",
         1231  +                   zUser, zUser);
   787   1232         url_add_parameter(&url, "u", zUser);
         1233  +      zThisUser = zUser;
   788   1234       }
   789   1235       if ( zSearch ){
   790   1236         blob_appendf(&sql,
   791   1237           " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
   792   1238           zSearch, zSearch);
   793   1239         url_add_parameter(&url, "s", zSearch);
   794   1240       }
   795         -    if( zAfter ){
   796         -      while( isspace(zAfter[0]) ){ zAfter++; }
   797         -      if( zAfter[0] ){
   798         -        blob_appendf(&sql, 
   799         -           " AND event.mtime>=(SELECT julianday(%Q, 'utc'))"
   800         -           " ORDER BY event.mtime ASC", zAfter);
         1241  +    rBefore = symbolic_name_to_mtime(zBefore);
         1242  +    rAfter = symbolic_name_to_mtime(zAfter);
         1243  +    rCirca = symbolic_name_to_mtime(zCirca);
         1244  +    if( rAfter>0.0 ){
         1245  +      if( rBefore>0.0 ){
         1246  +        blob_appendf(&sql,
         1247  +           " AND event.mtime>=%.17g AND event.mtime<=%.17g"
         1248  +           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
   801   1249           url_add_parameter(&url, "a", zAfter);
   802         -        zBefore = 0;
         1250  +        url_add_parameter(&url, "b", zBefore);
         1251  +        nEntry = 1000000;
   803   1252         }else{
   804         -        zAfter = 0;
   805         -      }
   806         -    }else if( zBefore ){
   807         -      while( isspace(zBefore[0]) ){ zBefore++; }
   808         -      if( zBefore[0] ){
   809         -        blob_appendf(&sql, 
   810         -           " AND event.mtime<=(SELECT julianday(%Q, 'utc'))"
   811         -           " ORDER BY event.mtime DESC", zBefore);
   812         -        url_add_parameter(&url, "b", zBefore);
   813         -       }else{
   814         -        zBefore = 0;
         1253  +        blob_appendf(&sql,
         1254  +           " AND event.mtime>=%.17g  ORDER BY event.mtime ASC",
         1255  +           rAfter-ONE_SECOND);
         1256  +        url_add_parameter(&url, "a", zAfter);
   815   1257         }
   816         -    }else if( zCirca ){
   817         -      while( isspace(zCirca[0]) ){ zCirca++; }
   818         -      if( zCirca[0] ){
   819         -        double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca);
   820         -        Blob sql2;
   821         -        blob_init(&sql2, blob_str(&sql), -1);
   822         -        blob_appendf(&sql2,
   823         -            " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d",
   824         -            rCirca, (nEntry+1)/2
   825         -        );
   826         -        db_multi_exec("%s", blob_str(&sql2));
   827         -        blob_reset(&sql2);
   828         -        blob_appendf(&sql,
   829         -            " AND event.mtime>=%f ORDER BY event.mtime ASC",
   830         -            rCirca
   831         -        );
   832         -        nEntry -= (nEntry+1)/2;
   833         -        timeline_add_dividers(zCirca);
   834         -        url_add_parameter(&url, "c", zCirca);
   835         -      }else{
   836         -        zCirca = 0;
   837         -      }
         1258  +    }else if( rBefore>0.0 ){
         1259  +      blob_appendf(&sql,
         1260  +         " AND event.mtime<=%.17g ORDER BY event.mtime DESC",
         1261  +         rBefore+ONE_SECOND);
         1262  +      url_add_parameter(&url, "b", zBefore);
         1263  +    }else if( rCirca>0.0 ){
         1264  +      Blob sql2;
         1265  +      blob_init(&sql2, blob_str(&sql), -1);
         1266  +      blob_appendf(&sql2,
         1267  +          " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d",
         1268  +          rCirca, (nEntry+1)/2
         1269  +      );
         1270  +      db_multi_exec("%s", blob_str(&sql2));
         1271  +      blob_reset(&sql2);
         1272  +      blob_appendf(&sql,
         1273  +          " AND event.mtime>=%f ORDER BY event.mtime ASC",
         1274  +          rCirca
         1275  +      );
         1276  +      nEntry -= (nEntry+1)/2;
         1277  +      if( useDividers ) timeline_add_dividers(rCirca, 0);
         1278  +      url_add_parameter(&url, "c", zCirca);
   838   1279       }else{
   839   1280         blob_appendf(&sql, " ORDER BY event.mtime DESC");
   840   1281       }
   841   1282       blob_appendf(&sql, " LIMIT %d", nEntry);
   842   1283       db_multi_exec("%s", blob_str(&sql));
   843   1284   
   844         -    n = db_int(0, "SELECT count(*) FROM timeline /*scan*/");
   845         -    if( n<nEntry && zAfter ){
   846         -      cgi_redirect(url_render(&url, "a", 0, "b", 0));
   847         -    }
         1285  +    n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
   848   1286       if( zAfter==0 && zBefore==0 && zCirca==0 ){
   849   1287         blob_appendf(&desc, "%d most recent %ss", n, zEType);
   850   1288       }else{
   851   1289         blob_appendf(&desc, "%d %ss", n, zEType);
   852   1290       }
         1291  +    if( zUses ){
         1292  +      char *zFilenames = names_of_file(zUses);
         1293  +      blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames,
         1294  +                   href("%R/artifact/%S",zUses), zUses);
         1295  +      tmFlags |= TIMELINE_DISJOINT;
         1296  +    }
   853   1297       if( zUser ){
   854   1298         blob_appendf(&desc, " by user %h", zUser);
   855   1299         tmFlags |= TIMELINE_DISJOINT;
   856   1300       }
   857         -    if( tagid>0 ){
         1301  +    if( zTagName ){
   858   1302         blob_appendf(&desc, " tagged with \"%h\"", zTagName);
   859   1303         tmFlags |= TIMELINE_DISJOINT;
         1304  +    }else if( zBrName ){
         1305  +      blob_appendf(&desc, " related to \"%h\"", zBrName);
         1306  +      tmFlags |= TIMELINE_DISJOINT;
   860   1307       }
   861         -    if( zAfter ){
   862         -      blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
   863         -    }else if( zBefore ){
   864         -      blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
   865         -    }else if( zCirca ){
   866         -      blob_appendf(&desc, " occurring around %h.<br>", zCirca);
         1308  +    if( rAfter>0.0 ){
         1309  +      if( rBefore>0.0 ){
         1310  +        blob_appendf(&desc, " occurring between %h and %h.<br>",
         1311  +                     zAfter, zBefore);
         1312  +      }else{
         1313  +        blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
         1314  +      }
         1315  +    }else if( rBefore>0.0 ){
         1316  +      blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
         1317  +    }else if( rCirca>0.0 ){
         1318  +      blob_appendf(&desc, " occurring around %h.<br />", zCirca);
   867   1319       }
   868   1320       if( zSearch ){
   869   1321         blob_appendf(&desc, " matching \"%h\"", zSearch);
   870   1322       }
   871         -    if( g.okHistory ){
         1323  +    if( g.perm.Hyperlink ){
   872   1324         if( zAfter || n==nEntry ){
   873   1325           zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
   874   1326           timeline_submenu(&url, "Older", "b", zDate, "a");
   875   1327           free(zDate);
   876   1328         }
   877   1329         if( zBefore || (zAfter && n==nEntry) ){
   878   1330           zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
   879   1331           timeline_submenu(&url, "Newer", "a", zDate, "b");
   880   1332           free(zDate);
   881   1333         }else if( tagid==0 ){
   882   1334           if( zType[0]!='a' ){
   883   1335             timeline_submenu(&url, "All Types", "y", "all", 0);
   884   1336           }
   885         -        if( zType[0]!='w' && g.okRdWiki ){
         1337  +        if( zType[0]!='w' && g.perm.RdWiki ){
   886   1338             timeline_submenu(&url, "Wiki Only", "y", "w", 0);
   887   1339           }
   888         -        if( zType[0]!='c' && g.okRead ){
         1340  +        if( zType[0]!='c' && g.perm.Read ){
   889   1341             timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
   890   1342           }
   891         -        if( zType[0]!='t' && g.okRdTkt ){
         1343  +        if( zType[0]!='t' && g.perm.RdTkt ){
   892   1344             timeline_submenu(&url, "Tickets Only", "y", "t", 0);
   893   1345           }
         1346  +        if( zType[0]!='e' && g.perm.RdWiki ){
         1347  +          timeline_submenu(&url, "Events Only", "y", "e", 0);
         1348  +        }
         1349  +        if( zType[0]!='g' && g.perm.Read ){
         1350  +          timeline_submenu(&url, "Tags Only", "y", "g", 0);
         1351  +        }
   894   1352         }
   895   1353         if( nEntry>20 ){
   896         -        timeline_submenu(&url, "20 Events", "n", "20", 0);
         1354  +        timeline_submenu(&url, "20 Entries", "n", "20", 0);
   897   1355         }
   898   1356         if( nEntry<200 ){
   899         -        timeline_submenu(&url, "200 Events", "n", "200", 0);
         1357  +        timeline_submenu(&url, "200 Entries", "n", "200", 0);
         1358  +      }
         1359  +      if( zType[0]=='a' || zType[0]=='c' ){
         1360  +        if( tmFlags & TIMELINE_FCHANGES ){
         1361  +          timeline_submenu(&url, "Hide Files", "fc", 0, 0);
         1362  +        }else{
         1363  +          timeline_submenu(&url, "Show Files", "fc", "", 0);
         1364  +        }
   900   1365         }
   901   1366       }
   902   1367     }
         1368  +  if( P("showsql") ){
         1369  +    @ <blockquote>%h(blob_str(&sql))</blockquote>
         1370  +  }
   903   1371     blob_zero(&sql);
   904         -  db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/");
         1372  +  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
   905   1373     @ <h2>%b(&desc)</h2>
   906   1374     blob_reset(&desc);
   907         -  www_print_timeline(&q, tmFlags, 0);
         1375  +  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0);
   908   1376     db_finalize(&q);
   909   1377     style_footer();
   910   1378   }
   911   1379   
   912   1380   /*
   913   1381   ** The input query q selects various records.  Print a human-readable
   914   1382   ** summary of those records.
   915   1383   **
   916   1384   ** Limit the number of entries printed to nLine.
   917         -** 
         1385  +**
   918   1386   ** The query should return these columns:
   919   1387   **
   920   1388   **    0.  rid
   921   1389   **    1.  uuid
   922   1390   **    2.  Date/Time
   923   1391   **    3.  Comment string and user
   924   1392   **    4.  Number of non-merge children
   925   1393   **    5.  Number of parents
         1394  +**    6.  mtime
         1395  +**    7.  branch
   926   1396   */
   927         -void print_timeline(Stmt *q, int mxLine){
         1397  +void print_timeline(Stmt *q, int mxLine, int showfiles){
   928   1398     int nLine = 0;
   929   1399     char zPrevDate[20];
   930   1400     const char *zCurrentUuid=0;
         1401  +  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
         1402  +  Stmt fchngQuery;            /* Query for file changes on check-ins */
   931   1403     zPrevDate[0] = 0;
   932   1404   
   933   1405     if( g.localOpen ){
   934   1406       int rid = db_lget_int("checkout", 0);
   935   1407       zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   936   1408     }
   937   1409   
................................................................................
   942   1414       const char *zCom = db_column_text(q, 3);
   943   1415       int nChild = db_column_int(q, 4);
   944   1416       int nParent = db_column_int(q, 5);
   945   1417       char *zFree = 0;
   946   1418       int n = 0;
   947   1419       char zPrefix[80];
   948   1420       char zUuid[UUID_SIZE+1];
   949         -    
   950         -    sprintf(zUuid, "%.10s", zId);
         1421  +
         1422  +    sqlite3_snprintf(sizeof(zUuid), zUuid, "%.10s", zId);
   951   1423       if( memcmp(zDate, zPrevDate, 10) ){
   952         -      printf("=== %.10s ===\n", zDate);
         1424  +      fossil_print("=== %.10s ===\n", zDate);
   953   1425         memcpy(zPrevDate, zDate, 10);
   954   1426         nLine++;
   955   1427       }
   956   1428       if( zCom==0 ) zCom = "";
   957         -    printf("%.8s ", &zDate[11]);
         1429  +    fossil_print("%.8s ", &zDate[11]);
   958   1430       zPrefix[0] = 0;
   959   1431       if( nParent>1 ){
   960   1432         sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
   961   1433         n = strlen(zPrefix);
   962   1434       }
   963   1435       if( nChild>1 ){
   964   1436         const char *zBrType;
................................................................................
   966   1438           zBrType = "*FORK* ";
   967   1439         }else{
   968   1440           zBrType = "*BRANCH* ";
   969   1441         }
   970   1442         sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType);
   971   1443         n = strlen(zPrefix);
   972   1444       }
   973         -    if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){
         1445  +    if( fossil_strcmp(zCurrentUuid,zId)==0 ){
   974   1446         sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
   975   1447         n += strlen(zPrefix);
   976   1448       }
   977   1449       zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom);
   978   1450       nLine += comment_print(zFree, 9, 79);
   979   1451       sqlite3_free(zFree);
         1452  +
         1453  +    if(showfiles){
         1454  +      if( !fchngQueryInit ){
         1455  +        db_prepare(&fchngQuery,
         1456  +           "SELECT (pid==0) AS isnew,"
         1457  +           "       (fid==0) AS isdel,"
         1458  +           "       (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name,"
         1459  +           "       (SELECT uuid FROM blob WHERE rid=fid),"
         1460  +           "       (SELECT uuid FROM blob WHERE rid=pid)"
         1461  +           "  FROM mlink"
         1462  +           " WHERE mid=:mid AND pid!=fid"
         1463  +           " ORDER BY 3 /*sort*/"
         1464  +        );
         1465  +        fchngQueryInit = 1;
         1466  +      }
         1467  +      db_bind_int(&fchngQuery, ":mid", rid);
         1468  +      while( db_step(&fchngQuery)==SQLITE_ROW ){
         1469  +        const char *zFilename = db_column_text(&fchngQuery, 2);
         1470  +        int isNew = db_column_int(&fchngQuery, 0);
         1471  +        int isDel = db_column_int(&fchngQuery, 1);
         1472  +        if( isNew ){
         1473  +          fossil_print("   ADDED %s\n",zFilename);
         1474  +        }else if( isDel ){
         1475  +          fossil_print("   DELETED %s\n",zFilename);
         1476  +        }else{
         1477  +          fossil_print("   EDITED %s\n", zFilename);
         1478  +        }
         1479  +      }
         1480  +      db_reset(&fchngQuery);
         1481  +    }
   980   1482     }
         1483  +  if( fchngQueryInit ) db_finalize(&fchngQuery);
   981   1484   }
   982   1485   
   983   1486   /*
   984   1487   ** Return a pointer to a static string that forms the basis for
   985   1488   ** a timeline query for display on a TTY.
   986   1489   */
   987   1490   const char *timeline_query_for_tty(void){
   988         -  static const char zBaseSql[] = 
         1491  +  static const char zBaseSql[] =
   989   1492       @ SELECT
   990         -    @   blob.rid,
         1493  +    @   blob.rid AS rid,
   991   1494       @   uuid,
   992         -    @   datetime(event.mtime,'localtime'),
         1495  +    @   datetime(event.mtime,'localtime') AS mDateTime,
   993   1496       @   coalesce(ecomment,comment)
   994   1497       @     || ' (user: ' || coalesce(euser,user,'?')
   995   1498       @     || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
   996   1499       @           FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
   997   1500       @                   FROM tag, tagxref
   998   1501       @                  WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
   999   1502       @                    AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
  1000         -    @     || ')',
  1001         -    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
  1002         -    @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
  1003         -    @ FROM event, blob
         1503  +    @     || ')' as comment,
         1504  +    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
         1505  +    @        AS primPlinkCount,
         1506  +    @   (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
         1507  +    @   event.mtime AS mtime,
         1508  +    @   tagxref.value AS branch
         1509  +    @ FROM tag CROSS JOIN event CROSS JOIN blob
         1510  +    @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
         1511  +    @   AND tagxref.tagtype>0
         1512  +    @   AND tagxref.rid=blob.rid 
  1004   1513       @ WHERE blob.rid=event.objid
         1514  +    @   AND tag.tagname='branch'
  1005   1515     ;
  1006   1516     return zBaseSql;
  1007   1517   }
  1008   1518   
  1009   1519   /*
  1010   1520   ** Return true if the input string is a date in the ISO 8601 format:
  1011   1521   ** YYYY-MM-DD.
  1012   1522   */
  1013   1523   static int isIsoDate(const char *z){
  1014   1524     return strlen(z)==10
  1015   1525         && z[4]=='-'
  1016   1526         && z[7]=='-'
  1017         -      && isdigit(z[0])
  1018         -      && isdigit(z[5]);
         1527  +      && fossil_isdigit(z[0])
         1528  +      && fossil_isdigit(z[5]);
  1019   1529   }
  1020   1530   
  1021   1531   /*
  1022   1532   ** COMMAND: timeline
  1023   1533   **
  1024         -** Usage: %fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n N? ?-t TYPE?
         1534  +** Usage: %fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n N? ?-t TYPE? ?-showfiles?
  1025   1535   **
  1026   1536   ** Print a summary of activity going backwards in date and time
  1027   1537   ** specified or from the current date and time if no arguments
  1028   1538   ** are given.  Show as many as N (default 20) check-ins.  The
  1029   1539   ** WHEN argument can be any unique abbreviation of one of these
  1030   1540   ** keywords:
  1031   1541   **
................................................................................
  1041   1551   **
  1042   1552   ** The optional TYPE argument may any types supported by the /timeline
  1043   1553   ** page. For example:
  1044   1554   **
  1045   1555   **     w  = wiki commits only
  1046   1556   **     ci = file commits only
  1047   1557   **     t  = tickets only
         1558  +**
         1559  +** The optional showfiles argument, if specified, prints the list of
         1560  +** files changed in a checkin after the checkin comment.
         1561  +**
  1048   1562   */
  1049   1563   void timeline_cmd(void){
  1050   1564     Stmt q;
  1051   1565     int n, k;
  1052   1566     const char *zCount;
  1053   1567     const char *zType;
  1054   1568     char *zOrigin;
  1055   1569     char *zDate;
  1056   1570     Blob sql;
  1057   1571     int objid = 0;
  1058   1572     Blob uuid;
  1059   1573     int mode = 0 ;       /* 0:none  1: before  2:after  3:children  4:parents */
  1060         -  db_find_and_open_repository(1);
         1574  +  int showfilesFlag = 0 ;
         1575  +  showfilesFlag = find_option("showfiles","f", 0)!=0;
         1576  +  db_find_and_open_repository(0, 0);
  1061   1577     zCount = find_option("count","n",1);
  1062   1578     zType = find_option("type","t",1);
  1063   1579     if( zCount ){
  1064   1580       n = atoi(zCount);
  1065   1581     }else{
  1066   1582       n = 20;
  1067   1583     }
................................................................................
  1079   1595         mode = 4;
  1080   1596       }else if( strncmp(g.argv[2],"parents",k)==0 ){
  1081   1597         mode = 4;
  1082   1598       }else if(!zType && !zCount){
  1083   1599         usage("?WHEN? ?BASELINE|DATETIME? ?-n|--count N? ?-t TYPE?");
  1084   1600       }
  1085   1601       if( '-' != *g.argv[3] ){
  1086         -	zOrigin = g.argv[3];
         1602  +      zOrigin = g.argv[3];
  1087   1603       }else{
  1088         -	zOrigin = "now";
         1604  +      zOrigin = "now";
  1089   1605       }
  1090   1606     }else if( g.argc==3 ){
  1091   1607       zOrigin = g.argv[2];
  1092   1608     }else{
  1093   1609       zOrigin = "now";
  1094   1610     }
  1095   1611     k = strlen(zOrigin);
  1096   1612     blob_zero(&uuid);
  1097   1613     blob_append(&uuid, zOrigin, -1);
  1098         -  if( strcmp(zOrigin, "now")==0 ){
         1614  +  if( fossil_strcmp(zOrigin, "now")==0 ){
  1099   1615       if( mode==3 || mode==4 ){
  1100   1616         fossil_fatal("cannot compute descendants or ancestors of a date");
  1101   1617       }
  1102   1618       zDate = mprintf("(SELECT datetime('now'))");
  1103   1619     }else if( strncmp(zOrigin, "current", k)==0 ){
  1104   1620       if( !g.localOpen ){
  1105   1621         fossil_fatal("must be within a local checkout to use 'current'");
  1106   1622       }
  1107   1623       objid = db_lget_int("checkout",0);
  1108   1624       zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  1109         -  }else if( name_to_uuid(&uuid, 0)==0 ){
         1625  +  }else if( name_to_uuid(&uuid, 0, "*")==0 ){
  1110   1626       objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
  1111   1627       zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  1112   1628     }else{
  1113   1629       const char *zShift = "";
  1114   1630       if( mode==3 || mode==4 ){
  1115   1631         fossil_fatal("cannot compute descendants or ancestors of a date");
  1116   1632       }
................................................................................
  1128   1644     );
  1129   1645   
  1130   1646     if( mode==3 || mode==4 ){
  1131   1647       db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
  1132   1648       if( mode==3 ){
  1133   1649         compute_descendants(objid, n);
  1134   1650       }else{
  1135         -      compute_ancestors(objid, n);
         1651  +      compute_ancestors(objid, n, 0);
  1136   1652       }
  1137   1653       blob_appendf(&sql, " AND blob.rid IN ok");
  1138   1654     }
  1139   1655     if( zType && (zType[0]!='a') ){
  1140   1656       blob_appendf(&sql, " AND event.type=%Q ", zType);
  1141   1657     }
  1142         -
  1143   1658     blob_appendf(&sql, " ORDER BY event.mtime DESC");
  1144   1659     db_prepare(&q, blob_str(&sql));
  1145   1660     blob_reset(&sql);
  1146         -  print_timeline(&q, n);
         1661  +  print_timeline(&q, n, showfilesFlag);
  1147   1662     db_finalize(&q);
  1148   1663   }
  1149   1664   
  1150   1665   /*
  1151   1666   ** This is a version of the "localtime()" function from the standard
  1152   1667   ** C library.  It converts a unix timestamp (seconds since 1970) into
  1153   1668   ** a broken-out local time structure.
................................................................................
  1161   1676     if( g.fTimeFormat==0 ){
  1162   1677       if( db_get_int("timeline-utc", 1) ){
  1163   1678         g.fTimeFormat = 1;
  1164   1679       }else{
  1165   1680         g.fTimeFormat = 2;
  1166   1681       }
  1167   1682     }
         1683  +  if( clock==0 ) return 0;
  1168   1684     if( g.fTimeFormat==1 ){
  1169   1685       return gmtime(clock);
  1170   1686     }else{
  1171   1687       return localtime(clock);
  1172   1688     }
  1173   1689   }
         1690  +
         1691  +
         1692  +/*
         1693  +** COMMAND: test-timewarp-list
         1694  +**
         1695  +** Usage: %fossil test-timewarp-list ?--detail?
         1696  +**
         1697  +** Display all instances of child checkins that appear earlier in time
         1698  +** than their parent.  If the --detail option is provided, both the
         1699  +** parent and child checking and their times are shown.
         1700  +*/
         1701  +void test_timewarp_cmd(void){
         1702  +  Stmt q;
         1703  +  int showDetail;
         1704  +
         1705  +  db_find_and_open_repository(0, 0);
         1706  +  showDetail = find_option("detail", 0, 0)!=0;
         1707  +  db_prepare(&q,
         1708  +     "SELECT (SELECT uuid FROM blob WHERE rid=p.cid),"
         1709  +     "       (SELECT uuid FROM blob WHERE rid=c.cid),"
         1710  +     "       datetime(p.mtime), datetime(c.mtime)"
         1711  +     "  FROM plink p, plink c"
         1712  +     " WHERE p.cid=c.pid  AND p.mtime>c.mtime"
         1713  +  );
         1714  +  while( db_step(&q)==SQLITE_ROW ){
         1715  +    if( !showDetail ){
         1716  +      fossil_print("%s\n", db_column_text(&q, 1));
         1717  +    }else{
         1718  +      fossil_print("%.14s -> %.14s   %s -> %s\n",
         1719  +         db_column_text(&q, 0),
         1720  +         db_column_text(&q, 1),
         1721  +         db_column_text(&q, 2),
         1722  +         db_column_text(&q, 3));
         1723  +    }
         1724  +  }
         1725  +  db_finalize(&q);
         1726  +}
         1727  +
         1728  +/*
         1729  +** WEBPAGE: test_timewarps
         1730  +*/
         1731  +void test_timewarp_page(void){
         1732  +  Stmt q;
         1733  +
         1734  +  login_check_credentials();
         1735  +  if( !g.perm.Read || !g.perm.Hyperlink ){ login_needed(); return; }
         1736  +  style_header("Instances of timewarp");
         1737  +  @ <ul>
         1738  +  db_prepare(&q,
         1739  +     "SELECT blob.uuid "
         1740  +     "  FROM plink p, plink c, blob"
         1741  +     " WHERE p.cid=c.pid  AND p.mtime>c.mtime"
         1742  +     "   AND blob.rid=c.cid"
         1743  +  );
         1744  +  while( db_step(&q)==SQLITE_ROW ){
         1745  +    const char *zUuid = db_column_text(&q, 0);
         1746  +    @ <li>
         1747  +    @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&amp;d=%S(zUuid)">%S(zUuid)</a>
         1748  +  }
         1749  +  db_finalize(&q);
         1750  +  style_footer();
         1751  +}

Changes to src/tkt.c.

    24     24   
    25     25   /*
    26     26   ** The list of database user-defined fields in the TICKET table.
    27     27   ** The real table also contains some addition fields for internal
    28     28   ** used.  The internal-use fields begin with "tkt_".
    29     29   */
    30     30   static int nField = 0;
    31         -static char **azField = 0;    /* Names of database fields */
    32         -static char **azValue = 0;    /* Original values */
    33         -static char **azAppend = 0;   /* Value to be appended */
           31  +static struct tktFieldInfo {
           32  +  char *zName;             /* Name of the database field */
           33  +  char *zValue;            /* Value to store */
           34  +  char *zAppend;           /* Value to append */
           35  +  unsigned mUsed;          /* 01: TICKET  02: TICKETCHNG */
           36  +} *aField;
           37  +#define USEDBY_TICKET      01
           38  +#define USEDBY_TICKETCHNG  02
           39  +#define USEDBY_BOTH        03
           40  +static u8 haveTicket = 0;        /* True if the TICKET table exists */
           41  +static u8 haveTicketCTime = 0;   /* True if TICKET.TKT_CTIME exists */
           42  +static u8 haveTicketChng = 0;    /* True if the TICKETCHNG table exists */
           43  +static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
    34     44   
    35     45   /*
    36         -** Compare two entries in azField for sorting purposes
           46  +** Compare two entries in aField[] for sorting purposes
    37     47   */
    38     48   static int nameCmpr(const void *a, const void *b){
    39         -  return strcmp(*(char**)a, *(char**)b);
           49  +  return fossil_strcmp(((const struct tktFieldInfo*)a)->zName,
           50  +                       ((const struct tktFieldInfo*)b)->zName);
    40     51   }
    41     52   
    42     53   /*
    43         -** Obtain a list of all fields of the TICKET table.  Put them 
    44         -** in sorted order in azField[].
           54  +** Return the index into aField[] of the given field name.
           55  +** Return -1 if zFieldName is not in aField[].
           56  +*/
           57  +static int fieldId(const char *zFieldName){
           58  +  int i;
           59  +  for(i=0; i<nField; i++){
           60  +    if( fossil_strcmp(aField[i].zName, zFieldName)==0 ) return i;
           61  +  }
           62  +  return -1;
           63  +}
           64  +
           65  +/*
           66  +** Obtain a list of all fields of the TICKET and TICKETCHNG tables.  Put them 
           67  +** in sorted order in aField[].
    45     68   **
    46         -** Also allocate space for azValue[] and azAppend[] and initialize
    47         -** all the values there to zero.
           69  +** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and
           70  +** TICKETCHANGE tables exist, respectively.
    48     71   */
    49     72   static void getAllTicketFields(void){
    50     73     Stmt q;
    51     74     int i;
    52         -  if( nField>0 ) return;
           75  +  static int once = 0;
           76  +  if( once ) return;
           77  +  once = 1;
    53     78     db_prepare(&q, "PRAGMA table_info(ticket)");
    54     79     while( db_step(&q)==SQLITE_ROW ){
    55         -    const char *zField = db_column_text(&q, 1);
    56         -    if( strncmp(zField,"tkt_",4)==0 ) continue;
           80  +    const char *zFieldName = db_column_text(&q, 1);
           81  +    haveTicket = 1;
           82  +    if( memcmp(zFieldName,"tkt_",4)==0 ){
           83  +      if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
           84  +      continue;
           85  +    }
           86  +    if( nField%10==0 ){
           87  +      aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
           88  +    }
           89  +    aField[nField].zName = mprintf("%s", zFieldName);
           90  +    aField[nField].mUsed = USEDBY_TICKET;
           91  +    nField++;
           92  +  }
           93  +  db_finalize(&q);
           94  +  db_prepare(&q, "PRAGMA table_info(ticketchng)");
           95  +  while( db_step(&q)==SQLITE_ROW ){
           96  +    const char *zFieldName = db_column_text(&q, 1);
           97  +    haveTicketChng = 1;
           98  +    if( memcmp(zFieldName,"tkt_",4)==0 ){
           99  +      if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1;
          100  +      continue;
          101  +    }
          102  +    if( (i = fieldId(zFieldName))>=0 ){
          103  +      aField[i].mUsed |= USEDBY_TICKETCHNG;
          104  +      continue;
          105  +    }
    57    106       if( nField%10==0 ){
    58         -      azField = realloc(azField, sizeof(azField)*3*(nField+10) );
    59         -      if( azField==0 ){
    60         -        fossil_fatal("out of memory");
    61         -      }
          107  +      aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
    62    108       }
    63         -    azField[nField] = mprintf("%s", zField);
          109  +    aField[nField].zName = mprintf("%s", zFieldName);
          110  +    aField[nField].mUsed = USEDBY_TICKETCHNG;
    64    111       nField++;
    65    112     }
    66    113     db_finalize(&q);
    67         -  qsort(azField, nField, sizeof(azField[0]), nameCmpr);
    68         -  azAppend = &azField[nField];
    69         -  memset(azAppend, 0, sizeof(azAppend[0])*nField);
    70         -  azValue = &azAppend[nField];
          114  +  qsort(aField, nField, sizeof(aField[0]), nameCmpr);
    71    115     for(i=0; i<nField; i++){
    72         -    azValue[i] = "";
          116  +    aField[i].zValue = "";
          117  +    aField[i].zAppend = 0;
    73    118     }
    74    119   }
    75    120   
    76         -/*
    77         -** Return the index into azField[] of the given field name.
    78         -** Return -1 if zField is not in azField[].
    79         -*/
    80         -static int fieldId(const char *zField){
    81         -  int i;
    82         -  for(i=0; i<nField; i++){
    83         -    if( strcmp(azField[i], zField)==0 ) return i;
    84         -  }
    85         -  return -1;
    86         -}
    87         -
    88    121   /*
    89    122   ** Query the database for all TICKET fields for the specific
    90    123   ** ticket whose name is given by the "name" CGI parameter.
    91    124   ** Load the values for all fields into the interpreter.
    92    125   **
    93    126   ** Only load those fields which do not already exist as
    94    127   ** variables.
    95    128   **
    96    129   ** Fields of the TICKET table that begin with "private_" are
    97         -** expanded using the db_reveal() function.  If g.okRdAddr is
          130  +** expanded using the db_reveal() function.  If g.perm.RdAddr is
    98    131   ** true, then the db_reveal() function will decode the content
    99         -** using the CONCEALED table so that the content legable.
          132  +** using the CONCEALED table so that the content legible.
   100    133   ** Otherwise, db_reveal() is a no-op and the content remains
   101    134   ** obscured.
   102    135   */
   103    136   static void initializeVariablesFromDb(void){
   104    137     const char *zName;
   105    138     Stmt q;
   106    139     int i, n, size, j;
   107    140   
   108    141     zName = PD("name","-none-");
   109         -  db_prepare(&q, "SELECT datetime(tkt_mtime) AS tkt_datetime, *"
          142  +  db_prepare(&q, "SELECT datetime(tkt_mtime,'localtime') AS tkt_datetime, *"
   110    143                    "  FROM ticket WHERE tkt_uuid GLOB '%q*'", zName);
   111    144     if( db_step(&q)==SQLITE_ROW ){
   112    145       n = db_column_count(&q);
   113    146       for(i=0; i<n; i++){
   114    147         const char *zVal = db_column_text(&q, i);
   115    148         const char *zName = db_column_name(&q, i);
   116    149         char *zRevealed = 0;
   117    150         if( zVal==0 ){
   118    151           zVal = "";
   119    152         }else if( strncmp(zName, "private_", 8)==0 ){
   120    153           zVal = zRevealed = db_reveal(zVal);
   121    154         }
   122         -      for(j=0; j<nField; j++){
   123         -        if( strcmp(azField[j],zName)==0 ){
   124         -          azValue[j] = mprintf("%s", zVal);
   125         -          break;
   126         -        }
   127         -      }
   128         -      if( Th_Fetch(zName, &size)==0 ){
          155  +      if( (j = fieldId(zName))>=0 ){
          156  +        aField[j].zValue = mprintf("%s", zVal);
          157  +      }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){
   129    158           Th_Store(zName, zVal);
   130    159         }
   131    160         free(zRevealed);
   132    161       }
   133         -  }else{
   134         -    db_finalize(&q);
   135         -    db_prepare(&q, "PRAGMA table_info(ticket)");
   136         -    if( Th_Fetch("tkt_uuid",&size)==0 ){
   137         -      Th_Store("tkt_uuid",zName);
   138         -    }
   139         -    while( db_step(&q)==SQLITE_ROW ){
   140         -      const char *zField = db_column_text(&q, 1);
   141         -      if( Th_Fetch(zField, &size)==0 ){
   142         -        Th_Store(zField, "");
   143         -      }
   144         -    }
   145         -    if( Th_Fetch("tkt_datetime",&size)==0 ){
   146         -      Th_Store("tkt_datetime","");
   147         -    }
   148    162     }
   149    163     db_finalize(&q);
          164  +  for(i=0; i<nField; i++){
          165  +    if( Th_Fetch(aField[i].zName, &size)==0 ){
          166  +      Th_Store(aField[i].zName, aField[i].zValue);
          167  +    }
          168  +  }
   150    169   }
   151    170   
   152    171   /*
   153    172   ** Transfer all CGI parameters to variables in the interpreter.
   154    173   */
   155    174   static void initializeVariablesFromCGI(void){
   156    175     int i;
................................................................................
   158    177   
   159    178     for(i=0; (z = cgi_parameter_name(i))!=0; i++){
   160    179       Th_Store(z, P(z));
   161    180     }
   162    181   }
   163    182   
   164    183   /*
   165         -** Update an entry of the TICKET table according to the information
   166         -** in the control file given in p.  Attempt to create the appropriate
   167         -** TICKET table entry if createFlag is true.  If createFlag is false,
   168         -** that means we already know the entry exists and so we can save the
   169         -** work of trying to create it.
          184  +** Update an entry of the TICKET and TICKETCHNG tables according to the
          185  +** information in the ticket artifact given in p.  Attempt to create
          186  +** the appropriate TICKET table entry if tktid is zero.  If tktid is nonzero
          187  +** then it will be the ROWID of an existing TICKET entry.
   170    188   **
   171         -** Return TRUE if a new TICKET entry was created and FALSE if an
   172         -** existing entry was revised.
          189  +** Parameter rid is the recordID for the ticket artifact in the BLOB table.
          190  +**
          191  +** Return the new rowid of the TICKET table entry.
   173    192   */
   174         -int ticket_insert(const Manifest *p, int createFlag, int rid){
   175         -  Blob sql;
          193  +static int ticket_insert(const Manifest *p, int rid, int tktid){
          194  +  Blob sql1, sql2, sql3;
   176    195     Stmt q;
   177         -  int i;
   178         -  const char *zSep;
   179         -  int rc = 0;
          196  +  int i, j;
          197  +  char *aUsed;
   180    198   
   181         -  getAllTicketFields();
   182         -  if( createFlag ){  
   183         -    db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
          199  +  if( tktid==0 ){
          200  +    db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
   184    201                     "VALUES(%Q, 0)", p->zTicketUuid);
   185         -    rc = db_changes();
          202  +    tktid = db_last_insert_rowid();
   186    203     }
   187         -  blob_zero(&sql);
   188         -  blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
   189         -  zSep = "SET";
          204  +  blob_zero(&sql1);
          205  +  blob_zero(&sql2);
          206  +  blob_zero(&sql3);
          207  +  blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
          208  +  if( haveTicketCTime ){
          209  +    blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
          210  +  }
          211  +  aUsed = fossil_malloc( nField );
          212  +  memset(aUsed, 0, nField);
   190    213     for(i=0; i<p->nField; i++){
   191    214       const char *zName = p->aField[i].zName;
   192         -    if( zName[0]=='+' ){
   193         -      zName++;
   194         -      if( fieldId(zName)<0 ) continue;
   195         -      blob_appendf(&sql,", %s=coalesce(%s,'') || %Q",
   196         -                   zName, zName, p->aField[i].zValue);
   197         -    }else{
   198         -      if( fieldId(zName)<0 ) continue;
   199         -      blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
          215  +    const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
          216  +    j = fieldId(zBaseName);
          217  +    if( j<0 ) continue;
          218  +    aUsed[j] = 1;
          219  +    if( aField[j].mUsed & USEDBY_TICKET ){
          220  +      if( zName[0]=='+' ){
          221  +        zName++;
          222  +        blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q",
          223  +                     zName, zName, p->aField[i].zValue);
          224  +      }else{
          225  +        blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue);
          226  +      }
          227  +    }
          228  +    if( aField[j].mUsed & USEDBY_TICKETCHNG ){
          229  +      blob_appendf(&sql2, ",%s", zName);
          230  +      blob_appendf(&sql3, ",%Q", p->aField[i].zValue);
   200    231       }
   201    232       if( rid>0 ){
   202    233         wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
   203    234       }
   204    235     }
   205         -  blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
   206         -                     p->zTicketUuid);
   207         -  db_prepare(&q, "%s", blob_str(&sql));
          236  +  blob_appendf(&sql1, " WHERE tkt_id=%d", tktid);
          237  +  db_prepare(&q, "%s", blob_str(&sql1));
   208    238     db_bind_double(&q, ":mtime", p->rDate);
   209    239     db_step(&q);
   210    240     db_finalize(&q);
   211         -  blob_reset(&sql);
   212         -  return rc;
          241  +  blob_reset(&sql1);
          242  +  if( blob_size(&sql2)>0 || haveTicketChngRid ){
          243  +    int fromTkt = 0;
          244  +    if( haveTicketChngRid ){
          245  +      blob_append(&sql2, ",tkt_rid", -1);
          246  +      blob_appendf(&sql3, ",%d", rid);
          247  +    }
          248  +    for(i=0; i<nField; i++){
          249  +      if( aUsed[i]==0
          250  +       && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH
          251  +      ){
          252  +        fromTkt = 1;
          253  +        blob_appendf(&sql2, ",%s", aField[i].zName);
          254  +        blob_appendf(&sql3, ",%s", aField[i].zName);
          255  +      }
          256  +    }
          257  +    if( fromTkt ){
          258  +      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
          259  +                     "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d",
          260  +                     blob_str(&sql2), tktid, blob_str(&sql3), tktid);
          261  +    }else{
          262  +      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
          263  +                     "VALUES(%d,:mtime%s)",
          264  +                     blob_str(&sql2), tktid, blob_str(&sql3));
          265  +    }
          266  +    db_bind_double(&q, ":mtime", p->rDate);
          267  +    db_step(&q);
          268  +    db_finalize(&q);
          269  +  }
          270  +  blob_reset(&sql2);
          271  +  blob_reset(&sql3);
          272  +  fossil_free(aUsed);
          273  +  return tktid;
   213    274   }
   214    275   
   215    276   /*
   216    277   ** Rebuild an entire entry in the TICKET table
   217    278   */
   218    279   void ticket_rebuild_entry(const char *zTktUuid){
   219    280     char *zTag = mprintf("tkt-%s", zTktUuid);
   220    281     int tagid = tag_findid(zTag, 1);
   221    282     Stmt q;
   222         -  Manifest manifest;
   223         -  Blob content;
          283  +  Manifest *pTicket;
          284  +  int tktid;
   224    285     int createFlag = 1;
   225         -  
   226         -  db_multi_exec(
   227         -     "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
   228         -  );
          286  +
          287  +  fossil_free(zTag);
          288  +  getAllTicketFields();
          289  +  if( haveTicket==0 ) return;
          290  +  tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid);
          291  +  if( haveTicketChng ){
          292  +    db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid);
          293  +  }
          294  +  db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid);
          295  +  tktid = 0;
   229    296     db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
   230    297     while( db_step(&q)==SQLITE_ROW ){
   231    298       int rid = db_column_int(&q, 0);
   232         -    content_get(rid, &content);
   233         -    manifest_parse(&manifest, &content);
   234         -    ticket_insert(&manifest, createFlag, rid);
   235         -    manifest_ticket_event(rid, &manifest, createFlag, tagid);
   236         -    manifest_clear(&manifest);
          299  +    pTicket = manifest_get(rid, CFTYPE_TICKET);
          300  +    if( pTicket ){
          301  +      tktid = ticket_insert(pTicket, rid, tktid);
          302  +      manifest_ticket_event(rid, pTicket, createFlag, tagid);
          303  +      manifest_destroy(pTicket);
          304  +    }
   237    305       createFlag = 0;
   238    306     }
   239    307     db_finalize(&q);
   240    308   }
          309  +
   241    310   
   242    311   /*
   243         -** Create the subscript interpreter and load the "common" code.
          312  +** Create the TH1 interpreter and load the "common" code.
   244    313   */
   245    314   void ticket_init(void){
   246    315     const char *zConfig;
   247         -  Th_FossilInit();
          316  +  Th_FossilInit(0, 0);
   248    317     zConfig = ticket_common_code();
   249    318     Th_Eval(g.interp, 0, zConfig, -1);
   250    319   }
   251    320   
   252    321   /*
   253         -** Recreate the ticket table.
          322  +** Create the TH1 interpreter and load the "change" code.
          323  +*/
          324  +int ticket_change(void){
          325  +  const char *zConfig;
          326  +  Th_FossilInit(0, 0);
          327  +  zConfig = ticket_change_code();
          328  +  return Th_Eval(g.interp, 0, zConfig, -1);
          329  +}
          330  +
          331  +/*
          332  +** Recreate the TICKET and TICKETCHNG tables.
   254    333   */
   255    334   void ticket_create_table(int separateConnection){
   256    335     const char *zSql;
   257    336   
   258         -  db_multi_exec("DROP TABLE IF EXISTS ticket;");
          337  +  db_multi_exec(
          338  +    "DROP TABLE IF EXISTS ticket;"
          339  +    "DROP TABLE IF EXISTS ticketchng;"
          340  +  );
   259    341     zSql = ticket_table_schema();
   260    342     if( separateConnection ){
          343  +    db_end_transaction(0);
   261    344       db_init_database(g.zRepositoryName, zSql, 0);
   262    345     }else{
   263    346       db_multi_exec("%s", zSql);
   264    347     }
   265    348   }
   266    349   
   267    350   /*
   268         -** Repopulate the ticket table
          351  +** Repopulate the TICKET and TICKETCHNG tables from scratch using all
          352  +** available ticket artifacts.
   269    353   */
   270    354   void ticket_rebuild(void){
   271    355     Stmt q;
   272    356     ticket_create_table(1);
   273    357     db_begin_transaction();
   274    358     db_prepare(&q,"SELECT tagname FROM tag WHERE tagname GLOB 'tkt-*'");
   275    359     while( db_step(&q)==SQLITE_ROW ){
................................................................................
   279    363       len = strlen(zName);
   280    364       if( len<20 || !validate16(zName, len) ) continue;
   281    365       ticket_rebuild_entry(zName);
   282    366     }
   283    367     db_finalize(&q);
   284    368     db_end_transaction(0);
   285    369   }
          370  +
          371  +/*
          372  +** COMMAND: test-ticket-rebuild
          373  +**
          374  +** Usage: %fossil test-ticket-rebuild TICKETID|all
          375  +**
          376  +** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID
          377  +** or for ALL.
          378  +*/
          379  +void test_ticket_rebuild(void){
          380  +  db_find_and_open_repository(0, 0);
          381  +  if( g.argc!=3 ) usage("TICKETID|all");
          382  +  if( fossil_strcmp(g.argv[2], "all")==0 ){
          383  +    ticket_rebuild();
          384  +  }else{
          385  +    const char *zUuid;
          386  +    zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag"
          387  +                       " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]);
          388  +    if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]);
          389  +    ticket_rebuild_entry(zUuid);
          390  +  }
          391  +}
          392  +
          393  +/*
          394  +** For trouble-shooting purposes, render a dump of the aField[] table to
          395  +** the webpage currently under construction.
          396  +*/
          397  +static void showAllFields(void){
          398  +  int i;
          399  +  @ <font color="blue">
          400  +  @ <p>Database fields:</p><ul>
          401  +  for(i=0; i<nField; i++){
          402  +    @ <li>aField[%d(i)].zName = "%h(aField[i].zName)";
          403  +    @ originally = "%h(aField[i].zValue)";
          404  +    @ currently = "%h(PD(aField[i].zName,""))"";
          405  +    if( aField[i].zAppend ){
          406  +      @ zAppend = "%h(aField[i].zAppend)";
          407  +    }
          408  +    @ mUsed = %d(aField[i].mUsed);
          409  +  }
          410  +  @ </ul></font>
          411  +}
   286    412   
   287    413   /*
   288    414   ** WEBPAGE: tktview
   289    415   ** URL:  tktview?name=UUID
   290    416   **
   291    417   ** View a ticket.
   292    418   */
   293    419   void tktview_page(void){
   294    420     const char *zScript;
   295    421     char *zFullName;
   296    422     const char *zUuid = PD("name","");
   297    423   
   298    424     login_check_credentials();
   299         -  if( !g.okRdTkt ){ login_needed(); return; }
   300         -  if( g.okWrTkt || g.okApndTkt ){
          425  +  if( !g.perm.RdTkt ){ login_needed(); return; }
          426  +  if( g.perm.WrTkt || g.perm.ApndTkt ){
   301    427       style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
   302    428           g.zTop, PD("name",""));
   303    429     }
   304         -  if( g.okHistory ){
          430  +  if( g.perm.Hyperlink ){
   305    431       style_submenu_element("History", "History Of This Ticket", 
   306    432           "%s/tkthistory/%T", g.zTop, zUuid);
   307    433       style_submenu_element("Timeline", "Timeline Of This Ticket", 
   308    434           "%s/tkttimeline/%T", g.zTop, zUuid);
   309    435       style_submenu_element("Check-ins", "Check-ins Of This Ticket", 
   310    436           "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
   311    437     }
   312         -  if( g.okNewTkt ){
          438  +  if( g.perm.NewTkt ){
   313    439       style_submenu_element("New Ticket", "Create a new ticket",
   314    440           "%s/tktnew", g.zTop);
   315    441     }
   316         -  if( g.okApndTkt && g.okAttach ){
          442  +  if( g.perm.ApndTkt && g.perm.Attach ){
   317    443       style_submenu_element("Attach", "Add An Attachment",
   318         -        "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
          444  +        "%s/attachadd?tkt=%T&from=%s/tktview/%t",
   319    445           g.zTop, zUuid, g.zTop, zUuid);
   320    446     }
          447  +  if( P("plaintext") ){
          448  +    style_submenu_element("Formatted", "Formatted", "%R/tktview/%S", zUuid);
          449  +  }else{
          450  +    style_submenu_element("Plaintext", "Plaintext",
          451  +                          "%R/tktview/%S?plaintext", zUuid);
          452  +  }
   321    453     style_header("View Ticket");
   322    454     if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   323    455     ticket_init();
          456  +  initializeVariablesFromCGI();
          457  +  getAllTicketFields();
   324    458     initializeVariablesFromDb();
   325    459     zScript = ticket_viewpage_code();
          460  +  if( P("showfields")!=0 ) showAllFields();
   326    461     if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
   327    462     Th_Render(zScript);
   328    463     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   329    464   
   330    465     zFullName = db_text(0, 
   331    466          "SELECT tkt_uuid FROM ticket"
   332    467          " WHERE tkt_uuid GLOB '%q*'", zUuid);
   333    468     if( zFullName ){
   334         -    int cnt = 0;
   335         -    Stmt q;
   336         -    db_prepare(&q,
   337         -       "SELECT datetime(mtime,'localtime'), filename, user"
   338         -       "  FROM attachment"
   339         -       " WHERE isLatest AND src!='' AND target=%Q"
   340         -       " ORDER BY mtime DESC",
   341         -       zFullName);
   342         -    while( db_step(&q)==SQLITE_ROW ){
   343         -      const char *zDate = db_column_text(&q, 0);
   344         -      const char *zFile = db_column_text(&q, 1);
   345         -      const char *zUser = db_column_text(&q, 2);
   346         -      if( cnt==0 ){
   347         -        @ <hr><h2>Attachments:</h2>
   348         -        @ <ul>
   349         -      }
   350         -      cnt++;
   351         -      @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
   352         -      @ %h(zFile)</a> add by %h(zUser) on
   353         -      hyperlink_to_date(zDate, ".");
   354         -      if( g.okWrTkt && g.okAttach ){
   355         -        @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
   356         -      }
   357         -    }
   358         -    if( cnt ){
   359         -      @ </ul>
   360         -    }
   361         -    db_finalize(&q);
          469  +    attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
   362    470     }
   363    471    
   364    472     style_footer();
   365    473   }
   366    474   
   367    475   /*
   368    476   ** TH command:   append_field FIELD STRING
................................................................................
   385    493       return Th_WrongNumArgs(interp, "append_field FIELD STRING");
   386    494     }
   387    495     if( g.thTrace ){
   388    496       Th_Trace("append_field %#h {%#h}<br />\n",
   389    497                 argl[1], argv[1], argl[2], argv[2]);
   390    498     }
   391    499     for(idx=0; idx<nField; idx++){
   392         -    if( strncmp(azField[idx], argv[1], argl[1])==0
   393         -        && azField[idx][argl[1]]==0 ){
          500  +    if( memcmp(aField[idx].zName, argv[1], argl[1])==0
          501  +        && aField[idx].zName[argl[1]]==0 ){
   394    502         break;
   395    503       }
   396    504     }
   397    505     if( idx>=nField ){
   398    506       Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]);
   399    507       return TH_ERROR;
   400    508     }
   401         -  azAppend[idx] = mprintf("%.*s", argl[2], argv[2]);
          509  +  aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]);
   402    510     return TH_OK;
   403    511   }
          512  +
          513  +/*
          514  +** Write a ticket into the repository.
          515  +*/
          516  +static void ticket_put(
          517  +  Blob *pTicket,           /* The text of the ticket change record */
          518  +  const char *zTktId,      /* The ticket to which this change is applied */
          519  +  int needMod              /* True if moderation is needed */
          520  +){
          521  +  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
          522  +  if( rid==0 ){
          523  +    fossil_panic("trouble committing ticket: %s", g.zErrMsg);
          524  +  }
          525  +  if( needMod ){
          526  +    moderation_table_create();
          527  +    db_multi_exec(
          528  +      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
          529  +      rid, zTktId
          530  +    );
          531  +  }else{
          532  +    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
          533  +    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
          534  +  }
          535  +  manifest_crosslink_begin();
          536  +  manifest_crosslink(rid, pTicket);
          537  +  assert( blob_is_reset(pTicket) );
          538  +  manifest_crosslink_end();
          539  +}
   404    540   
   405    541   /*
   406    542   ** Subscript command:   submit_ticket
   407    543   **
   408    544   ** Construct and submit a new ticket artifact.  The fields of the artifact
   409    545   ** are the names of the columns in the TICKET table.  The content is
   410    546   ** taken from TH variables.  If the content is unchanged, the field is
................................................................................
   417    553     int argc, 
   418    554     const char **argv, 
   419    555     int *argl
   420    556   ){
   421    557     char *zDate;
   422    558     const char *zUuid;
   423    559     int i;
   424         -  int rid;
          560  +  int nJ = 0;
   425    561     Blob tktchng, cksum;
   426    562   
   427    563     login_verify_csrf_secret();
          564  +  if( !captcha_is_correct() ){
          565  +    @ <p class="generalError">Error: Incorrect security code.</p>
          566  +    return TH_OK;
          567  +  }
   428    568     zUuid = (const char *)pUuid;
   429    569     blob_zero(&tktchng);
   430         -  zDate = db_text(0, "SELECT datetime('now')");
   431         -  zDate[10] = 'T';
          570  +  zDate = date_in_standard_format("now");
   432    571     blob_appendf(&tktchng, "D %s\n", zDate);
   433    572     free(zDate);
          573  +  for(i=0; i<nField; i++){
          574  +    if( aField[i].zAppend ){
          575  +      blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName,
          576  +                   fossilize(aField[i].zAppend, -1));
          577  +      ++nJ;
          578  +    }
          579  +  }
   434    580     for(i=0; i<nField; i++){
   435    581       const char *zValue;
   436    582       int nValue;
   437         -    if( azAppend[i] ){
   438         -      blob_appendf(&tktchng, "J +%s %z\n", azField[i],
   439         -                   fossilize(azAppend[i], -1));
   440         -    }else{
   441         -      zValue = Th_Fetch(azField[i], &nValue);
   442         -      if( zValue ){
   443         -        while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; }
   444         -        if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){
   445         -          if( strncmp(azField[i], "private_", 8)==0 ){
   446         -            zValue = db_conceal(zValue, nValue);
   447         -            blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
   448         -          }else{
   449         -            blob_appendf(&tktchng, "J %s %#F\n", azField[i], nValue, zValue);
   450         -          }
          583  +    if( aField[i].zAppend ) continue;
          584  +    zValue = Th_Fetch(aField[i].zName, &nValue);
          585  +    if( zValue ){
          586  +      while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
          587  +      if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
          588  +       || memcmp(zValue, aField[i].zValue, nValue)!=0
          589  +       || strlen(aField[i].zValue)!=nValue
          590  +      ){
          591  +        if( memcmp(aField[i].zName, "private_", 8)==0 ){
          592  +          zValue = db_conceal(zValue, nValue);
          593  +          blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue);
          594  +        }else{
          595  +          blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
   451    596           }
          597  +        nJ++;
   452    598         }
   453    599       }
   454    600     }
   455    601     if( *(char**)pUuid ){
   456    602       zUuid = db_text(0, 
   457         -       "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", P("name")
          603  +       "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", P("name")
   458    604       );
   459    605     }else{
   460    606       zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
   461    607     }
   462    608     *(const char**)pUuid = zUuid;
   463    609     blob_appendf(&tktchng, "K %s\n", zUuid);
   464    610     blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : "");
   465    611     md5sum_blob(&tktchng, &cksum);
   466    612     blob_appendf(&tktchng, "Z %b\n", &cksum);
   467         -  if( g.thTrace ){
          613  +  if( nJ==0 ){
          614  +    blob_reset(&tktchng);
          615  +    return TH_OK;
          616  +  }
          617  +  if( g.zPath[0]=='d' ){
          618  +    /* If called from /debug_tktnew or /debug_tktedit... */
          619  +    @ <font color="blue">
          620  +    @ <p>Ticket artifact that would have been submitted:</p>
          621  +    @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
          622  +    @ <hr /></font>
          623  +    return TH_OK;
          624  +  }else if( g.thTrace ){
   468    625       Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
   469    626                "}<br />\n",
   470    627          blob_str(&tktchng));
   471    628     }else{
   472         -    rid = content_put(&tktchng, 0, 0);
   473         -    if( rid==0 ){
   474         -      fossil_panic("trouble committing ticket: %s", g.zErrMsg);
   475         -    }
   476         -    manifest_crosslink_begin();
   477         -    manifest_crosslink(rid, &tktchng);
   478         -    manifest_crosslink_end();
          629  +    ticket_put(&tktchng, zUuid,
          630  +               (g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1));
   479    631     }
   480         -  return TH_RETURN;
          632  +  return ticket_change();
   481    633   }
   482    634   
   483    635   
   484    636   /*
   485    637   ** WEBPAGE: tktnew
   486    638   ** WEBPAGE: debug_tktnew
   487    639   **
   488         -** Enter a new ticket.  the tktnew_template script in the ticket
          640  +** Enter a new ticket.  The tktnew_template script in the ticket
   489    641   ** configuration is used.  The /tktnew page is the official ticket
   490    642   ** entry page.  The /debug_tktnew page is used for debugging the
   491    643   ** tktnew_template in the ticket configuration.  /debug_tktnew works
   492    644   ** just like /tktnew except that it does not really save the new ticket
   493    645   ** when you press submit - it just prints the ticket artifact at the
   494    646   ** top of the screen.
   495    647   */
   496    648   void tktnew_page(void){
   497    649     const char *zScript;
   498    650     char *zNewUuid = 0;
   499    651   
   500    652     login_check_credentials();
   501         -  if( !g.okNewTkt ){ login_needed(); return; }
          653  +  if( !g.perm.NewTkt ){ login_needed(); return; }
   502    654     if( P("cancel") ){
   503    655       cgi_redirect("home");
   504    656     }
   505    657     style_header("New Ticket");
   506    658     if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
   507    659     ticket_init();
          660  +  initializeVariablesFromCGI();
   508    661     getAllTicketFields();
   509    662     initializeVariablesFromDb();
   510         -  initializeVariablesFromCGI();
   511         -  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
          663  +  if( g.zPath[0]=='d' ) showAllFields();
          664  +  form_begin(0, "%R/%s", g.zPath);
   512    665     login_insert_csrf_secret();
          666  +  if( P("date_override") && g.perm.Setup ){
          667  +    @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
          668  +  }
   513    669     zScript = ticket_newpage_code();
   514         -  Th_Store("login", g.zLogin);
          670  +  Th_Store("login", g.zLogin ? g.zLogin : "nobody");
   515    671     Th_Store("date", db_text(0, "SELECT datetime('now')"));
   516    672     Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
   517    673                      (void*)&zNewUuid, 0);
   518    674     if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
   519    675     if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
   520         -    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
          676  +    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
   521    677       return;
   522    678     }
          679  +  captcha_generate();
   523    680     @ </form>
   524    681     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   525    682     style_footer();
   526    683   }
   527    684   
   528    685   /*
   529    686   ** WEBPAGE: tktedit
................................................................................
   539    696   void tktedit_page(void){
   540    697     const char *zScript;
   541    698     int nName;
   542    699     const char *zName;
   543    700     int nRec;
   544    701   
   545    702     login_check_credentials();
   546         -  if( !g.okApndTkt && !g.okWrTkt ){ login_needed(); return; }
          703  +  if( !g.perm.ApndTkt && !g.perm.WrTkt ){ login_needed(); return; }
   547    704     zName = P("name");
   548    705     if( P("cancel") ){
   549    706       cgi_redirectf("tktview?name=%T", zName);
   550    707     }
   551    708     style_header("Edit Ticket");
   552    709     if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
   553    710             || !validate16(zName,nName) ){
   554         -    @ <font color="red"><b>Not a valid ticket id: \"%h(zName)\"</b></font>
          711  +    @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
   555    712       style_footer();
   556    713       return;
   557    714     }
   558    715     nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
   559    716                   zName);
   560    717     if( nRec==0 ){
   561         -    @ <font color="red"><b>No such ticket: \"%h(zName)\"</b></font>
          718  +    @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
   562    719       style_footer();
   563    720       return;
   564    721     }
   565    722     if( nRec>1 ){
   566         -    @ <font color="red"><b>%d(nRec) tickets begin with: \"%h(zName)\"</b></font>
          723  +    @ <span class="tktError">%d(nRec) tickets begin with:
          724  +    @ \"%h(zName)\"</span>
   567    725       style_footer();
   568    726       return;
   569    727     }
   570    728     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
   571    729     ticket_init();
   572    730     getAllTicketFields();
   573    731     initializeVariablesFromCGI();
   574    732     initializeVariablesFromDb();
   575         -  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
   576         -  @ <input type="hidden" name="name" value="%s(zName)">
          733  +  if( g.zPath[0]=='d' ) showAllFields();
          734  +  form_begin(0, "%R/%s", g.zPath);
          735  +  @ <input type="hidden" name="name" value="%s(zName)" />
   577    736     login_insert_csrf_secret();
   578    737     zScript = ticket_editpage_code();
   579         -  Th_Store("login", g.zLogin);
          738  +  Th_Store("login", g.zLogin ? g.zLogin : "nobody");
   580    739     Th_Store("date", db_text(0, "SELECT datetime('now')"));
   581    740     Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
   582    741     Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
   583    742     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
   584    743     if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
   585         -    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
          744  +    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
   586    745       return;
   587    746     }
          747  +  captcha_generate();
   588    748     @ </form>
   589    749     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
   590    750     style_footer();
   591    751   }
   592    752   
   593    753   /*
   594    754   ** Check the ticket table schema in zSchema to see if it appears to
................................................................................
   605    765       rc = sqlite3_exec(db, zSchema, 0, 0, &zErr);
   606    766       if( rc!=SQLITE_OK ){
   607    767         sqlite3_close(db);
   608    768         return zErr;
   609    769       }
   610    770       rc = sqlite3_exec(db, "SELECT tkt_id, tkt_uuid, tkt_mtime FROM ticket",
   611    771                         0, 0, 0);
   612         -    sqlite3_close(db);
   613    772       if( rc!=SQLITE_OK ){
   614         -      zErr = mprintf("schema fails to define a valid ticket table "
   615         -                     "containing all required fields");
   616         -      return zErr;
          773  +      zErr = mprintf("schema fails to define valid a TICKET "
          774  +                     "table containing all required fields");
          775  +    }else{
          776  +      rc = sqlite3_exec(db, "SELECT tkt_id, tkt_mtime FROM ticketchng", 0,0,0);
          777  +      if( rc!=SQLITE_OK ){
          778  +        zErr = mprintf("schema fails to define valid a TICKETCHNG "
          779  +                       "table containing all required fields");
          780  +      }
   617    781       }
          782  +    sqlite3_close(db);
   618    783     }
   619         -  return 0;
          784  +  return zErr;
   620    785   }
   621    786   
   622    787   /*
   623    788   ** WEBPAGE: tkttimeline
   624    789   ** URL: /tkttimeline?name=TICKETUUID&y=TYPE
   625    790   **
   626    791   ** Show the change history for a single ticket in timeline format.
................................................................................
   632    797     const char *zUuid;
   633    798     char *zFullUuid;
   634    799     int tagid;
   635    800     char zGlobPattern[50];
   636    801     const char *zType;
   637    802   
   638    803     login_check_credentials();
   639         -  if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
          804  +  if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   640    805     zUuid = PD("name","");
   641    806     zType = PD("y","a");
   642    807     if( zType[0]!='c' ){
   643    808       style_submenu_element("Check-ins", "Check-ins",
   644    809          "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
   645    810     }else{
   646    811       style_submenu_element("Timeline", "Timeline",
................................................................................
   687    852                     " WHERE target=%Q) "
   688    853            "ORDER BY mtime DESC",
   689    854            timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
   690    855       );
   691    856     }
   692    857     db_prepare(&q, zSQL);
   693    858     free(zSQL);
   694         -  www_print_timeline(&q, TIMELINE_ARTID, 0);
          859  +  www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH,
          860  +                     0, 0, 0);
   695    861     db_finalize(&q);
   696    862     style_footer();
   697    863   }
   698    864   
   699    865   /*
   700    866   ** WEBPAGE: tkthistory
   701    867   ** URL: /tkthistory?name=TICKETUUID
................................................................................
   703    869   ** Show the complete change history for a single ticket
   704    870   */
   705    871   void tkthistory_page(void){
   706    872     Stmt q;
   707    873     char *zTitle;
   708    874     const char *zUuid;
   709    875     int tagid;
          876  +  int nChng = 0;
   710    877   
   711    878     login_check_credentials();
   712         -  if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
          879  +  if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   713    880     zUuid = PD("name","");
   714    881     zTitle = mprintf("History Of Ticket %h", zUuid);
   715    882     style_submenu_element("Status", "Status",
   716    883       "%s/info/%s", g.zTop, zUuid);
   717    884     style_submenu_element("Check-ins", "Check-ins",
   718         -    "%s/tkttimeline?name=%s?y=ci", g.zTop, zUuid);
          885  +    "%s/tkttimeline?name=%s&y=ci", g.zTop, zUuid);
   719    886     style_submenu_element("Timeline", "Timeline",
   720    887       "%s/tkttimeline?name=%s", g.zTop, zUuid);
          888  +  if( P("plaintext")!=0 ){
          889  +    style_submenu_element("Formatted", "Formatted",
          890  +                          "%R/tkthistory/%S", zUuid);
          891  +  }else{
          892  +    style_submenu_element("Plaintext", "Plaintext",
          893  +                          "%R/tkthistory/%S?plaintext", zUuid);
          894  +  }
   721    895     style_header(zTitle);
   722    896     free(zTitle);
   723    897   
   724    898     tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
   725    899     if( tagid==0 ){
   726    900       @ No such ticket: %h(zUuid)
   727    901       style_footer();
................................................................................
   733    907       " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
   734    908       "   AND blob.rid=event.objid"
   735    909       " UNION "
   736    910       "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
   737    911       "  FROM attachment, blob"
   738    912       " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
   739    913       "   AND blob.rid=attachid"
   740         -    " ORDER BY 1 DESC",
          914  +    " ORDER BY 1",
   741    915       tagid, tagid
   742    916     );
   743    917     while( db_step(&q)==SQLITE_ROW ){
   744         -    Blob content;
   745         -    Manifest m;
          918  +    Manifest *pTicket;
   746    919       char zShort[12];
   747    920       const char *zDate = db_column_text(&q, 0);
   748    921       int rid = db_column_int(&q, 1);
   749    922       const char *zChngUuid = db_column_text(&q, 2);
   750    923       const char *zFile = db_column_text(&q, 4);
   751    924       memcpy(zShort, zChngUuid, 10);
   752    925       zShort[10] = 0;
          926  +    if( nChng==0 ){
          927  +      @ <ol>
          928  +    }
          929  +    nChng++;
   753    930       if( zFile!=0 ){
   754    931         const char *zSrc = db_column_text(&q, 3);
   755    932         const char *zUser = db_column_text(&q, 5);
   756    933         if( zSrc==0 || zSrc[0]==0 ){
   757    934           @ 
   758         -        @ <p>Delete attachment "%h(zFile)"
          935  +        @ <li><p>Delete attachment "%h(zFile)"
   759    936         }else{
   760    937           @ 
   761         -        @ <p>Add attachment "%h(zFile)"
          938  +        @ <li><p>Add attachment
          939  +        @ "%z(href("%R/artifact/%S",zSrc))%h(zFile)</a>"
   762    940         }
   763         -      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
          941  +      @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
   764    942         @ (rid %d(rid)) by
   765    943         hyperlink_to_user(zUser,zDate," on");
   766    944         hyperlink_to_date(zDate, ".</p>");
   767    945       }else{
   768         -      content_get(rid, &content);
   769         -      if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
          946  +      pTicket = manifest_get(rid, CFTYPE_TICKET);
          947  +      if( pTicket ){
   770    948           @
   771         -        @ <p>Ticket change
   772         -        @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
          949  +        @ <li><p>Ticket change
          950  +        @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
   773    951           @ (rid %d(rid)) by
   774         -        hyperlink_to_user(m.zUser,zDate," on");
          952  +        hyperlink_to_user(pTicket->zUser,zDate," on");
   775    953           hyperlink_to_date(zDate, ":");
   776         -        ticket_output_change_artifact(&m);
   777    954           @ </p>
          955  +        ticket_output_change_artifact(pTicket, "a");
   778    956         }
   779         -      manifest_clear(&m);
          957  +      manifest_destroy(pTicket);
   780    958       }
   781    959     }
   782    960     db_finalize(&q);
          961  +  if( nChng ){
          962  +    @ </ol>
          963  +  }
   783    964     style_footer();
   784    965   }
   785    966   
   786    967   /*
   787    968   ** Return TRUE if the given BLOB contains a newline character.
   788    969   */
   789    970   static int contains_newline(Blob *p){
................................................................................
   795    976     return 0;
   796    977   }
   797    978   
   798    979   /*
   799    980   ** The pTkt object is a ticket change artifact.  Output a detailed
   800    981   ** description of this object.
   801    982   */
   802         -void ticket_output_change_artifact(Manifest *pTkt){
          983  +void ticket_output_change_artifact(Manifest *pTkt, const char *zListType){
   803    984     int i;
   804         -  @ <ol>
          985  +  int wikiFlags = WIKI_NOBADLINKS;
          986  +  const char *zBlock = "<blockquote>";
          987  +  const char *zEnd = "</blockquote>";
          988  +  if( P("plaintext")!=0 ){
          989  +    wikiFlags |= WIKI_LINKSONLY;
          990  +    zBlock = "<blockquote><pre class='verbatim'>";
          991  +    zEnd = "</pre></blockquote>";
          992  +  }
          993  +  if( zListType==0 ) zListType = "1";
          994  +  @ <ol type="%s(zListType)">
   805    995     for(i=0; i<pTkt->nField; i++){
   806    996       Blob val;
   807    997       const char *z;
   808    998       z = pTkt->aField[i].zName;
   809    999       blob_set(&val, pTkt->aField[i].zValue);
   810   1000       if( z[0]=='+' ){
   811         -      @ <li>Appended to %h(&z[1]):<blockquote>
   812         -      wiki_convert(&val, 0, 0);
   813         -      @ </blockquote></li>
   814         -    }else if( blob_size(&val)<=50 && contains_newline(&val) ){
   815         -      @ <li>Change %h(z) to:<blockquote>
   816         -      wiki_convert(&val, 0, 0);
   817         -      @ </blockquote></li>
         1001  +      @ <li>Appended to %h(&z[1]):%s(zBlock)
         1002  +      wiki_convert(&val, 0, wikiFlags);
         1003  +      @ %s(zEnd)</li>
         1004  +    }else if( blob_size(&val)>50 || contains_newline(&val) ){
         1005  +      @ <li>Change %h(z) to:%s(zBlock)
         1006  +      wiki_convert(&val, 0, wikiFlags);
         1007  +      @ %s(zEnd)</li>
   818   1008       }else{
   819   1009         @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
   820   1010       }
   821   1011       blob_reset(&val);
   822   1012     }
   823   1013     @ </ol>
   824   1014   }
         1015  +
         1016  +/*
         1017  +** COMMAND: ticket*
         1018  +** Usage: %fossil ticket SUBCOMMAND ...
         1019  +**
         1020  +** Run various subcommands to control tickets
         1021  +**
         1022  +**   %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
         1023  +**
         1024  +**     options can be:
         1025  +**       ?-l|--limit LIMITCHAR?
         1026  +**       ?-q|--quote?
         1027  +**       ?-R|--repository FILE?
         1028  +**
         1029  +**     Run the ticket report, identified by the report format title
         1030  +**     used in the gui. The data is written as flat file on stdout,
         1031  +**     using "," as separator. The separator "," can be changed using
         1032  +**     the -l or --limit option.
         1033  +**
         1034  +**     If TICKETFILTER is given on the commandline, the query is
         1035  +**     limited with a new WHERE-condition.
         1036  +**       example:  Report lists a column # with the uuid
         1037  +**                 TICKETFILTER may be [#]='uuuuuuuuu'
         1038  +**       example:  Report only lists rows with status not open
         1039  +**                 TICKETFILTER: status != 'open'
         1040  +**     If the option -q|--quote is used, the tickets are encoded by
         1041  +**     quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
         1042  +**     cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
         1043  +**     Otherwise, the simplified encoding as on the show report raw
         1044  +**     page in the gui is used. This has no effect in JSON mode.
         1045  +**
         1046  +**     Instead of the report title its possible to use the report
         1047  +**     number. Using the special report number 0 list all columns,
         1048  +**     defined in the ticket table.
         1049  +**
         1050  +**   %fossil ticket list fields
         1051  +**
         1052  +**     list all fields, defined for ticket in the fossil repository
         1053  +**
         1054  +**   %fossil ticket list reports
         1055  +**
         1056  +**     list all ticket reports, defined in the fossil repository
         1057  +**
         1058  +**   %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote?
         1059  +**   %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote?
         1060  +**
         1061  +**     change ticket identified by TICKETUUID and set the value of
         1062  +**     field FIELD to VALUE.
         1063  +**
         1064  +**     Field names as defined in the TICKET table.  By default, these
         1065  +**     names include: type, status, subsystem, priority, severity, foundin,
         1066  +**     resolution, title, and comment, but other field names can be added
         1067  +**     or substituted in customized installations.
         1068  +**
         1069  +**     If you use +FIELD, the VALUE Is appended to the field FIELD.
         1070  +**     You can use more than one field/value pair on the commandline.
         1071  +**     Using -q|--quote  enables the special character decoding as
         1072  +**     in "ticket show". So it's possible, to set multiline text or
         1073  +**     text with special characters.
         1074  +**
         1075  +**   %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
         1076  +**
         1077  +**     like set, but create a new ticket with the given values.
         1078  +**
         1079  +**   %fossil ticket history TICKETUUID
         1080  +**
         1081  +**     Show the complete change history for the ticket
         1082  +**
         1083  +** The values in set|add are not validated against the definitions
         1084  +** given in "Ticket Common Script".
         1085  +*/
         1086  +void ticket_cmd(void){
         1087  +  int n;
         1088  +  const char *zUser;
         1089  +  const char *zDate;
         1090  +  const char *zTktUuid;
         1091  +
         1092  +  /* do some ints, we want to be inside a checkout */
         1093  +  db_find_and_open_repository(0, 0);
         1094  +  user_select();
         1095  +
         1096  +  zUser = find_option("user-override",0,1);
         1097  +  if( zUser==0 ) zUser = g.zLogin;
         1098  +  zDate = find_option("date-override",0,1);
         1099  +  if( zDate==0 ) zDate = "now";
         1100  +  zDate = date_in_standard_format(zDate);
         1101  +  zTktUuid = find_option("uuid-override",0,1);
         1102  +  if( zTktUuid && (strlen(zTktUuid)!=40 || !validate16(zTktUuid,40)) ){
         1103  +    fossil_fatal("invalid --uuid-override: must be 40 characters of hex");
         1104  +  }
         1105  +
         1106  +  /*
         1107  +  ** Check that the user exists.
         1108  +  */
         1109  +  if( !db_exists("SELECT 1 FROM user WHERE login=%Q", zUser) ){
         1110  +    fossil_fatal("no such user: %s", zUser);
         1111  +  }
         1112  +
         1113  +  if( g.argc<3 ){
         1114  +    usage("add|fieldlist|set|show|history");
         1115  +  }
         1116  +  n = strlen(g.argv[2]);
         1117  +  if( n==1 && g.argv[2][0]=='s' ){
         1118  +    /* set/show cannot be distinguished, so show the usage */
         1119  +    usage("add|fieldlist|set|show|history");
         1120  +  }
         1121  +  if( strncmp(g.argv[2],"list",n)==0 ){
         1122  +    if( g.argc==3 ){
         1123  +      usage("list fields|reports");
         1124  +    }else{
         1125  +      n = strlen(g.argv[3]);
         1126  +      if( !strncmp(g.argv[3],"fields",n) ){
         1127  +        /* simply show all field names */
         1128  +        int i;
         1129  +
         1130  +        /* read all available ticket fields */
         1131  +        getAllTicketFields();
         1132  +        for(i=0; i<nField; i++){
         1133  +          printf("%s\n",aField[i].zName);
         1134  +        }
         1135  +      }else if( !strncmp(g.argv[3],"reports",n) ){
         1136  +        rpt_list_reports();
         1137  +      }else{
         1138  +        fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
         1139  +      }
         1140  +    }
         1141  +  }else{
         1142  +    /* add a new ticket or set fields on existing tickets */
         1143  +    tTktShowEncoding tktEncoding;
         1144  +
         1145  +    tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
         1146  +    
         1147  +    if( strncmp(g.argv[2],"show",n)==0 ){
         1148  +      if( g.argc==3 ){
         1149  +        usage("show REPORTNR");
         1150  +      }else{
         1151  +        const char *zRep = 0;
         1152  +        const char *zSep = 0;
         1153  +        const char *zFilterUuid = 0;
         1154  +        zSep = find_option("limit","l",1);
         1155  +        zRep = g.argv[3];
         1156  +        if( !strcmp(zRep,"0") ){
         1157  +          zRep = 0;
         1158  +        }
         1159  +        if( g.argc>4 ){
         1160  +          zFilterUuid = g.argv[4];
         1161  +        }
         1162  +        rptshow( zRep, zSep, zFilterUuid, tktEncoding );
         1163  +      }
         1164  +    }else{
         1165  +      /* add a new ticket or update an existing ticket */
         1166  +      enum { set,add,history,err } eCmd = err;
         1167  +      int i = 0;
         1168  +      Blob tktchng, cksum;
         1169  +
         1170  +      /* get command type (set/add) and get uuid, if needed for set */
         1171  +      if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
         1172  +         strncmp(g.argv[2],"history",n)==0 ){
         1173  +        if( strncmp(g.argv[2],"history",n)==0 ){
         1174  +          eCmd = history;
         1175  +        }else{
         1176  +          eCmd = set;
         1177  +        }
         1178  +        if( g.argc==3 ){
         1179  +          usage("set|change|history TICKETUUID");
         1180  +        }
         1181  +        zTktUuid = db_text(0, 
         1182  +          "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
         1183  +        );
         1184  +        if( !zTktUuid ){
         1185  +          fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
         1186  +        }
         1187  +        i=4;
         1188  +      }else if( strncmp(g.argv[2],"add",n)==0 ){
         1189  +        eCmd = add;
         1190  +        i = 3;
         1191  +        if( zTktUuid==0 ){
         1192  +          zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
         1193  +        }
         1194  +      }
         1195  +      /* none of set/add, so show the usage! */
         1196  +      if( eCmd==err ){
         1197  +        usage("add|fieldlist|set|show|history");
         1198  +      }
         1199  +
         1200  +      /* we just handle history separately here, does not get out */
         1201  +      if( eCmd==history ){
         1202  +        Stmt q;
         1203  +        int tagid;
         1204  +
         1205  +        if ( i != g.argc ){
         1206  +          fossil_fatal("no other parameters expected to %s!",g.argv[2]);
         1207  +        }
         1208  +        tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
         1209  +                       zTktUuid);
         1210  +        if( tagid==0 ){
         1211  +          fossil_fatal("no such ticket %h", zTktUuid);
         1212  +        }  
         1213  +        db_prepare(&q,
         1214  +          "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
         1215  +          "  FROM event, blob"
         1216  +          " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
         1217  +          "   AND blob.rid=event.objid"
         1218  +          " UNION "
         1219  +          "SELECT datetime(mtime,'localtime'), attachid, uuid, src, "
         1220  +          "       filename, user"
         1221  +          "  FROM attachment, blob"
         1222  +          " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
         1223  +          "   AND blob.rid=attachid"
         1224  +          " ORDER BY 1 DESC",
         1225  +          tagid, tagid
         1226  +        );
         1227  +        while( db_step(&q)==SQLITE_ROW ){
         1228  +          Manifest *pTicket;
         1229  +          char zShort[12];
         1230  +          const char *zDate = db_column_text(&q, 0);
         1231  +          int rid = db_column_int(&q, 1);
         1232  +          const char *zChngUuid = db_column_text(&q, 2);
         1233  +          const char *zFile = db_column_text(&q, 4);
         1234  +          memcpy(zShort, zChngUuid, 10);
         1235  +          zShort[10] = 0;
         1236  +          if( zFile!=0 ){
         1237  +            const char *zSrc = db_column_text(&q, 3);
         1238  +            const char *zUser = db_column_text(&q, 5);
         1239  +            if( zSrc==0 || zSrc[0]==0 ){
         1240  +              fossil_print("Delete attachment %s\n", zFile);
         1241  +            }else{
         1242  +              fossil_print("Add attachment %s\n", zFile);
         1243  +            }
         1244  +            fossil_print(" by %s on %s\n", zUser, zDate);
         1245  +          }else{
         1246  +            pTicket = manifest_get(rid, CFTYPE_TICKET);
         1247  +            if( pTicket ){
         1248  +              int i;
         1249  +
         1250  +              fossil_print("Ticket Change by %s on %s:\n",
         1251  +                           pTicket->zUser, zDate);
         1252  +              for(i=0; i<pTicket->nField; i++){
         1253  +                Blob val;
         1254  +                const char *z;
         1255  +                z = pTicket->aField[i].zName;
         1256  +                blob_set(&val, pTicket->aField[i].zValue);
         1257  +                if( z[0]=='+' ){
         1258  +                  fossil_print("  Append to ");
         1259  +		    z++;
         1260  +		  }else{
         1261  +		    fossil_print("  Change ");
         1262  +                }
         1263  +		  fossil_print("%h: ",z);
         1264  +		  if( blob_size(&val)>50 || contains_newline(&val)) {
         1265  +                  fossil_print("\n    ",blob_str(&val));
         1266  +                  comment_print(blob_str(&val),4,79);
         1267  +                }else{
         1268  +                  fossil_print("%s\n",blob_str(&val));
         1269  +                }
         1270  +                blob_reset(&val);
         1271  +              }
         1272  +            }
         1273  +            manifest_destroy(pTicket);
         1274  +          }
         1275  +        }
         1276  +        db_finalize(&q);
         1277  +        return;
         1278  +      }
         1279  +      /* read all given ticket field/value pairs from command line */
         1280  +      if( i==g.argc ){
         1281  +        fossil_fatal("empty %s command aborted!",g.argv[2]);
         1282  +      }
         1283  +      getAllTicketFields();
         1284  +      /* read commandline and assign fields in the aField[].zValue array */
         1285  +      while( i<g.argc ){
         1286  +        char *zFName;
         1287  +        char *zFValue;
         1288  +        int j;
         1289  +        int append = 0;
         1290  +
         1291  +        zFName = g.argv[i++];
         1292  +        if( i==g.argc ){
         1293  +          fossil_fatal("missing value for '%s'!",zFName);
         1294  +        }
         1295  +        zFValue = g.argv[i++];
         1296  +        if( tktEncoding == tktFossilize ){
         1297  +          zFValue=mprintf("%s",zFValue);
         1298  +          defossilize(zFValue);
         1299  +        }
         1300  +        append = (zFName[0] == '+');
         1301  +        if (append){
         1302  +          zFName++;
         1303  +        }
         1304  +        j = fieldId(zFName);
         1305  +        if( j == -1 ){
         1306  +          fossil_fatal("unknown field name '%s'!",zFName);
         1307  +        }else{
         1308  +          if (append) {
         1309  +            aField[j].zAppend = zFValue;
         1310  +          } else {
         1311  +            aField[j].zValue = zFValue;
         1312  +          }
         1313  +        }
         1314  +      }
         1315  +
         1316  +      /* now add the needed artifacts to the repository */
         1317  +      blob_zero(&tktchng);
         1318  +      /* add the time to the ticket manifest */
         1319  +      blob_appendf(&tktchng, "D %s\n", zDate);
         1320  +      /* append defined elements */
         1321  +      for(i=0; i<nField; i++){
         1322  +        char *zValue = 0;
         1323  +        char *zPfx;
         1324  +
         1325  +        if (aField[i].zAppend && aField[i].zAppend[0] ){
         1326  +          zPfx = " +";
         1327  +          zValue = aField[i].zAppend;
         1328  +        } else if( aField[i].zValue && aField[i].zValue[0] ){
         1329  +          zPfx = " ";
         1330  +          zValue = aField[i].zValue;
         1331  +        } else {
         1332  +          continue;
         1333  +        }
         1334  +        if( memcmp(aField[i].zName, "private_", 8)==0 ){
         1335  +          zValue = db_conceal(zValue, strlen(zValue));
         1336  +          blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue);
         1337  +        }else{
         1338  +          blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
         1339  +                       aField[i].zName, strlen(zValue), zValue);
         1340  +        }
         1341  +      }
         1342  +      blob_appendf(&tktchng, "K %s\n", zTktUuid);
         1343  +      blob_appendf(&tktchng, "U %F\n", zUser);
         1344  +      md5sum_blob(&tktchng, &cksum);
         1345  +      blob_appendf(&tktchng, "Z %b\n", &cksum);
         1346  +      ticket_put(&tktchng, zTktUuid, 0);
         1347  +      printf("ticket %s succeeded for %s\n",
         1348  +             (eCmd==set?"set":"add"),zTktUuid);
         1349  +    }
         1350  +  }
         1351  +}

Changes to src/tktsetup.c.

    24     24   
    25     25   /*
    26     26   ** Main sub-menu for configuring the ticketing system.
    27     27   ** WEBPAGE: tktsetup
    28     28   */
    29     29   void tktsetup_page(void){
    30     30     login_check_credentials();
    31         -  if( !g.okSetup ){
           31  +  if( !g.perm.Setup ){
    32     32       login_needed();
    33     33     }
    34     34   
    35     35     style_header("Ticket Setup");
    36     36     @ <table border="0" cellspacing="20">
    37     37     setup_menu_entry("Table", "tktsetup_tab",
    38     38       "Specify the schema of the  \"ticket\" table in the database.");
    39     39     setup_menu_entry("Timeline", "tktsetup_timeline",
    40     40       "How to display ticket status in the timeline");
    41     41     setup_menu_entry("Common", "tktsetup_com",
    42     42       "Common TH1 code run before all ticket processing.");
           43  +  setup_menu_entry("Change", "tktsetup_change",
           44  +    "The TH1 code run after a ticket is edited or created.");
    43     45     setup_menu_entry("New Ticket Page", "tktsetup_newpage",
    44     46       "HTML with embedded TH1 code for the \"new ticket\" webpage.");
    45     47     setup_menu_entry("View Ticket Page", "tktsetup_viewpage",
    46     48       "HTML with embedded TH1 code for the \"view ticket\" webpage.");
    47     49     setup_menu_entry("Edit Ticket Page", "tktsetup_editpage",
    48     50       "HTML with embedded TH1 code for the \"edit ticket\" webpage.");
    49     51     setup_menu_entry("Report List Page", "tktsetup_reportlist",
................................................................................
    63     65   /* @-comment: ** */
    64     66   static const char zDefaultTicketTable[] =
    65     67   @ CREATE TABLE ticket(
    66     68   @   -- Do not change any column that begins with tkt_
    67     69   @   tkt_id INTEGER PRIMARY KEY,
    68     70   @   tkt_uuid TEXT UNIQUE,
    69     71   @   tkt_mtime DATE,
    70         -@   -- Add as many field as required below this line
           72  +@   tkt_ctime DATE,
           73  +@   -- Add as many fields as required below this line
    71     74   @   type TEXT,
    72     75   @   status TEXT,
    73     76   @   subsystem TEXT,
    74     77   @   priority TEXT,
    75     78   @   severity TEXT,
    76     79   @   foundin TEXT,
    77     80   @   private_contact TEXT,
    78     81   @   resolution TEXT,
    79     82   @   title TEXT,
    80     83   @   comment TEXT
    81     84   @ );
           85  +@ CREATE TABLE ticketchng(
           86  +@   -- Do not change any column that begins with tkt_
           87  +@   tkt_id INTEGER REFERENCES ticket,
           88  +@   tkt_rid INTEGER REFERENCES blob,
           89  +@   tkt_mtime DATE,
           90  +@   -- Add as many fields as required below this line
           91  +@   login TEXT,
           92  +@   username TEXT,
           93  +@   mimetype TEXT,
           94  +@   icomment TEXT
           95  +@ );
           96  +@ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime);
    82     97   ;
    83     98   
    84     99   /*
    85    100   ** Return the ticket table definition
    86    101   */
    87    102   const char *ticket_table_schema(void){
    88    103     return db_get("ticket-table", (char*)zDefaultTicketTable);
................................................................................
    92    107   ** Common implementation for the ticket setup editor pages.
    93    108   */
    94    109   static void tktsetup_generic(
    95    110     const char *zTitle,           /* Page title */
    96    111     const char *zDbField,         /* Configuration field being edited */
    97    112     const char *zDfltValue,       /* Default text value */
    98    113     const char *zDesc,            /* Description of this field */
    99         -  char *(*xText)(const char*),  /* Validitity test or NULL */
   100         -  void (*xRebuild)(void),       /* Run after successulf update */
          114  +  char *(*xText)(const char*),  /* Validity test or NULL */
          115  +  void (*xRebuild)(void),       /* Run after successful update */
   101    116     int height                    /* Height of the edit box */
   102    117   ){
   103    118     const char *z;
   104    119     int isSubmit;
   105    120     
   106    121     login_check_credentials();
   107         -  if( !g.okSetup ){
          122  +  if( !g.perm.Setup ){
   108    123       login_needed();
   109    124     }
   110    125     if( P("setup") ){
   111    126       cgi_redirect("tktsetup");
   112    127     }
   113    128     isSubmit = P("submit")!=0;
   114    129     z = P("x");
................................................................................
   116    131       z = db_get(zDbField, (char*)zDfltValue);
   117    132     }
   118    133     style_header("Edit %s", zTitle);
   119    134     if( P("clear")!=0 ){
   120    135       login_verify_csrf_secret();
   121    136       db_unset(zDbField, 0);
   122    137       if( xRebuild ) xRebuild();
   123         -    z = zDfltValue;
          138  +    cgi_redirect("tktsetup");
   124    139     }else if( isSubmit ){
   125    140       char *zErr = 0;
   126    141       login_verify_csrf_secret();
   127    142       if( xText && (zErr = xText(z))!=0 ){
   128         -      @ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
          143  +      @ <p class="tktsetupError">ERROR: %h(zErr)</p>
   129    144       }else{
   130    145         db_set(zDbField, z, 0);
   131    146         if( xRebuild ) xRebuild();
   132    147         cgi_redirect("tktsetup");
   133    148       }
   134    149     }
   135         -  @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
          150  +  @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div>
   136    151     login_insert_csrf_secret();
   137    152     @ <p>%s(zDesc)</p>
   138    153     @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
   139         -  @ <blockquote>
   140         -  @ <input type="submit" name="submit" value="Apply Changes">
   141         -  @ <input type="submit" name="clear" value="Revert To Default">
   142         -  @ <input type="submit" name="setup" value="Cancel">
   143         -  @ </blockquote>
   144         -  @ </form>
   145         -  @ <hr>
          154  +  @ <blockquote><p>
          155  +  @ <input type="submit" name="submit" value="Apply Changes" />
          156  +  @ <input type="submit" name="clear" value="Revert To Default" />
          157  +  @ <input type="submit" name="setup" value="Cancel" />
          158  +  @ </p></blockquote>
          159  +  @ </div></form>
          160  +  @ <hr />
   146    161     @ <h2>Default %s(zTitle)</h2>
   147    162     @ <blockquote><pre>
   148    163     @ %h(zDfltValue)
   149    164     @ </pre></blockquote>
   150    165     style_footer();
   151    166   }
   152    167   
   153    168   /*
   154    169   ** WEBPAGE: tktsetup_tab
   155    170   */
   156    171   void tktsetup_tab_page(void){
   157    172     static const char zDesc[] =
   158         -  @ <p>Enter a valid CREATE TABLE statement for the "ticket" table.  The
          173  +  @ Enter a valid CREATE TABLE statement for the "ticket" table.  The
   159    174     @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
   160         -  @ with an unique index on "tkt_uuid" and "tkt_mtime".</p>
          175  +  @ with an unique index on "tkt_uuid" and "tkt_mtime".
   161    176     ;
   162    177     tktsetup_generic(
   163    178       "Ticket Table Schema",
   164    179       "ticket-table",
   165    180       zDefaultTicketTable,
   166    181       zDesc,
   167    182       ticket_schema_check,
................................................................................
   227    242   }
   228    243   
   229    244   /*
   230    245   ** WEBPAGE: tktsetup_com
   231    246   */
   232    247   void tktsetup_com_page(void){
   233    248     static const char zDesc[] =
   234         -  @ <p>Enter TH1 script that initializes variables prior to generating
   235         -  @ any of the ticket view, edit, or creation pages.</p>
          249  +  @ Enter TH1 script that initializes variables prior to generating
          250  +  @ any of the ticket view, edit, or creation pages.
   236    251     ;
   237    252     tktsetup_generic(
   238    253       "Ticket Common Script",
   239    254       "ticket-common",
   240    255       zDefaultTicketCommon,
   241    256       zDesc,
   242    257       0,
   243    258       0,
   244    259       30
   245    260     );
   246    261   }
          262  +
          263  +static const char zDefaultTicketChange[] =
          264  +@ return
          265  +;
          266  +
          267  +/*
          268  +** Return the ticket change code.
          269  +*/
          270  +const char *ticket_change_code(void){
          271  +  return db_get("ticket-change", (char*)zDefaultTicketChange);
          272  +}
          273  +
          274  +/*
          275  +** WEBPAGE: tktsetup_change
          276  +*/
          277  +void tktsetup_change_page(void){
          278  +  static const char zDesc[] =
          279  +  @ Enter TH1 script that runs after processing the ticket editing
          280  +  @ and creation pages.
          281  +  ;
          282  +  tktsetup_generic(
          283  +    "Ticket Change Script",
          284  +    "ticket-change",
          285  +    zDefaultTicketChange,
          286  +    zDesc,
          287  +    0,
          288  +    0,
          289  +    30
          290  +  );
          291  +}
   247    292   
   248    293   static const char zDefaultNew[] =
   249    294   @ <th1>
          295  +@   if {![info exists mutype]} {set mutype {[links only]}}
   250    296   @   if {[info exists submit]} {
   251    297   @      set status Open
          298  +@      if {$mutype eq "HTML"} {
          299  +@        set mimetype "text/html"
          300  +@      } elseif {$mutype eq "Wiki"} {
          301  +@        set mimetype "text/x-fossil-wiki"
          302  +@      } elseif {$mutype eq {[links only]}} {
          303  +@        set mimetype "text/x-fossil-plain"
          304  +@      } else {
          305  +@        set mimetype "text/plain"
          306  +@      }
   252    307   @      submit_ticket
          308  +@      set preview 1
   253    309   @   }
   254    310   @ </th1>
   255         -@ <h1 align="center">Enter A New Ticket</h1>
          311  +@ <h1 style="text-align: center;">Enter A New Ticket</h1>
   256    312   @ <table cellpadding="5">
   257    313   @ <tr>
   258         -@ <td colspan="2">
   259         -@ Enter a one-line summary of the ticket:<br>
   260         -@ <input type="text" name="title" size="60" value="$<title>">
          314  +@ <td colspan="3">
          315  +@ Enter a one-line summary of the ticket:<br />
          316  +@ <input type="text" name="title" size="60" value="$<title>" />
   261    317   @ </td>
   262    318   @ </tr>
   263    319   @ 
          320  +@ <tr>
          321  +@ <td align="right">Type:</td>
          322  +@ <td align="left"><th1>combobox type $type_choices 1</th1></td>
          323  +@ <td align="left">What type of ticket is this?</td>
          324  +@ </tr>
          325  +@ 
   264    326   @ <tr>
   265         -@ <td align="right">Type:
   266         -@ <th1>combobox type $type_choices 1</th1>
          327  +@ <td align="right">Version:</td>
          328  +@ <td align="left">
          329  +@ <input type="text" name="foundin" size="20" value="$<foundin>" />
   267    330   @ </td>
   268         -@ <td>What type of ticket is this?</td>
          331  +@ <td align="left">In what version or build number do you observe
          332  +@ the problem?</td>
   269    333   @ </tr>
   270    334   @ 
   271    335   @ <tr>
   272         -@ <td align="right">Version: 
   273         -@ <input type="text" name="foundin" size="20" value="$<foundin>">
   274         -@ </td>
   275         -@ <td>In what version or build number do you observe the problem?</td>
   276         -@ </tr>
   277         -@ 
   278         -@ <tr>
   279         -@ <td align="right">Severity:
   280         -@ <th1>combobox severity $severity_choices 1</th1>
   281         -@ </td>
   282         -@ <td>How debilitating is the problem?  How badly does the problem
          336  +@ <td align="right">Severity:</td>
          337  +@ <td align="left"><th1>combobox severity $severity_choices 1</th1></td>
          338  +@ <td align="left">How debilitating is the problem?  How badly does the problem
   283    339   @ affect the operation of the product?</td>
   284    340   @ </tr>
   285    341   @ 
   286    342   @ <tr>
   287         -@ <td align="right">EMail:
   288         -@ <input type="text" name="private_contact" value="$<private_contact>" size="30">
          343  +@ <td align="right">EMail:</td>
          344  +@ <td align="left">
          345  +@ <input type="text" name="private_contact" value="$<private_contact>"
          346  +@  size="30" />
   289    347   @ </td>
   290         -@ <td><u>Not publicly visible</u>. Used by developers to contact you with
   291         -@ questions.</td>
          348  +@ <td align="left"><u>Not publicly visible</u>
          349  +@ Used by developers to contact you with questions.</td>
   292    350   @ </tr>
   293    351   @ 
   294    352   @ <tr>
   295         -@ <td colspan="2">
          353  +@ <td colspan="3">
   296    354   @ Enter a detailed description of the problem.
   297    355   @ For code defects, be sure to provide details on exactly how
   298    356   @ the problem can be reproduced.  Provide as much detail as
   299         -@ possible.
   300         -@ <br>
          357  +@ possible.  Format:
          358  +@ <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1>
          359  +@ <br />
   301    360   @ <th1>set nline [linecount $comment 50 10]</th1>
   302         -@ <textarea name="comment" cols="80" rows="$nline"
   303         -@  wrap="virtual" class="wikiedit">$<comment></textarea><br>
   304         -@ <input type="submit" name="preview" value="Preview">
          361  +@ <textarea name="icomment" cols="80" rows="$nline"
          362  +@  wrap="virtual" class="wikiedit">$<icomment></textarea><br />
          363  +@ </tr>
          364  +@ 
          365  +@ <th1>enable_output [info exists preview]</th1>
          366  +@ <tr><td colspan="3">
          367  +@ Description Preview:<br /><hr />
          368  +@ <th1>
          369  +@ if {$mutype eq "Wiki"} {
          370  +@   wiki $icomment
          371  +@ } elseif {$mutype eq "Plain Text"} {
          372  +@   set r [randhex]
          373  +@   wiki "<verbatim-$r>[string trimright $icomment]\n</verbatim-$r>"
          374  +@ } elseif {$mutype eq {[links only]}} {
          375  +@   set r [randhex]
          376  +@   wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>"
          377  +@ } else {
          378  +@   wiki "<nowiki>$icomment\n</nowiki>"
          379  +@ }
          380  +@ </th1>
          381  +@ <hr /></td></tr>
          382  +@ <th1>enable_output 1</th1>
          383  +@ 
          384  +@ <tr>
          385  +@ <td><td align="left">
          386  +@ <input type="submit" name="preview" value="Preview" />
          387  +@ </td>
          388  +@ <td align="left">See how the description will appear after formatting.</td>
   305    389   @ </tr>
   306         -@
          390  +@ 
   307    391   @ <th1>enable_output [info exists preview]</th1>
   308         -@ <tr><td colspan="2">
   309         -@ Description Preview:<br><hr>
   310         -@ <th1>wiki $comment</th1>
   311         -@ <hr>
   312         -@ </td></tr>
          392  +@ <tr>
          393  +@ <td><td align="left">
          394  +@ <input type="submit" name="submit" value="Submit" />
          395  +@ </td>
          396  +@ <td align="left">After filling in the information above, press this 
          397  +@ button to create the new ticket</td>
          398  +@ </tr>
   313    399   @ <th1>enable_output 1</th1>
   314    400   @ 
   315    401   @ <tr>
   316         -@ <td align="right">
   317         -@ <input type="submit" name="submit" value="Submit">
   318         -@ </td>
   319         -@ <td>After filling in the information above, press this button to create
   320         -@ the new ticket</td>
   321         -@ </tr>
   322         -@ <tr>
   323         -@ <td align="right">
   324         -@ <input type="submit" name="cancel" value="Cancel">
          402  +@ <td><td align="left">
          403  +@ <input type="submit" name="cancel" value="Cancel" />
   325    404   @ </td>
   326    405   @ <td>Abandon and forget this ticket</td>
   327    406   @ </tr>
   328    407   @ </table>
   329    408   ;
   330    409   
   331    410   /*
................................................................................
   336    415   }
   337    416   
   338    417   /*
   339    418   ** WEBPAGE: tktsetup_newpage
   340    419   */
   341    420   void tktsetup_newpage_page(void){
   342    421     static const char zDesc[] =
   343         -  @ <p>Enter HTML with embedded TH1 script that will render the "new ticket"
   344         -  @ page</p>
          422  +  @ Enter HTML with embedded TH1 script that will render the "new ticket"
          423  +  @ page
   345    424     ;
   346    425     tktsetup_generic(
   347    426       "HTML For New Tickets",
   348    427       "ticket-newpage",
   349    428       zDefaultNew,
   350    429       zDesc,
   351    430       0,
................................................................................
   352    431       0,
   353    432       40
   354    433     );
   355    434   }
   356    435   
   357    436   static const char zDefaultView[] =
   358    437   @ <table cellpadding="5">
   359         -@ <tr><td align="right">Ticket&nbsp;UUID:</td><td bgcolor="#d0d0d0" colspan="3">
   360         -@ $<tkt_uuid>
          438  +@ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
          439  +@ <th1>
          440  +@ if {[hascap s]} {
          441  +@   html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
          442  +@   html "($tkt_id)</td></tr>\n"
          443  +@ } else {
          444  +@   html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"
          445  +@ }
          446  +@ </th1>
          447  +@ <tr><td class="tktDspLabel">Title:</td>
          448  +@ <td class="tktDspValue" colspan="3">
          449  +@ $<title>
   361    450   @ </td></tr>
   362         -@ <tr><td align="right">Title:</td>
   363         -@ <td bgcolor="#d0d0d0" colspan="3" valign="top">
   364         -@ <th1>wiki $title</th1>
   365         -@ </td></tr>
   366         -@ <tr><td align="right">Status:</td><td bgcolor="#d0d0d0">
          451  +@ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
   367    452   @ $<status>
   368    453   @ </td>
   369         -@ <td align="right">Type:</td><td bgcolor="#d0d0d0">
          454  +@ <td class="tktDspLabel">Type:</td><td class="tktDspValue">
   370    455   @ $<type>
   371    456   @ </td></tr>
   372         -@ <tr><td align="right">Severity:</td><td bgcolor="#d0d0d0">
          457  +@ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue">
   373    458   @ $<severity>
   374    459   @ </td>
   375         -@ <td align="right">Priority:</td><td bgcolor="#d0d0d0">
          460  +@ <td class="tktDspLabel">Priority:</td><td class="tktDspValue">
   376    461   @ $<priority>
   377    462   @ </td></tr>
   378         -@ <tr><td align="right">Subsystem:</td><td bgcolor="#d0d0d0">
          463  +@ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue">
   379    464   @ $<subsystem>
   380    465   @ </td>
   381         -@ <td align="right">Resolution:</td><td bgcolor="#d0d0d0">
          466  +@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
   382    467   @ $<resolution>
   383    468   @ </td></tr>
   384         -@ <tr><td align="right">Last&nbsp;Modified:</td><td bgcolor="#d0d0d0">
          469  +@ <tr><td class="tktDspLabel">Last&nbsp;Modified:</td><td class="tktDspValue">
   385    470   @ $<tkt_datetime>
   386    471   @ </td>
   387    472   @ <th1>enable_output [hascap e]</th1>
   388         -@   <td align="right">Contact:</td><td bgcolor="#d0d0d0">
          473  +@   <td class="tktDspLabel">Contact:</td><td class="tktDspValue">
   389    474   @   $<private_contact>
   390    475   @   </td>
   391    476   @ <th1>enable_output 1</th1>
   392    477   @ </tr>
   393         -@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td>
   394         -@ <td colspan="3" valign="top" bgcolor="#d0d0d0">
          478  +@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
          479  +@ <td colspan="3" valign="top" class="tktDspValue">
   395    480   @ $<foundin>
   396    481   @ </td></tr>
   397         -@ <tr><td>Description &amp; Comments:</td></tr>
   398         -@ <tr><td colspan="4" bgcolor="#d0d0d0">
   399         -@ <span  bgcolor="#d0d0d0"><th1>wiki $comment</th1></span>
   400         -@ </td></tr>
          482  +@ 
          483  +@ <th1>
          484  +@ if {[info exists comment] && [string length $comment]>10} {
          485  +@   html {
          486  +@     <tr><td class="tktDspLabel">Description:</td></tr>
          487  +@     <tr><td colspan="5" class="tktDspValue">
          488  +@   }
          489  +@   if {[info exists plaintext]} {
          490  +@     set r [randhex]
          491  +@     wiki "<verbatim-$r links>\n$comment\n</verbatim-$r>"
          492  +@   } else {
          493  +@     wiki $comment
          494  +@   }
          495  +@ }
          496  +@ set seenRow 0
          497  +@ set alwaysPlaintext [info exists plaintext]
          498  +@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
          499  +@               mimetype as xmimetype, icomment AS xcomment,
          500  +@               username AS xusername
          501  +@          FROM ticketchng
          502  +@         WHERE tkt_id=$tkt_id AND length(icomment)>0} {
          503  +@   if {$seenRow} {
          504  +@     html "<hr>\n"
          505  +@   } else {
          506  +@     html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n"
          507  +@     html "<tr><td colspan='5' class='tktDspValue'>\n"
          508  +@     set seenRow 1
          509  +@   }
          510  +@   html "[htmlize $xlogin]"
          511  +@   if {$xlogin ne $xusername && [string length $xusername]>0} {
          512  +@     html " (claiming to be [htmlize $xusername])"
          513  +@   }
          514  +@   html " added on $xdate:\n"
          515  +@   if {$alwaysPlaintext || $xmimetype eq "text/plain"} {
          516  +@     set r [randhex]
          517  +@     if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"}
          518  +@     wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n"
          519  +@   } elseif {$xmimetype eq "text/x-fossil-wiki"} {
          520  +@     wiki "<p>\n[string trimright $xcomment]\n</p>\n"
          521  +@   } elseif {$xmimetype eq "text/html"} {
          522  +@     wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
          523  +@   } else {
          524  +@     set r [randhex]
          525  +@     wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
          526  +@   }
          527  +@ }
          528  +@ if {$seenRow} {html "</td></tr>\n"}
          529  +@ </th1>
   401    530   @ </table>
   402    531   ;
   403    532   
   404    533   
   405    534   /*
   406    535   ** Return the code used to generate the view ticket page
   407    536   */
................................................................................
   410    539   }
   411    540   
   412    541   /*
   413    542   ** WEBPAGE: tktsetup_viewpage
   414    543   */
   415    544   void tktsetup_viewpage_page(void){
   416    545     static const char zDesc[] =
   417         -  @ <p>Enter HTML with embedded TH1 script that will render the "view ticket"
   418         -  @ page</p>
          546  +  @ Enter HTML with embedded TH1 script that will render the "view ticket" page
   419    547     ;
   420    548     tktsetup_generic(
   421    549       "HTML For Viewing Tickets",
   422    550       "ticket-viewpage",
   423    551       zDefaultView,
   424    552       zDesc,
   425    553       0,
................................................................................
   426    554       0,
   427    555       40
   428    556     );
   429    557   }
   430    558   
   431    559   static const char zDefaultEdit[] =
   432    560   @ <th1>
          561  +@   if {![info exists mutype]} {set mutype {[links only]}}
          562  +@   if {![info exists icomment]} {set icomment {}}
   433    563   @   if {![info exists username]} {set username $login}
   434    564   @   if {[info exists submit]} {
   435         -@     if {[info exists cmappnd]} {
   436         -@       if {[string length $cmappnd]>0} {
   437         -@         set ctxt "\n\n<hr><i>[htmlize $login]"
   438         -@         if {$username ne $login} {
   439         -@           set ctxt "$ctxt claiming to be [htmlize $username]"
   440         -@         }
   441         -@         set ctxt "$ctxt added on [date]:</i><br>\n$cmappnd"
   442         -@         append_field comment $ctxt
   443         -@       }
          565  +@     if {$mutype eq "Wiki"} {
          566  +@       set mimetype text/x-fossil-wiki
          567  +@     } elseif {$mutype eq "HTML"} {
          568  +@       set mimetype text/html
          569  +@     } elseif {$mutype eq {[links only]}} {
          570  +@       set mimetype text/x-fossil-plain
          571  +@     } else {
          572  +@       set mimetype text/plain
   444    573   @     }
   445    574   @     submit_ticket
          575  +@     set preview 1
   446    576   @   }
   447    577   @ </th1>
   448    578   @ <table cellpadding="5">
   449         -@ <tr><td align="right">Title:</td><td>
   450         -@ <input type="text" name="title" value="$<title>" size="60">
          579  +@ <tr><td class="tktDspLabel">Title:</td><td>
          580  +@ <input type="text" name="title" value="$<title>" size="60" />
   451    581   @ </td></tr>
   452         -@ <tr><td align="right">Status:</td><td>
          582  +@ 
          583  +@ <tr><td class="tktDspLabel">Status:</td><td>
   453    584   @ <th1>combobox status $status_choices 1</th1>
   454    585   @ </td></tr>
   455         -@ <tr><td align="right">Type:</td><td>
          586  +@ 
          587  +@ <tr><td class="tktDspLabel">Type:</td><td>
   456    588   @ <th1>combobox type $type_choices 1</th1>
   457    589   @ </td></tr>
   458         -@ <tr><td align="right">Severity:</td><td>
          590  +@ 
          591  +@ <tr><td class="tktDspLabel">Severity:</td><td>
   459    592   @ <th1>combobox severity $severity_choices 1</th1>
   460    593   @ </td></tr>
   461         -@ <tr><td align="right">Priority:</td><td>
          594  +@ 
          595  +@ <tr><td class="tktDspLabel">Priority:</td><td>
   462    596   @ <th1>combobox priority $priority_choices 1</th1>
   463    597   @ </td></tr>
   464         -@ <tr><td align="right">Resolution:</td><td>
          598  +@ 
          599  +@ <tr><td class="tktDspLabel">Resolution:</td><td>
   465    600   @ <th1>combobox resolution $resolution_choices 1</th1>
   466    601   @ </td></tr>
   467         -@ <tr><td align="right">Subsystem:</td><td>
          602  +@ 
          603  +@ <tr><td class="tktDspLabel">Subsystem:</td><td>
   468    604   @ <th1>combobox subsystem $subsystem_choices 1</th1>
   469    605   @ </td></tr>
          606  +@ 
   470    607   @ <th1>enable_output [hascap e]</th1>
   471         -@   <tr><td align="right">Contact:</td><td>
          608  +@   <tr><td class="tktDspLabel">Contact:</td><td>
   472    609   @   <input type="text" name="private_contact" size="40"
   473         -@    value="$<private_contact>">
          610  +@    value="$<private_contact>" />
   474    611   @   </td></tr>
   475    612   @ <th1>enable_output 1</th1>
   476         -@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
   477         -@ <input type="text" name="foundin" size="50" value="$<foundin>">
          613  +@ 
          614  +@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
          615  +@ <input type="text" name="foundin" size="50" value="$<foundin>" />
   478    616   @ </td></tr>
          617  +@ 
   479    618   @ <tr><td colspan="2">
   480         -@ <th1>
   481         -@   if {![info exists eall]} {set eall 0}
   482         -@   if {[info exists aonlybtn]} {set eall 0}
   483         -@   if {[info exists eallbtn]} {set eall 1}
   484         -@   if {![hascap w]} {set eall 0}
   485         -@   if {![info exists cmappnd]} {set cmappnd {}}
   486         -@   set nline [linecount $comment 15 10]
   487         -@   enable_output $eall
   488         -@ </th1>
   489         -@   Description And Comments:<br>
   490         -@   <textarea name="comment" cols="80" rows="$nline"
   491         -@    wrap="virtual" class="wikiedit">$<comment></textarea><br>
   492         -@   <input type="hidden" name="eall" value="1">
   493         -@   <input type="submit" name="aonlybtn" value="Append Remark">
   494         -@   <input type="submit" name="preview1btn" value="Preview">
   495         -@ <th1>enable_output [expr {!$eall}]</th1>
   496         -@   Append Remark from 
   497         -@   <input type="text" name="username" value="$<username>" size="30">:<br>
   498         -@   <textarea name="cmappnd" cols="80" rows="15"
   499         -@    wrap="virtual" class="wikiedit">$<cmappnd></textarea><br>
   500         -@ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
   501         -@   <input type="submit" name="eallbtn" value="Edit All">
   502         -@ <th1>enable_output [expr {!$eall}]</th1>
   503         -@   <input type="submit" name="preview2btn" value="Preview">
   504         -@ <th1>enable_output 1</th1>
          619  +@   Append Remark with format
          620  +@   <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1>
          621  +@   from
          622  +@   <input type="text" name="username" value="$<username>" size="30" />:<br />
          623  +@   <textarea name="icomment" cols="80" rows="15"
          624  +@    wrap="virtual" class="wikiedit">$<icomment></textarea>
   505    625   @ </td></tr>
   506         -@
   507         -@ <th1>enable_output [info exists preview1btn]</th1>
          626  +@ 
          627  +@ <th1>enable_output [info exists preview]</th1>
   508    628   @ <tr><td colspan="2">
   509    629   @ Description Preview:<br><hr>
   510         -@ <th1>wiki $comment</th1>
   511         -@ <hr>
   512         -@ </td></tr>
   513         -@ <th1>enable_output [info exists preview2btn]</th1>
   514         -@ <tr><td colspan="2">
   515         -@ Description Preview:<br><hr>
   516         -@ <th1>wiki $cmappnd</th1>
          630  +@ <th1>
          631  +@ if {$mutype eq "Wiki"} {
          632  +@   wiki $icomment
          633  +@ } elseif {$mutype eq "Plain Text"} {
          634  +@   set r [randhex]
          635  +@   wiki "<verbatim-$r>\n[string trimright $icomment]\n</verbatim-$r>"
          636  +@ } elseif {$mutype eq {[links only]}} {
          637  +@   set r [randhex]
          638  +@   wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>"
          639  +@ } else {
          640  +@   wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>"
          641  +@ }
          642  +@ </th1>
   517    643   @ <hr>
   518    644   @ </td></tr>
   519    645   @ <th1>enable_output 1</th1>
   520         -@
   521         -@ <tr><td align="right"></td><td>
   522         -@ <input type="submit" name="submit" value="Submit Changes">
   523         -@ <input type="submit" name="cancel" value="Cancel">
   524         -@ </td></tr>
          646  +@ 
          647  +@ <tr>
          648  +@ <td align="right">
          649  +@ <input type="submit" name="preview" value="Preview" />
          650  +@ </td>
          651  +@ <td align="left">See how the description will appear after formatting.</td>
          652  +@ </tr>
          653  +@ 
          654  +@ <th1>enable_output [info exists preview]</th1>
          655  +@ <tr>
          656  +@ <td align="right">
          657  +@ <input type="submit" name="submit" value="Submit" />
          658  +@ </td>
          659  +@ <td align="left">Apply the changes shown above</td>
          660  +@ </tr>
          661  +@ <th1>enable_output 1</th1>
          662  +@ 
          663  +@ <tr>
          664  +@ <td align="right">
          665  +@ <input type="submit" name="cancel" value="Cancel" />
          666  +@ </td>
          667  +@ <td>Abandon this edit</td>
          668  +@ </tr>
          669  +@ 
   525    670   @ </table>
   526    671   ;
   527    672   
   528    673   /*
   529    674   ** Return the code used to generate the edit ticket page
   530    675   */
   531    676   const char *ticket_editpage_code(void){
................................................................................
   533    678   }
   534    679   
   535    680   /*
   536    681   ** WEBPAGE: tktsetup_editpage
   537    682   */
   538    683   void tktsetup_editpage_page(void){
   539    684     static const char zDesc[] =
   540         -  @ <p>Enter HTML with embedded TH1 script that will render the "edit ticket"
   541         -  @ page</p>
          685  +  @ Enter HTML with embedded TH1 script that will render the "edit ticket" page
   542    686     ;
   543    687     tktsetup_generic(
   544    688       "HTML For Editing Tickets",
   545    689       "ticket-editpage",
   546    690       zDefaultEdit,
   547    691       zDesc,
   548    692       0,
................................................................................
   564    708   @ 
   565    709   @ <p>Choose a report format from the following list:</p>
   566    710   @ <ol>
   567    711   @ <th1>html $report_items</th1>
   568    712   @ </ol>
   569    713   @ 
   570    714   @ <th1>
   571         -@ if {[hascap t]} {
   572         -@   html "<p>Create a new ticket display format:</p>"
   573         -@   html "<ul><li><a href='rptnew'>New report format</a></li></ul>"
          715  +@ if {[hascap t q]} {
          716  +@   html "<p>Other options:</p>\n<ul>\n"
          717  +@   if {[hascap t]} {
          718  +@     html "<li><a href='rptnew'>New report format</a></li>\n"
          719  +@   }
          720  +@   if {[hascap q]} {
          721  +@     html "<li><a href='modreq'>Tend to pending moderation requests</a></li>\n"
          722  +@   }
   574    723   @ }
   575    724   @ </th1>
   576    725   ;
   577    726   
   578    727   /*
   579    728   ** Return the code used to generate the report list
   580    729   */
................................................................................
   583    732   }
   584    733   
   585    734   /*
   586    735   ** WEBPAGE: tktsetup_reportlist
   587    736   */
   588    737   void tktsetup_reportlist(void){
   589    738     static const char zDesc[] =
   590         -  @ <p>Enter HTML with embedded TH1 script that will render the "report list"
   591         -  @ page</p>
          739  +  @ Enter HTML with embedded TH1 script that will render the "report list" page
   592    740     ;
   593    741     tktsetup_generic(
   594    742       "HTML For Report List",
   595    743       "ticket-reportlist",
   596    744       zDefaultReportList,
   597    745       zDesc,
   598    746       0,
................................................................................
   631    779   }
   632    780   
   633    781   /*
   634    782   ** WEBPAGE: tktsetup_rpttplt
   635    783   */
   636    784   void tktsetup_rpttplt_page(void){
   637    785     static const char zDesc[] =
   638         -  @ <p>Enter the default ticket report format template.  This is the
   639         -  @ the template report format that initially appears when creating a
   640         -  @ new ticket summary report.</p>
          786  +  @ Enter the default ticket report format template.  This is the
          787  +  @ template report format that initially appears when creating a
          788  +  @ new ticket summary report.
   641    789     ;
   642    790     tktsetup_generic(
   643    791       "Default Report Template",
   644    792       "ticket-report-template",
   645    793       zDefaultReport,
   646    794       zDesc,
   647    795       0,
................................................................................
   672    820   }
   673    821   
   674    822   /*
   675    823   ** WEBPAGE: tktsetup_keytplt
   676    824   */
   677    825   void tktsetup_keytplt_page(void){
   678    826     static const char zDesc[] =
   679         -  @ <p>Enter the default ticket report color-key template.  This is the
          827  +  @ Enter the default ticket report color-key template.  This is the
   680    828     @ the color-key that initially appears when creating a
   681         -  @ new ticket summary report.</p>
          829  +  @ new ticket summary report.
   682    830     ;
   683    831     tktsetup_generic(
   684    832       "Default Report Color-Key Template",
   685    833       "ticket-key-template",
   686    834       zDefaultKey,
   687    835       zDesc,
   688    836       0,
................................................................................
   692    840   }
   693    841   
   694    842   /*
   695    843   ** WEBPAGE: tktsetup_timeline
   696    844   */
   697    845   void tktsetup_timeline_page(void){
   698    846     login_check_credentials();
   699         -  if( !g.okSetup ){
          847  +  if( !g.perm.Setup ){
   700    848       login_needed();
   701    849     }
   702    850   
   703    851     if( P("setup") ){
   704    852       cgi_redirect("tktsetup");
   705    853     }
   706    854     style_header("Ticket Display On Timelines");
   707    855     db_begin_transaction();
   708         -  @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
          856  +  @ <form action="%s(g.zTop)/tktsetup_timeline" method="post"><div>
   709    857     login_insert_csrf_secret();
   710    858   
   711         -  @ <hr>
          859  +  @ <hr />
   712    860     entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
   713    861     @ <p>An SQL expression in a query against the TICKET table that will
   714    862     @ return the title of the ticket for display purposes.</p>
   715    863   
   716         -  @ <hr>
          864  +  @ <hr />
   717    865     entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
   718    866     @ <p>The name of the column in the TICKET table that contains the ticket
   719    867     @ status in human-readable form.  Case sensitive.</p>
   720    868   
   721         -  @ <hr>
          869  +  @ <hr />
   722    870     entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
   723    871                     "status='Closed'");
   724    872     @ <p>An SQL expression that evaluates to true in a TICKET table query if
   725    873     @ the ticket is closed.</p>
   726    874   
   727         -  @ <hr>
          875  +  @ <hr />
   728    876     @ <p>
   729         -  @ <input type="submit"  name="submit" value="Apply Changes">
   730         -  @ <input type="submit" name="setup" value="Cancel">
          877  +  @ <input type="submit"  name="submit" value="Apply Changes" />
          878  +  @ <input type="submit" name="setup" value="Cancel" />
   731    879     @ </p>
   732         -  @ </form>
          880  +  @ </div></form>
   733    881     db_end_transaction(0);
   734    882     style_footer();
   735    883     
   736    884   }

Changes to src/translate.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** begin with the "@" character are translated into cgi_printf() statements
    19         -** and the translated code is written on standard output.
           18  +** SYNOPSIS: 
           19  +**
           20  +** Input lines that begin with the "@" character are translated into
           21  +** either cgi_printf() statements or string literals and the
           22  +** translated code is written on standard output.
    20     23   **
    21     24   ** The problem this program is attempt to solve is as follows:  When
    22     25   ** writing CGI programs in C, we typically want to output a lot of HTML
    23     26   ** text to standard output.  In pure C code, this involves doing a
    24     27   ** printf() with a big string containing all that text.  But we have
    25     28   ** to insert special codes (ex: \n and \") for many common characters,
    26     29   ** which interferes with the readability of the HTML.
    27     30   **
    28     31   ** This tool allows us to put raw HTML, without the special codes, in
    29     32   ** the middle of a C program.  This program then translates the text
    30     33   ** into standard C by inserting all necessary backslashes and other
    31     34   ** punctuation.
           35  +** 
    32     36   */
    33     37   #include <stdio.h>
    34     38   #include <ctype.h>
    35     39   #include <stdlib.h>
    36     40   #include <string.h>
    37     41   
    38     42   /*
................................................................................
    64     68     }
    65     69   }
    66     70   
    67     71   /*
    68     72   ** Translate the input stream into the output stream
    69     73   */
    70     74   static void trans(FILE *in, FILE *out){
    71         -  int i, j, k;        /* Loop counters */
    72         -  char c1, c2;        /* Characters used to start a comment */
    73         -  int lastWasEq = 0;  /* True if last non-whitespace character was "=" */
    74         -  char zLine[2000];   /* A single line of input */
    75         -  char zOut[4000];    /* The input line translated into appropriate output */
           75  +  int i, j, k;          /* Loop counters */
           76  +  char c1, c2;          /* Characters used to start a comment */
           77  +  int lastWasEq = 0;    /* True if last non-whitespace character was "=" */
           78  +  int lastWasComma = 0; /* True if last non-whitespace character was "," */
           79  +  char zLine[2000];     /* A single line of input */
           80  +  char zOut[4000];      /* The input line translated into appropriate output */
    76     81   
    77     82     c1 = c2 = '-';
    78     83     while( fgets(zLine, sizeof(zLine), in) ){
    79     84       for(i=0; zLine[i] && isspace(zLine[i]); i++){}
    80     85       if( zLine[i]!='@' ){
    81     86         if( inPrint || inStr ) end_block(out);
    82     87         fprintf(out,"%s",zLine);
................................................................................
    83     88                          /* 0123456789 12345 */
    84     89         if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
    85     90           c1 = zLine[14];
    86     91           c2 = zLine[15];
    87     92         }
    88     93         i += strlen(&zLine[i]);
    89     94         while( i>0 && isspace(zLine[i-1]) ){ i--; }
    90         -      lastWasEq = i>0 && zLine[i-1]=='=';
    91         -    }else if( lastWasEq ){
           95  +      lastWasEq    = i>0 && zLine[i-1]=='=';
           96  +      lastWasComma = i>0 && zLine[i-1]==',';
           97  +    }else if( lastWasEq || lastWasComma){
    92     98         /* If the last non-whitespace character before the first @ was
    93         -      ** an "=" then generate a string literal.  But skip comments
           99  +      ** an "="(var init/set) or a ","(const definition in list) then
          100  +      ** generate a string literal.  But skip comments
    94    101         ** consisting of all text between c1 and c2 (default "--")
    95    102         ** and end of line.
    96    103         */
    97    104         int indent, omitline;
    98    105         i++;
    99    106         if( isspace(zLine[i]) ){ i++; }
   100    107         indent = i - 2;
................................................................................
   113    120           fprintf(out,"\n");
   114    121         }else{
   115    122           fprintf(out,"%*s\"%s\\n\"\n",indent, "", zOut);
   116    123         }
   117    124       }else{
   118    125         /* Otherwise (if the last non-whitespace was not '=') then generate
   119    126         ** a cgi_printf() statement whose format is the text following the '@'.
   120         -      ** Substrings of the form "%C(...)" where C is any character will
   121         -      ** puts "%C" in the format and add the "..." as an argument to the
   122         -      ** cgi_printf call.
          127  +      ** Substrings of the form "%C(...)" (where C is any sequence of 
          128  +      ** characters other than \000 and '(') will put "%C" in the
          129  +      ** format and add the "(...)" as an argument to the cgi_printf call.
   123    130         */
   124    131         int indent;
          132  +      int nC;
          133  +      char c;
   125    134         i++;
   126    135         if( isspace(zLine[i]) ){ i++; }
   127    136         indent = i;
   128    137         for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
   129    138           if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; }
   130    139           zOut[j++] = zLine[i];
   131    140           if( zLine[i]!='%' || zLine[i+1]=='%' || zLine[i+1]==0 ) continue;
   132         -        if( zLine[i+2]!='(' ) continue;
   133         -        i++;
   134         -        zOut[j++] = zLine[i];
          141  +        for(nC=1; zLine[i+nC] && zLine[i+nC]!='('; nC++){}
          142  +        if( zLine[i+nC]!='(' || !isalpha(zLine[i+nC-1]) ) continue;
          143  +        while( --nC ) zOut[j++] = zLine[++i];
   135    144           zArg[nArg++] = ',';
   136         -        i += 2;
   137         -        k = 1;
   138         -        while( zLine[i] ){
   139         -          if( zLine[i]==')' ){
          145  +        k = 0; i++;
          146  +        while( (c = zLine[i])!=0 ){
          147  +          zArg[nArg++] = c;
          148  +          if( c==')' ){
   140    149               k--;
   141    150               if( k==0 ) break;
   142         -          }else if( zLine[i]=='(' ){
          151  +          }else if( c=='(' ){
   143    152               k++;
   144    153             }
   145         -          zArg[nArg++] = zLine[i++];
          154  +          i++;
   146    155           }
   147    156         }
   148    157         zOut[j] = 0;
   149    158         if( !inPrint ){
   150    159           fprintf(out,"%*scgi_printf(\"%s\\n\"",indent-2,"", zOut);
   151    160           inPrint = 1;
   152    161         }else{
................................................................................
   154    163         }
   155    164       }      
   156    165     }
   157    166   }
   158    167   
   159    168   int main(int argc, char **argv){
   160    169     if( argc==2 ){
          170  +    char *arg;
   161    171       FILE *in = fopen(argv[1], "r");
   162    172       if( in==0 ){
   163    173         fprintf(stderr,"can not open %s\n", argv[1]);
   164    174         exit(1);
   165    175       }
          176  +    printf("#line 1 \"");
          177  +    for(arg=argv[1]; *arg; arg++){
          178  +      if( *arg!='\\' ){
          179  +        printf("%c", *arg);
          180  +      }else{
          181  +        printf("\\\\");
          182  +      }
          183  +    }
          184  +    printf("\"\n");
   166    185       trans(in, stdout);
   167    186       fclose(in);
   168    187     }else{
   169    188       trans(stdin, stdout);
   170    189     }
   171    190     return 0;
   172    191   }

Changes to src/undo.c.

    28     28   ** true the redo a change.  If there is nothing to undo (or redo) then
    29     29   ** this routine is a noop.
    30     30   */
    31     31   static void undo_one(const char *zPathname, int redoFlag){
    32     32     Stmt q;
    33     33     char *zFullname;
    34     34     db_prepare(&q,
    35         -    "SELECT content, existsflag FROM undo WHERE pathname=%Q AND redoflag=%d",
           35  +    "SELECT content, existsflag, isExe, isLink FROM undo"
           36  +    " WHERE pathname=%Q AND redoflag=%d",
    36     37        zPathname, redoFlag
    37     38     );
    38     39     if( db_step(&q)==SQLITE_ROW ){
    39     40       int old_exists;
    40     41       int new_exists;
           42  +    int old_exe;
           43  +    int new_exe;
           44  +    int new_link;
           45  +    int old_link;
    41     46       Blob current;
    42     47       Blob new;
    43     48       zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
    44         -    new_exists = file_size(zFullname)>=0;
           49  +    old_link = db_column_int(&q, 3);
           50  +    new_link = file_wd_islink(zFullname);
           51  +    new_exists = file_wd_size(zFullname)>=0;
    45     52       if( new_exists ){
    46         -      blob_read_from_file(&current, zFullname);
           53  +      if( new_link ){
           54  +        blob_read_link(&current, zFullname);
           55  +      }else{
           56  +        blob_read_from_file(&current, zFullname);        
           57  +      }
           58  +      new_exe = file_wd_isexe(zFullname);
    47     59       }else{
    48     60         blob_zero(&current);
           61  +      new_exe = 0;
    49     62       }
    50     63       blob_zero(&new);
    51     64       old_exists = db_column_int(&q, 1);
           65  +    old_exe = db_column_int(&q, 2);
    52     66       if( old_exists ){
    53     67         db_ephemeral_blob(&q, 0, &new);
    54     68       }
    55     69       if( old_exists ){
    56     70         if( new_exists ){
    57         -        printf("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
           71  +        fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
           72  +      }else{
           73  +        fossil_print("NEW %s\n", zPathname);
           74  +      }
           75  +      if( new_exists && (new_link || old_link) ){
           76  +        file_delete(zFullname);
           77  +      }
           78  +      if( old_link ){
           79  +        symlink_create(blob_str(&new), zFullname);
    58     80         }else{
    59         -        printf("NEW %s\n", zPathname);
           81  +        blob_write_to_file(&new, zFullname);
    60     82         }
    61         -      blob_write_to_file(&new, zFullname);
           83  +      file_wd_setexe(zFullname, old_exe);
    62     84       }else{
    63         -      printf("DELETE %s\n", zPathname);
    64         -      unlink(zFullname);
           85  +      fossil_print("DELETE %s\n", zPathname);
           86  +      file_delete(zFullname);
    65     87       }
    66     88       blob_reset(&new);
    67     89       free(zFullname);
    68     90       db_finalize(&q);
    69     91       db_prepare(&q, 
    70         -       "UPDATE undo SET content=:c, existsflag=%d, redoflag=NOT redoflag"
           92  +       "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
           93  +             " redoflag=NOT redoflag"
    71     94          " WHERE pathname=%Q",
    72         -       new_exists, zPathname
           95  +       new_exists, new_exe, new_link, zPathname
    73     96       );
    74     97       if( new_exists ){
    75     98         db_bind_blob(&q, ":c", &current);
    76     99       }
    77    100       db_step(&q);
    78    101       blob_reset(&current);
    79    102     }
................................................................................
   102    125   
   103    126   /*
   104    127   ** Undo or redo all undoable or redoable changes.
   105    128   */
   106    129   static void undo_all(int redoFlag){
   107    130     int ucid;
   108    131     int ncid;
          132  +  const char *zDb = db_name("localdb");
   109    133     undo_all_filesystem(redoFlag);
   110    134     db_multi_exec(
   111    135       "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;"
   112    136       "DELETE FROM vfile;"
   113    137       "INSERT INTO vfile SELECT * FROM undo_vfile;"
   114    138       "DELETE FROM undo_vfile;"
   115    139       "INSERT INTO undo_vfile SELECT * FROM undo_vfile_2;"
................................................................................
   117    141       "CREATE TEMP TABLE undo_vmerge_2 AS SELECT * FROM vmerge;"
   118    142       "DELETE FROM vmerge;"
   119    143       "INSERT INTO vmerge SELECT * FROM undo_vmerge;"
   120    144       "DELETE FROM undo_vmerge;"
   121    145       "INSERT INTO undo_vmerge SELECT * FROM undo_vmerge_2;"
   122    146       "DROP TABLE undo_vmerge_2;"
   123    147     );
          148  +  if(db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='undo_stash'", zDb) ){
          149  +    if( redoFlag ){
          150  +      db_multi_exec(
          151  +        "DELETE FROM stash WHERE stashid IN (SELECT stashid FROM undo_stash);"
          152  +        "DELETE FROM stashfile"
          153  +        " WHERE stashid NOT IN (SELECT stashid FROM stash);"
          154  +      );
          155  +    }else{
          156  +      db_multi_exec(
          157  +        "INSERT OR IGNORE INTO stash SELECT * FROM undo_stash;"
          158  +        "INSERT OR IGNORE INTO stashfile SELECT * FROM undo_stashfile;"
          159  +      );
          160  +    }
          161  +  }
   124    162     ncid = db_lget_int("undo_checkout", 0);
   125    163     ucid = db_lget_int("checkout", 0);
   126    164     db_lset_int("undo_checkout", ucid);
   127    165     db_lset_int("checkout", ncid);
   128    166   }
   129    167   
   130    168   /*
   131         -** Reset the the undo memory.
          169  +** Reset the undo memory.
   132    170   */
   133    171   void undo_reset(void){
   134    172     static const char zSql[] =
   135    173       @ DROP TABLE IF EXISTS undo;
   136    174       @ DROP TABLE IF EXISTS undo_vfile;
   137    175       @ DROP TABLE IF EXISTS undo_vmerge;
   138         -    @ DROP TABLE IF EXISTS undo_pending;
          176  +    @ DROP TABLE IF EXISTS undo_stash;
          177  +    @ DROP TABLE IF EXISTS undo_stashfile;
   139    178       ;
   140    179     db_multi_exec(zSql);
   141    180     db_lset_int("undo_available", 0);
   142    181     db_lset_int("undo_checkout", 0);
   143    182   }
   144    183   
          184  +/*
          185  +** The following variable stores the original command-line of the
          186  +** command that is a candidate to be undone.
          187  +*/
          188  +static char *undoCmd = 0;
          189  +
   145    190   /*
   146    191   ** This flag is true if we are in the process of collecting file changes
   147    192   ** for undo.  When this flag is false, undo_save() is a no-op.
          193  +**
          194  +** The undoDisable flag, if set, prevents undo from being activated.
   148    195   */
   149    196   static int undoActive = 0;
          197  +static int undoDisable = 0;
          198  +
          199  +
          200  +/*
          201  +** Capture the current command-line and store it as part of the undo
          202  +** state.  This routine is called before options are extracted from the
          203  +** command-line so that we can record the complete command-line.
          204  +*/
          205  +void undo_capture_command_line(void){
          206  +  Blob cmdline;
          207  +  int i;
          208  +  if( undoCmd!=0 || undoDisable ) return;
          209  +  blob_zero(&cmdline);
          210  +  for(i=1; i<g.argc; i++){
          211  +    if( i>1 ) blob_append(&cmdline, " ", 1);
          212  +    blob_append(&cmdline, g.argv[i], -1);
          213  +  }
          214  +  undoCmd = blob_str(&cmdline);
          215  +}
   150    216   
   151    217   /*
   152    218   ** Begin capturing a snapshot that can be undone.
   153    219   */
   154    220   void undo_begin(void){
   155    221     int cid;
          222  +  const char *zDb = db_name("localdb");
   156    223     static const char zSql[] = 
   157         -    @ CREATE TABLE undo(
          224  +    @ CREATE TABLE %s.undo(
   158    225       @   pathname TEXT UNIQUE,             -- Name of the file
   159    226       @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
   160    227       @   existsflag BOOLEAN,               -- True if the file exists
          228  +    @   isExe BOOLEAN,                    -- True if the file is executable
          229  +    @   isLink BOOLEAN,                   -- True if the file is symlink
   161    230       @   content BLOB                      -- Saved content
   162    231       @ );
   163         -    @ CREATE TABLE undo_vfile AS SELECT * FROM vfile;
   164         -    @ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge;
   165         -    @ CREATE TABLE undo_pending(undoId INTEGER PRIMARY KEY);
          232  +    @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
          233  +    @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
   166    234     ;
          235  +  if( undoDisable ) return;
   167    236     undo_reset();
   168         -  db_multi_exec(zSql);
          237  +  db_multi_exec(zSql, zDb, zDb, zDb);
   169    238     cid = db_lget_int("checkout", 0);
   170    239     db_lset_int("undo_checkout", cid);
   171    240     db_lset_int("undo_available", 1);
          241  +  db_lset("undo_cmdline", undoCmd);
   172    242     undoActive = 1;
   173    243   }
          244  +
          245  +/*
          246  +** Permanently disable undo 
          247  +*/
          248  +void undo_disable(void){
          249  +  undoDisable = 1;
          250  +}
   174    251   
   175    252   /*
   176    253   ** This flag is true if one or more files have changed and have been
   177    254   ** recorded in the undo log but the undo log has not yet been committed.
   178    255   **
   179    256   ** If a fatal error occurs and this flag is set, that means we should
   180    257   ** rollback all the filesystem changes.
................................................................................
   186    263   ** will be undoable.  The name is relative to the root of the
   187    264   ** tree.
   188    265   */
   189    266   void undo_save(const char *zPathname){
   190    267     char *zFullname;
   191    268     Blob content;
   192    269     int existsFlag;
          270  +  int isLink;
   193    271     Stmt q;
   194    272   
   195    273     if( !undoActive ) return;
   196         -  zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
   197         -  existsFlag = file_size(zFullname)>=0;
          274  +  zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
          275  +  existsFlag = file_wd_size(zFullname)>=0;
          276  +  isLink = file_wd_islink(zFullname);
   198    277     db_prepare(&q,
   199         -    "REPLACE INTO undo(pathname,redoflag,existsflag,content)"
   200         -    " VALUES(%Q,0,%d,:c)",
   201         -    zPathname, existsFlag
          278  +    "INSERT OR IGNORE INTO"
          279  +    "   undo(pathname,redoflag,existsflag,isExe,isLink,content)"
          280  +    " VALUES(%Q,0,%d,%d,%d,:c)",
          281  +    zPathname, existsFlag, file_wd_isexe(zFullname), isLink
   202    282     );
   203    283     if( existsFlag ){
   204         -    blob_read_from_file(&content, zFullname);
          284  +    if( isLink ){
          285  +      blob_read_link(&content, zFullname); 
          286  +    }else{
          287  +      blob_read_from_file(&content, zFullname);
          288  +    }
   205    289       db_bind_blob(&q, ":c", &content);
   206    290     }
   207    291     free(zFullname);
   208    292     db_step(&q);
   209    293     db_finalize(&q);
   210    294     if( existsFlag ){
   211    295       blob_reset(&content);
   212    296     }
   213    297     undoNeedRollback = 1;
   214    298   }
          299  +
          300  +/*
          301  +** Make the current state of stashid undoable.
          302  +*/
          303  +void undo_save_stash(int stashid){
          304  +  const char *zDb = db_name("localdb");
          305  +  db_multi_exec(
          306  +    "CREATE TABLE IF NOT EXISTS %s.undo_stash"
          307  +    "  AS SELECT * FROM stash WHERE 0;"
          308  +    "INSERT INTO undo_stash"
          309  +    " SELECT * FROM stash WHERE stashid=%d;",
          310  +    zDb, stashid
          311  +  );
          312  +  db_multi_exec(
          313  +    "CREATE TABLE IF NOT EXISTS %s.undo_stashfile"
          314  +    "  AS SELECT * FROM stashfile WHERE 0;"
          315  +    "INSERT INTO undo_stashfile"
          316  +    " SELECT * FROM stashfile WHERE stashid=%d;",
          317  +    zDb, stashid
          318  +  );
          319  +}
   215    320   
   216    321   /*
   217    322   ** Complete the undo process is one is currently in process.
   218    323   */
   219    324   void undo_finish(void){
   220    325     if( undoActive ){
          326  +    if( undoNeedRollback ){
          327  +      fossil_print(" \"fossil undo\" is available to undo changes"
          328  +             " to the working checkout.\n");
          329  +    }
   221    330       undoActive = 0;
   222    331       undoNeedRollback = 0;
   223    332     }
   224    333   }
   225    334   
   226    335   /*
   227    336   ** This routine is called when the process aborts due to an error.
................................................................................
   233    342   ** was locked or had permissions turned off.
   234    343   */
   235    344   void undo_rollback(void){
   236    345     if( !undoNeedRollback ) return;
   237    346     assert( undoActive );
   238    347     undoNeedRollback = 0;
   239    348     undoActive = 0;
   240         -  printf("Rolling back prior filesystem changes...\n");
          349  +  fossil_print("Rolling back prior filesystem changes...\n");
   241    350     undo_all_filesystem(0);
   242    351   }
   243    352   
   244    353   /*
   245    354   ** COMMAND: undo
          355  +** COMMAND: redo*
          356  +**
          357  +** Usage: %fossil undo ?OPTIONS? ?FILENAME...?
          358  +**    or: %fossil redo ?OPTIONS? ?FILENAME...?
          359  +**
          360  +** Undo the changes to the working checkout caused by the most recent
          361  +** of the following operations:
   246    362   **
   247         -** Usage: %fossil undo ?FILENAME...?
          363  +**    (1) fossil update             (5) fossil stash apply
          364  +**    (2) fossil merge              (6) fossil stash drop
          365  +**    (3) fossil revert             (7) fossil stash goto
          366  +**    (4) fossil stash pop
   248    367   **
   249         -** Undo the most recent update or merge or revert operation.  If FILENAME is
   250         -** specified then restore the content of the named file(s) but otherwise
   251         -** leave the update or merge or revert in effect.
          368  +** If FILENAME is specified then restore the content of the named
          369  +** file(s) but otherwise leave the update or merge or revert in effect. 
          370  +** The redo command undoes the effect of the most recent undo.
   252    371   **
   253         -** A single level of undo/redo is supported.  The undo/redo stack
   254         -** is cleared by the commit and checkout commands.
   255         -*/
   256         -void undo_cmd(void){
   257         -  int undo_available;
   258         -  db_must_be_within_tree();
   259         -  db_begin_transaction();
   260         -  undo_available = db_lget_int("undo_available", 0);
   261         -  if( g.argc==2 ){
   262         -    if( undo_available!=1 ){
   263         -      fossil_fatal("no update or merge operation is available to undo");
   264         -    }
   265         -    undo_all(0);
   266         -    db_lset_int("undo_available", 2);
   267         -  }else if( g.argc>=3 ){
   268         -    int i;
   269         -    if( undo_available==0 ){
   270         -      fossil_fatal("no update or merge operation is available to undo");
   271         -    }
   272         -    for(i=2; i<g.argc; i++){
   273         -      const char *zFile = g.argv[i];
   274         -      Blob path;
   275         -      file_tree_name(zFile, &path, 1);
   276         -      undo_one(blob_str(&path), 0);
   277         -      blob_reset(&path);
   278         -    }
   279         -  }
   280         -  db_end_transaction(0);
   281         -}
   282         -
   283         -/*
   284         -** COMMAND: redo
   285         -**
   286         -** Usage: %fossil redo ?FILENAME...?
   287         -**
   288         -** Redo the an update or merge or revert operation that has been undone
   289         -** by the undo command.  If FILENAME is specified then restore the changes
   290         -** associated with the named file(s) but otherwise leave the update
   291         -** or merge undone.
          372  +** If the --explain option is present, no changes are made and instead
          373  +** the undo or redo command explains what actions the undo or redo would
          374  +** have done had the --explain been omitted.
   292    375   **
   293    376   ** A single level of undo/redo is supported.  The undo/redo stack
   294    377   ** is cleared by the commit and checkout commands.
          378  +**
          379  +** Options:
          380  +**   --explain    do not make changes but show what would be done
          381  +**
          382  +** See also: commit, status
   295    383   */
   296         -void redo_cmd(void){
          384  +void undo_cmd(void){
          385  +  int isRedo = g.argv[1][0]=='r';
   297    386     int undo_available;
          387  +  int explainFlag = find_option("explain", 0, 0)!=0;
          388  +  const char *zCmd = isRedo ? "redo" : "undo";
   298    389     db_must_be_within_tree();
          390  +  verify_all_options();
   299    391     db_begin_transaction();
   300    392     undo_available = db_lget_int("undo_available", 0);
   301         -  if( g.argc==2 ){
   302         -    if( undo_available!=2 ){
   303         -      fossil_fatal("no undone update or merge operation is available to redo");
   304         -    }
   305         -    undo_all(1);
   306         -    db_lset_int("undo_available", 1);
   307         -  }else if( g.argc>=3 ){
   308         -    int i;
          393  +  if( explainFlag ){
   309    394       if( undo_available==0 ){
   310         -      fossil_fatal("no update or merge operation is available to redo");
          395  +      fossil_print("No undo or redo is available\n");
          396  +    }else{
          397  +      Stmt q;
          398  +      int nChng = 0;
          399  +      zCmd = undo_available==1 ? "undo" : "redo";
          400  +      fossil_print("A %s is available for the following command:\n\n"
          401  +                   "   %s %s\n\n",
          402  +                   zCmd, g.argv[0], db_lget("undo_cmdline", "???"));
          403  +      db_prepare(&q,
          404  +        "SELECT existsflag, pathname FROM undo ORDER BY pathname"
          405  +      );
          406  +      while( db_step(&q)==SQLITE_ROW ){
          407  +        if( nChng==0 ){
          408  +          fossil_print("The following file changes would occur if the "
          409  +                       "command above is %sne:\n\n", zCmd);
          410  +        }
          411  +        nChng++;
          412  +        fossil_print("%s %s\n", 
          413  +           db_column_int(&q,0) ? "UPDATE" : "DELETE",
          414  +           db_column_text(&q, 1)
          415  +        );
          416  +      }
          417  +      db_finalize(&q);
          418  +      if( nChng==0 ){
          419  +        fossil_print("No file changes would occur with this undo/redo.\n");
          420  +      }
          421  +    }
          422  +  }else{
          423  +    int vid1 = db_lget_int("checkout", 0);
          424  +    int vid2;
          425  +    if( g.argc==2 ){
          426  +      if( undo_available!=(1+isRedo) ){
          427  +        fossil_fatal("nothing to %s", zCmd);
          428  +      }
          429  +      undo_all(isRedo);
          430  +      db_lset_int("undo_available", 2-isRedo);
          431  +    }else if( g.argc>=3 ){
          432  +      int i;
          433  +      if( undo_available==0 ){
          434  +        fossil_fatal("nothing to %s", zCmd);
          435  +      }
          436  +      for(i=2; i<g.argc; i++){
          437  +        const char *zFile = g.argv[i];
          438  +        Blob path;
          439  +        file_tree_name(zFile, &path, 1);
          440  +        undo_one(blob_str(&path), isRedo);
          441  +        blob_reset(&path);
          442  +      }
   311    443       }
   312         -    for(i=2; i<g.argc; i++){
   313         -      const char *zFile = g.argv[i];
   314         -      Blob path;
   315         -      file_tree_name(zFile, &path, 1);
   316         -      undo_one(blob_str(&path), 0);
   317         -      blob_reset(&path);
          444  +    vid2 = db_lget_int("checkout", 0);
          445  +    if( vid1!=vid2 ){
          446  +      fossil_print("--------------------\n");
          447  +      show_common_info(vid2, "updated-to:", 1, 0);
   318    448       }
   319    449     }
   320    450     db_end_transaction(0);
   321    451   }

Added src/unicode.c.

            1  +/*
            2  +** Copyright (c) 2013 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +**
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file is copied from ext/fts3/fts3_unicode2.c of SQLite3 with
           19  +** minor changes.
           20  +*/
           21  +#include "config.h"
           22  +#include "unicode.h"
           23  +
           24  +/*
           25  +** Return true if the argument corresponds to a unicode codepoint
           26  +** classified as either a letter or a number. Otherwise false.
           27  +**
           28  +** The results are undefined if the value passed to this function
           29  +** is less than zero.
           30  +*/
           31  +int unicode_isalnum(int c){
           32  +  /* Each unsigned integer in the following array corresponds to a contiguous
           33  +  ** range of unicode codepoints that are not either letters or numbers (i.e.
           34  +  ** codepoints for which this function should return 0).
           35  +  **
           36  +  ** The most significant 22 bits in each 32-bit value contain the first 
           37  +  ** codepoint in the range. The least significant 10 bits are used to store
           38  +  ** the size of the range (always at least 1). In other words, the value 
           39  +  ** ((C<<22) + N) represents a range of N codepoints starting with codepoint 
           40  +  ** C. It is not possible to represent a range larger than 1023 codepoints 
           41  +  ** using this format.
           42  +  */
           43  +  const static unsigned int aEntry[] = {
           44  +    0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
           45  +    0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
           46  +    0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
           47  +    0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
           48  +    0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
           49  +    0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
           50  +    0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
           51  +    0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
           52  +    0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
           53  +    0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
           54  +    0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
           55  +    0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
           56  +    0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
           57  +    0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
           58  +    0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
           59  +    0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
           60  +    0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
           61  +    0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
           62  +    0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
           63  +    0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
           64  +    0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
           65  +    0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
           66  +    0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
           67  +    0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
           68  +    0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
           69  +    0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
           70  +    0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
           71  +    0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
           72  +    0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
           73  +    0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
           74  +    0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
           75  +    0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
           76  +    0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
           77  +    0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
           78  +    0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
           79  +    0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
           80  +    0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
           81  +    0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
           82  +    0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
           83  +    0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
           84  +    0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
           85  +    0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
           86  +    0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
           87  +    0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
           88  +    0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
           89  +    0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
           90  +    0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
           91  +    0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
           92  +    0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
           93  +    0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
           94  +    0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
           95  +    0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
           96  +    0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
           97  +    0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
           98  +    0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
           99  +    0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
          100  +    0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
          101  +    0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
          102  +    0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
          103  +    0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
          104  +    0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
          105  +    0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810,
          106  +    0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023,
          107  +    0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
          108  +    0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
          109  +    0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E,
          110  +    0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01,
          111  +    0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01,
          112  +    0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016,
          113  +    0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004,
          114  +    0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004,
          115  +    0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5,
          116  +    0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
          117  +    0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
          118  +    0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064,
          119  +    0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F,
          120  +    0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009,
          121  +    0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014,
          122  +    0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001,
          123  +    0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018,
          124  +    0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401,
          125  +    0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001,
          126  +    0x43FFF401,
          127  +  };
          128  +  static const unsigned int aAscii[4] = {
          129  +    0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
          130  +  };
          131  +
          132  +  if( c<128 ){
          133  +    return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
          134  +  }else if( c<(1<<22) ){
          135  +    unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
          136  +    int iRes = 0;
          137  +    int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
          138  +    int iLo = 0;
          139  +    while( iHi>=iLo ){
          140  +      int iTest = (iHi + iLo) / 2;
          141  +      if( key >= aEntry[iTest] ){
          142  +        iRes = iTest;
          143  +        iLo = iTest+1;
          144  +      }else{
          145  +        iHi = iTest-1;
          146  +      }
          147  +    }
          148  +    assert( aEntry[0]<key );
          149  +    assert( key>=aEntry[iRes] );
          150  +    return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
          151  +  }
          152  +  return 1;
          153  +}
          154  +
          155  +
          156  +/*
          157  +** If the argument is a codepoint corresponding to a lowercase letter
          158  +** in the ASCII range with a diacritic added, return the codepoint
          159  +** of the ASCII letter only. For example, if passed 235 - "LATIN
          160  +** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
          161  +** E"). The resuls of passing a codepoint that corresponds to an
          162  +** uppercase letter are undefined.
          163  +*/
          164  +static int unicode_remove_diacritic(int c){
          165  +  unsigned short aDia[] = {
          166  +        0,  1797,  1848,  1859,  1891,  1928,  1940,  1995, 
          167  +     2024,  2040,  2060,  2110,  2168,  2206,  2264,  2286, 
          168  +     2344,  2383,  2472,  2488,  2516,  2596,  2668,  2732, 
          169  +     2782,  2842,  2894,  2954,  2984,  3000,  3028,  3336, 
          170  +     3456,  3696,  3712,  3728,  3744,  3896,  3912,  3928, 
          171  +     3968,  4008,  4040,  4106,  4138,  4170,  4202,  4234, 
          172  +     4266,  4296,  4312,  4344,  4408,  4424,  4472,  4504, 
          173  +     6148,  6198,  6264,  6280,  6360,  6429,  6505,  6529, 
          174  +    61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, 
          175  +    61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, 
          176  +    62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, 
          177  +    62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, 
          178  +    62924, 63050, 63082, 63274, 63390, 
          179  +  };
          180  +  char aChar[] = {
          181  +    '\0', 'a',  'c',  'e',  'i',  'n',  'o',  'u',  'y',  'y',  'a',  'c',  
          182  +    'd',  'e',  'e',  'g',  'h',  'i',  'j',  'k',  'l',  'n',  'o',  'r',  
          183  +    's',  't',  'u',  'u',  'w',  'y',  'z',  'o',  'u',  'a',  'i',  'o',  
          184  +    'u',  'g',  'k',  'o',  'j',  'g',  'n',  'a',  'e',  'i',  'o',  'r',  
          185  +    'u',  's',  't',  'h',  'a',  'e',  'o',  'y',  '\0', '\0', '\0', '\0', 
          186  +    '\0', '\0', '\0', '\0', 'a',  'b',  'd',  'd',  'e',  'f',  'g',  'h',  
          187  +    'h',  'i',  'k',  'l',  'l',  'm',  'n',  'p',  'r',  'r',  's',  't',  
          188  +    'u',  'v',  'w',  'w',  'x',  'y',  'z',  'h',  't',  'w',  'y',  'a',  
          189  +    'e',  'i',  'o',  'u',  'y',  
          190  +  };
          191  +
          192  +  unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
          193  +  int iRes = 0;
          194  +  int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
          195  +  int iLo = 0;
          196  +  while( iHi>=iLo ){
          197  +    int iTest = (iHi + iLo) / 2;
          198  +    if( key >= aDia[iTest] ){
          199  +      iRes = iTest;
          200  +      iLo = iTest+1;
          201  +    }else{
          202  +      iHi = iTest-1;
          203  +    }
          204  +  }
          205  +  assert( key>=aDia[iRes] );
          206  +  return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
          207  +};
          208  +
          209  +
          210  +/*
          211  +** Return true if the argument interpreted as a unicode codepoint
          212  +** is a diacritical modifier character.
          213  +*/
          214  +int unicode_is_diacritic(int c){
          215  +  unsigned int mask0 = 0x08029FDF;
          216  +  unsigned int mask1 = 0x000361F8;
          217  +  if( c<768 || c>817 ) return 0;
          218  +  return (c < 768+32) ?
          219  +      (mask0 & (1 << (c-768))) :
          220  +      (mask1 & (1 << (c-768-32)));
          221  +}
          222  +
          223  +
          224  +/*
          225  +** Interpret the argument as a unicode codepoint. If the codepoint
          226  +** is an upper case character that has a lower case equivalent,
          227  +** return the codepoint corresponding to the lower case version.
          228  +** Otherwise, return a copy of the argument.
          229  +**
          230  +** The results are undefined if the value passed to this function
          231  +** is less than zero.
          232  +*/
          233  +int unicode_fold(int c, int bRemoveDiacritic){
          234  +  /* Each entry in the following array defines a rule for folding a range
          235  +  ** of codepoints to lower case. The rule applies to a range of nRange
          236  +  ** codepoints starting at codepoint iCode.
          237  +  **
          238  +  ** If the least significant bit in flags is clear, then the rule applies
          239  +  ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
          240  +  ** need to be folded). Or, if it is set, then the rule only applies to
          241  +  ** every second codepoint in the range, starting with codepoint C.
          242  +  **
          243  +  ** The 7 most significant bits in flags are an index into the aiOff[]
          244  +  ** array. If a specific codepoint C does require folding, then its lower
          245  +  ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
          246  +  **
          247  +  ** The contents of this array are generated by parsing the CaseFolding.txt
          248  +  ** file distributed as part of the "Unicode Character Database". See
          249  +  ** http://www.unicode.org for details.
          250  +  */
          251  +  static const struct TableEntry {
          252  +    unsigned short iCode;
          253  +    unsigned char flags;
          254  +    unsigned char nRange;
          255  +  } aEntry[] = {
          256  +    {65, 14, 26},          {181, 64, 1},          {192, 14, 23},
          257  +    {216, 14, 7},          {256, 1, 48},          {306, 1, 6},
          258  +    {313, 1, 16},          {330, 1, 46},          {376, 116, 1},
          259  +    {377, 1, 6},           {383, 104, 1},         {385, 50, 1},
          260  +    {386, 1, 4},           {390, 44, 1},          {391, 0, 1},
          261  +    {393, 42, 2},          {395, 0, 1},           {398, 32, 1},
          262  +    {399, 38, 1},          {400, 40, 1},          {401, 0, 1},
          263  +    {403, 42, 1},          {404, 46, 1},          {406, 52, 1},
          264  +    {407, 48, 1},          {408, 0, 1},           {412, 52, 1},
          265  +    {413, 54, 1},          {415, 56, 1},          {416, 1, 6},
          266  +    {422, 60, 1},          {423, 0, 1},           {425, 60, 1},
          267  +    {428, 0, 1},           {430, 60, 1},          {431, 0, 1},
          268  +    {433, 58, 2},          {435, 1, 4},           {439, 62, 1},
          269  +    {440, 0, 1},           {444, 0, 1},           {452, 2, 1},
          270  +    {453, 0, 1},           {455, 2, 1},           {456, 0, 1},
          271  +    {458, 2, 1},           {459, 1, 18},          {478, 1, 18},
          272  +    {497, 2, 1},           {498, 1, 4},           {502, 122, 1},
          273  +    {503, 134, 1},         {504, 1, 40},          {544, 110, 1},
          274  +    {546, 1, 18},          {570, 70, 1},          {571, 0, 1},
          275  +    {573, 108, 1},         {574, 68, 1},          {577, 0, 1},
          276  +    {579, 106, 1},         {580, 28, 1},          {581, 30, 1},
          277  +    {582, 1, 10},          {837, 36, 1},          {880, 1, 4},
          278  +    {886, 0, 1},           {902, 18, 1},          {904, 16, 3},
          279  +    {908, 26, 1},          {910, 24, 2},          {913, 14, 17},
          280  +    {931, 14, 9},          {962, 0, 1},           {975, 4, 1},
          281  +    {976, 140, 1},         {977, 142, 1},         {981, 146, 1},
          282  +    {982, 144, 1},         {984, 1, 24},          {1008, 136, 1},
          283  +    {1009, 138, 1},        {1012, 130, 1},        {1013, 128, 1},
          284  +    {1015, 0, 1},          {1017, 152, 1},        {1018, 0, 1},
          285  +    {1021, 110, 3},        {1024, 34, 16},        {1040, 14, 32},
          286  +    {1120, 1, 34},         {1162, 1, 54},         {1216, 6, 1},
          287  +    {1217, 1, 14},         {1232, 1, 88},         {1329, 22, 38},
          288  +    {4256, 66, 38},        {4295, 66, 1},         {4301, 66, 1},
          289  +    {7680, 1, 150},        {7835, 132, 1},        {7838, 96, 1},
          290  +    {7840, 1, 96},         {7944, 150, 8},        {7960, 150, 6},
          291  +    {7976, 150, 8},        {7992, 150, 8},        {8008, 150, 6},
          292  +    {8025, 151, 8},        {8040, 150, 8},        {8072, 150, 8},
          293  +    {8088, 150, 8},        {8104, 150, 8},        {8120, 150, 2},
          294  +    {8122, 126, 2},        {8124, 148, 1},        {8126, 100, 1},
          295  +    {8136, 124, 4},        {8140, 148, 1},        {8152, 150, 2},
          296  +    {8154, 120, 2},        {8168, 150, 2},        {8170, 118, 2},
          297  +    {8172, 152, 1},        {8184, 112, 2},        {8186, 114, 2},
          298  +    {8188, 148, 1},        {8486, 98, 1},         {8490, 92, 1},
          299  +    {8491, 94, 1},         {8498, 12, 1},         {8544, 8, 16},
          300  +    {8579, 0, 1},          {9398, 10, 26},        {11264, 22, 47},
          301  +    {11360, 0, 1},         {11362, 88, 1},        {11363, 102, 1},
          302  +    {11364, 90, 1},        {11367, 1, 6},         {11373, 84, 1},
          303  +    {11374, 86, 1},        {11375, 80, 1},        {11376, 82, 1},
          304  +    {11378, 0, 1},         {11381, 0, 1},         {11390, 78, 2},
          305  +    {11392, 1, 100},       {11499, 1, 4},         {11506, 0, 1},
          306  +    {42560, 1, 46},        {42624, 1, 24},        {42786, 1, 14},
          307  +    {42802, 1, 62},        {42873, 1, 4},         {42877, 76, 1},
          308  +    {42878, 1, 10},        {42891, 0, 1},         {42893, 74, 1},
          309  +    {42896, 1, 4},         {42912, 1, 10},        {42922, 72, 1},
          310  +    {65313, 14, 26},       
          311  +  };
          312  +  static const unsigned short aiOff[] = {
          313  +   1,     2,     8,     15,    16,    26,    28,    32,    
          314  +   37,    38,    40,    48,    63,    64,    69,    71,    
          315  +   79,    80,    116,   202,   203,   205,   206,   207,   
          316  +   209,   210,   211,   213,   214,   217,   218,   219,   
          317  +   775,   7264,  10792, 10795, 23228, 23256, 30204, 54721, 
          318  +   54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, 
          319  +   57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406, 
          320  +   65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, 
          321  +   65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, 
          322  +   65514, 65521, 65527, 65528, 65529, 
          323  +  };
          324  +
          325  +  int ret = c;
          326  +
          327  +  assert( c>=0 );
          328  +  assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
          329  +
          330  +  if( c<128 ){
          331  +    if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
          332  +  }else if( c<65536 ){
          333  +    int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
          334  +    int iLo = 0;
          335  +    int iRes = -1;
          336  +
          337  +    while( iHi>=iLo ){
          338  +      int iTest = (iHi + iLo) / 2;
          339  +      int cmp = (c - aEntry[iTest].iCode);
          340  +      if( cmp>=0 ){
          341  +        iRes = iTest;
          342  +        iLo = iTest+1;
          343  +      }else{
          344  +        iHi = iTest-1;
          345  +      }
          346  +    }
          347  +    assert( iRes<0 || c>=aEntry[iRes].iCode );
          348  +
          349  +    if( iRes>=0 ){
          350  +      const struct TableEntry *p = &aEntry[iRes];
          351  +      if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
          352  +        ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
          353  +        assert( ret>0 );
          354  +      }
          355  +    }
          356  +
          357  +    if( bRemoveDiacritic ) ret = unicode_remove_diacritic(ret);
          358  +  }
          359  +  
          360  +  else if( c>=66560 && c<66600 ){
          361  +    ret = c + 40;
          362  +  }
          363  +
          364  +  return ret;
          365  +}

Changes to src/update.c.

    22     22   #include "update.h"
    23     23   #include <assert.h>
    24     24   
    25     25   /*
    26     26   ** Return true if artifact rid is a version
    27     27   */
    28     28   int is_a_version(int rid){
    29         -  return db_exists("SELECT 1 FROM plink WHERE cid=%d", rid);
           29  +  return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid);
           30  +}
           31  +
           32  +/* This variable is set if we are doing an internal update.  It is clear
           33  +** when running the "update" command.
           34  +*/
           35  +static int internalUpdate = 0;
           36  +static int internalConflictCnt = 0;
           37  +
           38  +/*
           39  +** Do an update to version vid.  
           40  +**
           41  +** Start an undo session but do not terminate it.  Do not autosync.
           42  +*/
           43  +int update_to(int vid){
           44  +  int savedArgc;
           45  +  char **savedArgv;
           46  +  char *newArgv[3];
           47  +  newArgv[0] = g.argv[0];
           48  +  newArgv[1] = "update";
           49  +  newArgv[2] = 0;
           50  +  savedArgv = g.argv;
           51  +  savedArgc = g.argc;
           52  +  g.argc = 2;
           53  +  g.argv = newArgv;
           54  +  internalUpdate = vid;
           55  +  internalConflictCnt = 0;
           56  +  update_cmd();
           57  +  g.argc = savedArgc;
           58  +  g.argv = savedArgv;
           59  +  return internalConflictCnt;
    30     60   }
    31     61   
    32     62   /*
    33     63   ** COMMAND: update
    34     64   **
    35         -** Usage: %fossil update ?VERSION? ?FILES...?
           65  +** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...?
    36     66   **
    37     67   ** Change the version of the current checkout to VERSION.  Any uncommitted
    38     68   ** changes are retained and applied to the new checkout.
    39     69   **
    40     70   ** The VERSION argument can be a specific version or tag or branch name.
    41         -** If the VERSION argument is omitted, then the leaf of the the subtree
           71  +** If the VERSION argument is omitted, then the leaf of the subtree
    42     72   ** that begins at the current version is used, if there is only a single
    43     73   ** leaf.  VERSION can also be "current" to select the leaf of the current
    44     74   ** version or "latest" to select the most recent check-in.
    45     75   **
    46     76   ** If one or more FILES are listed after the VERSION then only the
    47     77   ** named files are candidates to be updated.  If FILES is omitted, all
    48         -** files in the current checkout are subject to be updated.
           78  +** files in the current checkout are subject to be updated.  Using
           79  +** a directory name for one of the FILES arguments is the same as
           80  +** using every subdirectory and file beneath that directory.
    49     81   **
    50     82   ** The -n or --nochange option causes this command to do a "dry run".  It
    51     83   ** prints out what would have happened but does not actually make any
    52     84   ** changes to the current checkout or the repository.
    53     85   **
    54     86   ** The -v or --verbose option prints status information about unchanged
    55     87   ** files in addition to those file that actually do change.
           88  +**
           89  +** Options:
           90  +**   --debug          print debug information on stdout
           91  +**   --latest         acceptable in place of VERSION, update to latest version
           92  +**   -n|--nochange    do not perform changes but show what would be done
           93  +**   -v|--verbose     print status information about all files
           94  +**
           95  +** See also: revert
    56     96   */
    57     97   void update_cmd(void){
    58     98     int vid;              /* Current version */
    59     99     int tid=0;            /* Target version - version we are changing to */
    60    100     Stmt q;
    61    101     int latestFlag;       /* --latest.  Pick the latest version if true */
    62    102     int nochangeFlag;     /* -n or --nochange.  Do a dry run */
    63    103     int verboseFlag;      /* -v or --verbose.  Output extra information */
          104  +  int debugFlag;        /* --debug option */
          105  +  int setmtimeFlag;     /* --setmtime.  Set mtimes on files */
          106  +  int nChng;            /* Number of file renames */
          107  +  int *aChng;           /* Array of file renames */
          108  +  int i;                /* Loop counter */
          109  +  int nConflict = 0;    /* Number of merge conflicts */
          110  +  int nOverwrite = 0;   /* Number of unmanaged files overwritten */
          111  +  int nUpdate = 0;      /* Number of changes of any kind */
          112  +  Stmt mtimeXfer;       /* Statement to transfer mtimes */
    64    113   
    65         -  url_proxy_options();
          114  +  if( !internalUpdate ){
          115  +    undo_capture_command_line();
          116  +    url_proxy_options();
          117  +  }
    66    118     latestFlag = find_option("latest",0, 0)!=0;
    67    119     nochangeFlag = find_option("nochange","n",0)!=0;
    68    120     verboseFlag = find_option("verbose","v",0)!=0;
          121  +  debugFlag = find_option("debug",0,0)!=0;
          122  +  setmtimeFlag = find_option("setmtime",0,0)!=0;
    69    123     db_must_be_within_tree();
    70    124     vid = db_lget_int("checkout", 0);
    71    125     if( vid==0 ){
    72    126       fossil_fatal("cannot find current version");
    73    127     }
    74         -  if( db_exists("SELECT 1 FROM vmerge") ){
    75         -    fossil_fatal("cannot update an uncommitted merge");
          128  +  if( !nochangeFlag && !internalUpdate ){
          129  +    autosync(SYNC_PULL + SYNC_VERBOSE*verboseFlag);
    76    130     }
    77         -  if( !nochangeFlag ) autosync(AUTOSYNC_PULL);
          131  +  
          132  +  /* Create any empty directories now, as well as after the update,
          133  +  ** so changes in settings are reflected now */
          134  +  if( !nochangeFlag ) ensure_empty_dirs_created();
    78    135   
    79         -  if( g.argc>=3 ){
    80         -    if( strcmp(g.argv[2], "current")==0 ){
          136  +  if( internalUpdate ){
          137  +    tid = internalUpdate;
          138  +  }else if( g.argc>=3 ){
          139  +    if( fossil_strcmp(g.argv[2], "current")==0 ){
    81    140         /* If VERSION is "current", then use the same algorithm to find the
    82    141         ** target as if VERSION were omitted. */
    83         -    }else if( strcmp(g.argv[2], "latest")==0 ){
          142  +    }else if( fossil_strcmp(g.argv[2], "latest")==0 ){
    84    143         /* If VERSION is "latest", then use the same algorithm to find the
    85    144         ** target as if VERSION were omitted and the --latest flag is present.
    86    145         */
    87    146         latestFlag = 1;
    88    147       }else{
    89         -      tid = name_to_rid(g.argv[2]);
          148  +      tid = name_to_typed_rid(g.argv[2],"ci");
    90    149         if( tid==0 ){
    91    150           fossil_fatal("no such version: %s", g.argv[2]);
    92    151         }else if( !is_a_version(tid) ){
    93    152           fossil_fatal("no such version: %s", g.argv[2]);
    94    153         }
    95    154       }
    96    155     }
    97    156     
          157  +  /* If no VERSION is specified on the command-line, then look for a
          158  +  ** descendent of the current version.  If there are multiple descendants,
          159  +  ** look for one from the same branch as the current version.  If there
          160  +  ** are still multiple descendants, show them all and refuse to update
          161  +  ** until the user selects one.
          162  +  */
    98    163     if( tid==0 ){
    99         -    compute_leaves(vid, 1);
          164  +    int closeCode = 1;
          165  +    compute_leaves(vid, closeCode);
          166  +    if( !db_exists("SELECT 1 FROM leaves") ){
          167  +      closeCode = 0;
          168  +      compute_leaves(vid, closeCode);
          169  +    }
   100    170       if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){
   101         -      db_prepare(&q, 
   102         -        "%s "
   103         -        "   AND event.objid IN leaves"
   104         -        " ORDER BY event.mtime DESC",
   105         -        timeline_query_for_tty()
          171  +      db_multi_exec(
          172  +        "DELETE FROM leaves WHERE rid NOT IN"
          173  +        "   (SELECT leaves.rid FROM leaves, tagxref"
          174  +        "     WHERE leaves.rid=tagxref.rid AND tagxref.tagid=%d"
          175  +        "       AND tagxref.value==(SELECT value FROM tagxref"
          176  +                                   " WHERE tagid=%d AND rid=%d))",
          177  +        TAG_BRANCH, TAG_BRANCH, vid
   106    178         );
   107         -      print_timeline(&q, 100);
   108         -      db_finalize(&q);
   109         -      fossil_fatal("Multiple descendants");
          179  +      if( db_int(0, "SELECT count(*) FROM leaves")>1 ){
          180  +        compute_leaves(vid, closeCode);
          181  +        db_prepare(&q, 
          182  +          "%s "
          183  +          "   AND event.objid IN leaves"
          184  +          " ORDER BY event.mtime DESC",
          185  +          timeline_query_for_tty()
          186  +        );
          187  +        print_timeline(&q, 100, 0);
          188  +        db_finalize(&q);
          189  +        fossil_fatal("Multiple descendants");
          190  +      }
   110    191       }
   111    192       tid = db_int(0, "SELECT rid FROM leaves, event"
   112    193                       " WHERE event.objid=leaves.rid"
   113    194                       " ORDER BY event.mtime DESC"); 
          195  +    if( tid==0 ) tid = vid;
          196  +  }
          197  +
          198  +  if( tid==0 ){
          199  +    fossil_panic("Internal Error: unable to find a version to update to.");
   114    200     }
   115    201   
   116    202     db_begin_transaction();
   117         -  vfile_check_signature(vid, 1);
   118         -  if( !nochangeFlag ) undo_begin();
          203  +  vfile_check_signature(vid, CKSIG_ENOTFILE);
          204  +  if( !nochangeFlag && !internalUpdate ) undo_begin();
   119    205     load_vfile_from_rid(tid);
   120    206   
   121    207     /*
   122    208     ** The record.fn field is used to match files against each other.  The
   123    209     ** FV table contains one row for each each unique filename in
   124    210     ** in the current checkout, the pivot, and the version being merged.
   125    211     */
................................................................................
   126    212     db_multi_exec(
   127    213       "DROP TABLE IF EXISTS fv;"
   128    214       "CREATE TEMP TABLE fv("
   129    215       "  fn TEXT PRIMARY KEY,"      /* The filename relative to root */
   130    216       "  idv INTEGER,"              /* VFILE entry for current version */
   131    217       "  idt INTEGER,"              /* VFILE entry for target version */
   132    218       "  chnged BOOLEAN,"           /* True if current version has been edited */
          219  +    "  islinkv BOOLEAN,"          /* True if current file is a link */
          220  +    "  islinkt BOOLEAN,"          /* True if target file is a link */
   133    221       "  ridv INTEGER,"             /* Record ID for current version */
   134         -    "  ridt INTEGER "             /* Record ID for target */
          222  +    "  ridt INTEGER,"             /* Record ID for target */
          223  +    "  isexe BOOLEAN,"            /* Does target have execute permission? */
          224  +    "  deleted BOOLEAN DEFAULT 0,"/* File marke by "rm" to become unmanaged */
          225  +    "  fnt TEXT"                  /* Filename of same file on target version */
   135    226       ");"
   136         -    "INSERT OR IGNORE INTO fv"
   137         -    " SELECT pathname, 0, 0, 0, 0, 0 FROM vfile"
          227  +  );
          228  +
          229  +  /* Add files found in the current version
          230  +  */
          231  +  db_multi_exec(
          232  +    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged,deleted)"
          233  +    " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged, deleted"
          234  +    "   FROM vfile WHERE vid=%d",
          235  +    vid
          236  +  );
          237  +
          238  +  /* Compute file name changes on V->T.  Record name changes in files that
          239  +  ** have changed locally.
          240  +  */
          241  +  find_filename_changes(vid, tid, 1, &nChng, &aChng, debugFlag ? "V->T": 0);
          242  +  if( nChng ){
          243  +    for(i=0; i<nChng; i++){
          244  +      db_multi_exec(
          245  +        "UPDATE fv"
          246  +        "   SET fnt=(SELECT name FROM filename WHERE fnid=%d)"
          247  +        " WHERE fn=(SELECT name FROM filename WHERE fnid=%d) AND chnged",
          248  +        aChng[i*2+1], aChng[i*2]
          249  +      );
          250  +    }
          251  +    fossil_free(aChng);
          252  +  }
          253  +
          254  +  /* Add files found in the target version T but missing from the current
          255  +  ** version V.
          256  +  */
          257  +  db_multi_exec(
          258  +    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
          259  +    " SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile"
          260  +    "  WHERE vid=%d"
          261  +    "    AND pathname NOT IN (SELECT fnt FROM fv)",
          262  +    tid
   138    263     );
   139         -  db_prepare(&q,
   140         -    "SELECT id, pathname, rid FROM vfile"
   141         -    " WHERE vid=%d", tid
          264  +
          265  +  /*
          266  +  ** Compute the file version ids for T
          267  +  */
          268  +  db_multi_exec(
          269  +    "UPDATE fv SET"
          270  +    " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0),"
          271  +    " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)",
          272  +    tid, tid
   142    273     );
   143         -  while( db_step(&q)==SQLITE_ROW ){
   144         -    int id = db_column_int(&q, 0);
   145         -    const char *fn = db_column_text(&q, 1);
   146         -    int rid = db_column_int(&q, 2);
   147         -    db_multi_exec(
   148         -      "UPDATE fv SET idt=%d, ridt=%d WHERE fn=%Q",
   149         -      id, rid, fn
   150         -    );
   151         -  }
   152         -  db_finalize(&q);
   153         -  db_prepare(&q,
   154         -    "SELECT id, pathname, rid, chnged FROM vfile"
   155         -    " WHERE vid=%d", vid
          274  +
          275  +  /*
          276  +  ** Add islink information
          277  +  */
          278  +  db_multi_exec(
          279  +    "UPDATE fv SET"
          280  +    " islinkv=coalesce((SELECT islink FROM vfile"
          281  +                       " WHERE vid=%d AND pathname=fnt),0),"
          282  +    " islinkt=coalesce((SELECT islink FROM vfile"
          283  +                       " WHERE vid=%d AND pathname=fnt),0)",
          284  +    vid, tid
   156    285     );
   157         -  while( db_step(&q)==SQLITE_ROW ){
   158         -    int id = db_column_int(&q, 0);
   159         -    const char *fn = db_column_text(&q, 1);
   160         -    int rid = db_column_int(&q, 2);
   161         -    int chnged = db_column_int(&q, 3);
   162         -    db_multi_exec(
   163         -      "UPDATE fv SET idv=%d, ridv=%d, chnged=%d WHERE fn=%Q",
   164         -      id, rid, chnged, fn
          286  +
          287  +
          288  +  if( debugFlag ){
          289  +    db_prepare(&q,
          290  +       "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe,"
          291  +       "       islinkv, islinkt FROM fv"
   165    292       );
          293  +    while( db_step(&q)==SQLITE_ROW ){
          294  +       fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d"
          295  +                    " islinkv=%d  islinkt=%d\n",
          296  +          db_column_int(&q, 0),
          297  +          db_column_int(&q, 4),
          298  +          db_column_int(&q, 5),
          299  +          db_column_int(&q, 3),
          300  +          db_column_int(&q, 6),
          301  +          db_column_int(&q, 7),
          302  +          db_column_int(&q, 8));
          303  +       fossil_print("     fnv = [%s]\n", db_column_text(&q, 1));
          304  +       fossil_print("     fnt = [%s]\n", db_column_text(&q, 2));
          305  +    }
          306  +    db_finalize(&q);
   166    307     }
   167         -  db_finalize(&q);
   168    308   
   169    309     /* If FILES appear on the command-line, remove from the "fv" table
   170         -  ** every entry that is not named on the command-line.
          310  +  ** every entry that is not named on the command-line or which is not
          311  +  ** in a directory named on the command-line.
   171    312     */
   172    313     if( g.argc>=4 ){
   173    314       Blob sql;              /* SQL statement to purge unwanted entries */
   174         -    char *zSep = "(";      /* Separator in the list of filenames */
   175    315       Blob treename;         /* Normalized filename */
   176    316       int i;                 /* Loop counter */
          317  +    const char *zSep;      /* Term separator */
   177    318   
   178    319       blob_zero(&sql);
   179         -    blob_append(&sql, "DELETE FROM fv WHERE fn NOT IN ", -1);
          320  +    blob_append(&sql, "DELETE FROM fv WHERE ", -1);
          321  +    zSep = "";
   180    322       for(i=3; i<g.argc; i++){
   181    323         file_tree_name(g.argv[i], &treename, 1);
   182         -      blob_appendf(&sql, "%s'%q'", zSep, blob_str(&treename));
          324  +      if( file_wd_isdir(g.argv[i])==1 ){
          325  +        if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){
          326  +          blob_appendf(&sql, "%sfn NOT GLOB '%b/*' ", zSep, &treename);
          327  +        }else{
          328  +          blob_reset(&sql);
          329  +          break;
          330  +        }
          331  +      }else{
          332  +        blob_appendf(&sql, "%sfn<>%B ", zSep, &treename);
          333  +      }
          334  +      zSep = "AND ";
   183    335         blob_reset(&treename);
   184         -      zSep = ",";
   185    336       }
   186         -    blob_append(&sql, ")", -1);
   187    337       db_multi_exec(blob_str(&sql));
   188    338       blob_reset(&sql);
   189    339     }
   190    340   
          341  +  /*
          342  +  ** Alter the content of the checkout so that it conforms with the
          343  +  ** target
          344  +  */
   191    345     db_prepare(&q, 
   192         -    "SELECT fn, idv, ridv, idt, ridt, chnged FROM fv ORDER BY 1"
          346  +    "SELECT fn, idv, ridv, idt, ridt, chnged, fnt,"
          347  +    "       isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1"
          348  +  );
          349  +  db_prepare(&mtimeXfer,
          350  +    "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
          351  +    " WHERE id=:idt"
   193    352     );
   194    353     assert( g.zLocalRoot!=0 );
   195    354     assert( strlen(g.zLocalRoot)>1 );
   196    355     assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
   197    356     while( db_step(&q)==SQLITE_ROW ){
   198    357       const char *zName = db_column_text(&q, 0);  /* The filename from root */
   199    358       int idv = db_column_int(&q, 1);             /* VFILE entry for current */
   200    359       int ridv = db_column_int(&q, 2);            /* RecordID for current */
   201    360       int idt = db_column_int(&q, 3);             /* VFILE entry for target */
   202    361       int ridt = db_column_int(&q, 4);            /* RecordID for target */
   203    362       int chnged = db_column_int(&q, 5);          /* Current is edited */
          363  +    const char *zNewName = db_column_text(&q,6);/* New filename */
          364  +    int isexe = db_column_int(&q, 7);           /* EXE perm for new file */
          365  +    int islinkv = db_column_int(&q, 8);         /* Is current file is a link */
          366  +    int islinkt = db_column_int(&q, 9);         /* Is target file is a link */
          367  +    int deleted = db_column_int(&q, 10);        /* Marked for deletion */
   204    368       char *zFullPath;                            /* Full pathname of the file */
          369  +    char *zFullNewPath;                         /* Full pathname of dest */
          370  +    char nameChng;                              /* True if the name changed */
   205    371   
   206    372       zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
          373  +    zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
          374  +    nameChng = fossil_strcmp(zName, zNewName);
          375  +    nUpdate++;
          376  +    if( deleted ){
          377  +      db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
          378  +    }
   207    379       if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
   208    380         /* Conflict.  This file has been added to the current checkout
   209    381         ** but also exists in the target checkout.  Use the current version.
   210    382         */
   211         -      printf("CONFLICT %s\n", zName);
          383  +      fossil_print("CONFLICT %s\n", zName);
          384  +      nConflict++;
   212    385       }else if( idt>0 && idv==0 ){
   213    386         /* File added in the target. */
   214         -      printf("ADD %s\n", zName);
          387  +      if( file_wd_isfile_or_link(zFullPath) ){
          388  +        fossil_print("ADD %s - overwrites an unmanaged file\n", zName);
          389  +        nOverwrite++;
          390  +      }else{
          391  +        fossil_print("ADD %s\n", zName);
          392  +      }
   215    393         undo_save(zName);
   216         -      if( !nochangeFlag ) vfile_to_disk(0, idt, 0);
   217         -    }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){
          394  +      if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
          395  +    }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
   218    396         /* The file is unedited.  Change it to the target version */
   219         -      printf("UPDATE %s\n", zName);
   220    397         undo_save(zName);
   221         -      if( !nochangeFlag ) vfile_to_disk(0, idt, 0);
   222         -    }else if( idt>0 && idv>0 && file_size(zFullPath)<0 ){
          398  +      if( deleted ){
          399  +        fossil_print("UPDATE %s - change to unmanged file\n", zName);
          400  +      }else{
          401  +        fossil_print("UPDATE %s\n", zName);
          402  +      }
          403  +      if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
          404  +    }else if( idt>0 && idv>0 && file_wd_size(zFullPath)<0 ){
   223    405         /* The file missing from the local check-out. Restore it to the
   224    406         ** version that appears in the target. */
   225         -      printf("UPDATE %s\n", zName);
          407  +      fossil_print("UPDATE %s%s\n", zName,
          408  +                    deleted?" - change to unmanaged file":"");
   226    409         undo_save(zName);
   227         -      if( !nochangeFlag ) vfile_to_disk(0, idt, 0);
          410  +      if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
   228    411       }else if( idt==0 && idv>0 ){
   229    412         if( ridv==0 ){
   230    413           /* Added in current checkout.  Continue to hold the file as
   231    414           ** as an addition */
   232    415           db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
   233    416         }else if( chnged ){
   234         -        /* Edited locally but deleted from the target.  Delete it. */
   235         -        printf("CONFLICT %s\n", zName);
          417  +        /* Edited locally but deleted from the target.  Do not track the
          418  +        ** file but keep the edited version around. */
          419  +        fossil_print("CONFLICT %s - edited locally but deleted by update\n",
          420  +                     zName);
          421  +        nConflict++;
   236    422         }else{
   237         -        char *zFullPath;
   238         -        printf("REMOVE %s\n", zName);
          423  +        fossil_print("REMOVE %s\n", zName);
   239    424           undo_save(zName);
   240         -        zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
   241         -        if( !nochangeFlag ) unlink(zFullPath);
   242         -        free(zFullPath);
          425  +        if( !nochangeFlag ) file_delete(zFullPath);
   243    426         }
   244    427       }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
   245    428         /* Merge the changes in the current tree into the target version */
   246         -      Blob e, r, t, v;
          429  +      Blob r, t, v;
   247    430         int rc;
   248         -      printf("MERGE %s\n", zName);
   249         -      undo_save(zName);
   250         -      content_get(ridt, &t);
   251         -      content_get(ridv, &v);
   252         -      blob_zero(&e);
   253         -      blob_read_from_file(&e, zFullPath);
   254         -      rc = blob_merge(&v, &e, &t, &r);
   255         -      if( rc>=0 ){
   256         -        if( !nochangeFlag ) blob_write_to_file(&r, zFullPath);
   257         -        if( rc>0 ){
   258         -          printf("***** %d merge conflicts in %s\n", rc, zName);
   259         -        }
          431  +      if( nameChng ){
          432  +        fossil_print("MERGE %s -> %s\n", zName, zNewName);
          433  +      }else{
          434  +        fossil_print("MERGE %s\n", zName);
          435  +      }
          436  +      if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){
          437  +        fossil_print("***** Cannot merge symlink %s\n", zNewName);
          438  +        nConflict++;        
   260    439         }else{
   261         -        printf("***** Cannot merge binary file %s\n", zName);
          440  +        unsigned mergeFlags = nochangeFlag ? MERGE_DRYRUN : 0;
          441  +        undo_save(zName);
          442  +        content_get(ridt, &t);
          443  +        content_get(ridv, &v);
          444  +        rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
          445  +        if( rc>=0 ){
          446  +          if( !nochangeFlag ){
          447  +            blob_write_to_file(&r, zFullNewPath);
          448  +            file_wd_setexe(zFullNewPath, isexe);
          449  +          }
          450  +          if( rc>0 ){
          451  +            fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
          452  +            nConflict++;
          453  +          }
          454  +        }else{
          455  +          if( !nochangeFlag ){
          456  +            blob_write_to_file(&t, zFullNewPath);
          457  +            file_wd_setexe(zFullNewPath, isexe);
          458  +          }
          459  +          fossil_print("***** Cannot merge binary file %s\n", zNewName);
          460  +          nConflict++;
          461  +        }
   262    462         }
          463  +      if( nameChng && !nochangeFlag ) file_delete(zFullPath);
   263    464         blob_reset(&v);
   264         -      blob_reset(&e);
   265    465         blob_reset(&t);
   266    466         blob_reset(&r);
   267         -    }else if( verboseFlag ){
   268         -      printf("UNCHANGED %s\n", zName);
          467  +    }else{
          468  +      nUpdate--;
          469  +      if( chnged ){
          470  +        if( verboseFlag ) fossil_print("EDITED %s\n", zName);
          471  +      }else{
          472  +        db_bind_int(&mtimeXfer, ":idv", idv);
          473  +        db_bind_int(&mtimeXfer, ":idt", idt);
          474  +        db_step(&mtimeXfer);
          475  +        db_reset(&mtimeXfer);
          476  +        if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
          477  +      }
   269    478       }
   270    479       free(zFullPath);
          480  +    free(zFullNewPath);
   271    481     }
   272    482     db_finalize(&q);
          483  +  db_finalize(&mtimeXfer);
          484  +  fossil_print("%.79c\n",'-');
          485  +  if( nUpdate==0 ){
          486  +    show_common_info(tid, "checkout:", 1, 0);
          487  +    fossil_print("%-13s None. Already up-to-date\n", "changes:");
          488  +  }else{
          489  +    show_common_info(tid, "updated-to:", 1, 0);
          490  +    fossil_print("%-13s %d file%s modified.\n", "changes:",
          491  +                 nUpdate, nUpdate>1 ? "s" : "");
          492  +  }
          493  +
          494  +  /* Report on conflicts
          495  +  */
          496  +  if( !nochangeFlag ){
          497  +    Stmt q;
          498  +    int nMerge = 0;
          499  +    db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
          500  +                   " WHERE id<=0");
          501  +    while( db_step(&q)==SQLITE_ROW ){
          502  +      const char *zLabel = "merge";
          503  +      switch( db_column_int(&q, 1) ){
          504  +        case -1:  zLabel = "cherrypick merge"; break;
          505  +        case -2:  zLabel = "backout merge";    break;
          506  +      }
          507  +      fossil_warning("uncommitted %s against %S.",
          508  +                     zLabel, db_column_text(&q, 0));
          509  +      nMerge++;
          510  +    }
          511  +    db_finalize(&q);
          512  +    
          513  +    if( nConflict ){
          514  +      if( internalUpdate ){
          515  +        internalConflictCnt = nConflict;
          516  +        nConflict = 0;
          517  +      }else{
          518  +        fossil_warning("WARNING: %d merge conflicts", nConflict);
          519  +      }
          520  +    }
          521  +    if( nOverwrite ){
          522  +      fossil_warning("WARNING: %d unmanaged files were overwritten",
          523  +                     nOverwrite);
          524  +    }
          525  +    if( nMerge ){
          526  +      fossil_warning("WARNING: %d uncommitted prior merges", nMerge);
          527  +    }
          528  +  }
   273    529     
   274    530     /*
   275    531     ** Clean up the mid and pid VFILE entries.  Then commit the changes.
   276    532     */
   277    533     if( nochangeFlag ){
   278    534       db_end_transaction(1);  /* With --nochange, rollback changes */
   279    535     }else{
          536  +    ensure_empty_dirs_created();
   280    537       if( g.argc<=3 ){
   281    538         /* All files updated.  Shift the current checkout to the target. */
   282    539         db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
          540  +      checkout_set_all_exe(tid);
   283    541         manifest_to_disk(tid);
   284    542         db_lset_int("checkout", tid);
   285    543       }else{
   286    544         /* A subset of files have been checked out.  Keep the current
   287    545         ** checkout unchanged. */
   288    546         db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   289    547       }
   290         -    undo_finish();
          548  +    if( !internalUpdate ) undo_finish();
          549  +    if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME);
   291    550       db_end_transaction(0);
   292    551     }
   293    552   }
          553  +
          554  +/*
          555  +** Make sure empty directories are created
          556  +*/
          557  +void ensure_empty_dirs_created(void){
          558  +  /* Make empty directories? */
          559  +  char *zEmptyDirs = db_get("empty-dirs", 0);
          560  +  if( zEmptyDirs!=0 ){
          561  +    char *bc;
          562  +    Blob dirName;
          563  +    Blob dirsList;
          564  +
          565  +    blob_zero(&dirsList);
          566  +    blob_init(&dirsList, zEmptyDirs, strlen(zEmptyDirs));
          567  +    /* Replace commas by spaces */
          568  +    bc = blob_str(&dirsList);
          569  +    while( (*bc)!='\0' ){
          570  +      if( (*bc)==',' ) { *bc = ' '; }
          571  +      ++bc;
          572  +    }
          573  +    /* Make directories */
          574  +    blob_zero(&dirName);
          575  +    while( blob_token(&dirsList, &dirName) ){
          576  +      const char *zDir = blob_str(&dirName);
          577  +      /* Make full pathname of the directory */
          578  +      Blob path;
          579  +      const char *zPath;
          580  +
          581  +      blob_zero(&path);
          582  +      blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir);
          583  +      zPath = blob_str(&path);      
          584  +      /* Handle various cases of existence of the directory */
          585  +      switch( file_wd_isdir(zPath) ){
          586  +        case 0: { /* doesn't exist */
          587  +          if( file_mkdir(zPath, 0)!=0 ) {
          588  +            fossil_warning("couldn't create directory %s as "
          589  +                           "required by empty-dirs setting", zDir);
          590  +          }          
          591  +          break;
          592  +        }
          593  +        case 1: { /* exists, and is a directory */
          594  +          /* do nothing - required directory exists already */
          595  +          break;
          596  +        }
          597  +        case 2: { /* exists, but isn't a directory */
          598  +          fossil_warning("file %s found, but a directory is required "
          599  +                         "by empty-dirs setting", zDir);          
          600  +        }
          601  +      }
          602  +      blob_reset(&path);
          603  +    }
          604  +  }
          605  +}
   294    606   
   295    607   
   296    608   /*
   297         -** Get the contents of a file within a given revision.
          609  +** Get the contents of a file within the checking "revision".  If
          610  +** revision==NULL then get the file content for the current checkout.
   298    611   */
   299    612   int historical_version_of_file(
   300         -  const char *revision,    /* The baseline name containing the file */
          613  +  const char *revision,    /* The checkin containing the file */
   301    614     const char *file,        /* Full treename of the file */
   302    615     Blob *content,           /* Put the content here */
          616  +  int *pIsLink,            /* Set to true if file is link. */
          617  +  int *pIsExe,             /* Set to true if file is executable */
          618  +  int *pIsBin,             /* Set to true if file is binary */
   303    619     int errCode              /* Error code if file not found.  Panic if 0. */
   304    620   ){
   305         -  Blob mfile;
   306         -  Manifest m;
   307         -  int i, rid=0;
          621  +  Manifest *pManifest;
          622  +  ManifestFile *pFile;
          623  +  int rid=0;
   308    624     
   309    625     if( revision ){
   310         -    rid = name_to_rid(revision);
          626  +    rid = name_to_typed_rid(revision,"ci");
   311    627     }else{
   312    628       rid = db_lget_int("checkout", 0);
   313    629     }
   314    630     if( !is_a_version(rid) ){
   315    631       if( errCode>0 ) return errCode;
   316         -    fossil_fatal("no such check-out: %s", revision);
          632  +    fossil_fatal("no such checkin: %s", revision);
   317    633     }
   318         -  content_get(rid, &mfile);
          634  +  pManifest = manifest_get(rid, CFTYPE_MANIFEST);
   319    635     
   320         -  if( manifest_parse(&m, &mfile) ){
   321         -    for(i=0; i<m.nFile; i++){
   322         -      if( strcmp(m.aFile[i].zName, file)==0 ){
   323         -        rid = uuid_to_rid(m.aFile[i].zUuid, 0);
   324         -        return content_get(rid, content);
          636  +  if( pManifest ){
          637  +    pFile = manifest_file_find(pManifest, file);
          638  +    if( pFile ){
          639  +      int rc;
          640  +      rid = uuid_to_rid(pFile->zUuid, 0);
          641  +      if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
          642  +      if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
          643  +      manifest_destroy(pManifest);
          644  +      rc = content_get(rid, content);
          645  +      if( rc && pIsBin ){
          646  +        *pIsBin = looks_like_binary(content);
   325    647         }
          648  +      return rc;
   326    649       }
          650  +    manifest_destroy(pManifest);
   327    651       if( errCode<=0 ){
   328         -      fossil_fatal("file %s does not exist in baseline: %s", file, revision);
          652  +      fossil_fatal("file %s does not exist in checkin: %s", file, revision);
   329    653       }
   330    654     }else if( errCode<=0 ){
   331         -    fossil_panic("could not parse manifest for baseline: %s", revision);
          655  +    if( revision==0 ){
          656  +      revision = db_text("current", "SELECT uuid FROM blob WHERE rid=%d", rid);
          657  +    }
          658  +    fossil_panic("could not parse manifest for checkin: %s", revision);
   332    659     }
   333    660     return errCode;
   334    661   }
   335    662   
   336    663   
   337    664   /*
   338    665   ** COMMAND: revert
   339    666   **
   340         -** Usage: %fossil revert ?-r REVISION? FILE ...
          667  +** Usage: %fossil revert ?-r REVISION? ?FILE ...?
   341    668   **
   342    669   ** Revert to the current repository version of FILE, or to
   343    670   ** the version associated with baseline REVISION if the -r flag
   344    671   ** appears.
          672  +**
          673  +** If FILE was part of a rename operation, both the original file
          674  +** and the renamed file are reverted.
          675  +**
          676  +** Revert all files if no file name is provided.
   345    677   **
   346    678   ** If a file is reverted accidently, it can be restored using
   347    679   ** the "fossil undo" command.
          680  +**
          681  +** Options:
          682  +**   -r REVISION    revert given FILE(s) back to given REVISION
          683  +**
          684  +** See also: redo, undo, update
   348    685   */
   349    686   void revert_cmd(void){
   350    687     const char *zFile;
   351    688     const char *zRevision;
   352    689     Blob record;
   353    690     int i;
   354    691     int errCode;
   355         -  int rid = 0;
   356    692     Stmt q;
   357         -  
          693  +
          694  +  undo_capture_command_line();  
   358    695     zRevision = find_option("revision", "r", 1);
   359    696     verify_all_options();
   360    697     
   361    698     if( g.argc<2 ){
   362    699       usage("?OPTIONS? [FILE] ...");
   363    700     }
   364    701     if( zRevision && g.argc<3 ){
................................................................................
   370    707     db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
   371    708   
   372    709     if( g.argc>2 ){
   373    710       for(i=2; i<g.argc; i++){
   374    711         Blob fname;
   375    712         zFile = mprintf("%/", g.argv[i]);
   376    713         file_tree_name(zFile, &fname, 1);
   377         -      db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname);
          714  +      db_multi_exec(
          715  +        "REPLACE INTO torevert VALUES(%B);"
          716  +        "INSERT OR IGNORE INTO torevert"
          717  +        " SELECT pathname"
          718  +        "   FROM vfile"
          719  +        "  WHERE origname IN(%B)"
          720  +        " UNION ALL"
          721  +        " SELECT origname"
          722  +        "   FROM vfile"
          723  +        "  WHERE pathname IN(%B) AND origname IS NOT NULL;",
          724  +        &fname, &fname, &fname
          725  +      );
   378    726         blob_reset(&fname);
   379    727       }
   380    728     }else{
   381    729       int vid;
   382    730       vid = db_lget_int("checkout", 0);
   383    731       vfile_check_signature(vid, 0);
   384    732       db_multi_exec(
   385         -      "INSERT INTO torevert "
   386         -      "SELECT pathname"
   387         -      "  FROM vfile "
   388         -      " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
          733  +      "DELETE FROM vmerge;"
          734  +      "INSERT OR IGNORE INTO torevert "
          735  +      " SELECT pathname"
          736  +      "   FROM vfile "
          737  +      "  WHERE chnged OR deleted OR rid=0 OR pathname!=origname "
          738  +      " UNION ALL "
          739  +      " SELECT origname"
          740  +      "   FROM vfile"
          741  +      "  WHERE origname!=pathname;"
   389    742       );
   390    743     }
   391    744     blob_zero(&record);
   392    745     db_prepare(&q, "SELECT name FROM torevert");
          746  +  if( zRevision==0 ){
          747  +    int vid = db_lget_int("checkout", 0);
          748  +    zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
          749  +  }
   393    750     while( db_step(&q)==SQLITE_ROW ){
          751  +    int isExe = 0;
          752  +    int isLink = 0;
          753  +    char *zFull;
   394    754       zFile = db_column_text(&q, 0);
   395         -    if( zRevision!=0 ){
   396         -      errCode = historical_version_of_file(zRevision, zFile, &record, 2);
   397         -    }else{
   398         -      rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile);
   399         -      if( rid==0 ){
   400         -        errCode = 2;
   401         -      }else{
   402         -        content_get(rid, &record);
   403         -        errCode = 0;
   404         -      }
   405         -    }
   406         -
          755  +    zFull = mprintf("%/%/", g.zLocalRoot, zFile);
          756  +    errCode = historical_version_of_file(zRevision, zFile, &record,
          757  +                                         &isLink, &isExe, 0, 2);
   407    758       if( errCode==2 ){
   408         -      fossil_warning("file not in repository: %s", zFile);
          759  +      if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
          760  +                 zFile, zFile)==0 ){
          761  +        fossil_print("UNMANAGE: %s\n", zFile);
          762  +      }else{
          763  +        undo_save(zFile);
          764  +        file_delete(zFull);
          765  +        fossil_print("DELETE: %s\n", zFile);
          766  +      }
          767  +      db_multi_exec(
          768  +        "UPDATE vfile"
          769  +        "   SET pathname=origname, origname=NULL"
          770  +        " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;"
          771  +        "DELETE FROM vfile WHERE pathname=%Q",
          772  +        zFile, zFile
          773  +      );
   409    774       }else{
   410         -      char *zFull = mprintf("%//%/", g.zLocalRoot, zFile);
          775  +      sqlite3_int64 mtime;
   411    776         undo_save(zFile);
   412         -      blob_write_to_file(&record, zFull);
   413         -      printf("REVERTED: %s\n", zFile);
   414         -      free(zFull);
          777  +      if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
          778  +        file_delete(zFull);
          779  +      }
          780  +      if( isLink ){
          781  +        symlink_create(blob_str(&record), zFull);
          782  +      }else{
          783  +        blob_write_to_file(&record, zFull);
          784  +      }
          785  +      file_wd_setexe(zFull, isExe);
          786  +      fossil_print("REVERTED: %s\n", zFile);
          787  +      mtime = file_wd_mtime(zFull);
          788  +      db_multi_exec(
          789  +         "UPDATE vfile"
          790  +         "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid"
          791  +         " WHERE pathname=%Q OR origname=%Q",
          792  +         mtime, isExe, isLink, zFile, zFile
          793  +      );
   415    794       }
   416    795       blob_reset(&record);
          796  +    free(zFull);
   417    797     }
   418    798     db_finalize(&q);
   419    799     undo_finish();
   420    800     db_end_transaction(0);
   421         -  printf("\"fossil undo\" is available to undo the changes shown above.\n");
   422    801   }

Changes to src/url.c.

    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code for parsing URLs that appear on the command-line
    19     19   */
    20     20   #include "config.h"
    21     21   #include "url.h"
           22  +
           23  +/*
           24  +** Convert a string to lower-case.
           25  +*/
           26  +static void url_tolower(char *z){
           27  +  while( *z ){
           28  +     *z = fossil_tolower(*z);
           29  +     z++;
           30  +  }
           31  +}
    22     32   
    23     33   /*
    24     34   ** Parse the given URL.  Populate variables in the global "g" structure.
    25     35   **
    26     36   **      g.urlIsFile      True if FILE:
    27     37   **      g.urlIsHttps     True if HTTPS: 
           38  +**      g.urlIsSsh       True if SSH:
    28     39   **      g.urlProtocol    "http" or "https" or "file"
    29         -**      g.urlName        Hostname for HTTP: or HTTPS:.  Filename for FILE:
           40  +**      g.urlName        Hostname for HTTP:, HTTPS:, SSH:.  Filename for FILE:
    30     41   **      g.urlPort        TCP port number for HTTP or HTTPS.
    31     42   **      g.urlDfltPort    Default TCP port number (80 or 443).
    32     43   **      g.urlPath        Path name for HTTP or HTTPS.
    33     44   **      g.urlUser        Userid.
    34     45   **      g.urlPasswd      Password.
    35     46   **      g.urlHostname    HOST:PORT or just HOST if port is the default.
    36     47   **      g.urlCanonical   The URL in canonical form, omitting the password
    37     48   **
    38     49   ** HTTP url format is:
    39     50   **
    40         -**     http://userid:password@host:port/path?query#fragment
           51  +**     http://userid:password@host:port/path
           52  +**
           53  +** SSH url format is:
           54  +**
           55  +**     ssh://userid:password@host:port/path?fossil=path/to/fossil.exe
    41     56   **
    42     57   */
    43     58   void url_parse(const char *zUrl){
    44     59     int i, j, c;
    45     60     char *zFile = 0;
    46         -  if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
           61  +  if( strncmp(zUrl, "http://", 7)==0
           62  +   || strncmp(zUrl, "https://", 8)==0
           63  +   || strncmp(zUrl, "ssh://", 6)==0
           64  +  ){
    47     65       int iStart;
    48     66       char *zLogin;
           67  +    char *zExe;
           68  +    char cQuerySep = '?';
           69  +
    49     70       g.urlIsFile = 0;
    50     71       if( zUrl[4]=='s' ){
    51     72         g.urlIsHttps = 1;
    52     73         g.urlProtocol = "https";
    53     74         g.urlDfltPort = 443;
    54     75         iStart = 8;
           76  +    }else if( zUrl[0]=='s' ){
           77  +      g.urlIsSsh = 1;
           78  +      g.urlProtocol = "ssh";
           79  +      g.urlDfltPort = 22;
           80  +      g.urlFossil = "fossil";
           81  +      g.urlShell = 0;
           82  +      iStart = 6;
    55     83       }else{
    56     84         g.urlIsHttps = 0;
    57     85         g.urlProtocol = "http";
    58     86         g.urlDfltPort = 80;
    59     87         iStart = 7;
    60     88       }
    61     89       for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
    62     90       if( c=='@' ){
           91  +      /* Parse up the user-id and password */
    63     92         for(j=iStart; j<i && zUrl[j]!=':'; j++){}
    64     93         g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
    65     94         dehttpize(g.urlUser);
    66     95         if( j<i ){
    67     96           g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
    68     97           dehttpize(g.urlPasswd);
    69     98         }
           99  +      if( g.urlIsSsh && g.urlPasswd ){
          100  +        zLogin = mprintf("%t:*@", g.urlUser);
          101  +      }else{
          102  +        zLogin = mprintf("%t@", g.urlUser);
          103  +      }
    70    104         for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
    71    105         g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
    72    106         i = j;
    73         -      zLogin = mprintf("%t@", g.urlUser);
    74    107       }else{
    75    108         for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
    76    109         g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
    77    110         zLogin = mprintf("");
    78    111       }
    79         -    for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
          112  +    url_tolower(g.urlName);
    80    113       if( c==':' ){
    81    114         g.urlPort = 0;
    82    115         i++;
    83         -      while( (c = zUrl[i])!=0 && isdigit(c) ){
          116  +      while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
    84    117           g.urlPort = g.urlPort*10 + c - '0';
    85    118           i++;
    86    119         }
    87    120         g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
    88    121       }else{
    89    122         g.urlPort = g.urlDfltPort;
    90    123         g.urlHostname = g.urlName;
    91    124       }
    92         -    g.urlPath = mprintf(&zUrl[i]);
    93    125       dehttpize(g.urlName);
          126  +    g.urlPath = mprintf("%s", &zUrl[i]);
          127  +    for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){}
          128  +    if( g.urlPath[i] ){
          129  +      g.urlPath[i] = 0;
          130  +      i++;
          131  +    }
          132  +    zExe = mprintf("");
          133  +    while( g.urlPath[i]!=0 ){
          134  +      char *zName, *zValue;
          135  +      zName = &g.urlPath[i];
          136  +      zValue = zName;
          137  +      while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; }
          138  +      if( g.urlPath[i]=='=' ){
          139  +        g.urlPath[i] = 0;
          140  +        i++;
          141  +        zValue = &g.urlPath[i];
          142  +        while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; }
          143  +      }
          144  +      if( g.urlPath[i] ){
          145  +        g.urlPath[i] = 0;
          146  +        i++;
          147  +      }
          148  +      if( fossil_strcmp(zName,"fossil")==0 ){
          149  +        g.urlFossil = zValue;
          150  +        dehttpize(g.urlFossil);
          151  +        zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
          152  +        cQuerySep = '&';
          153  +      }
          154  +      if( fossil_strcmp(zName,"shell")==0 ){
          155  +        g.urlShell = zValue;
          156  +        dehttpize(g.urlShell);
          157  +        zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil);
          158  +        cQuerySep = '&';
          159  +      }
          160  +    }
          161  +
    94    162       dehttpize(g.urlPath);
    95    163       if( g.urlDfltPort==g.urlPort ){
    96    164         g.urlCanonical = mprintf(
    97         -        "%s://%s%T%T", 
    98         -        g.urlProtocol, zLogin, g.urlName, g.urlPath
          165  +        "%s://%s%T%T%s", 
          166  +        g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe
    99    167         );
   100    168       }else{
   101    169         g.urlCanonical = mprintf(
   102         -        "%s://%s%T:%d%T",
   103         -        g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
          170  +        "%s://%s%T:%d%T%s",
          171  +        g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe
   104    172         );
   105    173       }
          174  +    if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++;
   106    175       free(zLogin);
   107    176     }else if( strncmp(zUrl, "file:", 5)==0 ){
   108    177       g.urlIsFile = 1;
   109    178       if( zUrl[5]=='/' && zUrl[6]=='/' ){
   110    179         i = 7;
   111    180       }else{
   112    181         i = 5;
................................................................................
   125    194       }
   126    195     }else{
   127    196       fossil_panic("unknown repository: %s", zUrl);
   128    197     }
   129    198     if( g.urlIsFile ){
   130    199       Blob cfile;
   131    200       dehttpize(zFile);  
   132         -    file_canonical_name(zFile, &cfile);
          201  +    file_canonical_name(zFile, &cfile, 0);
   133    202       free(zFile);
   134    203       g.urlProtocol = "file";
   135    204       g.urlPath = "";
   136    205       g.urlName = mprintf("%b", &cfile);
   137    206       g.urlCanonical = mprintf("file://%T", g.urlName);
   138    207       blob_reset(&cfile);
   139    208     }
................................................................................
   146    215     int i;
   147    216     url_proxy_options();
   148    217     if( g.argc!=3 && g.argc!=4 ){
   149    218       usage("URL");
   150    219     }
   151    220     url_parse(g.argv[2]);
   152    221     for(i=0; i<2; i++){
   153         -    printf("g.urlIsFile    = %d\n", g.urlIsFile);
   154         -    printf("g.urlIsHttps   = %d\n", g.urlIsHttps);
   155         -    printf("g.urlProtocol  = %s\n", g.urlProtocol);
   156         -    printf("g.urlName      = %s\n", g.urlName);
   157         -    printf("g.urlPort      = %d\n", g.urlPort);
   158         -    printf("g.urlDfltPort  = %d\n", g.urlDfltPort);
   159         -    printf("g.urlHostname  = %s\n", g.urlHostname);
   160         -    printf("g.urlPath      = %s\n", g.urlPath);
   161         -    printf("g.urlUser      = %s\n", g.urlUser);
   162         -    printf("g.urlPasswd    = %s\n", g.urlPasswd);
   163         -    printf("g.urlCanonical = %s\n", g.urlCanonical);
          222  +    fossil_print("g.urlIsFile    = %d\n", g.urlIsFile);
          223  +    fossil_print("g.urlIsHttps   = %d\n", g.urlIsHttps);
          224  +    fossil_print("g.urlIsSsh     = %d\n", g.urlIsSsh);
          225  +    fossil_print("g.urlProtocol  = %s\n", g.urlProtocol);
          226  +    fossil_print("g.urlName      = %s\n", g.urlName);
          227  +    fossil_print("g.urlPort      = %d\n", g.urlPort);
          228  +    fossil_print("g.urlDfltPort  = %d\n", g.urlDfltPort);
          229  +    fossil_print("g.urlHostname  = %s\n", g.urlHostname);
          230  +    fossil_print("g.urlPath      = %s\n", g.urlPath);
          231  +    fossil_print("g.urlUser      = %s\n", g.urlUser);
          232  +    fossil_print("g.urlPasswd    = %s\n", g.urlPasswd);
          233  +    fossil_print("g.urlCanonical = %s\n", g.urlCanonical);
          234  +    fossil_print("g.urlFossil    = %s\n", g.urlFossil);
          235  +    if( g.urlIsFile || g.urlIsSsh ) break;
   164    236       if( i==0 ){
   165         -      printf("********\n");
          237  +      fossil_print("********\n");
   166    238         url_enable_proxy("Using proxy: ");
   167    239       }
   168    240     }
   169    241   }
   170    242   
   171    243   /*
   172    244   ** Proxy specified on the command-line using the --proxy option.
................................................................................
   200    272   */
   201    273   void url_enable_proxy(const char *zMsg){
   202    274     const char *zProxy;
   203    275     zProxy = zProxyOpt;
   204    276     if( zProxy==0 ){
   205    277       zProxy = db_get("proxy", 0);
   206    278       if( zProxy==0 || zProxy[0]==0 || is_truth(zProxy) ){
   207         -      zProxy = getenv("http_proxy");
          279  +      zProxy = fossil_getenv("http_proxy");
   208    280       }
   209    281     }
   210    282     if( zProxy && zProxy[0] && !is_false(zProxy) ){
   211    283       char *zOriginalUrl = g.urlCanonical;
   212    284       char *zOriginalHost = g.urlHostname;
   213    285       char *zOriginalUser = g.urlUser;
   214    286       char *zOriginalPasswd = g.urlPasswd;
   215    287       g.urlUser = 0;
   216    288       g.urlPasswd = "";
   217    289       url_parse(zProxy);
   218         -    if( zMsg ) printf("%s%s\n", zMsg, g.urlCanonical);
          290  +    if( zMsg ) fossil_print("%s%s\n", zMsg, g.urlCanonical);
   219    291       g.urlPath = zOriginalUrl;
   220    292       g.urlHostname = zOriginalHost;
   221    293       if( g.urlUser ){
   222    294         char *zCredentials1 = mprintf("%s:%s", g.urlUser, g.urlPasswd);
   223    295         char *zCredentials2 = encode64(zCredentials1, -1);
   224    296         g.urlProxyAuth = mprintf("Basic %z", zCredentials2);
   225    297         free(zCredentials1);
................................................................................
   233    305   /*
   234    306   ** An instance of this object is used to build a URL with query parameters.
   235    307   */
   236    308   struct HQuery {
   237    309     Blob url;                  /* The URL */
   238    310     const char *zBase;         /* The base URL */
   239    311     int nParam;                /* Number of parameters.  Max 10 */
   240         -  const char *azName[10];    /* Parameter names */
   241         -  const char *azValue[10];   /* Parameter values */
          312  +  const char *azName[15];    /* Parameter names */
          313  +  const char *azValue[15];   /* Parameter values */
   242    314   };
   243    315   #endif
   244    316   
   245    317   /*
   246    318   ** Initialize the URL object.
   247    319   */
   248    320   void url_initialize(HQuery *p, const char *zBase){
................................................................................
   272    344     const char *zName2,     /* Second override */
   273    345     const char *zValue2     /* Second override value */
   274    346   ){
   275    347     const char *zSep = "?";
   276    348     int i;
   277    349     
   278    350     blob_reset(&p->url);
   279         -  blob_appendf(&p->url, "%s/%s", g.zBaseURL, p->zBase);
          351  +  blob_appendf(&p->url, "%s/%s", g.zTop, p->zBase);
   280    352     for(i=0; i<p->nParam; i++){
   281    353       const char *z = p->azValue[i];
   282         -    if( zName1 && strcmp(zName1,p->azName[i])==0 ){
          354  +    if( zName1 && fossil_strcmp(zName1,p->azName[i])==0 ){
   283    355         zName1 = 0;
   284    356         z = zValue1;
   285    357         if( z==0 ) continue;
   286    358       }
   287         -    if( zName2 && strcmp(zName2,p->azName[i])==0 ){
          359  +    if( zName2 && fossil_strcmp(zName2,p->azName[i])==0 ){
   288    360         zName2 = 0;
   289    361         z = zValue2;
   290    362         if( z==0 ) continue;
   291    363       }
   292         -    blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
          364  +    blob_appendf(&p->url, "%s%s", zSep, p->azName[i]);
          365  +    if( z && z[0] ) blob_appendf(&p->url, "=%T", z);
   293    366       zSep = "&";
   294    367     }
   295    368     if( zName1 && zValue1 ){
   296         -    blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
          369  +    blob_appendf(&p->url, "%s%s", zSep, zName1);
          370  +    if( zValue1[0] ) blob_appendf(&p->url, "=%T", zValue1);
   297    371     }
   298    372     if( zName2 && zValue2 ){
   299         -    blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2);
          373  +    blob_appendf(&p->url, "%s%s", zSep, zName2);
          374  +    if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2);
   300    375     }
   301    376     return blob_str(&p->url);
   302    377   }
   303    378   
   304    379   /*
   305    380   ** Prompt the user for the password for g.urlUser.  Store the result
   306    381   ** in g.urlPasswd.
   307    382   */
   308    383   void url_prompt_for_password(void){
   309    384     if( isatty(fileno(stdin)) ){
   310         -    char *zPrompt = mprintf("password for %s: ", g.urlUser);
          385  +    char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
   311    386       Blob x;
   312    387       prompt_for_password(zPrompt, &x, 0);
   313    388       free(zPrompt);
   314    389       g.urlPasswd = mprintf("%b", &x);
   315    390       blob_reset(&x);
   316    391     }else{
   317    392       fossil_fatal("missing or incorrect password for user \"%s\"",
   318    393                    g.urlUser);
   319    394     }
   320    395   }
          396  +
          397  +/* Preemptively prompt for a password if a username is given in the
          398  +** URL but no password.
          399  +*/
          400  +void url_get_password_if_needed(void){
          401  +  if( (g.urlUser && g.urlUser[0])
          402  +   && (g.urlPasswd==0 || g.urlPasswd[0]==0)
          403  +   && isatty(fileno(stdin)) 
          404  +   && g.urlIsSsh==0
          405  +  ){
          406  +    url_prompt_for_password();
          407  +  }
          408  +}

Changes to src/user.c.

    25     25   /*
    26     26   ** Strip leading and trailing space from a string and add the string
    27     27   ** onto the end of a blob.
    28     28   */
    29     29   static void strip_string(Blob *pBlob, char *z){
    30     30     int i;
    31     31     blob_reset(pBlob);
    32         -  while( isspace(*z) ){ z++; }
           32  +  while( fossil_isspace(*z) ){ z++; }
    33     33     for(i=0; z[i]; i++){
    34     34       if( z[i]=='\r' || z[i]=='\n' ){
    35         -       while( i>0 && isspace(z[i-1]) ){ i--; }
           35  +       while( i>0 && fossil_isspace(z[i-1]) ){ i--; }
    36     36          z[i] = 0;
    37     37          break;
    38     38       }
    39     39       if( z[i]<' ' ) z[i] = ' ';
    40     40     }
    41     41     blob_append(pBlob, z, -1);
    42     42   }
    43     43   
           44  +#if defined(_WIN32)
    44     45   #ifdef __MINGW32__
           46  +#include <conio.h>
           47  +#endif
    45     48   /*
    46     49   ** getpass for Windows
    47     50   */
    48     51   static char *getpass(const char *prompt){
    49     52     static char pwd[64];
    50     53     size_t i;
    51     54   
................................................................................
   110    113     Blob secondTry;
   111    114     blob_zero(pPassphrase);
   112    115     blob_zero(&secondTry);
   113    116     while(1){
   114    117       prompt_for_passphrase(zPrompt, pPassphrase);
   115    118       if( verify==0 ) break;
   116    119       if( verify==1 && blob_size(pPassphrase)==0 ) break;
   117         -    prompt_for_passphrase("Again: ", &secondTry);
          120  +    prompt_for_passphrase("Retype new password: ", &secondTry);
   118    121       if( blob_compare(pPassphrase, &secondTry) ){
   119         -      printf("Passphrases do not match.  Try again...\n");
          122  +      fossil_print("Passphrases do not match.  Try again...\n");
   120    123       }else{
   121    124         break;
   122    125       }
   123    126     }
   124    127     blob_reset(&secondTry);
   125    128   }
   126    129   
................................................................................
   127    130   /*
   128    131   ** Prompt the user to enter a single line of text.
   129    132   */
   130    133   void prompt_user(const char *zPrompt, Blob *pIn){
   131    134     char *z;
   132    135     char zLine[1000];
   133    136     blob_zero(pIn);
   134         -  printf("%s", zPrompt);
          137  +  fossil_force_newline();
          138  +  fossil_print("%s", zPrompt);
   135    139     fflush(stdout);
   136    140     z = fgets(zLine, sizeof(zLine), stdin);
   137    141     if( z ){
   138    142       strip_string(pIn, z);
   139    143     }
   140    144   }
   141    145   
   142    146   
   143    147   /*
   144         -** COMMAND:  user
          148  +** COMMAND: user*
   145    149   **
   146    150   ** Usage: %fossil user SUBCOMMAND ...  ?-R|--repository FILE?
   147    151   **
   148    152   ** Run various subcommands on users of the open repository or of
   149    153   ** the repository identified by the -R or --repository option.
   150    154   **
   151    155   **    %fossil user capabilities USERNAME ?STRING?
................................................................................
   169    173   **
   170    174   **    %fossil user password USERNAME ?PASSWORD?
   171    175   **
   172    176   **        Change the web access password for a user.
   173    177   */
   174    178   void user_cmd(void){
   175    179     int n;
   176         -  db_find_and_open_repository(1);
          180  +  db_find_and_open_repository(0, 0);
   177    181     if( g.argc<3 ){
   178    182       usage("capabilities|default|list|new|password ...");
   179    183     }
   180    184     n = strlen(g.argv[2]);
   181    185     if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
   182         -    Blob passwd, login, contact;
          186  +    Blob passwd, login, caps, contact;
   183    187       char *zPw;
          188  +    blob_init(&caps, db_get("default-perms", "u"), -1);
   184    189   
   185    190       if( g.argc>=4 ){
   186    191         blob_init(&login, g.argv[3], -1);
   187    192       }else{
   188    193         prompt_user("login: ", &login);
   189    194       }
   190    195       if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
................................................................................
   196    201         prompt_user("contact-info: ", &contact);
   197    202       }
   198    203       if( g.argc>=6 ){
   199    204         blob_init(&passwd, g.argv[5], -1);
   200    205       }else{
   201    206         prompt_for_password("password: ", &passwd, 1);
   202    207       }
   203         -    zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
          208  +    zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
   204    209       db_multi_exec(
   205         -      "INSERT INTO user(login,pw,cap,info)"
   206         -      "VALUES(%B,%Q,'v',%B)",
   207         -      &login, zPw, &contact
          210  +      "INSERT INTO user(login,pw,cap,info,mtime)"
          211  +      "VALUES(%B,%Q,%B,%B,now())",
          212  +      &login, zPw, &caps, &contact
   208    213       );
   209    214       free(zPw);
   210    215     }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
   211    216       user_select();
   212    217       if( g.argc==3 ){
   213         -      printf("%s\n", g.zLogin);
          218  +      fossil_print("%s\n", g.zLogin);
   214    219       }else{
   215    220         if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
   216    221           fossil_fatal("no such user: %s", g.argv[3]);
   217    222         }
   218    223         if( g.localOpen ){
   219    224           db_lset("default-user", g.argv[3]);
   220    225         }else{
................................................................................
   221    226           db_set("default-user", g.argv[3], 0);
   222    227         }
   223    228       }
   224    229     }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){
   225    230       Stmt q;
   226    231       db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
   227    232       while( db_step(&q)==SQLITE_ROW ){
   228         -      printf("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
          233  +      fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
   229    234       }
   230    235       db_finalize(&q);
   231    236     }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){
   232    237       char *zPrompt;
   233    238       int uid;
   234    239       Blob pw;
   235    240       if( g.argc!=4 && g.argc!=5 ) usage("password USERNAME ?NEW-PASSWORD?");
................................................................................
   236    241       uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
   237    242       if( uid==0 ){
   238    243         fossil_fatal("no such user: %s", g.argv[3]);
   239    244       }
   240    245       if( g.argc==5 ){
   241    246         blob_init(&pw, g.argv[4], -1);
   242    247       }else{
   243         -      zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
          248  +      zPrompt = mprintf("New password for %s: ", g.argv[3]);
   244    249         prompt_for_password(zPrompt, &pw, 1);
   245    250       }
   246    251       if( blob_size(&pw)==0 ){
   247         -      printf("password unchanged\n");
          252  +      fossil_print("password unchanged\n");
   248    253       }else{
   249         -      char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
   250         -      db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
          254  +      char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
          255  +      db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d",
          256  +                    zSecret, uid);
   251    257         free(zSecret);
   252    258       }
   253    259     }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
   254    260       int uid;
   255    261       if( g.argc!=4 && g.argc!=5 ){
   256    262         usage("user capabilities USERNAME ?PERMISSIONS?");
   257    263       }
   258    264       uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
   259    265       if( uid==0 ){
   260    266         fossil_fatal("no such user: %s", g.argv[3]);
   261    267       }
   262    268       if( g.argc==5 ){
   263    269         db_multi_exec(
   264         -        "UPDATE user SET cap=%Q WHERE uid=%d", g.argv[4],
   265         -        uid
          270  +        "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d",
          271  +        g.argv[4], uid
   266    272         );
   267    273       }
   268         -    printf("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid));
          274  +    fossil_print("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid));
   269    275     }else{
   270    276       fossil_panic("user subcommand should be one of: "
   271    277                    "capabilities default list new password");
   272    278     }
   273    279   }
   274    280   
   275    281   /*
................................................................................
   295    301   **
   296    302   **   (1)  Use the --user and -U command-line options.
   297    303   **
   298    304   **   (2)  If the local database is open, check in VVAR.
   299    305   **
   300    306   **   (3)  Check the default user in the repository
   301    307   **
   302         -**   (4)  Try the USER environment variable.
          308  +**   (4)  Try the FOSSIL_USER environment variable.
          309  +**
          310  +**   (5)  Try the USER environment variable.
          311  +**
          312  +**   (6)  Try the USERNAME environment variable.
   303    313   **
   304         -**   (5)  Use the first user in the USER table.
          314  +**   (7)  Check if the user can be extracted from the remote URL.
   305    315   **
   306    316   ** The user name is stored in g.zLogin.  The uid is in g.userUid.
   307    317   */
   308    318   void user_select(void){
   309         -  Stmt s;
          319  +  char *zUrl;
   310    320   
   311    321     if( g.userUid ) return;
   312         -  if( attempt_user(g.zLogin) ) return;
          322  +  if( g.zLogin ){
          323  +    if( attempt_user(g.zLogin)==0 ){
          324  +      fossil_fatal("no such user: %s", g.zLogin);
          325  +    }else{
          326  +      return;
          327  +    }
          328  +  }
   313    329   
   314    330     if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
   315    331   
   316    332     if( attempt_user(db_get("default-user", 0)) ) return;
   317    333   
   318         -  if( attempt_user(getenv("USER")) ) return;
          334  +  if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return;
          335  +
          336  +  if( attempt_user(fossil_getenv("USER")) ) return;
   319    337   
   320         -  db_prepare(&s,
   321         -    "SELECT uid, login FROM user"
   322         -    " WHERE login NOT IN ('anonymous','nobody','reader','developer')"
   323         -  );
   324         -  if( db_step(&s)==SQLITE_ROW ){
   325         -    g.userUid = db_column_int(&s, 0);
   326         -    g.zLogin = mprintf("%s", db_column_text(&s, 1));
   327         -  }
   328         -  db_finalize(&s);
          338  +  if( attempt_user(fossil_getenv("USERNAME")) ) return;
   329    339   
   330         -  if( g.userUid==0 ){
   331         -    db_prepare(&s, "SELECT uid, login FROM user");
   332         -    if( db_step(&s)==SQLITE_ROW ){
   333         -      g.userUid = db_column_int(&s, 0);
   334         -      g.zLogin = mprintf("%s", db_column_text(&s, 1));
   335         -    }
   336         -    db_finalize(&s);
          340  +  zUrl = db_get("last-sync-url", 0);
          341  +  if( zUrl ){
          342  +    url_parse(zUrl);
          343  +    if( attempt_user(g.urlUser) ) return;
   337    344     }
   338    345   
   339         -  if( g.userUid==0 ){
   340         -    db_multi_exec(
   341         -      "INSERT INTO user(login, pw, cap, info)"
   342         -      "VALUES('anonymous', '', 'cfghjkmnoqw', '')"
   343         -    );
   344         -    g.userUid = db_last_insert_rowid();
   345         -    g.zLogin = "anonymous";
   346         -  }
          346  +  fossil_print(
          347  +    "Cannot figure out who you are!  Consider using the --user\n"
          348  +    "command line option, setting your USER environment variable,\n"
          349  +    "or setting a default user with \"fossil user default USER\".\n"
          350  +  );
          351  +  fossil_fatal("cannot determine user");
   347    352   }
   348    353   
   349         -/*
   350         -** Compute the shared secret for a user.
   351         -*/
   352         -static void user_sha1_shared_secret_func(
   353         -  sqlite3_context *context,
   354         -  int argc,
   355         -  sqlite3_value **argv
   356         -){
   357         -  char *zPw;
   358         -  char *zLogin;
   359         -  assert( argc==2 );
   360         -  zPw = (char*)sqlite3_value_text(argv[0]);
   361         -  zLogin = (char*)sqlite3_value_text(argv[1]);
   362         -  if( zPw && zLogin ){ 
   363         -    sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
   364         -  }
   365         -}
   366    354   
   367    355   /*
   368    356   ** COMMAND: test-hash-passwords
   369    357   **
   370    358   ** Usage: %fossil test-hash-passwords REPOSITORY
   371    359   **
   372    360   ** Convert all local password storage to use a SHA1 hash of the password
   373    361   ** rather than cleartext.  Passwords that are already stored as the SHA1
   374    362   ** has are unchanged.
   375    363   */
   376    364   void user_hash_passwords_cmd(void){
   377    365     if( g.argc!=3 ) usage("REPOSITORY");
   378    366     db_open_repository(g.argv[2]);
   379         -  sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
   380         -                          user_sha1_shared_secret_func, 0, 0);
          367  +  sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0,
          368  +                          sha1_shared_secret_sql_function, 0, 0);
   381    369     db_multi_exec(
   382         -    "UPDATE user SET pw=sha1_shared_secret(pw,login)"
          370  +    "UPDATE user SET pw=shared_secret(pw,login), mtime=now()"
   383    371       " WHERE length(pw)>0 AND length(pw)!=40"
   384    372     );
   385    373   }
          374  +
          375  +/*
          376  +** WEBPAGE: access_log
          377  +**
          378  +**    y=N      1: success only.  2: failure only.  3: both
          379  +**    n=N      Number of entries to show
          380  +**    o=N      Skip this many entries
          381  +*/
          382  +void access_log_page(void){
          383  +  int y = atoi(PD("y","3"));
          384  +  int n = atoi(PD("n","50"));
          385  +  int skip = atoi(PD("o","0"));
          386  +  Blob sql;
          387  +  Stmt q;
          388  +  int cnt = 0;
          389  +  int rc;
          390  +
          391  +  login_check_credentials();
          392  +  if( !g.perm.Admin ){ login_needed(); return; }
          393  +  create_accesslog_table();
          394  +
          395  +  if( P("delall") && P("delallbtn") ){
          396  +    db_multi_exec("DELETE FROM accesslog");
          397  +    cgi_redirectf("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
          398  +    return;
          399  +  }
          400  +  if( P("delanon") && P("delanonbtn") ){
          401  +    db_multi_exec("DELETE FROM accesslog WHERE uname='anonymous'");
          402  +    cgi_redirectf("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
          403  +    return;
          404  +  }
          405  +  if( P("delfail") && P("delfailbtn") ){
          406  +    db_multi_exec("DELETE FROM accesslog WHERE NOT success");
          407  +    cgi_redirectf("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
          408  +    return;
          409  +  }
          410  +  if( P("delold") && P("deloldbtn") ){
          411  +    db_multi_exec("DELETE FROM accesslog WHERE rowid in"
          412  +                  "(SELECT rowid FROM accesslog ORDER BY rowid DESC"
          413  +                  " LIMIT -1 OFFSET 200)");
          414  +    cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
          415  +    return;
          416  +  }
          417  +  style_header("Access Log");
          418  +  blob_zero(&sql);
          419  +  blob_append(&sql, 
          420  +    "SELECT uname, ipaddr, datetime(mtime, 'localtime'), success"
          421  +    "  FROM accesslog", -1
          422  +  );
          423  +  if( y==1 ){
          424  +    blob_append(&sql, "  WHERE success", -1);
          425  +  }else if( y==2 ){
          426  +    blob_append(&sql, "  WHERE NOT success", -1);
          427  +  }
          428  +  blob_appendf(&sql,"  ORDER BY rowid DESC LIMIT %d OFFSET %d", n+1, skip);
          429  +  if( skip ){
          430  +    style_submenu_element("Newer", "Newer entries",
          431  +              "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0,
          432  +              n, y);
          433  +  }
          434  +  rc = db_prepare_ignore_error(&q, blob_str(&sql));
          435  +  @ <center><table border="1" cellpadding="5">
          436  +  @ <tr><th width="33%%">Date</th><th width="34%%">User</th>
          437  +  @ <th width="33%%">IP Address</th></tr>
          438  +  while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){
          439  +    const char *zName = db_column_text(&q, 0);
          440  +    const char *zIP = db_column_text(&q, 1);
          441  +    const char *zDate = db_column_text(&q, 2);
          442  +    int bSuccess = db_column_int(&q, 3);
          443  +    cnt++;
          444  +    if( cnt>n ){
          445  +      style_submenu_element("Older", "Older entries",
          446  +                  "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip+n, n, y);
          447  +      break;
          448  +    }
          449  +    if( bSuccess ){
          450  +      @ <tr>
          451  +    }else{
          452  +      @ <tr bgcolor="#ffacc0">
          453  +    }
          454  +    @ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr>
          455  +  }
          456  +  if( skip>0 || cnt>n ){
          457  +    style_submenu_element("All", "All entries",
          458  +          "%s/access_log?n=10000000", g.zTop);
          459  +  }
          460  +  @ </table></center>
          461  +  db_finalize(&q);
          462  +  @ <hr>
          463  +  @ <form method="post" action="%s(g.zTop)/access_log">
          464  +  @ <label><input type="checkbox" name="delold">
          465  +  @ Delete all but the most recent 200 entries</input></label>
          466  +  @ <input type="submit" name="deloldbtn" value="Delete"></input>
          467  +  @ </form>
          468  +  @ <form method="post" action="%s(g.zTop)/access_log">
          469  +  @ <label><input type="checkbox" name="delanon">
          470  +  @ Delete all entries for user "anonymous"</input></label>
          471  +  @ <input type="submit" name="delanonbtn" value="Delete"></input>
          472  +  @ </form>
          473  +  @ <form method="post" action="%s(g.zTop)/access_log">
          474  +  @ <label><input type="checkbox" name="delfail">
          475  +  @ Delete all failed login attempts</input></label>
          476  +  @ <input type="submit" name="delfailbtn" value="Delete"></input>
          477  +  @ </form>
          478  +  @ <form method="post" action="%s(g.zTop)/access_log">
          479  +  @ <label><input type="checkbox" name="delall">
          480  +  @ Delete all entries</input></label>
          481  +  @ <input type="submit" name="delallbtn" value="Delete"></input>
          482  +  @ </form>
          483  +  style_footer();
          484  +}

Added src/utf8.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains utilities for converting text between UTF-8 (which
           19  +** is always used internally) and whatever encodings are used by the underlying
           20  +** filesystem and operating system.
           21  +*/
           22  +#include "config.h"
           23  +#include "utf8.h"
           24  +#include <sqlite3.h>
           25  +#ifdef _WIN32
           26  +# include <windows.h>
           27  +#endif
           28  +
           29  +/*
           30  +** Translate MBCS to UTF8.  Return a pointer to the translated text.
           31  +** Call fossil_mbcs_free() to deallocate any memory used to store the
           32  +** returned pointer when done.
           33  +*/
           34  +char *fossil_mbcs_to_utf8(const char *zMbcs){
           35  +#ifdef _WIN32
           36  +  extern char *sqlite3_win32_mbcs_to_utf8(const char*);
           37  +  return sqlite3_win32_mbcs_to_utf8(zMbcs);
           38  +#else
           39  +  return (char*)zMbcs;  /* No-op on unix */
           40  +#endif
           41  +}
           42  +
           43  +/*
           44  +** After translating from UTF8 to MBCS, invoke this routine to deallocate
           45  +** any memory used to hold the translation
           46  +*/
           47  +void fossil_mbcs_free(char *zOld){
           48  +#ifdef _WIN32
           49  +  sqlite3_free(zOld);
           50  +#else
           51  +  /* No-op on unix */
           52  +#endif
           53  +}
           54  +
           55  +/*
           56  +** Translate Unicode text into UTF8.
           57  +** Return a pointer to the translated text.
           58  +** Call fossil_unicode_free() to deallocate any memory used to store the
           59  +** returned pointer when done.
           60  +*/
           61  +char *fossil_unicode_to_utf8(const void *zUnicode){
           62  +#ifdef _WIN32
           63  +  int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0);
           64  +  char *zUtf = sqlite3_malloc( nByte );
           65  +  if( zUtf==0 ){
           66  +    return 0;
           67  +  }
           68  +  WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0);
           69  +  return zUtf;
           70  +#else
           71  +  return (char *)zUnicode;  /* No-op on unix */
           72  +#endif
           73  +}
           74  +
           75  +/*
           76  +** Translate UTF8 to unicode for use in system calls.  Return a pointer to the
           77  +** translated text..  Call fossil_unicode_free() to deallocate any memory
           78  +** used to store the returned pointer when done.
           79  +*/
           80  +void *fossil_utf8_to_unicode(const char *zUtf8){
           81  +#ifdef _WIN32
           82  +  int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
           83  +  wchar_t *zUnicode = sqlite3_malloc( nByte * 2 );
           84  +  if( zUnicode==0 ){
           85  +    return 0;
           86  +  }
           87  +  MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte);
           88  +  return zUnicode;
           89  +#else
           90  +  return (void *)zUtf8;  /* No-op on unix */
           91  +#endif
           92  +}
           93  +
           94  +/*
           95  +** Deallocate any memory that was previously allocated by
           96  +** fossil_unicode_to_utf8().
           97  +*/
           98  +void fossil_unicode_free(void *pOld){
           99  +#ifdef _WIN32
          100  +  sqlite3_free(pOld);
          101  +#else
          102  +  /* No-op on unix */
          103  +#endif
          104  +}
          105  +
          106  +#if defined(__APPLE__) && !defined(WITHOUT_ICONV)
          107  +# include <iconv.h>
          108  +#endif
          109  +
          110  +/*
          111  +** Translate text from the filename character set into
          112  +** to precomposed UTF8.  Return a pointer to the translated text.
          113  +** Call fossil_filename_free() to deallocate any memory used to store the
          114  +** returned pointer when done.
          115  +*/
          116  +char *fossil_filename_to_utf8(const void *zFilename){
          117  +#if defined(_WIN32)
          118  +  int nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0);
          119  +  char *zUtf = sqlite3_malloc( nByte );
          120  +  if( zUtf==0 ){
          121  +    return 0;
          122  +  }
          123  +  WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0);
          124  +  return zUtf;
          125  +#elif defined(__APPLE__) && !defined(WITHOUT_ICONV)
          126  +  char *zIn = (char*)zFilename;
          127  +  char *zOut;
          128  +  iconv_t cd;
          129  +  size_t n, x;
          130  +  for(n=0; zIn[n]>0 && zIn[n]<=0x7f; n++){}
          131  +  if( zIn[n]!=0 && (cd = iconv_open("UTF-8", "UTF-8-MAC"))!=(iconv_t)-1 ){
          132  +    char *zOutx;
          133  +    char *zOrig = zIn;
          134  +    size_t nIn, nOutx;
          135  +    nIn = n = strlen(zIn);
          136  +    nOutx = nIn+100;
          137  +    zOutx = zOut = fossil_malloc( nOutx+1 );
          138  +    x = iconv(cd, &zIn, &nIn, &zOutx, &nOutx);
          139  +    if( x==(size_t)-1 ){
          140  +      fossil_free(zOut);
          141  +      zOut = fossil_strdup(zOrig);
          142  +    }else{
          143  +      zOut[n+100-nOutx] = 0;
          144  +    }
          145  +    iconv_close(cd);
          146  +  }else{
          147  +    zOut = fossil_strdup(zFilename);
          148  +  }
          149  +  return zOut;
          150  +#else
          151  +  return (char *)zFilename;  /* No-op on non-mac unix */
          152  +#endif
          153  +}
          154  +
          155  +/*
          156  +** Deallocate any memory that was previously allocated by
          157  +** fossil_filename_to_utf8().
          158  +*/
          159  +void fossil_filename_free(char *pOld){
          160  +#if defined(_WIN32)
          161  +  sqlite3_free(pOld);
          162  +#elif defined(__APPLE__) && !defined(WITHOUT_ICONV)
          163  +  fossil_free(pOld);
          164  +#else
          165  +  /* No-op on all other unix */
          166  +#endif
          167  +}
          168  +
          169  +/*
          170  +** Display UTF8 on the console.  Return the number of
          171  +** Characters written. If stdout or stderr is redirected
          172  +** to a file, -1 is returned and nothing is written
          173  +** to the console.
          174  +*/
          175  +int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){
          176  +#ifdef _WIN32
          177  +  int nChar;
          178  +  wchar_t *zUnicode; /* Unicode version of zUtf8 */
          179  +  DWORD dummy;
          180  +
          181  +  static int istty[2] = { -1, -1 };
          182  +  if( istty[toStdErr] == -1 ){
          183  +    istty[toStdErr] = _isatty(toStdErr + 1) != 0;
          184  +  }
          185  +  if( !istty[toStdErr] ){
          186  +    /* stdout/stderr is not a console. */
          187  +    return -1;
          188  +  }
          189  +
          190  +  nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, nByte, NULL, 0);
          191  +  zUnicode = malloc( (nChar + 1) *sizeof(zUnicode[0]) );
          192  +  if( zUnicode==0 ){
          193  +    return 0;
          194  +  }
          195  +  nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, nByte, zUnicode, nChar);
          196  +  if( nChar==0 ){
          197  +    free(zUnicode);
          198  +    return 0;
          199  +  }
          200  +  zUnicode[nChar] = '\0';
          201  +  WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE - toStdErr), zUnicode, nChar,
          202  +                &dummy, 0);
          203  +  return nChar;
          204  +#else
          205  +  return -1;  /* No-op on unix */
          206  +#endif
          207  +}

Changes to src/verify.c.

    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18         -** This file contains code used to help verify the integrity of the
           18  +** This file contains code used to help verify the integrity of
    19     19   ** the repository.
    20     20   **
    21     21   ** This file primarily implements the verify_before_commit() interface.
    22     22   ** Any function can call verify_before_commit() with a record id (RID)
    23     23   ** as an argument.  Then before the next change to the database commits,
    24     24   ** this routine will reach in and check that the record can be extracted
    25     25   ** correctly from the BLOB table.
................................................................................
    33     33   ** without error.
    34     34   **
    35     35   ** Panic if anything goes wrong.  If this procedure returns it means
    36     36   ** that everything is OK.
    37     37   */
    38     38   static void verify_rid(int rid){
    39     39     Blob uuid, hash, content;
    40         -  if( db_int(0, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
           40  +  if( content_size(rid, 0)<0 ){
    41     41       return;  /* No way to verify phantoms */
    42     42     }
    43     43     blob_zero(&uuid);
    44     44     db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
    45     45     if( blob_size(&uuid)!=UUID_SIZE ){
    46     46       fossil_panic("not a valid rid: %d", rid);
    47     47     }

Changes to src/vfile.c.

    17     17   **
    18     18   ** Procedures for managing the VFILE table.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "vfile.h"
    22     22   #include <assert.h>
    23     23   #include <sys/types.h>
    24         -#include <dirent.h>
           24  +
           25  +/*
           26  +** The input is guaranteed to be a 40-character well-formed UUID.
           27  +** Find its rid.
           28  +*/
           29  +int fast_uuid_to_rid(const char *zUuid){
           30  +  static Stmt q;
           31  +  int rid;
           32  +  db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
           33  +  db_bind_text(&q, ":uuid", zUuid);
           34  +  if( db_step(&q)==SQLITE_ROW ){
           35  +    rid = db_column_int(&q, 0);
           36  +  }else{
           37  +    rid = 0;
           38  +  }
           39  +  db_reset(&q);
           40  +  return rid;
           41  +}
    25     42   
    26     43   /*
    27     44   ** Given a UUID, return the corresponding record ID.  If the UUID
    28     45   ** does not exist, then return 0.
    29     46   **
    30     47   ** For this routine, the UUID must be exact.  For a match against
    31     48   ** user input with mixed case, use resolve_uuid().
    32     49   **
    33         -** If the UUID is not found and phantomize is 1, then attempt to 
    34         -** create a phantom record.
           50  +** If the UUID is not found and phantomize is 1 or 2, then attempt to 
           51  +** create a phantom record.  A private phantom is created for 2 and
           52  +** a public phantom is created for 1.
    35     53   */
    36     54   int uuid_to_rid(const char *zUuid, int phantomize){
    37     55     int rid, sz;
    38         -  static Stmt q;
    39     56     char z[UUID_SIZE+1];
    40     57     
    41     58     sz = strlen(zUuid);
    42     59     if( sz!=UUID_SIZE || !validate16(zUuid, sz) ){
    43     60       return 0;
    44     61     }
    45         -  strcpy(z, zUuid);
           62  +  memcpy(z, zUuid, UUID_SIZE+1);
    46     63     canonical16(z, sz);
    47         -  db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
    48         -  db_bind_text(&q, ":uuid", z);
    49         -  if( db_step(&q)==SQLITE_ROW ){
    50         -    rid = db_column_int(&q, 0);
    51         -  }else{
    52         -    rid = 0;
    53         -  }
    54         -  db_reset(&q);
           64  +  rid = fast_uuid_to_rid(z);
    55     65     if( rid==0 && phantomize ){
    56         -    rid = content_new(zUuid);
           66  +    rid = content_new(zUuid, phantomize-1);
    57     67     }
    58     68     return rid;
    59     69   }
    60     70   
    61         -/*
    62         -** Verify that an object is not a phantom.  If the object is
    63         -** a phantom, output an error message and quick.
    64         -*/
    65         -void vfile_verify_not_phantom(int rid, const char *zFilename){
    66         -  if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
    67         -    if( zFilename ){
    68         -      fossil_fatal("content missing for %s", zFilename);
    69         -    }else{
    70         -      char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    71         -      if( zUuid ){
    72         -        fossil_fatal("content missing for [%.10s]", zUuid);
    73         -      }else{
    74         -        fossil_panic("bad object id: %d", rid);
    75         -      }
    76         -    }
    77         -  }
    78         -}
    79         -
    80         -/*
    81         -** Build a catalog of all files in a baseline.
    82         -** We scan the baseline file for lines of the form:
    83         -**
    84         -**     F NAME UUID
    85         -**
    86         -** Each such line makes an entry in the VFILE table.
    87         -*/
    88         -void vfile_build(int vid, Blob *p){
    89         -  int rid;
    90         -  char *zName, *zUuid;
    91         -  Stmt ins;
    92         -  Blob line, token, name, uuid;
    93         -  int seenHeader = 0;
           71  +
           72  +/*
           73  +** Load a vfile from a record ID.
           74  +*/
           75  +void load_vfile_from_rid(int vid){
           76  +  int rid, size;
           77  +  Stmt ins, ridq;
           78  +  Manifest *p;
           79  +  ManifestFile *pFile;
           80  +
           81  +  if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
           82  +    return;
           83  +  }
           84  +
    94     85     db_begin_transaction();
    95         -  vfile_verify_not_phantom(vid, 0);
           86  +  p = manifest_get(vid, CFTYPE_MANIFEST);
           87  +  if( p==0 ) return;
    96     88     db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
    97     89     db_prepare(&ins,
    98         -    "INSERT INTO vfile(vid,rid,mrid,pathname) "
    99         -    " VALUES(:vid,:id,:id,:name)");
           90  +    "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) "
           91  +    " VALUES(:vid,:isexe,:islink,:id,:id,:name)");
           92  +  db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
   100     93     db_bind_int(&ins, ":vid", vid);
   101         -  while( blob_line(p, &line) ){
   102         -    char *z = blob_buffer(&line);
   103         -    if( z[0]=='-' ){
   104         -      if( seenHeader ) break;
   105         -      while( blob_line(p, &line)>2 ){}
   106         -      if( blob_line(p, &line)==0 ) break;
   107         -    }
   108         -    seenHeader = 1;
   109         -    if( z[0]!='F' || z[1]!=' ' ) continue;
   110         -    blob_token(&line, &token);  /* Skip the "F" token */
   111         -    if( blob_token(&line, &name)==0 ) break;
   112         -    if( blob_token(&line, &uuid)==0 ) break;
   113         -    zName = blob_str(&name);
   114         -    defossilize(zName);
   115         -    zUuid = blob_str(&uuid);
   116         -    rid = uuid_to_rid(zUuid, 0);
   117         -    vfile_verify_not_phantom(rid, zName);
   118         -    if( rid>0 && file_is_simple_pathname(zName) ){
   119         -      db_bind_int(&ins, ":id", rid);
   120         -      db_bind_text(&ins, ":name", zName);
   121         -      db_step(&ins);
   122         -      db_reset(&ins);
   123         -    }
   124         -    blob_reset(&name);
   125         -    blob_reset(&uuid);
   126         -  }
           94  +  manifest_file_rewind(p);
           95  +  while( (pFile = manifest_file_next(p,0))!=0 ){
           96  +    if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
           97  +    db_bind_text(&ridq, ":uuid", pFile->zUuid);
           98  +    if( db_step(&ridq)==SQLITE_ROW ){
           99  +      rid = db_column_int(&ridq, 0);
          100  +      size = db_column_int(&ridq, 0);
          101  +    }else{
          102  +      rid = 0;
          103  +      size = 0;
          104  +    }
          105  +    db_reset(&ridq);
          106  +    if( rid==0 || size<0 ){
          107  +      fossil_warning("content missing for %s", pFile->zName);
          108  +      continue;
          109  +    }
          110  +    db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE ));
          111  +    db_bind_int(&ins, ":id", rid);
          112  +    db_bind_text(&ins, ":name", pFile->zName);
          113  +    db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK ));
          114  +    db_step(&ins);
          115  +    db_reset(&ins);
          116  +  }
          117  +  db_finalize(&ridq);
   127    118     db_finalize(&ins);
          119  +  manifest_destroy(p);
   128    120     db_end_transaction(0);
   129    121   }
   130    122   
   131         -/*
   132         -** Check the file signature of the disk image for every VFILE of vid.
   133         -**
   134         -** Set the VFILE.CHNGED field on every file that has changed.  Also 
   135         -** set VFILE.CHNGED on every folder that contains a file or folder 
   136         -** that has changed.
   137         -**
   138         -** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume
   139         -** the file has changed without having the check the on-disk image.
   140         -*/
   141         -void vfile_check_signature(int vid, int notFileIsFatal){
          123  +#if INTERFACE
          124  +/*
          125  +** The cksigFlags parameter to vfile_check_signature() is an OR-ed
          126  +** combination of the following bits:
          127  +*/
          128  +#define CKSIG_ENOTFILE  0x001   /* non-file FS objects throw an error */
          129  +#define CKSIG_SHA1      0x002   /* Verify file content using sha1sum */
          130  +#define CKSIG_SETMTIME  0x004   /* Set mtime to last check-out time */
          131  +
          132  +#endif /* INTERFACE */
          133  +
          134  +/*
          135  +** Look at every VFILE entry with the given vid and update
          136  +** VFILE.CHNGED field according to whether or not
          137  +** the file has changed.  0 means no change.  1 means edited.  2 means
          138  +** the file has changed due to a merge.  3 means the file was added
          139  +** by a merge.
          140  +**
          141  +** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was either
          142  +** removed from configuration management via "fossil rm" or added via
          143  +** "fossil add", respectively, and in both cases we always know that 
          144  +** the file has changed without having the check the size, mtime,
          145  +** or on-disk content.
          146  +**
          147  +** If the size of the file has changed, then we always know that the file
          148  +** changed without having to look at the mtime or on-disk content.
          149  +**
          150  +** The mtime of the file is only a factor if the mtime-changes setting
          151  +** is false and the useSha1sum flag is false.  If the mtime-changes
          152  +** setting is true (or undefined - it defaults to true) or if useSha1sum
          153  +** is true, then we do not trust the mtime and will examine the on-disk
          154  +** content to determine if a file really is the same.
          155  +**
          156  +** If the mtime is used, it is used only to determine if files are the same.
          157  +** If the mtime of a file has changed, we still examine the on-disk content
          158  +** to see whether or not the edit was a null-edit.
          159  +*/
          160  +void vfile_check_signature(int vid, unsigned int cksigFlags){
   142    161     int nErr = 0;
   143    162     Stmt q;
   144    163     Blob fileCksum, origCksum;
   145         -  int checkMtime = db_get_boolean("mtime-changes", 1);
          164  +  int useMtime = (cksigFlags & CKSIG_SHA1)==0
          165  +                    && db_get_boolean("mtime-changes", 1);
   146    166   
   147    167     db_begin_transaction();
   148    168     db_prepare(&q, "SELECT id, %Q || pathname,"
   149         -                 "       vfile.mrid, deleted, chnged, uuid, mtime"
          169  +                 "       vfile.mrid, deleted, chnged, uuid, size, mtime"
   150    170                    "  FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid"
   151    171                    " WHERE vid=%d ", g.zLocalRoot, vid);
   152    172     while( db_step(&q)==SQLITE_ROW ){
   153    173       int id, rid, isDeleted;
   154    174       const char *zName;
   155    175       int chnged = 0;
   156    176       int oldChnged;
   157    177       i64 oldMtime;
   158    178       i64 currentMtime;
          179  +    i64 origSize;
          180  +    i64 currentSize;
   159    181   
   160    182       id = db_column_int(&q, 0);
   161    183       zName = db_column_text(&q, 1);
   162    184       rid = db_column_int(&q, 2);
   163    185       isDeleted = db_column_int(&q, 3);
   164         -    oldChnged = db_column_int(&q, 4);
   165         -    oldMtime = db_column_int64(&q, 6);
   166         -    if( isDeleted ){
          186  +    oldChnged = chnged = db_column_int(&q, 4);
          187  +    oldMtime = db_column_int64(&q, 7);
          188  +    currentSize = file_wd_size(zName);
          189  +    origSize = db_column_int64(&q, 6);
          190  +    currentMtime = file_wd_mtime(0);
          191  +    if( chnged==0 && (isDeleted || rid==0) ){
          192  +      /* "fossil rm" or "fossil add" always change the file */
   167    193         chnged = 1;
   168         -    }else if( !file_isfile(zName) && file_size(0)>=0 ){
   169         -      if( notFileIsFatal ){
          194  +    }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){
          195  +      if( cksigFlags & CKSIG_ENOTFILE ){
   170    196           fossil_warning("not an ordinary file: %s", zName);
   171    197           nErr++;
   172    198         }
   173    199         chnged = 1;
   174         -    }else if( oldChnged>=2 ){
   175         -      chnged = oldChnged;
   176         -    }else if( rid==0 ){
   177         -      chnged = 1;
   178    200       }
   179         -    if( chnged!=1 ){
   180         -      currentMtime = file_mtime(0);
   181         -    }
   182         -    if( chnged!=1 && (checkMtime==0 || currentMtime!=oldMtime) ){
          201  +    if( origSize!=currentSize ){
          202  +      if( chnged!=1 ){
          203  +        /* A file size change is definitive - the file has changed.  No
          204  +        ** need to check the mtime or sha1sum */
          205  +        chnged = 1;
          206  +      }
          207  +    }else if( chnged==1 && rid!=0 && !isDeleted ){
          208  +      /* File is believed to have changed but it is the same size.
          209  +      ** Double check that it really has changed by looking at content. */
          210  +      assert( origSize==currentSize );
          211  +      db_ephemeral_blob(&q, 5, &origCksum);
          212  +      if( sha1sum_file(zName, &fileCksum) ){
          213  +        blob_zero(&fileCksum);
          214  +      }
          215  +      if( blob_compare(&fileCksum, &origCksum)==0 ) chnged = 0;
          216  +      blob_reset(&origCksum);
          217  +      blob_reset(&fileCksum);
          218  +    }else if( (chnged==0 || chnged==2)
          219  +           && (useMtime==0 || currentMtime!=oldMtime) ){
          220  +      /* For files that were formerly believed to be unchanged or that were
          221  +      ** changed by merging, if their mtime changes, or unconditionally
          222  +      ** if --sha1sum is used, check to see if they have been edited by
          223  +      ** looking at their SHA1 sum */
          224  +      assert( origSize==currentSize );
   183    225         db_ephemeral_blob(&q, 5, &origCksum);
   184    226         if( sha1sum_file(zName, &fileCksum) ){
   185    227           blob_zero(&fileCksum);
   186    228         }
   187    229         if( blob_compare(&fileCksum, &origCksum) ){
   188    230           chnged = 1;
   189         -      }else if( currentMtime!=oldMtime ){
   190         -        db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
   191         -                      currentMtime, id);
   192    231         }
   193    232         blob_reset(&origCksum);
   194    233         blob_reset(&fileCksum);
   195    234       }
   196         -    if( chnged!=oldChnged ){
   197         -      db_multi_exec("UPDATE vfile SET chnged=%d WHERE id=%d", chnged, id);
          235  +    if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2) ){
          236  +      i64 desiredMtime;
          237  +      if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){
          238  +        if( currentMtime!=desiredMtime ){
          239  +          file_set_mtime(zName, desiredMtime);
          240  +          currentMtime = file_wd_mtime(zName);
          241  +        }
          242  +      }
          243  +    }
          244  +    if( currentMtime!=oldMtime || chnged!=oldChnged ){
          245  +      db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d",
          246  +                    currentMtime, chnged, id);
   198    247       }
   199    248     }
   200    249     db_finalize(&q);
   201    250     if( nErr ) fossil_fatal("abort due to prior errors");
   202    251     db_end_transaction(0);
   203    252   }
   204    253   
   205    254   /*
   206    255   ** Write all files from vid to the disk.  Or if vid==0 and id!=0
   207    256   ** write just the specific file where VFILE.ID=id.
   208    257   */
   209         -void vfile_to_disk(int vid, int id, int verbose){
          258  +void vfile_to_disk(
          259  +  int vid,               /* vid to write to disk */
          260  +  int id,                /* Write this one file, if not zero */
          261  +  int verbose,           /* Output progress information */
          262  +  int promptFlag         /* Prompt user to confirm overwrites */
          263  +){
   210    264     Stmt q;
   211    265     Blob content;
   212    266     int nRepos = strlen(g.zLocalRoot);
   213    267   
   214    268     if( vid>0 && id==0 ){
   215         -    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
          269  +    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
   216    270                      "  FROM vfile"
   217    271                      " WHERE vid=%d AND mrid>0",
   218    272                      g.zLocalRoot, vid);
   219    273     }else{
   220    274       assert( vid==0 && id>0 );
   221         -    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
          275  +    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
   222    276                      "  FROM vfile"
   223    277                      " WHERE id=%d AND mrid>0",
   224    278                      g.zLocalRoot, id);
   225    279     }
   226    280     while( db_step(&q)==SQLITE_ROW ){
   227         -    int id, rid;
          281  +    int id, rid, isExe, isLink;
   228    282       const char *zName;
   229    283   
   230    284       id = db_column_int(&q, 0);
   231    285       zName = db_column_text(&q, 1);
   232    286       rid = db_column_int(&q, 2);
          287  +    isExe = db_column_int(&q, 3);
          288  +    isLink = db_column_int(&q, 4);
   233    289       content_get(rid, &content);
   234         -    if( verbose ) printf("%s\n", &zName[nRepos]);
   235         -    blob_write_to_file(&content, zName);
          290  +    if( file_is_the_same(&content, zName) ){
          291  +      blob_reset(&content);
          292  +      if( file_wd_setexe(zName, isExe) ){
          293  +        db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
          294  +                      file_wd_mtime(zName), id);
          295  +      }
          296  +      continue;
          297  +    }
          298  +    if( promptFlag && file_wd_size(zName)>=0 ){
          299  +      Blob ans;
          300  +      char *zMsg;
          301  +      char cReply;
          302  +      zMsg = mprintf("overwrite %s (a=always/y/N)? ", zName);
          303  +      prompt_user(zMsg, &ans);
          304  +      free(zMsg);
          305  +      cReply = blob_str(&ans)[0];
          306  +      blob_reset(&ans);
          307  +      if( cReply=='a' || cReply=='A' ){
          308  +        promptFlag = 0;
          309  +      } else if( cReply!='y' && cReply!='Y' ){
          310  +        blob_reset(&content);
          311  +        continue;
          312  +      }
          313  +    }
          314  +    if( verbose ) fossil_print("%s\n", &zName[nRepos]);
          315  +    if( file_wd_isdir(zName) == 1 ){
          316  +      /*TODO(dchest): remove directories? */
          317  +      fossil_fatal("%s is directory, cannot overwrite\n", zName);
          318  +    }    
          319  +    if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
          320  +      file_delete(zName);
          321  +    }
          322  +    if( isLink ){
          323  +      symlink_create(blob_str(&content), zName);
          324  +    }else{
          325  +      blob_write_to_file(&content, zName);
          326  +    }
          327  +    file_wd_setexe(zName, isExe);
   236    328       blob_reset(&content);
   237    329       db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
   238         -                  file_mtime(zName), id);
          330  +                  file_wd_mtime(zName), id);
   239    331     }
   240    332     db_finalize(&q);
   241    333   }
   242    334   
   243    335   
   244    336   /*
   245    337   ** Delete from the disk every file in VFILE vid.
................................................................................
   248    340     Stmt q;
   249    341     db_prepare(&q, "SELECT %Q || pathname FROM vfile"
   250    342                    " WHERE vid=%d AND mrid>0", g.zLocalRoot, vid);
   251    343     while( db_step(&q)==SQLITE_ROW ){
   252    344       const char *zName;
   253    345   
   254    346       zName = db_column_text(&q, 0);
   255         -    unlink(zName);
          347  +    file_delete(zName);
   256    348     }
   257    349     db_finalize(&q);
   258    350     db_multi_exec("UPDATE vfile SET mtime=NULL WHERE vid=%d AND mrid>0", vid);
   259    351   }
   260    352   
          353  +/*
          354  +** Check to see if the directory named in zPath is the top of a checkout.
          355  +** In other words, check to see if directory pPath contains a file named
          356  +** "_FOSSIL_" or ".fslckout".  Return true or false.
          357  +*/
          358  +int vfile_top_of_checkout(const char *zPath){
          359  +  char *zFile;
          360  +  int fileFound = 0;
          361  +
          362  +  zFile = mprintf("%s/_FOSSIL_", zPath);
          363  +  fileFound = file_size(zFile)>=1024;
          364  +  fossil_free(zFile);
          365  +  if( !fileFound ){
          366  +    zFile = mprintf("%s/.fslckout", zPath);
          367  +    fileFound = file_size(zFile)>=1024;
          368  +    fossil_free(zFile);
          369  +  }
          370  +
          371  +  /* Check for ".fos" for legacy support.  But the use of ".fos" as the
          372  +  ** per-checkout database name is deprecated.  At some point, all support
          373  +  ** for ".fos" will end and this code should be removed.  This comment
          374  +  ** added on 2012-02-04.
          375  +  */
          376  +  if( !fileFound ){
          377  +    zFile = mprintf("%s/.fos", zPath);
          378  +    fileFound = file_size(zFile)>=1024;
          379  +    fossil_free(zFile);
          380  +  }
          381  +  return fileFound;
          382  +}
          383  +
          384  +/*
          385  +** Return TRUE if zFile is a temporary file.  Return FALSE if not.
          386  +*/
          387  +static int is_temporary_file(const char *zName){
          388  +  static const char *const azTemp[] = {
          389  +     "baseline",
          390  +     "merge",
          391  +     "original",
          392  +     "output",
          393  +  };
          394  +  int i, j, n;
          395  +  
          396  +  if( strglob("ci-comment-????????????.txt", zName) ) return 1;
          397  +  for(; zName[0]!=0; zName++){
          398  +    if( zName[0]=='/' && strglob("/ci-comment-????????????.txt", zName) ){
          399  +      return 1;
          400  +    }
          401  +    if( zName[0]!='-' ) continue;
          402  +    for(i=0; i<sizeof(azTemp)/sizeof(azTemp[0]); i++){
          403  +      n = (int)strlen(azTemp[i]);
          404  +      if( memcmp(azTemp[i], zName+1, n) ) continue;
          405  +      if( zName[n+1]==0 ) return 1;
          406  +      if( zName[n+1]=='-' ){
          407  +        for(j=n+2; zName[j] && fossil_isdigit(zName[j]); j++){}
          408  +        if( zName[j]==0 ) return 1;
          409  +      }
          410  +    }      
          411  +  }
          412  +  return 0;
          413  +}
          414  +
          415  +#if INTERFACE
          416  +/*
          417  +** Values for the scanFlags parameter to vfile_scan().
          418  +*/
          419  +#define SCAN_ALL    0x001    /* Includes files that begin with "." */
          420  +#define SCAN_TEMP   0x002    /* Only Fossil-generated files like *-baseline */
          421  +#endif /* INTERFACE */
          422  +
   261    423   /*
   262    424   ** Load into table SFILE the name of every ordinary file in
   263    425   ** the directory pPath.   Omit the first nPrefix characters of
   264    426   ** of pPath when inserting into the SFILE table.
   265    427   **
   266    428   ** Subdirectories are scanned recursively.
   267         -** Omit files named in VFILE.vid
          429  +** Omit files named in VFILE.
          430  +**
          431  +** Files whose names begin with "." are omitted unless allFlag is true.
          432  +**
          433  +** Any files or directories that match the glob pattern pIgnore are 
          434  +** excluded from the scan.  Name matching occurs after the first
          435  +** nPrefix characters are elided from the filename.
   268    436   */
   269         -void vfile_scan(int vid, Blob *pPath, int nPrefix, int allFlag){
          437  +void vfile_scan(Blob *pPath, int nPrefix, unsigned scanFlags, Glob *pIgnore){
   270    438     DIR *d;
   271    439     int origSize;
   272    440     const char *zDir;
   273    441     struct dirent *pEntry;
   274         -  static const char *zSql = "SELECT 1 FROM vfile "
   275         -                            " WHERE pathname=%Q AND NOT deleted";
          442  +  int skipAll = 0;
          443  +  static Stmt ins;
          444  +  static int depth = 0;
          445  +  void *zNative;
   276    446   
   277    447     origSize = blob_size(pPath);
          448  +  if( pIgnore ){
          449  +    blob_appendf(pPath, "/");
          450  +    if( glob_match(pIgnore, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
          451  +    blob_resize(pPath, origSize);
          452  +  }
          453  +  if( skipAll ) return;
          454  +
          455  +  if( depth==0 ){
          456  +    db_prepare(&ins,
          457  +       "INSERT OR IGNORE INTO sfile(x) SELECT :file"
          458  +       "  WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE pathname=:file)"
          459  +    );
          460  +  }
          461  +  depth++;
          462  +
   278    463     zDir = blob_str(pPath);
   279         -  d = opendir(zDir);
          464  +  zNative = fossil_utf8_to_unicode(zDir);
          465  +  d = opendir(zNative);
   280    466     if( d ){
   281    467       while( (pEntry=readdir(d))!=0 ){
   282    468         char *zPath;
          469  +      char *zUtf8;
   283    470         if( pEntry->d_name[0]=='.' ){
   284         -        if( !allFlag ) continue;
          471  +        if( (scanFlags & SCAN_ALL)==0 ) continue;
   285    472           if( pEntry->d_name[1]==0 ) continue;
   286    473           if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
   287    474         }
   288         -      blob_appendf(pPath, "/%s", pEntry->d_name);
          475  +      zUtf8 = fossil_filename_to_utf8(pEntry->d_name);
          476  +      blob_appendf(pPath, "/%s", zUtf8);
   289    477         zPath = blob_str(pPath);
   290         -      if( file_isdir(zPath)==1 ){
   291         -        vfile_scan(vid, pPath, nPrefix, allFlag);
   292         -      }else if( file_isfile(zPath) && !db_exists(zSql, &zPath[nPrefix+1]) ){
   293         -        db_multi_exec("INSERT INTO sfile VALUES(%Q)", &zPath[nPrefix+1]);
          478  +      if( glob_match(pIgnore, &zPath[nPrefix+1]) ){
          479  +        /* do nothing */
          480  +      }else if( file_wd_isdir(zPath)==1 ){
          481  +        if( !vfile_top_of_checkout(zPath) ){
          482  +          vfile_scan(pPath, nPrefix, scanFlags, pIgnore);
          483  +        }
          484  +      }else if( file_wd_isfile_or_link(zPath) ){
          485  +        if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){
          486  +          db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
          487  +          db_step(&ins);
          488  +          db_reset(&ins);
          489  +        }
   294    490         }
          491  +      fossil_filename_free(zUtf8);
   295    492         blob_resize(pPath, origSize);
   296    493       }
          494  +    closedir(d);
   297    495     }
   298         -  closedir(d);
          496  +  fossil_unicode_free(zNative);
          497  +
          498  +  depth--;
          499  +  if( depth==0 ){
          500  +    db_finalize(&ins);
          501  +  }
   299    502   }
   300    503   
   301    504   /*
   302    505   ** Compute an aggregate MD5 checksum over the disk image of every
   303         -** file in vid.  The file names are part of the checksum.
          506  +** file in vid.  The file names are part of the checksum.  The resulting
          507  +** checksum is the same as is expected on the R-card of a manifest.
   304    508   **
   305    509   ** This function operates differently if the Global.aCommitFile
   306    510   ** variable is not NULL. In that case, the disk image is used for
   307         -** each file in aCommitFile[] and the repository image (see
   308         -** vfile_aggregate_checksum_repository() is used for all others).
          511  +** each file in aCommitFile[] and the repository image
          512  +** is used for all others).
          513  +**
   309    514   ** Newly added files that are not contained in the repository are
   310         -** omitted from the checksum if they are not in Global.aCommitFile.
          515  +** omitted from the checksum if they are not in Global.aCommitFile[].
          516  +**
          517  +** Newly deleted files are included in the checksum if they are not
          518  +** part of Global.aCommitFile[]
          519  +**
          520  +** Renamed files use their new name if they are in Global.aCommitFile[]
          521  +** and their original name if they are not in Global.aCommitFile[]
   311    522   **
   312    523   ** Return the resulting checksum in blob pOut.
   313    524   */
   314    525   void vfile_aggregate_checksum_disk(int vid, Blob *pOut){
   315    526     FILE *in;
   316    527     Stmt q;
   317    528     char zBuf[4096];
   318    529   
   319    530     db_must_be_within_tree();
   320    531     db_prepare(&q, 
   321         -      "SELECT %Q || pathname, pathname, file_is_selected(id), rid FROM vfile"
   322         -      " WHERE NOT deleted AND vid=%d"
   323         -      " ORDER BY pathname /*scan*/",
          532  +      "SELECT %Q || pathname, pathname, origname, is_selected(id), rid"
          533  +      "  FROM vfile"
          534  +      " WHERE (NOT deleted OR NOT is_selected(id)) AND vid=%d"
          535  +      " ORDER BY if_selected(id, pathname, origname) /*scan*/",
   324    536         g.zLocalRoot, vid
   325    537     );
   326    538     md5sum_init();
   327    539     while( db_step(&q)==SQLITE_ROW ){
   328    540       const char *zFullpath = db_column_text(&q, 0);
   329    541       const char *zName = db_column_text(&q, 1);
   330         -    int isSelected = db_column_int(&q, 2);
          542  +    int isSelected = db_column_int(&q, 3);
   331    543   
   332    544       if( isSelected ){
   333    545         md5sum_step_text(zName, -1);
   334         -      in = fopen(zFullpath,"rb");
   335         -      if( in==0 ){
   336         -        md5sum_step_text(" 0\n", -1);
   337         -        continue;
   338         -      }
   339         -      fseek(in, 0L, SEEK_END);
   340         -      sprintf(zBuf, " %ld\n", ftell(in));
   341         -      fseek(in, 0L, SEEK_SET);
   342         -      md5sum_step_text(zBuf, -1);
   343         -      for(;;){
   344         -        int n;
   345         -        n = fread(zBuf, 1, sizeof(zBuf), in);
   346         -        if( n<=0 ) break;
   347         -        md5sum_step_text(zBuf, n);
          546  +      if( file_wd_islink(zFullpath) ){
          547  +        /* Instead of file content, use link destination path */
          548  +        Blob pathBuf;
          549  +
          550  +        sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", 
          551  +                         blob_read_link(&pathBuf, zFullpath));
          552  +        md5sum_step_text(zBuf, -1);
          553  +        md5sum_step_text(blob_str(&pathBuf), -1);
          554  +        blob_reset(&pathBuf);
          555  +      }else{
          556  +        in = fossil_fopen(zFullpath,"rb");
          557  +        if( in==0 ){
          558  +          md5sum_step_text(" 0\n", -1);
          559  +          continue;
          560  +        }
          561  +        fseek(in, 0L, SEEK_END);
          562  +        sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
          563  +        fseek(in, 0L, SEEK_SET);
          564  +        md5sum_step_text(zBuf, -1);
          565  +        /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
          566  +        for(;;){
          567  +          int n;
          568  +          n = fread(zBuf, 1, sizeof(zBuf), in);
          569  +          if( n<=0 ) break;
          570  +          md5sum_step_text(zBuf, n);
          571  +        }
          572  +        fclose(in);
   348    573         }
   349         -      fclose(in);
   350    574       }else{
   351         -      int rid = db_column_int(&q, 3);
          575  +      int rid = db_column_int(&q, 4);
          576  +      const char *zOrigName = db_column_text(&q, 2);
   352    577         char zBuf[100];
   353    578         Blob file;
   354    579   
          580  +      if( zOrigName ) zName = zOrigName;
   355    581         if( rid>0 ){
   356    582           md5sum_step_text(zName, -1);
   357    583           blob_zero(&file);
   358    584           content_get(rid, &file);
   359         -        sprintf(zBuf, " %d\n", blob_size(&file));
          585  +        sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file));
   360    586           md5sum_step_text(zBuf, -1);
   361    587           md5sum_step_blob(&file);
   362    588           blob_reset(&file);
   363    589         }
   364    590       }
   365    591     }
   366    592     db_finalize(&q);
   367    593     md5sum_finish(pOut);
   368    594   }
          595  +
          596  +/*
          597  +** Write a BLOB into a random filename.  Return the name of the file.
          598  +*/
          599  +char *write_blob_to_temp_file(Blob *pBlob){
          600  +  sqlite3_uint64 r;
          601  +  char *zOut = 0;
          602  +  do{
          603  +    sqlite3_free(zOut);
          604  +    sqlite3_randomness(8, &r);
          605  +    zOut = sqlite3_mprintf("file-%08llx", r);
          606  +  }while( file_size(zOut)>=0 );
          607  +  blob_write_to_file(pBlob, zOut);
          608  +  return zOut;
          609  +}
          610  +
          611  +/*
          612  +** Do a file-by-file comparison of the content of the repository and
          613  +** the working check-out on disk.  Report any errors.
          614  +*/
          615  +void vfile_compare_repository_to_disk(int vid){
          616  +  int rc;
          617  +  Stmt q;
          618  +  Blob disk, repo;
          619  +  char *zOut;
          620  +  
          621  +  db_must_be_within_tree();
          622  +  db_prepare(&q, 
          623  +      "SELECT %Q || pathname, pathname, rid FROM vfile"
          624  +      " WHERE NOT deleted AND vid=%d AND is_selected(id)"
          625  +      " ORDER BY if_selected(id, pathname, origname) /*scan*/",
          626  +      g.zLocalRoot, vid
          627  +  );
          628  +  md5sum_init();
          629  +  while( db_step(&q)==SQLITE_ROW ){
          630  +    const char *zFullpath = db_column_text(&q, 0);
          631  +    const char *zName = db_column_text(&q, 1);
          632  +    int rid = db_column_int(&q, 2);
          633  +
          634  +    blob_zero(&disk);
          635  +    if( file_wd_islink(zFullpath) ){
          636  +      rc = blob_read_link(&disk, zFullpath);
          637  +    }else{
          638  +      rc = blob_read_from_file(&disk, zFullpath);
          639  +    }
          640  +    if( rc<0 ){
          641  +      fossil_print("ERROR: cannot read file [%s]\n", zFullpath);
          642  +      blob_reset(&disk);
          643  +      continue;
          644  +    }
          645  +    blob_zero(&repo);
          646  +    content_get(rid, &repo);
          647  +    if( blob_size(&repo)!=blob_size(&disk) ){
          648  +      fossil_print("ERROR: [%s] is %d bytes on disk but %d in the repository\n",
          649  +             zName, blob_size(&disk), blob_size(&repo));
          650  +      zOut = write_blob_to_temp_file(&repo);
          651  +      fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n",
          652  +                   zName, zOut);
          653  +      sqlite3_free(zOut);
          654  +      blob_reset(&disk);
          655  +      blob_reset(&repo);
          656  +      continue;
          657  +    }
          658  +    if( blob_compare(&repo, &disk) ){
          659  +      fossil_print(
          660  +          "ERROR: [%s] is different on disk compared to the repository\n",
          661  +          zName);
          662  +      zOut = write_blob_to_temp_file(&repo);
          663  +      fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n",
          664  +          zName, zOut);
          665  +      sqlite3_free(zOut);
          666  +    }
          667  +    blob_reset(&disk);
          668  +    blob_reset(&repo);
          669  +  }
          670  +  db_finalize(&q);
          671  +}
   369    672   
   370    673   /*
   371    674   ** Compute an aggregate MD5 checksum over the repository image of every
   372         -** file in vid.  The file names are part of the checksum.
          675  +** file in vid.  The file names are part of the checksum.  The resulting
          676  +** checksum is suitable for the R-card of a manifest.
   373    677   **
   374    678   ** Return the resulting checksum in blob pOut.
   375    679   */
   376    680   void vfile_aggregate_checksum_repository(int vid, Blob *pOut){
   377    681     Blob file;
   378    682     Stmt q;
   379    683     char zBuf[100];
   380    684   
   381    685     db_must_be_within_tree();
   382    686    
   383         -  db_prepare(&q, "SELECT pathname, rid FROM vfile"
   384         -                 " WHERE NOT deleted AND rid>0 AND vid=%d"
   385         -                 " ORDER BY pathname /*scan*/",
          687  +  db_prepare(&q, "SELECT pathname, origname, rid, is_selected(id)"
          688  +                 " FROM vfile"
          689  +                 " WHERE (NOT deleted OR NOT is_selected(id))"
          690  +                 "   AND rid>0 AND vid=%d"
          691  +                 " ORDER BY if_selected(id,pathname,origname) /*scan*/",
   386    692                    vid);
   387    693     blob_zero(&file);
   388    694     md5sum_init();
   389    695     while( db_step(&q)==SQLITE_ROW ){
   390    696       const char *zName = db_column_text(&q, 0);
   391         -    int rid = db_column_int(&q, 1);
          697  +    const char *zOrigName = db_column_text(&q, 1);
          698  +    int rid = db_column_int(&q, 2);
          699  +    int isSelected = db_column_int(&q, 3);
          700  +    if( zOrigName && !isSelected ) zName = zOrigName;
   392    701       md5sum_step_text(zName, -1);
   393    702       content_get(rid, &file);
   394         -    sprintf(zBuf, " %d\n", blob_size(&file));
          703  +    sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file));
   395    704       md5sum_step_text(zBuf, -1);
          705  +    /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
   396    706       md5sum_step_blob(&file);
   397    707       blob_reset(&file);
   398    708     }
   399    709     db_finalize(&q);
   400    710     md5sum_finish(pOut);
   401    711   }
   402    712   
   403    713   /*
   404    714   ** Compute an aggregate MD5 checksum over the repository image of every
   405         -** file in manifest vid.  The file names are part of the checksum.
          715  +** file in manifest vid.  The file names are part of the checksum.  The
          716  +** resulting checksum is suitable for use as the R-card of a manifest.
          717  +**
   406    718   ** Return the resulting checksum in blob pOut.
   407    719   **
   408    720   ** If pManOut is not NULL then fill it with the checksum found in the
   409         -** "R" card near the end of the manifest.  
          721  +** "R" card near the end of the manifest.
          722  +**
          723  +** In a well-formed manifest, the two checksums computed here, pOut and
          724  +** pManOut, should be identical.  
   410    725   */
   411    726   void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){
   412         -  int i, fid;
   413         -  Blob file, mfile;
   414         -  Manifest m;
          727  +  int fid;
          728  +  Blob file;
          729  +  Manifest *pManifest;
          730  +  ManifestFile *pFile;
   415    731     char zBuf[100];
   416    732   
   417    733     blob_zero(pOut);
   418    734     if( pManOut ){
   419    735       blob_zero(pManOut);
   420    736     }
   421    737     db_must_be_within_tree();
   422         -  content_get(vid, &mfile);
   423         -  if( manifest_parse(&m, &mfile)==0 ){
          738  +  pManifest = manifest_get(vid, CFTYPE_MANIFEST);
          739  +  if( pManifest==0 ){
   424    740       fossil_panic("manifest file (%d) is malformed", vid);
   425    741     }
   426         -  for(i=0; i<m.nFile; i++){
   427         -    fid = uuid_to_rid(m.aFile[i].zUuid, 0);
   428         -    md5sum_step_text(m.aFile[i].zName, -1);
          742  +  manifest_file_rewind(pManifest);
          743  +  while( (pFile = manifest_file_next(pManifest,0))!=0 ){
          744  +    if( pFile->zUuid==0 ) continue;
          745  +    fid = uuid_to_rid(pFile->zUuid, 0);
          746  +    md5sum_step_text(pFile->zName, -1);
   429    747       content_get(fid, &file);
   430         -    sprintf(zBuf, " %d\n", blob_size(&file));
          748  +    sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file));
   431    749       md5sum_step_text(zBuf, -1);
   432    750       md5sum_step_blob(&file);
   433    751       blob_reset(&file);
   434    752     }
   435    753     if( pManOut ){
   436         -    blob_append(pManOut, m.zRepoCksum, -1);
          754  +    if( pManifest->zRepoCksum ){
          755  +      blob_append(pManOut, pManifest->zRepoCksum, -1);
          756  +    }else{
          757  +      blob_zero(pManOut);
          758  +    }
   437    759     }
   438         -  manifest_clear(&m);
          760  +  manifest_destroy(pManifest);
   439    761     md5sum_finish(pOut);
   440    762   }
   441    763   
   442    764   /*
   443    765   ** COMMAND: test-agg-cksum
   444    766   */
   445    767   void test_agg_cksum_cmd(void){

Changes to src/wiki.c.

    25     25   
    26     26   /*
    27     27   ** Return true if the input string is a well-formed wiki page name.
    28     28   **
    29     29   ** Well-formed wiki page names do not begin or end with whitespace,
    30     30   ** and do not contain tabs or other control characters and do not
    31     31   ** contain more than a single space character in a row.  Well-formed
    32         -** names must be between 3 and 100 chracters in length, inclusive.
           32  +** names must be between 3 and 100 characters in length, inclusive.
    33     33   */
    34     34   int wiki_name_is_wellformed(const unsigned char *z){
    35     35     int i;
    36     36     if( z[0]<=0x20 ){
    37     37       return 0;
    38     38     }
    39     39     for(i=1; z[i]; i++){
................................................................................
    46     46   }
    47     47   
    48     48   /*
    49     49   ** Output rules for well-formed wiki pages
    50     50   */
    51     51   static void well_formed_wiki_name_rules(void){
    52     52     @ <ul>
    53         -  @ <li> Must not begin or end with a space.
           53  +  @ <li> Must not begin or end with a space.</li>
    54     54     @ <li> Must not contain any control characters, including tab or
    55         -  @      newline.
    56         -  @ <li> Must not have two or more spaces in a row internally.
    57         -  @ <li> Must be between 3 and 100 characters in length.
           55  +  @      newline.</li>
           56  +  @ <li> Must not have two or more spaces in a row internally.</li>
           57  +  @ <li> Must be between 3 and 100 characters in length.</li>
    58     58     @ </ul>
    59     59   }
    60     60   
    61     61   /*
    62     62   ** Check a wiki name.  If it is not well-formed, then issue an error
    63     63   ** and return true.  If it is well-formed, return false.
    64     64   */
    65     65   static int check_name(const char *z){
    66     66     if( !wiki_name_is_wellformed((const unsigned char *)z) ){
    67     67       style_header("Wiki Page Name Error");
    68         -    @ The wiki name "<b>%h(z)</b>" is not well-formed.  Rules for
    69         -    @ wiki page names:
           68  +    @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
           69  +    @ Rules for wiki page names:
    70     70       well_formed_wiki_name_rules();
    71     71       style_footer();
    72     72       return 1;
    73     73     }
    74     74     return 0;
    75     75   }
    76     76   
................................................................................
    77     77   /*
    78     78   ** WEBPAGE: home
    79     79   ** WEBPAGE: index
    80     80   ** WEBPAGE: not_found
    81     81   */
    82     82   void home_page(void){
    83     83     char *zPageName = db_get("project-name",0);
           84  +  char *zIndexPage = db_get("index-page",0);
    84     85     login_check_credentials();
    85         -  if( !g.okRdWiki ){
    86         -    cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
           86  +  if( zIndexPage ){
           87  +    const char *zPathInfo = P("PATH_INFO");
           88  +    while( zIndexPage[0]=='/' ) zIndexPage++;
           89  +    while( zPathInfo[0]=='/' ) zPathInfo++;
           90  +    if( fossil_strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0;
           91  +  }
           92  +  if( zIndexPage ){
           93  +    cgi_redirectf("%s/%s", g.zTop, zIndexPage);
           94  +  }
           95  +  if( !g.perm.RdWiki ){
           96  +    cgi_redirectf("%s/login?g=%s/home", g.zTop, g.zTop);
    87     97     }
    88     98     if( zPageName ){
    89     99       login_check_credentials();
    90    100       g.zExtra = zPageName;
    91    101       cgi_set_parameter_nocopy("name", g.zExtra);
    92    102       g.isHome = 1;
    93    103       wiki_page();
    94    104       return;
    95    105     }
    96    106     style_header("Home");
    97    107     @ <p>This is a stub home-page for the project.
    98    108     @ To fill in this page, first go to
    99         -  @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a>
          109  +  @ %z(href("%R/setup_config"))setup/config</a>
   100    110     @ and establish a "Project Name".  Then create a
   101    111     @ wiki page with that name.  The content of that wiki page
   102         -  @ will be displayed in place of this message.
          112  +  @ will be displayed in place of this message.</p>
   103    113     style_footer();
   104    114   }
   105    115   
   106    116   /*
   107    117   ** Return true if the given pagename is the name of the sandbox
   108    118   */
   109    119   static int is_sandbox(const char *zPagename){
   110         -  return strcasecmp(zPagename,"sandbox")==0 ||
   111         -         strcasecmp(zPagename,"sand box")==0;
          120  +  return fossil_stricmp(zPagename,"sandbox")==0 ||
          121  +         fossil_stricmp(zPagename,"sand box")==0;
   112    122   }
   113    123   
   114    124   /*
   115    125   ** WEBPAGE: wiki
   116    126   ** URL: /wiki?name=PAGENAME
   117    127   */
   118    128   void wiki_page(void){
   119    129     char *zTag;
   120    130     int rid = 0;
   121    131     int isSandbox;
          132  +  char *zUuid;
   122    133     Blob wiki;
   123         -  Manifest m;
          134  +  Manifest *pWiki = 0;
   124    135     const char *zPageName;
   125    136     char *zBody = mprintf("%s","<i>Empty Page</i>");
   126         -  Stmt q;
   127         -  int cnt = 0;
   128    137   
   129    138     login_check_credentials();
   130         -  if( !g.okRdWiki ){ login_needed(); return; }
          139  +  if( !g.perm.RdWiki ){ login_needed(); return; }
   131    140     zPageName = P("name");
   132    141     if( zPageName==0 ){
   133    142       style_header("Wiki");
   134    143       @ <ul>
   135    144       { char *zHomePageName = db_get("project-name",0);
   136    145         if( zHomePageName ){
   137         -        @ <li> <a href="%s(g.zBaseURL)/wiki?name=%t(zHomePageName)">
          146  +        @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
   138    147           @      %h(zHomePageName)</a> wiki home page.</li>
   139    148         }
   140    149       }
   141         -    @ <li> <a href="%s(g.zBaseURL)/timeline?y=w">Recent changes</a> to wiki
   142         -    @      pages. </li>
   143         -    @ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for 
   144         -    @      wiki.</li>
   145         -    @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
          150  +    @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
          151  +    @ <li> %z(href("%R/wiki_rules"))Formatting rules</a> for wiki.</li>
          152  +    @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
   146    153       @      to experiment.</li>
   147         -    if( g.okNewWiki ){
   148         -      @ <li>  Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
          154  +    if( g.perm.NewWiki ){
          155  +      @ <li>  Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
          156  +      if( g.perm.Write ){
          157  +        @ <li>   Create a %z(href("%R/eventedit"))new event</a>.</li>
          158  +      }
   149    159       }
   150         -    @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
          160  +    @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
   151    161       @      available on this server.</li>
   152         -	@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
   153         -	@     Search wiki titles: <input type="text" name="title"/>
   154         -        @  &nbsp; <input type="submit" />
   155         -	@ </li>
          162  +    if( g.perm.ModWiki ){
          163  +      @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
          164  +    }
          165  +    @ <li>
          166  +    form_begin(0, "%R/wfind");
          167  +    @  <div>Search wiki titles: <input type="text" name="title"/>
          168  +    @  &nbsp; <input type="submit" /></div></form>
          169  +    @ </li>
   156    170       @ </ul>
   157    171       style_footer();
   158    172       return;
   159    173     }
   160    174     if( check_name(zPageName) ) return;
   161    175     isSandbox = is_sandbox(zPageName);
   162    176     if( isSandbox ){
   163    177       zBody = db_get("sandbox",zBody);
          178  +    rid = 0;
   164    179     }else{
   165    180       zTag = mprintf("wiki-%s", zPageName);
   166    181       rid = db_int(0, 
   167    182         "SELECT rid FROM tagxref"
   168    183         " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
   169    184         " ORDER BY mtime DESC", zTag
   170    185       );
   171    186       free(zTag);
   172         -    memset(&m, 0, sizeof(m));
   173         -    blob_zero(&m.content);
   174         -    if( rid ){
   175         -      Blob content;
   176         -      content_get(rid, &content);
   177         -      manifest_parse(&m, &content);
   178         -      if( m.type==CFTYPE_WIKI && m.zWiki ){
   179         -        while( isspace(m.zWiki[0]) ) m.zWiki++;
   180         -        if( m.zWiki[0] ) zBody = m.zWiki;
   181         -      }
          187  +    pWiki = manifest_get(rid, CFTYPE_WIKI);
          188  +    if( pWiki ){
          189  +      zBody = pWiki->zWiki;
   182    190       }
   183    191     }
   184    192     if( !g.isHome ){
   185         -    if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
   186         -      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
   187         -           g.zTop, zPageName);
          193  +    if( rid ){
          194  +      style_submenu_element("Diff", "Last change",
          195  +                 "%R/wdiff?name=%T&a=%d", zPageName, rid);
          196  +      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          197  +      style_submenu_element("Details", "Details",
          198  +                   "%R/info/%S", zUuid);
   188    199       }
   189         -    if( rid && g.okApndWiki && g.okAttach ){
          200  +    if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
          201  +      if( db_get_boolean("wysiwyg-wiki", 0) ){
          202  +        style_submenu_element("Edit", "Edit Wiki Page",
          203  +             "%s/wikiedit?name=%T&wysiwyg=1",
          204  +             g.zTop, zPageName);
          205  +      }else{
          206  +        style_submenu_element("Edit", "Edit Wiki Page",
          207  +             "%s/wikiedit?name=%T",
          208  +             g.zTop, zPageName);
          209  +      }
          210  +    }
          211  +    if( rid && g.perm.ApndWiki && g.perm.Attach ){
   190    212         style_submenu_element("Attach", "Add An Attachment",
   191    213              "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
   192    214              g.zTop, zPageName, g.zTop, zPageName);
   193    215       }
   194         -    if( rid && g.okApndWiki ){
          216  +    if( rid && g.perm.ApndWiki ){
   195    217         style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
   196    218              g.zTop, zPageName);
   197    219       }
   198         -    if( g.okHistory ){
          220  +    if( g.perm.Hyperlink ){
   199    221         style_submenu_element("History", "History", "%s/whistory?name=%T",
   200    222              g.zTop, zPageName);
   201    223       }
   202    224     }
          225  +  style_set_current_page("%s?name=%T", g.zPath, zPageName);
   203    226     style_header(zPageName);
   204    227     blob_init(&wiki, zBody, -1);
   205    228     wiki_convert(&wiki, 0, 0);
   206    229     blob_reset(&wiki);
   207         -
   208         -  db_prepare(&q,
   209         -     "SELECT datetime(mtime,'localtime'), filename, user"
   210         -     "  FROM attachment"
   211         -     " WHERE isLatest AND src!='' AND target=%Q"
   212         -     " ORDER BY mtime DESC",
   213         -     zPageName);
   214         -  while( db_step(&q)==SQLITE_ROW ){
   215         -    const char *zDate = db_column_text(&q, 0);
   216         -    const char *zFile = db_column_text(&q, 1);
   217         -    const char *zUser = db_column_text(&q, 2);
   218         -    if( cnt==0 ){
   219         -      @ <hr><h2>Attachments:</h2>
   220         -      @ <ul>
   221         -    }
   222         -    cnt++;
   223         -    if( g.okHistory ){
   224         -      @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
   225         -    }else{
   226         -      @ <li>
   227         -    }
   228         -    @ %h(zFile)</a> add by %h(zUser) on
   229         -    hyperlink_to_date(zDate, ".");
   230         -    if( g.okWrWiki && g.okAttach ){
   231         -      @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
   232         -    }
   233         -  }
   234         -  if( cnt ){
   235         -    @ </ul>
   236         -  }
   237         -  db_finalize(&q);
   238         - 
   239         -  if( !isSandbox ){
   240         -    manifest_clear(&m);
   241         -  }
          230  +  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
          231  +  manifest_destroy(pWiki);
   242    232     style_footer();
   243    233   }
          234  +
          235  +/*
          236  +** Write a wiki artifact into the repository
          237  +*/
          238  +static void wiki_put(Blob *pWiki, int parent){
          239  +  int nrid;
          240  +  if( g.perm.ModWiki || db_get_boolean("modreq-wiki",0)==0 ){
          241  +    nrid = content_put_ex(pWiki, 0, 0, 0, 0);
          242  +    if( parent) content_deltify(parent, nrid, 0);
          243  +  }else{
          244  +    nrid = content_put_ex(pWiki, 0, 0, 0, 1);
          245  +    moderation_table_create();
          246  +    db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid);
          247  +  }
          248  +  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
          249  +  db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid);
          250  +  manifest_crosslink(nrid, pWiki);
          251  +}
   244    252   
   245    253   /*
   246    254   ** WEBPAGE: wikiedit
   247    255   ** URL: /wikiedit?name=PAGENAME
   248    256   */
   249    257   void wikiedit_page(void){
   250    258     char *zTag;
   251    259     int rid = 0;
   252    260     int isSandbox;
   253    261     Blob wiki;
   254         -  Manifest m;
          262  +  Manifest *pWiki = 0;
   255    263     const char *zPageName;
   256         -  char *zHtmlPageName;
   257    264     int n;
   258    265     const char *z;
   259    266     char *zBody = (char*)P("w");
          267  +  int isWysiwyg = P("wysiwyg")!=0;
          268  +  int goodCaptcha = 1;
   260    269   
          270  +  if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
          271  +  if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
   261    272     if( zBody ){
   262         -    zBody = mprintf("%s", zBody);
          273  +    if( isWysiwyg ){
          274  +      Blob body;
          275  +      blob_zero(&body);
          276  +      htmlTidy(zBody, &body);
          277  +      zBody = blob_str(&body);
          278  +    }else{
          279  +      zBody = mprintf("%s", zBody);
          280  +    }
   263    281     }
   264    282     login_check_credentials();
   265    283     zPageName = PD("name","");
   266    284     if( check_name(zPageName) ) return;
   267    285     isSandbox = is_sandbox(zPageName);
   268    286     if( isSandbox ){
   269         -    if( !g.okWrWiki ){
          287  +    if( !g.perm.WrWiki ){
   270    288         login_needed();
   271    289         return;
   272    290       }
   273    291       if( zBody==0 ){
   274    292         zBody = db_get("sandbox","");
   275    293       }
   276    294     }else{
................................................................................
   277    295       zTag = mprintf("wiki-%s", zPageName);
   278    296       rid = db_int(0, 
   279    297         "SELECT rid FROM tagxref"
   280    298         " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
   281    299         " ORDER BY mtime DESC", zTag
   282    300       );
   283    301       free(zTag);
   284         -    if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
          302  +    if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
   285    303         login_needed();
   286    304         return;
   287    305       }
   288         -    memset(&m, 0, sizeof(m));
   289         -    blob_zero(&m.content);
   290         -    if( rid && zBody==0 ){
   291         -      Blob content;
   292         -      content_get(rid, &content);
   293         -      manifest_parse(&m, &content);
   294         -      if( m.type==CFTYPE_WIKI ){
   295         -        zBody = m.zWiki;
   296         -      }
          306  +    if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
          307  +      zBody = pWiki->zWiki;
   297    308       }
   298    309     }
   299         -  if( P("submit")!=0 && zBody!=0 ){
          310  +  if( P("submit")!=0 && zBody!=0
          311  +   && (goodCaptcha = captcha_is_correct())
          312  +  ){
   300    313       char *zDate;
   301    314       Blob cksum;
   302         -    int nrid;
   303    315       blob_zero(&wiki);
   304    316       db_begin_transaction();
   305    317       if( isSandbox ){
   306    318         db_set("sandbox",zBody,0);
   307    319       }else{
   308    320         login_verify_csrf_secret();
   309         -      zDate = db_text(0, "SELECT datetime('now')");
   310         -      zDate[10] = 'T';
          321  +      zDate = date_in_standard_format("now");
   311    322         blob_appendf(&wiki, "D %s\n", zDate);
   312    323         free(zDate);
   313    324         blob_appendf(&wiki, "L %F\n", zPageName);
   314    325         if( rid ){
   315    326           char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   316    327           blob_appendf(&wiki, "P %s\n", zUuid);
   317    328           free(zUuid);
................................................................................
   319    330         if( g.zLogin ){
   320    331           blob_appendf(&wiki, "U %F\n", g.zLogin);
   321    332         }
   322    333         blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
   323    334         md5sum_blob(&wiki, &cksum);
   324    335         blob_appendf(&wiki, "Z %b\n", &cksum);
   325    336         blob_reset(&cksum);
   326         -      nrid = content_put(&wiki, 0, 0);
   327         -      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   328         -      manifest_crosslink(nrid, &wiki);
   329         -      blob_reset(&wiki);
   330         -      content_deltify(rid, nrid, 0);
          337  +      wiki_put(&wiki, 0);
   331    338       }
   332    339       db_end_transaction(0);
   333    340       cgi_redirectf("wiki?name=%T", zPageName);
   334    341     }
   335    342     if( P("cancel")!=0 ){
   336    343       cgi_redirectf("wiki?name=%T", zPageName);
   337    344       return;
   338    345     }
   339    346     if( zBody==0 ){
   340    347       zBody = mprintf("<i>Empty Page</i>");
   341    348     }
   342         -  zHtmlPageName = mprintf("Edit: %s", zPageName);
   343         -  style_header(zHtmlPageName);
          349  +  style_set_current_page("%s?name=%T", g.zPath, zPageName);
          350  +  style_header("Edit: %s", zPageName);
          351  +  if( !goodCaptcha ){
          352  +    @ <p class="generalError">Error:  Incorrect security code.</p>
          353  +  }
          354  +  blob_zero(&wiki);
          355  +  blob_append(&wiki, zBody, -1);
   344    356     if( P("preview")!=0 ){
   345         -    blob_zero(&wiki);
   346         -    blob_append(&wiki, zBody, -1);
   347         -    @ Preview:<hr>
          357  +    @ Preview:<hr />
   348    358       wiki_convert(&wiki, 0, 0);
   349         -    @ <hr>
          359  +    @ <hr />
   350    360       blob_reset(&wiki);
   351    361     }
   352    362     for(n=2, z=zBody; z[0]; z++){
   353    363       if( z[0]=='\n' ) n++;
   354    364     }
   355    365     if( n<20 ) n = 20;
   356         -  if( n>40 ) n = 40;
   357         -  @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
          366  +  if( n>30 ) n = 30;
          367  +  if( !isWysiwyg ){
          368  +    /* Traditional markup-only editing */
          369  +    form_begin(0, "%R/wikiedit");
          370  +    @ <div>
          371  +    @ <textarea name="w" class="wikiedit" cols="80" 
          372  +    @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
          373  +    @ <br />
          374  +    if( db_get_boolean("wysiwyg-wiki", 0) ){
          375  +      @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
          376  +      @  onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' />
          377  +    }
          378  +    @ <input type="submit" name="preview" value="Preview Your Changes" />
          379  +  }else{
          380  +    /* Wysiwyg editing */
          381  +    Blob html, temp;
          382  +    form_begin("onsubmit='wysiwygSubmit()'", "%R/wikiedit");
          383  +    @ <div>
          384  +    @ <input type="hidden" name="wysiwyg" value="1" />
          385  +    blob_zero(&temp);
          386  +    wiki_convert(&wiki, &temp, 0);
          387  +    blob_zero(&html);
          388  +    htmlTidy(blob_str(&temp), &html);
          389  +    blob_reset(&temp);
          390  +    wysiwygEditor("w", blob_str(&html), 60, n);
          391  +    blob_reset(&html);
          392  +    @ <br />
          393  +    @ <input type="submit" name="edit-markup" value="Markup Editor"
          394  +    @  onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
          395  +  }
   358    396     login_insert_csrf_secret();
   359         -  @ <input type="hidden" name="name" value="%h(zPageName)">
   360         -  @ <textarea name="w" class="wikiedit" cols="80" 
   361         -  @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
   362         -  @ <br>
   363         -  @ <input type="submit" name="preview" value="Preview Your Changes">
   364         -  @ <input type="submit" name="submit" value="Apply These Changes">
   365         -  @ <input type="submit" name="cancel" value="Cancel">
          397  +  @ <input type="submit" name="submit" value="Apply These Changes" />
          398  +  @ <input type="hidden" name="name" value="%h(zPageName)" />
          399  +  @ <input type="submit" name="cancel" value="Cancel"
          400  +  @  onclick='confirm("Abandon your changes?")' />
          401  +  @ </div>
          402  +  captcha_generate();
   366    403     @ </form>
   367         -  if( !isSandbox ){
   368         -    manifest_clear(&m);
   369         -  }
          404  +  manifest_destroy(pWiki);
          405  +  blob_reset(&wiki);
   370    406     style_footer();
   371    407   }
   372    408   
   373    409   /*
   374    410   ** WEBPAGE: wikinew
   375    411   ** URL /wikinew
   376    412   **
   377    413   ** Prompt the user to enter the name of a new wiki page.  Then redirect
   378    414   ** to the wikiedit screen for that new page.
   379    415   */
   380    416   void wikinew_page(void){
   381    417     const char *zName;
   382    418     login_check_credentials();
   383         -  if( !g.okNewWiki ){
          419  +  if( !g.perm.NewWiki ){
   384    420       login_needed();
   385    421       return;
   386    422     }  
   387    423     zName = PD("name","");
   388    424     if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
   389         -    cgi_redirectf("wikiedit?name=%T", zName);
          425  +    if( db_get_boolean("wysiwyg-wiki", 0) ){
          426  +      cgi_redirectf("wikiedit?name=%T&wysiwyg=1", zName);
          427  +    }else{
          428  +      cgi_redirectf("wikiedit?name=%T", zName);
          429  +    }
   390    430     }
   391    431     style_header("Create A New Wiki Page");
   392         -  @ <p>Rules for wiki page names:
          432  +  @ <p>Rules for wiki page names:</p>
   393    433     well_formed_wiki_name_rules();
   394         -  @ </p>
   395         -  @ <form method="POST" action="%s(g.zBaseURL)/wikinew">
          434  +  form_begin(0, "%R/wikinew");
   396    435     @ <p>Name of new wiki page:
   397         -  @ <input type="text" width="35" name="name" value="%h(zName)">
   398         -  @ <input type="submit" value="Create">
          436  +  @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
          437  +  @ <input type="submit" value="Create" />
   399    438     @ </p></form>
   400    439     if( zName[0] ){
   401         -    @ <p><b><font color="red">
   402         -    @ "%h(zName)" is not a valid wiki page name!</font></b></p>
          440  +    @ <p><span class="wikiError">
          441  +    @ "%h(zName)" is not a valid wiki page name!</span></p>
   403    442     }
   404    443     style_footer();
   405    444   }
   406    445   
   407    446   
   408    447   /*
   409    448   ** Append the wiki text for an remark to the end of the given BLOB.
................................................................................
   416    455   
   417    456     zDate = db_text(0, "SELECT datetime('now')");
   418    457     zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
   419    458     blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", 
   420    459       zId, zDate, g.zLogin);
   421    460     free(zDate);
   422    461     zUser = PD("u",g.zLogin);
   423         -  if( zUser[0] && strcmp(zUser,g.zLogin) ){
          462  +  if( zUser[0] && fossil_strcmp(zUser,g.zLogin) ){
   424    463       blob_appendf(p, " (claiming to be %h)", zUser);
   425    464     }
   426    465     zRemark = PD("r","");
   427    466     blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
   428    467   }
   429    468   
   430    469   /*
................................................................................
   432    471   ** URL: /wikiappend?name=PAGENAME
   433    472   */
   434    473   void wikiappend_page(void){
   435    474     char *zTag;
   436    475     int rid = 0;
   437    476     int isSandbox;
   438    477     const char *zPageName;
   439         -  char *zHtmlPageName;
   440    478     const char *zUser;
          479  +  int goodCaptcha = 1;
   441    480   
   442    481     login_check_credentials();
   443    482     zPageName = PD("name","");
   444    483     if( check_name(zPageName) ) return;
   445    484     isSandbox = is_sandbox(zPageName);
   446    485     if( !isSandbox ){
   447    486       zTag = mprintf("wiki-%s", zPageName);
................................................................................
   452    491       );
   453    492       free(zTag);
   454    493       if( !rid ){
   455    494         fossil_redirect_home();
   456    495         return;
   457    496       }
   458    497     }
   459         -  if( !g.okApndWiki ){
          498  +  if( !g.perm.ApndWiki ){
   460    499       login_needed();
   461    500       return;
   462    501     }
   463         -  if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
          502  +  if( P("submit")!=0 && P("r")!=0 && P("u")!=0
          503  +   && (goodCaptcha = captcha_is_correct())
          504  +  ){
   464    505       char *zDate;
   465    506       Blob cksum;
   466         -    int nrid;
   467    507       Blob body;
   468         -    Blob content;
   469    508       Blob wiki;
   470         -    Manifest m;
          509  +    Manifest *pWiki = 0;
   471    510   
   472    511       blob_zero(&body);
   473    512       if( isSandbox ){
   474    513         blob_appendf(&body, db_get("sandbox",""));
   475    514         appendRemark(&body);
   476    515         db_set("sandbox", blob_str(&body), 0);
   477    516       }else{
   478    517         login_verify_csrf_secret();
   479         -      content_get(rid, &content);
   480         -      manifest_parse(&m, &content);
   481         -      if( m.type==CFTYPE_WIKI ){
   482         -        blob_append(&body, m.zWiki, -1);
          518  +      pWiki = manifest_get(rid, CFTYPE_WIKI);
          519  +      if( pWiki ){
          520  +        blob_append(&body, pWiki->zWiki, -1);
          521  +        manifest_destroy(pWiki);
   483    522         }
   484         -      manifest_clear(&m);
   485    523         blob_zero(&wiki);
   486    524         db_begin_transaction();
   487         -      zDate = db_text(0, "SELECT datetime('now')");
   488         -      zDate[10] = 'T';
          525  +      zDate = date_in_standard_format("now");
   489    526         blob_appendf(&wiki, "D %s\n", zDate);
   490    527         blob_appendf(&wiki, "L %F\n", zPageName);
   491    528         if( rid ){
   492    529           char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   493    530           blob_appendf(&wiki, "P %s\n", zUuid);
   494    531           free(zUuid);
   495    532         }
................................................................................
   497    534           blob_appendf(&wiki, "U %F\n", g.zLogin);
   498    535         }
   499    536         appendRemark(&body);
   500    537         blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
   501    538         md5sum_blob(&wiki, &cksum);
   502    539         blob_appendf(&wiki, "Z %b\n", &cksum);
   503    540         blob_reset(&cksum);
   504         -      nrid = content_put(&wiki, 0, 0);
   505         -      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   506         -      manifest_crosslink(nrid, &wiki);
   507         -      blob_reset(&wiki);
   508         -      content_deltify(rid, nrid, 0);
          541  +      wiki_put(&wiki, rid);
   509    542         db_end_transaction(0);
   510    543       }
   511    544       cgi_redirectf("wiki?name=%T", zPageName);
   512    545     }
   513    546     if( P("cancel")!=0 ){
   514    547       cgi_redirectf("wiki?name=%T", zPageName);
   515    548       return;
   516    549     }
   517         -  zHtmlPageName = mprintf("Append Comment To: %s", zPageName);
   518         -  style_header(zHtmlPageName);
          550  +  style_set_current_page("%s?name=%T", g.zPath, zPageName);
          551  +  style_header("Append Comment To: %s", zPageName);
          552  +  if( !goodCaptcha ){
          553  +    @ <p class="generalError">Error: Incorrect security code.</p>
          554  +  }
   519    555     if( P("preview")!=0 ){
   520    556       Blob preview;
   521    557       blob_zero(&preview);
   522    558       appendRemark(&preview);
   523    559       @ Preview:<hr>
   524    560       wiki_convert(&preview, 0, 0);
   525    561       @ <hr>
   526    562       blob_reset(&preview);
   527    563     }
   528    564     zUser = PD("u", g.zLogin);
   529         -  @ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
          565  +  form_begin(0, "%R/wikiappend");
   530    566     login_insert_csrf_secret();
   531         -  @ <input type="hidden" name="name" value="%h(zPageName)">
          567  +  @ <input type="hidden" name="name" value="%h(zPageName)" />
   532    568     @ Your Name:
   533         -  @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
   534         -  @ Comment to append:<br>
          569  +  @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
          570  +  @ Comment to append:<br />
   535    571     @ <textarea name="r" class="wikiedit" cols="80" 
   536    572     @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
   537         -  @ <br>
   538         -  @ <input type="submit" name="preview" value="Preview Your Comment">
   539         -  @ <input type="submit" name="submit" value="Append Your Changes">
   540         -  @ <input type="submit" name="cancel" value="Cancel">
          573  +  @ <br />
          574  +  @ <input type="submit" name="preview" value="Preview Your Comment" />
          575  +  @ <input type="submit" name="submit" value="Append Your Changes" />
          576  +  @ <input type="submit" name="cancel" value="Cancel" />
          577  +  captcha_generate();
   541    578     @ </form>
   542    579     style_footer();
   543    580   }
   544    581   
   545    582   /*
   546    583   ** Name of the wiki history page being generated
   547    584   */
................................................................................
   549    586   
   550    587   /*
   551    588   ** Function called to output extra text at the end of each line in
   552    589   ** a wiki history listing.
   553    590   */
   554    591   static void wiki_history_extra(int rid){
   555    592     if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
   556         -    @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
          593  +    @ %z(href("%R/wdiff?name=%t&a=%d",zWikiPageName,rid))[diff]</a>
   557    594     }
   558    595   }
   559    596   
   560    597   /*
   561    598   ** WEBPAGE: whistory
   562    599   ** URL: /whistory?name=PAGENAME
   563    600   **
................................................................................
   565    602   */
   566    603   void whistory_page(void){
   567    604     Stmt q;
   568    605     char *zTitle;
   569    606     char *zSQL;
   570    607     const char *zPageName;
   571    608     login_check_credentials();
   572         -  if( !g.okHistory ){ login_needed(); return; }
          609  +  if( !g.perm.Hyperlink ){ login_needed(); return; }
   573    610     zPageName = PD("name","");
   574    611     zTitle = mprintf("History Of %s", zPageName);
   575    612     style_header(zTitle);
   576    613     free(zTitle);
   577    614   
   578    615     zSQL = mprintf("%s AND event.objid IN "
   579    616                    "  (SELECT rid FROM tagxref WHERE tagid="
................................................................................
   581    618                    "   UNION SELECT attachid FROM attachment"
   582    619                             " WHERE target=%Q)"
   583    620                    "ORDER BY mtime DESC",
   584    621                    timeline_query_for_www(), zPageName, zPageName);
   585    622     db_prepare(&q, zSQL);
   586    623     free(zSQL);
   587    624     zWikiPageName = zPageName;
   588         -  www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
          625  +  www_print_timeline(&q, TIMELINE_ARTID, 0, 0, wiki_history_extra);
   589    626     db_finalize(&q);
   590    627     style_footer();
   591    628   }
   592    629   
   593    630   /*
   594    631   ** WEBPAGE: wdiff
   595    632   ** URL: /whistory?name=PAGENAME&a=RID1&b=RID2
................................................................................
   596    633   **
   597    634   ** Show the difference between two wiki pages.
   598    635   */
   599    636   void wdiff_page(void){
   600    637     char *zTitle;
   601    638     int rid1, rid2;
   602    639     const char *zPageName;
   603         -  Blob content1, content2;
   604         -  Manifest m1, m2;
          640  +  Manifest *pW1, *pW2 = 0;
   605    641     Blob w1, w2, d;
          642  +  u64 diffFlags;
   606    643   
   607    644     login_check_credentials();
   608    645     rid1 = atoi(PD("a","0"));
   609         -  if( !g.okHistory ){ login_needed(); return; }
          646  +  if( !g.perm.Hyperlink ){ login_needed(); return; }
   610    647     if( rid1==0 ) fossil_redirect_home();
   611    648     rid2 = atoi(PD("b","0"));
   612    649     zPageName = PD("name","");
   613    650     zTitle = mprintf("Changes To %s", zPageName);
   614    651     style_header(zTitle);
   615    652     free(zTitle);
   616    653   
................................................................................
   619    656         "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid="
   620    657                           "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
   621    658         " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
   622    659         " ORDER BY event.mtime DESC LIMIT 1",
   623    660         zPageName, rid1
   624    661       );
   625    662     }
   626         -  content_get(rid1, &content1);
   627         -  manifest_parse(&m1, &content1);
   628         -  if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
   629         -  blob_init(&w1, m1.zWiki, -1);
          663  +  pW1 = manifest_get(rid1, CFTYPE_WIKI);
          664  +  if( pW1==0 ) fossil_redirect_home();
          665  +  blob_init(&w1, pW1->zWiki, -1);
   630    666     blob_zero(&w2);
   631         -  if( rid2 ){
   632         -    content_get(rid2, &content2);
   633         -    manifest_parse(&m2, &content2);
   634         -    if( m2.type==CFTYPE_WIKI ){
   635         -      blob_init(&w2, m2.zWiki, -1);
   636         -    }
          667  +  if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
          668  +    blob_init(&w2, pW2->zWiki, -1);
   637    669     }
   638    670     blob_zero(&d);
   639         -  text_diff(&w2, &w1, &d, 5);
   640         -  @ <pre>
   641         -  @ %h(blob_str(&d))
   642         -  @ </pre>
          671  +  diffFlags = construct_diff_flags(1,0);
          672  +  text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
          673  +  @ <div class="udiff">
          674  +  @ %s(blob_str(&d))
          675  +  @ </div>
          676  +  manifest_destroy(pW1);
          677  +  manifest_destroy(pW2);
   643    678     style_footer();
   644    679   }
   645    680   
          681  +/*
          682  +** prepare()s pStmt with a query requesting:
          683  +**
          684  +** - wiki page name
          685  +** - tagxref (whatever that really is!)
          686  +**
          687  +** Used by wcontent_page() and the JSON wiki code.
          688  +*/
          689  +void wiki_prepare_page_list( Stmt * pStmt ){
          690  +  db_prepare(pStmt, 
          691  +    "SELECT"
          692  +    "  substr(tagname, 6) as name,"
          693  +    "  (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
          694  +    "  FROM tag WHERE tagname GLOB 'wiki-*'"
          695  +    " ORDER BY lower(tagname) /*sort*/"
          696  +  );
          697  +}
   646    698   /*
   647    699   ** WEBPAGE: wcontent
   648    700   **
   649    701   **     all=1         Show deleted pages
   650    702   **
   651    703   ** List all available wiki pages with date created and last modified.
   652    704   */
   653    705   void wcontent_page(void){
   654    706     Stmt q;
   655    707     int showAll = P("all")!=0;
   656    708   
   657    709     login_check_credentials();
   658         -  if( !g.okRdWiki ){ login_needed(); return; }
          710  +  if( !g.perm.RdWiki ){ login_needed(); return; }
   659    711     style_header("Available Wiki Pages");
   660    712     if( showAll ){
   661    713       style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
   662    714     }else{
   663    715       style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
   664    716     }
   665    717     @ <ul>
   666         -  db_prepare(&q, 
   667         -    "SELECT"
   668         -    "  substr(tagname, 6),"
   669         -    "  (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
   670         -    "  FROM tag WHERE tagname GLOB 'wiki-*'"
   671         -    " ORDER BY lower(tagname) /*sort*/"
   672         -  );
          718  +  wiki_prepare_page_list(&q);
   673    719     while( db_step(&q)==SQLITE_ROW ){
   674    720       const char *zName = db_column_text(&q, 0);
   675    721       int size = db_column_int(&q, 1);
   676    722       if( size>0 ){
   677         -      @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
          723  +      @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>
   678    724       }else if( showAll ){
   679         -      @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
          725  +      @ <li>%z(href("%R/wiki?name=%T",zName))<s>%h(zName)</s></a></li>
   680    726       }
   681    727     }
   682    728     db_finalize(&q);
   683    729     @ </ul>
   684    730     style_footer();
   685    731   }
   686    732   
................................................................................
   690    736   ** URL: /wfind?title=TITLE
   691    737   ** List all wiki pages whose titles contain the search text
   692    738   */
   693    739   void wfind_page(void){
   694    740     Stmt q;
   695    741     const char * zTitle;
   696    742     login_check_credentials();
   697         -  if( !g.okRdWiki ){ login_needed(); return; }
          743  +  if( !g.perm.RdWiki ){ login_needed(); return; }
   698    744     zTitle = PD("title","*");
   699    745     style_header("Wiki Pages Found");
   700    746     @ <ul>
   701    747     db_prepare(&q, 
   702    748       "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%'"
   703    749       " ORDER BY lower(tagname) /*sort*/" ,
   704    750   	zTitle);
   705    751     while( db_step(&q)==SQLITE_ROW ){
   706    752       const char *zName = db_column_text(&q, 0);
   707         -    @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li>
          753  +    @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>
   708    754     }
   709    755     db_finalize(&q);
   710    756     @ </ul>
   711    757     style_footer();
   712    758   }
   713    759   
   714    760   /*
................................................................................
   729    775     @ <li>Most ordinary HTML works.</li>
   730    776     @ <li>&lt;verbatim&gt; and &lt;nowiki&gt;.</li>
   731    777     @ </ol>
   732    778     @ <p>We call the first five rules above "wiki" formatting rules.  The
   733    779     @ last two rules are the HTML formatting rule.</p>
   734    780     @ <h2>Formatting Rule Details</h2>
   735    781     @ <ol>
   736         -  @ <li> <p><b>Paragraphs</b>.  Any sequence of one or more blank lines forms
          782  +  @ <li> <p><span class="wikiruleHead">Paragraphs</span>.  Any sequence of one or more blank lines forms
   737    783     @ a paragraph break.  Centered or right-justified paragraphs are not
   738    784     @ supported by wiki markup, but you can do these things if you need them
   739         -  @ using HTML.</p>
   740         -  @ <li> <p><b>Bullet Lists</b>.
          785  +  @ using HTML.</p></li>
          786  +  @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
   741    787     @ A bullet list item is a line that begins with a single "*" character
   742    788     @ surrounded on
   743    789     @ both sides by two or more spaces or by a tab.  Only a single level
   744         -  @ of bullet list is supported by wiki.  For nested lists, use HTML.</p>
   745         -  @ <li> <p><b>Enumeration Lists</b>.
          790  +  @ of bullet list is supported by wiki.  For nested lists, use HTML.</p></li>
          791  +  @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>.
   746    792     @ An enumeration list item is a line that begins with a single "#" character
   747    793     @ surrounded on both sides by two or more spaces or by a tab.  Only a single
   748    794     @ level of enumeration list is supported by wiki.  For nested lists or for
   749         -  @ enumerations that count using letters or roman numerials, use HTML.</p>
   750         -  @ <li> <p><b>Indented Paragraphs</b>.
          795  +  @ enumerations that count using letters or roman numerials, use HTML.</p></li>
          796  +  @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>.
   751    797     @ Any paragraph that begins with two or more spaces or a tab and
   752    798     @ which is not a bullet or enumeration list item is rendered 
   753    799     @ indented.  Only a single level of indentation is supported by wiki; use
   754         -  @ HTML for deeper indentation.</p>
   755         -  @ <li> <p><b>Hyperlinks</b>.
          800  +  @ HTML for deeper indentation.</p></li>
          801  +  @ <li> <p><span class="wikiruleHead">Hyperlinks</span>.
   756    802     @ Text within square brackets ("[...]") becomes a hyperlink.  The
   757    803     @ target can be a wiki page name, the artifact ID of a check-in or ticket,
   758    804     @ the name of an image, or a URL.  By default, the target is displayed
   759    805     @ as the text of the hyperlink.  But you can specify alternative text
   760    806     @ after the target name separated by a "|" character.</p>
   761    807     @ <p>You can also link to internal anchor names using [#anchor-name], providing
   762    808     @ you have added the necessary "&lt;a name="anchor-name"&gt;&lt;/a&gt;"
   763         -  @ tag to your wiki page.</p>
   764         -  @ <li> <p><b>HTML</b>.
          809  +  @ tag to your wiki page.</p></li>
          810  +  @ <li> <p><span class="wikiruleHead">HTML</span>.
   765    811     @ The following standard HTML elements may be used:
   766         -  @ &lt;a&gt;
   767         -  @ &lt;address&gt;
   768         -  @ &lt;b&gt;
   769         -  @ &lt;big&gt;
   770         -  @ &lt;blockquote&gt;
   771         -  @ &lt;br&gt;
   772         -  @ &lt;center&gt;
   773         -  @ &lt;cite&gt;
   774         -  @ &lt;code&gt;
   775         -  @ &lt;dd&gt;
   776         -  @ &lt;dfn&gt;
   777         -  @ &lt;div&gt;
   778         -  @ &lt;dl&gt;
   779         -  @ &lt;dt&gt;
   780         -  @ &lt;em&gt;
   781         -  @ &lt;font&gt;
   782         -  @ &lt;h1&gt;
   783         -  @ &lt;h2&gt;
   784         -  @ &lt;h3&gt;
   785         -  @ &lt;h4&gt;
   786         -  @ &lt;h5&gt;
   787         -  @ &lt;h6&gt;
   788         -  @ &lt;hr&gt;
   789         -  @ &lt;img&gt;
   790         -  @ &lt;i&gt;
   791         -  @ &lt;kbd&gt;
   792         -  @ &lt;li&gt;
   793         -  @ &lt;nobr&gt;
   794         -  @ &lt;ol&gt;
   795         -  @ &lt;p&gt;
   796         -  @ &lt;pre&gt;
   797         -  @ &lt;s&gt;
   798         -  @ &lt;samp&gt;
   799         -  @ &lt;small&gt;
   800         -  @ &lt;strike&gt;
   801         -  @ &lt;strong&gt;
   802         -  @ &lt;sub&gt;
   803         -  @ &lt;sup&gt;
   804         -  @ &lt;table&gt;
   805         -  @ &lt;td&gt;
   806         -  @ &lt;th&gt;
   807         -  @ &lt;tr&gt;
   808         -  @ &lt;tt&gt;
   809         -  @ &lt;u&gt;
   810         -  @ &lt;ul&gt;
   811         -  @ &lt;var&gt;.
   812         -  @ In addition, there are two non-standard elements available:
          812  +  show_allowed_wiki_markup();
          813  +  @ . There are two non-standard elements available:
   813    814     @ &lt;verbatim&gt; and &lt;nowiki&gt;.
   814    815     @ No other elements are allowed.  All attributes are checked and
   815    816     @ only a few benign attributes are allowed on each element.
   816    817     @ In particular, any attributes that specify javascript or CSS
   817    818     @ are elided.</p></li>
   818         -  @ <li><p><b>Special Markup.</b>
          819  +  @ <li><p><span class="wikiruleHead">Special Markup.</span>
   819    820     @ The &lt;nowiki&gt; tag disables all wiki formatting rules
   820    821     @ through the matching &lt;/nowiki&gt; element.
   821    822     @ The &lt;verbatim&gt; tag works like &lt;pre&gt; with the addition
   822    823     @ that it also disables all wiki and HTML markup
   823         -  @ through the matching &lt;/verbatim&gt;.
          824  +  @ through the matching &lt;/verbatim&gt;.</p></li>
   824    825     @ </ol>
   825    826     style_footer();
   826    827   }
   827    828   
   828    829   /*
   829         -** Add a new wiki page to the respository.  The page name is
          830  +** Add a new wiki page to the repository.  The page name is
   830    831   ** given by the zPageName parameter.  isNew must be true to create
   831    832   ** a new page.  If no previous page with the name zPageName exists
   832    833   ** and isNew is false, then this routine throws an error.
   833    834   **
   834    835   ** The content of the new page is given by the blob pContent.
   835    836   */
   836    837   int wiki_cmd_commit(char const * zPageName, int isNew, Blob *pContent){
   837    838     Blob wiki;              /* Wiki page content */
   838    839     Blob cksum;             /* wiki checksum */
   839    840     int rid;                /* artifact ID of parent page */
   840         -  int nrid;               /* artifact ID of new wiki page */
   841    841     char *zDate;            /* timestamp */
   842    842     char *zUuid;            /* uuid for rid */
   843    843   
   844    844     rid = db_int(0,
   845    845        "SELECT x.rid FROM tag t, tagxref x"
   846    846        " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
   847    847        " ORDER BY x.mtime DESC LIMIT 1",
   848    848        zPageName
   849    849     );
   850    850     if( rid==0 && !isNew ){
          851  +#ifdef FOSSIL_ENABLE_JSON
          852  +    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
          853  +#endif
   851    854       fossil_fatal("no such wiki page: %s", zPageName);
   852    855     }
   853    856     if( rid!=0 && isNew ){
          857  +#ifdef FOSSIL_ENABLE_JSON
          858  +    g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
          859  +#endif
   854    860       fossil_fatal("wiki page %s already exists", zPageName);
   855    861     }
   856    862   
   857    863     blob_zero(&wiki);
   858         -  zDate = db_text(0, "SELECT datetime('now')");
   859         -  zDate[10] = 'T';
          864  +  zDate = date_in_standard_format("now");
   860    865     blob_appendf(&wiki, "D %s\n", zDate);
   861    866     free(zDate);
   862    867     blob_appendf(&wiki, "L %F\n", zPageName );
   863    868     if( rid ){
   864    869       zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   865    870       blob_appendf(&wiki, "P %s\n", zUuid);
   866    871       free(zUuid);
................................................................................
   871    876     }
   872    877     blob_appendf( &wiki, "W %d\n%s\n", blob_size(pContent),
   873    878                   blob_str(pContent) );
   874    879     md5sum_blob(&wiki, &cksum);
   875    880     blob_appendf(&wiki, "Z %b\n", &cksum);
   876    881     blob_reset(&cksum);
   877    882     db_begin_transaction();
   878         -  nrid = content_put( &wiki, 0, 0 );
   879         -  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   880         -  manifest_crosslink(nrid,&wiki);
   881         -  blob_reset(&wiki);
   882         -  content_deltify(rid,nrid,0);
          883  +  wiki_put(&wiki, 0);
   883    884     db_end_transaction(0);
   884         -  autosync(AUTOSYNC_PUSH);  
   885    885     return 1;
   886    886   }
   887    887   
   888    888   /*
   889         -** COMMAND: wiki
          889  +** COMMAND: wiki*
   890    890   **
   891    891   ** Usage: %fossil wiki (export|create|commit|list) WikiName
   892    892   **
   893    893   ** Run various subcommands to work with wiki entries.
   894    894   **
   895    895   **     %fossil wiki export PAGENAME ?FILE?
   896    896   **
................................................................................
   906    906   **
   907    907   **        Create a new wiki page with initial content taken from
   908    908   **        FILE or from standard input.
   909    909   **
   910    910   **     %fossil wiki list
   911    911   **
   912    912   **        Lists all wiki entries, one per line, ordered
   913         -**        case-insentively by name.
          913  +**        case-insensitively by name.
   914    914   **
   915         -** TODOs:
   916         -**
   917         -**     %fossil wiki export ?-u ARTIFACT? WikiName ?FILE?
   918         -**
   919         -**        Outputs the selected version of WikiName.
   920         -**
   921         -**     %fossil wiki delete ?-m MESSAGE? WikiName
   922         -**
   923         -**        The same as deleting a file entry, but i don't know if fossil
   924         -**        supports a commit message for Wiki entries.
   925         -**
   926         -**     %fossil wiki ?-u? ?-d? ?-s=[|]? list
   927         -**
   928         -**        Lists the artifact ID and/or Date of last change along with
   929         -**        each entry name, delimited by the -s char.
   930         -**
   931         -**     %fossil wiki diff ?ARTIFACT? ?-f infile[=stdin]? EntryName
   932         -**
   933         -**        Diffs the local copy of a page with a given version (defaulting
   934         -**        to the head version).
   935    915   */
   936    916   void wiki_cmd(void){
   937    917     int n;
   938         -  db_find_and_open_repository(1);
          918  +  db_find_and_open_repository(0, 0);
   939    919     if( g.argc<3 ){
   940    920       goto wiki_cmd_usage;
   941    921     }
   942    922     n = strlen(g.argv[2]);
   943    923     if( n==0 ){
   944    924       goto wiki_cmd_usage;
   945    925     }
   946    926   
   947    927     if( strncmp(g.argv[2],"export",n)==0 ){
   948    928       char const *zPageName;        /* Name of the wiki page to export */
   949    929       char const *zFile;            /* Name of the output file (0=stdout) */
   950         -    int rid;                /* Artifact ID of the wiki page */
   951         -    int i;                  /* Loop counter */
   952         -    char *zBody = 0;        /* Wiki page content */
   953         -    Manifest m;             /* Parsed wiki page content */
          930  +    int rid;                      /* Artifact ID of the wiki page */
          931  +    int i;                        /* Loop counter */
          932  +    char *zBody = 0;              /* Wiki page content */
          933  +    Blob body;                    /* Wiki page content */
          934  +    Manifest *pWiki = 0;          /* Parsed wiki page content */
          935  +
   954    936       if( (g.argc!=4) && (g.argc!=5) ){
   955    937         usage("export PAGENAME ?FILE?");
   956    938       }
   957    939       zPageName = g.argv[3];
   958    940       rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
   959    941         " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
   960    942         " ORDER BY x.mtime DESC LIMIT 1",
   961    943         zPageName 
   962    944       );
   963         -    if( rid ){
   964         -      Blob content;
   965         -      content_get(rid, &content);
   966         -      manifest_parse(&m, &content);
   967         -      if( m.type==CFTYPE_WIKI ){
   968         -        zBody = m.zWiki;
   969         -      }
          945  +    if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
          946  +      zBody = pWiki->zWiki;
   970    947       }
   971    948       if( zBody==0 ){
   972    949         fossil_fatal("wiki page [%s] not found",zPageName);
   973    950       }
   974         -    for(i=strlen(zBody); i>0 && isspace(zBody[i-1]); i--){}
   975         -    zFile  = (g.argc==4) ? 0 : g.argv[4];
   976         -    if( zFile ){
   977         -      FILE * zF;
   978         -      short doClose = 0;
   979         -      if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){
   980         -        zF = stdout;
   981         -      }else{
   982         -        zF = fopen( zFile, "w" );
   983         -        doClose = zF ? 1 : 0;
   984         -      }
   985         -      if( ! zF ){
   986         -        fossil_fatal("wiki export could not open output file for writing.");
   987         -      }
   988         -      fprintf(zF,"%.*s\n", i, zBody);
   989         -      if( doClose ) fclose(zF);
   990         -    }else{
   991         -	printf("%.*s\n", i, zBody);
   992         -    }
          951  +    for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
          952  +    zBody[i] = 0;
          953  +    zFile  = (g.argc==4) ? "-" : g.argv[4];
          954  +    blob_init(&body, zBody, -1);
          955  +    blob_append(&body, "\n", 1);
          956  +    blob_write_to_file(&body, zFile);
          957  +    blob_reset(&body);
          958  +    manifest_destroy(pWiki);
   993    959       return;
   994    960     }else
   995    961     if( strncmp(g.argv[2],"commit",n)==0
   996    962         || strncmp(g.argv[2],"create",n)==0 ){
   997    963       char *zPageName;
   998    964       Blob content;
   999    965       if( g.argc!=4 && g.argc!=5 ){
................................................................................
  1003    969       if( g.argc==4 ){
  1004    970         blob_read_from_channel(&content, stdin, -1);
  1005    971       }else{
  1006    972         blob_read_from_file(&content, g.argv[4]);
  1007    973       }
  1008    974       if( g.argv[2][1]=='r' ){
  1009    975         wiki_cmd_commit(zPageName, 1, &content);
  1010         -      printf("Created new wiki page %s.\n", zPageName);
          976  +      fossil_print("Created new wiki page %s.\n", zPageName);
  1011    977       }else{
  1012    978         wiki_cmd_commit(zPageName, 0, &content);
  1013         -      printf("Updated wiki page %s.\n", zPageName);
          979  +      fossil_print("Updated wiki page %s.\n", zPageName);
  1014    980       }
  1015    981       blob_reset(&content);
  1016    982     }else
  1017    983     if( strncmp(g.argv[2],"delete",n)==0 ){
  1018    984       if( g.argc!=5 ){
  1019    985         usage("delete PAGENAME");
  1020    986       }
................................................................................
  1024    990       Stmt q;
  1025    991       db_prepare(&q, 
  1026    992         "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
  1027    993         " ORDER BY lower(tagname) /*sort*/"
  1028    994       );
  1029    995       while( db_step(&q)==SQLITE_ROW ){
  1030    996         const char *zName = db_column_text(&q, 0);
  1031         -      printf( "%s\n",zName);
          997  +      fossil_print( "%s\n",zName);
  1032    998       }
  1033    999       db_finalize(&q);
  1034   1000     }else
  1035   1001     {
  1036   1002       goto wiki_cmd_usage;
  1037   1003     }
  1038   1004     return;
  1039   1005   
  1040   1006   wiki_cmd_usage:
  1041   1007     usage("export|create|commit|list ...");
  1042   1008   }

Changes to src/wikiformat.c.

    21     21   #include "config.h"
    22     22   #include "wikiformat.h"
    23     23   
    24     24   #if INTERFACE
    25     25   /*
    26     26   ** Allowed wiki transformation operations
    27     27   */
    28         -#define WIKI_NOFOLLOW       0x001
    29         -#define WIKI_HTML           0x002
    30         -#define WIKI_INLINE         0x004  /* Do not surround with <p>..</p> */
    31         -#define WIKI_NOBLOCK        0x008  /* No block markup of any kind */
           28  +#define WIKI_HTMLONLY       0x001  /* HTML markup only.  No wiki */
           29  +#define WIKI_INLINE         0x002  /* Do not surround with <p>..</p> */
           30  +#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
           31  +#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
           32  +#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
           33  +#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
    32     34   #endif
    33     35   
    34     36   
    35     37   /*
    36     38   ** These are the only markup attributes allowed.
    37     39   */
    38     40   #define ATTR_ALIGN              1
    39     41   #define ATTR_ALT                2
    40     42   #define ATTR_BGCOLOR            3
    41     43   #define ATTR_BORDER             4
    42     44   #define ATTR_CELLPADDING        5
    43     45   #define ATTR_CELLSPACING        6
    44         -#define ATTR_CLEAR              7
    45         -#define ATTR_COLOR              8
    46         -#define ATTR_COLSPAN            9
    47         -#define ATTR_COMPACT            10
    48         -#define ATTR_FACE               11
    49         -#define ATTR_HEIGHT             12
    50         -#define ATTR_HREF               13
    51         -#define ATTR_HSPACE             14
    52         -#define ATTR_ID                 15
    53         -#define ATTR_NAME               16
    54         -#define ATTR_ROWSPAN            17
    55         -#define ATTR_SIZE               18
    56         -#define ATTR_SRC                19
    57         -#define ATTR_START              20
    58         -#define ATTR_TYPE               21
    59         -#define ATTR_VALIGN             22
    60         -#define ATTR_VALUE              23
    61         -#define ATTR_VSPACE             24
    62         -#define ATTR_WIDTH              25
    63         -#define AMSK_ALIGN              0x0000001
    64         -#define AMSK_ALT                0x0000002
    65         -#define AMSK_BGCOLOR            0x0000004
    66         -#define AMSK_BORDER             0x0000008
    67         -#define AMSK_CELLPADDING        0x0000010
    68         -#define AMSK_CELLSPACING        0x0000020
    69         -#define AMSK_CLEAR              0x0000040
    70         -#define AMSK_COLOR              0x0000080
    71         -#define AMSK_COLSPAN            0x0000100
    72         -#define AMSK_COMPACT            0x0000200
    73         -#define AMSK_FACE               0x0000400
    74         -#define AMSK_HEIGHT             0x0000800
    75         -#define AMSK_HREF               0x0001000
    76         -#define AMSK_HSPACE             0x0002000
    77         -#define AMSK_ID                 0x0004000
    78         -#define AMSK_NAME               0x0008000
    79         -#define AMSK_ROWSPAN            0x0010000
    80         -#define AMSK_SIZE               0x0020000
    81         -#define AMSK_SRC                0x0040000
    82         -#define AMSK_START              0x0080000
    83         -#define AMSK_TYPE               0x0100000
    84         -#define AMSK_VALIGN             0x0200000
    85         -#define AMSK_VALUE              0x0400000
    86         -#define AMSK_VSPACE             0x0800000
    87         -#define AMSK_WIDTH              0x1000000
    88         -#define AMSK_CLASS              0x2000000
           46  +#define ATTR_CLASS              7
           47  +#define ATTR_CLEAR              8
           48  +#define ATTR_COLOR              9
           49  +#define ATTR_COLSPAN            10
           50  +#define ATTR_COMPACT            11
           51  +#define ATTR_FACE               12
           52  +#define ATTR_HEIGHT             13
           53  +#define ATTR_HREF               14
           54  +#define ATTR_HSPACE             15
           55  +#define ATTR_ID                 16
           56  +#define ATTR_LINKS              17
           57  +#define ATTR_NAME               18
           58  +#define ATTR_ROWSPAN            19
           59  +#define ATTR_SIZE               20
           60  +#define ATTR_SRC                21
           61  +#define ATTR_START              22
           62  +#define ATTR_STYLE              23
           63  +#define ATTR_TARGET             24
           64  +#define ATTR_TYPE               25
           65  +#define ATTR_VALIGN             26
           66  +#define ATTR_VALUE              27
           67  +#define ATTR_VSPACE             28
           68  +#define ATTR_WIDTH              29
           69  +#define AMSK_ALIGN              0x00000001
           70  +#define AMSK_ALT                0x00000002
           71  +#define AMSK_BGCOLOR            0x00000004
           72  +#define AMSK_BORDER             0x00000008
           73  +#define AMSK_CELLPADDING        0x00000010
           74  +#define AMSK_CELLSPACING        0x00000020
           75  +#define AMSK_CLASS              0x00000040
           76  +#define AMSK_CLEAR              0x00000080
           77  +#define AMSK_COLOR              0x00000100
           78  +#define AMSK_COLSPAN            0x00000200
           79  +#define AMSK_COMPACT            0x00000400
           80  +#define AMSK_FACE               0x00000800
           81  +#define AMSK_HEIGHT             0x00001000
           82  +#define AMSK_HREF               0x00002000
           83  +#define AMSK_HSPACE             0x00004000
           84  +#define AMSK_ID                 0x00008000
           85  +#define AMSK_LINKS              0x00010000
           86  +#define AMSK_NAME               0x00020000
           87  +#define AMSK_ROWSPAN            0x00040000
           88  +#define AMSK_SIZE               0x00080000
           89  +#define AMSK_SRC                0x00100000
           90  +#define AMSK_START              0x00200000
           91  +#define AMSK_STYLE              0x00400000
           92  +#define AMSK_TARGET             0x00800000
           93  +#define AMSK_TYPE               0x01000000
           94  +#define AMSK_VALIGN             0x02000000
           95  +#define AMSK_VALUE              0x04000000
           96  +#define AMSK_VSPACE             0x08000000
           97  +#define AMSK_WIDTH              0x10000000
    89     98   
    90     99   static const struct AllowedAttribute {
    91    100     const char *zName;
    92    101     unsigned int iMask;
    93    102   } aAttribute[] = {
    94    103     { 0, 0 },
    95    104     { "align",         AMSK_ALIGN,          },
................................................................................
   104    113     { "colspan",       AMSK_COLSPAN,        },
   105    114     { "compact",       AMSK_COMPACT,        },
   106    115     { "face",          AMSK_FACE,           },
   107    116     { "height",        AMSK_HEIGHT,         },
   108    117     { "href",          AMSK_HREF,           },
   109    118     { "hspace",        AMSK_HSPACE,         },
   110    119     { "id",            AMSK_ID,             },
          120  +  { "links",         AMSK_LINKS,          },
   111    121     { "name",          AMSK_NAME,           },
   112    122     { "rowspan",       AMSK_ROWSPAN,        },
   113    123     { "size",          AMSK_SIZE,           },
   114    124     { "src",           AMSK_SRC,            },
   115    125     { "start",         AMSK_START,          },
          126  +  { "style",         AMSK_STYLE,          },
          127  +  { "target",        AMSK_TARGET,         },
   116    128     { "type",          AMSK_TYPE,           },
   117    129     { "valign",        AMSK_VALIGN,         },
   118    130     { "value",         AMSK_VALUE,          },
   119    131     { "vspace",        AMSK_VSPACE,         },
   120    132     { "width",         AMSK_WIDTH,          },
   121    133   };
   122    134   
................................................................................
   125    137   */
   126    138   static int findAttr(const char *z){
   127    139     int i, c, first, last;
   128    140     first = 1;
   129    141     last = sizeof(aAttribute)/sizeof(aAttribute[0]) - 1;
   130    142     while( first<=last ){
   131    143       i = (first+last)/2;
   132         -    c = strcmp(aAttribute[i].zName, z);
          144  +    c = fossil_strcmp(aAttribute[i].zName, z);
   133    145       if( c==0 ){
   134    146         return i;
   135    147       }else if( c<0 ){
   136    148         first = i+1;
   137    149       }else{
   138    150         last = i-1;
   139    151       }
................................................................................
   147    159   ** Allowed markup.
   148    160   **
   149    161   ** Except for MARKUP_INVALID, this must all be in alphabetical order
   150    162   ** and in numerical sequence.  The first markup type must be zero.
   151    163   ** The value for MARKUP_XYZ must correspond to the <xyz> entry
   152    164   ** in aAllowedMarkup[].
   153    165   */
   154         -#define MARKUP_INVALID           0
   155         -#define MARKUP_A                 1
   156         -#define MARKUP_ADDRESS           2
   157         -#define MARKUP_B                 3
   158         -#define MARKUP_BIG               4
   159         -#define MARKUP_BLOCKQUOTE        5
   160         -#define MARKUP_BR                6
   161         -#define MARKUP_CENTER            7
   162         -#define MARKUP_CITE              8
   163         -#define MARKUP_CODE              9
   164         -#define MARKUP_DD               10
   165         -#define MARKUP_DFN              11
   166         -#define MARKUP_DIV              12
   167         -#define MARKUP_DL               13
   168         -#define MARKUP_DT               14
   169         -#define MARKUP_EM               15
   170         -#define MARKUP_FONT             16
   171         -#define MARKUP_H1               17
   172         -#define MARKUP_H2               18
   173         -#define MARKUP_H3               19
   174         -#define MARKUP_H4               20
   175         -#define MARKUP_H5               21
   176         -#define MARKUP_H6               22
   177         -#define MARKUP_HR               23
   178         -#define MARKUP_I                24
   179         -#define MARKUP_IMG              25
   180         -#define MARKUP_KBD              26
   181         -#define MARKUP_LI               27
   182         -#define MARKUP_NOBR             28
   183         -#define MARKUP_NOWIKI           29
   184         -#define MARKUP_OL               30
   185         -#define MARKUP_P                31
   186         -#define MARKUP_PRE              32
   187         -#define MARKUP_S                33
   188         -#define MARKUP_SAMP             34
   189         -#define MARKUP_SMALL            35
   190         -#define MARKUP_STRIKE           36
   191         -#define MARKUP_STRONG           37
   192         -#define MARKUP_SUB              38
   193         -#define MARKUP_SUP              39
   194         -#define MARKUP_TABLE            40
   195         -#define MARKUP_TD               41
   196         -#define MARKUP_TH               42
   197         -#define MARKUP_TR               43
   198         -#define MARKUP_TT               44
   199         -#define MARKUP_U                45
   200         -#define MARKUP_UL               46
   201         -#define MARKUP_VAR              47
   202         -#define MARKUP_VERBATIM         48
          166  +#define MARKUP_INVALID            0
          167  +#define MARKUP_A                  1
          168  +#define MARKUP_ADDRESS            2
          169  +#define MARKUP_B                  3
          170  +#define MARKUP_BIG                4
          171  +#define MARKUP_BLOCKQUOTE         5
          172  +#define MARKUP_BR                 6
          173  +#define MARKUP_CENTER             7
          174  +#define MARKUP_CITE               8
          175  +#define MARKUP_CODE               9
          176  +#define MARKUP_COL                10
          177  +#define MARKUP_COLGROUP           11
          178  +#define MARKUP_DD                 12
          179  +#define MARKUP_DFN                13
          180  +#define MARKUP_DIV                14
          181  +#define MARKUP_DL                 15
          182  +#define MARKUP_DT                 16
          183  +#define MARKUP_EM                 17
          184  +#define MARKUP_FONT               18
          185  +#define MARKUP_H1                 19
          186  +#define MARKUP_H2                 20
          187  +#define MARKUP_H3                 21
          188  +#define MARKUP_H4                 22
          189  +#define MARKUP_H5                 23
          190  +#define MARKUP_H6                 24
          191  +#define MARKUP_HR                 25
          192  +#define MARKUP_I                  26
          193  +#define MARKUP_IMG                27
          194  +#define MARKUP_KBD                28
          195  +#define MARKUP_LI                 29
          196  +#define MARKUP_NOBR               30
          197  +#define MARKUP_NOWIKI             31
          198  +#define MARKUP_OL                 32
          199  +#define MARKUP_P                  33
          200  +#define MARKUP_PRE                34
          201  +#define MARKUP_S                  35
          202  +#define MARKUP_SAMP               36
          203  +#define MARKUP_SMALL              37
          204  +#define MARKUP_SPAN               38
          205  +#define MARKUP_STRIKE             39
          206  +#define MARKUP_STRONG             40
          207  +#define MARKUP_SUB                41
          208  +#define MARKUP_SUP                42
          209  +#define MARKUP_TABLE              43
          210  +#define MARKUP_TBODY              44
          211  +#define MARKUP_TD                 45
          212  +#define MARKUP_TFOOT              46
          213  +#define MARKUP_TH                 47
          214  +#define MARKUP_THEAD              48
          215  +#define MARKUP_TR                 49
          216  +#define MARKUP_TT                 50
          217  +#define MARKUP_U                  51
          218  +#define MARKUP_UL                 52
          219  +#define MARKUP_VAR                53
          220  +#define MARKUP_VERBATIM           54
   203    221   
   204    222   /*
   205    223   ** The various markup is divided into the following types:
   206    224   */
   207    225   #define MUTYPE_SINGLE      0x0001   /* <img>, <br>, or <hr> */
   208    226   #define MUTYPE_BLOCK       0x0002   /* Forms a new paragraph. ex: <p>, <h2> */
   209    227   #define MUTYPE_FONT        0x0004   /* Font changes. ex: <b>, <font>, <sub> */
................................................................................
   229    247     const char *zName;       /* Name of the markup */
   230    248     char iCode;              /* The MARKUP_* code */
   231    249     short int iType;         /* The MUTYPE_* code */
   232    250     int allowedAttr;         /* Allowed attributes on this markup */
   233    251   } aMarkup[] = {
   234    252    { 0,               MARKUP_INVALID,      0,                    0  },
   235    253    { "a",             MARKUP_A,            MUTYPE_HYPERLINK,
   236         -                    AMSK_HREF|AMSK_NAME|AMSK_CLASS },
   237         - { "address",       MARKUP_ADDRESS,      MUTYPE_BLOCK,         0  },
   238         - { "b",             MARKUP_B,            MUTYPE_FONT,          0  },
   239         - { "big",           MARKUP_BIG,          MUTYPE_FONT,          0  },
   240         - { "blockquote",    MARKUP_BLOCKQUOTE,   MUTYPE_BLOCK,         0  },
   241         - { "br",            MARKUP_BR,           MUTYPE_SINGLE,        AMSK_CLEAR  },
   242         - { "center",        MARKUP_CENTER,       MUTYPE_BLOCK,         0  },
   243         - { "cite",          MARKUP_CITE,         MUTYPE_FONT,          0  },
   244         - { "code",          MARKUP_CODE,         MUTYPE_FONT,          0  },
   245         - { "dd",            MARKUP_DD,           MUTYPE_LI,            0  },
   246         - { "dfn",           MARKUP_DFN,          MUTYPE_FONT,          0  },
   247         - { "div",           MARKUP_DIV,          MUTYPE_BLOCK,         AMSK_ID|AMSK_CLASS      },
   248         - { "dl",            MARKUP_DL,           MUTYPE_LIST,          AMSK_COMPACT },
   249         - { "dt",            MARKUP_DT,           MUTYPE_LI,            0  },
   250         - { "em",            MARKUP_EM,           MUTYPE_FONT,          0  },
          254  +                    AMSK_HREF|AMSK_NAME|AMSK_CLASS|AMSK_TARGET|AMSK_STYLE },
          255  + { "address",       MARKUP_ADDRESS,      MUTYPE_BLOCK,         AMSK_STYLE },
          256  + { "b",             MARKUP_B,            MUTYPE_FONT,          AMSK_STYLE },
          257  + { "big",           MARKUP_BIG,          MUTYPE_FONT,          AMSK_STYLE },
          258  + { "blockquote",    MARKUP_BLOCKQUOTE,   MUTYPE_BLOCK,         AMSK_STYLE },
          259  + { "br",            MARKUP_BR,           MUTYPE_SINGLE,        AMSK_CLEAR },
          260  + { "center",        MARKUP_CENTER,       MUTYPE_BLOCK,         AMSK_STYLE },
          261  + { "cite",          MARKUP_CITE,         MUTYPE_FONT,          AMSK_STYLE },
          262  + { "code",          MARKUP_CODE,         MUTYPE_FONT,          AMSK_STYLE },
          263  + { "col",           MARKUP_COL,          MUTYPE_SINGLE,
          264  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE },
          265  + { "colgroup",      MARKUP_COLGROUP,     MUTYPE_BLOCK,
          266  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE},
          267  + { "dd",            MARKUP_DD,           MUTYPE_LI,            AMSK_STYLE },
          268  + { "dfn",           MARKUP_DFN,          MUTYPE_FONT,          AMSK_STYLE },
          269  + { "div",           MARKUP_DIV,          MUTYPE_BLOCK,
          270  +                    AMSK_ID|AMSK_CLASS|AMSK_STYLE },
          271  + { "dl",            MARKUP_DL,           MUTYPE_LIST,
          272  +                    AMSK_COMPACT|AMSK_STYLE },
          273  + { "dt",            MARKUP_DT,           MUTYPE_LI,            AMSK_STYLE },
          274  + { "em",            MARKUP_EM,           MUTYPE_FONT,          AMSK_STYLE },
   251    275    { "font",          MARKUP_FONT,         MUTYPE_FONT,
   252         -                    AMSK_COLOR|AMSK_FACE|AMSK_SIZE   },
   253         - { "h1",            MARKUP_H1,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   254         - { "h2",            MARKUP_H2,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   255         - { "h3",            MARKUP_H3,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   256         - { "h4",            MARKUP_H4,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   257         - { "h5",            MARKUP_H5,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   258         - { "h6",            MARKUP_H6,           MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
          276  +                    AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE },
          277  + { "h1",            MARKUP_H1,           MUTYPE_BLOCK,
          278  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          279  + { "h2",            MARKUP_H2,           MUTYPE_BLOCK,
          280  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          281  + { "h3",            MARKUP_H3,           MUTYPE_BLOCK,
          282  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          283  + { "h4",            MARKUP_H4,           MUTYPE_BLOCK,
          284  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          285  + { "h5",            MARKUP_H5,           MUTYPE_BLOCK,
          286  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          287  + { "h6",            MARKUP_H6,           MUTYPE_BLOCK,
          288  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
   259    289    { "hr",            MARKUP_HR,           MUTYPE_SINGLE,
   260         -                    AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH|AMSK_CLASS  },
   261         - { "i",             MARKUP_I,            MUTYPE_FONT,          0  },
          290  +                    AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH|
          291  +                    AMSK_STYLE|AMSK_CLASS  },
          292  + { "i",             MARKUP_I,            MUTYPE_FONT,          AMSK_STYLE },
   262    293    { "img",           MARKUP_IMG,          MUTYPE_SINGLE,
   263    294                       AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT|
   264         -                    AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH  },
   265         - { "kbd",           MARKUP_KBD,          MUTYPE_FONT,          0  },
          295  +                    AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH|AMSK_STYLE  },
          296  + { "kbd",           MARKUP_KBD,          MUTYPE_FONT,          AMSK_STYLE },
   266    297    { "li",            MARKUP_LI,           MUTYPE_LI,
   267         -                    AMSK_TYPE|AMSK_VALUE  },
          298  +                    AMSK_TYPE|AMSK_VALUE|AMSK_STYLE  },
   268    299    { "nobr",          MARKUP_NOBR,         MUTYPE_FONT,          0  },
   269    300    { "nowiki",        MARKUP_NOWIKI,       MUTYPE_SPECIAL,       0  },
   270    301    { "ol",            MARKUP_OL,           MUTYPE_LIST,
   271         -                    AMSK_START|AMSK_TYPE|AMSK_COMPACT  },
   272         - { "p",             MARKUP_P,            MUTYPE_BLOCK,         AMSK_ALIGN|AMSK_CLASS  },
   273         - { "pre",           MARKUP_PRE,          MUTYPE_BLOCK,         0  },
   274         - { "s",             MARKUP_S,            MUTYPE_FONT,          0  },
   275         - { "samp",          MARKUP_SAMP,         MUTYPE_FONT,          0  },
   276         - { "small",         MARKUP_SMALL,        MUTYPE_FONT,          0  },
   277         - { "strike",        MARKUP_STRIKE,       MUTYPE_FONT,          0  },
   278         - { "strong",        MARKUP_STRONG,       MUTYPE_FONT,          0  },
   279         - { "sub",           MARKUP_SUB,          MUTYPE_FONT,          0  },
   280         - { "sup",           MARKUP_SUP,          MUTYPE_FONT,          0  },
          302  +                    AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE  },
          303  + { "p",             MARKUP_P,            MUTYPE_BLOCK,
          304  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          305  + { "pre",           MARKUP_PRE,          MUTYPE_BLOCK,         AMSK_STYLE },
          306  + { "s",             MARKUP_S,            MUTYPE_FONT,          AMSK_STYLE },
          307  + { "samp",          MARKUP_SAMP,         MUTYPE_FONT,          AMSK_STYLE },
          308  + { "small",         MARKUP_SMALL,        MUTYPE_FONT,          AMSK_STYLE },
          309  + { "span",          MARKUP_SPAN,         MUTYPE_BLOCK,
          310  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
          311  + { "strike",        MARKUP_STRIKE,       MUTYPE_FONT,          AMSK_STYLE },
          312  + { "strong",        MARKUP_STRONG,       MUTYPE_FONT,          AMSK_STYLE },
          313  + { "sub",           MARKUP_SUB,          MUTYPE_FONT,          AMSK_STYLE },
          314  + { "sup",           MARKUP_SUP,          MUTYPE_FONT,          AMSK_STYLE },
   281    315    { "table",         MARKUP_TABLE,        MUTYPE_TABLE,
   282    316                       AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
   283         -                    AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS  },
          317  +                    AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS|
          318  +                    AMSK_STYLE  },
          319  + { "tbody",         MARKUP_TBODY,        MUTYPE_BLOCK,
          320  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
   284    321    { "td",            MARKUP_TD,           MUTYPE_TD,
   285    322                       AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
   286         -                    AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS  },
          323  +                    AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE  },
          324  + { "tfoot",         MARKUP_TFOOT,        MUTYPE_BLOCK,
          325  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
   287    326    { "th",            MARKUP_TH,           MUTYPE_TD,
   288    327                       AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
   289         -                    AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS  },
          328  +                    AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE  },
          329  + { "thead",         MARKUP_THEAD,        MUTYPE_BLOCK,
          330  +                    AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE  },
   290    331    { "tr",            MARKUP_TR,           MUTYPE_TR,
   291         -                    AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN|AMSK_CLASS  },
   292         - { "tt",            MARKUP_TT,           MUTYPE_FONT,          0  },
   293         - { "u",             MARKUP_U,            MUTYPE_FONT,          0  },
          332  +                    AMSK_ALIGN|AMSK_BGCOLOR|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE },
          333  + { "tt",            MARKUP_TT,           MUTYPE_FONT,          AMSK_STYLE },
          334  + { "u",             MARKUP_U,            MUTYPE_FONT,          AMSK_STYLE },
   294    335    { "ul",            MARKUP_UL,           MUTYPE_LIST,
   295         -                    AMSK_TYPE|AMSK_COMPACT  },
   296         - { "var",           MARKUP_VAR,          MUTYPE_FONT,          0  },
   297         - { "verbatim",      MARKUP_VERBATIM,     MUTYPE_SPECIAL,       AMSK_ID|AMSK_TYPE },
          336  +                    AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE  },
          337  + { "var",           MARKUP_VAR,          MUTYPE_FONT,          AMSK_STYLE },
          338  + { "verbatim",      MARKUP_VERBATIM,     MUTYPE_SPECIAL,
          339  +                    AMSK_ID|AMSK_TYPE },
   298    340   };
          341  +
          342  +void show_allowed_wiki_markup( void ){
          343  +  int i; /* loop over allowedAttr */
          344  +
          345  +  for( i=1 ; i<=sizeof(aMarkup)/sizeof(aMarkup[0]) - 1 ; i++ ){
          346  +    @ &lt;%s(aMarkup[i].zName)&gt;
          347  +  }
          348  +}
   299    349   
   300    350   /*
   301    351   ** Use binary search to locate a tag in the aMarkup[] table.
   302    352   */
   303    353   static int findTag(const char *z){
   304    354     int i, c, first, last;
   305    355     first = 1;
   306    356     last = sizeof(aMarkup)/sizeof(aMarkup[0]) - 1;
   307    357     while( first<=last ){
   308    358       i = (first+last)/2;
   309         -    c = strcmp(aMarkup[i].zName, z);
          359  +    c = fossil_strcmp(aMarkup[i].zName, z);
   310    360       if( c==0 ){
   311    361         assert( aMarkup[i].iCode==i );
   312    362         return i;
   313    363       }else if( c<0 ){
   314    364         first = i+1;
   315    365       }else{
   316    366         last = i-1;
................................................................................
   318    368     }
   319    369     return MARKUP_INVALID;
   320    370   }
   321    371   
   322    372   /*
   323    373   ** Token types
   324    374   */
   325         -#define TOKEN_MARKUP        1    /* <...> */
   326         -#define TOKEN_CHARACTER     2    /* "&" or "<" not part of markup */
   327         -#define TOKEN_LINK          3    /* [...] */
   328         -#define TOKEN_PARAGRAPH     4    /* blank lines */
   329         -#define TOKEN_NEWLINE       5    /* A single "\n" */
   330         -#define TOKEN_BUL_LI        6    /*  "  *  " */
   331         -#define TOKEN_NUM_LI        7    /*  "  #  " */
   332         -#define TOKEN_ENUM          8    /*  "  \(?\d+[.)]?  " */
   333         -#define TOKEN_INDENT        9    /*  "   " */
   334         -#define TOKEN_RAW           10   /* Output exactly (used when wiki-use-html==1) */
   335         -#define TOKEN_TEXT          11   /* None of the above */
   336         -
   337         -/*
   338         -** State flags
   339         -*/
   340         -#define AT_NEWLINE          0x001  /* At start of a line */
   341         -#define AT_PARAGRAPH        0x002  /* At start of a paragraph */
   342         -#define ALLOW_WIKI          0x004  /* Allow wiki markup */
   343         -#define FONT_MARKUP_ONLY    0x008  /* Only allow MUTYPE_FONT markup */
   344         -#define INLINE_MARKUP_ONLY  0x010  /* Allow only "inline" markup */
   345         -#define IN_LIST             0x020  /* Within wiki <ul> or <ol> */
   346         -#define WIKI_USE_HTML       0x040  /* wiki-use-html option = on */
          375  +#define TOKEN_MARKUP        1  /* <...> */
          376  +#define TOKEN_CHARACTER     2  /* "&" or "<" not part of markup */
          377  +#define TOKEN_LINK          3  /* [...] */
          378  +#define TOKEN_PARAGRAPH     4  /* blank lines */
          379  +#define TOKEN_NEWLINE       5  /* A single "\n" */
          380  +#define TOKEN_BUL_LI        6  /*  "  *  " */
          381  +#define TOKEN_NUM_LI        7  /*  "  #  " */
          382  +#define TOKEN_ENUM          8  /*  "  \(?\d+[.)]?  " */
          383  +#define TOKEN_INDENT        9  /*  "   " */
          384  +#define TOKEN_RAW           10 /* Output exactly (used when wiki-use-html==1) */
          385  +#define TOKEN_TEXT          11 /* None of the above */
          386  +
          387  +/*
          388  +** State flags.  Save the lower 16 bits for the WIKI_* flags.
          389  +*/
          390  +#define AT_NEWLINE          0x0010000  /* At start of a line */
          391  +#define AT_PARAGRAPH        0x0020000  /* At start of a paragraph */
          392  +#define ALLOW_WIKI          0x0040000  /* Allow wiki markup */
          393  +#define ALLOW_LINKS         0x0080000  /* Allow [...] hyperlinks */
          394  +#define FONT_MARKUP_ONLY    0x0100000  /* Only allow MUTYPE_FONT markup */
          395  +#define INLINE_MARKUP_ONLY  0x0200000  /* Allow only "inline" markup */
          396  +#define IN_LIST             0x0400000  /* Within wiki <ul> or <ol> */
   347    397   
   348    398   /*
   349    399   ** Current state of the rendering engine
   350    400   */
   351    401   typedef struct Renderer Renderer;
   352    402   struct Renderer {
   353    403     Blob *pOut;                 /* Output appended to this blob */
   354    404     int state;                  /* Flag that govern rendering */
          405  +  unsigned renderFlags;       /* Flags from the client */
   355    406     int wikiList;               /* Current wiki list type */
   356    407     int inVerbatim;             /* True in <verbatim> mode */
   357    408     int preVerbState;           /* Value of state prior to verbatim */
   358    409     int wantAutoParagraph;      /* True if a <p> is desired */
   359    410     int inAutoParagraph;        /* True if within an automatic paragraph */
   360    411     const char *zVerbatimId;    /* The id= attribute of <verbatim> */
   361    412     int nStack;                 /* Number of elements on the stack */
................................................................................
   376    427   */
   377    428   static int wikiUsesHtml(void){
   378    429     static int r = -1;
   379    430     if( r<0 ) r = db_get_boolean("wiki-use-html", 0);
   380    431     return r;
   381    432   }
   382    433   
   383         -
   384    434   /*
   385    435   ** z points to a "<" character.  Check to see if this is the start of
   386    436   ** a valid markup.  If it is, return the total number of characters in
   387    437   ** the markup including the initial "<" and the terminating ">".  If
   388    438   ** it is not well-formed markup, return 0.
   389    439   */
   390    440   static int markupLength(const char *z){
   391    441     int n = 1;
   392    442     int inparen = 0;
   393    443     int c;
   394    444     if( z[n]=='/' ){ n++; }
   395         -  if( !isalpha(z[n]) ) return 0;
   396         -  while( isalnum(z[n]) ){ n++; }
   397         -  if( (c = z[n])!='>' && !isspace(c) ) return 0;
          445  +  if( !fossil_isalpha(z[n]) ) return 0;
          446  +  while( fossil_isalnum(z[n]) || z[n]=='-' ){ n++; }
          447  +  c = z[n];
          448  +  if( c=='/' && z[n+1]=='>' ){ return n+2; }
          449  +  if( c!='>' && !fossil_isspace(c) ) return 0;
   398    450     while( (c = z[n])!=0 && (c!='>' || inparen) ){
   399    451       if( c==inparen ){
   400    452         inparen = 0;
   401         -    }else if( c=='"' || c=='\'' ){
          453  +    }else if( inparen==0 && (c=='"' || c=='\'') ){
   402    454         inparen = c;
   403    455       }
   404    456       n++;
   405    457     }
   406    458     if( z[n]!='>' ) return 0;
   407    459     return n+1;
   408    460   }
................................................................................
   411    463   ** z points to a "\n" character.  Check to see if this newline is
   412    464   ** followed by one or more blank lines.  If it is, return the number
   413    465   ** of characters through the closing "\n".  If not, return 0.
   414    466   */
   415    467   static int paragraphBreakLength(const char *z){
   416    468     int i, n;
   417    469     int nNewline = 1;
   418         -  for(i=1, n=0; isspace(z[i]); i++){
          470  +  for(i=1, n=0; fossil_isspace(z[i]); i++){
   419    471       if( z[i]=='\n' ){
   420    472         nNewline++;
   421    473         n = i;
   422    474       }
   423    475     }
   424    476     if( nNewline>=2 ){
   425    477       return n+1;
................................................................................
   435    487   ** Interesting characters are:
   436    488   **
   437    489   **      <
   438    490   **      &
   439    491   **      \n
   440    492   **      [
   441    493   **
   442         -** The "[" and "\n" are only considered interesting if the "useWiki"
   443         -** flag is set.
          494  +** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI.
          495  +** The "\n" is only considered interesting if the flags constains ALLOW_WIKI.
   444    496   */
   445         -static int textLength(const char *z, int useWiki){
          497  +static int textLength(const char *z, int flags){
   446    498     int n = 0;
   447         -  int c;
   448         -  while( (c = z[0])!=0 && c!='<' && c!='&' &&
   449         -               (useWiki==0 || (c!='[' && c!='\n')) ){
          499  +  int c, x1, x2;
          500  +
          501  +  if( flags & ALLOW_WIKI ){
          502  +    x1 = '[';
          503  +    x2 = '\n';
          504  +  }else if( flags & ALLOW_LINKS ){
          505  +    x1 = '[';
          506  +    x2 = 0;
          507  +  }else{
          508  +    x1 = x2 = 0;
          509  +  }
          510  +  while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
   450    511       n++;
   451    512       z++;
   452    513     }
   453    514     return n;
   454    515   }
   455    516   
   456    517   /*
   457    518   ** Return true if z[] begins with an HTML character element.
   458    519   */
   459    520   static int isElement(const char *z){
   460    521     int i;
   461    522     assert( z[0]=='&' );
   462    523     if( z[1]=='#' ){
   463         -    for(i=2; isdigit(z[i]); i++){}
          524  +    for(i=2; fossil_isdigit(z[i]); i++){}
   464    525       return i>2 && z[i]==';';
   465    526     }else{
   466         -    for(i=1; isalpha(z[i]); i++){}
          527  +    for(i=1; fossil_isalpha(z[i]); i++){}
   467    528       return i>1 && z[i]==';';
   468    529     }
   469    530   }
   470    531   
   471    532   /*
   472    533   ** Check to see if the z[] string is the beginning of a wiki list item.
   473    534   ** If it is, return the length of the bullet text.  Otherwise return 0.
................................................................................
   485    546     n++;
   486    547     i = 0;
   487    548     while( z[n]==' ' || z[n]=='\t' ){
   488    549       if( z[n]=='\t' ) i++;
   489    550       i++;
   490    551       n++;
   491    552     }
   492         -  if( i<2 || isspace(z[n]) ) return 0;
          553  +  if( i<2 || fossil_isspace(z[n]) ) return 0;
   493    554     return n;
   494    555   }
   495    556   
   496    557   /*
   497    558   ** Check to see if the z[] string is the beginning of a enumeration value.
   498    559   ** If it is, return the length of the bullet text.  Otherwise return 0.
   499    560   **
................................................................................
   510    571     i = 0;
   511    572     while( z[n]==' ' || z[n]=='\t' ){
   512    573       if( z[n]=='\t' ) i++;
   513    574       i++;
   514    575       n++;
   515    576     }
   516    577     if( i<2 ) return 0;
   517         -  for(i=0; isdigit(z[n]); i++, n++){}
          578  +  for(i=0; fossil_isdigit(z[n]); i++, n++){}
   518    579     if( i==0 ) return 0;
   519    580     if( z[n]=='.' ){
   520    581       n++;
   521    582     }
   522    583     i = 0;
   523    584     while( z[n]==' ' || z[n]=='\t' ){
   524    585       if( z[n]=='\t' ) i++;
   525    586       i++;
   526    587       n++;
   527    588     }
   528         -  if( i<2 || isspace(z[n]) ) return 0;
          589  +  if( i<2 || fossil_isspace(z[n]) ) return 0;
   529    590     return n;
   530    591   }
   531    592   
   532    593   /*
   533    594   ** Check to see if the z[] string is the beginning of an indented
   534    595   ** paragraph.  If it is, return the length of the indent.  Otherwise
   535    596   ** return 0.
................................................................................
   539    600     n = 0;
   540    601     i = 0;
   541    602     while( z[n]==' ' || z[n]=='\t' ){
   542    603       if( z[n]=='\t' ) i++;
   543    604       i++;
   544    605       n++;
   545    606     }
   546         -  if( i<2 || isspace(z[n]) ) return 0;
          607  +  if( i<2 || fossil_isspace(z[n]) ) return 0;
   547    608     return n;
   548    609   }
   549    610   
   550    611   /*
   551    612   ** Check to see if the z[] string is a wiki hyperlink.  If it is,
   552    613   ** return the length of the hyperlink.  Otherwise return 0.
   553    614   */
................................................................................
   586    647     }
   587    648     if( (p->state & ALLOW_WIKI)!=0 ){
   588    649       if( z[0]=='\n' ){
   589    650         n = paragraphBreakLength(z);
   590    651         if( n>0 ){
   591    652           *pTokenType = TOKEN_PARAGRAPH;
   592    653           return n;
   593         -      }else if( isspace(z[1]) ){
          654  +      }else if( fossil_isspace(z[1]) ){
   594    655           *pTokenType = TOKEN_NEWLINE;
   595    656           return 1;
   596    657         }
   597    658       }
   598         -    if( (p->state & AT_NEWLINE)!=0 && isspace(z[0]) ){
          659  +    if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){
   599    660         n = listItemLength(z, '*');
   600    661         if( n>0 ){
   601    662           *pTokenType = TOKEN_BUL_LI;
   602    663           return n;
   603    664         }
   604    665         n = listItemLength(z, '#');
   605    666         if( n>0 ){
................................................................................
   608    669         }
   609    670         n = enumLength(z);
   610    671         if( n>0 ){
   611    672           *pTokenType = TOKEN_ENUM;
   612    673           return n;
   613    674         }
   614    675       }
   615         -    if( (p->state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){
          676  +    if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
   616    677         n = indentLength(z);
   617    678         if( n>0 ){
   618    679           *pTokenType = TOKEN_INDENT;
   619    680           return n;
   620    681         }
   621    682       }
   622    683       if( z[0]=='[' && (n = linkLength(z))>0 ){
   623    684         *pTokenType = TOKEN_LINK;
   624    685         return n;
   625    686       }
          687  +  }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
          688  +    *pTokenType = TOKEN_LINK;
          689  +    return n;
   626    690     }
   627    691     *pTokenType = TOKEN_TEXT;
   628         -  return 1 + textLength(z+1, p->state & ALLOW_WIKI);
          692  +  return 1 + textLength(z+1, p->state);
   629    693   }
   630    694   
   631    695   /*
   632    696   ** Parse only Wiki links, return everything else as TOKEN_RAW.
   633    697   **
   634    698   ** z points to the start of a token.  Return the number of
   635    699   ** characters in that token. Write the token type into *pTokenType.
................................................................................
   679    743       p->endTag = 1;
   680    744       i = 2;
   681    745     }else{
   682    746       p->endTag = 0;
   683    747       i = 1;
   684    748     }
   685    749     j = 0;
   686         -  while( isalnum(z[i]) ){
   687         -    if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
          750  +  while( fossil_isalnum(z[i]) ){
          751  +    if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
   688    752       i++;
   689    753     }
   690    754     zTag[j] = 0;
   691    755     p->iCode = findTag(zTag);
   692    756     p->iType = aMarkup[p->iCode].iType;
   693    757     p->nAttr = 0;
   694         -  while( isspace(z[i]) ){ i++; }
   695         -  while( p->nAttr<8 && isalpha(z[i]) ){
          758  +  c = 0;
          759  +  if( z[i]=='-' ){
          760  +    p->aAttr[0].iACode = iACode = ATTR_ID;
          761  +    i++;
          762  +    p->aAttr[0].zValue = &z[i];
          763  +    while( fossil_isalnum(z[i]) ){ i++; }
          764  +    p->aAttr[0].cTerm = c = z[i];
          765  +    z[i++] = 0;
          766  +    p->nAttr = 1;
          767  +    if( c=='>' ) return;
          768  +  }
          769  +  while( fossil_isspace(z[i]) ){ i++; }
          770  +  while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){
   696    771       int attrOk;    /* True to preserver attribute.  False to ignore it */
   697    772       j = 0;
   698         -    while( isalnum(z[i]) ){
   699         -      if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
          773  +    while( fossil_isalnum(z[i]) ){
          774  +      if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
   700    775         i++;
   701    776       }
   702    777       zTag[j] = 0;
   703    778       p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
   704    779       attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
   705         -    while( isspace(z[i]) ){ z++; }
          780  +    while( fossil_isspace(z[i]) ){ z++; }
   706    781       if( z[i]!='=' ){
   707    782         p->aAttr[p->nAttr].zValue = 0;
   708    783         p->aAttr[p->nAttr].cTerm = 0;
   709    784         c = 0;
   710    785       }else{
   711    786         i++;
   712         -      while( isspace(z[i]) ){ z++; }
          787  +      while( fossil_isspace(z[i]) ){ z++; }
   713    788         if( z[i]=='"' ){
   714    789           i++;
   715    790           zValue = &z[i];
   716    791           while( z[i] && z[i]!='"' ){ i++; }
   717    792         }else if( z[i]=='\'' ){
   718    793           i++;
   719    794           zValue = &z[i];
   720    795           while( z[i] && z[i]!='\'' ){ i++; }
   721    796         }else{
   722    797           zValue = &z[i];
   723         -        while( !isspace(z[i]) && z[i]!='>' ){ z++; }
          798  +        while( !fossil_isspace(z[i]) && z[i]!='>' ){ z++; }
   724    799         }
   725    800         if( attrOk ){
   726    801           p->aAttr[p->nAttr].zValue = zValue;
   727    802           p->aAttr[p->nAttr].cTerm = c = z[i];
   728    803           z[i] = 0;
   729    804         }
   730    805         i++;
   731    806       }
   732    807       if( attrOk ){
   733    808         seen |= aAttribute[iACode].iMask;
   734    809         p->nAttr++;
   735    810       }
   736         -    while( isspace(z[i]) ){ i++; }
          811  +    while( fossil_isspace(z[i]) ){ i++; }
   737    812       if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
   738    813     }
   739    814   }
   740    815   
   741    816   /*
   742    817   ** Render markup on the given blob.
   743    818   */
................................................................................
   746    821     if( p->endTag ){
   747    822       blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
   748    823     }else{
   749    824       blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
   750    825       for(i=0; i<p->nAttr; i++){
   751    826         blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
   752    827         if( p->aAttr[i].zValue ){
   753         -        blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue);
          828  +        const char *zVal = p->aAttr[i].zValue;
          829  +        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
          830  +          blob_appendf(pOut, "=\"%s%s\"", g.zTop, zVal);
          831  +        }else{
          832  +          blob_appendf(pOut, "=\"%s\"", zVal);
          833  +        }
   754    834         }
          835  +    }
          836  +    if (p->iType & MUTYPE_SINGLE){
          837  +      blob_append(pOut, " /", 2);
   755    838       }
   756    839       blob_append(pOut, ">", 1);
   757    840     }
   758    841   }
   759    842   
   760    843   /*
   761    844   ** When the markup was parsed, some "\000" may have been inserted.
................................................................................
   769    852       if( z==0 ) continue;
   770    853       n = strlen(z);
   771    854       z[n] = p->aAttr[i].cTerm;
   772    855     }
   773    856   }
   774    857   
   775    858   /*
   776         -** Return the ID attribute for markup.  Return NULL if there is no
          859  +** Return the value of attribute attrId.  Return NULL if there is no
   777    860   ** ID attribute.
   778    861   */
   779         -static const char *markupId(ParsedMarkup *p){
          862  +static const char *attributeValue(ParsedMarkup *p, int attrId){
   780    863     int i;
   781    864     for(i=0; i<p->nAttr; i++){
   782         -    if( p->aAttr[i].iACode==ATTR_ID ){
          865  +    if( p->aAttr[i].iACode==attrId ){
   783    866         return p->aAttr[i].zValue;
   784    867       }
   785    868     }
   786    869     return 0;
   787    870   }
          871  +
          872  +/*
          873  +** Return the ID attribute for markup.  Return NULL if there is no
          874  +** ID attribute.
          875  +*/
          876  +static const char *markupId(ParsedMarkup *p){
          877  +  return attributeValue(p, ATTR_ID);
          878  +}
          879  +
          880  +/*
          881  +** Check markup pMarkup to see if it is a hyperlink with class "button"
          882  +** that is follows by simple text and an </a> only.  Example:
          883  +**
          884  +**     <a class="button" href="../index.wiki">Index</a>
          885  +**
          886  +** If the markup matches this pattern, and if the WIKI_BUTTONS flag was
          887  +** passed to wiki_convert(), then transform this link into a submenu
          888  +** button, skip the text, and set *pN equal to the total length of the
          889  +** text through the end of </a> and return true.  If the markup does
          890  +** not match or if WIKI_BUTTONS is not set, then make no changes to *pN
          891  +** and return false.
          892  +*/
          893  +static int isButtonHyperlink(
          894  +  Renderer *p,              /* Renderer state */
          895  +  ParsedMarkup *pMarkup,    /* Potential button markup */
          896  +  const char *z,            /* Complete text of Wiki */
          897  +  int *pN                   /* Characters of z[] consumed */
          898  +){
          899  +  const char *zClass;
          900  +  const char *zHref;
          901  +  char *zTag;
          902  +  int i, j;
          903  +  if( (p->state & WIKI_BUTTONS)==0 ) return 0;
          904  +  zClass = attributeValue(pMarkup, ATTR_CLASS);
          905  +  if( zClass==0 ) return 0;
          906  +  if( fossil_strcmp(zClass, "button")!=0 ) return 0;
          907  +  zHref = attributeValue(pMarkup, ATTR_HREF);
          908  +  if( zHref==0 ) return 0;
          909  +  i = *pN;
          910  +  while( z[i] && z[i]!='<' ){ i++; }
          911  +  if( fossil_strnicmp(&z[i], "</a>",4)!=0 ) return 0;
          912  +  for(j=*pN; fossil_isspace(z[j]); j++){}
          913  +  zTag = mprintf("%.*s", i-j, &z[j]);
          914  +  j = (int)strlen(zTag);
          915  +  while( j>0 && fossil_isspace(zTag[j-1]) ){ j--; }
          916  +  if( j==0 ) return 0;
          917  +  style_submenu_element(zTag, zTag, "%s", zHref);
          918  +  *pN = i+4;
          919  +  return 1;
          920  +}
   788    921   
   789    922   /*
   790    923   ** Pop a single element off of the stack.  As the element is popped,
   791    924   ** output its end tag if it is not a </div> tag.
   792    925   */
   793    926   static void popStack(Renderer *p){
   794    927     if( p->nStack ){
................................................................................
   804    937   /*
   805    938   ** Push a new markup value onto the stack.  Enlarge the stack
   806    939   ** if necessary.
   807    940   */
   808    941   static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){
   809    942     if( p->nStack>=p->nAlloc ){
   810    943       p->nAlloc = p->nAlloc*2 + 100;
   811         -    p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0]));
   812         -    if( p->aStack==0 ){
   813         -      fossil_panic("out of memory");
   814         -    }
          944  +    p->aStack = fossil_realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0]));
   815    945     }
   816    946     p->aStack[p->nStack].iCode = elem;
   817    947     p->aStack[p->nStack].zId = zId;
   818    948     p->aStack[p->nStack].allowWiki = w;
   819    949     p->nStack++;
   820    950   }
   821    951   static void pushStack(Renderer *p, int elem){
................................................................................
   846    976   */
   847    977   static int findTagWithId(Renderer *p, int iTag, const char *zId){
   848    978     int i;
   849    979     assert( zId!=0 );
   850    980     for(i=p->nStack-1; i>=0; i--){
   851    981       if( p->aStack[i].iCode!=iTag ) continue;
   852    982       if( p->aStack[i].zId==0 ) continue;
   853         -    if( strcmp(zId, p->aStack[i].zId)!=0 ) continue;
          983  +    if( fossil_strcmp(zId, p->aStack[i].zId)!=0 ) continue;
   854    984       break;
   855    985     }
   856    986     return i;
   857    987   }
   858    988   
   859    989   /*
   860    990   ** Pop the stack until the top-most element of the stack
................................................................................
   877   1007     return p->aStack[i-1].iCode;
   878   1008   }
   879   1009   
   880   1010   /*
   881   1011   ** Begin a new paragraph if that something that is needed.
   882   1012   */
   883   1013   static void startAutoParagraph(Renderer *p){
   884         -  if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
         1014  +  if( p->wantAutoParagraph==0 ) return;
         1015  +  if( p->state & WIKI_LINKSONLY ) return;
         1016  +  if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
   885   1017     blob_appendf(p->pOut, "<p>", -1);
   886   1018     pushStack(p, MARKUP_P);
   887   1019     p->wantAutoParagraph = 0;
   888   1020     p->inAutoParagraph = 1;
   889   1021   }
   890   1022   
   891   1023   /*
................................................................................
   904   1036   */
   905   1037   static int is_valid_uuid(const char *z){
   906   1038     int n = strlen(z);
   907   1039     if( n<4 || n>UUID_SIZE ) return 0;
   908   1040     if( !validate16(z, n) ) return 0;
   909   1041     return 1;
   910   1042   }
         1043  +
         1044  +/*
         1045  +** Return TRUE if a UUID corresponds to an artifact in this
         1046  +** repository.
         1047  +*/
         1048  +static int in_this_repo(const char *zUuid){
         1049  +  static Stmt q;
         1050  +  int rc;
         1051  +  int n;
         1052  +  char zU2[UUID_SIZE+1];
         1053  +  db_static_prepare(&q,
         1054  +     "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2"
         1055  +  );
         1056  +  db_bind_text(&q, ":u", zUuid);
         1057  +  n = (int)strlen(zUuid);
         1058  +  if( n>=sizeof(zU2) ) n = sizeof(zU2)-1;
         1059  +  memcpy(zU2, zUuid, n);
         1060  +  zU2[n-1]++;
         1061  +  zU2[n] = 0;
         1062  +  db_bind_text(&q, ":u2", zU2);
         1063  +  rc = db_step(&q);
         1064  +  db_reset(&q);
         1065  +  return rc==SQLITE_ROW;
         1066  +}
   911   1067   
   912   1068   /*
   913   1069   ** zTarget is guaranteed to be a UUID.  It might be the UUID of a ticket.
   914   1070   ** If it is, store in *pClosed a true or false depending on whether or not
   915   1071   ** the ticket is closed and return true. If zTarget
   916   1072   ** is not the UUID of a ticket, return false.
   917   1073   */
................................................................................
   946   1102       *pClosed = db_column_int(&q, 0);
   947   1103     }else{
   948   1104       rc = 0;
   949   1105     }
   950   1106     db_reset(&q);
   951   1107     return rc;
   952   1108   }
         1109  +
         1110  +/*
         1111  +** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
         1112  +** if there is one) if zTarget is a valid wiki page name.  Return NULL if
         1113  +** zTarget names a page that does not exist.
         1114  +*/
         1115  +static const char *validWikiPageName(Renderer *p, const char *zTarget){
         1116  +  if( strncmp(zTarget, "wiki:", 5)==0
         1117  +      && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
         1118  +    return zTarget+5;
         1119  +  }
         1120  +  if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
         1121  +  if( wiki_name_is_wellformed((const unsigned char *)zTarget)
         1122  +   && ((p->state & WIKI_NOBADLINKS)==0 ||
         1123  +        db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget))
         1124  +  ){
         1125  +    return zTarget;
         1126  +  }
         1127  +  return 0;
         1128  +}
   953   1129   
   954   1130   /*
   955   1131   ** Resolve a hyperlink.  The zTarget argument is the content of the [...]
   956         -** in the wiki.  Append to the output string whatever text is approprate
         1132  +** in the wiki.  Append to the output string whatever text is appropriate
   957   1133   ** for opening the hyperlink.  Write into zClose[0...nClose-1] text that will
   958   1134   ** close the markup.
         1135  +**
         1136  +** If this routine determines that no hyperlink should be generated, then
         1137  +** set zClose[0] to 0.
   959   1138   **
   960   1139   ** Actually, this routine might or might not append the hyperlink, depending
   961   1140   ** on current rendering rules: specifically does the current user have
   962   1141   ** "History" permission.
   963   1142   **
   964   1143   **    [http://www.fossil-scm.org/]
   965   1144   **    [https://www.fossil-scm.org/]
................................................................................
   967   1146   **    [mailto:fossil-users@lists.fossil-scm.org]
   968   1147   **
   969   1148   **    [/path]
   970   1149   **
   971   1150   **    [./relpath]
   972   1151   **
   973   1152   **    [WikiPageName]
         1153  +**    [wiki:WikiPageName]
   974   1154   **
   975   1155   **    [0123456789abcdef]
   976   1156   **
   977   1157   **    [#fragment]
   978   1158   **
   979   1159   **    [2010-02-27 07:13]
   980   1160   */
   981   1161   static void openHyperlink(
   982   1162     Renderer *p,            /* Rendering context */
   983         -  const char *zTarget,    /* Hyperlink traget; text within [...] */
         1163  +  const char *zTarget,    /* Hyperlink target; text within [...] */
   984   1164     char *zClose,           /* Write hyperlink closing text here */
   985         -  int nClose              /* Bytes available in zClose[] */
         1165  +  int nClose,             /* Bytes available in zClose[] */
         1166  +  const char *zOrig       /* Complete document text */
   986   1167   ){
   987   1168     const char *zTerm = "</a>";
         1169  +  const char *z;
         1170  +
   988   1171     assert( nClose>=20 );
   989         -
   990   1172     if( strncmp(zTarget, "http:", 5)==0
   991   1173      || strncmp(zTarget, "https:", 6)==0
   992   1174      || strncmp(zTarget, "ftp:", 4)==0
   993   1175      || strncmp(zTarget, "mailto:", 7)==0
   994   1176     ){
   995   1177       blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
   996         -    /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
   997   1178     }else if( zTarget[0]=='/' ){
   998         -    if( 1 /* g.okHistory */ ){
   999         -      blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
  1000         -    }else{
  1001         -      zTerm = "";
  1002         -    }
  1003         -  }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
  1004         -    if( 1 /* g.okHistory */ ){
  1005         -      blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
  1006         -    }else{
  1007         -      zTerm = "";
  1008         -    }
         1179  +    blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
         1180  +  }else if( zTarget[0]=='.'
         1181  +         && (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/'))
         1182  +         && (p->state & WIKI_LINKSONLY)==0 ){
         1183  +    blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
         1184  +  }else if( zTarget[0]=='#' ){
         1185  +    blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
  1009   1186     }else if( is_valid_uuid(zTarget) ){
  1010   1187       int isClosed = 0;
  1011   1188       if( is_ticket(zTarget, &isClosed) ){
  1012   1189         /* Special display processing for tickets.  Display the hyperlink
  1013   1190         ** as crossed out if the ticket is closed.
  1014   1191         */
  1015   1192         if( isClosed ){
  1016         -        if( g.okHistory ){
  1017         -          blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
  1018         -              g.zBaseURL, zTarget
         1193  +        if( g.perm.Hyperlink ){
         1194  +          blob_appendf(p->pOut,
         1195  +             "%z<span class=\"wikiTagCancelled\">[",
         1196  +             href("%R/info/%s",zTarget)
  1019   1197             );
  1020         -          zTerm = "</s></a>";
         1198  +          zTerm = "]</span></a>";
  1021   1199           }else{
  1022         -          blob_appendf(p->pOut,"<s>");
  1023         -          zTerm = "</s>";
         1200  +          blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">[");
         1201  +          zTerm = "]</span>";
  1024   1202           }
  1025   1203         }else{
  1026         -        if( g.okHistory ){
  1027         -          blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
  1028         -              g.zBaseURL, zTarget
  1029         -          );
         1204  +        if( g.perm.Hyperlink ){
         1205  +          blob_appendf(p->pOut,"%z[", href("%R/info/%s", zTarget));
         1206  +          zTerm = "]</a>";
  1030   1207           }else{
  1031         -          zTerm = "";
         1208  +          blob_appendf(p->pOut, "[");
         1209  +          zTerm = "]";
  1032   1210           }
  1033   1211         }
  1034         -    }else if( g.okHistory ){
  1035         -      blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
         1212  +    }else if( !in_this_repo(zTarget) ){
         1213  +      if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
         1214  +        zTerm = "";
         1215  +      }else{
         1216  +        blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
         1217  +        zTerm = "]</span>";
         1218  +      }
         1219  +    }else if( g.perm.Hyperlink ){
         1220  +      blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
         1221  +      zTerm = "]</a>";
  1036   1222       }
  1037         -  }else if( strlen(zTarget)>=10 && isdigit(zTarget[0]) && zTarget[4]=='-'
         1223  +  }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
  1038   1224               && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
  1039         -    blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget);
  1040         -  }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){
  1041         -    blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget);
  1042         -  }else{
  1043         -    blob_appendf(p->pOut, "[bad-link: %h]", zTarget);
         1225  +    blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
         1226  +  }else if( (z = validWikiPageName(p, zTarget))!=0 ){
         1227  +    blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
         1228  +  }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
         1229  +    /* Probably an array subscript in code */
         1230  +    zTerm = "";
         1231  +  }else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
  1044   1232       zTerm = "";
         1233  +  }else{
         1234  +    blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
         1235  +    zTerm = "</span>";
  1045   1236     }
  1046   1237     assert( strlen(zTerm)<nClose );
  1047         -  strcpy(zClose, zTerm);
         1238  +  sqlite3_snprintf(nClose, zClose, "%s", zTerm);
  1048   1239   }
  1049   1240   
  1050   1241   /*
  1051   1242   ** Check to see if the given parsed markup is the correct
  1052   1243   ** </verbatim> tag.
  1053   1244   */
  1054   1245   static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){
................................................................................
  1055   1246     char *z;
  1056   1247     assert( p->inVerbatim );
  1057   1248     if( pMarkup->iCode!=MARKUP_VERBATIM ) return 0;
  1058   1249     if( !pMarkup->endTag ) return 0;
  1059   1250     if( p->zVerbatimId==0 ) return 1;
  1060   1251     if( pMarkup->nAttr!=1 ) return 0;
  1061   1252     z = pMarkup->aAttr[0].zValue;
  1062         -  return strcmp(z, p->zVerbatimId)==0;
         1253  +  return fossil_strcmp(z, p->zVerbatimId)==0;
  1063   1254   }
  1064   1255   
  1065   1256   /*
  1066   1257   ** Return the MUTYPE for the top of the stack.
  1067   1258   */
  1068   1259   static int stackTopType(Renderer *p){
  1069   1260     if( p->nStack<=0 ) return 0;
................................................................................
  1077   1268   ** This routine will probably modify the content of z[].
  1078   1269   */
  1079   1270   static void wiki_render(Renderer *p, char *z){
  1080   1271     int tokenType;
  1081   1272     ParsedMarkup markup;
  1082   1273     int n;
  1083   1274     int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
  1084         -  int wikiUseHtml = (p->state & WIKI_USE_HTML)!=0;
         1275  +  int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
         1276  +  int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
         1277  +  char *zOrig = z;
         1278  +
         1279  +  /* Make sure the attribute constants and names still align
         1280  +  ** following changes in the attribute list. */
         1281  +  assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
  1085   1282   
  1086   1283     while( z[0] ){
  1087         -    if( wikiUseHtml ){
         1284  +    if( wikiHtmlOnly ){
  1088   1285         n = nextRawToken(z, p, &tokenType);
  1089   1286       }else{
  1090   1287         n = nextWikiToken(z, p, &tokenType);
  1091   1288       }
  1092   1289       p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
  1093   1290       switch( tokenType ){
  1094   1291         case TOKEN_PARAGRAPH: {
................................................................................
  1116   1313           if( inlineOnly ){
  1117   1314             blob_append(p->pOut, " &bull; ", -1);
  1118   1315           }else{
  1119   1316             if( p->wikiList!=MARKUP_UL ){
  1120   1317               if( p->wikiList ){
  1121   1318                 popStackToTag(p, p->wikiList);
  1122   1319               }
         1320  +            endAutoParagraph(p);
  1123   1321               pushStack(p, MARKUP_UL);
  1124   1322               blob_append(p->pOut, "<ul>", 4);
  1125   1323               p->wikiList = MARKUP_UL;
  1126   1324             }
  1127   1325             popStackToTag(p, MARKUP_LI);
  1128   1326             startAutoParagraph(p);
  1129   1327             pushStack(p, MARKUP_LI);
................................................................................
  1135   1333           if( inlineOnly ){
  1136   1334             blob_append(p->pOut, " # ", -1);
  1137   1335           }else{
  1138   1336             if( p->wikiList!=MARKUP_OL ){
  1139   1337               if( p->wikiList ){
  1140   1338                 popStackToTag(p, p->wikiList);
  1141   1339               }
         1340  +            endAutoParagraph(p);
  1142   1341               pushStack(p, MARKUP_OL);
  1143   1342               blob_append(p->pOut, "<ol>", 4);
  1144   1343               p->wikiList = MARKUP_OL;
  1145   1344             }
  1146   1345             popStackToTag(p, MARKUP_LI);
  1147   1346             startAutoParagraph(p);
  1148   1347             pushStack(p, MARKUP_LI);
................................................................................
  1154   1353           if( inlineOnly ){
  1155   1354             blob_appendf(p->pOut, " (%d) ", atoi(z));
  1156   1355           }else{
  1157   1356             if( p->wikiList!=MARKUP_OL ){
  1158   1357               if( p->wikiList ){
  1159   1358                 popStackToTag(p, p->wikiList);
  1160   1359               }
         1360  +            endAutoParagraph(p);
  1161   1361               pushStack(p, MARKUP_OL);
  1162   1362               blob_append(p->pOut, "<ol>", 4);
  1163   1363               p->wikiList = MARKUP_OL;
  1164   1364             }
  1165   1365             popStackToTag(p, MARKUP_LI);
  1166   1366             startAutoParagraph(p);
  1167   1367             pushStack(p, MARKUP_LI);
................................................................................
  1190   1390         }
  1191   1391         case TOKEN_LINK: {
  1192   1392           char *zTarget;
  1193   1393           char *zDisplay = 0;
  1194   1394           int i, j;
  1195   1395           int savedState;
  1196   1396           char zClose[20];
         1397  +        char cS1 = 0;
         1398  +        int iS1 = 0;
  1197   1399   
  1198   1400           startAutoParagraph(p);
  1199   1401           zTarget = &z[1];
  1200   1402           for(i=1; z[i] && z[i]!=']'; i++){
  1201   1403             if( z[i]=='|' && zDisplay==0 ){
  1202   1404               zDisplay = &z[i+1];
  1203         -            z[i] = 0;
  1204         -            for(j=i-1; j>0 && isspace(z[j]); j--){ z[j] = 0; }
         1405  +            for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
         1406  +            iS1 = j;
         1407  +            cS1 = z[j];
         1408  +            z[j] = 0;
  1205   1409             }
  1206   1410           }
  1207   1411           z[i] = 0;
  1208   1412           if( zDisplay==0 ){
  1209   1413             zDisplay = zTarget;
  1210   1414           }else{
  1211         -          while( isspace(*zDisplay) ) zDisplay++;
         1415  +          while( fossil_isspace(*zDisplay) ) zDisplay++;
  1212   1416           }
  1213         -        openHyperlink(p, zTarget, zClose, sizeof(zClose));
  1214         -        savedState = p->state;
  1215         -        p->state &= ~ALLOW_WIKI;
  1216         -        p->state |= FONT_MARKUP_ONLY;
  1217         -        wiki_render(p, zDisplay);
  1218         -        p->state = savedState;
  1219         -        blob_append(p->pOut, zClose, -1);
         1417  +        openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
         1418  +        if( linksOnly || zClose[0]==0 || p->inVerbatim ){
         1419  +          if( cS1 ) z[iS1] = cS1;
         1420  +          if( zClose[0]!=']' ){
         1421  +            blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
         1422  +          }else{
         1423  +            blob_appendf(p->pOut, "%h%s", zTarget, zClose);
         1424  +          }
         1425  +        }else{
         1426  +          savedState = p->state;
         1427  +          p->state &= ~ALLOW_WIKI;
         1428  +          p->state |= FONT_MARKUP_ONLY;
         1429  +          wiki_render(p, zDisplay);
         1430  +          p->state = savedState;
         1431  +          blob_append(p->pOut, zClose, -1);
         1432  +        }
  1220   1433           break;
  1221   1434         }
  1222   1435         case TOKEN_TEXT: {
  1223         -        startAutoParagraph(p);
         1436  +        int i;
         1437  +        for(i=0; i<n && fossil_isspace(z[i]); i++){}
         1438  +        if( i<n ) startAutoParagraph(p);
  1224   1439           blob_append(p->pOut, z, n);
  1225   1440           break;
  1226   1441         }
  1227   1442         case TOKEN_RAW: {
  1228         -        blob_append(p->pOut, z, n);
         1443  +        if( linksOnly ){
         1444  +          htmlize_to_blob(p->pOut, z, n);
         1445  +        }else{
         1446  +          blob_append(p->pOut, z, n);
         1447  +        }
  1229   1448           break;
  1230   1449         }
  1231   1450         case TOKEN_MARKUP: {
  1232   1451           const char *zId;
  1233   1452           int iDiv;
  1234   1453           parseMarkup(&markup, z);
  1235   1454   
  1236   1455           /* Markup of the form </div id=ID> where there is a matching
  1237         -        ** ID somewhere on the stack.  Exit the verbatim if were are in
  1238         -        ** it.  Pop the stack up to the matching <div>.  Discard the
  1239         -        ** </div>
         1456  +        ** ID somewhere on the stack.  Exit any contained verbatim.
         1457  +        ** Pop the stack up to the matching <div>.  Discard the </div>
  1240   1458           */
  1241   1459           if( markup.iCode==MARKUP_DIV && markup.endTag &&
  1242   1460                (zId = markupId(&markup))!=0 &&
  1243   1461                (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0
  1244   1462           ){
  1245   1463             if( p->inVerbatim ){
  1246   1464               p->inVerbatim = 0;
................................................................................
  1316   1534           }else
  1317   1535   
  1318   1536           /* Enter <verbatim> processing.  With verbatim enabled, all other
  1319   1537           ** markup other than the corresponding end-tag with the same ID is
  1320   1538           ** ignored.
  1321   1539           */
  1322   1540           if( markup.iCode==MARKUP_VERBATIM ){
  1323         -          int vAttrIdx, vAttrDidAppend=0;
         1541  +          int ii, vAttrDidAppend=0;
  1324   1542             p->zVerbatimId = 0;
  1325   1543             p->inVerbatim = 1;
  1326   1544             p->preVerbState = p->state;
  1327   1545             p->state &= ~ALLOW_WIKI;
  1328         -          for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
  1329         -            if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
  1330         -              p->zVerbatimId = markup.aAttr[0].zValue;
  1331         -            }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
         1546  +          for(ii=0; ii<markup.nAttr; ii++){
         1547  +            if( markup.aAttr[ii].iACode == ATTR_ID ){
         1548  +              p->zVerbatimId = markup.aAttr[ii].zValue;
         1549  +            }else if( markup.aAttr[ii].iACode==ATTR_TYPE ){
  1332   1550                 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
  1333         -                markup.aAttr[vAttrIdx].zValue);
         1551  +                markup.aAttr[ii].zValue);
  1334   1552                 vAttrDidAppend=1;
         1553  +            }else if( markup.aAttr[ii].iACode==ATTR_LINKS
         1554  +                   && !is_false(markup.aAttr[ii].zValue) ){
         1555  +              p->state |= ALLOW_LINKS;
  1335   1556               }
  1336   1557             }
  1337         -          if( !vAttrDidAppend )
         1558  +          if( !vAttrDidAppend ) {
         1559  +            endAutoParagraph(p);
  1338   1560               blob_append(p->pOut, "<pre class='verbatim'>",-1);
         1561  +          }
  1339   1562             p->wantAutoParagraph = 0;
  1340   1563           }else
  1341   1564           if( markup.iType==MUTYPE_LI ){
  1342   1565             if( backupToType(p, MUTYPE_LIST)==0 ){
         1566  +            endAutoParagraph(p);
  1343   1567               pushStack(p, MARKUP_UL);
  1344   1568               blob_append(p->pOut, "<ul>", 4);
  1345   1569             }
  1346   1570             pushStack(p, MARKUP_LI);
  1347   1571             renderMarkup(p->pOut, &markup);
  1348   1572           }else
  1349   1573           if( markup.iType==MUTYPE_TR ){
................................................................................
  1359   1583                 blob_append(p->pOut, "<tr>", 4);
  1360   1584               }
  1361   1585               pushStack(p, markup.iCode);
  1362   1586               renderMarkup(p->pOut, &markup);
  1363   1587             }
  1364   1588           }else
  1365   1589           if( markup.iType==MUTYPE_HYPERLINK ){
  1366         -          popStackToTag(p, markup.iCode);
  1367         -          startAutoParagraph(p);
  1368         -          renderMarkup(p->pOut, &markup);
  1369         -          pushStack(p, markup.iCode);
         1590  +          if( !isButtonHyperlink(p, &markup, z, &n) ){
         1591  +            popStackToTag(p, markup.iCode);
         1592  +            startAutoParagraph(p);
         1593  +            renderMarkup(p->pOut, &markup);
         1594  +            pushStack(p, markup.iCode);
         1595  +          }
  1370   1596           }else
  1371   1597           {
  1372   1598             if( markup.iType==MUTYPE_FONT ){
  1373   1599               startAutoParagraph(p);
  1374         -          }else if( markup.iType==MUTYPE_BLOCK ){
         1600  +          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
  1375   1601               p->wantAutoParagraph = 0;
         1602  +          }
         1603  +          if(   markup.iCode==MARKUP_HR
         1604  +             || markup.iCode==MARKUP_H1
         1605  +             || markup.iCode==MARKUP_H2
         1606  +             || markup.iCode==MARKUP_H3
         1607  +             || markup.iCode==MARKUP_H4
         1608  +             || markup.iCode==MARKUP_H5
         1609  +             || markup.iCode==MARKUP_P
         1610  +          ){
         1611  +            endAutoParagraph(p);
  1376   1612             }
  1377   1613             if( (markup.iType & MUTYPE_STACK )!=0 ){
  1378   1614               pushStack(p, markup.iCode);
  1379   1615             }
  1380   1616             renderMarkup(p->pOut, &markup);
  1381   1617           }
  1382   1618           break;
  1383   1619         }
  1384   1620       }
  1385   1621       z += n;
  1386   1622     }
  1387   1623   }
  1388   1624   
  1389         -/*
  1390         -** Skip over the UTF-8 Byte-Order-Mark that some broken Windows
  1391         -** tools add to the beginning of text files.
  1392         -*/
  1393         -char *skip_bom(char *z){
  1394         -  static const char bom[] = { 0xEF, 0xBB, 0xBF };
  1395         -  if( z && memcmp(z, bom, 3)==0 ) z += 3;
  1396         -  return z;
  1397         -}
  1398         -
  1399   1625   /*
  1400   1626   ** Transform the text in the pIn blob.  Write the results
  1401   1627   ** into the pOut blob.  The pOut blob should already be
  1402   1628   ** initialized.  The output is merely appended to pOut.
  1403   1629   ** If pOut is NULL, then the output is appended to the CGI
  1404   1630   ** reply.
  1405   1631   */
  1406   1632   void wiki_convert(Blob *pIn, Blob *pOut, int flags){
  1407         -  char *z;
  1408   1633     Renderer renderer;
  1409   1634   
  1410   1635     memset(&renderer, 0, sizeof(renderer));
  1411         -  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
         1636  +  renderer.renderFlags = flags;
         1637  +  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
  1412   1638     if( flags & WIKI_NOBLOCK ){
  1413   1639       renderer.state |= INLINE_MARKUP_ONLY;
  1414   1640     }
  1415   1641     if( flags & WIKI_INLINE ){
  1416   1642       renderer.wantAutoParagraph = 0;
  1417   1643     }else{
  1418   1644       renderer.wantAutoParagraph = 1;
  1419   1645     }
  1420   1646     if( wikiUsesHtml() ){
  1421         -    renderer.state |= WIKI_USE_HTML;
         1647  +    renderer.state |= WIKI_HTMLONLY;
  1422   1648     }
  1423   1649     if( pOut ){
  1424   1650       renderer.pOut = pOut;
  1425   1651     }else{
  1426   1652       renderer.pOut = cgi_output_blob();
  1427   1653     }
  1428   1654   
  1429         -  z = skip_bom(blob_str(pIn));
  1430         -  wiki_render(&renderer, z);
         1655  +  blob_to_utf8_no_bom(pIn, 0);
         1656  +  wiki_render(&renderer, blob_str(pIn));
  1431   1657     endAutoParagraph(&renderer);
  1432   1658     while( renderer.nStack ){
  1433   1659       popStack(&renderer);
  1434   1660     }
  1435   1661     blob_append(renderer.pOut, "\n", 1);
  1436   1662     free(renderer.aStack);
  1437   1663   }
         1664  +
         1665  +/*
         1666  +** Send a string as wiki to CGI output.
         1667  +*/
         1668  +void wiki_write(const char *zIn, int flags){
         1669  +  Blob in;
         1670  +  blob_init(&in, zIn, -1);
         1671  +  wiki_convert(&in, 0, flags);
         1672  +  blob_reset(&in);
         1673  +}
  1438   1674   
  1439   1675   /*
  1440   1676   ** COMMAND: test-wiki-render
         1677  +**
         1678  +** %fossil test-wiki-render FILE [OPTIONS]
         1679  +**
         1680  +** Options:
         1681  +**    --buttons        Set the WIKI_BUTTONS flag
         1682  +**    --htmlonly       Set the WIKI_HTMLONLY flag
         1683  +**    --linksonly      Set the WIKI_LINKSONLY flag
         1684  +**    --nobadlinks     Set the WIKI_NOBADLINKS flag
         1685  +**    --inline         Set the WIKI_INLINE flag
         1686  +**    --noblock        Set the WIKI_NOBLOCK flag
  1441   1687   */
  1442   1688   void test_wiki_render(void){
  1443   1689     Blob in, out;
         1690  +  int flags = 0;
         1691  +  if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
         1692  +  if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
         1693  +  if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
         1694  +  if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
         1695  +  if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
         1696  +  if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
         1697  +  db_find_and_open_repository(0,0);
         1698  +  verify_all_options();
  1444   1699     if( g.argc!=3 ) usage("FILE");
  1445   1700     blob_zero(&out);
  1446   1701     blob_read_from_file(&in, g.argv[2]);
  1447         -  wiki_convert(&in, &out, 0);
         1702  +  wiki_convert(&in, &out, flags);
  1448   1703     blob_write_to_file(&out, "-");
  1449   1704   }
  1450   1705   
  1451   1706   /*
  1452   1707   ** Search for a <title>...</title> at the beginning of a wiki page.
  1453   1708   ** Return true (nonzero) if a title is found.  Return zero if there is
  1454   1709   ** not title.
................................................................................
  1457   1712   ** of the title and initialize pTail to be the text that follows the
  1458   1713   ** title.
  1459   1714   */
  1460   1715   int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
  1461   1716     char *z;
  1462   1717     int i;
  1463   1718     int iStart;
  1464         -  z = skip_bom(blob_str(pIn));
  1465         -  for(i=0; isspace(z[i]); i++){}
         1719  +  blob_to_utf8_no_bom(pIn, 0);
         1720  +  z = blob_str(pIn);
         1721  +  for(i=0; fossil_isspace(z[i]); i++){}
  1466   1722     if( z[i]!='<' ) return 0;
  1467   1723     i++;
  1468   1724     if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
  1469   1725     iStart = i+6;
  1470   1726     for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
  1471   1727     if( z[i]!='<' ) return 0;
  1472   1728     blob_init(pTitle, &z[iStart], i-iStart);
................................................................................
  1493   1749     int flags          /* wiki parsing flags */
  1494   1750   ){
  1495   1751     Renderer renderer;
  1496   1752     int tokenType;
  1497   1753     ParsedMarkup markup;
  1498   1754     int n;
  1499   1755     int inlineOnly;
  1500         -  int wikiUseHtml = 0;
         1756  +  int wikiHtmlOnly = 0;
  1501   1757   
  1502   1758     memset(&renderer, 0, sizeof(renderer));
  1503   1759     renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
  1504   1760     if( flags & WIKI_NOBLOCK ){
  1505   1761       renderer.state |= INLINE_MARKUP_ONLY;
  1506   1762     }
  1507   1763     if( wikiUsesHtml() ){
  1508         -    renderer.state |= WIKI_USE_HTML;
  1509         -    wikiUseHtml = 1;
         1764  +    renderer.state |= WIKI_HTMLONLY;
         1765  +    wikiHtmlOnly = 1;
  1510   1766     }
  1511   1767     inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
  1512   1768     if( replaceFlag ){
  1513   1769       db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
  1514   1770                     srctype, srcid);
  1515   1771     }
  1516   1772   
  1517   1773     while( z[0] ){
  1518         -    if( wikiUseHtml ){
         1774  +    if( wikiHtmlOnly ){
  1519   1775         n = nextRawToken(z, &renderer, &tokenType);
  1520   1776       }else{
  1521   1777         n = nextWikiToken(z, &renderer, &tokenType);
  1522   1778       }
  1523   1779       switch( tokenType ){
  1524   1780         case TOKEN_LINK: {
  1525   1781           char *zTarget;
................................................................................
  1623   1879           }else
  1624   1880   
  1625   1881           /* Enter <verbatim> processing.  With verbatim enabled, all other
  1626   1882           ** markup other than the corresponding end-tag with the same ID is
  1627   1883           ** ignored.
  1628   1884           */
  1629   1885           if( markup.iCode==MARKUP_VERBATIM ){
  1630         -          int vAttrIdx, vAttrDidAppend=0;
         1886  +          int vAttrIdx;
  1631   1887             renderer.zVerbatimId = 0;
  1632   1888             renderer.inVerbatim = 1;
  1633   1889             renderer.preVerbState = renderer.state;
  1634   1890             renderer.state &= ~ALLOW_WIKI;
  1635   1891             for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
  1636   1892               if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
  1637   1893                 renderer.zVerbatimId = markup.aAttr[0].zValue;
  1638         -            }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
  1639         -              vAttrDidAppend=1;
  1640   1894               }
  1641   1895             }
  1642   1896             renderer.wantAutoParagraph = 0;
  1643   1897           }
  1644   1898   
  1645   1899           /* Restore the input text to its original configuration
  1646   1900           */
................................................................................
  1648   1902           break;
  1649   1903         }
  1650   1904         default: {
  1651   1905           break;
  1652   1906         }
  1653   1907       }
  1654   1908       z += n;
         1909  +  }
         1910  +  free(renderer.aStack);
         1911  +}
         1912  +
         1913  +/*
         1914  +** Get the next HTML token.
         1915  +**
         1916  +** z points to the start of a token.  Return the number of
         1917  +** characters in that token.
         1918  +*/
         1919  +static int nextHtmlToken(const char *z){
         1920  +  int n;
         1921  +  if( z[0]=='<' ){
         1922  +    n = markupLength(z);
         1923  +    if( n<=0 ) n = 1;
         1924  +  }else if( fossil_isspace(z[0]) ){
         1925  +    for(n=1; z[n] && fossil_isspace(z[n]); n++){}
         1926  +  }else{
         1927  +    for(n=1; z[n] && z[n]!='<' && !fossil_isspace(z[n]); n++){}
         1928  +  }
         1929  +  return n;
         1930  +}
         1931  +
         1932  +/*
         1933  +** Attempt to reformat messy HTML to be easily readable by humans.
         1934  +**
         1935  +**    *  Try to keep lines less than 80 characters in length
         1936  +**    *  Collapse white space into a single space
         1937  +**    *  Put a blank line before:
         1938  +**          <blockquote><center><code><hN><p><pre><table>
         1939  +**    *  Put a newline after <br> and <hr>
         1940  +**    *  Start each of the following elements on a new line:
         1941  +**          <address><cite><dd><div><dl><dt><li><ol><samp>
         1942  +**          <tbody><td><tfoot><th><thead><tr><ul>
         1943  +**
         1944  +** Except, do not do any reformatting inside of <pre>...</pre>
         1945  +*/
         1946  +void htmlTidy(const char *zIn, Blob *pOut){
         1947  +  int n;
         1948  +  int nPre = 0;
         1949  +  int iCur = 0;
         1950  +  int wantSpace = 0;
         1951  +  int omitSpace = 1;
         1952  +  while( zIn[0] ){
         1953  +    n = nextHtmlToken(zIn);
         1954  +    if( zIn[0]=='<' && n>1 ){
         1955  +      int i, j;
         1956  +      int isCloseTag;
         1957  +      int eTag;
         1958  +      int eType;
         1959  +      char zTag[32];
         1960  +      isCloseTag = zIn[1]=='/';
         1961  +      for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
         1962  +         zTag[i] = fossil_tolower(zIn[j]);
         1963  +      }
         1964  +      zTag[i] = 0;
         1965  +      eTag = findTag(zTag);
         1966  +      eType = aMarkup[eTag].iType;
         1967  +      if( eTag==MARKUP_PRE ){
         1968  +        if( isCloseTag ){
         1969  +          nPre--;
         1970  +          blob_append(pOut, zIn, n);
         1971  +          zIn += n;
         1972  +          if( nPre==0 ){ blob_append(pOut, "\n", 1); iCur = 0; }
         1973  +          continue;
         1974  +        }else{
         1975  +          if( iCur && nPre==0 ){ blob_append(pOut, "\n", 1); iCur = 0; }
         1976  +          nPre++;
         1977  +        }
         1978  +      }else if( eType & (MUTYPE_BLOCK|MUTYPE_TABLE) ){
         1979  +        if( !isCloseTag && nPre==0 && blob_size(pOut)>0 ){
         1980  +          blob_append(pOut, "\n\n", 1 + (iCur>0));
         1981  +          iCur = 0;
         1982  +        }
         1983  +        wantSpace = 0;
         1984  +        omitSpace = 1;
         1985  +      }else if( (eType & (MUTYPE_LIST|MUTYPE_LI|MUTYPE_TR|MUTYPE_TD))!=0
         1986  +             || eTag==MARKUP_HR
         1987  +      ){
         1988  +        if( nPre==0 && (!isCloseTag || (eType&MUTYPE_LIST)!=0) && iCur>0 ){
         1989  +          blob_append(pOut, "\n", 1);
         1990  +          iCur = 0;
         1991  +        }
         1992  +        wantSpace = 0;
         1993  +        omitSpace = 1;
         1994  +      }
         1995  +      if( wantSpace && nPre==0 ){
         1996  +        if( iCur+n+1>=80 ){
         1997  +          blob_append(pOut, "\n", 1);
         1998  +          iCur = 0;
         1999  +        }else{
         2000  +          blob_append(pOut, " ", 1);
         2001  +          iCur++;
         2002  +        }
         2003  +      }
         2004  +      blob_append(pOut, zIn, n);
         2005  +      iCur += n;
         2006  +      wantSpace = 0;
         2007  +      if( eTag==MARKUP_BR || eTag==MARKUP_HR ){
         2008  +        blob_append(pOut, "\n", 1);
         2009  +        iCur = 0;
         2010  +      }
         2011  +    }else if( fossil_isspace(zIn[0]) ){
         2012  +      if( nPre ){
         2013  +        blob_append(pOut, zIn, n);
         2014  +      }else{
         2015  +        wantSpace = !omitSpace;
         2016  +      }
         2017  +    }else{
         2018  +      if( wantSpace && nPre==0 ){
         2019  +        if( iCur+n+1>=80 ){
         2020  +          blob_append(pOut, "\n", 1);
         2021  +          iCur = 0;
         2022  +        }else{
         2023  +          blob_append(pOut, " ", 1);
         2024  +          iCur++;
         2025  +        }
         2026  +      }
         2027  +      blob_append(pOut, zIn, n);
         2028  +      iCur += n;
         2029  +      wantSpace = omitSpace = 0;
         2030  +    }
         2031  +    zIn += n;
         2032  +  }
         2033  +  if( iCur ) blob_append(pOut, "\n", 1);
         2034  +}
         2035  +
         2036  +/*
         2037  +** COMMAND: test-html-tidy
         2038  +*/
         2039  +void test_html_tidy(void){
         2040  +  Blob in, out;
         2041  +  int i;
         2042  +
         2043  +  for(i=2; i<g.argc; i++){
         2044  +    blob_read_from_file(&in, g.argv[i]);
         2045  +    blob_zero(&out);
         2046  +    htmlTidy(blob_str(&in), &out);
         2047  +    blob_reset(&in);
         2048  +    fossil_puts(blob_str(&out), 0);
         2049  +    blob_reset(&out);
  1655   2050     }
  1656   2051   }

Changes to src/winhttp.c.

    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file implements a very simple (and low-performance) HTTP server
    19         -** for windows.
           19  +** for windows. It also implements a Windows Service which allows the HTTP
           20  +** server to be run without any user logged on.
    20     21   */
    21         -#ifdef __MINGW32__           /* This code is for win32 only */
    22     22   #include "config.h"
    23         -#include "winhttp.h"
           23  +#ifdef _WIN32
           24  +/* This code is for win32 only */
    24     25   #include <windows.h>
           26  +#include "winhttp.h"
    25     27   
    26     28   /*
    27     29   ** The HttpRequest structure holds information about each incoming
    28     30   ** HTTP request.
    29     31   */
    30     32   typedef struct HttpRequest HttpRequest;
    31     33   struct HttpRequest {
    32         -  int id;             /* ID counter */
    33         -  SOCKET s;           /* Socket on which to receive data */
    34         -  SOCKADDR_IN addr;   /* Address from which data is coming */
    35         -  const char *zNotFound;  /* --notfound option, or an empty string */
           34  +  int id;                /* ID counter */
           35  +  SOCKET s;              /* Socket on which to receive data */
           36  +  SOCKADDR_IN addr;      /* Address from which data is coming */
           37  +  const char *zOptions;  /* --notfound and/or --localauth options */
    36     38   };
    37     39   
    38     40   /*
    39     41   ** Prefix for a temporary file.
    40     42   */
    41     43   static char *zTempPrefix;
    42     44   
................................................................................
    45     47   ** length and return it.  Return 0 if there is no Content-Length:
    46     48   ** header line.
    47     49   */
    48     50   static int find_content_length(const char *zHdr){
    49     51     while( *zHdr ){
    50     52       if( zHdr[0]=='\n' ){
    51     53         if( zHdr[1]=='\r' ) return 0;
    52         -      if( strncasecmp(&zHdr[1], "content-length:", 15)==0 ){
           54  +      if( fossil_strnicmp(&zHdr[1], "content-length:", 15)==0 ){
    53     55           return atoi(&zHdr[17]);
    54     56         }
    55     57       }
    56     58       zHdr++;
    57     59     }
    58     60     return 0;
    59     61   }
    60     62   
    61     63   /*
    62     64   ** Process a single incoming HTTP request.
    63     65   */
    64         -void win32_process_one_http_request(void *pAppData){
           66  +static void win32_process_one_http_request(void *pAppData){
    65     67     HttpRequest *p = (HttpRequest*)pAppData;
    66     68     FILE *in = 0, *out = 0;
    67     69     int amt, got;
    68     70     int wanted = 0;
    69     71     char *z;
    70         -  char zRequestFName[100];
    71         -  char zReplyFName[100];
           72  +  char zRequestFName[MAX_PATH];
           73  +  char zReplyFName[MAX_PATH];
    72     74     char zCmd[2000];          /* Command-line to process the request */
    73     75     char zHdr[2000];          /* The HTTP request header */
    74     76   
    75         -  sprintf(zRequestFName, "%s_in%d.txt", zTempPrefix, p->id);
    76         -  sprintf(zReplyFName, "%s_out%d.txt", zTempPrefix, p->id);
           77  +  sqlite3_snprintf(MAX_PATH, zRequestFName,
           78  +                   "%s_in%d.txt", zTempPrefix, p->id);
           79  +  sqlite3_snprintf(MAX_PATH, zReplyFName,
           80  +                   "%s_out%d.txt", zTempPrefix, p->id);
    77     81     amt = 0;
    78     82     while( amt<sizeof(zHdr) ){
    79     83       got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0);
    80     84       if( got==SOCKET_ERROR ) goto end_request;
    81     85       if( got==0 ){
    82     86         wanted = 0;
    83     87         break;
................................................................................
    87     91       z = strstr(zHdr, "\r\n\r\n");
    88     92       if( z ){
    89     93         wanted = find_content_length(zHdr) + (&z[4]-zHdr) - amt;
    90     94         break;
    91     95       }
    92     96     }
    93     97     if( amt>=sizeof(zHdr) ) goto end_request;
    94         -  out = fopen(zRequestFName, "wb");
           98  +  out = fossil_fopen(zRequestFName, "wb");
    95     99     if( out==0 ) goto end_request;
    96    100     fwrite(zHdr, 1, amt, out);
    97    101     while( wanted>0 ){
    98    102       got = recv(p->s, zHdr, sizeof(zHdr), 0);
    99    103       if( got==SOCKET_ERROR ) goto end_request;
   100    104       if( got ){
   101    105         fwrite(zHdr, 1, got, out);
................................................................................
   102    106       }else{
   103    107         break;
   104    108       }
   105    109       wanted -= got;
   106    110     }
   107    111     fclose(out);
   108    112     out = 0;
   109         -  sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s",
   110         -    g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, 
   111         -    inet_ntoa(p->addr.sin_addr), p->zNotFound
          113  +  sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http \"%s\" %s %s %s --nossl%s",
          114  +    g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName,
          115  +    inet_ntoa(p->addr.sin_addr), p->zOptions
   112    116     );
   113         -  portable_system(zCmd);
   114         -  in = fopen(zReplyFName, "rb");
          117  +  fossil_system(zCmd);
          118  +  in = fossil_fopen(zReplyFName, "rb");
   115    119     if( in ){
   116    120       while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
   117    121         send(p->s, zHdr, got, 0);
   118    122       }
   119    123     }
   120    124   
   121    125   end_request:
   122    126     if( out ) fclose(out);
   123    127     if( in ) fclose(in);
   124    128     closesocket(p->s);
   125         -  unlink(zRequestFName);
   126         -  unlink(zReplyFName);
          129  +  file_delete(zRequestFName);
          130  +  file_delete(zReplyFName);
   127    131     free(p);
   128    132   }
   129    133   
   130    134   /*
   131    135   ** Start a listening socket and process incoming HTTP requests on
   132    136   ** that socket.
   133    137   */
   134    138   void win32_http_server(
   135    139     int mnPort, int mxPort,   /* Range of allowed TCP port numbers */
   136    140     const char *zBrowser,     /* Command to launch browser.  (Or NULL) */
   137    141     const char *zStopper,     /* Stop server when this file is exists (Or NULL) */
   138         -  const char *zNotFound     /* The --notfound option, or NULL */
          142  +  const char *zNotFound,    /* The --notfound option, or NULL */
          143  +  const char *zFileGlob,    /* The --fileglob option, or NULL */
          144  +  int flags                 /* One or more HTTP_SERVER_ flags */
   139    145   ){
   140    146     WSADATA wd;
   141    147     SOCKET s = INVALID_SOCKET;
   142    148     SOCKADDR_IN addr;
   143    149     int idCnt = 0;
   144    150     int iPort = mnPort;
   145         -  char *zNotFoundOption;
          151  +  Blob options;
          152  +  WCHAR zTmpPath[MAX_PATH];
   146    153   
   147         -  if( zStopper ) unlink(zStopper);
          154  +  if( zStopper ) file_delete(zStopper);
          155  +  blob_zero(&options);
   148    156     if( zNotFound ){
   149         -    zNotFoundOption = mprintf(" --notfound %s", zNotFound);
   150         -  }else{
   151         -    zNotFoundOption = "";
          157  +    blob_appendf(&options, " --notfound %s", zNotFound);
          158  +  }
          159  +  if( zFileGlob ){
          160  +    blob_appendf(&options, " --files-urlenc %T", zFileGlob);
          161  +  }
          162  +  if( g.useLocalauth ){
          163  +    blob_appendf(&options, " --localauth");
   152    164     }
   153    165     if( WSAStartup(MAKEWORD(1,1), &wd) ){
   154    166       fossil_fatal("unable to initialize winsock");
   155    167     }
   156    168     while( iPort<=mxPort ){
   157    169       s = socket(AF_INET, SOCK_STREAM, 0);
   158    170       if( s==INVALID_SOCKET ){
   159    171         fossil_fatal("unable to create a socket");
   160    172       }
   161    173       addr.sin_family = AF_INET;
   162    174       addr.sin_port = htons(iPort);
   163         -    addr.sin_addr.s_addr = htonl(INADDR_ANY);
          175  +    if( flags & HTTP_SERVER_LOCALHOST ){
          176  +      addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
          177  +    }else{
          178  +      addr.sin_addr.s_addr = htonl(INADDR_ANY);
          179  +    }
   164    180       if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
   165    181         closesocket(s);
   166    182         iPort++;
   167    183         continue;
   168    184       }
   169    185       if( listen(s, SOMAXCONN)==SOCKET_ERROR ){
   170    186         closesocket(s);
................................................................................
   177    193       if( mnPort==mxPort ){
   178    194         fossil_fatal("unable to open listening socket on ports %d", mnPort);
   179    195       }else{
   180    196         fossil_fatal("unable to open listening socket on any"
   181    197                      " port in the range %d..%d", mnPort, mxPort);
   182    198       }
   183    199     }
   184         -  zTempPrefix = mprintf("fossil_server_P%d_", iPort);
   185         -  printf("Listening for HTTP requests on TCP port %d\n", iPort);
          200  +  if( !GetTempPathW(MAX_PATH, zTmpPath) ){
          201  +    fossil_fatal("unable to get path to the temporary directory.");
          202  +  }
          203  +  zTempPrefix = mprintf("%sfossil_server_P%d_", fossil_unicode_to_utf8(zTmpPath), iPort);
          204  +  fossil_print("Listening for HTTP requests on TCP port %d\n", iPort);
   186    205     if( zBrowser ){
   187    206       zBrowser = mprintf(zBrowser, iPort);
   188         -    printf("Launch webbrowser: %s\n", zBrowser);
   189         -    portable_system(zBrowser);
          207  +    fossil_print("Launch webbrowser: %s\n", zBrowser);
          208  +    fossil_system(zBrowser);
   190    209     }
   191         -  printf("Type Ctrl-C to stop the HTTP server\n");
          210  +  fossil_print("Type Ctrl-C to stop the HTTP server\n");
          211  +  /* Set the service status to running and pass the listener socket to the
          212  +  ** service handling procedures. */
          213  +  win32_http_service_running(s);
   192    214     for(;;){
   193    215       SOCKET client;
   194    216       SOCKADDR_IN client_addr;
   195    217       HttpRequest *p;
   196    218       int len = sizeof(client_addr);
          219  +    int wsaError;
   197    220   
   198    221       client = accept(s, (struct sockaddr*)&client_addr, &len);
   199         -    if( zStopper && file_size(zStopper)>=0 ){
          222  +    if( client==INVALID_SOCKET ){
          223  +      /* If the service control handler has closed the listener socket,
          224  +      ** cleanup and return, otherwise report a fatal error. */
          225  +      wsaError =  WSAGetLastError();
          226  +      if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
          227  +        WSACleanup();
          228  +        return;
          229  +      }else{
          230  +        closesocket(s);
          231  +        WSACleanup();
          232  +        fossil_fatal("error from accept()");
          233  +      }
          234  +    }else if( zStopper && file_size(zStopper)>=0 ){
   200    235         break;
   201    236       }
   202         -    if( client==INVALID_SOCKET ){
   203         -      closesocket(s);
   204         -      fossil_fatal("error from accept()");
   205         -    }
   206         -    p = malloc( sizeof(*p) );
   207         -    if( p==0 ){
   208         -      fossil_fatal("out of memory");
   209         -    }
          237  +    p = fossil_malloc( sizeof(*p) );
   210    238       p->id = ++idCnt;
   211    239       p->s = client;
   212    240       p->addr = client_addr;
   213         -    p->zNotFound = zNotFoundOption;
          241  +    p->zOptions = blob_str(&options);
   214    242       _beginthread(win32_process_one_http_request, 0, (void*)p);
   215    243     }
   216    244     closesocket(s);
   217    245     WSACleanup();
   218    246   }
   219    247   
   220         -#endif /* __MINGW32__  -- This code is for win32 only */
          248  +/*
          249  +** The HttpService structure is used to pass information to the service main
          250  +** function and to the service control handler function.
          251  +*/
          252  +typedef struct HttpService HttpService;
          253  +struct HttpService {
          254  +  int port;                 /* Port on which the http server should run */
          255  +  const char *zNotFound;    /* The --notfound option, or NULL */
          256  +  const char *zFileGlob;    /* The --files option, or NULL */
          257  +  int flags;                /* One or more HTTP_SERVER_ flags */
          258  +  int isRunningAsService;   /* Are we running as a service ? */
          259  +  const WCHAR *zServiceName;/* Name of the service */
          260  +  SOCKET s;                 /* Socket on which the http server listens */
          261  +};
          262  +
          263  +/*
          264  +** Variables used for running as windows service.
          265  +*/
          266  +static HttpService hsData = {8080, NULL, NULL, 0, 0, NULL, INVALID_SOCKET};
          267  +static SERVICE_STATUS ssStatus;
          268  +static SERVICE_STATUS_HANDLE sshStatusHandle;
          269  +
          270  +/*
          271  +** Get message string of the last system error. Return a pointer to the
          272  +** message string. Call fossil_unicode_free() to deallocate any memory used
          273  +** to store the message string when done.
          274  +*/
          275  +static char *win32_get_last_errmsg(void){
          276  +  DWORD nMsg;
          277  +  DWORD nErr = GetLastError();
          278  +  LPWSTR tmp = NULL;
          279  +  char *zMsg = NULL;
          280  +
          281  +  /* Try first to get the error text in English. */
          282  +  nMsg = FormatMessageW(
          283  +           FORMAT_MESSAGE_ALLOCATE_BUFFER |
          284  +           FORMAT_MESSAGE_FROM_SYSTEM     |
          285  +           FORMAT_MESSAGE_IGNORE_INSERTS,
          286  +           NULL,
          287  +           nErr,
          288  +           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
          289  +           (LPWSTR) &tmp,
          290  +           0,
          291  +           NULL
          292  +         );
          293  +  if( !nMsg ){
          294  +    /* No english, get what the system has available. */
          295  +    nMsg = FormatMessageW(
          296  +             FORMAT_MESSAGE_ALLOCATE_BUFFER |
          297  +             FORMAT_MESSAGE_FROM_SYSTEM     |
          298  +             FORMAT_MESSAGE_IGNORE_INSERTS,
          299  +             NULL,
          300  +             nErr,
          301  +             0,
          302  +             (LPWSTR) &tmp,
          303  +             0,
          304  +             NULL
          305  +           );
          306  +  }
          307  +  if( nMsg ){
          308  +    zMsg = fossil_unicode_to_utf8(tmp);
          309  +  }else{
          310  +    fossil_fatal("unable to get system error message.");
          311  +  }
          312  +  if( tmp ){
          313  +    LocalFree((HLOCAL) tmp);
          314  +  }
          315  +  return zMsg;
          316  +}
          317  +
          318  +/*
          319  +** Report the current status of the service to the service control manager.
          320  +** Make sure that during service startup no control codes are accepted.
          321  +*/
          322  +static void win32_report_service_status(
          323  +  DWORD dwCurrentState,     /* The current state of the service */
          324  +  DWORD dwWin32ExitCode,    /* The error code to report */
          325  +  DWORD dwWaitHint          /* The estimated time for a pending operation */
          326  +){
          327  +  if( dwCurrentState==SERVICE_START_PENDING ){
          328  +    ssStatus.dwControlsAccepted = 0;
          329  +  }else{
          330  +    ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
          331  +  }
          332  +  ssStatus.dwCurrentState = dwCurrentState;
          333  +  ssStatus.dwWin32ExitCode = dwWin32ExitCode;
          334  +  ssStatus.dwWaitHint = dwWaitHint;
          335  +
          336  +  if( (dwCurrentState==SERVICE_RUNNING) ||
          337  +      (dwCurrentState==SERVICE_STOPPED) ){
          338  +    ssStatus.dwCheckPoint = 0;
          339  +  }else{
          340  +    ssStatus.dwCheckPoint++;
          341  +  }
          342  +  SetServiceStatus(sshStatusHandle, &ssStatus);
          343  +  return ;
          344  +}
          345  +
          346  +/*
          347  +** Handle control codes sent from the service control manager.
          348  +** The control dispatcher in the main thread of the service process invokes
          349  +** this function whenever it receives a control request from the service
          350  +** control manager.
          351  +*/
          352  +static void WINAPI win32_http_service_ctrl(
          353  +  DWORD dwCtrlCode
          354  +){
          355  +  switch( dwCtrlCode ){
          356  +    case SERVICE_CONTROL_STOP: {
          357  +      win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
          358  +      if( hsData.s != INVALID_SOCKET ){
          359  +        closesocket(hsData.s);
          360  +      }
          361  +      win32_report_service_status(ssStatus.dwCurrentState, NO_ERROR, 0);
          362  +      break;
          363  +    }
          364  +    default: {
          365  +      break;
          366  +    }
          367  +  }
          368  +  return;
          369  +}
          370  +
          371  +/*
          372  +** This is the main entry point for the service.
          373  +** When the service control manager receives a request to start the service,
          374  +** it starts the service process (if it is not already running). The main
          375  +** thread of the service process calls the StartServiceCtrlDispatcher
          376  +** function with a pointer to an array of SERVICE_TABLE_ENTRY structures.
          377  +** Then the service control manager sends a start request to the service
          378  +** control dispatcher for this service process. The service control dispatcher
          379  +** creates a new thread to execute the ServiceMain function (this function)
          380  +** of the service being started.
          381  +*/
          382  +static void WINAPI win32_http_service_main(
          383  +  DWORD argc,              /* Number of arguments in argv */
          384  +  LPWSTR *argv             /* Arguments passed */
          385  +){
          386  +
          387  +  /* Update the service information. */
          388  +  hsData.isRunningAsService = 1;
          389  +  if( argc>0 ){
          390  +    hsData.zServiceName = argv[0];
          391  +  }
          392  +
          393  +  /* Register the service control handler function */
          394  +  sshStatusHandle = RegisterServiceCtrlHandlerW(L"", win32_http_service_ctrl);
          395  +  if( !sshStatusHandle ){
          396  +    win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
          397  +    return;
          398  +  }
          399  +
          400  +  /* Set service specific data and report that the service is starting. */
          401  +  ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
          402  +  ssStatus.dwServiceSpecificExitCode = 0;
          403  +  win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000);
          404  +
          405  +   /* Execute the http server */
          406  +  win32_http_server(hsData.port, hsData.port,
          407  +                    NULL, NULL, hsData.zNotFound, hsData.zFileGlob,
          408  +                    hsData.flags);
          409  +
          410  +  /* Service has stopped now. */
          411  +  win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
          412  +  return;
          413  +}
          414  +
          415  +/*
          416  +** When running as service, update the HttpService structure with the
          417  +** listener socket and update the service status. This procedure must be
          418  +** called from the http server when he is ready to accept connections.
          419  +*/
          420  +LOCAL void win32_http_service_running(SOCKET s){
          421  +  if( hsData.isRunningAsService ){
          422  +    hsData.s = s;
          423  +    win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
          424  +  }
          425  +}
          426  +
          427  +/*
          428  +** Try to start the http server as a windows service. If we are running in
          429  +** a interactive console session, this routine fails and returns a non zero
          430  +** integer value. When running as service, this routine does not return until
          431  +** the service is stopped. In this case, the return value is zero.
          432  +*/
          433  +int win32_http_service(
          434  +  int nPort,                /* TCP port number */
          435  +  const char *zNotFound,    /* The --notfound option, or NULL */
          436  +  const char *zFileGlob,    /* The --files option, or NULL */
          437  +  int flags                 /* One or more HTTP_SERVER_ flags */
          438  +){
          439  +  /* Define the service table. */
          440  +  SERVICE_TABLE_ENTRYW ServiceTable[] =
          441  +    {{L"", (LPSERVICE_MAIN_FUNCTIONW)win32_http_service_main}, {NULL, NULL}};
          442  +
          443  +  /* Initialize the HttpService structure. */
          444  +  hsData.port = nPort;
          445  +  hsData.zNotFound = zNotFound;
          446  +  hsData.zFileGlob = zFileGlob;
          447  +  hsData.flags = flags;
          448  +
          449  +  /* Try to start the control dispatcher thread for the service. */
          450  +  if( !StartServiceCtrlDispatcherW(ServiceTable) ){
          451  +    if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
          452  +      return 1;
          453  +    }else{
          454  +      fossil_fatal("error from StartServiceCtrlDispatcher()");
          455  +    }
          456  +  }
          457  +  return 0;
          458  +}
          459  +
          460  +/* dupe ifdef needed for mkindex
          461  +** COMMAND: winsrv*
          462  +** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS?
          463  +**
          464  +** Where METHOD is one of: create delete show start stop.
          465  +**
          466  +** The winsrv command manages Fossil as a Windows service.  This allows
          467  +** (for example) Fossil to be running in the background when no user
          468  +** is logged in.
          469  +**
          470  +** In the following description of the methods, "Fossil-DSCM" will be
          471  +** used as the default SERVICE-NAME:
          472  +**
          473  +**    fossil winsrv create ?SERVICE-NAME? ?OPTIONS?
          474  +**
          475  +**         Creates a service. Available options include:
          476  +**
          477  +**         -D|--display DISPLAY-NAME
          478  +**
          479  +**              Sets the display name of the service. This name is shown
          480  +**              by graphical interface programs. By default, the display name
          481  +**              equals to the service name.
          482  +**
          483  +**         -S|--start TYPE
          484  +**
          485  +**              Sets the start type of the service. TYPE can be "manual",
          486  +**              which means you need to start the service yourself with the
          487  +**              'fossil winsrv start' command or with the "net start" command
          488  +**              from the operating system. If TYPE is set to "auto", the service
          489  +**              will be started automatically by the system during startup.
          490  +**
          491  +**         -U|--username USERNAME
          492  +**
          493  +**              Specifies the user account which will be used to run the
          494  +**              service. The account needs the "Logon as a service" right
          495  +**              enabled in its profile. Specify local accounts as follows:
          496  +**              ".\\USERNAME". By default, the "LocalSystem" account will be
          497  +**              used.
          498  +**
          499  +**         -W|--password PASSWORD
          500  +**
          501  +**              Password for the user account.
          502  +**
          503  +**         The following options are more or less the same as for the "server"
          504  +**         command and influence the behaviour of the http server:
          505  +**
          506  +**         -P|--port TCPPORT
          507  +**
          508  +**              Specifies the TCP port (default port is 8080) on which the
          509  +**              server should listen.
          510  +**
          511  +**         -R|--repository REPOSITORY
          512  +**
          513  +**              Specifies the name of the repository to be served.
          514  +**              The repository option may be omitted if the working directory
          515  +**              is within an open checkout.
          516  +**              The REPOSITORY can be a directory (aka folder) that contains
          517  +**              one or more repositories with names ending in ".fossil".
          518  +**              In that case, the first element of the URL is used to select
          519  +**              among the various repositories.
          520  +**
          521  +**         --notfound URL
          522  +**
          523  +**              If REPOSITORY is a directory that contains one or more
          524  +**              repositories with names of the form "*.fossil" then the
          525  +**              first element of the URL  pathname selects among the various
          526  +**              repositories. If the pathname does not select a valid
          527  +**              repository and the --notfound option is available,
          528  +**              then the server redirects (HTTP code 302) to the URL of
          529  +**              --notfound.
          530  +**
          531  +**         --localauth
          532  +**
          533  +**              Enables automatic login if the --localauth option is present
          534  +**              and the "localauth" setting is off and the connection is from
          535  +**              localhost.
          536  +**
          537  +**
          538  +**    fossil winsrv delete ?SERVICE-NAME?
          539  +**
          540  +**         Deletes a service. If the service is currently running, it will be
          541  +**         stopped first and then deleted.
          542  +**
          543  +**
          544  +**    fossil winsrv show ?SERVICE-NAME?
          545  +**
          546  +**         Shows how the service is configured and its current state.
          547  +**
          548  +**
          549  +**    fossil winsrv start ?SERVICE-NAME?
          550  +**
          551  +**         Start the service.
          552  +**
          553  +**
          554  +**    fossil winsrv stop ?SERVICE-NAME?
          555  +**
          556  +**         Stop the service.
          557  +**
          558  +**
          559  +** NOTE: This command is available on Windows operating systems only and
          560  +**       requires administrative rights on the machine executed.
          561  +**
          562  +*/
          563  +void cmd_win32_service(void){
          564  +  int n;
          565  +  const char *zMethod;
          566  +  const char *zSvcName = "Fossil-DSCM";    /* Default service name */
          567  +
          568  +  if( g.argc<3 ){
          569  +    usage("create|delete|show|start|stop ...");
          570  +  }
          571  +  zMethod = g.argv[2];
          572  +  n = strlen(zMethod);
          573  +
          574  +  if( strncmp(zMethod, "create", n)==0 ){
          575  +    SC_HANDLE hScm;
          576  +    SC_HANDLE hSvc;
          577  +    SERVICE_DESCRIPTIONW
          578  +      svcDescr = {L"Fossil - Distributed Software Configuration Management"};
          579  +    char *zErrFmt = "unable to create service '%s': %s";
          580  +    DWORD dwStartType = SERVICE_DEMAND_START;
          581  +    const char *zDisplay    = find_option("display", "D", 1);
          582  +    const char *zStart      = find_option("start", "S", 1);
          583  +    const char *zUsername   = find_option("username", "U", 1);
          584  +    const char *zPassword   = find_option("password", "W", 1);
          585  +    const char *zPort       = find_option("port", "P", 1);
          586  +    const char *zNotFound   = find_option("notfound", 0, 1);
          587  +    const char *zFileGlob   = find_option("files", 0, 1);
          588  +    const char *zLocalAuth  = find_option("localauth", 0, 0);
          589  +    const char *zRepository = find_option("repository", "R", 1);
          590  +    Blob binPath;
          591  +
          592  +    verify_all_options();
          593  +    if( g.argc==4 ){
          594  +      zSvcName = g.argv[3];
          595  +    }else if( g.argc>4 ){
          596  +      fossil_fatal("to much arguments for create method.");
          597  +    }
          598  +    /* Process service creation specific options. */
          599  +    if( !zDisplay ){
          600  +      zDisplay = zSvcName;
          601  +    }
          602  +    if( zStart ){
          603  +      if( strncmp(zStart, "auto", strlen(zStart))==0 ){
          604  +        dwStartType = SERVICE_AUTO_START;
          605  +      }else if( strncmp(zStart, "manual", strlen(zStart))==0 ){
          606  +        dwStartType = SERVICE_DEMAND_START;
          607  +      }else{
          608  +        fossil_fatal(zErrFmt, zSvcName,
          609  +                     "specify 'auto' or 'manual' for the '-S|--start' option");
          610  +      }
          611  +    }
          612  +    /* Process options for Fossil running as server. */
          613  +    if( zPort && (atoi(zPort)<=0) ){
          614  +      fossil_fatal(zErrFmt, zSvcName,
          615  +                   "port number must be in the range 1 - 65535.");
          616  +    }
          617  +    if( !zRepository ){
          618  +      db_must_be_within_tree();
          619  +    }else if( file_isdir(zRepository)==1 ){
          620  +      g.zRepositoryName = mprintf("%s", zRepository);
          621  +      file_simplify_name(g.zRepositoryName, -1, 0);
          622  +    }else{
          623  +      db_open_repository(zRepository);
          624  +    }
          625  +    db_close(0);
          626  +    /* Build the fully-qualified path to the service binary file. */
          627  +    blob_zero(&binPath);
          628  +    blob_appendf(&binPath, "\"%s\" server", g.nameOfExe);
          629  +    if( zPort ) blob_appendf(&binPath, " --port %s", zPort);
          630  +    if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound);
          631  +    if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob);
          632  +    if( zLocalAuth ) blob_append(&binPath, " --localauth", -1);
          633  +    blob_appendf(&binPath, " \"%s\"", g.zRepositoryName);
          634  +    /* Create the service. */
          635  +    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          636  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          637  +    hSvc = CreateServiceW(
          638  +             hScm,                                    /* Handle to the SCM */
          639  +             fossil_utf8_to_unicode(zSvcName),        /* Name of the service */
          640  +             fossil_utf8_to_unicode(zDisplay),        /* Display name */
          641  +             SERVICE_ALL_ACCESS,                      /* Desired access */
          642  +             SERVICE_WIN32_OWN_PROCESS,               /* Service type */
          643  +             dwStartType,                             /* Start type */
          644  +             SERVICE_ERROR_NORMAL,                    /* Error control */
          645  +             fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */
          646  +             NULL,                                    /* Load ordering group */
          647  +             NULL,                                    /* Tag value */
          648  +             NULL,                                    /* Service dependencies */
          649  +             fossil_utf8_to_unicode(zUsername),       /* Service account */
          650  +             fossil_utf8_to_unicode(zPassword)        /* Account password */
          651  +           );
          652  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          653  +    /* Set the service description. */
          654  +    ChangeServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr);
          655  +    fossil_print("Service '%s' successfully created.\n", zSvcName);
          656  +    CloseServiceHandle(hSvc);
          657  +    CloseServiceHandle(hScm);
          658  +  }else
          659  +  if( strncmp(zMethod, "delete", n)==0 ){
          660  +    SC_HANDLE hScm;
          661  +    SC_HANDLE hSvc;
          662  +    SERVICE_STATUS sstat;
          663  +    char *zErrFmt = "unable to delete service '%s': %s";
          664  +
          665  +    verify_all_options();
          666  +    if( g.argc==4 ){
          667  +      zSvcName = g.argv[3];
          668  +    }else if( g.argc>4 ){
          669  +      fossil_fatal("to much arguments for delete method.");
          670  +    }
          671  +    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          672  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          673  +    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
          674  +                        SERVICE_ALL_ACCESS);
          675  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          676  +    QueryServiceStatus(hSvc, &sstat);
          677  +    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
          678  +      fossil_print("Stopping service '%s'", zSvcName);
          679  +      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
          680  +        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          681  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          682  +        }
          683  +      }
          684  +      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
          685  +        Sleep(100);
          686  +        fossil_print(".");
          687  +        QueryServiceStatus(hSvc, &sstat);
          688  +      }
          689  +      fossil_print("\nService '%s' stopped.\n", zSvcName);
          690  +    }
          691  +    if( !DeleteService(hSvc) ){
          692  +      if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
          693  +        fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
          694  +      }else{
          695  +        fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          696  +      }
          697  +    }else{
          698  +      fossil_print("Service '%s' successfully deleted.\n", zSvcName);
          699  +    }
          700  +    CloseServiceHandle(hSvc);
          701  +    CloseServiceHandle(hScm);
          702  +  }else
          703  +  if( strncmp(zMethod, "show", n)==0 ){
          704  +    SC_HANDLE hScm;
          705  +    SC_HANDLE hSvc;
          706  +    SERVICE_STATUS sstat;
          707  +    LPQUERY_SERVICE_CONFIGW pSvcConfig;
          708  +    LPSERVICE_DESCRIPTIONW pSvcDescr;
          709  +    BOOL bStatus;
          710  +    DWORD nRequired;
          711  +    const char *zErrFmt = "unable to show service '%s': %s";
          712  +    static const char *const zSvcTypes[] = {
          713  +      "Driver service",
          714  +      "File system driver service",
          715  +      "Service runs in its own process",
          716  +      "Service shares a process with other services",
          717  +      "Service can interact with the desktop"
          718  +    };
          719  +    const char *zSvcType = "";
          720  +    static const char *const zSvcStartTypes[] = {
          721  +      "Started by the system loader",
          722  +      "Started by the IoInitSystem function",
          723  +      "Started automatically by the service control manager",
          724  +      "Started manually",
          725  +      "Service cannot be started"
          726  +    };
          727  +    const char *zSvcStartType = "";
          728  +    static const char *const zSvcStates[] = {
          729  +      "Stopped", "Starting", "Stopping", "Running",
          730  +      "Continue pending", "Pause pending", "Paused"
          731  +    };
          732  +    const char *zSvcState = "";
          733  +
          734  +    verify_all_options();
          735  +    if( g.argc==4 ){
          736  +      zSvcName = g.argv[3];
          737  +    }else if( g.argc>4 ){
          738  +      fossil_fatal("to much arguments for show method.");
          739  +    }
          740  +    hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ);
          741  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          742  +    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ);
          743  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          744  +    /* Get the service configuration */
          745  +    bStatus = QueryServiceConfigW(hSvc, NULL, 0, &nRequired);
          746  +    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
          747  +      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          748  +    }
          749  +    pSvcConfig = fossil_malloc(nRequired);
          750  +    bStatus = QueryServiceConfigW(hSvc, pSvcConfig, nRequired, &nRequired);
          751  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          752  +    /* Translate the service type */
          753  +    switch( pSvcConfig->dwServiceType ){
          754  +      case SERVICE_KERNEL_DRIVER:       zSvcType = zSvcTypes[0]; break;
          755  +      case SERVICE_FILE_SYSTEM_DRIVER:  zSvcType = zSvcTypes[1]; break;
          756  +      case SERVICE_WIN32_OWN_PROCESS:   zSvcType = zSvcTypes[2]; break;
          757  +      case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break;
          758  +      case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break;
          759  +    }
          760  +    /* Translate the service start type */
          761  +    switch( pSvcConfig->dwStartType ){
          762  +      case SERVICE_BOOT_START:    zSvcStartType = zSvcStartTypes[0]; break;
          763  +      case SERVICE_SYSTEM_START:  zSvcStartType = zSvcStartTypes[1]; break;
          764  +      case SERVICE_AUTO_START:    zSvcStartType = zSvcStartTypes[2]; break;
          765  +      case SERVICE_DEMAND_START:  zSvcStartType = zSvcStartTypes[3]; break;
          766  +      case SERVICE_DISABLED:      zSvcStartType = zSvcStartTypes[4]; break;
          767  +    }
          768  +    /* Get the service description. */
          769  +    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
          770  +                                  NULL, 0, &nRequired);
          771  +    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
          772  +      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          773  +    }
          774  +    pSvcDescr = fossil_malloc(nRequired);
          775  +    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
          776  +                                  (LPBYTE)pSvcDescr, nRequired, &nRequired);
          777  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          778  +    /* Retrieves the current status of the specified service. */
          779  +    bStatus = QueryServiceStatus(hSvc, &sstat);
          780  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          781  +    /* Translate the current state. */
          782  +    switch( sstat.dwCurrentState ){
          783  +      case SERVICE_STOPPED:          zSvcState = zSvcStates[0]; break;
          784  +      case SERVICE_START_PENDING:    zSvcState = zSvcStates[1]; break;
          785  +      case SERVICE_STOP_PENDING:     zSvcState = zSvcStates[2]; break;
          786  +      case SERVICE_RUNNING:          zSvcState = zSvcStates[3]; break;
          787  +      case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break;
          788  +      case SERVICE_PAUSE_PENDING:    zSvcState = zSvcStates[5]; break;
          789  +      case SERVICE_PAUSED:           zSvcState = zSvcStates[6]; break;
          790  +    }
          791  +    /* Print service information to terminal */
          792  +    fossil_print("Service name .......: %s\n", zSvcName);
          793  +    fossil_print("Display name .......: %s\n",
          794  +                 fossil_unicode_to_utf8(pSvcConfig->lpDisplayName));
          795  +    fossil_print("Service description : %s\n",
          796  +                 fossil_unicode_to_utf8(pSvcDescr->lpDescription));
          797  +    fossil_print("Service type .......: %s.\n", zSvcType);
          798  +    fossil_print("Service start type .: %s.\n", zSvcStartType);
          799  +    fossil_print("Binary path name ...: %s\n",
          800  +                 fossil_unicode_to_utf8(pSvcConfig->lpBinaryPathName));
          801  +    fossil_print("Service username ...: %s\n",
          802  +                 fossil_unicode_to_utf8(pSvcConfig->lpServiceStartName));
          803  +    fossil_print("Current state ......: %s.\n", zSvcState);
          804  +    /* Cleanup */
          805  +    fossil_free(pSvcConfig);
          806  +    fossil_free(pSvcDescr);
          807  +    CloseServiceHandle(hSvc);
          808  +    CloseServiceHandle(hScm);
          809  +  }else
          810  +  if( strncmp(zMethod, "start", n)==0 ){
          811  +    SC_HANDLE hScm;
          812  +    SC_HANDLE hSvc;
          813  +    SERVICE_STATUS sstat;
          814  +    char *zErrFmt = "unable to start service '%s': %s";
          815  +
          816  +    verify_all_options();
          817  +    if( g.argc==4 ){
          818  +      zSvcName = g.argv[3];
          819  +    }else if( g.argc>4 ){
          820  +      fossil_fatal("to much arguments for start method.");
          821  +    }
          822  +    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          823  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          824  +    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
          825  +                        SERVICE_ALL_ACCESS);
          826  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          827  +    QueryServiceStatus(hSvc, &sstat);
          828  +    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
          829  +      fossil_print("Starting service '%s'", zSvcName);
          830  +      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
          831  +        if( !StartServiceW(hSvc, 0, NULL) ){
          832  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          833  +        }
          834  +      }
          835  +      while( sstat.dwCurrentState!=SERVICE_RUNNING ){
          836  +        Sleep(100);
          837  +        fossil_print(".");
          838  +        QueryServiceStatus(hSvc, &sstat);
          839  +      }
          840  +      fossil_print("\nService '%s' started.\n", zSvcName);
          841  +    }else{
          842  +      fossil_print("Service '%s' is already started.\n", zSvcName);
          843  +    }
          844  +    CloseServiceHandle(hSvc);
          845  +    CloseServiceHandle(hScm);
          846  +  }else
          847  +  if( strncmp(zMethod, "stop", n)==0 ){
          848  +    SC_HANDLE hScm;
          849  +    SC_HANDLE hSvc;
          850  +    SERVICE_STATUS sstat;
          851  +    char *zErrFmt = "unable to stop service '%s': %s";
          852  +
          853  +    verify_all_options();
          854  +    if( g.argc==4 ){
          855  +      zSvcName = g.argv[3];
          856  +    }else if( g.argc>4 ){
          857  +      fossil_fatal("to much arguments for stop method.");
          858  +    }
          859  +    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          860  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          861  +    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
          862  +                        SERVICE_ALL_ACCESS);
          863  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          864  +    QueryServiceStatus(hSvc, &sstat);
          865  +    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
          866  +      fossil_print("Stopping service '%s'", zSvcName);
          867  +      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
          868  +        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          869  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          870  +        }
          871  +      }
          872  +      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
          873  +        Sleep(100);
          874  +        fossil_print(".");
          875  +        QueryServiceStatus(hSvc, &sstat);
          876  +      }
          877  +      fossil_print("\nService '%s' stopped.\n", zSvcName);
          878  +    }else{
          879  +      fossil_print("Service '%s' is already stopped.\n", zSvcName);
          880  +    }
          881  +    CloseServiceHandle(hSvc);
          882  +    CloseServiceHandle(hScm);
          883  +  }else
          884  +  {
          885  +    fossil_fatal("METHOD should be one of:"
          886  +                 " create delete show start stop");
          887  +  }
          888  +  return;
          889  +}
          890  +#endif /* _WIN32  -- This code is for win32 only */

Added src/wysiwyg.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +**
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code that generates WYSIWYG text editors on
           19  +** web pages.
           20  +*/
           21  +#include <assert.h>
           22  +#include <ctype.h>
           23  +#include "config.h"
           24  +#include "wysiwyg.h"
           25  +
           26  +
           27  +/*
           28  +** Output code for a WYSIWYG editor.  The caller must have already generated
           29  +** the <form> that will contain the editor, and the call must generate the
           30  +** corresponding </form> after this routine returns.  The caller must include
           31  +** an onsubmit= attribute on the <form> element that invokes the
           32  +** wysiwygSubmit() function.
           33  +**
           34  +** There can only be a single WYSIWYG editor per frame.
           35  +*/
           36  +void wysiwygEditor(
           37  +  const char *zId,        /* ID for this editor */
           38  +  const char *zContent,   /* Initial content (HTML) */
           39  +  int w, int h            /* Initial width and height */
           40  +){
           41  +
           42  +  @ <style type="text/css">
           43  +  @ .intLink { cursor: pointer; }
           44  +  @ img.intLink { border: 0; }
           45  +  @ #wysiwygBox {
           46  +  @   border: 1px #000000 solid;
           47  +  @   padding: 12px;
           48  +  @ }
           49  +  @ #editMode label { cursor: pointer; }
           50  +  @ </style>
           51  +
           52  +  @ <input id="wysiwygValue" type="hidden" name="%s(zId)">
           53  +  @ <div id="editModeDiv">Edit mode:
           54  +  @   <select id="editMode" size=1 onchange="setDocMode(this.selectedIndex)">
           55  +  @ <option value="0">WYSIWYG</option>
           56  +  @ <option value="1">Raw HTML</option>
           57  +  @ </select></div>
           58  +  @ <div id="toolBar1">
           59  +  @ <select onchange="formatDoc('formatblock',this[this.selectedIndex].value);
           60  +  @                   this.selectedIndex=0;">
           61  +  @ <option selected>- formatting -</option>
           62  +  @ <option value="h1">Title 1 &lt;h1&gt;</option>
           63  +  @ <option value="h2">Title 2 &lt;h2&gt;</option>
           64  +  @ <option value="h3">Title 3 &lt;h3&gt;</option>
           65  +  @ <option value="h4">Title 4 &lt;h4&gt;</option>
           66  +  @ <option value="h5">Title 5 &lt;h5&gt;</option>
           67  +  @ <option value="h6">Subtitle &lt;h6&gt;</option>
           68  +  @ <option value="p">Paragraph &lt;p&gt;</option>
           69  +  @ <option value="pre">Preformatted &lt;pre&gt;</option>
           70  +  @ </select>
           71  +  @ <select onchange="formatDoc('fontname',this[this.selectedIndex].value);
           72  +  @                   this.selectedIndex=0;">
           73  +  @ <option class="heading" selected>- font -</option>
           74  +  @ <option>Arial</option>
           75  +  @ <option>Arial Black</option>
           76  +  @ <option>Courier New</option>
           77  +  @ <option>Times New Roman</option>
           78  +  @ </select>
           79  +  @ <select onchange="formatDoc('fontsize',this[this.selectedIndex].value);
           80  +  @                   this.selectedIndex=0;">
           81  +  @ <option class="heading" selected>- size -</option>
           82  +  @ <option value="1">Very small</option>
           83  +  @ <option value="2">A bit small</option>
           84  +  @ <option value="3">Normal</option>
           85  +  @ <option value="4">Medium-large</option>
           86  +  @ <option value="5">Big</option>
           87  +  @ <option value="6">Very big</option>
           88  +  @ <option value="7">Maximum</option>
           89  +  @ </select>
           90  +  @ <select onchange="formatDoc('forecolor',this[this.selectedIndex].value);
           91  +  @                   this.selectedIndex=0;">
           92  +  @ <option class="heading" selected>- color -</option>
           93  +  @ <option value="red">Red</option>
           94  +  @ <option value="blue">Blue</option>
           95  +  @ <option value="green">Green</option>
           96  +  @ <option value="black">Black</option>
           97  +  @ </select>
           98  +  @ </div>
           99  +  @ <div id="toolBar2">
          100  +  @ <img class="intLink" title="Undo" onclick="formatDoc('undo');"
          101  +  @ src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7
          102  +  @ /I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680
          103  +  @ 7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE
          104  +  @ whdRIH4fh/DZMICe3/C4nBQBADs=">
          105  +
          106  +  @ <img class="intLink" title="Redo" onclick="formatDoc('redo');"
          107  +  @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/
          108  +  @ ///yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na
          109  +  @ EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw==">
          110  +
          111  +  @ <img class="intLink" title="Remove formatting"
          112  +  @ onclick="formatDoc('removeFormat')"
          113  +  @ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA
          114  +  @ AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA
          115  +  @ AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5
          116  +  @ 01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG
          117  +  @ Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz
          118  +  @ deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95
          119  +  @ DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2C
          120  +  @ lrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA
          121  +  @ wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n
          122  +  @ 7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1
          123  +  @ lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj
          124  +  @ 5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf
          125  +  @ fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj
          126  +  @ b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCc
          127  +  @ XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66
          128  +  @ 6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH
          129  +  @ 6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg==">
          130  +
          131  +  @ <img class="intLink" title="Bold" onclick="formatDoc('bold');"
          132  +  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
          133  +  @ YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" />
          134  +
          135  +  @ <img class="intLink" title="Italic" onclick="formatDoc('italic');"
          136  +  @ src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALA
          137  +  @ AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" />
          138  +
          139  +  @ <img class="intLink" title="Underline" onclick="formatDoc('underline');"
          140  +  @ src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALA
          141  +  @ AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA
          142  +  @ 7" />
          143  +
          144  +  @ <img class="intLink" title="Left align"
          145  +  @ onclick="formatDoc('justifyleft');"
          146  +  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
          147  +  @ YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" />
          148  +
          149  +  @ <img class="intLink" title="Center align"
          150  +  @ onclick="formatDoc('justifycenter');"
          151  +  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
          152  +  @ YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" />
          153  +
          154  +  @ <img class="intLink" title="Right align"
          155  +  @ onclick="formatDoc('justifyright');"
          156  +  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
          157  +  @ YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" />
          158  +  @ <img class="intLink" title="Numbered list"
          159  +  @ onclick="formatDoc('insertorderedlist');"
          160  +  @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P////
          161  +  @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO
          162  +  @ zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" />
          163  +
          164  +  @ <img class="intLink" title="Dotted list"
          165  +  @ onclick="formatDoc('insertunorderedlist');"
          166  +  @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv////
          167  +  @ ///yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6
          168  +  @ 4UwiuKnigGQliQuWOyKQykgAAOw==" />
          169  +
          170  +  @ <img class="intLink" title="Quote"
          171  +  @ onclick="formatDoc('formatblock','blockquote');"
          172  +  @ src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2
          173  +  @ R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P/////////
          174  +  @ //////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk
          175  +  @ ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT
          176  +  @ q/b7/i8fp8PAQA7" />
          177  +
          178  +  @ <img class="intLink" title="Delete indentation"
          179  +  @ onclick="formatDoc('outdent');"
          180  +  @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P
          181  +  @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz
          182  +  @ sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" />
          183  +
          184  +  @ <img class="intLink" title="Add indentation"
          185  +  @ onclick="formatDoc('indent');"
          186  +  @ src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N
          187  +  @ Ha7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650
          188  +  @ B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==">
          189  +
          190  +  @ <img class="intLink" title="Hyperlink"
          191  +  @ onclick="var sLnk=prompt('Target URL:','');
          192  +  @          if(sLnk&&sLnk!=''){formatDoc('createlink',sLnk)}"
          193  +  @ src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb
          194  +  @ /I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682
          195  +  @ 7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG
          196  +  @ i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" />
          197  +
          198  +#if 0  /* Cut/Copy/Paste requires special browser permissions for security
          199  +       ** reasons.  So omit these buttons */
          200  +  @ <img class="intLink" title="Cut"
          201  +  @ onclick="formatDoc('cut');"
          202  +  @ src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1
          203  +  @ dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P/////////////////////////////
          204  +  @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE
          205  +  @ bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9
          206  +  @ ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" />
          207  +
          208  +  @ <img class="intLink" title="Copy"
          209  +  @ onclick="formatDoc('copy');"
          210  +  @ src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31
          211  +  @ iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP
          212  +  @ s++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/
          213  +  @ kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ
          214  +  @ ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA
          215  +  @ Grpy+wsbKzIiEAOw==" />
          216  +
          217  +  @ <img class="intLink" title="Paste"
          218  +  @ onclick="formatDoc('paste');"
          219  +  @ src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp
          220  +  @ qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f/////////////////////
          221  +  @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB
          222  +  @ SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ
          223  +  @ o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA
          224  +  @ MOaK+bLAOrtLUyt7i5uiUhADs=" />
          225  +#endif
          226  +
          227  +  @ </div>
          228  +  @ <div id="wysiwygBox"
          229  +  @  style="resize:both; overflow:auto; width: %d(w)em; height: %d(h)em;"
          230  +  @  contenteditable="true">%s(zContent)</div>
          231  +  @ <script>
          232  +  @ var oDoc;
          233  +  @ 
          234  +  @ /* Initialize the document editor */
          235  +  @ function initDoc() {
          236  +  @   oDoc = document.getElementById("wysiwygBox");
          237  +  @   if (!isWysiwyg()) { setDocMode(true); }
          238  +  @ }
          239  +  @ 
          240  +  @ /* Return true if the document editor is in WYSIWYG mode.  Return 
          241  +  @ ** false if it is in Markup mode */
          242  +  @ function isWysiwyg() {
          243  +  @   return document.getElementById("editMode").selectedIndex==0;
          244  +  @ }
          245  +  @
          246  +  @ /* Invoke this routine prior to submitting the HTML content back
          247  +  @ ** to the server */
          248  +  @ function wysiwygSubmit() {
          249  +  @   if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);}
          250  +  @   document.getElementById("wysiwygValue").value=oDoc.innerHTML;
          251  +  @ }
          252  +  @ 
          253  +  @ /* Run the editing command if in WYSIWYG mode */ 
          254  +  @ function formatDoc(sCmd, sValue) {
          255  +  @   if (isWysiwyg()){
          256  +  @     document.execCommand("styleWithCSS", false, false);
          257  +  @     document.execCommand(sCmd, false, sValue);
          258  +  @     oDoc.focus();
          259  +  @   }
          260  +  @ }
          261  +  @ 
          262  +  @ /* Change the editing mode.  Convert to markup if the argument
          263  +  @ ** is true and wysiwyg if the argument is false. */ 
          264  +  @ function setDocMode(bToMarkup) {
          265  +  @   var oContent;
          266  +  @   if (bToMarkup) {
          267  +  @     /* WYSIWYG -> Markup */
          268  +  @     var linebreak = new RegExp("</p><p>","ig");
          269  +  @     oContent = document.createTextNode(
          270  +  @                  oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>"));
          271  +  @     oDoc.innerHTML = "";
          272  +  @     oDoc.style.whiteSpace = "pre-wrap";
          273  +  @     oDoc.appendChild(oContent);
          274  +  @     document.getElementById("toolBar1").style.visibility="hidden";
          275  +  @     document.getElementById("toolBar2").style.visibility="hidden";
          276  +  @   } else {
          277  +  @     /* Markup -> WYSIWYG */
          278  +  @     if (document.all) {
          279  +  @       oDoc.innerHTML = oDoc.innerText;
          280  +  @     } else {
          281  +  @       oContent = document.createRange();
          282  +  @       oContent.selectNodeContents(oDoc.firstChild);
          283  +  @       oDoc.innerHTML = oContent.toString();
          284  +  @     }
          285  +  @     oDoc.style.whiteSpace = "normal";
          286  +  @     document.getElementById("toolBar1").style.visibility="visible";
          287  +  @     document.getElementById("toolBar2").style.visibility="visible";
          288  +  @   }
          289  +  @   oDoc.focus();
          290  +  @ }
          291  +  @ initDoc();
          292  +  @ </script>
          293  +
          294  +}

Changes to src/xfer.c.

    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file contains code to implement the file transfer protocol.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "xfer.h"
    22     22   
           23  +#include <time.h>
           24  +
           25  +/*
           26  +** Maximum number of HTTP redirects that any http_exchange() call will
           27  +** follow before throwing a fatal error. Most browsers use a limit of 20.
           28  +*/
           29  +#define MAX_REDIRECTS 20
           30  +
    23     31   /*
    24     32   ** This structure holds information about the current state of either
    25     33   ** a client or a server that is participating in xfer.
    26     34   */
    27     35   typedef struct Xfer Xfer;
    28     36   struct Xfer {
    29     37     Blob *pIn;          /* Input text from the other side */
    30     38     Blob *pOut;         /* Compose our reply here */
    31     39     Blob line;          /* The current line of input */
    32         -  Blob aToken[5];     /* Tokenized version of line */
           40  +  Blob aToken[6];     /* Tokenized version of line */
    33     41     Blob err;           /* Error message text */
    34     42     int nToken;         /* Number of tokens in line */
    35     43     int nIGotSent;      /* Number of "igot" cards sent */
    36     44     int nGimmeSent;     /* Number of gimme cards sent */
    37     45     int nFileSent;      /* Number of files sent */
    38     46     int nDeltaSent;     /* Number of deltas sent */
    39     47     int nFileRcvd;      /* Number of files received */
    40     48     int nDeltaRcvd;     /* Number of deltas received */
    41     49     int nDanglingFile;  /* Number of dangling deltas received */
    42     50     int mxSend;         /* Stop sending "file" with pOut reaches this size */
           51  +  u8 syncPrivate;     /* True to enable syncing private content */
           52  +  u8 nextIsPrivate;   /* If true, next "file" received is a private */
           53  +  time_t maxTime;     /* Time when this transfer should be finished */
    43     54   };
    44     55   
    45     56   
    46     57   /*
    47     58   ** The input blob contains a UUID.  Convert it into a record ID.
    48     59   ** Create a phantom record if no prior record exists and
    49     60   ** phantomize is true.
    50     61   **
    51     62   ** Compare to uuid_to_rid().  This routine takes a blob argument
    52     63   ** and does less error checking.
    53     64   */
    54         -static int rid_from_uuid(Blob *pUuid, int phantomize){
           65  +static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){
    55     66     static Stmt q;
    56     67     int rid;
    57     68   
    58     69     db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
    59     70     db_bind_str(&q, ":uuid", pUuid);
    60     71     if( db_step(&q)==SQLITE_ROW ){
    61     72       rid = db_column_int(&q, 0);
    62     73     }else{
    63     74       rid = 0;
    64     75     }
    65     76     db_reset(&q);
    66     77     if( rid==0 && phantomize ){
    67         -    rid = content_new(blob_str(pUuid));
           78  +    rid = content_new(blob_str(pUuid), isPrivate);
    68     79     }
    69     80     return rid;
    70     81   }
    71     82   
    72     83   /*
    73     84   ** Remember that the other side of the connection already has a copy
    74     85   ** of the file rid.
    75     86   */
    76     87   static void remote_has(int rid){
    77         -  if( rid ) db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid);
           88  +  if( rid ){
           89  +    static Stmt q;
           90  +    db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)");
           91  +    db_bind_int(&q, ":r", rid);
           92  +    db_step(&q);
           93  +    db_reset(&q);
           94  +  }
    78     95   }
    79     96   
    80     97   /*
    81     98   ** The aToken[0..nToken-1] blob array is a parse of a "file" line 
    82     99   ** message.  This routine finishes parsing that message and does
    83    100   ** a record insert of the file.
    84    101   **
................................................................................
    93    110   **
    94    111   ** If any error occurs, write a message into pErr which has already
    95    112   ** be initialized to an empty string.
    96    113   **
    97    114   ** Any artifact successfully received by this routine is considered to
    98    115   ** be public and is therefore removed from the "private" table.
    99    116   */
   100         -static void xfer_accept_file(Xfer *pXfer){
          117  +static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
   101    118     int n;
   102    119     int rid;
   103    120     int srcid = 0;
   104    121     Blob content, hash;
          122  +  int isPriv;
   105    123     
          124  +  isPriv = pXfer->nextIsPrivate;
          125  +  pXfer->nextIsPrivate = 0;
   106    126     if( pXfer->nToken<3 
   107    127      || pXfer->nToken>4
   108    128      || !blob_is_uuid(&pXfer->aToken[1])
   109    129      || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
   110    130      || n<0
   111    131      || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2]))
   112    132     ){
   113    133       blob_appendf(&pXfer->err, "malformed file line");
   114    134       return;
   115    135     }
   116    136     blob_zero(&content);
   117    137     blob_zero(&hash);
   118    138     blob_extract(pXfer->pIn, n, &content);
   119         -  if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
          139  +  if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
   120    140       /* Ignore files that have been shunned */
          141  +    blob_reset(&content);
          142  +    return;
          143  +  }
          144  +  if( isPriv && !g.perm.Private ){
          145  +    /* Do not accept private files if not authorized */
          146  +    blob_reset(&content);
          147  +    return;
          148  +  }
          149  +  if( cloneFlag ){
          150  +    if( pXfer->nToken==4 ){
          151  +      srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
          152  +      pXfer->nDeltaRcvd++;
          153  +    }else{
          154  +      srcid = 0;
          155  +      pXfer->nFileRcvd++;
          156  +    }
          157  +    rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
          158  +                         0, isPriv);
          159  +    remote_has(rid);
          160  +    blob_reset(&content);
   121    161       return;
   122    162     }
   123    163     if( pXfer->nToken==4 ){
   124         -    Blob src;
   125         -    srcid = rid_from_uuid(&pXfer->aToken[2], 1);
          164  +    Blob src, next;
          165  +    srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
   126    166       if( content_get(srcid, &src)==0 ){
   127         -      rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
          167  +      rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
          168  +                           0, isPriv);
   128    169         pXfer->nDanglingFile++;
   129    170         db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
   130         -      content_make_public(rid);
          171  +      if( !isPriv ) content_make_public(rid);
          172  +      blob_reset(&src);
          173  +      blob_reset(&content);
   131    174         return;
   132    175       }
   133    176       pXfer->nDeltaRcvd++;
   134         -    blob_delta_apply(&src, &content, &content);
          177  +    blob_delta_apply(&src, &content, &next);
   135    178       blob_reset(&src);
          179  +    blob_reset(&content);
          180  +    content = next;
   136    181     }else{
   137    182       pXfer->nFileRcvd++;
   138    183     }
   139    184     sha1sum_blob(&content, &hash);
   140    185     if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
   141    186       blob_appendf(&pXfer->err, "content does not match sha1 hash");
   142    187     }
   143         -  rid = content_put(&content, blob_str(&hash), 0);
          188  +  rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv);
   144    189     blob_reset(&hash);
   145    190     if( rid==0 ){
   146    191       blob_appendf(&pXfer->err, "%s", g.zErrMsg);
          192  +    blob_reset(&content);
   147    193     }else{
   148         -    content_make_public(rid);
          194  +    if( !isPriv ) content_make_public(rid);
   149    195       manifest_crosslink(rid, &content);
   150    196     }
          197  +  assert( blob_is_reset(&content) );
          198  +  remote_has(rid);
          199  +}
          200  +
          201  +/*
          202  +** The aToken[0..nToken-1] blob array is a parse of a "cfile" line 
          203  +** message.  This routine finishes parsing that message and does
          204  +** a record insert of the file.  The difference between "file" and
          205  +** "cfile" is that with "cfile" the content is already compressed.
          206  +**
          207  +** The file line is in one of the following two forms:
          208  +**
          209  +**      cfile UUID USIZE CSIZE \n CONTENT
          210  +**      cfile UUID DELTASRC USIZE CSIZE \n CONTENT
          211  +**
          212  +** The content is CSIZE bytes immediately following the newline.
          213  +** If DELTASRC exists, then the CONTENT is a delta against the
          214  +** content of DELTASRC.
          215  +**
          216  +** The original size of the UUID artifact is USIZE.
          217  +**
          218  +** If any error occurs, write a message into pErr which has already
          219  +** be initialized to an empty string.
          220  +**
          221  +** Any artifact successfully received by this routine is considered to
          222  +** be public and is therefore removed from the "private" table.
          223  +*/
          224  +static void xfer_accept_compressed_file(Xfer *pXfer){
          225  +  int szC;   /* CSIZE */
          226  +  int szU;   /* USIZE */
          227  +  int rid;
          228  +  int srcid = 0;
          229  +  Blob content;
          230  +  int isPriv;
          231  +  
          232  +  isPriv = pXfer->nextIsPrivate;
          233  +  pXfer->nextIsPrivate = 0;
          234  +  if( pXfer->nToken<4 
          235  +   || pXfer->nToken>5
          236  +   || !blob_is_uuid(&pXfer->aToken[1])
          237  +   || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU)
          238  +   || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC)
          239  +   || szC<0 || szU<0
          240  +   || (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2]))
          241  +  ){
          242  +    blob_appendf(&pXfer->err, "malformed cfile line");
          243  +    return;
          244  +  }
          245  +  if( isPriv && !g.perm.Private ){
          246  +    /* Do not accept private files if not authorized */
          247  +    return;
          248  +  }
          249  +  blob_zero(&content);
          250  +  blob_extract(pXfer->pIn, szC, &content);
          251  +  if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
          252  +    /* Ignore files that have been shunned */
          253  +    blob_reset(&content);
          254  +    return;
          255  +  }
          256  +  if( pXfer->nToken==5 ){
          257  +    srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
          258  +    pXfer->nDeltaRcvd++;
          259  +  }else{
          260  +    srcid = 0;
          261  +    pXfer->nFileRcvd++;
          262  +  }
          263  +  rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
          264  +                       szC, isPriv);
   151    265     remote_has(rid);
          266  +  blob_reset(&content);
   152    267   }
   153    268   
   154    269   /*
   155    270   ** Try to send a file as a delta against its parent.
   156    271   ** If successful, return the number of bytes in the delta.
   157    272   ** If we cannot generate an appropriate delta, then send
   158    273   ** nothing and return zero.
   159    274   **
   160    275   ** Never send a delta against a private artifact.
   161    276   */
   162    277   static int send_delta_parent(
   163    278     Xfer *pXfer,            /* The transfer context */
   164    279     int rid,                /* record id of the file to send */
          280  +  int isPrivate,          /* True if rid is a private artifact */
   165    281     Blob *pContent,         /* The content of the file to send */
   166    282     Blob *pUuid             /* The UUID of the file to send */
   167    283   ){
   168         -  static const char *azQuery[] = {
          284  +  static const char *const azQuery[] = {
   169    285       "SELECT pid FROM plink x"
   170    286       " WHERE cid=%d"
   171         -    "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
   172         -    "   AND NOT EXISTS(SELECT 1 FROM plink y"
   173         -                      " WHERE y.pid=x.cid AND y.cid=x.pid)",
   174         -
   175         -    "SELECT pid FROM mlink x"
          287  +    "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
          288  +    
          289  +    "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid"
   176    290       " WHERE fid=%d"
   177    291       "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
   178         -    "   AND NOT EXISTS(SELECT 1 FROM mlink y"
   179         -                     "  WHERE y.pid=x.fid AND y.fid=x.pid)"
   180    292     };
   181    293     int i;
   182    294     Blob src, delta;
   183    295     int size = 0;
   184    296     int srcId = 0;
   185    297   
   186    298     for(i=0; srcId==0 && i<count(azQuery); i++){
   187    299       srcId = db_int(0, azQuery[i], rid);
   188    300     }
   189         -  if( srcId>0 && !content_is_private(srcId) && content_get(srcId, &src) ){
          301  +  if( srcId>0
          302  +   && (pXfer->syncPrivate || !content_is_private(srcId))
          303  +   && content_get(srcId, &src)
          304  +  ){
   190    305       char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
   191    306       blob_delta_create(&src, pContent, &delta);
   192    307       size = blob_size(&delta);
   193    308       if( size>=blob_size(pContent)-50 ){
   194    309         size = 0;
   195    310       }else if( uuid_is_shunned(zUuid) ){
   196    311         size = 0;
   197    312       }else{
          313  +       if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
   198    314         blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
   199    315         blob_append(pXfer->pOut, blob_buffer(&delta), size);
   200         -      /* blob_appendf(pXfer->pOut, "\n", 1); */
   201    316       }
   202    317       blob_reset(&delta);
   203    318       free(zUuid);
   204    319       blob_reset(&src);
   205    320     }
   206    321     return size;
   207    322   }
................................................................................
   213    328   ** nothing and return zero.
   214    329   **
   215    330   ** Never send a delta against a private artifact.
   216    331   */
   217    332   static int send_delta_native(
   218    333     Xfer *pXfer,            /* The transfer context */
   219    334     int rid,                /* record id of the file to send */
          335  +  int isPrivate,          /* True if rid is a private artifact */
   220    336     Blob *pUuid             /* The UUID of the file to send */
   221    337   ){
   222    338     Blob src, delta;
   223    339     int size = 0;
   224    340     int srcId;
   225    341   
   226    342     srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
   227         -  if( srcId>0 && !content_is_private(srcId) ){
          343  +  if( srcId>0
          344  +   && (pXfer->syncPrivate || !content_is_private(srcId))
          345  +  ){
   228    346       blob_zero(&src);
   229    347       db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId);
   230    348       if( uuid_is_shunned(blob_str(&src)) ){
   231    349         blob_reset(&src);
   232    350         return 0;
   233    351       }
   234    352       blob_zero(&delta);
   235    353       db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid);
   236    354       blob_uncompress(&delta, &delta);
          355  +    if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
   237    356       blob_appendf(pXfer->pOut, "file %b %b %d\n",
   238    357                   pUuid, &src, blob_size(&delta));
   239    358       blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta));
   240    359       size = blob_size(&delta);
   241    360       blob_reset(&delta);
   242    361       blob_reset(&src);
   243    362     }else{
................................................................................
   258    377   ** It should never be the case that rid is a private artifact.  But
   259    378   ** as a precaution, this routine does check on rid and if it is private
   260    379   ** this routine becomes a no-op.
   261    380   */
   262    381   static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
   263    382     Blob content, uuid;
   264    383     int size = 0;
          384  +  int isPriv = content_is_private(rid);
   265    385   
   266         -  if( content_is_private(rid) ) return;
          386  +  if( pXfer->syncPrivate==0 && isPriv ) return;
   267    387     if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
   268    388        return;
   269    389     }
   270    390     blob_zero(&uuid);
   271    391     db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
   272    392     if( blob_size(&uuid)==0 ){
   273    393       return;
................................................................................
   280    400     }else{
   281    401       pUuid = &uuid;
   282    402     }
   283    403     if( uuid_is_shunned(blob_str(pUuid)) ){
   284    404       blob_reset(&uuid);
   285    405       return;
   286    406     }
   287         -  if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
   288         -    blob_appendf(pXfer->pOut, "igot %b\n", pUuid);
          407  +  if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || 
          408  +       pXfer->mxSend<=blob_size(pXfer->pOut) ){
          409  +    const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
          410  +    blob_appendf(pXfer->pOut, zFormat, pUuid);
   289    411       pXfer->nIGotSent++;
   290    412       blob_reset(&uuid);
   291    413       return;
   292    414     }
   293    415     if( nativeDelta ){
   294         -    size = send_delta_native(pXfer, rid, pUuid);
          416  +    size = send_delta_native(pXfer, rid, isPriv, pUuid);
   295    417       if( size ){
   296    418         pXfer->nDeltaSent++;
   297    419       }
   298    420     }
   299    421     if( size==0 ){
   300    422       content_get(rid, &content);
   301    423   
   302    424       if( !nativeDelta && blob_size(&content)>100 ){
   303         -      size = send_delta_parent(pXfer, rid, &content, pUuid);
          425  +      size = send_delta_parent(pXfer, rid, isPriv, &content, pUuid);
   304    426       }
   305    427       if( size==0 ){
   306    428         int size = blob_size(&content);
          429  +      if( isPriv ) blob_append(pXfer->pOut, "private\n", -1);
   307    430         blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
   308    431         blob_append(pXfer->pOut, blob_buffer(&content), size);
   309    432         pXfer->nFileSent++;
   310    433       }else{
   311    434         pXfer->nDeltaSent++;
   312    435       }
          436  +    blob_reset(&content);
   313    437     }
   314    438     remote_has(rid);
   315    439     blob_reset(&uuid);
          440  +#if 0
          441  +  if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
          442  +    blob_appendf(pXfer->pOut, "\n", 1);
          443  +  }
          444  +#endif
          445  +}
          446  +
          447  +/*
          448  +** Send the file identified by rid as a compressed artifact.  Basically,
          449  +** send the content exactly as it appears in the BLOB table using 
          450  +** a "cfile" card.
          451  +*/
          452  +static void send_compressed_file(Xfer *pXfer, int rid){
          453  +  const char *zContent;
          454  +  const char *zUuid;
          455  +  const char *zDelta;
          456  +  int szU;
          457  +  int szC;
          458  +  int rc;
          459  +  int isPrivate;
          460  +  int srcIsPrivate;
          461  +  static Stmt q1;
          462  +  Blob fullContent;
          463  +
          464  +  isPrivate = content_is_private(rid);
          465  +  if( isPrivate && pXfer->syncPrivate==0 ) return;
          466  +  db_static_prepare(&q1,
          467  +    "SELECT uuid, size, content, delta.srcid IN private,"
          468  +         "  (SELECT uuid FROM blob WHERE rid=delta.srcid)"
          469  +    " FROM blob LEFT JOIN delta ON (blob.rid=delta.rid)"
          470  +    " WHERE blob.rid=:rid"
          471  +    "   AND blob.size>=0"
          472  +    "   AND NOT EXISTS(SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)"
          473  +  );
          474  +  db_bind_int(&q1, ":rid", rid);
          475  +  rc = db_step(&q1);
          476  +  if( rc==SQLITE_ROW ){
          477  +    zUuid = db_column_text(&q1, 0);
          478  +    szU = db_column_int(&q1, 1);
          479  +    szC = db_column_bytes(&q1, 2);
          480  +    zContent = db_column_raw(&q1, 2);
          481  +    srcIsPrivate = db_column_int(&q1, 3);
          482  +    zDelta = db_column_text(&q1, 4);
          483  +    if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
          484  +    blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
          485  +    if( !isPrivate && srcIsPrivate ){
          486  +      content_get(rid, &fullContent);
          487  +      szU = blob_size(&fullContent);
          488  +      blob_compress(&fullContent, &fullContent);
          489  +      szC = blob_size(&fullContent);
          490  +      zContent = blob_buffer(&fullContent);
          491  +      zDelta = 0;
          492  +    }
          493  +    if( zDelta ){
          494  +      blob_appendf(pXfer->pOut, "%s ", zDelta);
          495  +      pXfer->nDeltaSent++;
          496  +    }else{
          497  +      pXfer->nFileSent++;
          498  +    }
          499  +    blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
          500  +    blob_append(pXfer->pOut, zContent, szC);
          501  +    if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
          502  +      blob_appendf(pXfer->pOut, "\n", 1);
          503  +    }
          504  +    if( !isPrivate && srcIsPrivate ){
          505  +      blob_reset(&fullContent);
          506  +    }
          507  +  }
          508  +  db_reset(&q1);
   316    509   }
   317    510   
   318    511   /*
   319    512   ** Send a gimme message for every phantom.
   320    513   **
   321         -** It should not be possible to have a private phantom.  But just to be
   322         -** sure, take care not to send any "gimme" messagse on private artifacts.
          514  +** Except: do not request shunned artifacts.  And do not request
          515  +** private artifacts if we are not doing a private transfer.
   323    516   */
   324    517   static void request_phantoms(Xfer *pXfer, int maxReq){
   325    518     Stmt q;
   326    519     db_prepare(&q, 
   327    520       "SELECT uuid FROM phantom JOIN blob USING(rid)"
   328         -    " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
   329         -    "   AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
          521  +    " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s",
          522  +    (pXfer->syncPrivate ? "" :
          523  +         "   AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)")
   330    524     );
   331    525     while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){
   332    526       const char *zUuid = db_column_text(&q, 0);
   333    527       blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
   334    528       pXfer->nGimmeSent++;
   335    529     }
   336    530     db_finalize(&q);
................................................................................
   358    552   **
   359    553   **        login LOGIN NONCE SIGNATURE
   360    554   **
   361    555   ** The NONCE is the SHA1 hash of the remainder of the input.  
   362    556   ** SIGNATURE is the SHA1 checksum of the NONCE concatenated 
   363    557   ** with the users password.
   364    558   **
   365         -** The parameters to this routine are ephermeral blobs holding the
          559  +** The parameters to this routine are ephemeral blobs holding the
   366    560   ** LOGIN, NONCE and SIGNATURE.
   367    561   **
   368    562   ** This routine attempts to locate the user and verify the signature.
   369    563   ** If everything checks out, the USER.CAP column for the USER table
   370    564   ** is consulted to set privileges in the global g variable.
   371    565   **
   372    566   ** If anything fails to check out, no changes are made to privileges.
................................................................................
   378    572   */
   379    573   int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
   380    574     Stmt q;
   381    575     int rc = -1;
   382    576     char *zLogin = blob_terminate(pLogin);
   383    577     defossilize(zLogin);
   384    578   
   385         -  if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){
          579  +  if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){
   386    580       return 0;   /* Anybody is allowed to sync as "nobody" or "anonymous" */
          581  +  }
          582  +  if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 ){
          583  +    return 0;   /* Accept Basic Authorization */
   387    584     }
   388    585     db_prepare(&q,
   389    586        "SELECT pw, cap, uid FROM user"
   390    587        " WHERE login=%Q"
   391    588        "   AND login NOT IN ('anonymous','nobody','developer','reader')"
   392    589        "   AND length(pw)>0",
   393    590        zLogin
................................................................................
   399    596       db_ephemeral_blob(&q, 0, &pw);
   400    597       szPw = blob_size(&pw);
   401    598       blob_zero(&combined);
   402    599       blob_copy(&combined, pNonce);
   403    600       blob_append(&combined, blob_buffer(&pw), szPw);
   404    601       sha1sum_blob(&combined, &hash);
   405    602       assert( blob_size(&hash)==40 );
   406         -    rc = blob_compare(&hash, pSig);
          603  +    rc = blob_constant_time_cmp(&hash, pSig);
   407    604       blob_reset(&hash);
   408    605       blob_reset(&combined);
   409    606       if( rc!=0 && szPw!=40 ){
   410    607         /* If this server stores cleartext passwords and the password did not
   411    608         ** match, then perhaps the client is sending SHA1 passwords.  Try
   412    609         ** again with the SHA1 password.
   413    610         */
   414    611         const char *zPw = db_column_text(&q, 0);
   415         -      char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin));
          612  +      char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
   416    613         blob_zero(&combined);
   417    614         blob_copy(&combined, pNonce);
   418    615         blob_append(&combined, zSecret, -1);
   419    616         free(zSecret);
   420    617         sha1sum_blob(&combined, &hash);
   421         -      rc = blob_compare(&hash, pSig);
          618  +      rc = blob_constant_time_cmp(&hash, pSig);
   422    619         blob_reset(&hash);
   423    620         blob_reset(&combined);
   424    621       }
   425    622       if( rc==0 ){
   426    623         const char *zCap;
   427    624         zCap = db_column_text(&q, 1);
   428         -      login_set_capabilities(zCap);
          625  +      login_set_capabilities(zCap, 0);
   429    626         g.userUid = db_column_int(&q, 2);
   430    627         g.zLogin = mprintf("%b", pLogin);
   431    628         g.zNonce = mprintf("%b", pNonce);
   432         -      if( g.fHttpTrace ){
   433         -        fprintf(stderr, "# login [%s] with capabilities [%s]\n", g.zLogin,zCap);
   434         -      }
   435    629       }
   436    630     }
   437    631     db_finalize(&q);
   438         -
   439         -  if( rc==0 ){
   440         -    /* If the login was successful. */
   441         -    login_set_anon_nobody_capabilities();
   442         -  }
   443    632     return rc;
   444    633   }
   445    634   
   446    635   /*
   447    636   ** Send the content of all files in the unsent table.
   448    637   **
   449    638   ** This is really just an optimization.  If you clear the
................................................................................
   463    652   
   464    653   /*
   465    654   ** Check to see if the number of unclustered entries is greater than
   466    655   ** 100 and if it is, form a new cluster.  Unclustered phantoms do not
   467    656   ** count toward the 100 total.  And phantoms are never added to a new
   468    657   ** cluster.
   469    658   */
   470         -static void create_cluster(void){
          659  +void create_cluster(void){
   471    660     Blob cluster, cksum;
          661  +  Blob deleteWhere;
   472    662     Stmt q;
   473    663     int nUncl;
          664  +  int nRow = 0;
          665  +  int rid;
   474    666   
          667  +#if 0
   475    668     /* We should not ever get any private artifacts in the unclustered table.
   476    669     ** But if we do (because of a bug) now is a good time to delete them. */
   477    670     db_multi_exec(
   478    671       "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
   479    672     );
          673  +#endif
   480    674   
   481         -  nUncl = db_int(0, "SELECT count(*) FROM unclustered"
          675  +  nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
   482    676                       " WHERE NOT EXISTS(SELECT 1 FROM phantom"
   483    677                                         " WHERE rid=unclustered.rid)");
   484         -  if( nUncl<100 ){
   485         -    return;
   486         -  }
   487         -  blob_zero(&cluster);
   488         -  db_prepare(&q, "SELECT uuid FROM unclustered, blob"
   489         -                 " WHERE NOT EXISTS(SELECT 1 FROM phantom"
   490         -                 "                   WHERE rid=unclustered.rid)"
   491         -                 "   AND unclustered.rid=blob.rid"
   492         -                 "   AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
   493         -                 " ORDER BY 1");
   494         -  while( db_step(&q)==SQLITE_ROW ){
   495         -    blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0));
   496         -  }
   497         -  db_finalize(&q);
   498         -  md5sum_blob(&cluster, &cksum);
   499         -  blob_appendf(&cluster, "Z %b\n", &cksum);
   500         -  blob_reset(&cksum);
   501         -  db_multi_exec("DELETE FROM unclustered");
   502         -  content_put(&cluster, 0, 0);
   503         -  blob_reset(&cluster);
          678  +  if( nUncl>=100 ){
          679  +    blob_zero(&cluster);
          680  +    blob_zero(&deleteWhere);
          681  +    db_prepare(&q, "SELECT uuid FROM unclustered, blob"
          682  +                   " WHERE NOT EXISTS(SELECT 1 FROM phantom"
          683  +                   "                   WHERE rid=unclustered.rid)"
          684  +                   "   AND unclustered.rid=blob.rid"
          685  +                   "   AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
          686  +                   " ORDER BY 1");
          687  +    while( db_step(&q)==SQLITE_ROW ){
          688  +      blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0));
          689  +      nRow++;
          690  +      if( nRow>=800 && nUncl>nRow+100 ){
          691  +        md5sum_blob(&cluster, &cksum);
          692  +        blob_appendf(&cluster, "Z %b\n", &cksum);
          693  +        blob_reset(&cksum);
          694  +        rid = content_put(&cluster);
          695  +        blob_reset(&cluster);
          696  +        nUncl -= nRow;
          697  +        nRow = 0;
          698  +        blob_appendf(&deleteWhere, ",%d", rid);
          699  +      }
          700  +    }
          701  +    db_finalize(&q);
          702  +    db_multi_exec(
          703  +      "DELETE FROM unclustered WHERE rid NOT IN (0 %s)", 
          704  +      blob_str(&deleteWhere)
          705  +    );
          706  +    blob_reset(&deleteWhere);
          707  +    if( nRow>0 ){
          708  +      md5sum_blob(&cluster, &cksum);
          709  +      blob_appendf(&cluster, "Z %b\n", &cksum);
          710  +      blob_reset(&cksum);
          711  +      content_put(&cluster);
          712  +      blob_reset(&cluster);
          713  +    }
          714  +  }
          715  +}
          716  +
          717  +/*
          718  +** Send igot messages for every private artifact
          719  +*/
          720  +static int send_private(Xfer *pXfer){
          721  +  int cnt = 0;
          722  +  Stmt q;
          723  +  if( pXfer->syncPrivate ){
          724  +    db_prepare(&q, "SELECT uuid FROM private JOIN blob USING(rid)");
          725  +    while( db_step(&q)==SQLITE_ROW ){
          726  +      blob_appendf(pXfer->pOut, "igot %s 1\n", db_column_text(&q,0));
          727  +      cnt++;
          728  +    }
          729  +    db_finalize(&q);
          730  +  }
          731  +  return cnt;
   504    732   }
   505    733   
   506    734   /*
   507    735   ** Send an igot message for every entry in unclustered table.
   508    736   ** Return the number of cards sent.
   509    737   */
   510    738   static int send_unclustered(Xfer *pXfer){
   511    739     Stmt q;
   512    740     int cnt = 0;
   513    741     db_prepare(&q, 
   514    742       "SELECT uuid FROM unclustered JOIN blob USING(rid)"
   515    743       " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
   516         -    "   AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
   517    744       "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
          745  +    "   AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
   518    746     );
   519    747     while( db_step(&q)==SQLITE_ROW ){
   520    748       blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
   521    749       cnt++;
   522    750     }
   523    751     db_finalize(&q);
   524    752     return cnt;
................................................................................
   538    766     while( db_step(&q)==SQLITE_ROW ){
   539    767       blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
   540    768     }
   541    769     db_finalize(&q);
   542    770   }
   543    771   
   544    772   /*
   545         -** Send a single config card for configuration item zName
          773  +** Send a single old-style config card for configuration item zName.
          774  +**
          775  +** This routine and the functionality it implements is scheduled for
          776  +** removal on 2012-05-01.
   546    777   */
   547         -static void send_config_card(Xfer *pXfer, const char *zName){
          778  +static void send_legacy_config_card(Xfer *pXfer, const char *zName){
   548    779     if( zName[0]!='@' ){
   549    780       Blob val;
   550    781       blob_zero(&val);
   551    782       db_blob(&val, "SELECT value FROM config WHERE name=%Q", zName);
   552    783       if( blob_size(&val)>0 ){
   553    784         blob_appendf(pXfer->pOut, "config %s %d\n", zName, blob_size(&val));
   554    785         blob_append(pXfer->pOut, blob_buffer(&val), blob_size(&val));
................................................................................
   561    792       configure_render_special_name(zName, &content);
   562    793       blob_appendf(pXfer->pOut, "config %s %d\n%s\n", zName,
   563    794                    blob_size(&content), blob_str(&content));
   564    795       blob_reset(&content);
   565    796     }
   566    797   }
   567    798   
          799  +/*
          800  +** Called when there is an attempt to transfer private content to and
          801  +** from a server without authorization.
          802  +*/
          803  +static void server_private_xfer_not_authorized(void){
          804  +  @ error not\sauthorized\sto\ssync\sprivate\scontent
          805  +}
          806  +
          807  +/*
          808  +** Run the specified TH1 script, if any, and returns the return code or TH_OK
          809  +** when there is no script.
          810  +*/
          811  +static int run_script(const char *zScript){
          812  +  if( !zScript ){
          813  +    return TH_OK; /* No script, return success. */
          814  +  }
          815  +  Th_FossilInit(0, 0); /* Make sure TH1 is ready. */
          816  +  return Th_Eval(g.interp, 0, zScript, -1);
          817  +}
          818  +
          819  +/*
          820  +** Run the pre-transfer TH1 script, if any, and returns the return code.
          821  +*/
          822  +static int run_common_script(void){
          823  +  return run_script(db_get("xfer-common-script", 0));
          824  +}
          825  +
          826  +/*
          827  +** Run the post-push TH1 script, if any, and returns the return code.
          828  +*/
          829  +static int run_push_script(void){
          830  +  return run_script(db_get("xfer-push-script", 0));
          831  +}
   568    832   
   569    833   /*
   570    834   ** If this variable is set, disable login checks.  Used for debugging
   571    835   ** only.
   572    836   */
   573    837   static int disableLogin = 0;
   574    838   
   575    839   /*
   576    840   ** The CGI/HTTP preprocessor always redirects requests with a content-type
   577    841   ** of application/x-fossil or application/x-fossil-debug to this page,
   578    842   ** regardless of what path was specified in the HTTP header.  This allows
   579    843   ** clone clients to specify a URL that omits default pathnames, such
   580         -** as "http://fossil-scm.morg/" instead of "http://fossil-scm.org/index.cgi".
          844  +** as "http://fossil-scm.org/" instead of "http://fossil-scm.org/index.cgi".
   581    845   **
   582    846   ** WEBPAGE: xfer
   583    847   **
   584    848   ** This is the transfer handler on the server side.  The transfer
   585    849   ** message has been uncompressed and placed in the g.cgiIn blob.
   586    850   ** Process this message and form an appropriate reply.
   587    851   */
................................................................................
   591    855     int nErr = 0;
   592    856     Xfer xfer;
   593    857     int deltaFlag = 0;
   594    858     int isClone = 0;
   595    859     int nGimme = 0;
   596    860     int size;
   597    861     int recvConfig = 0;
          862  +  char *zNow;
   598    863   
   599         -  if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
          864  +  if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
   600    865        fossil_redirect_home();
   601    866     }
          867  +  g.zLogin = "anonymous";
          868  +  login_set_anon_nobody_capabilities();
          869  +  login_check_credentials();
   602    870     memset(&xfer, 0, sizeof(xfer));
   603    871     blobarray_zero(xfer.aToken, count(xfer.aToken));
   604    872     cgi_set_content_type(g.zContentType);
          873  +  cgi_reset_content();
          874  +  if( db_schema_is_outofdate() ){
          875  +    @ error database\sschema\sis\sout-of-date\son\sthe\sserver.
          876  +    return;
          877  +  }
   605    878     blob_zero(&xfer.err);
   606    879     xfer.pIn = &g.cgiIn;
   607    880     xfer.pOut = cgi_output_blob();
   608    881     xfer.mxSend = db_get_int("max-download", 5000000);
          882  +  xfer.maxTime = db_get_int("max-download-time", 30);
          883  +  if( xfer.maxTime<1 ) xfer.maxTime = 1;
          884  +  xfer.maxTime += time(NULL);
   609    885     g.xferPanic = 1;
   610    886   
   611    887     db_begin_transaction();
   612    888     db_multi_exec(
   613    889        "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
   614    890     );
   615    891     manifest_crosslink_begin();
          892  +  if( run_common_script()==TH_ERROR ){
          893  +    cgi_reset_content();
          894  +    @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
          895  +    nErr++;
          896  +  }
   616    897     while( blob_line(xfer.pIn, &xfer.line) ){
   617    898       if( blob_buffer(&xfer.line)[0]=='#' ) continue;
          899  +    if( blob_size(&xfer.line)==0 ) continue;
   618    900       xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
   619    901   
   620    902       /*   file UUID SIZE \n CONTENT
   621    903       **   file UUID DELTASRC SIZE \n CONTENT
   622    904       **
   623    905       ** Accept a file from the client.
   624    906       */
................................................................................
   625    907       if( blob_eq(&xfer.aToken[0], "file") ){
   626    908         if( !isPush ){
   627    909           cgi_reset_content();
   628    910           @ error not\sauthorized\sto\swrite
   629    911           nErr++;
   630    912           break;
   631    913         }
   632         -      xfer_accept_file(&xfer);
          914  +      xfer_accept_file(&xfer, 0);
          915  +      if( blob_size(&xfer.err) ){
          916  +        cgi_reset_content();
          917  +        @ error %T(blob_str(&xfer.err))
          918  +        nErr++;
          919  +        break;
          920  +      }
          921  +    }else
          922  +
          923  +    /*   cfile UUID USIZE CSIZE \n CONTENT
          924  +    **   cfile UUID DELTASRC USIZE CSIZE \n CONTENT
          925  +    **
          926  +    ** Accept a file from the client.
          927  +    */
          928  +    if( blob_eq(&xfer.aToken[0], "cfile") ){
          929  +      if( !isPush ){
          930  +        cgi_reset_content();
          931  +        @ error not\sauthorized\sto\swrite
          932  +        nErr++;
          933  +        break;
          934  +      }
          935  +      xfer_accept_compressed_file(&xfer);
   633    936         if( blob_size(&xfer.err) ){
   634    937           cgi_reset_content();
   635    938           @ error %T(blob_str(&xfer.err))
   636    939           nErr++;
   637    940           break;
   638    941         }
   639    942       }else
................................................................................
   644    947       */
   645    948       if( blob_eq(&xfer.aToken[0], "gimme")
   646    949        && xfer.nToken==2
   647    950        && blob_is_uuid(&xfer.aToken[1])
   648    951       ){
   649    952         nGimme++;
   650    953         if( isPull ){
   651         -        int rid = rid_from_uuid(&xfer.aToken[1], 0);
          954  +        int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
   652    955           if( rid ){
   653    956             send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
   654    957           }
   655    958         }
   656    959       }else
   657    960   
   658         -    /*   igot UUID
          961  +    /*   igot UUID ?ISPRIVATE?
   659    962       **
   660         -    ** Client announces that it has a particular file.
          963  +    ** Client announces that it has a particular file.  If the ISPRIVATE
          964  +    ** argument exists and is non-zero, then the file is a private file.
   661    965       */
   662         -    if( xfer.nToken==2
          966  +    if( xfer.nToken>=2
   663    967        && blob_eq(&xfer.aToken[0], "igot")
   664    968        && blob_is_uuid(&xfer.aToken[1])
   665    969       ){
   666    970         if( isPush ){
   667         -        rid_from_uuid(&xfer.aToken[1], 1);
          971  +        if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
          972  +          rid_from_uuid(&xfer.aToken[1], 1, 0);
          973  +        }else if( g.perm.Private ){
          974  +          rid_from_uuid(&xfer.aToken[1], 1, 1);
          975  +        }else{
          976  +          server_private_xfer_not_authorized();
          977  +        }
   668    978         }
   669    979       }else
   670    980     
   671    981       
   672    982       /*    pull  SERVERCODE  PROJECTCODE
   673    983       **    push  SERVERCODE  PROJECTCODE
   674    984       **
................................................................................
   689    999           cgi_reset_content();
   690   1000           @ error wrong\sproject
   691   1001           nErr++;
   692   1002           break;
   693   1003         }
   694   1004         login_check_credentials();
   695   1005         if( blob_eq(&xfer.aToken[0], "pull") ){
   696         -        if( !g.okRead ){
         1006  +        if( !g.perm.Read ){
   697   1007             cgi_reset_content();
   698   1008             @ error not\sauthorized\sto\sread
   699   1009             nErr++;
   700   1010             break;
   701   1011           }
   702   1012           isPull = 1;
   703   1013         }else{
   704         -        if( !g.okWrite ){
         1014  +        if( !g.perm.Write ){
   705   1015             if( !isPull ){
   706   1016               cgi_reset_content();
   707   1017               @ error not\sauthorized\sto\swrite
   708   1018               nErr++;
   709   1019             }else{
   710   1020               @ message pull\sonly\s-\snot\sauthorized\sto\spush
   711   1021             }
   712   1022           }else{
   713   1023             isPush = 1;
   714   1024           }
   715   1025         }
   716   1026       }else
   717   1027   
   718         -    /*    clone
         1028  +    /*    clone   ?PROTOCOL-VERSION?  ?SEQUENCE-NUMBER?
   719   1029       **
   720   1030       ** The client knows nothing.  Tell all.
   721   1031       */
   722   1032       if( blob_eq(&xfer.aToken[0], "clone") ){
         1033  +      int iVers;
   723   1034         login_check_credentials();
   724         -      if( !g.okClone ){
         1035  +      if( !g.perm.Clone ){
   725   1036           cgi_reset_content();
   726   1037           @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
   727   1038           @ error not\sauthorized\sto\sclone
   728   1039           nErr++;
   729   1040           break;
   730   1041         }
   731         -      isClone = 1;
   732         -      isPull = 1;
   733         -      deltaFlag = 1;
         1042  +      if( xfer.nToken==3
         1043  +       && blob_is_int(&xfer.aToken[1], &iVers)
         1044  +       && iVers>=2
         1045  +      ){
         1046  +        int seqno, max;
         1047  +        if( iVers>=3 ){
         1048  +          cgi_set_content_type("application/x-fossil-uncompressed");
         1049  +        }
         1050  +        blob_is_int(&xfer.aToken[2], &seqno);
         1051  +        max = db_int(0, "SELECT max(rid) FROM blob");
         1052  +        while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){
         1053  +          if( time(NULL) >= xfer.maxTime ) break;
         1054  +          if( iVers>=3 ){
         1055  +            send_compressed_file(&xfer, seqno);
         1056  +          }else{
         1057  +            send_file(&xfer, seqno, 0, 1);
         1058  +          }
         1059  +          seqno++;
         1060  +        }
         1061  +        if( seqno>max ) seqno = 0;
         1062  +        @ clone_seqno %d(seqno)
         1063  +      }else{
         1064  +        isClone = 1;
         1065  +        isPull = 1;
         1066  +        deltaFlag = 1;
         1067  +      }
   734   1068         @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
   735   1069       }else
   736   1070   
   737   1071       /*    login  USER  NONCE  SIGNATURE
   738   1072       **
   739   1073       ** Check for a valid login.  This has to happen before anything else.
   740   1074       ** The client can send multiple logins.  Permissions are cumulative.
   741   1075       */
   742   1076       if( blob_eq(&xfer.aToken[0], "login")
   743   1077        && xfer.nToken==4
   744   1078       ){
   745   1079         if( disableLogin ){
   746         -        g.okRead = g.okWrite = 1;
         1080  +        g.perm.Read = g.perm.Write = g.perm.Private = g.perm.Admin = 1;
   747   1081         }else{
   748   1082           if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
   749   1083            || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
   750   1084           ){
   751   1085             cgi_reset_content();
   752   1086             @ error login\sfailed
   753   1087             nErr++;
................................................................................
   759   1093       /*    reqconfig  NAME
   760   1094       **
   761   1095       ** Request a configuration value
   762   1096       */
   763   1097       if( blob_eq(&xfer.aToken[0], "reqconfig")
   764   1098        && xfer.nToken==2
   765   1099       ){
   766         -      if( g.okRead ){
         1100  +      if( g.perm.Read ){
   767   1101           char *zName = blob_str(&xfer.aToken[1]);
   768         -        if( configure_is_exportable(zName) ){
   769         -          send_config_card(&xfer, zName);
         1102  +        if( zName[0]=='/' ){
         1103  +          /* New style configuration transfer */
         1104  +          int groupMask = configure_name_to_mask(&zName[1], 0);
         1105  +          if( !g.perm.Admin ) groupMask &= ~CONFIGSET_USER;
         1106  +          if( !g.perm.RdAddr ) groupMask &= ~CONFIGSET_ADDR;
         1107  +          configure_send_group(xfer.pOut, groupMask, 0);
         1108  +        }else if( configure_is_exportable(zName) ){
         1109  +          /* Old style configuration transfer */
         1110  +          send_legacy_config_card(&xfer, zName);
   770   1111           }
   771   1112         }
   772   1113       }else
   773   1114       
   774   1115       /*   config NAME SIZE \n CONTENT
   775   1116       **
   776   1117       ** Receive a configuration value from the client.  This is only
................................................................................
   778   1119       */
   779   1120       if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
   780   1121           && blob_is_int(&xfer.aToken[2], &size) ){
   781   1122         const char *zName = blob_str(&xfer.aToken[1]);
   782   1123         Blob content;
   783   1124         blob_zero(&content);
   784   1125         blob_extract(xfer.pIn, size, &content);
   785         -      if( !g.okAdmin ){
         1126  +      if( !g.perm.Admin ){
   786   1127           cgi_reset_content();
   787   1128           @ error not\sauthorized\sto\spush\sconfiguration
   788   1129           nErr++;
   789   1130           break;
   790   1131         }
   791         -      if( zName[0]!='@' ){
   792         -        if( strcmp(zName, "logo-image")==0 ){
   793         -          Stmt ins;
   794         -          db_prepare(&ins,
   795         -            "REPLACE INTO config(name, value) VALUES(:name, :value)"
   796         -          );
   797         -          db_bind_text(&ins, ":name", zName);
   798         -          db_bind_blob(&ins, ":value", &content);
   799         -          db_step(&ins);
   800         -          db_finalize(&ins);
   801         -        }else{
   802         -          db_multi_exec(
   803         -              "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
   804         -              zName, blob_str(&content)
   805         -          );
   806         -        }
   807         -      }else{
   808         -        /* Notice that we are evaluating arbitrary SQL received from the
   809         -        ** client.  But this can only happen if the client has authenticated
   810         -        ** as an administrator, so presumably we trust the client at this
   811         -        ** point.
   812         -        */
   813         -        if( !recvConfig ){
   814         -          configure_prepare_to_receive(0);
   815         -          recvConfig = 1;
   816         -        }
   817         -        db_multi_exec("%s", blob_str(&content));
         1132  +      if( !recvConfig && zName[0]=='@' ){
         1133  +        configure_prepare_to_receive(0);
         1134  +        recvConfig = 1;
   818   1135         }
         1136  +      configure_receive(zName, &content, CONFIGSET_ALL);
   819   1137         blob_reset(&content);
   820   1138         blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
   821   1139       }else
   822   1140   
   823   1141         
   824   1142   
   825   1143       /*    cookie TEXT
................................................................................
   839   1157       ** back several different cookies to the server.  The server should be
   840   1158       ** prepared to sift through the cookies and pick the one that it wants.
   841   1159       */
   842   1160       if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
   843   1161         /* Process the cookie */
   844   1162       }else
   845   1163   
         1164  +
         1165  +    /*    private
         1166  +    **
         1167  +    ** This card indicates that the next "file" or "cfile" will contain
         1168  +    ** private content.
         1169  +    */
         1170  +    if( blob_eq(&xfer.aToken[0], "private") ){
         1171  +      if( !g.perm.Private ){
         1172  +        server_private_xfer_not_authorized();
         1173  +      }else{
         1174  +        xfer.nextIsPrivate = 1;
         1175  +      }
         1176  +    }else
         1177  +
         1178  +
         1179  +    /*    pragma NAME VALUE...
         1180  +    **
         1181  +    ** The client issue pragmas to try to influence the behavior of the
         1182  +    ** server.  These are requests only.  Unknown pragmas are silently
         1183  +    ** ignored.
         1184  +    */
         1185  +    if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
         1186  +      /*   pragma send-private
         1187  +      **
         1188  +      ** If the user has the "x" privilege (which must be set explicitly -
         1189  +      ** it is not automatic with "a" or "s") then this pragma causes
         1190  +      ** private information to be pulled in addition to public records.
         1191  +      */
         1192  +      if( blob_eq(&xfer.aToken[1], "send-private") ){
         1193  +        login_check_credentials();
         1194  +        if( !g.perm.Private ){
         1195  +          server_private_xfer_not_authorized();
         1196  +        }else{
         1197  +          xfer.syncPrivate = 1;
         1198  +        }
         1199  +      }
         1200  +    }else
         1201  +
   846   1202       /* Unknown message
   847   1203       */
   848   1204       {
   849   1205         cgi_reset_content();
   850   1206         @ error bad\scommand:\s%F(blob_str(&xfer.line))
   851   1207       }
   852   1208       blobarray_reset(xfer.aToken, xfer.nToken);
   853   1209     }
   854   1210     if( isPush ){
         1211  +    if( run_push_script()==TH_ERROR ){
         1212  +      cgi_reset_content();
         1213  +      @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
         1214  +      nErr++;
         1215  +    }
   855   1216       request_phantoms(&xfer, 500);
   856   1217     }
   857   1218     if( isClone && nGimme==0 ){
   858   1219       /* The initial "clone" message from client to server contains no
   859   1220       ** "gimme" cards. On that initial message, send the client an "igot"
   860         -    ** card for every artifact currently in the respository.  This will
         1221  +    ** card for every artifact currently in the repository.  This will
   861   1222       ** cause the client to create phantoms for all artifacts, which will
   862   1223       ** in turn make sure that the entire repository is sent efficiently
   863   1224       ** and expeditiously.
   864   1225       */
   865   1226       send_all(&xfer);
         1227  +    if( xfer.syncPrivate ) send_private(&xfer);
   866   1228     }else if( isPull ){
   867   1229       create_cluster();
   868   1230       send_unclustered(&xfer);
         1231  +    if( xfer.syncPrivate ) send_private(&xfer);
   869   1232     }
   870   1233     if( recvConfig ){
   871   1234       configure_finalize_receive();
   872   1235     }
   873   1236     manifest_crosslink_end();
         1237  +
         1238  +  /* Send the server timestamp last, in case prior processing happened
         1239  +  ** to use up a significant fraction of our time window.
         1240  +  */
         1241  +  zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
         1242  +  @ # timestamp %s(zNow)
         1243  +  free(zNow);
         1244  +
   874   1245     db_end_transaction(0);
   875   1246   }
   876   1247   
   877   1248   /*
   878   1249   ** COMMAND: test-xfer
   879   1250   **
   880   1251   ** This command is used for debugging the server.  There is a single
................................................................................
   891   1262   ** into a file named (for example) out.txt.  Then run the
   892   1263   ** server in gdb:
   893   1264   **
   894   1265   **     gdb fossil
   895   1266   **     r test-xfer out.txt
   896   1267   */
   897   1268   void cmd_test_xfer(void){
   898         -  int notUsed;
         1269  +  db_find_and_open_repository(0,0);
   899   1270     if( g.argc!=2 && g.argc!=3 ){
   900   1271       usage("?MESSAGEFILE?");
   901   1272     }
   902         -  db_must_be_within_tree();
   903   1273     blob_zero(&g.cgiIn);
   904   1274     blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]);
   905   1275     disableLogin = 1;
   906   1276     page_xfer();
   907         -  printf("%s\n", cgi_extract_content(&notUsed));
         1277  +  fossil_print("%s\n", cgi_extract_content());
   908   1278   }
   909   1279   
   910   1280   /*
   911   1281   ** Format strings for progress reporting.
   912   1282   */
   913   1283   static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
   914   1284   static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
         1285  +static const char zBriefFormat[] =
         1286  +   "Round-trips: %d   Artifacts sent: %d  received: %d\r";
   915   1287   
         1288  +#if INTERFACE
         1289  +/*
         1290  +** Flag options for controlling client_sync()
         1291  +*/
         1292  +#define SYNC_PUSH      0x0001
         1293  +#define SYNC_PULL      0x0002
         1294  +#define SYNC_CLONE     0x0004
         1295  +#define SYNC_PRIVATE   0x0008
         1296  +#define SYNC_VERBOSE   0x0010
         1297  +#endif
   916   1298   
   917   1299   /*
   918   1300   ** Sync to the host identified in g.urlName and g.urlPath.  This
   919   1301   ** routine is called by the client.
   920   1302   **
   921   1303   ** Records are pushed to the server if pushFlag is true.  Records
   922   1304   ** are pulled if pullFlag is true.  A full sync occurs if both are
   923   1305   ** true.
   924   1306   */
   925         -void client_sync(
   926         -  int pushFlag,           /* True to do a push (or a sync) */
   927         -  int pullFlag,           /* True to do a pull (or a sync) */
   928         -  int cloneFlag,          /* True if this is a clone */
   929         -  int configRcvMask,      /* Receive these configuration items */
   930         -  int configSendMask      /* Send these configuration items */
         1307  +int client_sync(
         1308  +  unsigned syncFlags,     /* Mask of SYNC_* flags */
         1309  +  unsigned configRcvMask, /* Receive these configuration items */
         1310  +  unsigned configSendMask /* Send these configuration items */
   931   1311   ){
   932   1312     int go = 1;             /* Loop until zero */
   933   1313     int nCardSent = 0;      /* Number of cards sent */
   934   1314     int nCardRcvd = 0;      /* Number of cards received */
   935   1315     int nCycle = 0;         /* Number of round trips to the server */
   936   1316     int size;               /* Size of a config value */
   937         -  int nFileSend = 0;
   938   1317     int origConfigRcvMask;  /* Original value of configRcvMask */
   939   1318     int nFileRecv;          /* Number of files received */
   940   1319     int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
   941   1320     const char *zCookie;    /* Server cookie */
   942         -  int nSent, nRcvd;       /* Bytes sent and received (after compression) */
         1321  +  i64 nSent, nRcvd;       /* Bytes sent and received (after compression) */
         1322  +  int cloneSeqno = 1;     /* Sequence number for clones */
   943   1323     Blob send;              /* Text we are sending to the server */
   944   1324     Blob recv;              /* Reply we got back from the server */
   945   1325     Xfer xfer;              /* Transfer data */
         1326  +  int pctDone;            /* Percentage done with a message */
         1327  +  int lastPctDone = -1;   /* Last displayed pctDone */
         1328  +  double rArrivalTime;    /* Time at which a message arrived */
   946   1329     const char *zSCode = db_get("server-code", "x");
   947   1330     const char *zPCode = db_get("project-code", 0);
         1331  +  int nErr = 0;           /* Number of errors */
         1332  +  int nRoundtrip= 0;      /* Number of HTTP requests */
         1333  +  int nArtifactSent = 0;  /* Total artifacts sent */
         1334  +  int nArtifactRcvd = 0;  /* Total artifacts received */
         1335  +  const char *zOpType = 0;/* Push, Pull, Sync, Clone */
   948   1336   
   949         -  if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
   950         -  if( pushFlag + pullFlag + cloneFlag == 0 
   951         -     && configRcvMask==0 && configSendMask==0 ) return;
         1337  +  if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
         1338  +  if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0 
         1339  +     && configRcvMask==0 && configSendMask==0 ) return 0;
   952   1340   
   953   1341     transport_stats(0, 0, 1);
   954   1342     socket_global_init();
   955   1343     memset(&xfer, 0, sizeof(xfer));
   956   1344     xfer.pIn = &recv;
   957   1345     xfer.pOut = &send;
   958   1346     xfer.mxSend = db_get_int("max-upload", 250000);
         1347  +  xfer.maxTime = -1;
         1348  +  if( syncFlags & SYNC_PRIVATE ){
         1349  +    g.perm.Private = 1;
         1350  +    xfer.syncPrivate = 1;
         1351  +  }
   959   1352   
   960         -  assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
   961   1353     db_begin_transaction();
   962   1354     db_record_repository_filename(0);
   963   1355     db_multi_exec(
   964   1356       "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
   965   1357     );
   966   1358     blobarray_zero(xfer.aToken, count(xfer.aToken));
   967   1359     blob_zero(&send);
   968   1360     blob_zero(&recv);
   969   1361     blob_zero(&xfer.err);
   970   1362     blob_zero(&xfer.line);
   971   1363     origConfigRcvMask = 0;
   972   1364   
         1365  +
         1366  +  /* Send the send-private pragma if we are trying to sync private data */
         1367  +  if( syncFlags & SYNC_PRIVATE ){
         1368  +    blob_append(&send, "pragma send-private\n", -1);
         1369  +  }
         1370  +
   973   1371     /*
   974   1372     ** Always begin with a clone, pull, or push message
   975   1373     */
   976         -  if( cloneFlag ){
   977         -    blob_appendf(&send, "clone\n");
   978         -    pushFlag = 0;
   979         -    pullFlag = 0;
         1374  +  if( syncFlags & SYNC_CLONE ){
         1375  +    blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
         1376  +    syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
   980   1377       nCardSent++;
   981   1378       /* TBD: Request all transferable configuration values */
   982         -  }else if( pullFlag ){
         1379  +    content_enable_dephantomize(0);
         1380  +    zOpType = "Clone";
         1381  +  }else if( syncFlags & SYNC_PULL ){
   983   1382       blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
   984   1383       nCardSent++;
         1384  +    zOpType = "Pull";
   985   1385     }
   986         -  if( pushFlag ){
         1386  +  if( syncFlags & SYNC_PUSH ){
   987   1387       blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
   988   1388       nCardSent++;
         1389  +    if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
   989   1390     }
   990   1391     manifest_crosslink_begin();
   991         -  fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
         1392  +  transport_global_startup();
         1393  +  if( syncFlags & SYNC_VERBOSE ){
         1394  +    fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
         1395  +  }
   992   1396   
   993   1397     while( go ){
   994   1398       int newPhantom = 0;
   995   1399       char *zRandomness;
   996   1400   
   997   1401       /* Send make the most recently received cookie.  Let the server
   998   1402       ** figure out if this is a cookie that it cares about.
................................................................................
  1001   1405       if( zCookie ){
  1002   1406         blob_appendf(&send, "cookie %s\n", zCookie);
  1003   1407       }
  1004   1408       
  1005   1409       /* Generate gimme cards for phantoms and leaf cards
  1006   1410       ** for all leaves.
  1007   1411       */
  1008         -    if( pullFlag || cloneFlag ){
         1412  +    if( (syncFlags & SYNC_PULL)!=0
         1413  +     || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
         1414  +    ){
  1009   1415         request_phantoms(&xfer, mxPhantomReq);
  1010   1416       }
  1011         -    if( pushFlag ){
         1417  +    if( syncFlags & SYNC_PUSH ){
  1012   1418         send_unsent(&xfer);
  1013   1419         nCardSent += send_unclustered(&xfer);
         1420  +      if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
  1014   1421       }
  1015   1422   
  1016   1423       /* Send configuration parameter requests.  On a clone, delay sending
  1017   1424       ** this until the second cycle since the login card might fail on 
  1018   1425       ** the first cycle.
  1019   1426       */
  1020         -    if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
         1427  +    if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
  1021   1428         const char *zName;
         1429  +      if( zOpType==0 ) zOpType = "Pull";
  1022   1430         zName = configure_first_name(configRcvMask);
  1023   1431         while( zName ){
  1024   1432           blob_appendf(&send, "reqconfig %s\n", zName);
  1025   1433           zName = configure_next_name(configRcvMask);
  1026   1434           nCardSent++;
  1027   1435         }
  1028         -      if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
  1029         -        configure_prepare_to_receive(0);
         1436  +      if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
         1437  +       && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
         1438  +      ){
         1439  +        int overwrite = (configRcvMask & CONFIGSET_OVERWRITE)!=0;
         1440  +        configure_prepare_to_receive(overwrite);
  1030   1441         }
  1031   1442         origConfigRcvMask = configRcvMask;
  1032   1443         configRcvMask = 0;
  1033   1444       }
  1034   1445   
  1035   1446       /* Send configuration parameters being pushed */
  1036   1447       if( configSendMask ){
  1037         -      const char *zName;
  1038         -      zName = configure_first_name(configSendMask);
  1039         -      while( zName ){
  1040         -        send_config_card(&xfer, zName);
  1041         -        zName = configure_next_name(configSendMask);
  1042         -        nCardSent++;
         1448  +      if( zOpType==0 ) zOpType = "Push";
         1449  +      if( configSendMask & CONFIGSET_OLDFORMAT ){
         1450  +        const char *zName;
         1451  +        zName = configure_first_name(configSendMask);
         1452  +        while( zName ){
         1453  +          send_legacy_config_card(&xfer, zName);
         1454  +          zName = configure_next_name(configSendMask);
         1455  +          nCardSent++;
         1456  +        }
         1457  +      }else{
         1458  +        nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
  1043   1459         }
  1044   1460         configSendMask = 0;
  1045   1461       }
  1046   1462   
  1047   1463       /* Append randomness to the end of the message.  This makes all
  1048   1464       ** messages unique so that that the login-card nonce will always
  1049   1465       ** be unique.
  1050   1466       */
  1051   1467       zRandomness = db_text(0, "SELECT hex(randomblob(20))");
  1052   1468       blob_appendf(&send, "# %s\n", zRandomness);
  1053   1469       free(zRandomness);
  1054   1470   
  1055   1471       /* Exchange messages with the server */
  1056         -    nFileSend = xfer.nFileSent + xfer.nDeltaSent;
  1057         -    fossil_print(zValueFormat, "Send:",
  1058         -                 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
  1059         -                 xfer.nFileSent, xfer.nDeltaSent);
         1472  +    if( syncFlags & SYNC_VERBOSE ){
         1473  +      fossil_print(zValueFormat, "Sent:",
         1474  +                   blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
         1475  +                   xfer.nFileSent, xfer.nDeltaSent);
         1476  +    }else{
         1477  +      nRoundtrip++;
         1478  +      nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
         1479  +      fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
         1480  +    }
  1060   1481       nCardSent = 0;
  1061   1482       nCardRcvd = 0;
  1062   1483       xfer.nFileSent = 0;
  1063   1484       xfer.nDeltaSent = 0;
  1064   1485       xfer.nGimmeSent = 0;
  1065   1486       xfer.nIGotSent = 0;
         1487  +    if( syncFlags & SYNC_VERBOSE ){
         1488  +      fossil_print("waiting for server...");
         1489  +    }
  1066   1490       fflush(stdout);
  1067         -    http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
         1491  +    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
         1492  +        MAX_REDIRECTS) ){
         1493  +      nErr++;
         1494  +      break;
         1495  +    }
         1496  +    lastPctDone = -1;
  1068   1497       blob_reset(&send);
         1498  +    rArrivalTime = db_double(0.0, "SELECT julianday('now')");
         1499  +
         1500  +    /* Send the send-private pragma if we are trying to sync private data */
         1501  +    if( syncFlags & SYNC_PRIVATE ){
         1502  +      blob_append(&send, "pragma send-private\n", -1);
         1503  +    }
  1069   1504   
  1070   1505       /* Begin constructing the next message (which might never be
  1071   1506       ** sent) by beginning with the pull or push cards
  1072   1507       */
  1073         -    if( pullFlag ){
         1508  +    if( syncFlags & SYNC_PULL ){
  1074   1509         blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
  1075   1510         nCardSent++;
  1076   1511       }
  1077         -    if( pushFlag ){
         1512  +    if( syncFlags & SYNC_PUSH ){
  1078   1513         blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
  1079   1514         nCardSent++;
  1080   1515       }
  1081   1516       go = 0;
  1082   1517   
  1083   1518       /* Process the reply that came back from the server */
  1084   1519       while( blob_line(&recv, &xfer.line) ){
  1085   1520         if( blob_buffer(&xfer.line)[0]=='#' ){
         1521  +        const char *zLine = blob_buffer(&xfer.line);
         1522  +        if( memcmp(zLine, "# timestamp ", 12)==0 ){
         1523  +          char zTime[20];
         1524  +          double rDiff;
         1525  +          sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]);
         1526  +          rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g",
         1527  +                            zTime, rArrivalTime);
         1528  +          if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0;
         1529  +          if( (rDiff*24.0*3600.0) > 10.0 ){
         1530  +             fossil_warning("*** time skew *** server is fast by %s",
         1531  +                            db_timespan_name(rDiff));
         1532  +             g.clockSkewSeen = 1;
         1533  +          }else if( rDiff*24.0*3600.0 < -(blob_size(&recv)/5000.0 + 20.0) ){
         1534  +             fossil_warning("*** time skew *** server is slow by %s",
         1535  +                            db_timespan_name(-rDiff));
         1536  +             g.clockSkewSeen = 1;
         1537  +          }
         1538  +        }
         1539  +        nCardRcvd++;
  1086   1540           continue;
  1087   1541         }
  1088   1542         xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
  1089   1543         nCardRcvd++;
  1090         -      if( !g.cgiOutput && !g.fQuiet ){
  1091         -        printf("\r%d", nCardRcvd);
  1092         -        fflush(stdout);
         1544  +      if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){
         1545  +        pctDone = (recv.iCursor*100)/recv.nUsed;
         1546  +        if( pctDone!=lastPctDone ){
         1547  +          fossil_print("\rprocessed: %d%%         ", pctDone);
         1548  +          lastPctDone = pctDone;
         1549  +          fflush(stdout);
         1550  +        }
  1093   1551         }
  1094   1552   
  1095   1553         /*   file UUID SIZE \n CONTENT
  1096   1554         **   file UUID DELTASRC SIZE \n CONTENT
  1097   1555         **
  1098   1556         ** Receive a file transmitted from the server.
  1099   1557         */
  1100   1558         if( blob_eq(&xfer.aToken[0],"file") ){
  1101         -        xfer_accept_file(&xfer);
         1559  +        xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0);
         1560  +        nArtifactRcvd++;
         1561  +      }else
         1562  +
         1563  +      /*   cfile UUID USIZE CSIZE \n CONTENT
         1564  +      **   cfile UUID DELTASRC USIZE CSIZE \n CONTENT
         1565  +      **
         1566  +      ** Receive a compressed file transmitted from the server.
         1567  +      */
         1568  +      if( blob_eq(&xfer.aToken[0],"cfile") ){
         1569  +        xfer_accept_compressed_file(&xfer);
         1570  +        nArtifactRcvd++;
  1102   1571         }else
  1103   1572   
  1104   1573         /*   gimme UUID
  1105   1574         **
  1106   1575         ** Server is requesting a file.  If the file is a manifest, assume
  1107   1576         ** that the server will also want to know all of the content files
  1108   1577         ** associated with the manifest and send those too.
  1109   1578         */
  1110   1579         if( blob_eq(&xfer.aToken[0], "gimme")
  1111   1580          && xfer.nToken==2
  1112   1581          && blob_is_uuid(&xfer.aToken[1])
  1113   1582         ){
  1114         -        if( pushFlag ){
  1115         -          int rid = rid_from_uuid(&xfer.aToken[1], 0);
         1583  +        if( syncFlags & SYNC_PUSH ){
         1584  +          int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
  1116   1585             if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
  1117   1586           }
  1118   1587         }else
  1119   1588     
  1120         -      /*   igot UUID
         1589  +      /*   igot UUID  ?PRIVATEFLAG?
  1121   1590         **
  1122   1591         ** Server announces that it has a particular file.  If this is
  1123   1592         ** not a file that we have and we are pulling, then create a
  1124   1593         ** phantom to cause this file to be requested on the next cycle.
  1125   1594         ** Always remember that the server has this file so that we do
  1126   1595         ** not transmit it by accident.
         1596  +      **
         1597  +      ** If the PRIVATE argument exists and is 1, then the file is 
         1598  +      ** private.  Pretend it does not exists if we are not pulling
         1599  +      ** private files.
  1127   1600         */
  1128         -      if( xfer.nToken==2
         1601  +      if( xfer.nToken>=2
  1129   1602          && blob_eq(&xfer.aToken[0], "igot")
  1130   1603          && blob_is_uuid(&xfer.aToken[1])
  1131   1604         ){
  1132         -        int rid = rid_from_uuid(&xfer.aToken[1], 0);
         1605  +        int rid;
         1606  +        int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
         1607  +        rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
  1133   1608           if( rid>0 ){
  1134         -          content_make_public(rid);
  1135         -        }else if( pullFlag || cloneFlag ){
  1136         -          rid = content_new(blob_str(&xfer.aToken[1]));
         1609  +          if( !isPriv ) content_make_public(rid);
         1610  +        }else if( isPriv && !g.perm.Private ){
         1611  +          /* ignore private files */
         1612  +        }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
         1613  +          rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
  1137   1614             if( rid ) newPhantom = 1;
  1138   1615           }
  1139   1616           remote_has(rid);
  1140   1617         }else
  1141   1618       
  1142   1619         
  1143   1620         /*   push  SERVERCODE  PRODUCTCODE
  1144   1621         **
  1145   1622         ** Should only happen in response to a clone.  This message tells
  1146   1623         ** the client what product to use for the new database.
  1147   1624         */
  1148   1625         if( blob_eq(&xfer.aToken[0],"push")
  1149   1626          && xfer.nToken==3
  1150         -       && cloneFlag
         1627  +       && (syncFlags & SYNC_CLONE)!=0
  1151   1628          && blob_is_uuid(&xfer.aToken[1])
  1152   1629          && blob_is_uuid(&xfer.aToken[2])
  1153   1630         ){
  1154   1631           if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
  1155   1632             fossil_fatal("server loop");
  1156   1633           }
  1157   1634           if( zPCode==0 ){
  1158   1635             zPCode = mprintf("%b", &xfer.aToken[2]);
  1159   1636             db_set("project-code", zPCode, 0);
  1160   1637           }
  1161         -        blob_appendf(&send, "clone\n");
         1638  +        if( cloneSeqno>0 ) blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
  1162   1639           nCardSent++;
  1163   1640         }else
  1164   1641         
  1165   1642         /*   config NAME SIZE \n CONTENT
  1166   1643         **
  1167   1644         ** Receive a configuration value from the server.
         1645  +      **
         1646  +      ** The received configuration setting is silently ignored if it was
         1647  +      ** not requested by a prior "reqconfig" sent from client to server.
  1168   1648         */
  1169   1649         if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
  1170   1650             && blob_is_int(&xfer.aToken[2], &size) ){
  1171   1651           const char *zName = blob_str(&xfer.aToken[1]);
  1172   1652           Blob content;
  1173   1653           blob_zero(&content);
  1174   1654           blob_extract(xfer.pIn, size, &content);
  1175         -        g.okAdmin = g.okRdAddr = 1;
  1176         -        if( configure_is_exportable(zName) & origConfigRcvMask ){
  1177         -          if( zName[0]!='@' ){
  1178         -            if( strcmp(zName, "logo-image")==0 ){
  1179         -              Stmt ins;
  1180         -              db_prepare(&ins,
  1181         -                "REPLACE INTO config(name, value) VALUES(:name, :value)"
  1182         -              );
  1183         -              db_bind_text(&ins, ":name", zName);
  1184         -              db_bind_blob(&ins, ":value", &content);
  1185         -              db_step(&ins);
  1186         -              db_finalize(&ins);
  1187         -            }else{
  1188         -              db_multi_exec(
  1189         -                  "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
  1190         -                  zName, blob_str(&content)
  1191         -              );
  1192         -            }
  1193         -          }else{
  1194         -            /* Notice that we are evaluating arbitrary SQL received from the
  1195         -            ** server.  But this can only happen if we have specifically
  1196         -            ** requested configuration information from the server, so
  1197         -            ** presumably the operator trusts the server.
  1198         -            */
  1199         -            db_multi_exec("%s", blob_str(&content));
  1200         -          }
  1201         -        }
  1202         -        nCardSent++;
         1655  +        g.perm.Admin = g.perm.RdAddr = 1;
         1656  +        configure_receive(zName, &content, origConfigRcvMask);
         1657  +        nCardRcvd++;
         1658  +        nArtifactRcvd++;
  1203   1659           blob_reset(&content);
  1204   1660           blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
  1205   1661         }else
  1206   1662   
  1207   1663         
  1208   1664         /*    cookie TEXT
  1209   1665         **
................................................................................
  1214   1670         ** Each cookie received overwrites the prior cookie from the
  1215   1671         ** same server.
  1216   1672         */
  1217   1673         if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
  1218   1674           db_set("cookie", blob_str(&xfer.aToken[1]), 0);
  1219   1675         }else
  1220   1676   
         1677  +
         1678  +      /*    private
         1679  +      **
         1680  +      ** This card indicates that the next "file" or "cfile" will contain
         1681  +      ** private content.
         1682  +      */
         1683  +      if( blob_eq(&xfer.aToken[0], "private") ){
         1684  +        xfer.nextIsPrivate = 1;
         1685  +      }else
         1686  +
         1687  +
         1688  +      /*    clone_seqno N
         1689  +      **
         1690  +      ** When doing a clone, the server tries to send all of its artifacts
         1691  +      ** in sequence.  This card indicates the sequence number of the next
         1692  +      ** blob that needs to be sent.  If N<=0 that indicates that all blobs
         1693  +      ** have been sent.
         1694  +      */
         1695  +      if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
         1696  +        blob_is_int(&xfer.aToken[1], &cloneSeqno);
         1697  +      }else
         1698  +
  1221   1699         /*   message MESSAGE
  1222   1700         **
  1223   1701         ** Print a message.  Similar to "error" but does not stop processing.
  1224   1702         **
  1225   1703         ** If the "login failed" message is seen, clear the sync password prior
  1226   1704         ** to the next cycle.
  1227   1705         */        
  1228   1706         if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
  1229   1707           char *zMsg = blob_terminate(&xfer.aToken[1]);
  1230   1708           defossilize(zMsg);
  1231         -        if( zMsg ) fossil_print("\rServer says: %s\n", zMsg);
         1709  +        if( (syncFlags & SYNC_PUSH) && zMsg && strglob("pull only *", zMsg) ){
         1710  +          syncFlags &= ~SYNC_PUSH;
         1711  +          zMsg = 0;
         1712  +        }
         1713  +        fossil_force_newline();
         1714  +        fossil_print("Server says: %s\n", zMsg);
         1715  +      }else
         1716  +
         1717  +      /*    pragma NAME VALUE...
         1718  +      **
         1719  +      ** The server can send pragmas to try to convey meta-information to
         1720  +      ** the client.  These are informational only.  Unknown pragmas are 
         1721  +      ** silently ignored.
         1722  +      */
         1723  +      if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
  1232   1724         }else
  1233   1725   
  1234   1726         /*   error MESSAGE
  1235   1727         **
  1236   1728         ** Report an error and abandon the sync session.
  1237   1729         **
  1238   1730         ** Except, when cloning we will sometimes get an error on the
................................................................................
  1239   1731         ** first message exchange because the project-code is unknown
  1240   1732         ** and so the login card on the request was invalid.  The project-code
  1241   1733         ** is returned in the reply before the error card, so second and 
  1242   1734         ** subsequent messages should be OK.  Nevertheless, we need to ignore
  1243   1735         ** the error card on the first message of a clone.
  1244   1736         */        
  1245   1737         if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
  1246         -        if( !cloneFlag || nCycle>0 ){
         1738  +        if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
  1247   1739             char *zMsg = blob_terminate(&xfer.aToken[1]);
  1248   1740             defossilize(zMsg);
  1249         -          if( strcmp(zMsg, "login failed")==0 ){
         1741  +          if( fossil_strcmp(zMsg, "login failed")==0 ){
  1250   1742               if( nCycle<2 ){
  1251   1743                 if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
  1252   1744                 go = 1;
  1253   1745               }
  1254   1746             }else{
  1255   1747               blob_appendf(&xfer.err, "\rserver says: %s", zMsg);
  1256   1748             }
  1257         -          fossil_fatal("\rError: %s", zMsg);
         1749  +          fossil_warning("\rError: %s", zMsg);
         1750  +          nErr++;
         1751  +          break;
  1258   1752           }
  1259   1753         }else
  1260   1754   
  1261   1755         /* Unknown message */
  1262         -      {
         1756  +      if( xfer.nToken>0 ){
  1263   1757           if( blob_str(&xfer.aToken[0])[0]=='<' ){
  1264         -          fossil_fatal(
         1758  +          fossil_warning(
  1265   1759               "server replies with HTML instead of fossil sync protocol:\n%b",
  1266   1760               &recv
  1267   1761             );
         1762  +          nErr++;
         1763  +          break;
  1268   1764           }
  1269         -        blob_appendf(&xfer.err, "unknown command: %b", &xfer.aToken[0]);
         1765  +        blob_appendf(&xfer.err, "unknown command: [%b]", &xfer.aToken[0]);
  1270   1766         }
  1271   1767   
  1272   1768         if( blob_size(&xfer.err) ){
  1273         -        fossil_fatal("%b", &xfer.err);
         1769  +        fossil_warning("%b", &xfer.err);
         1770  +        nErr++;
         1771  +        break;
  1274   1772         }
  1275   1773         blobarray_reset(xfer.aToken, xfer.nToken);
  1276   1774         blob_reset(&xfer.line);
  1277   1775       }
  1278         -    if( origConfigRcvMask & (CONFIGSET_TKT|CONFIGSET_USER) ){
         1776  +    if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
         1777  +     && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
         1778  +    ){
  1279   1779         configure_finalize_receive();
  1280   1780       }
  1281   1781       origConfigRcvMask = 0;
  1282         -    if( nCardRcvd>0 ){
         1782  +    if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
  1283   1783         fossil_print(zValueFormat, "Received:",
  1284   1784                      blob_size(&recv), nCardRcvd,
  1285   1785                      xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
         1786  +    }else{
         1787  +      fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
  1286   1788       }
  1287   1789       blob_reset(&recv);
  1288   1790       nCycle++;
  1289   1791   
  1290   1792       /* If we received one or more files on the previous exchange but
  1291   1793       ** there are still phantoms, then go another round.
  1292   1794       */
  1293   1795       nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
  1294   1796       if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
  1295   1797         go = 1;
  1296   1798         mxPhantomReq = nFileRecv*2;
  1297   1799         if( mxPhantomReq<200 ) mxPhantomReq = 200;
         1800  +    }else if( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
         1801  +      go = 1;
  1298   1802       }
  1299   1803       nCardRcvd = 0;
  1300   1804       xfer.nFileRcvd = 0;
  1301   1805       xfer.nDeltaRcvd = 0;
  1302   1806       xfer.nDanglingFile = 0;
  1303   1807   
  1304   1808       /* If we have one or more files queued to send, then go
................................................................................
  1305   1809       ** another round 
  1306   1810       */
  1307   1811       if( xfer.nFileSent+xfer.nDeltaSent>0 ){
  1308   1812         go = 1;
  1309   1813       }
  1310   1814   
  1311   1815       /* If this is a clone, the go at least two rounds */
  1312         -    if( cloneFlag && nCycle==1 ) go = 1;
         1816  +    if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
         1817  +
         1818  +    /* Stop the cycle if the server sends a "clone_seqno 0" card and
         1819  +    ** we have gone at least two rounds.  Always go at least two rounds
         1820  +    ** on a clone in order to be sure to retrieve the configuration
         1821  +    ** information which is only sent on the second round.
         1822  +    */
         1823  +    if( cloneSeqno<=0 && nCycle>1 ) go = 0;   
  1313   1824     };
  1314   1825     transport_stats(&nSent, &nRcvd, 1);
  1315         -  fossil_print("Total network traffic: %d bytes sent, %d bytes received\n",
  1316         -               nSent, nRcvd);
         1826  +  fossil_force_newline();
         1827  +  fossil_print(
         1828  +     "%s finished with %lld bytes sent, %lld bytes received\n",
         1829  +     zOpType, nSent, nRcvd);
  1317   1830     transport_close();
  1318   1831     transport_global_shutdown();
  1319   1832     db_multi_exec("DROP TABLE onremote");
  1320   1833     manifest_crosslink_end();
         1834  +  content_enable_dephantomize(1);
  1321   1835     db_end_transaction(0);
         1836  +  return nErr;
  1322   1837   }

Added src/xfersetup.c.

            1  +/*
            2  +** Copyright (c) 2007 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code to implement the transfer configuration
           19  +** setup screens.
           20  +*/
           21  +#include "config.h"
           22  +#include "xfersetup.h"
           23  +#include <assert.h>
           24  +
           25  +/*
           26  +** Main sub-menu for configuring the transfer system.
           27  +** WEBPAGE: xfersetup
           28  +*/
           29  +void xfersetup_page(void){
           30  +  login_check_credentials();
           31  +  if( !g.perm.Setup ){
           32  +    login_needed();
           33  +  }
           34  +
           35  +  style_header("Transfer Setup");
           36  +  @ <table border="0" cellspacing="20">
           37  +  setup_menu_entry("Common", "xfersetup_com",
           38  +    "Common TH1 code run before all transfer request processing.");
           39  +  setup_menu_entry("Push", "xfersetup_push",
           40  +    "Specific TH1 code to run after \"push\" transfer requests.");
           41  +  @ </table>
           42  +  style_footer();
           43  +}
           44  +
           45  +/*
           46  +** Common implementation for the transfer setup editor pages.
           47  +*/
           48  +static void xfersetup_generic(
           49  +  const char *zTitle,           /* Page title */
           50  +  const char *zDbField,         /* Configuration field being edited */
           51  +  const char *zDfltValue,       /* Default text value */
           52  +  const char *zDesc,            /* Description of this field */
           53  +  char *(*xText)(const char*),  /* Validity test or NULL */
           54  +  void (*xRebuild)(void),       /* Run after successful update */
           55  +  int height                    /* Height of the edit box */
           56  +){
           57  +  const char *z;
           58  +  int isSubmit;
           59  +
           60  +  login_check_credentials();
           61  +  if( !g.perm.Setup ){
           62  +    login_needed();
           63  +  }
           64  +  if( P("setup") ){
           65  +    cgi_redirect("xfersetup");
           66  +  }
           67  +  isSubmit = P("submit")!=0;
           68  +  z = P("x");
           69  +  if( z==0 ){
           70  +    z = db_get(zDbField, (char*)zDfltValue);
           71  +  }
           72  +  style_header("Edit %s", zTitle);
           73  +  if( P("clear")!=0 ){
           74  +    login_verify_csrf_secret();
           75  +    db_unset(zDbField, 0);
           76  +    if( xRebuild ) xRebuild();
           77  +    z = zDfltValue;
           78  +  }else if( isSubmit ){
           79  +    char *zErr = 0;
           80  +    login_verify_csrf_secret();
           81  +    if( xText && (zErr = xText(z))!=0 ){
           82  +      @ <p class="xfersetupError">ERROR: %h(zErr)</p>
           83  +    }else{
           84  +      db_set(zDbField, z, 0);
           85  +      if( xRebuild ) xRebuild();
           86  +      cgi_redirect("xfersetup");
           87  +    }
           88  +  }
           89  +  @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div>
           90  +  login_insert_csrf_secret();
           91  +  @ <p>%s(zDesc)</p>
           92  +  @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
           93  +  @ <blockquote><p>
           94  +  @ <input type="submit" name="submit" value="Apply Changes" />
           95  +  @ <input type="submit" name="clear" value="Revert To Default" />
           96  +  @ <input type="submit" name="setup" value="Cancel" />
           97  +  @ </p></blockquote>
           98  +  @ </div></form>
           99  +  @ <hr />
          100  +  if ( zDfltValue ){
          101  +    @ <h2>Default %s(zTitle)</h2>
          102  +    @ <blockquote><pre>
          103  +    @ %h(zDfltValue)
          104  +    @ </pre></blockquote>
          105  +  }
          106  +  style_footer();
          107  +}
          108  +
          109  +static const char *zDefaultXferCommon = 0;
          110  +
          111  +/*
          112  +** WEBPAGE: xfersetup_com
          113  +*/
          114  +void xfersetup_com_page(void){
          115  +  static const char zDesc[] =
          116  +  @ Enter TH1 script that initializes variables prior to running
          117  +  @ any of the transfer request scripts.
          118  +  ;
          119  +  xfersetup_generic(
          120  +    "Transfer Common Script",
          121  +    "xfer-common-script",
          122  +    zDefaultXferCommon,
          123  +    zDesc,
          124  +    0,
          125  +    0,
          126  +    30
          127  +  );
          128  +}
          129  +
          130  +static const char *zDefaultXferPush = 0;
          131  +
          132  +/*
          133  +** WEBPAGE: xfersetup_push
          134  +*/
          135  +void xfersetup_push_page(void){
          136  +  static const char zDesc[] =
          137  +  @ Enter TH1 script that runs after processing "push" transfer requests.
          138  +  ;
          139  +  xfersetup_generic(
          140  +    "Transfer Push Script",
          141  +    "xfer-push-script",
          142  +    zDefaultXferPush,
          143  +    zDesc,
          144  +    0,
          145  +    0,
          146  +    30
          147  +  );
          148  +}

Changes to src/zip.c.

    96     96     int i, c;
    97     97     int j;
    98     98     for(i=0; zName[i]; i++){
    99     99       if( zName[i]=='/' ){
   100    100         c = zName[i+1];
   101    101         zName[i+1] = 0;
   102    102         for(j=0; j<nDir; j++){
   103         -        if( strcmp(zName, azDir[j])==0 ) break;
          103  +        if( fossil_strcmp(zName, azDir[j])==0 ) break;
   104    104         }
   105    105         if( j>=nDir ){
   106    106           nDir++;
   107         -        azDir = realloc(azDir, sizeof(azDir[0])*nDir);
          107  +        azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
   108    108           azDir[j] = mprintf("%s", zName);
   109         -        zip_add_file(zName, 0);
          109  +        zip_add_file(zName, 0, 0);
   110    110         }
   111    111         zName[i+1] = c;
   112    112       }
   113    113     }
   114    114   }
   115    115   
   116    116   /*
   117    117   ** Append a single file to a growing ZIP archive.
   118    118   **
   119    119   ** pFile is the file to be appended.  zName is the name
   120    120   ** that the file should be saved as.
   121    121   */
   122         -void zip_add_file(const char *zName, const Blob *pFile){
          122  +void zip_add_file(const char *zName, const Blob *pFile, int mPerm){
   123    123     z_stream stream;
   124    124     int nameLen;
   125    125     int toOut = 0;
   126    126     int iStart;
   127    127     int iCRC = 0;
   128    128     int nByte = 0;
   129    129     int nByteCompr = 0;
................................................................................
   137    137     char zOutBuf[100000];
   138    138   
   139    139     /* Fill in as much of the header as we know.
   140    140     */
   141    141     nBlob = pFile ? blob_size(pFile) : 0;
   142    142     if( nBlob>0 ){
   143    143       iMethod = 8;
   144         -    iMode = 0100644;
          144  +    switch( mPerm ){
          145  +      case PERM_LNK:   iMode = 0120755;   break;
          146  +      case PERM_EXE:   iMode = 0100755;   break;
          147  +      default:         iMode = 0100644;   break;
          148  +    }
   145    149     }else{
   146    150       iMethod = 0;
   147    151       iMode = 040755;
   148    152     }
   149    153     nameLen = strlen(zName);
   150    154     memset(zHdr, 0, sizeof(zHdr));
   151    155     put32(&zHdr[0], 0x04034b50);
   152    156     put16(&zHdr[4], 0x000a);
   153         -  put16(&zHdr[6], 0);
          157  +  put16(&zHdr[6], 0x0800);
   154    158     put16(&zHdr[8], iMethod);
   155    159     put16(&zHdr[10], dosTime);
   156    160     put16(&zHdr[12], dosDate);
   157    161     put16(&zHdr[26], nameLen);
   158    162     put16(&zHdr[28], 13);
   159    163     
   160    164     put16(&zExTime[0], 0x5455);
................................................................................
   211    215     
   212    216     /* Make an entry in the tables of contents
   213    217     */
   214    218     memset(zBuf, 0, sizeof(zBuf));
   215    219     put32(&zBuf[0], 0x02014b50);
   216    220     put16(&zBuf[4], 0x0317);
   217    221     put16(&zBuf[6], 0x000a);
   218         -  put16(&zBuf[8], 0);
          222  +  put16(&zBuf[8], 0x0800);
   219    223     put16(&zBuf[10], iMethod);
   220    224     put16(&zBuf[12], dosTime);
   221    225     put16(&zBuf[14], dosDate);
   222    226     put32(&zBuf[16], iCRC);
   223    227     put32(&zBuf[20], nByteCompr);
   224    228     put32(&zBuf[24], nByte);
   225    229     put16(&zBuf[28], nameLen);
................................................................................
   285    289     if( g.argc<3 ){
   286    290       usage("ARCHIVE FILE....");
   287    291     }
   288    292     zip_open();
   289    293     for(i=3; i<g.argc; i++){
   290    294       blob_zero(&file);
   291    295       blob_read_from_file(&file, g.argv[i]);
   292         -    zip_add_file(g.argv[i], &file);
          296  +    zip_add_file(g.argv[i], &file, file_wd_perm(g.argv[i]));
   293    297       blob_reset(&file);
   294    298     }
   295    299     zip_close(&zip);
   296    300     blob_write_to_file(&zip, g.argv[2]);
   297    301   }
   298    302   
   299    303   /*
................................................................................
   311    315   ** added to as part of the zip file. It may be 0 or an empty string,
   312    316   ** in which case it is ignored. The intention is to create a zip which
   313    317   ** politely expands into a subdir instead of filling your current dir
   314    318   ** with source files. For example, pass a UUID or "ProjectName".
   315    319   **
   316    320   */
   317    321   void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
   318         -  int i;
   319         -  Blob mfile, file, hash;
   320         -  Manifest m;
          322  +  Blob mfile, hash, file;
          323  +  Manifest *pManifest;
          324  +  ManifestFile *pFile;
   321    325     Blob filename;
   322    326     int nPrefix;
   323    327     
   324    328     content_get(rid, &mfile);
   325    329     if( blob_size(&mfile)==0 ){
   326    330       blob_zero(pZip);
   327    331       return;
   328    332     }
   329         -  blob_zero(&file);
   330    333     blob_zero(&hash);
   331         -  blob_copy(&file, &mfile);
   332    334     blob_zero(&filename);
   333    335     zip_open();
   334    336   
   335    337     if( zDir && zDir[0] ){
   336    338       blob_appendf(&filename, "%s/", zDir);
   337    339     }
   338    340     nPrefix = blob_size(&filename);
   339    341   
   340         -  if( manifest_parse(&m, &mfile) ){
          342  +  pManifest = manifest_get(rid, CFTYPE_MANIFEST);
          343  +  if( pManifest ){
   341    344       char *zName;
   342         -    zip_set_timedate(m.rDate);
   343         -    blob_append(&filename, "manifest", -1);
   344         -    zName = blob_str(&filename);
   345         -    zip_add_folders(zName);
   346         -    zip_add_file(zName, &file);
   347         -    sha1sum_blob(&file, &hash);
   348         -    blob_reset(&file);
   349         -    blob_append(&hash, "\n", 1);
   350         -    blob_resize(&filename, nPrefix);
   351         -    blob_append(&filename, "manifest.uuid", -1);
   352         -    zName = blob_str(&filename);
   353         -    zip_add_file(zName, &hash);
   354         -    blob_reset(&hash);
   355         -    for(i=0; i<m.nFile; i++){
   356         -      int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
          345  +    zip_set_timedate(pManifest->rDate);
          346  +    if( db_get_boolean("manifest", 0) ){
          347  +      blob_append(&filename, "manifest", -1);
          348  +      zName = blob_str(&filename);
          349  +      zip_add_folders(zName);
          350  +      zip_add_file(zName, &mfile, 0);
          351  +      sha1sum_blob(&mfile, &hash);
          352  +      blob_reset(&mfile);
          353  +      blob_append(&hash, "\n", 1);
          354  +      blob_resize(&filename, nPrefix);
          355  +      blob_append(&filename, "manifest.uuid", -1);
          356  +      zName = blob_str(&filename);
          357  +      zip_add_file(zName, &hash, 0);
          358  +      blob_reset(&hash);
          359  +    }
          360  +    manifest_file_rewind(pManifest);
          361  +    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
          362  +      int fid = uuid_to_rid(pFile->zUuid, 0);
   357    363         if( fid ){
   358    364           content_get(fid, &file);
   359    365           blob_resize(&filename, nPrefix);
   360         -        blob_append(&filename, m.aFile[i].zName, -1);
          366  +        blob_append(&filename, pFile->zName, -1);
   361    367           zName = blob_str(&filename);
   362    368           zip_add_folders(zName);
   363         -        zip_add_file(zName, &file);
          369  +        zip_add_file(zName, &file, manifest_file_mperm(pFile));
   364    370           blob_reset(&file);
   365    371         }
   366    372       }
   367         -    manifest_clear(&m);
   368    373     }else{
   369    374       blob_reset(&mfile);
   370         -    blob_reset(&file);
   371    375     }
          376  +  manifest_destroy(pManifest);
   372    377     blob_reset(&filename);
   373    378     zip_close(pZip);
   374    379   }
   375    380   
   376    381   /*
   377         -** COMMAND: zip
          382  +** COMMAND: zip*
   378    383   **
   379         -** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
          384  +** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] [-R|--repository REPO]
   380    385   **
   381    386   ** Generate a ZIP archive for a specified version.  If the --name option is
   382    387   ** used, it argument becomes the name of the top-level directory in the
   383    388   ** resulting ZIP archive.  If --name is omitted, the top-level directory
   384    389   ** named is derived from the project name, the check-in date and time, and
   385    390   ** the artifact ID of the check-in.
   386    391   */
   387    392   void baseline_zip_cmd(void){
   388    393     int rid;
   389    394     Blob zip;
   390    395     const char *zName;
   391    396     zName = find_option("name", 0, 1);
   392         -  db_find_and_open_repository(1);
          397  +  db_find_and_open_repository(0, 0);
   393    398     if( g.argc!=4 ){
   394    399       usage("VERSION OUTPUTFILE");
   395    400     }
   396         -  rid = name_to_rid(g.argv[2]);
          401  +  rid = name_to_typed_rid(g.argv[2],"ci");
   397    402     if( zName==0 ){
   398    403       zName = db_text("default-name",
   399    404          "SELECT replace(%Q,' ','_') "
   400    405             " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
   401    406             " || substr(blob.uuid, 1, 10)"
   402    407          "  FROM event, blob"
   403    408          " WHERE event.objid=%d"
................................................................................
   419    424   void baseline_zip_page(void){
   420    425     int rid;
   421    426     char *zName, *zRid;
   422    427     int nName, nRid;
   423    428     Blob zip;
   424    429   
   425    430     login_check_credentials();
   426         -  if( !g.okZip ){ login_needed(); return; }
          431  +  if( !g.perm.Zip ){ login_needed(); return; }
   427    432     zName = mprintf("%s", PD("name",""));
   428    433     nName = strlen(zName);
   429         -  zRid = mprintf("%s", PD("uuid",""));
          434  +  zRid = mprintf("%s", PD("uuid","trunk"));
   430    435     nRid = strlen(zRid);
   431    436     for(nName=strlen(zName)-1; nName>5; nName--){
   432    437       if( zName[nName]=='.' ){
   433    438         zName[nName] = 0;
   434    439         break;
   435    440       }
   436    441     }
   437         -  rid = name_to_rid(nRid?zRid:zName);
          442  +  rid = name_to_typed_rid(nRid?zRid:zName,"ci");
   438    443     if( rid==0 ){
   439    444       @ Not found
   440    445       return;
   441    446     }
   442    447     if( nRid==0 && nName>10 ) zName[10] = 0;
   443    448     zip_of_baseline(rid, &zip, zName);
   444    449     free( zName );
   445    450     free( zRid );
   446    451     cgi_set_content(&zip);
   447    452     cgi_set_content_type("application/zip");
   448    453   }

Added test/cmdline.test.

            1  +#
            2  +# Copyright (c) 2012 D. Richard Hipp
            3  +#
            4  +# This program is free software; you can redistribute it and/or
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
            7  +#
            8  +# This program is distributed in the hope that it will be useful,
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
           11  +#
           12  +# Author contact information:
           13  +#   drh@hwaci.com
           14  +#   http://www.hwaci.com/drh/
           15  +#
           16  +############################################################################
           17  +#
           18  +# Test command line parsing
           19  +#
           20  +
           21  +proc cmd-line {testname args} {
           22  +  set i 1
           23  +  foreach {cmdline result} $args {
           24  +    fossil test-echo $cmdline
           25  +    test cmd-line-$testname.$i {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$result\]\}"}
           26  +    incr i
           27  +  }
           28  +}
           29  +cmd-line 100 abc abc a\"bc a\"bc \"abc\" \"abc\"
           30  +cmd-line 101 * * *.* *.*

Changes to test/delta1.test.

     1      1   #
     2      2   # Copyright (c) 2006 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
................................................................................
    29     23   # For each test, copy the file intact to "./t1".  Make
    30     24   # some random changes in "./t2".  Then call test-delta on the
    31     25   # two files to make sure that deltas between these two files
    32     26   # work properly.
    33     27   #
    34     28   set filelist [glob $testdir/*]
    35     29   foreach f $filelist {
           30  +  if {[file isdir $f]} continue
    36     31     set base [file root [file tail $f]]
    37         -puts "base=$base f=$f"
    38     32     set f1 [read_file $f]
    39     33     write_file t1 $f1
    40     34     for {set i 0} {$i<100} {incr i} {
    41     35       write_file t2 [random_changes $f1 1 1 0 0.1]
    42     36       fossil test-delta t1 t2
    43     37       test delta-$base-$i-1 {$RESULT=="ok"}
    44     38       write_file t2 [random_changes $f1 1 1 0 0.2]

Added test/diff-test-1.wiki.

            1  +<title>Graph Test One</title>
            2  +
            3  +This page contains list of URLs of interesting diffs.
            4  +Click on all URLs, one by one, to verify 
            5  +the correct operation of the diff logic.
            6  +
            7  +  *  <a href="../../../info/030035345c#chunk59" target="testwindow">
            8  +     Multiple edits on a single line.</a>  This is an SQLite version
            9  +     update diff.  It is a large diff and contains many other interesting
           10  +     features.  Scan the whole diff.
           11  +  *  <a href="../../../fdiff?v1=6da016415dc52d61&v2=af6df3466e3c4a88"
           12  +     target="testwindow">Tricky alignment and multiple edits per line</a>.
           13  +  *  <a href="../../../fdiff?v1=7108d4748b111d23&v2=2303a98525b39d19#chunk3"
           14  +     target="testwindow">Add a column to a table</a>
           15  +  *  <a href="../../../fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113"
           16  +     target="testwindow">Column alignment with multibyte characters.</a>
           17  +     The edit of a line with multibyte characters is the first chunk.
           18  +  *  <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
           19  +     target="testwindow">Large diff of sqlite3.c</a>.  This diff was very
           20  +     slow prior to the preformance enhancement change [9e15437e97].
           21  +  *  <a href="../../../info/bda00cbada#chunk42" target="testwindow">
           22  +     A difficult indentation change.
           23  +  *  <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13"
           24  +      target="testwindow">Another tricky indentation.</a>  Notice especially
           25  +      lines 59398 and 59407 on the left.
           26  +  *  <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13"
           27  +      target="testwindow">Inverse of the previous.</a>
           28  +  *  <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24"
           29  +      target="testwindow">A complex change</a> that is difficult to align, and
           30  +      hence falls back to the "delete left and insert right" strategy.
           31  +  *  <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk24"
           32  +      target="testwindow">Inverse of the previous.</a>
           33  +  *  <a href="../../../fdiff?v1=21f9a00fe2fa4a17&v2=d5c4ff0532bd89c3#chunk5"
           34  +      target="testwindow">sqlite3.c changes</a>
           35  +      that are difficult to align.
           36  +  *  <a href="../../../fdiff?v2=21f9a00fe2fa4a17&v1=d5c4ff0532bd89c3#chunk5"
           37  +      target="testwindow">sqlite3.c changes inverted.</a>
           38  +
           39  +External:
           40  +
           41  +  *  <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow">
           42  +     Code indentation change.</a>
           43  +  *  <a href="http://www.sqlite.org/src/info/52e755943f" target="testwindow">
           44  +     A complex change (chunk 1) in which the alignment becomes so complex
           45  +     that it is better for clarity to abandon it and just show the left
           46  +     and right sides contiguously.</a>
           47  +  *  <a href="http://www.sqlite.org/src/info/3d65c70343#chunk5"
           48  +     target="testwindow">
           49  +     An indentation change.  See especially lines 2313 and 2317 on the right,
           50  +     that their green indentation addition is left-justified.</a>

Added test/file1.test.

            1  +#
            2  +# Copyright (c) 2011 D. Richard Hipp
            3  +#
            4  +# This program is free software; you can redistribute it and/or
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
            7  +#
            8  +# This program is distributed in the hope that it will be useful,
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
           11  +#
           12  +# Author contact information:
           13  +#   drh@hwaci.com
           14  +#   http://www.hwaci.com/drh/
           15  +#
           16  +############################################################################
           17  +#
           18  +# File utilities
           19  +#
           20  +
           21  +proc simplify-name {testname args} {
           22  +  set i 1
           23  +  foreach {path result} $args {
           24  +    fossil test-simplify-name $path
           25  +    test simplify-name-$testname.$i {$::RESULT=="\[$path\] -> \[$result\]"}
           26  +    incr i
           27  +  }
           28  +}
           29  +
           30  +simplify-name 100 . . .// . .. .. ..///// ..
           31  +simplify-name 101 {} {} / / ///////// / ././././ .
           32  +simplify-name 102 x x /x /x ///x //x
           33  +simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
           34  +simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
           35  +simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
           36  +simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
           37  +simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y

Added test/graph-test-1.wiki.

            1  +<title>Graph Test One</title>
            2  +
            3  +This page contains examples a list of URLs of timelines with
            4  +interesting graphs.  Click on all URLs, one by one, to verify 
            5  +the correct operation of the graph drawing logic.
            6  +
            7  +  *  <a href="../../../timeline?n=20&y=ci&b=2010-11-08" target="testwindow">
            8  +     20-element timeline, check-ins only, before 2010-11-08</a>
            9  +  *  <a href="../../../timeline?n=20&y=ci&b=2010-11-08&ng" target="testwindow">
           10  +     20-element timeline, check-ins only, no graph, before 2010-11-08</a>
           11  +  *  <a href="../../../timeline?n=20&y=ci&b=2010-11-08&fc" target="testwindow">
           12  +     20-element timeline, check-ins only, file changes, before 2010-11-08</a>
           13  +  *  <a href="../../../timeline?n=40&y=ci&b=2010-11-08" target="testwindow">
           14  +     40-element timeline, check-ins only, before 2010-11-08</a>
           15  +  *  <a href="../../../timeline?n=1000&y=ci&b=2010-11-08" target="testwindow">
           16  +     1000-element timeline, check-ins only, before 2010-11-08</a>
           17  +  *  <a href="../../../timeline?n=10&c=2010-11-07+10:23:00" target="testwindow">
           18  +     10-elements circa 2010-11-07 10:23:00, with dividers</a>
           19  +  *  <a href="../../../timeline?n=10&c=2010-11-07+10:23:00&nd" 
           20  +     target="testwindow">
           21  +     10-elements circa 2010-11-07 10:23:00, without dividers</a>
           22  +  *  <a href="../../../timeline?f=3ea66260b5555" target="testwindow">
           23  +     Parents and children of check-in 3ea66260b5555</a>
           24  +  *  <a href="../../../timeline?d=e5fe4164f74a7576&p=e5fe4164f74a7576&n=3"
           25  +     target="testwindow">multiple merge descenders from the penultimate node.
           26  +     </a>
           27  +  *  <a href="../../../timeline?y=ci&a=2010-12-20" target="testwindow">
           28  +     multiple branch risers.</a>
           29  +  *  <a href="../../../timeline?y=ci&a=2010-12-20&n=18" target="testwindow">
           30  +     multiple branch risers, n=18.</a>
           31  +  *  <a href="../../../timeline?y=ci&a=2010-12-20&n=9" target="testwindow">
           32  +     multiple branch risers, n=9.</a>
           33  +  *  <a href="../../../timeline?r=experimental" target="testwindow">
           34  +     Experimental branch with related check-ins.</a>
           35  +  *  <a href="../../../timeline?r=experimental&mionly" target="testwindow">
           36  +     Experimental branch with merge-ins only.</a>
           37  +  *  <a href="../../../timeline?t=experimental" target="testwindow">
           38  +     Experimental branch check-ins only.</a>
           39  +  *  <a href="../../../timeline?r=experimental&n=1000" target="testwindow">
           40  +     Experimental branch using and related check-ins - 1000 elements.</a>
           41  +  *  <a href="../../../timeline?r=release" target="testwindow">
           42  +     Check-ins tagged "release" and related check-ins</a>
           43  +  *  <a href="../../../timeline?r=release&mionly" target="testwindow">
           44  +     Check-ins tagged "release" and merge-ins</a>
           45  +  *  <a href="../../../timeline?t=release" target="testwindow">
           46  +     Only check-ins tagged "release"</a>
           47  +  *  <a href="../../../finfo?name=Makefile" target="testwindow">
           48  +     History of source file "Makefile".</a>
           49  +  *  <a href="../../../timeline?a=1970-01-01" target="testwindow">
           50  +     20 elements after 1970-01-01.</a>
           51  +  *  <a href="../../../timeline?n=100000000&y=ci" target="testwindow">
           52  +     All check-ins - a huge graph.</a>
           53  +  *  <a href="../../../timeline?f=8dfed953f7530442" target="testwindow">
           54  +     This malformed commit has a 
           55  +     merge parent which is not a valid checkin.</a>
           56  +  *  <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9"
           57  +     target="testwindow">
           58  +     From e663bac6f7 to a298a0e2f9 by shortest path.</a>
           59  +  *  <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&nomerge"
           60  +     target="testwindow">
           61  +     From e663bac6f7 to a298a0e2f9 without merge links.</a>
           62  +  *  <a href="../../../timeline?me=e663bac6f7&you=a298a0e2f9"
           63  +     target="testwindow">
           64  +     Common ancestor path of e663bac6f7 to a298a0e2f9.</a>
           65  +  *  <a href="../../../timeline?f=65dd90fb95a2af55">
           66  +     Merge on the same branch does not result in a leaf.
           67  +     </a>
           68  +
           69  +External:
           70  +
           71  +  *  <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd"
           72  +     target="testwindow">Timewarp due to a mis-configured system clock.</a>

Added test/many-www.tcl.

            1  +#!/usr/bin/tclsh
            2  +#
            3  +# Run this script from within any open Fossil checkout.  Example:
            4  +#
            5  +#   tclsh many-www.tcl | tee out.txt
            6  +#
            7  +# About 10,000 different web page requests will be made.  Each is timed
            8  +# and the time shown on output. Use this script to search for segfault problems
            9  +# or to look for pages that need optimization.
           10  +#
           11  +proc run_query {url} {
           12  +  set fd [open q.txt w]
           13  +  puts $fd "GET $url HTTP/1.0\r\n\r"
           14  +  close $fd
           15  +  return [exec fossil test-http <q.txt]
           16  +}
           17  +set todo {}
           18  +foreach url {
           19  +  /home
           20  +  /timeline
           21  +  /brlist
           22  +  /taglist
           23  +  /reportlist
           24  +  /setup
           25  +  /dir
           26  +  /wcontent
           27  +  /attachlist
           28  +  /taglist
           29  +  /test_env
           30  +  /stat
           31  +  /rcvfromlist
           32  +  /urllist
           33  +  /modreq
           34  +  /info/d5c4
           35  +  /test-all-help
           36  +  /leaves
           37  +  /timeline?a=1970-01-01
           38  +} {
           39  +  set seen($url) 1
           40  +  set pending($url) 1
           41  +}
           42  +set round 1
           43  +set limit 25000
           44  +set npending [llength [array names pending]]
           45  +proc get_pending {} {
           46  +  global pending npending round next
           47  +  if {$npending==0} {
           48  +    incr round
           49  +    array set pending [array get next]
           50  +    set npending [llength [array names pending]]
           51  +    unset -nocomplain next
           52  +  }
           53  +  set res [lindex [array names pending] [expr {int(rand()*$npending)}]]
           54  +  unset pending($res)
           55  +  incr npending -1
           56  +  return $res
           57  +}
           58  +for {set i 0} {$i<$limit} {incr i} {
           59  +  set url [get_pending]
           60  +  puts -nonewline "($round/[expr {$i+1}]) $url "
           61  +  flush stdout
           62  +  set tm [time {set x [run_query $url]}]
           63  +  set ms [lindex $tm 0]
           64  +  puts [format {%.3fs} [expr {$ms/1000000.0}]]
           65  +  flush stdout
           66  +  if {[string length $x]>1000000} {
           67  +    set x [string range $x 0 1000000]
           68  +  }
           69  +  set k 0
           70  +  while {[regexp {<[aA] .*?href="(/[a-z].*?)".*?>(.*)$} $x all url tail]} {
           71  +    # if {$npending>2*($limit - $i)} break
           72  +    incr k
           73  +    if {$k>100} break
           74  +    set u2 [string map {&lt; < &gt; > &quot; \" &amp; &} $url]
           75  +    if {![info exists seen($u2)]} {
           76  +      set next($u2) 1
           77  +      set seen($u2) 1
           78  +    }
           79  +    set x $tail
           80  +  }
           81  +}

Changes to test/merge1.test.

     1      1   #
     2      2   # Copyright (c) 2006 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
................................................................................
    75     69     111 - This is line one OF the demo program - 1111
    76     70     222 - The second line program line in code - 2222
    77     71     333 - This is a test of the merging algohm - 3333
    78     72     444 - If all goes well, we will be pleased - 4444
    79     73     555 - we think it well and other stuff too - 5555
    80     74   }
    81     75   write_file_indented t23 {
    82         -  >>>>>>> BEGIN MERGE CONFLICT
           76  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
    83     77     111 - This is line ONE of the demo program - 1111
    84         -  ============================
           78  +  ======= COMMON ANCESTOR content follows ============================
           79  +  111 - This is line one of the demo program - 1111
           80  +  ======= MERGED IN content follows ==================================
    85     81     111 - This is line one OF the demo program - 1111
    86         -  <<<<<<< END MERGE CONFLICT
           82  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    87     83     222 - The second line program line in code - 2222
    88     84     333 - This is a test of the merging algohm - 3333
    89     85     444 - If all goes well, we will be pleased - 4444
    90     86     555 - we think it well and other stuff too - 5555
    91     87   }
    92     88   write_file_indented t32 {
    93         -  >>>>>>> BEGIN MERGE CONFLICT
           89  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
    94     90     111 - This is line one OF the demo program - 1111
    95         -  ============================
           91  +  ======= COMMON ANCESTOR content follows ============================
           92  +  111 - This is line one of the demo program - 1111
           93  +  ======= MERGED IN content follows ==================================
    96     94     111 - This is line ONE of the demo program - 1111
    97         -  <<<<<<< END MERGE CONFLICT
           95  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    98     96     222 - The second line program line in code - 2222
    99     97     333 - This is a test of the merging algohm - 3333
   100     98     444 - If all goes well, we will be pleased - 4444
   101     99     555 - we think it well and other stuff too - 5555
   102    100   }
   103    101   fossil test-3 t1 t3 t2 a32
   104    102   test merge1-2.1 {[same_file t32 a32]}
................................................................................
   156    154   write_file_indented t3 {
   157    155     222 - The second line program line in code - 2222
   158    156     333 - This is a test of the merging algohm - 3333
   159    157     444 - If all goes well, we will be pleased - 4444
   160    158     555 - we think it well and other stuff too - 5555
   161    159   }
   162    160   write_file_indented t32 {
   163         -  >>>>>>> BEGIN MERGE CONFLICT
   164         -  ============================
          161  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
          162  +  ======= COMMON ANCESTOR content follows ============================
          163  +  111 - This is line one of the demo program - 1111
          164  +  ======= MERGED IN content follows ==================================
   165    165     000 - Zero lines added to the beginning of - 0000
   166    166     111 - This is line one of the demo program - 1111
   167         -  <<<<<<< END MERGE CONFLICT
          167  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   168    168     222 - The second line program line in code - 2222
   169    169     333 - This is a test of the merging algohm - 3333
   170    170     444 - If all goes well, we will be pleased - 4444
   171    171     555 - we think it well and other stuff too - 5555
   172    172   }
   173    173   write_file_indented t23 {
   174         -  >>>>>>> BEGIN MERGE CONFLICT
          174  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
   175    175     000 - Zero lines added to the beginning of - 0000
   176    176     111 - This is line one of the demo program - 1111
   177         -  ============================
   178         -  <<<<<<< END MERGE CONFLICT
          177  +  ======= COMMON ANCESTOR content follows ============================
          178  +  111 - This is line one of the demo program - 1111
          179  +  ======= MERGED IN content follows ==================================
          180  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   179    181     222 - The second line program line in code - 2222
   180    182     333 - This is a test of the merging algohm - 3333
   181    183     444 - If all goes well, we will be pleased - 4444
   182    184     555 - we think it well and other stuff too - 5555
   183    185   }
   184    186   fossil test-3 t1 t3 t2 a32
   185    187   test merge1-4.1 {[same_file t32 a32]}
................................................................................
   291    293     KLMN
   292    294     OPQR
   293    295     STUV
   294    296     XYZ.
   295    297   }
   296    298   write_file_indented t23 {
   297    299     abcd
   298         -  >>>>>>> BEGIN MERGE CONFLICT
          300  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
   299    301     efgh 2
   300    302     ijkl 2
   301    303     mnop 2
   302    304     qrst
   303    305     uvwx
   304    306     yzAB 2
   305    307     CDEF 2
   306    308     GHIJ 2
   307         -  ============================
          309  +  ======= COMMON ANCESTOR content follows ============================
          310  +  efgh
          311  +  ijkl
          312  +  mnop
          313  +  qrst
          314  +  uvwx
          315  +  yzAB
          316  +  CDEF
          317  +  GHIJ
          318  +  ======= MERGED IN content follows ==================================
   308    319     efgh
   309    320     ijkl
   310    321     mnop 3
   311    322     qrst 3
   312    323     uvwx 3
   313    324     yzAB 3
   314    325     CDEF
   315    326     GHIJ
   316         -  <<<<<<< END MERGE CONFLICT
          327  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   317    328     KLMN
   318    329     OPQR
   319    330     STUV
   320    331     XYZ.
   321    332   }
   322    333   fossil test-3 t1 t2 t3 a23
   323    334   test merge1-7.1 {[same_file t23 a23]}
................................................................................
   350    361     KLMN
   351    362     OPQR
   352    363     STUV
   353    364     XYZ.
   354    365   }
   355    366   write_file_indented t23 {
   356    367     abcd
          368  +  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
   357    369     efgh 2
   358    370     ijkl 2
   359         -  >>>>>>> BEGIN MERGE CONFLICT
   360         -  mnop
          371  +  mnop 
   361    372     qrst
   362    373     uvwx
   363    374     yzAB 2
   364    375     CDEF 2
   365    376     GHIJ 2
   366         -  ============================
          377  +  ======= COMMON ANCESTOR content follows ============================
          378  +  efgh
          379  +  ijkl
          380  +  mnop
          381  +  qrst
          382  +  uvwx
          383  +  yzAB
          384  +  CDEF
          385  +  GHIJ
          386  +  ======= MERGED IN content follows ==================================
          387  +  efgh
          388  +  ijkl
   367    389     mnop 3
   368    390     qrst 3
   369    391     uvwx 3
   370    392     yzAB 3
   371    393     CDEF
   372    394     GHIJ
   373         -  <<<<<<< END MERGE CONFLICT
          395  +  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   374    396     KLMN
   375    397     OPQR
   376    398     STUV
   377    399     XYZ.
   378    400   }
   379    401   fossil test-3 t1 t2 t3 a23
   380    402   test merge1-7.2 {[same_file t23 a23]}

Changes to test/merge2.test.

     1      1   #
     2      2   # Copyright (c) 2006 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
    24     18   # Tests of the delta mechanism.
    25     19   #
    26     20   
    27     21   set filelist [glob $testdir/*]
    28     22   foreach f $filelist {
           23  +  if {[file isdir $f]} continue
    29     24     set base [file root [file tail $f]]
    30     25     set f1 [read_file $f]
    31     26     write_file t1 $f1
    32     27     for {set i 0} {$i<100} {incr i} {
    33     28       expr {srand($i*2)}
    34     29       write_file t2 [set f2 [random_changes $f1 2 4 0 0.1]]
    35     30       expr {srand($i*2+1)}

Changes to test/merge3.test.

     1      1   #
     2      2   # Copyright (c) 2009 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
................................................................................
    26     20   
    27     21   proc merge-test {testid basis v1 v2 result} {
    28     22     write_file t1 [join [string trim $basis] \n]\n
    29     23     write_file t2 [join [string trim $v1] \n]\n
    30     24     write_file t3 [join [string trim $v2] \n]\n
    31     25     fossil test-3-way-merge t1 t2 t3 t4
    32     26     set x [read_file t4]
    33         -  regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x
    34         -  regsub -all {============================} $x {=} x
    35         -  regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x
           27  +  regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \
           28  +              {MINE:} x
           29  +  regsub -all {======= COMMON ANCESTOR content follows =+} $x {COM:} x
           30  +  regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x
           31  +  regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x
    36     32     set x [split [string trim $x] \n]
    37     33     set result [string trim $result]
    38     34     if {$x!=$result} {
    39         -    puts "  Expected \[$result\]"
    40         -    puts "       Got \[$x\]"
           35  +    protOut "  Expected \[$result\]"
           36  +    protOut "       Got \[$x\]"
    41     37       test merge3-$testid 0
    42     38     } else {
    43     39       test merge3-$testid 1
    44     40     }
    45     41   }
    46     42   
    47     43   merge-test 1 {
................................................................................
    66     62   merge-test 3 {
    67     63     1 2 3 4 5 6 7 8 9
    68     64   } {
    69     65     1 2 3b 4b 5b 6 7 8 9
    70     66   } {
    71     67     1 2 3 4 5c 6 7 8 9
    72     68   } {
    73         -  1 2 > 3b 4b 5b = 3 4 5c < 6 7 8 9
           69  +  1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
    74     70   }
    75     71   merge-test 4 {
    76     72     1 2 3 4 5 6 7 8 9
    77     73   } {
    78     74     1 2 3b 4b 5b 6b 7 8 9
    79     75   } {
    80     76     1 2 3 4 5c 6 7 8 9
    81     77   } {
    82         -  1 2 > 3b 4b 5b 6b = 3 4 5c 6 < 7 8 9
           78  +  1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
    83     79   }
    84     80   merge-test 5 {
    85     81     1 2 3 4 5 6 7 8 9
    86     82   } {
    87     83     1 2 3b 4b 5b 6b 7 8 9
    88     84   } {
    89     85     1 2 3 4 5c 6c 7c 8 9
    90     86   } {
    91         -  1 2 > 3b 4b 5b 6b 7 = 3 4 5c 6c 7c < 8 9
           87  +  1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
    92     88   }
    93     89   merge-test 6 {
    94     90     1 2 3 4 5 6 7 8 9
    95     91   } {
    96     92     1 2 3b 4b 5b 6b 7 8b 9
    97     93   } {
    98     94     1 2 3 4 5c 6c 7c 8 9
    99     95   } {
   100         -  1 2 > 3b 4b 5b 6b 7 = 3 4 5c 6c 7c < 8b 9
           96  +  1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
   101     97   }
   102     98   merge-test 7 {
   103     99     1 2 3 4 5 6 7 8 9
   104    100   } {
   105    101     1 2 3b 4b 5b 6b 7 8b 9
   106    102   } {
   107    103     1 2 3 4 5c 6c 7c 8c 9
   108    104   } {
   109         -  1 2 > 3b 4b 5b 6b 7 8b = 3 4 5c 6c 7c 8c < 9
          105  +  1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
   110    106   }
   111    107   merge-test 8 {
   112    108     1 2 3 4 5 6 7 8 9
   113    109   } {
   114    110     1 2 3b 4b 5b 6b 7 8b 9b
   115    111   } {
   116    112     1 2 3 4 5c 6c 7c 8c 9
   117    113   } {
   118         -  1 2 > 3b 4b 5b 6b 7 8b 9b = 3 4 5c 6c 7c 8c 9 <
          114  +  1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
   119    115   }
   120    116   merge-test 9 {
   121    117     1 2 3 4 5 6 7 8 9
   122    118   } {
   123    119     1 2 3b 4b 5 6 7 8b 9b
   124    120   } {
   125    121     1 2 3 4 5c 6c 7c 8 9
................................................................................
   139    135   merge-test 11 {
   140    136     1 2 3 4 5 6 7 8 9
   141    137   } {
   142    138     1 2 3b 4b 5 6 7 8b 9b
   143    139   } {
   144    140     1 2 3b 4c 5 6c 7c 8 9
   145    141   } {
   146         -  1 2 > 3b 4b = 3b 4c < 5 6c 7c 8b 9b
          142  +  1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
   147    143   }
   148    144   merge-test 12 {
   149    145     1 2 3 4 5 6 7 8 9
   150    146   } {
   151    147     1 2 3b4b 5 6 7 8b 9b
   152    148   } {
   153    149     1 2 3b4b 5 6c 7c 8 9
................................................................................
   194    190   merge-test 24 {
   195    191     1 2 3 4 5 6 7 8 9
   196    192   } {
   197    193     1 6 7 8 9
   198    194   } {
   199    195     1 2 3 4 9
   200    196   } {
   201         -  1 > 6 7 8 = 2 3 4 < 9
          197  +  1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
   202    198   }
   203    199   merge-test 25 {
   204    200     1 2 3 4 5 6 7 8 9
   205    201   } {
   206    202     1 7 8 9
   207    203   } {
   208    204     1 2 3 9
   209    205   } {
   210         -  1 > 7 8 = 2 3 < 9
          206  +  1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
   211    207   }
   212    208   
   213    209   merge-test 30 {
   214    210     1 2 3 4 5 6 7 8 9
   215    211   } {
   216    212     1 2 3 4 5 6 7 9
   217    213   } {
................................................................................
   249    245   merge-test 34 {
   250    246     1 2 3 4 5 6 7 8 9
   251    247   } {
   252    248     1 2 3 4 9
   253    249   } {
   254    250     1 6 7 8 9
   255    251   } {
   256         -  1 > 2 3 4 = 6 7 8 < 9
          252  +  1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
   257    253   }
   258    254   merge-test 35 {
   259    255     1 2 3 4 5 6 7 8 9
   260    256   } {
   261    257     1 2 3 9
   262    258   } {
   263    259     1 7 8 9
   264    260   } {
   265         -  1 > 2 3 = 7 8 < 9
          261  +  1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
   266    262   }
   267    263   
   268    264   merge-test 40 {
   269    265     2 3 4 5 6 7 8
   270    266   } {
   271    267     3 4 5 6 7 8
   272    268   } {
................................................................................
   304    300   merge-test 44 {
   305    301     2 3 4 5 6 7 8
   306    302   } {
   307    303     6 7 8
   308    304   } {
   309    305     2 3 4
   310    306   } {
   311         -  > 6 7 8 = 2 3 4 <
          307  +  MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
   312    308   }
   313    309   merge-test 45 {
   314    310     2 3 4 5 6 7 8
   315    311   } {
   316    312     7 8
   317    313   } {
   318    314     2 3
   319    315   } {
   320         -  > 7 8 = 2 3 <
          316  +  MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
   321    317   }
   322    318   
   323    319   merge-test 50 {
   324    320     2 3 4 5 6 7 8
   325    321   } {
   326    322     2 3 4 5 6 7
   327    323   } {
................................................................................
   358    354   merge-test 54 {
   359    355     2 3 4 5 6 7 8
   360    356   } {
   361    357     2 3 4
   362    358   } {
   363    359     6 7 8
   364    360   } {
   365         -  > 2 3 4 = 6 7 8 <
          361  +  MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
   366    362   }
   367    363   merge-test 55 {
   368    364     2 3 4 5 6 7 8
   369    365   } {
   370    366     2 3
   371    367   } {
   372    368     7 8
   373    369   } {
   374         -  > 2 3 = 7 8 <
          370  +  MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
   375    371   }
   376    372   
   377    373   merge-test 60 {
   378    374     1 2 3 4 5 6 7 8 9
   379    375   } {
   380    376     1 2b 3 4 5 6 7 8 9
   381    377   } {
................................................................................
   413    409   merge-test 64 {
   414    410     1 2 3 4 5 6 7 8 9
   415    411   } {
   416    412     1 2b 3b 4b 5b 6 7 8 9
   417    413   } {
   418    414     1 2 3 4 9
   419    415   } {
   420         -  1 > 2b 3b 4b 5b 6 7 8 = 2 3 4 < 9
          416  +  1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
   421    417   }
   422    418   merge-test 65 {
   423    419     1 2 3 4 5 6 7 8 9
   424    420   } {
   425    421     1 2b 3b 4b 5b 6b 7 8 9
   426    422   } {
   427    423     1 2 3 9
   428    424   } {
   429         -  1 > 2b 3b 4b 5b 6b 7 8 = 2 3 < 9
          425  +  1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
   430    426   }
   431    427   
   432    428   merge-test 70 {
   433    429     1 2 3 4 5 6 7 8 9
   434    430   } {
   435    431     1 2 3 4 5 6 7 9
   436    432   } {
................................................................................
   468    464   merge-test 74 {
   469    465     1 2 3 4 5 6 7 8 9
   470    466   } {
   471    467     1 2 3 4 9
   472    468   } {
   473    469     1 2b 3b 4b 5b 6 7 8 9
   474    470   } {
   475         -  1 > 2 3 4 = 2b 3b 4b 5b 6 7 8 < 9
          471  +  1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
   476    472   }
   477    473   merge-test 75 {
   478    474     1 2 3 4 5 6 7 8 9
   479    475   } {
   480    476     1 2 3 9
   481    477   } {
   482    478     1 2b 3b 4b 5b 6b 7 8 9
   483    479   } {
   484         -  1 > 2 3 = 2b 3b 4b 5b 6b 7 8 < 9
          480  +  1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
   485    481   }
   486    482   
   487    483   merge-test 80 {
   488    484     2 3 4 5 6 7 8
   489    485   } {
   490    486     2b 3 4 5 6 7 8
   491    487   } {
................................................................................
   523    519   merge-test 84 {
   524    520     2 3 4 5 6 7 8
   525    521   } {
   526    522     2b 3b 4b 5b 6 7 8
   527    523   } {
   528    524     2 3 4
   529    525   } {
   530         -  > 2b 3b 4b 5b 6 7 8 = 2 3 4 <
          526  +  MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
   531    527   }
   532    528   merge-test 85 {
   533    529     2 3 4 5 6 7 8
   534    530   } {
   535    531     2b 3b 4b 5b 6b 7 8
   536    532   } {
   537    533     2 3
   538    534   } {
   539         -  > 2b 3b 4b 5b 6b 7 8 = 2 3 <
          535  +  MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
   540    536   }
   541    537   
   542    538   merge-test 90 {
   543    539     2 3 4 5 6 7 8
   544    540   } {
   545    541     2 3 4 5 6 7
   546    542   } {
................................................................................
   578    574   merge-test 94 {
   579    575     2 3 4 5 6 7 8
   580    576   } {
   581    577     2 3 4
   582    578   } {
   583    579     2b 3b 4b 5b 6 7 8
   584    580   } {
   585         -  > 2 3 4 = 2b 3b 4b 5b 6 7 8 <
          581  +  MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
   586    582   }
   587    583   merge-test 95 {
   588    584     2 3 4 5 6 7 8
   589    585   } {
   590    586     2 3
   591    587   } {
   592    588     2b 3b 4b 5b 6b 7 8
   593    589   } {
   594         -  > 2 3 = 2b 3b 4b 5b 6b 7 8 <
          590  +  MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
   595    591   }
   596    592   
   597    593   merge-test 100 {
   598    594     1 2 3 4 5 6 7 8 9
   599    595   } {
   600    596     1 2b 3 4 5 7 8 9 a b c d e
   601    597   } {
................................................................................
   624    620   merge-test 103 {
   625    621     1 2 3 4 5 6 7 8 9
   626    622   } {
   627    623     1 2 3 4 5 7 8 9b
   628    624   } {
   629    625     1 2 3 4 5 7 8 9b a b c d e
   630    626   } {
   631         -  1 2 3 4 5 7 8 > 9b = 9b a b c d e <
          627  +  1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END
   632    628   }
   633    629   merge-test 104 {
   634    630     1 2 3 4 5 6 7 8 9
   635    631   } {
   636    632     1 2 3 4 5 7 8 9b a b c d e
   637    633   } {
   638    634     1 2 3 4 5 7 8 9b
   639    635   } {
   640         -  1 2 3 4 5 7 8 > 9b a b c d e = 9b <
          636  +  1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END
   641    637   }

Changes to test/merge4.test.

     1      1   #
     2      2   # Copyright (c) 2009 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
................................................................................
    27     21   proc merge-test {testid basis v1 v2 result1 result2} {
    28     22     write_file t1 [join [string trim $basis] \n]\n
    29     23     write_file t2 [join [string trim $v1] \n]\n
    30     24     write_file t3 [join [string trim $v2] \n]\n
    31     25     fossil test-3-way-merge t1 t2 t3 t4
    32     26     fossil test-3-way-merge t1 t3 t2 t5
    33     27     set x [read_file t4]
    34         -  regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x
    35         -  regsub -all {============================} $x {=} x
    36         -  regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x
           28  +  regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $x {>} x
           29  +  regsub -all {=======.*=======} $x {=} x
           30  +  regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
    37     31     set x [split [string trim $x] \n]
    38     32     set y [read_file t5]
    39         -  regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $y {>} y
    40         -  regsub -all {============================} $y {=} y
    41         -  regsub -all {<<<<<<< END MERGE CONFLICT} $y {<} y
           33  +  regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $y {>} y
           34  +  regsub -all {=======.*=======} $y {=} y
           35  +  regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
    42     36     set y [split [string trim $y] \n]
    43     37     set result1 [string trim $result1]
    44     38     if {$x!=$result1} {
    45         -    puts "  Expected \[$result1\]"
    46         -    puts "       Got \[$x\]"
           39  +    protOut "  Expected \[$result1\]"
           40  +    protOut "       Got \[$x\]"
    47     41       test merge3-$testid 0
    48     42     } else {
    49     43       set result2 [string trim $result2]
    50     44       if {$y!=$result2} {
    51         -      puts "  Expected \[$result2\]"
    52         -      puts "       Got \[$y\]"
           45  +      protOut "  Expected \[$result2\]"
           46  +      protOut "       Got \[$y\]"
    53     47         test merge3-$testid 0
    54     48       } else {
    55     49         test merge3-$testid 1
    56     50       }
    57     51     }
    58     52   }
    59     53   

Added test/merge5.test.

            1  +#
            2  +# Copyright (c) 2010 D. Richard Hipp
            3  +#
            4  +# This program is free software; you can redistribute it and/or
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
            7  +#
            8  +# This program is distributed in the hope that it will be useful,
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
           11  +#
           12  +# Author contact information:
           13  +#   drh@hwaci.com
           14  +#   http://www.hwaci.com/drh/
           15  +#
           16  +############################################################################
           17  +#
           18  +# Tests of the "merge" command
           19  +#
           20  +
           21  +# Verify the results of a check-out
           22  +#
           23  +proc checkout-test {testid expected_content} {
           24  +  set flist {}
           25  +  foreach {status filename} [exec $::fossilexe ls -l] {
           26  +    if {$status!="DELETED"} {lappend flist $filename}
           27  +  }
           28  +  eval fossil sha1sum [lsort $flist]
           29  +  global RESULT
           30  +  regsub -all {\n *} [string trim $expected_content] "\n    " expected
           31  +  regsub -all {\n *} [string trim $RESULT] "\n    " result
           32  +  if {$result!=$expected} {
           33  +    protOut "  Expected:\n    $expected"
           34  +    protOut "  Got:\n    $result"
           35  +    test merge5-$testid 0
           36  +  } else {
           37  +    test merge5-$testid 1
           38  +  }    
           39  +}
           40  +
           41  +catch {exec $::fossilexe info} res
           42  +puts res=$res
           43  +if {![regexp {use --repository} $res]} {
           44  +  puts stderr "Cannot run this test within an open checkout"
           45  +  return
           46  +}
           47  +#
           48  +# Fossil will write data on $HOME, running 'fossil open' here.
           49  +# We need not to clutter the $HOME of the test caller.
           50  +set env(HOME) [pwd]
           51  +
           52  +# Construct a test repository
           53  +#
           54  +exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql
           55  +fossil rebuild m5.fossil
           56  +fossil open m5.fossil
           57  +fossil update baseline
           58  +checkout-test 10 {
           59  +  da5c8346496f3421cb58f84b6e59e9531d9d424d  one.txt
           60  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
           61  +  278a402316510f6ae4a77186796a6bde78c7dbc1  two.txt
           62  +}
           63  +
           64  +# Update to the tip of the trunk
           65  +#
           66  +fossil update trunk
           67  +checkout-test 20 {
           68  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
           69  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
           70  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
           71  +}
           72  +
           73  +# Merge in the change that adds file four.txt
           74  +#
           75  +fossil merge br1
           76  +checkout-test 30 {
           77  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
           78  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
           79  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
           80  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
           81  +}
           82  +
           83  +# Undo the merge.  Verify restoration of former state.
           84  +#
           85  +fossil undo
           86  +checkout-test 40 {
           87  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
           88  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
           89  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
           90  +}
           91  +
           92  +# Now switch to br1 then merge in the trunk.  Verify that the result
           93  +# is the same as merging br1 into trunk.
           94  +#
           95  +fossil update br1
           96  +fossil merge trunk
           97  +checkout-test 50 {
           98  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
           99  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          100  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          101  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          102  +}
          103  +fossil undo
          104  +fossil update trunk
          105  +checkout-test 60 {
          106  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          107  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          108  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          109  +}
          110  +
          111  +# Go back to the tip of the trunk and merge branch br1 again.  This
          112  +# time do a check-in of the merge.  Verify that the same content appears
          113  +# after the merge.
          114  +#
          115  +fossil update chng3
          116  +fossil merge br1
          117  +checkout-test 70 {
          118  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          119  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          120  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          121  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          122  +}
          123  +fossil commit -tag m1 -m {merge with br1} -nosign -f
          124  +checkout-test 71 {
          125  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          126  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          127  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          128  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          129  +}
          130  +fossil update chng3
          131  +checkout-test 72 {
          132  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          133  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          134  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          135  +}
          136  +fossil update m1
          137  +checkout-test 73 {
          138  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          139  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          140  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          141  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          142  +}
          143  +
          144  +# Merge br2 into the trunk.  br2 contains some independent change to the
          145  +# two.txt file.  Verify that these are merge in correctly.
          146  +#
          147  +fossil update m1
          148  +fossil merge br2
          149  +checkout-test 80 {
          150  +  8f09bc55a60eb8ca06f10a3b577aafa869b31695  five.txt
          151  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          152  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          153  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          154  +  68eeee8b843eaea76e33d3911f416b745d0e5e5c  two.txt
          155  +}
          156  +fossil undo
          157  +checkout-test 81 {
          158  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          159  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          160  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          161  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          162  +}
          163  +
          164  +# Now merge trunk into br2.  Verify that the same set of changes result.
          165  +#
          166  +fossil update br2
          167  +fossil merge trunk
          168  +checkout-test 90 {
          169  +  8f09bc55a60eb8ca06f10a3b577aafa869b31695  five.txt
          170  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          171  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          172  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          173  +  68eeee8b843eaea76e33d3911f416b745d0e5e5c  two.txt
          174  +}
          175  +fossil undo
          176  +checkout-test 91 {
          177  +  8f09bc55a60eb8ca06f10a3b577aafa869b31695  five.txt
          178  +  da5c8346496f3421cb58f84b6e59e9531d9d424d  one.txt
          179  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
          180  +  85286cb3bc6d9e6f2f586eb5532f6065678f75b9  two.txt
          181  +}
          182  +
          183  +# Starting from chng3, merge in br4.  The one file is deleted from br4, so
          184  +# the merge should cause the one file to disappear from the checkout.
          185  +#
          186  +fossil update chng3
          187  +checkout-test 100 {
          188  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          189  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          190  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          191  +}
          192  +fossil merge br4
          193  +checkout-test 101 {
          194  +  6e167b139c294bed560e2e30b352361b101e1f39  four.txt
          195  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          196  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          197  +}
          198  +fossil undo
          199  +checkout-test 102 {
          200  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          201  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          202  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          203  +}
          204  +
          205  +# Do the same merge of br4 into chng3, but this time check it in as a new
          206  +# branch.
          207  +#
          208  +fossil update chng3
          209  +fossil merge br4
          210  +fossil commit -nosign -branch br4-b -m {merge in br4} -tag m2
          211  +checkout-test 110 {
          212  +  6e167b139c294bed560e2e30b352361b101e1f39  four.txt
          213  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          214  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          215  +}
          216  +
          217  +# Branches br1 and br4 both add file four.txt.  So if we merge them together,
          218  +# the version of file four.txt in the original should be preserved.
          219  +#
          220  +fossil update br1
          221  +checkout-test 120 {
          222  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          223  +  da5c8346496f3421cb58f84b6e59e9531d9d424d  one.txt
          224  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
          225  +  278a402316510f6ae4a77186796a6bde78c7dbc1  two.txt
          226  +}
          227  +fossil merge br4
          228  +checkout-test 121 {
          229  +  35815cf5804e8933eab64ae34e00bbb381be72c5  four.txt
          230  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
          231  +  278a402316510f6ae4a77186796a6bde78c7dbc1  two.txt
          232  +}
          233  +fossil undo
          234  +fossil update br4
          235  +checkout-test 122 {
          236  +  6e167b139c294bed560e2e30b352361b101e1f39  four.txt
          237  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
          238  +  278a402316510f6ae4a77186796a6bde78c7dbc1  two.txt
          239  +}
          240  +fossil merge br1
          241  +checkout-test 123 {
          242  +  6e167b139c294bed560e2e30b352361b101e1f39  four.txt
          243  +  ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4  three.txt
          244  +  278a402316510f6ae4a77186796a6bde78c7dbc1  two.txt
          245  +}
          246  +fossil undo
          247  +
          248  +# Merge br5 (which includes a file rename) into chng3
          249  +#
          250  +fossil update chng3
          251  +checkout-test 130 {
          252  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          253  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          254  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          255  +}
          256  +fossil merge br5
          257  +checkout-test 131 {
          258  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          259  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          260  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          261  +}
          262  +fossil undo
          263  +checkout-test 132 {
          264  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          265  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          266  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          267  +}
          268  +fossil merge br5
          269  +checkout-test 133 {
          270  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          271  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          272  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          273  +}
          274  +fossil commit -nosign -m {merge with rename} -branch {trunk+br5}
          275  +checkout-test 134 {
          276  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          277  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          278  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          279  +}
          280  +fossil update chng3
          281  +checkout-test 135 {
          282  +  6f525ab779ad66e24474d845c5fb7938be42d50d  one.txt
          283  +  64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b  three.txt
          284  +  b262fee89ed8a27a23a5e09d3917e0bebe22cd24  two.txt
          285  +}
          286  +fossil update trunk+br5
          287  +checkout-test 136 {
          288  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          289  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          290  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          291  +}
          292  +
          293  +# Merge the chng3 check-in into br5, verifying that the change to two.txt
          294  +# from chng3 are applies to two-rename.txt in br5.
          295  +#
          296  +fossil update br5
          297  +checkout-test 140 {
          298  +  e866bb885d5184cba497cfb6a4eb281688519521  one.txt
          299  +  e09593950837f76e70ca2f8ff2272ae3df0ba017  three.txt
          300  +  5ebb3c9ad50740a7382902657b84a6105c32fc7b  two-rename.txt
          301  +}
          302  +fossil merge chng3
          303  +checkout-test 141 {
          304  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          305  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          306  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          307  +}
          308  +fossil commit -nosign -m {change to two} -branch br5-2
          309  +checkout-test 142 {
          310  +  7eaf64a2c9141277b4c24259c7766d6a77047af7  one.txt
          311  +  98e47f99bb9fed4fdcd407f553615ca7f15a38a2  three.txt
          312  +  e58c5da3e6007d0e30600ea31611813093ad180f  two-rename.txt
          313  +}

Added test/merge5_repo.sql.

            1  +PRAGMA foreign_keys=OFF;
            2  +BEGIN TRANSACTION;
            3  +CREATE TABLE blob(
            4  +  rid INTEGER PRIMARY KEY,
            5  +  rcvid INTEGER,
            6  +  size INTEGER,
            7  +  uuid TEXT UNIQUE NOT NULL,
            8  +  content BLOB,
            9  +  CHECK( length(uuid)==40 AND rid>0 )
           10  +);
           11  +INSERT INTO "blob" VALUES(1,1,160,'85d5623f065ba80b20a035dcb4f9aea045215406',X'000000A0789C1DC8BD0E82301000E0BD4F71334993FE1CB465D507300617C352AED7D0208D011C787BA3E3F75DA0D47294F81A775EDFC739EE34332DB2547105A3B492DA48AD078D7D1B7A6BC54DDC21A14E9E52F059A9C928E4E0950AC133658FC6B118A099B6586986068EED5397DFECE72AFF80463C206DB37802C69CACCB31B8EC983B424D2D2361B04645EE92F802B1932DED');
           12  +INSERT INTO "blob" VALUES(2,2,10,'da5c8346496f3421cb58f84b6e59e9531d9d424d',X'0000000A789C73E472E272E672E172E5020008CA0182');
           13  +INSERT INTO "blob" VALUES(3,2,10,'ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4',X'0000000A789CF3E6F2E1F2E5F2E3F2E7020009F601B4');
           14  +INSERT INTO "blob" VALUES(4,2,10,'278a402316510f6ae4a77186796a6bde78c7dbc1',X'0000000A789C73E372E7F2E0F2E4F2E202000960019B');
           15  +INSERT INTO "blob" VALUES(5,2,325,'69cc5363f67013c09bf506e3fe9533c32d0bc1ef',X'000000AF789C05C1C10AC2300C00D07BBF621FB0439A365B3B2F0351F426CA2EB24BBAA4AC3005D7FD3FBE47645EC3B96191B91EEBAE3AD7F22D47E16DAEB96C5AEFE3B5F5030E00388DF6D1DADF1048A84397A1A3C401120283235992CF9195C1135AF2D09967B308E788392FA187B8B0042F2E893AC60E803499A9917D35EF2670C09853E0A8092893EF859CB33D6A8414351AFCF0ED72FA03D5E231F2');
           16  +INSERT INTO "blob" VALUES(6,3,190,'3bfa21d09f8718adc3195848d57114e1fbf564fe',X'000000BE789C8DCC3B0EC2300C00D03DA7C88E2AF9A32475678E5016C49238B6400206D285DB036261E400EFED2301C2843421AE9816A02595B0C65DAF9BC52CAA89337B2E80AC20CD1364633749CCCAD4A1299AFF24A7F15D003ECB78DEA656875D2FF7FFB77088FD710EC758BA6095590C082BEAECEC4AC58CDEBEA2617801EE253190');
           17  +INSERT INTO "blob" VALUES(7,4,10,'35815cf5804e8933eab64ae34e00bbb381be72c5',X'0000000A789C0BE00AE40AE20AE60AE102000A8C01CD');
           18  +INSERT INTO "blob" VALUES(8,4,364,'341db367709dc7bc5046ab4fda4f7d9423118cba',X'0000016C789C35CE3B4E44310C85E1FEAE221B1864C78F24B4206A84A0413476EC888A918641B07CEE0CA23D3A9FF4DF158B78FB5CC7AFD3CDF9E7BCDD970A0807AC07C467945BE05B94EDA1FC1F0A4947994B3A70F64194E6CA96C409E0EED4D1B3D57931C78FBC9230999D5879E822AE385DFAEAEC9A327208618CE0CAB193F3FB29FF50C6BEE0885635B0D1C21EBED886C1EA821C6694348D2FE8FB7825B57563A8842A084B2DD95AC3AE6DA8A947B63E5BF8C4EDB1E8985348696903A409C3978026AD4B0F4DAA01FB31D7F65422C1AC73A4A3D5BD7CEF07016B16BBB5C9DB4B89D3FBF65A6C7873C33052034A6D94E82CAED85BA4ACED1765FC64F9');
           19  +INSERT INTO "blob" VALUES(9,5,294,'9223b4cad40c0bc997b747fe4eee586559a43815',X'00000126789C8D8E310E83300C45774E91B955243B714860EE11E85275B16322A4AA0C0186DEBEA00E5DD9FEF2FE7B37E300C1A2B38803861EA8F7D40CE62295E73C194FA8E2DB18A1D31C2507A096858A3295A81D398F98B2B0918A07B67CDE769FA7B99DB92AAFE379D1BFF7B9FC8231EC2FF630AF759B5FE7DD77A3756A1EC68D0C3E69424DA20E3460804C259172F15E52F305DF884EE5');
           20  +INSERT INTO "blob" VALUES(10,6,10,'8f09bc55a60eb8ca06f10a3b577aafa869b31695',X'0000000A789C0BE50AE30AE78AE08AE402000B2201E6');
           21  +INSERT INTO "blob" VALUES(11,6,426,'3d5bce2e44a6b39205eb514c581aa41d86f48540',X'00000116789C458ECB4A04311444F7F98AAC075BEE23B949DA8DA01F203A832082E471C30C6A2FBA47D1BF37BA7157A7288A23DDBCCC3736B7F6BCF5D3A75E9EBFCEE6D612204C4813E21EFD0C7E266AD78F177C9829C4EC8018C52374C9EA7208182524C9529A8658432B15CD9D9554AB67E12E01902BA4D23D8872D7E4992B538331D46EEE6D23AA320E1CE4C6B1A5CCDDA973181C6BF705CCDEEECA9A977AB43B5B56FAE5EDFB7D1AD1EE064C7F90377D3B2DFADF9CD78FE575E0C1B6F5689E6C57284961887A14A89A628B340C2825E118C020A6F0E0AE7E00A7744B2A');
           22  +INSERT INTO "blob" VALUES(12,7,10,'c6536e40b3d4755ead483c2e0fb2f16ecb751b6a',X'0000000A789C33E032E432E232E632E1020006CC012D');
           23  +INSERT INTO "blob" VALUES(13,7,424,'b95701e0b73e5ebf452a9a7ae00360bf7fd187fd',X'000001A8789C4590414F83310886EFDFAFE879C90C9496B65E359E8DD18BF10285668B3A936F33EABFB753136FBCC003BC5C05317B3A1EF79F17A7CFD3721D22206C316E11EF315F025FC6B8DC84B7839FEBC124F74A8953E3412962D75C474DCA9E9BB74C68CD524C3691BF91A17326F6044A964ACE2E962AF5E830340E64EF5A322ACB444EBBD57FF7B8CD21D8AC44362C34B09A8E244D60D48CC944C8A94B3A431F6F3F482C55124442CE0883C5939482954B6361352FB517D38ECB6DE0D6FB3C8A061740EAD0746460A771B6409DA2C16CF4B1DC85311D4D9AA4E4DEB21924D10AAA0834AD132CF761A3AB1CFA2E6C82AE74D6C7AFD7ED0CC3668AED8F90A3BFEC0FFE9F39ADEF87E7291F82ADBBE5312895341D420637C74AB5CD6F5185E23AC41B2FDF3224768A');
           24  +INSERT INTO "blob" VALUES(14,8,11,'6f525ab779ad66e24474d845c5fb7938be42d50d',X'0000000B789C73E4728AE072E672E172E502000C1801DA');
           25  +INSERT INTO "blob" VALUES(15,8,319,'c19f6ae71a35cddef0d5de3e1e6109c4378c8215',X'000000E0789C0DCA314E04310C05D03EA7C80158E41F27F6646890587A844048681B274E986A4762B6A0E2ECF0EA977FC3B63EC5BED9F56B1C97E3B65F8EFD3AEE6F3FB7708E894027A413F086B292AE00F6C7ED2E7DAC4917CB94185240536C6453C5225AC5A4F9D0A5ABB78EF012A5F65E58788A12B8536DB3900C9EA316E6CEC9E93F8E195EA361641573481EA5D6E900235B422FD49435BC47FFDEC2672469A9CEC98AAEE685672ECD45B28FEA8B590BA99D9FBD3DFC010C0C3CFF');
           26  +INSERT INTO "blob" VALUES(16,9,11,'b262fee89ed8a27a23a5e09d3917e0bebe22cd24',X'0000000B789C73E372E7F288E0F2E4F2E202000C5A01F3');
           27  +INSERT INTO "blob" VALUES(17,9,319,'41a02f2b2277d771d85e7f1202c82d4d6675662f',X'000000D8789C0DCE3B4E03410C00D07E4EB107D820DB63CF8F668B488882068890E8BCB6478806258A142ACE4E0EF0A4C77FE96983F56DDC7E1EAEBFD7745C08100E4807C47794017590206CDFAB8F7062C7EE958A63CD139BEF93B52BCC26C8AE9A239BF265A39715CFC3B0CFA25151B3987B4C70F1C8815110BA71AECD1AA1A4D785F5AE4929B07397CADE729F6462358A36F3745AFCF2953E17C9AD30766AA5CE3DD8E27EA1BD548FD80D9C533E3F9FE6C7E33F8FCE3AED');
           28  +INSERT INTO "blob" VALUES(18,10,187,'6c22c86ee8a603f5e18171efb6ff571b59e35cf3',X'000000BB789C8DCCAD0EC230140650DFA7A8274BFAB5B4B7ADE611862198EEFE30038261787B9843A24F724E3E068409710266E41E6A4776B33FC878A967342B4309236516510B924593420B42E363A2CA3522FF24D76D5FA803FBB2BDEF13AF8F1BFEAEDCD9CB7375176F45D8961C858C9651BED86C080189381621F701B249318A');
           29  +INSERT INTO "blob" VALUES(19,11,187,'8b9f82378ea7a5f9ef8e80d31929f6dfab988acc',X'000000BB789C8DCC3D0EC2300C06D03DA7C88E22D95F623BEACC11CA8258F24B1718280BB747DD18790778670F620A8CC0BCB22C9497486EF5A75EDEC3272E84890A987533EE59864D06A165F4D4554D54317F92DB7E2CB6408E65FF3C42DB9E77FC5DB98BEFAFCD5DBD0C9E604819AC31D5A489F2906AA5C5AAA4C57D01A0C52E74');
           30  +INSERT INTO "blob" VALUES(20,12,11,'64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b',X'0000000B789CF3E6F2E1F2E5F28BE0F2E702000CA7020C');
           31  +INSERT INTO "blob" VALUES(21,12,321,'5a71ecd64585155b076a4f1e4d489eb5fe3d05f4',X'00000141789C3D8F314E04310C45FB9C221758643B719C6C0BA246081A44E3C40E53ED48BB23C1F1095B50B8F8FA7A4FDF8F716C7AF9F2DBE7EDD8D76D57F787E3E7084F9100E18474427C433E239D81C273DC2FF73E96C9C4DA459A5A294E394BB69A79F0ECD252ED9EC9186C21FFD258B256E52189608E31357772B6B91823504E15B596C1FD0FFADEEF48A742D3BD36B7AA244A49D9A1596A280EDDBB130DA31C5E6246059AD489444C04ADB2CB44021A952CAF99C265D9C2EBB2226371186AA4C35704D7C252C0B06A1FE13DDA750B1F9111E6C4543CF1C4A1136606582F0FE406D424FC021EAB59DA');
           32  +INSERT INTO "blob" VALUES(22,13,187,'a2c1640c98b3059a83df0b597d673ad073b782de',X'000000BB789C8DCC310EC2300C00C03DAFC88E22D9899DA49D794259108B93D874818176E1F7805818994FBAA38F80103006C40579C63863718B3F0CD9D5B314D43E327165646E50B290A1D2A03A6963D334808D7E92CBF65D207E96ED790B7DBD5FD3DF953BF9F158DDD96B1EB1D91B92285649B917AD5627A8066842EE05469C30D4');
           33  +INSERT INTO "blob" VALUES(23,14,10,'6e167b139c294bed560e2e30b352361b101e1f39',X'0000000A789C33E532E332E7B2E0B2E4020007620146');
           34  +INSERT INTO "blob" VALUES(24,14,388,'8015e80a2391d4497530b5d0f36689f359e374ba',X'00000184789C4590314FC4300C85F7FE8ACC2715D971E234C7086246E8585017277174086851DBD3C1BF27BD85CDCFF6A7F7EC075396F97B5CE749EFC7554A19D749AFE35AE7CBD23D1A0B083DDA1EF184FE8874F4DC3D997D78B7FD6C86153924A4986D74498B6750AB0489BC25C684808A956263B6F3A27A83B45857309660B960A08A4349D54914A88347574448298BDBA1EB7C436C18C48125648F5059D449083870882C9C8A862187923276CF8663CE9E982A0740CA1053F5C04A55A327CA640BB445ADDD8B69E611497373AF425407B0FB19A145C6C04CDDC91CD222533E9B83498BDBF5FAFBD5B7D21C9AE86F4256FD7C9FF4BFB32D97E9A3C9D7F6DA73F7666A96907CB351CB9A23D89CAB44675B50715CA8FB033C856E21');
           35  +INSERT INTO "blob" VALUES(25,15,11,'e866bb885d5184cba497cfb6a4eb281688519521',X'0000000B789C73E472E272E672E1728DE002000AF201DA');
           36  +INSERT INTO "blob" VALUES(26,15,372,'b882b17dc56af1e0297bf8a57c9d15050215c64a',X'00000174789C458E414B04310C85EFF32B7A5E1869D23669BD2A9E45D68B78499BD41175166657D47F6F7711841CF2F2DE47DE8D6B8BAC2FF67C3CAC36DD3AF4E067C019600FE97A0CD274E7867775FA3E39CB44B5E69C34418EAD4A2CDC7A25895631030D074A4218C869D9EC0F528C0A451949814387ACB54729E2FB88471509169AC433F475B820C859A2C70094C077128BC20C99B8905055E3DC586B83E9DE51692D050A9DD84368BED49E3C59E85652082DA0FA11B43E3D38EB297266D4D414C427E30E56C2E8D0A1F9D4A7BDDBD54DD6B6B89DAB5B3AEBE3CFC73C56B71B62BE0839DAFBEB6AFF97D3F6B9BE0DF9E8745BA6A7F1463B76C4AC68218D9A1AA216C2D822D4C63CFD0205E667AF');
           37  +INSERT INTO "blob" VALUES(27,16,11,'e09593950837f76e70ca2f8ff2272ae3df0ba017',X'0000000B789CF38EE0F2E1F2E5F2E3F2E702000DB2020C');
           38  +INSERT INTO "blob" VALUES(28,16,341,'9df70ff13b57e70ea2de63f655eb72df7b83121a',X'000000EF789C0DCD3D4E03311040E1DEA7F001089AF17AFE360D52525220040D8A846C8FAD5004A4CD8AE5F86CF99AEFD173B8CDA7D8FD6BBDDCD7EBD2FBF1725FFA77B9F5BDB79F708E09100E980E886F4833F28C80BF4FFE90C69C444B86342113C2E0D273114165312E5CBD8B36F1DA306E71B71ED7BF35BCC4AA9A2A8A37E232B04332A9430B493347028284D43897F01AD5D4B077A326640C5981477611A324FB7A84F7E8CB357C441BD51C9407541D0A669433FBA05C26622E14A6069F1B1FFF019008411F');
           39  +INSERT INTO "blob" VALUES(29,17,11,'5ebb3c9ad50740a7382902657b84a6105c32fc7b',X'0000000B789C73E372E7F2E0F28CE4F2E202000C0101F4');
           40  +INSERT INTO "blob" VALUES(30,17,322,'94bd3e6b45cc15e86cc76ecfd2207fddbb675b15',X'00000142789C3DCE314E44310C04D0FE9F221758643B711C6F0BA246081A441327B696825D69F9121C9F8010F5F8CDF836F97CDB5F3FF6CFCBE1EAE7FEEEDB5D224038201D109F908F588F85B6FB7439FBCDFEB5276FB59AB5C693B19561BDA88CB0DA8B1B35AC2B4165C245F6D3D5FF10286B56869625A4BAC0E8142D8248A87B9E01D601E507FDBFF22BD9CDF2D03E19A44097DC48812A8BB5D22B028F4C31C4B687A4330422301BCB5AF04ED36B8ECAAB436885D63212F6ED31E56985D1EB2A96A22DFA8800CC6C485A88FAF69CE6F5B4BD24556990111A87AECBEEEAB50C5F8EAB308FED1BE1CB587B');
           41  +INSERT INTO "blob" VALUES(31,18,11,'85286cb3bc6d9e6f2f586eb5532f6065678f75b9',X'0000000B789C73E372E7F2E0F2E4F28AE402000BA301F4');
           42  +INSERT INTO "blob" VALUES(32,18,360,'333c4d56d218735e20565944b50202f24827e3b8',X'00000168789C25CF3D4E43311004E0DEA7F00582BCDE1FAFD382A8118206D1ECDA5E250D91C213E1F83C9476349F34F398D73C6F9FDFDBED929E722D500E500F006F20472A47C4F49CE3FCB31EB6DF2D6B94EE83D9A42CD76145028AA1736B66612ADD11A4F36E2E5F77328D8722097509A40AC35943C965715F9D11669F5469EE643B5DD71DADB927D067AB32A161804E0FB26E259481A6192E1C46FFE876B94FE3AA321C7DC8EC4BA206AB2C67C61A5284A56934F69E5E324EF6B1EA223271ECB5F0DE031AAC604630558294A9A4D76C4B54753F568B8B16F4D5102946AD4EA1BAD27B9ED753FAC80338F6EBB37A31F568AD888A556519A32B78FA037C076278');
           43  +CREATE TABLE delta(
           44  +  rid INTEGER PRIMARY KEY,
           45  +  srcid INTEGER NOT NULL REFERENCES blob
           46  +);
           47  +INSERT INTO "delta" VALUES(5,8);
           48  +INSERT INTO "delta" VALUES(11,32);
           49  +INSERT INTO "delta" VALUES(15,17);
           50  +INSERT INTO "delta" VALUES(17,21);
           51  +INSERT INTO "delta" VALUES(28,30);
           52  +CREATE TABLE rcvfrom(
           53  +  rcvid INTEGER PRIMARY KEY,
           54  +  uid INTEGER REFERENCES user,
           55  +  mtime DATETIME,
           56  +  nonce TEXT UNIQUE,
           57  +  ipaddr TEXT
           58  +);
           59  +INSERT INTO "rcvfrom" VALUES(1,1,2455542.12469579,NULL,NULL);
           60  +INSERT INTO "rcvfrom" VALUES(2,1,2455542.12638891,NULL,NULL);
           61  +INSERT INTO "rcvfrom" VALUES(3,1,2455542.12705387,NULL,'127.0.0.1');
           62  +INSERT INTO "rcvfrom" VALUES(4,1,2455542.12796271,NULL,NULL);
           63  +INSERT INTO "rcvfrom" VALUES(5,1,2455542.12818105,NULL,'127.0.0.1');
           64  +INSERT INTO "rcvfrom" VALUES(6,1,2455542.12873891,NULL,NULL);
           65  +INSERT INTO "rcvfrom" VALUES(7,1,2455542.1294266,NULL,NULL);
           66  +INSERT INTO "rcvfrom" VALUES(8,1,2455542.12999842,NULL,NULL);
           67  +INSERT INTO "rcvfrom" VALUES(9,1,2455542.13015219,NULL,NULL);
           68  +INSERT INTO "rcvfrom" VALUES(10,1,2455542.13073123,NULL,'127.0.0.1');
           69  +INSERT INTO "rcvfrom" VALUES(11,1,2455542.13090438,NULL,'127.0.0.1');
           70  +INSERT INTO "rcvfrom" VALUES(12,1,2455542.1333612,NULL,NULL);
           71  +INSERT INTO "rcvfrom" VALUES(13,1,2455542.13353873,NULL,'127.0.0.1');
           72  +INSERT INTO "rcvfrom" VALUES(14,1,2455542.13467894,NULL,NULL);
           73  +INSERT INTO "rcvfrom" VALUES(15,1,2455542.13571911,NULL,NULL);
           74  +INSERT INTO "rcvfrom" VALUES(16,1,2455542.13623403,NULL,NULL);
           75  +INSERT INTO "rcvfrom" VALUES(17,1,2455542.13660848,NULL,NULL);
           76  +INSERT INTO "rcvfrom" VALUES(18,1,2455542.19483546,NULL,NULL);
           77  +CREATE TABLE user(
           78  +  uid INTEGER PRIMARY KEY,
           79  +  login TEXT,
           80  +  pw TEXT,
           81  +  cap TEXT,
           82  +  cookie TEXT,
           83  +  ipaddr TEXT,
           84  +  cexpire DATETIME,
           85  +  info TEXT,
           86  +  photo BLOB
           87  +);
           88  +INSERT INTO "user" VALUES(1,'drh','efbbd0','s',NULL,NULL,NULL,'',NULL);
           89  +INSERT INTO "user" VALUES(2,'anonymous','42568D1800352E28','ghmncz',NULL,NULL,NULL,'Anon',NULL);
           90  +INSERT INTO "user" VALUES(3,'nobody','','jor',NULL,NULL,NULL,'Nobody',NULL);
           91  +INSERT INTO "user" VALUES(4,'developer','','dei',NULL,NULL,NULL,'Dev',NULL);
           92  +INSERT INTO "user" VALUES(5,'reader','','kptw',NULL,NULL,NULL,'Reader',NULL);
           93  +CREATE TABLE config(
           94  +  name TEXT PRIMARY KEY NOT NULL,
           95  +  value CLOB,
           96  +  CHECK( typeof(name)='text' AND length(name)>=1 )
           97  +);
           98  +INSERT INTO "config" VALUES('server-code','48d0adf1afd58dae13c5b70ff7aff208fb8b9f53');
           99  +INSERT INTO "config" VALUES('project-code','8201587e22864f2fe6b8eb623626de6f9f747058');
          100  +INSERT INTO "config" VALUES('autosync','1');
          101  +INSERT INTO "config" VALUES('localauth','0');
          102  +INSERT INTO "config" VALUES('project-name','Test Case 1');
          103  +INSERT INTO "config" VALUES('project-description','Fossil repository for testing');
          104  +INSERT INTO "config" VALUES('index-page','/timeline');
          105  +INSERT INTO "config" VALUES('content-schema','1');
          106  +INSERT INTO "config" VALUES('aux-schema','2010-11-24');
          107  +CREATE TABLE shun(uuid UNIQUE);
          108  +CREATE TABLE private(rid INTEGER PRIMARY KEY);
          109  +CREATE TABLE reportfmt(
          110  +   rn integer primary key,
          111  +   owner text,
          112  +   title text,
          113  +   cols text,
          114  +   sqlcode text
          115  +);
          116  +INSERT INTO "reportfmt" VALUES(1,NULL,'All Tickets','#ffffff Key:
          117  +#f2dcdc Active
          118  +#e8e8e8 Review
          119  +#cfe8bd Fixed
          120  +#bde5d6 Tested
          121  +#cacae5 Deferred
          122  +#c8c8c8 Closed','SELECT
          123  +  CASE WHEN status IN (''Open'',''Verified'') THEN ''#f2dcdc''
          124  +       WHEN status=''Review'' THEN ''#e8e8e8''
          125  +       WHEN status=''Fixed'' THEN ''#cfe8bd''
          126  +       WHEN status=''Tested'' THEN ''#bde5d6''
          127  +       WHEN status=''Deferred'' THEN ''#cacae5''
          128  +       ELSE ''#c8c8c8'' END AS ''bgcolor'',
          129  +  substr(tkt_uuid,1,10) AS ''#'',
          130  +  datetime(tkt_mtime) AS ''mtime'',
          131  +  type,
          132  +  status,
          133  +  subsystem,
          134  +  title
          135  +FROM ticket');
          136  +CREATE TABLE concealed(
          137  +  hash TEXT PRIMARY KEY,
          138  +  content TEXT
          139  +);
          140  +CREATE TABLE filename(
          141  +  fnid INTEGER PRIMARY KEY,
          142  +  name TEXT UNIQUE
          143  +);
          144  +INSERT INTO "filename" VALUES(1,'four.txt');
          145  +INSERT INTO "filename" VALUES(2,'one.txt');
          146  +INSERT INTO "filename" VALUES(3,'three.txt');
          147  +INSERT INTO "filename" VALUES(4,'two.txt');
          148  +INSERT INTO "filename" VALUES(5,'five.txt');
          149  +INSERT INTO "filename" VALUES(6,'six.txt');
          150  +INSERT INTO "filename" VALUES(7,'two-rename.txt');
          151  +CREATE TABLE mlink(
          152  +  mid INTEGER REFERENCES blob,
          153  +  pid INTEGER REFERENCES blob,
          154  +  fid INTEGER REFERENCES blob,
          155  +  fnid INTEGER REFERENCES filename,
          156  +  pfnid INTEGER REFERENCES filename
          157  +);
          158  +INSERT INTO "mlink" VALUES(8,0,7,1,0);
          159  +INSERT INTO "mlink" VALUES(5,0,2,2,0);
          160  +INSERT INTO "mlink" VALUES(5,0,3,3,0);
          161  +INSERT INTO "mlink" VALUES(5,0,4,4,0);
          162  +INSERT INTO "mlink" VALUES(11,0,10,5,0);
          163  +INSERT INTO "mlink" VALUES(13,0,12,6,0);
          164  +INSERT INTO "mlink" VALUES(21,3,20,3,0);
          165  +INSERT INTO "mlink" VALUES(17,4,16,4,0);
          166  +INSERT INTO "mlink" VALUES(15,2,14,2,0);
          167  +INSERT INTO "mlink" VALUES(24,0,23,1,0);
          168  +INSERT INTO "mlink" VALUES(24,2,0,2,0);
          169  +INSERT INTO "mlink" VALUES(26,2,25,2,0);
          170  +INSERT INTO "mlink" VALUES(30,4,29,7,0);
          171  +INSERT INTO "mlink" VALUES(28,3,27,3,0);
          172  +INSERT INTO "mlink" VALUES(28,4,4,7,4);
          173  +INSERT INTO "mlink" VALUES(28,4,0,4,0);
          174  +INSERT INTO "mlink" VALUES(32,4,31,4,0);
          175  +CREATE TABLE plink(
          176  +  pid INTEGER REFERENCES blob,
          177  +  cid INTEGER REFERENCES blob,
          178  +  isprim BOOLEAN,
          179  +  mtime DATETIME,
          180  +  UNIQUE(pid, cid)
          181  +);
          182  +INSERT INTO "plink" VALUES(5,8,1,2455542.12795139);
          183  +INSERT INTO "plink" VALUES(1,5,1,2455542.12638889);
          184  +INSERT INTO "plink" VALUES(5,11,1,2455542.12873843);
          185  +INSERT INTO "plink" VALUES(5,13,1,2455542.1294213);
          186  +INSERT INTO "plink" VALUES(17,21,1,2455542.13335648);
          187  +INSERT INTO "plink" VALUES(15,17,1,2455542.13015046);
          188  +INSERT INTO "plink" VALUES(5,15,1,2455542.12998843);
          189  +INSERT INTO "plink" VALUES(5,24,1,2455542.13467593);
          190  +INSERT INTO "plink" VALUES(5,26,1,2455542.13571759);
          191  +INSERT INTO "plink" VALUES(28,30,1,2455542.13659722);
          192  +INSERT INTO "plink" VALUES(26,28,1,2455542.13622685);
          193  +INSERT INTO "plink" VALUES(11,32,1,2455542.19482639);
          194  +CREATE TABLE event(
          195  +  type TEXT,
          196  +  mtime DATETIME,
          197  +  objid INTEGER PRIMARY KEY,
          198  +  tagid INTEGER,
          199  +  uid INTEGER REFERENCES user,
          200  +  bgcolor TEXT,
          201  +  euser TEXT,
          202  +  user TEXT,
          203  +  ecomment TEXT,
          204  +  comment TEXT,
          205  +  brief TEXT,
          206  +  omtime DATETIME
          207  +);
          208  +INSERT INTO "event" VALUES('ci',2455542.1246875,1,NULL,NULL,NULL,NULL,'drh',NULL,'initial empty check-in',NULL,NULL);
          209  +INSERT INTO "event" VALUES('ci',2455542.12638889,5,NULL,NULL,NULL,NULL,'drh',NULL,'add three initial files',NULL,NULL);
          210  +INSERT INTO "event" VALUES('ci',2455542.12795139,8,NULL,NULL,NULL,NULL,'drh',NULL,'add four.txt',NULL,2455542.12795139);
          211  +INSERT INTO "event" VALUES('ci',2455542.12873843,11,NULL,NULL,NULL,NULL,'drh',NULL,'add five.txt',NULL,NULL);
          212  +INSERT INTO "event" VALUES('ci',2455542.1294213,13,NULL,NULL,NULL,NULL,'drh',NULL,'add six.txt',NULL,NULL);
          213  +INSERT INTO "event" VALUES('ci',2455542.12998843,15,NULL,NULL,NULL,NULL,'drh',NULL,'changes to one.txt',NULL,NULL);
          214  +INSERT INTO "event" VALUES('ci',2455542.13015046,17,NULL,NULL,NULL,NULL,'drh',NULL,'changes to two.txt',NULL,NULL);
          215  +INSERT INTO "event" VALUES('ci',2455542.13335648,21,NULL,NULL,NULL,NULL,'drh',NULL,'changes to three.txt',NULL,2455542.13335648);
          216  +INSERT INTO "event" VALUES('ci',2455542.13467593,24,NULL,NULL,NULL,NULL,'drh',NULL,'drop one; add new four',NULL,NULL);
          217  +INSERT INTO "event" VALUES('ci',2455542.13571759,26,NULL,NULL,NULL,NULL,'drh',NULL,'change one',NULL,NULL);
          218  +INSERT INTO "event" VALUES('ci',2455542.13622685,28,NULL,NULL,NULL,NULL,'drh',NULL,'edit three; rename two',NULL,NULL);
          219  +INSERT INTO "event" VALUES('ci',2455542.13659722,30,NULL,NULL,NULL,NULL,'drh',NULL,'edit two-rename',NULL,NULL);
          220  +INSERT INTO "event" VALUES('ci',2455542.19482639,32,NULL,NULL,NULL,NULL,'drh',NULL,'edit two',NULL,NULL);
          221  +CREATE TABLE phantom(
          222  +  rid INTEGER PRIMARY KEY
          223  +);
          224  +CREATE TABLE orphan(
          225  +  rid INTEGER PRIMARY KEY,
          226  +  baseline INTEGER
          227  +);
          228  +CREATE TABLE unclustered(
          229  +  rid INTEGER PRIMARY KEY
          230  +);
          231  +INSERT INTO "unclustered" VALUES(1);
          232  +INSERT INTO "unclustered" VALUES(2);
          233  +INSERT INTO "unclustered" VALUES(3);
          234  +INSERT INTO "unclustered" VALUES(4);
          235  +INSERT INTO "unclustered" VALUES(5);
          236  +INSERT INTO "unclustered" VALUES(6);
          237  +INSERT INTO "unclustered" VALUES(7);
          238  +INSERT INTO "unclustered" VALUES(8);
          239  +INSERT INTO "unclustered" VALUES(9);
          240  +INSERT INTO "unclustered" VALUES(10);
          241  +INSERT INTO "unclustered" VALUES(11);
          242  +INSERT INTO "unclustered" VALUES(12);
          243  +INSERT INTO "unclustered" VALUES(13);
          244  +INSERT INTO "unclustered" VALUES(14);
          245  +INSERT INTO "unclustered" VALUES(15);
          246  +INSERT INTO "unclustered" VALUES(16);
          247  +INSERT INTO "unclustered" VALUES(17);
          248  +INSERT INTO "unclustered" VALUES(18);
          249  +INSERT INTO "unclustered" VALUES(19);
          250  +INSERT INTO "unclustered" VALUES(20);
          251  +INSERT INTO "unclustered" VALUES(21);
          252  +INSERT INTO "unclustered" VALUES(22);
          253  +INSERT INTO "unclustered" VALUES(23);
          254  +INSERT INTO "unclustered" VALUES(24);
          255  +INSERT INTO "unclustered" VALUES(25);
          256  +INSERT INTO "unclustered" VALUES(26);
          257  +INSERT INTO "unclustered" VALUES(27);
          258  +INSERT INTO "unclustered" VALUES(28);
          259  +INSERT INTO "unclustered" VALUES(29);
          260  +INSERT INTO "unclustered" VALUES(30);
          261  +INSERT INTO "unclustered" VALUES(31);
          262  +INSERT INTO "unclustered" VALUES(32);
          263  +CREATE TABLE unsent(
          264  +  rid INTEGER PRIMARY KEY
          265  +);
          266  +INSERT INTO "unsent" VALUES(31);
          267  +INSERT INTO "unsent" VALUES(32);
          268  +CREATE TABLE tag(
          269  +  tagid INTEGER PRIMARY KEY,
          270  +  tagname TEXT UNIQUE
          271  +);
          272  +INSERT INTO "tag" VALUES(1,'bgcolor');
          273  +INSERT INTO "tag" VALUES(2,'comment');
          274  +INSERT INTO "tag" VALUES(3,'user');
          275  +INSERT INTO "tag" VALUES(4,'date');
          276  +INSERT INTO "tag" VALUES(5,'hidden');
          277  +INSERT INTO "tag" VALUES(6,'private');
          278  +INSERT INTO "tag" VALUES(7,'cluster');
          279  +INSERT INTO "tag" VALUES(8,'branch');
          280  +INSERT INTO "tag" VALUES(9,'closed');
          281  +INSERT INTO "tag" VALUES(10,'sym-trunk');
          282  +INSERT INTO "tag" VALUES(11,'sym-baseline');
          283  +INSERT INTO "tag" VALUES(12,'sym-br1');
          284  +INSERT INTO "tag" VALUES(13,'sym-br2');
          285  +INSERT INTO "tag" VALUES(14,'sym-br3');
          286  +INSERT INTO "tag" VALUES(15,'sym-chng1');
          287  +INSERT INTO "tag" VALUES(16,'sym-chng2');
          288  +INSERT INTO "tag" VALUES(17,'sym-chng3');
          289  +INSERT INTO "tag" VALUES(18,'sym-br4');
          290  +INSERT INTO "tag" VALUES(19,'sym-br5');
          291  +CREATE TABLE tagxref(
          292  +  tagid INTEGER REFERENCES tag,
          293  +  tagtype INTEGER,
          294  +  srcid INTEGER REFERENCES blob,
          295  +  origid INTEGER REFERENCES blob,
          296  +  value TEXT,
          297  +  mtime TIMESTAMP,
          298  +  rid INTEGER REFERENCE blob,
          299  +  UNIQUE(rid, tagid)
          300  +);
          301  +INSERT INTO "tagxref" VALUES(8,2,1,1,'trunk',2455542.1246875,1);
          302  +INSERT INTO "tagxref" VALUES(10,2,1,1,NULL,2455542.1246875,1);
          303  +INSERT INTO "tagxref" VALUES(4,1,6,5,'2010-12-11 15:02:00',2455542.12704861,5);
          304  +INSERT INTO "tagxref" VALUES(11,1,6,5,NULL,2455542.12704861,5);
          305  +INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,5);
          306  +INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,5);
          307  +INSERT INTO "tagxref" VALUES(8,2,9,8,'br1',2455542.1281713,8);
          308  +INSERT INTO "tagxref" VALUES(12,2,9,8,NULL,2455542.1281713,8);
          309  +INSERT INTO "tagxref" VALUES(4,1,9,8,'2010-12-11 15:04:15',2455542.1281713,8);
          310  +INSERT INTO "tagxref" VALUES(10,0,9,8,NULL,2455542.1281713,8);
          311  +INSERT INTO "tagxref" VALUES(8,2,11,11,'br2',2455542.12873843,11);
          312  +INSERT INTO "tagxref" VALUES(13,2,11,11,NULL,2455542.12873843,11);
          313  +INSERT INTO "tagxref" VALUES(11,0,11,11,NULL,2455542.12873843,11);
          314  +INSERT INTO "tagxref" VALUES(10,0,11,11,NULL,2455542.12873843,11);
          315  +INSERT INTO "tagxref" VALUES(8,2,13,13,'br3',2455542.1294213,13);
          316  +INSERT INTO "tagxref" VALUES(14,2,13,13,NULL,2455542.1294213,13);
          317  +INSERT INTO "tagxref" VALUES(11,0,13,13,NULL,2455542.1294213,13);
          318  +INSERT INTO "tagxref" VALUES(10,0,13,13,NULL,2455542.1294213,13);
          319  +INSERT INTO "tagxref" VALUES(4,1,18,15,'2010-12-11 15:07:11',2455542.13072917,15);
          320  +INSERT INTO "tagxref" VALUES(15,1,18,15,NULL,2455542.13072917,15);
          321  +INSERT INTO "tagxref" VALUES(4,1,19,17,'2010-12-11 15:07:25',2455542.13090278,17);
          322  +INSERT INTO "tagxref" VALUES(16,1,19,17,NULL,2455542.13090278,17);
          323  +INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,15);
          324  +INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,17);
          325  +INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,21);
          326  +INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,15);
          327  +INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,17);
          328  +INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,21);
          329  +INSERT INTO "tagxref" VALUES(4,1,22,21,'2010-12-11 15:12:02',2455542.13353009,21);
          330  +INSERT INTO "tagxref" VALUES(17,1,22,21,NULL,2455542.13353009,21);
          331  +INSERT INTO "tagxref" VALUES(8,2,24,24,'br4',2455542.13467593,24);
          332  +INSERT INTO "tagxref" VALUES(18,2,24,24,NULL,2455542.13467593,24);
          333  +INSERT INTO "tagxref" VALUES(11,0,24,24,NULL,2455542.13467593,24);
          334  +INSERT INTO "tagxref" VALUES(10,0,24,24,NULL,2455542.13467593,24);
          335  +INSERT INTO "tagxref" VALUES(8,2,26,26,'br5',2455542.13571759,26);
          336  +INSERT INTO "tagxref" VALUES(19,2,26,26,NULL,2455542.13571759,26);
          337  +INSERT INTO "tagxref" VALUES(11,0,26,26,NULL,2455542.13571759,26);
          338  +INSERT INTO "tagxref" VALUES(10,0,26,26,NULL,2455542.13571759,26);
          339  +INSERT INTO "tagxref" VALUES(8,2,0,26,'br5',2455542.13571759,28);
          340  +INSERT INTO "tagxref" VALUES(8,2,0,26,'br5',2455542.13571759,30);
          341  +INSERT INTO "tagxref" VALUES(19,2,0,26,NULL,2455542.13571759,28);
          342  +INSERT INTO "tagxref" VALUES(19,2,0,26,NULL,2455542.13571759,30);
          343  +INSERT INTO "tagxref" VALUES(8,2,0,11,'br2',2455542.12873843,32);
          344  +INSERT INTO "tagxref" VALUES(13,2,0,11,NULL,2455542.12873843,32);
          345  +CREATE TABLE backlink(
          346  +  target TEXT,
          347  +  srctype INT,
          348  +  srcid INT,
          349  +  mtime TIMESTAMP,
          350  +  UNIQUE(target, srctype, srcid)
          351  +);
          352  +CREATE TABLE attachment(
          353  +  attachid INTEGER PRIMARY KEY,
          354  +  isLatest BOOLEAN DEFAULT 0,
          355  +  mtime TIMESTAMP,
          356  +  src TEXT,
          357  +  target TEXT,
          358  +  filename TEXT,
          359  +  comment TEXT,
          360  +  user TEXT
          361  +);
          362  +CREATE TABLE ticket(
          363  +  -- Do not change any column that begins with tkt_
          364  +  tkt_id INTEGER PRIMARY KEY,
          365  +  tkt_uuid TEXT UNIQUE,
          366  +  tkt_mtime DATE,
          367  +  -- Add as many field as required below this line
          368  +  type TEXT,
          369  +  status TEXT,
          370  +  subsystem TEXT,
          371  +  priority TEXT,
          372  +  severity TEXT,
          373  +  foundin TEXT,
          374  +  private_contact TEXT,
          375  +  resolution TEXT,
          376  +  title TEXT,
          377  +  comment TEXT
          378  +);
          379  +CREATE INDEX delta_i1 ON delta(srcid);
          380  +CREATE INDEX mlink_i1 ON mlink(mid);
          381  +CREATE INDEX mlink_i2 ON mlink(fnid);
          382  +CREATE INDEX mlink_i3 ON mlink(fid);
          383  +CREATE INDEX mlink_i4 ON mlink(pid);
          384  +CREATE INDEX plink_i2 ON plink(cid,pid);
          385  +CREATE INDEX event_i1 ON event(mtime);
          386  +CREATE INDEX orphan_baseline ON orphan(baseline);
          387  +CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
          388  +CREATE INDEX backlink_src ON backlink(srcid, srctype);
          389  +CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
          390  +CREATE INDEX attachment_idx2 ON attachment(src);
          391  +COMMIT;

Added test/merge_renames.test.

            1  +#
            2  +# Tests for merging with renames
            3  +# 
            4  +#
            5  +
            6  +catch {exec $::fossilexe info} res
            7  +puts res=$res
            8  +if {![regexp {use --repository} $res]} {
            9  +  puts stderr "Cannot run this test within an open checkout"
           10  +  return
           11  +}
           12  +
           13  +
           14  +# Fossil will write data on $HOME, running 'fossil new' here.
           15  +# We need not to clutter the $HOME of the test caller.
           16  +set env(HOME) [pwd]
           17  +
           18  +
           19  +######################################
           20  +#  Test 1                            #
           21  +#  Reported: Ticket [554f44ee74e3d]  #
           22  +######################################
           23  +
           24  +fossil new rep.fossil
           25  +fossil open rep.fossil
           26  +
           27  +write_file f1 "line"
           28  +fossil add f1
           29  +fossil commit -m "c1"
           30  +fossil tag add pivot current
           31  +
           32  +write_file f1 "line2"
           33  +fossil commit -m "c2"
           34  +
           35  +write_file f1 "line3"
           36  +fossil commit -m "c3"
           37  +
           38  +write_file f1 "line4"
           39  +fossil commit -m "c4"
           40  +
           41  +write_file f1 "line5"
           42  +fossil commit -m "c4"
           43  +
           44  +write_file f1 "line6"
           45  +fossil commit -m "c4"
           46  +
           47  +fossil update pivot
           48  +fossil mv f1 f2
           49  +exec mv f1 f2
           50  +fossil commit -b rename -m "c5"
           51  +
           52  +fossil merge trunk
           53  +fossil commit -m "trunk merged"
           54  +
           55  +fossil update pivot
           56  +write_file f3 "someline"
           57  +fossil add f3
           58  +fossil commit -b branch2 -m "newbranch"
           59  +
           60  +fossil merge trunk
           61  +puts $RESULT
           62  +
           63  +set deletes 0
           64  +foreach {status filename} $RESULT {
           65  +    if {$status=="DELETE"} {
           66  +        set deletes [expr $deletes + 1]
           67  +    }
           68  +}
           69  +
           70  +if {$deletes!=0} {
           71  +    # failed
           72  +    protOut "Error, the merge should not delete any file"
           73  +    test merge_renames-1 0
           74  +} else {
           75  +    test merge_renames-1 1
           76  +}
           77  +
           78  +fossil close -f
           79  +exec rm rep.fossil
           80  +
           81  +######################################
           82  +#  Test 2                            #
           83  +#  Reported: Ticket [74413366fe5067] #
           84  +######################################
           85  +
           86  +fossil new rep.fossil
           87  +fossil open rep.fossil
           88  +
           89  +write_file f1 "line"
           90  +fossil add f1
           91  +fossil commit -m "base file"
           92  +fossil tag add pivot current
           93  +
           94  +write_file f2 "line2"
           95  +fossil add f2
           96  +fossil commit -m "newfile"
           97  +
           98  +fossil mv f2 f2new
           99  +exec mv f2 f2new
          100  +fossil commit -m "rename"
          101  +
          102  +fossil update pivot
          103  +write_file f1 "line3"
          104  +fossil commit -b branch -m "change"
          105  +
          106  +fossil merge trunk
          107  +fossil commit -m "trunk merged"
          108  +
          109  +fossil update trunk
          110  +
          111  +fossil merge branch
          112  +puts $RESULT
          113  +
          114  +# Not a nice way to check, but I don't know more tcl now
          115  +set deletes 0
          116  +foreach {status filename} $RESULT {
          117  +    if {$status=="DELETE"} {
          118  +        set deletes [expr $deletes + 1]
          119  +    }
          120  +}
          121  +
          122  +if {$deletes!=0} {
          123  +    # failed
          124  +    protOut "Error, the merge should not delete any file"
          125  +    test merge_renames-2 0
          126  +} else {
          127  +    test merge_renames-2 1
          128  +}
          129  +
          130  +fossil close -f
          131  +exec rm rep.fossil
          132  +
          133  +######################################
          134  +#  Test 3                            #
          135  +#  Reported: Ticket [30b28cf351]     #
          136  +######################################
          137  +
          138  +fossil new rep.fossil
          139  +fossil open rep.fossil
          140  +
          141  +write_file f1 "line"
          142  +fossil add f1
          143  +fossil commit -m "base file"
          144  +fossil tag add pivot current
          145  +
          146  +write_file f2 "line2"
          147  +fossil add f2
          148  +fossil commit -m "newfile"
          149  +
          150  +fossil mv f2 f2new
          151  +exec mv f2 f2new
          152  +fossil commit -m "rename"
          153  +
          154  +fossil update pivot
          155  +write_file f1 "line3"
          156  +fossil commit -b branch -m "change"
          157  +
          158  +fossil merge trunk
          159  +fossil commit -m "trunk merged"
          160  +
          161  +fossil update trunk
          162  +
          163  +fossil merge branch
          164  +puts $RESULT
          165  +
          166  +# Not a nice way to check, but I don't know more tcl now
          167  +set deletes 0
          168  +foreach {status filename} $RESULT {
          169  +    if {$status=="DELETE"} {
          170  +        set deletes [expr $deletes + 1]
          171  +    }
          172  +}
          173  +
          174  +if {$deletes!=0} {
          175  +    # failed
          176  +    protOut "Error, the merge should not delete any file"
          177  +    test merge_renames-2 0
          178  +} else {
          179  +    test merge_renames-2 1
          180  +}
          181  +
          182  +fossil close -f
          183  +exec rm rep.fossil
          184  +
          185  +######################################
          186  +#  Test 4                            #
          187  +#  Reported: Ticket [67176c3aa4]     #
          188  +######################################
          189  +
          190  +# TO BE WRITTEN.
          191  +
          192  +
          193  +# Tests for troubles not specifically linked with renames but that I'd like to
          194  +# write:
          195  +#  [c26c63eb1b] - 'merge --backout' does not handle conflicts properly
          196  +#  [953031915f] - Lack of warning when overwriting extra files 
          197  +#  [4df5f38f1e] - Troubles merging a file delete with a file change 

Added test/release-checklist.wiki.

            1  +<title>Release Checklist</title>
            2  +
            3  +This file describes the testing procedures for Fossil prior to an
            4  +official release.
            5  +
            6  +<ol>
            7  +<li><p>
            8  +From a private directory (not the source tree) run 
            9  +"<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the
           10  +name of the executable under test and $SRC is the source tree.
           11  +Verify that there are no errors.
           12  +
           13  +<li><p>
           14  +Click on each of the links in in the
           15  +[./graph-test-1.wiki] document and verify that all graphs are
           16  +rendered correctly.
           17  +
           18  +<li><p>
           19  +Click on each of the links in in the
           20  +[./diff-test-1.wiki] document and verify that all diffs are
           21  +rendered correctly.
           22  +
           23  +<li><p>
           24  +Verify correct name-change tracking behavior (no net changes) for:
           25  +<blockquote><b>
           26  +fossil test-name-changes --debug  b120bc8b262ac 374920b20944b
           27  +</b></blockquote>
           28  +
           29  +<li><p>
           30  +Compile for all of the following platforms:
           31  +<ol type="a">
           32  +<li> Linux x86
           33  +<li> Linux x86_64
           34  +<li> Mac x86
           35  +<li> Mac x86_64
           36  +<li> Windows (mingw)
           37  +<li> Windows (vc++)
           38  +<li> OpenBSD
           39  +</ol>
           40  +
           41  +<li><p>
           42  +Run at least one occurrence of the following commands on every
           43  +platform:
           44  +<ol type="a">
           45  +<li> <b>fossil rebuild</b>
           46  +<li> <b>fossil sync</b>
           47  +<li> <b>fossil test-integrity</b>
           48  +</ol>
           49  +
           50  +<li><p>
           51  +Run the following commands on Linux and verify no major memory leaks
           52  +and no run-time errors or warnings (except for the well-known jump on an
           53  +uninitialized value that occurs within zlib).
           54  +<ol type="a">
           55  +<li> <b>valgrind fossil rebuild</b>
           56  +<li> <b>valgrind fossil sync</b>
           57  +</ol>
           58  +
           59  +<li><p>
           60  +Inspect all code changes since the previous release, paying particular
           61  +attention to the following details:
           62  +<ol type="a">
           63  +<li> Can a malicious HTTP request cause a buffer overrun.
           64  +<li> Can a malicious HTTP request expose privileged information to
           65  +     unauthorized users.
           66  +</ol>
           67  +
           68  +
           69  +<li><p>
           70  +Use the release candidate version of fossil in production on the
           71  +[http://www.fossil-scm.org/] website for at least 48 hours (without
           72  +incident) prior to making the release official.
           73  +
           74  +<li><p>
           75  +Verify that the [../www/changes.wiki | Change Log] is correct and
           76  +up-to-date.
           77  +</ol>
           78  +
           79  +<hr>
           80  +
           81  +Upon successful completion of all tests above, tag the release candidate
           82  +with the "release" tag and set its background color to "#d0c0ff".  Update
           83  +the www/changes.wiki file to show the date of the release.

Added test/revert.test.

            1  +#
            2  +# Tests for 'fossil revert'
            3  +# 
            4  +#
            5  +
            6  +catch {exec $::fossilexe info} res
            7  +puts res=$res
            8  +if {![regexp {use --repository} $res]} {
            9  +  puts stderr "Cannot run this test within an open checkout"
           10  +  return
           11  +}
           12  +
           13  +# Fossil will write data on $HOME, running 'fossil new' here.
           14  +# We need not to clutter the $HOME of the test caller.
           15  +#
           16  +set env(HOME) [pwd]
           17  +
           18  +
           19  +# Normalize file status lists (like those returned by 'fossil changes')
           20  +# so they can be compared using simple string comparison
           21  +#
           22  +proc normalize-status-list {list} {
           23  +  set normalized [list]
           24  +  set matches [regexp -all -inline -line {^\s*([A-Z]+)\s+(.*)$} $list]
           25  +  foreach {_ status file} $matches {
           26  +    lappend normalized [list $status [string trim $file]]
           27  +  }
           28  +  set normalized [lsort -index 1 $normalized]
           29  +  return $normalized
           30  +}
           31  +
           32  +# Test 'fossil revert' against expected results from 'fossil changes' and
           33  +# 'fossil addremove --test', as well as by verifying the existence of files
           34  +# on the file system. 'fossil undo' is called after each test
           35  +#
           36  +proc revert-test {testid args} {
           37  +  global RESULT
           38  +  set passed 1
           39  +  
           40  +  if {[llength $args] % 2} {
           41  +    set revertArgs [lindex $args 0]
           42  +    set args [lrange $args 1 end]
           43  +  } else {
           44  +    set revertArgs {}
           45  +  }
           46  +  set args [dict merge {
           47  +    -changes {} -addremove {} -exists {} -notexists {}
           48  +  } $args]
           49  +  
           50  +  fossil revert {*}$revertArgs
           51  +  
           52  +  set statusListTests [list -changes changes -addremove {addremove --test}]
           53  +  foreach {key fossilArgs} $statusListTests {
           54  +    set expected [normalize-status-list [dict get $args $key]]
           55  +    set result [normalize-status-list [fossil {*}$fossilArgs]]
           56  +    if {$result ne $expected} {
           57  +      set passed 0
           58  +      protOut "  Expected:\n    [join $expected "\n    "]"
           59  +      protOut "  Got:\n    [join $result "\n    "]"
           60  +    }
           61  +  }
           62  +  
           63  +  set fileExistsTests [list -exists 1 does -notexists 0 should]
           64  +  foreach {key expected verb} $fileExistsTests {
           65  +    foreach path [dict get $args $key] {
           66  +      if {[file exists $path] != $expected} {
           67  +        set passed 0
           68  +        protOut "  Failure: File $verb not exist: $path"
           69  +      }
           70  +    }
           71  +  }
           72  +  
           73  +  fossil undo
           74  +  test revert-$testid $passed
           75  +}
           76  +
           77  +# Create the repo
           78  +#
           79  +fossil new rep.fossil
           80  +fossil open rep.fossil
           81  +
           82  +# Prepare first commit
           83  +#
           84  +write_file f1 "f1"
           85  +write_file f2 "f2"
           86  +write_file f3 "f3"
           87  +fossil add f1 f2 f3
           88  +fossil commit -m "c1"
           89  +
           90  +# Make changes to be reverted
           91  +#
           92  +# Add f0
           93  +write_file f0 "f0"
           94  +fossil add f0
           95  +# Remove f1
           96  +exec rm f1
           97  +fossil rm f1
           98  +# Edit f2
           99  +write_file f2 "f2.1"
          100  +# Rename f3 to f3n
          101  +exec mv f3 f3n
          102  +fossil mv f3 f3n
          103  +
          104  +# Test 'fossil revert' with no arguments
          105  +#
          106  +revert-test 1 -addremove {
          107  +  ADDED f0
          108  +} -exists {f0 f1 f2 f3} -notexists f3n
          109  +
          110  +# Test with a single filename argument
          111  +#
          112  +revert-test 2 f0 -changes {
          113  +  DELETED f1
          114  +  EDITED f2
          115  +  RENAMED f3n
          116  +} -addremove {
          117  +  ADDED f0
          118  +} -exists {f0 f2 f3n} -notexists f3
          119  +
          120  +revert-test 3 f1 -changes {
          121  +  ADDED f0
          122  +  EDITED f2
          123  +  RENAMED f3n
          124  +} -exists {f0 f1 f2 f3n} -notexists f3
          125  +
          126  +revert-test 4 f2 -changes {
          127  +  ADDED f0
          128  +  DELETED f1
          129  +  RENAMED f3n
          130  +} -exists {f0 f2 f3n} -notexists {f1 f3}
          131  +
          132  +# Both files involved in a rename are reverted regardless of which filename
          133  +# is used as an argument to 'fossil revert'
          134  +#
          135  +revert-test 5 f3 -changes {
          136  +  ADDED f0
          137  +  DELETED f1
          138  +  EDITED f2
          139  +} -exists {f0 f2 f3} -notexists {f1 f3n}
          140  +
          141  +revert-test 6 f3n -changes {
          142  +  ADDED f0
          143  +  DELETED f1
          144  +  EDITED f2
          145  +} -exists {f0 f2 f3} -notexists {f1 f3n}
          146  +
          147  +# Test with multiple filename arguments
          148  +#
          149  +revert-test 7 {f0 f2 f3n} -changes {
          150  +  DELETED f1
          151  +} -addremove {
          152  +  ADDED f0
          153  +} -exists {f0 f2 f3} -notexists {f1 f3n}

Changes to test/tester.tcl.

     1      1   #
     2      2   # Copyright (c) 2006 D. Richard Hipp
     3      3   #
     4      4   # This program is free software; you can redistribute it and/or
     5         -# modify it under the terms of the GNU General Public
     6         -# License version 2 as published by the Free Software Foundation.
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
     7      7   #
     8      8   # This program is distributed in the hope that it will be useful,
     9         -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    10         -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    11         -# General Public License for more details.
    12         -# 
    13         -# You should have received a copy of the GNU General Public
    14         -# License along with this library; if not, write to the
    15         -# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    16         -# Boston, MA  02111-1307, USA.
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
    17     11   #
    18     12   # Author contact information:
    19     13   #   drh@hwaci.com
    20     14   #   http://www.hwaci.com/drh/
    21     15   #
    22     16   ############################################################################
    23     17   #
................................................................................
    36     30   set i [lsearch $argv -halt]
    37     31   if {$i>=0} {
    38     32     set HALT 1
    39     33     set argv [lreplace $argv $i $i]
    40     34   } else {
    41     35     set HALT 0
    42     36   }
           37  +
           38  +set i [lsearch $argv -prot]
           39  +if {$i>=0} {
           40  +  set PROT 1
           41  +  set argv [lreplace $argv $i $i]
           42  +} else {
           43  +  set PROT 0
           44  +}
    43     45   
    44     46   if {[llength $argv]==0} {
    45     47     foreach f [lsort [glob $testdir/*.test]] {
    46     48       set base [file root [file tail $f]]
    47     49       lappend argv $base
    48     50     }
    49     51   }
           52  +
           53  +# start protocol
           54  +#
           55  +proc protInit {cmd} {
           56  +  if {$::PROT} {
           57  +    set out [open "prot" w]
           58  +    fconfigure $out -translation platform
           59  +    puts $out "starting tests with:$cmd"
           60  +    close $out
           61  +  }
           62  +}
           63  +
           64  +# write protocol
           65  +#
           66  +proc protOut {msg} {
           67  +  puts "$msg"
           68  +  if {$::PROT} {
           69  +    set out [open "prot" a]
           70  +    fconfigure $out -translation platform
           71  +    puts $out "$msg"
           72  +    close $out
           73  +  }
           74  +}
    50     75   
    51     76   # Run the fossil program
    52     77   #
    53     78   proc fossil {args} {
    54     79     global fossilexe
    55     80     set cmd $fossilexe
    56     81     foreach a $args {
    57     82       lappend cmd $a
    58     83     }
    59         -  puts $cmd
           84  +  protOut $cmd
           85  +
    60     86     flush stdout
    61     87     set rc [catch {eval exec $cmd} result]
    62     88     global RESULT CODE
    63     89     set CODE $rc
           90  +  if {$rc} {puts "ERROR: $result"}
    64     91     set RESULT $result
    65     92   }
    66     93   
    67     94   # Read a file into memory. 
    68     95   #
    69     96   proc read_file {filename} {
    70     97     set in [open $filename r]
................................................................................
    94    121     set y [read_file $b]
    95    122     regsub -all { +\n} $y \n y
    96    123     return [expr {$x==$y}]
    97    124   }
    98    125   
    99    126   # Perform a test
   100    127   #
          128  +set test_count 0
   101    129   proc test {name expr} {
   102         -  global bad_test
          130  +  global bad_test test_count
          131  +  incr test_count
   103    132     set r [uplevel 1 [list expr $expr]]
   104    133     if {$r} {
   105         -    puts "test $name OK"
          134  +    protOut "test $name OK"
   106    135     } else {
   107         -    puts "test $name FAILED!"
          136  +    protOut "test $name FAILED!"
   108    137       lappend bad_test $name
   109    138       if {$::HALT} exit
   110    139     }
   111    140   }
   112    141   set bad_test {}
   113    142   
   114    143   # Return a random string N characters long.
................................................................................
   165    194         }
   166    195       }
   167    196       append out \n$line
   168    197     }
   169    198     return [string range $out 1 end]
   170    199   }
   171    200   
          201  +protInit $fossilexe
   172    202   foreach testfile $argv {
   173         -  puts "***** $testfile ******"
          203  +  set dir [file root [file tail $testfile]]
          204  +  file delete -force $dir
          205  +  file mkdir $dir
          206  +  set origwd [pwd]
          207  +  cd $dir
          208  +  protOut "***** $testfile ******"
   174    209     source $testdir/$testfile.test
          210  +  protOut "***** End of $testfile: [llength $bad_test] errors so far ******"
          211  +  cd $origwd
   175    212   }
   176         -puts "[llength $bad_test] errors: $bad_test"
          213  +set nErr [llength $bad_test]
          214  +protOut "***** Final result: $nErr errors out of $test_count tests"
          215  +if {$nErr>0} {
          216  +  protOut "***** Failures: $bad_test"
          217  +}

Added test/th1-tcl.test.

            1  +#
            2  +# Copyright (c) 2011 D. Richard Hipp
            3  +#
            4  +# This program is free software; you can redistribute it and/or
            5  +# modify it under the terms of the Simplified BSD License (also
            6  +# known as the "2-Clause License" or "FreeBSD License".)
            7  +#
            8  +# This program is distributed in the hope that it will be useful,
            9  +# but without any warranty; without even the implied warranty of
           10  +# merchantability or fitness for a particular purpose.
           11  +#
           12  +# Author contact information:
           13  +#   drh@hwaci.com
           14  +#   http://www.hwaci.com/drh/
           15  +#
           16  +############################################################################
           17  +#
           18  +# TH1/Tcl integration
           19  +#
           20  +
           21  +set dir [file dirname [info script]]
           22  +
           23  +###############################################################################
           24  +
           25  +set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.
           26  +
           27  +###############################################################################
           28  +
           29  +fossil test-th-render [file nativename [file join $dir th1-tcl1.txt]]
           30  +
           31  +test th1-tcl-1 {[regexp -- {^\d+
           32  +\d+
           33  +\d+
           34  +via Tcl invoke
           35  +4
           36  +4
           37  +two words
           38  +one_word
           39  +three words now
           40  +\d+
           41  +two words
           42  +4
           43  +\d+
           44  +two words
           45  +4
           46  +\d+
           47  +one_word
           48  +three words now
           49  +$} [string map [list \r\n \n] $RESULT]]}
           50  +
           51  +###############################################################################
           52  +
           53  +fossil test-th-render [file nativename [file join $dir th1-tcl2.txt]]
           54  +
           55  +test th1-tcl-2 {[regexp -- {^\d+
           56  +$} [string map [list \r\n \n] $RESULT]]}
           57  +
           58  +###############################################################################
           59  +
           60  +fossil test-th-render [file nativename [file join $dir th1-tcl3.txt]]
           61  +
           62  +test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           63  +invalid command name &quot;bad_command&quot;</p>}}
           64  +
           65  +###############################################################################
           66  +
           67  +fossil test-th-render [file nativename [file join $dir th1-tcl4.txt]]
           68  +
           69  +test th1-tcl-4 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           70  +divide by zero</p>}}
           71  +
           72  +###############################################################################
           73  +
           74  +fossil test-th-render [file nativename [file join $dir th1-tcl5.txt]]
           75  +
           76  +test th1-tcl-5 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           77  +Tcl command not found: bad_command</p>} || $RESULT eq {<hr><p\
           78  +class="thmainError">ERROR: invalid command name &quot;bad_command&quot;</p>}}
           79  +
           80  +###############################################################################
           81  +
           82  +fossil test-th-render [file nativename [file join $dir th1-tcl6.txt]]
           83  +
           84  +test th1-tcl-6 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           85  +no such command:  bad_command</p>}}
           86  +
           87  +###############################################################################
           88  +
           89  +fossil test-th-render [file nativename [file join $dir th1-tcl7.txt]]
           90  +
           91  +test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           92  +syntax error in expression: &quot;2**0&quot;</p>}}
           93  +
           94  +###############################################################################
           95  +
           96  +fossil test-th-render [file nativename [file join $dir th1-tcl8.txt]]
           97  +
           98  +test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\
           99  +Cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\
          100  +class="thmainError">ERROR: tailcall can only be called from a proc or\
          101  +lambda</p>}}
          102  +
          103  +###############################################################################
          104  +
          105  +fossil test-th-render [file nativename [file join $dir th1-tcl9.txt]]
          106  +
          107  +test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 2 \
          108  +[list test-th-render [file nativename [file join $dir th1-tcl9.txt]]]]}

Added test/th1-tcl1.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  set channel stdout; tclInvoke set channel $channel
            8  +  proc doOut {msg} {puts $msg; puts \n}
            9  +  doOut [tclEval clock seconds]
           10  +  doOut [tclEval {set x [clock seconds]}]
           11  +  tclEval {puts $channel "[clock seconds]"}
           12  +  tclInvoke puts $channel "via Tcl invoke"
           13  +  doOut [tclExpr 2+2]
           14  +  doOut [tclExpr 2 + 2]
           15  +  doOut [tclInvoke set x "two words"]
           16  +  doOut [tclInvoke eval set y one_word]
           17  +  doOut [tclInvoke eval {set z "three words now"}]
           18  +  doOut [set x [tclEval {set x [clock seconds]}]]
           19  +  doOut [tclInvoke th1Eval {set y "two words"}]
           20  +  doOut [set z [tclInvoke th1Expr {2+2}]]
           21  +  doOut $x
           22  +  doOut $y
           23  +  doOut $z
           24  +  doOut [tclEval set x]
           25  +  doOut [tclEval set y]
           26  +  doOut [tclEval set z]
           27  +</th1>

Added test/th1-tcl2.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  # NOTE: This test requires that the SQLite package be available for the Tcl
            8  +  #       interpreter that is linked to the Fossil executable.
            9  +  #
           10  +  tclInvoke set repository_name [repository 1]
           11  +  proc doOut {msg} {puts $msg; puts \n}
           12  +  doOut [tclEval {
           13  +    package require sqlite3
           14  +    sqlite3 db $repository_name -readonly true
           15  +    set x [db eval {SELECT COUNT(*) FROM user;}]
           16  +    db close
           17  +    return $x
           18  +  }]
           19  +</th1>

Added test/th1-tcl3.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +  doOut [tclEval bad_command]
            9  +</th1>

Added test/th1-tcl4.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +  doOut [tclExpr 2/0]
            9  +</th1>

Added test/th1-tcl5.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +  doOut [tclInvoke bad_command]
            9  +</th1>

Added test/th1-tcl6.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +  doOut [tclEval th1Eval bad_command]
            9  +</th1>

Added test/th1-tcl7.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +
            9  +  #
           10  +  # BUGBUG: Attempting to divide by zero will crash TH1 with the error:
           11  +  #         "child killed: floating-point exception"
           12  +  #
           13  +  # doOut [tclEval th1Expr 2/0]
           14  +
           15  +  #
           16  +  # NOTE: For now, just cause an expression syntax error.
           17  +  #
           18  +  doOut [tclEval th1Expr 2**0]
           19  +</th1>

Added test/th1-tcl8.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  proc doOut {msg} {puts $msg; puts \n}
            8  +
            9  +  if {[tclInvoke set tcl_version] >= 8.6} {
           10  +    doOut [tclInvoke tailcall set x 1]
           11  +  } else {
           12  +    doOut "This test requires Tcl 8.6 or higher."
           13  +  }
           14  +</th1>

Added test/th1-tcl9.txt.

            1  +<th1>
            2  +  #
            3  +  # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
            4  +  # The corresponding test file executes this file using the test-th-render
            5  +  # Fossil command.
            6  +  #
            7  +  set channel stdout; tclInvoke set channel $channel
            8  +  tclEval {puts $channel [list [file tail $argv0] $argc $argv]}
            9  +</th1>

Added test/update-test-1.sh.

            1  +#!/bin/sh
            2  +#
            3  +# Run this script in an empty directory.  A single argument is the full
            4  +# pathname of the fossil binary.  Example:
            5  +#
            6  +#     sh update-test-1.sh /home/drh/fossil/m1/fossil
            7  +#
            8  +export FOSSIL=$1
            9  +rm -rf aaa bbb update-test-1.fossil
           10  +
           11  +# Create a test repository
           12  +$FOSSIL new update-test-1.fossil
           13  +
           14  +# In checkout aaa, add file one.txt
           15  +mkdir aaa
           16  +cd aaa
           17  +$FOSSIL open ../update-test-1.fossil
           18  +echo one >one.txt
           19  +$FOSSIL add one.txt
           20  +$FOSSIL commit -m add-one --tag add-one
           21  +
           22  +# Open checkout bbb.
           23  +mkdir ../bbb
           24  +cd ../bbb
           25  +$FOSSIL open ../update-test-1.fossil
           26  +
           27  +# Back in aaa, add file two.txt
           28  +cd ../aaa
           29  +echo two >two.txt
           30  +$FOSSIL add two.txt
           31  +$FOSSIL commit -m add-two --tag add-two
           32  +
           33  +# In bbb, delete file one.txt.  Then update the change from aaa that
           34  +# adds file two.  Verify that one.txt says deleted.
           35  +cd ../bbb
           36  +$FOSSIL rm one.txt
           37  +$FOSSIL changes
           38  +echo '========================================================================'
           39  +$FOSSIL update
           40  +echo '======== The previous should show "ADD two.txt" ========================'
           41  +$FOSSIL changes
           42  +echo '======== The previous should show "DELETE one.txt" ====================='
           43  +$FOSSIL commit --test -m check-in
           44  +echo '======== Only file two.txt is checked in ==============================='

Added test/update-test-2.sh.

            1  +#!/bin/sh
            2  +#
            3  +# Run this script in an empty directory.  A single argument is the full
            4  +# pathname of the fossil binary.  Example:
            5  +#
            6  +#     sh update-test-2.sh /home/drh/fossil/m1/fossil
            7  +#
            8  +export FOSSIL=$1
            9  +rm -rf aaa bbb update-test-2.fossil
           10  +
           11  +# Create a test repository
           12  +$FOSSIL new update-test-2.fossil
           13  +
           14  +# In checkout aaa, add file one.txt.
           15  +mkdir aaa
           16  +cd aaa
           17  +$FOSSIL open ../update-test-2.fossil
           18  +echo one >one.txt
           19  +$FOSSIL add one.txt
           20  +$FOSSIL commit -m add-one --tag add-one
           21  +
           22  +# Create checkout bbb.
           23  +mkdir ../bbb
           24  +cd ../bbb
           25  +$FOSSIL open ../update-test-2.fossil
           26  +
           27  +# Back in aaa, make changes to one.txt.  Add file two.txt.
           28  +cd ../aaa
           29  +echo change >>one.txt
           30  +echo two >two.txt
           31  +$FOSSIL add two.txt
           32  +$FOSSIL commit -m 'chng one and add two' --tag add-two
           33  +
           34  +# In bbb, remove one.txt, then update.
           35  +cd ../bbb
           36  +$FOSSIL rm one.txt
           37  +$FOSSIL changes
           38  +echo '========================================================================'
           39  +$FOSSIL update
           40  +echo '======== Previous should show "ADD two.txt" and conflict on one.txt ===='
           41  +$FOSSIL changes
           42  +echo '======== The previous should show "DELETE one.txt" ====================='
           43  +$FOSSIL commit --test -m 'check-in'
           44  +echo '======== Only file two.txt is checked in ==============================='

Added test/valgrind-www.tcl.

            1  +#!/usr/bin/tclsh
            2  +#
            3  +# Run this script in an open Fossil checkout at the top-level with a 
            4  +# fresh build of Fossil itself.  This script will run fossil on hundreds
            5  +# of different web-pages looking for memory allocation problems using 
            6  +# valgrind.  Valgrind output appears on stderr.  Suggested test scenario:
            7  +#
            8  +#     make
            9  +#     tclsh valgrind-www.tcl 2>&1 | tee valgrind-out.txt
           10  +#
           11  +# Then examine the valgrind-out.txt file for issues.
           12  +#
           13  +proc run_query {url} {
           14  +  set fd [open q.txt w]
           15  +  puts $fd "GET $url HTTP/1.0\r\n\r"
           16  +  close $fd
           17  +  return [exec valgrind ./fossil test-http <q.txt 2>@ stderr]
           18  +}
           19  +set todo {}
           20  +foreach url {
           21  +  /home
           22  +  /timeline
           23  +  /brlist
           24  +  /taglist
           25  +  /reportlist
           26  +  /setup
           27  +  /dir
           28  +  /wcontent
           29  +} {
           30  +  set seen($url) 1
           31  +  set pending($url) 1
           32  +}
           33  +set limit 1000
           34  +set npending [llength [array names pending]]
           35  +proc get_pending {} {
           36  +  global pending npending
           37  +  set res [lindex [array names pending] [expr {int(rand()*$npending)}]]
           38  +  unset pending($res)
           39  +  incr npending -1
           40  +  return $res
           41  +}
           42  +for {set i 0} {$npending>0 && $i<$limit} {incr i} {
           43  +  set url [get_pending]
           44  +  puts "====== ([expr {$i+1}]) $url ======"
           45  +  set x [run_query $url]
           46  +  while {[regexp {<[aA] .*?href="(/[a-z].*?)".*?>(.*)$} $x all url tail]} {
           47  +    set u2 [string map {&lt; < &gt; > &quot; \" &amp; &} $url]
           48  +    if {![info exists seen($u2)]} {
           49  +      set pending($u2) 1
           50  +      set seen($u2) 1
           51  +      incr npending
           52  +    }
           53  +    set x $tail
           54  +  }
           55  +}

Added tools/fossil-autocomplete.bash.

            1  +# Command name completion for Fossil.
            2  +# Mailing-list contribution by Stuart Rackham.
            3  +function _fossil() {
            4  +    local cur commands
            5  +    cur=${COMP_WORDS[COMP_CWORD]}
            6  +    commands=$(fossil help --all)
            7  +    if [ $COMP_CWORD -eq 1 ] || [ ${COMP_WORDS[1]} = help ]; then
            8  +            # Command name completion for 1st argument or 2nd if help command.
            9  +        COMPREPLY=( $(compgen -W "$commands" $cur) )
           10  +    else
           11  +            # File name completion for other arguments.
           12  +        COMPREPLY=( $(compgen -f $cur) )
           13  +    fi
           14  +}
           15  +complete -o default -F _fossil fossil f

Added tools/fossilwiki.

            1  +#!/usr/bin/perl
            2  +# vim: cin :
            3  +
            4  +$repofile = shift;
            5  +$repocmd = '';
            6  +$repocmd = "-R $repofile" if -f $repofile;
            7  +$mainpage = '';
            8  +
            9  +@rep = ();
           10  +if ( ! -f $repofile )
           11  +{
           12  +	@rep = `fossil info | grep 'project-name'`;
           13  +}
           14  +else
           15  +{
           16  +	@rep = `fossil info $repofile | grep 'project-name'`;
           17  +}
           18  +
           19  +$mainpage = $rep[0];
           20  +chomp $mainpage;
           21  +$mainpage =~ s/^project-name:\s+//;
           22  +
           23  +
           24  +@pages = `fossil wiki list $repocmd`;
           25  +
           26  +%pages = ();
           27  +foreach $page ( @pages )
           28  +{
           29  +	chomp $page;
           30  +	$text = `fossil wiki export "$page" $repocmd`;
           31  +	$pages{$page} = $text;
           32  +}
           33  +
           34  +@orphans = ();
           35  +@nointernals = ();
           36  +@terminals = ();
           37  +@empties = ();
           38  +%badlinks = ();
           39  +%alllinks = ();
           40  +%links = ();
           41  +foreach $page ( keys %pages )
           42  +{
           43  +	my @links = ();
           44  +	my $text = $pages{$page};
           45  +	while ( $text =~ m/\[([^][]+)\]/g )
           46  +	{
           47  +		push @links,$1;
           48  +	}
           49  +
           50  +	$numlinks = $#links;
           51  +
           52  +	if (@links == ()) 
           53  +	{
           54  +		push @terminals, $page;
           55  +	}
           56  +	else
           57  +	{
           58  +		my @internals = grep { $_ !~ /(http:)|(mailto:)|(https:)/ } @links;
           59  +		if (@internals == ()) 
           60  +		{
           61  +			push @nointernals, $page;
           62  +		}
           63  +		else
           64  +		{
           65  +			@{$links{$page}{'links'}} = map {my ($a,$b) = split /\|/; $a;} @internals;
           66  +			foreach $internal ( @internals )
           67  +			{
           68  +				my ($int_link, $display) = split /\|/, $internal;
           69  +				${$links{$int_link}{'refs'}}++;
           70  +				$alllinks{$int_link} = 1;
           71  +			}
           72  +		}
           73  +	}
           74  +
           75  +	if ($text eq '' || $text =~ m/^<i>Empty Page<\/i>/)
           76  +	{
           77  +		chomp $tail;
           78  +		my ($head, $tail) = split /\/i>/ , $text;
           79  +		if ($tail =~ m/^\s*$/s) {
           80  +		 	push @empties, $page;
           81  +		}
           82  +	}
           83  +}
           84  +foreach $page ( keys %links )
           85  +{
           86  +	if ($page ne $mainpage &&  (${$links{$page}{'refs'}} == 0))
           87  +	{
           88  +		push @orphans, $page;
           89  +	}
           90  +}
           91  +foreach $link (keys %alllinks )
           92  +{
           93  +	if (! exists($pages{$link}) && $link !~ /^\./ && $link !~ /^\//)
           94  +	{
           95  +		$badlinks{$link} = 1;
           96  +	}
           97  +}
           98  +foreach $empty ( @empties )
           99  +{
          100  +	print ("empty: '$empty'\n");
          101  +}
          102  +foreach $nointernals ( @nointernals )
          103  +{
          104  +	print ("nointernals: '$nointernals'\n");
          105  +}
          106  +foreach $terminal ( @terminals )
          107  +{
          108  +	print ("terminal: '$terminal'\n");
          109  +}
          110  +foreach $orphan ( @orphans )
          111  +{
          112  +	print ("orphan: '$orphan'\n");
          113  +}
          114  +foreach $link ( keys %badlinks )
          115  +{
          116  +	print ("badlink: '$link'\n");
          117  +}
          118  +foreach $page ( sort keys %links )
          119  +{
          120  +	my @links = @{$links{$page}{'links'}};
          121  +	if (@links != ()) 
          122  +	{
          123  +		if ($page eq $mainpage)
          124  +		{
          125  +			print "links: *** '$page' *** -> ", join (", ", @links), "\n";
          126  +		}
          127  +		else
          128  +		{
          129  +			print "links: '$page' -> ", join (", ", @links), "\n";
          130  +		}
          131  +	}
          132  +}

Added win/Makefile.PellesCGMake.

            1  +#
            2  +##############################################################################
            3  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            4  +##############################################################################
            5  +#
            6  +# This file is automatically generated.  Instead of editing this
            7  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
            8  +# to regenerate this file.
            9  +#
           10  +# HowTo
           11  +# -----
           12  +#
           13  +# This is a Makefile to compile fossil with PellesC from
           14  +#  http://www.smorgasbordet.com/pellesc/index.htm
           15  +# In addition to the Compiler envrionment, you need
           16  +#  gmake from http://sourceforge.net/projects/unxutils/, Pelles make version
           17  +#        couldn't handle the complex dependencies in this build
           18  +#  zlib sources
           19  +# Then you do
           20  +# 1. create a directory PellesC in the project root directory
           21  +# 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation
           22  +# 3. open a dos prompt window and change working directory into PellesC (step 1)
           23  +# 4. run gmake -f ..\win\Makefile.PellesCGMake
           24  +#
           25  +# this file is tested with
           26  +#   PellesC         5.00.13
           27  +#   gmake           3.80
           28  +#   zlib sources    1.2.5
           29  +#   Windows XP SP 2
           30  +# and
           31  +#   PellesC         6.00.4
           32  +#   gmake           3.80
           33  +#   zlib sources    1.2.5
           34  +#   Windows 7 Home Premium
           35  +#  
           36  +
           37  +#  
           38  +PellesCDir=c:\Programme\PellesC
           39  +
           40  +# Select between 32/64 bit code, default is 32 bit
           41  +#TARGETVERSION=64
           42  +
           43  +ifeq ($(TARGETVERSION),64)
           44  +# 64 bit version
           45  +TARGETMACHINE_CC=amd64
           46  +TARGETMACHINE_LN=amd64
           47  +TARGETEXTEND=64
           48  +else
           49  +# 32 bit version
           50  +TARGETMACHINE_CC=x86
           51  +TARGETMACHINE_LN=ix86
           52  +TARGETEXTEND=
           53  +endif
           54  +
           55  +# define the project directories
           56  +B=..
           57  +SRCDIR=$(B)/src/
           58  +WINDIR=$(B)/win/
           59  +ZLIBSRCDIR=../../zlib/
           60  +
           61  +# define linker command and options
           62  +LINK=$(PellesCDir)/bin/polink.exe
           63  +LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib
           64  +
           65  +# define standard C-compiler and flags, used to compile
           66  +# the fossil binary. Some special definitions follow for
           67  +# special files follow
           68  +CC=$(PellesCDir)\bin\pocc.exe
           69  +DEFINES=-D_pgmptr=g.argv[0]
           70  +CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
           71  +INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)
           72  +
           73  +# define commands for building the windows resource files
           74  +RESOURCE=fossil.res
           75  +RC=$(PellesCDir)\bin\porc.exe
           76  +RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION)
           77  +
           78  +# define the special utilities files, needed to generate
           79  +# the automatically generated source files
           80  +UTILS=translate.exe mkindex.exe makeheaders.exe
           81  +UTILS_OBJ=$(UTILS:.exe=.obj)
           82  +UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))
           83  +
           84  +# define the sqlite files, which need special flags on compile
           85  +SQLITESRC=sqlite3.c
           86  +ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
           87  +SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
           88  +SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
           89  +
           90  +# define the sqlite shell files, which need special flags on compile
           91  +SQLITESHELLSRC=shell.c
           92  +ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
           93  +SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
           94  +SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1
           95  +
           96  +# define the th scripting files, which need special flags on compile
           97  +THSRC=th.c th_lang.c
           98  +ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
           99  +THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
          100  +
          101  +# define the zlib files, needed by this compile
          102  +ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
          103  +ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
          104  +ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))
          105  +
          106  +# define all fossil sources, using the standard compile and
          107  +# source generation. These are all files in SRCDIR, which are not
          108  +# mentioned as special files above:
          109  +ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC) $(ORIGSQLITESHELLSRC),$(wildcard $(SRCDIR)*.c))
          110  +SRC=$(subst $(SRCDIR),,$(ORIGSRC))
          111  +TRANSLATEDSRC=$(SRC:.c=_.c)
          112  +TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj)
          113  +
          114  +# main target file is the application
          115  +APPLICATION=fossil.exe
          116  +
          117  +# define the standard make target
          118  +.PHONY:	default
          119  +default:	page_index.h headers $(APPLICATION)
          120  +
          121  +# symbolic target to generate the source generate utils
          122  +.PHONY:	utils
          123  +utils:	$(UTILS)
          124  +
          125  +# link utils
          126  +$(UTILS) version.exe:	%.exe:	%.obj
          127  +	$(LINK) $(LINKFLAGS) -out:"$@" $<
          128  +
          129  +# compiling standard fossil utils
          130  +$(UTILS_OBJ):	%.obj:	$(SRCDIR)%.c
          131  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
          132  +
          133  +# compile special windows utils
          134  +version.obj:	$(SRCDIR)mkversion.c
          135  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
          136  +
          137  +# generate the translated c-source files
          138  +$(TRANSLATEDSRC):	%_.c:	$(SRCDIR)%.c translate.exe
          139  +	translate.exe $< >$@
          140  +
          141  +# generate the index source, containing all web references,..
          142  +page_index.h:	$(TRANSLATEDSRC) mkindex.exe
          143  +	mkindex.exe $(TRANSLATEDSRC) >$@
          144  +
          145  +# extracting version info from manifest
          146  +VERSION.h:	version.exe ..\manifest.uuid ..\manifest ..\VERSION
          147  +	version.exe ..\manifest.uuid ..\manifest ..\VERSION  > $@
          148  +
          149  +# generate the simplified headers
          150  +headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h
          151  +	makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
          152  +	echo Done >$@
          153  +
          154  +# compile C sources with relevant options
          155  +
          156  +$(TRANSLATEDOBJ):	%_.obj:	%_.c %.h
          157  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
          158  +
          159  +$(SQLITEOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)%.h
          160  +	$(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@"
          161  +
          162  +$(SQLITESHELLOBJ):	%.obj:	$(SRCDIR)%.c
          163  +	$(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@"
          164  +
          165  +$(THOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)th.h
          166  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
          167  +
          168  +$(ZLIBOBJ):	%.obj:	$(ZLIBSRCDIR)%.c
          169  +	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
          170  +
          171  +# create the windows resource with icon and version info
          172  +$(RESOURCE):	%.res:	../win/%.rc ../win/*.ico
          173  +	$(RC) $(RCFLAGS) $< -Fo"$@"
          174  +
          175  +# link the application
          176  +$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
          177  +	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)
          178  +
          179  +# cleanup
          180  +
          181  +.PHONY: clean
          182  +clean:
          183  +	del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
          184  +	del /F $(TRANSLATEDSRC)
          185  +	del /F *.h headers
          186  +	del /F $(RESOURCE)
          187  +
          188  +.PHONY: clobber
          189  +clobber: clean
          190  +	del /F *.exe
          191  +

Added win/Makefile.dmc.

            1  +#
            2  +##############################################################################
            3  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            4  +##############################################################################
            5  +#
            6  +# This file is automatically generated.  Instead of editing this
            7  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
            8  +# to regenerate this file.
            9  +#
           10  +B      = ..
           11  +SRCDIR = $B\src
           12  +OBJDIR = .
           13  +O      = .obj
           14  +E      = .exe
           15  +
           16  +
           17  +# Maybe DMDIR, SSL or INCL needs adjustment
           18  +DMDIR  = c:\DM
           19  +INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include
           20  +
           21  +#SSL   =  -DFOSSIL_ENABLE_SSL=1
           22  +SSL    =
           23  +
           24  +CFLAGS = -o
           25  +BCC    = $(DMDIR)\bin\dmc $(CFLAGS)
           26  +TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
           27  +LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
           28  +
           29  +SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
           30  +
           31  +SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c 
           32  +
           33  +OBJ   = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O 
           34  +
           35  +
           36  +RC=$(DMDIR)\bin\rcc
           37  +RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
           38  +
           39  +APPNAME = $(OBJDIR)\fossil$(E)
           40  +
           41  +all: $(APPNAME)
           42  +
           43  +$(APPNAME) : translate$E mkindex$E headers  $(OBJ) $(OBJDIR)\link
           44  +	cd $(OBJDIR) 
           45  +	$(DMDIR)\bin\link @link
           46  +
           47  +$(OBJDIR)\fossil.res:	$B\win\fossil.rc
           48  +	$(RC) $(RCFLAGS) -o$@ $**
           49  +
           50  +$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
           51  +	+echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_tag json_timeline json_user json_wiki leaf login main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild regexp report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
           52  +	+echo fossil >> $@
           53  +	+echo fossil >> $@
           54  +	+echo $(LIBS) >> $@
           55  +	+echo. >> $@
           56  +	+echo fossil >> $@
           57  +
           58  +translate$E: $(SRCDIR)\translate.c
           59  +	$(BCC) -o$@ $**
           60  +
           61  +makeheaders$E: $(SRCDIR)\makeheaders.c
           62  +	$(BCC) -o$@ $**
           63  +
           64  +mkindex$E: $(SRCDIR)\mkindex.c
           65  +	$(BCC) -o$@ $**
           66  +
           67  +version$E: $B\src\mkversion.c
           68  +	$(BCC) -o$@ $**
           69  +
           70  +$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
           71  +	$(TCC) -o$@ -c -Dmain=sqlite3_shell $(SQLITE_OPTIONS) $**
           72  +
           73  +$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
           74  +	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $**
           75  +
           76  +$(OBJDIR)\th$O : $(SRCDIR)\th.c
           77  +	$(TCC) -o$@ -c $**
           78  +
           79  +$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
           80  +	$(TCC) -o$@ -c $**
           81  +
           82  +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
           83  +	cp $@ $@
           84  +
           85  +VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
           86  +	+$** > $@
           87  +
           88  +page_index.h: mkindex$E $(SRC) 
           89  +	+$** > $@
           90  +
           91  +clean:
           92  +	-del $(OBJDIR)\*.obj
           93  +	-del *.obj *_.c *.h *.map
           94  +
           95  +realclean:
           96  +	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
           97  +
           98  +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
           99  +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
          100  +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
          101  +$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
          102  +$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
          103  +$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
          104  +$(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h
          105  +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
          106  +$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
          107  +$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
          108  +$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
          109  +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
          110  +$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
          111  +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
          112  +
          113  +
          114  +
          115  +$(OBJDIR)\add$O : add_.c add.h
          116  +	$(TCC) -o$@ -c add_.c
          117  +
          118  +add_.c : $(SRCDIR)\add.c
          119  +	+translate$E $** > $@
          120  +
          121  +$(OBJDIR)\allrepo$O : allrepo_.c allrepo.h
          122  +	$(TCC) -o$@ -c allrepo_.c
          123  +
          124  +allrepo_.c : $(SRCDIR)\allrepo.c
          125  +	+translate$E $** > $@
          126  +
          127  +$(OBJDIR)\attach$O : attach_.c attach.h
          128  +	$(TCC) -o$@ -c attach_.c
          129  +
          130  +attach_.c : $(SRCDIR)\attach.c
          131  +	+translate$E $** > $@
          132  +
          133  +$(OBJDIR)\bag$O : bag_.c bag.h
          134  +	$(TCC) -o$@ -c bag_.c
          135  +
          136  +bag_.c : $(SRCDIR)\bag.c
          137  +	+translate$E $** > $@
          138  +
          139  +$(OBJDIR)\bisect$O : bisect_.c bisect.h
          140  +	$(TCC) -o$@ -c bisect_.c
          141  +
          142  +bisect_.c : $(SRCDIR)\bisect.c
          143  +	+translate$E $** > $@
          144  +
          145  +$(OBJDIR)\blob$O : blob_.c blob.h
          146  +	$(TCC) -o$@ -c blob_.c
          147  +
          148  +blob_.c : $(SRCDIR)\blob.c
          149  +	+translate$E $** > $@
          150  +
          151  +$(OBJDIR)\branch$O : branch_.c branch.h
          152  +	$(TCC) -o$@ -c branch_.c
          153  +
          154  +branch_.c : $(SRCDIR)\branch.c
          155  +	+translate$E $** > $@
          156  +
          157  +$(OBJDIR)\browse$O : browse_.c browse.h
          158  +	$(TCC) -o$@ -c browse_.c
          159  +
          160  +browse_.c : $(SRCDIR)\browse.c
          161  +	+translate$E $** > $@
          162  +
          163  +$(OBJDIR)\captcha$O : captcha_.c captcha.h
          164  +	$(TCC) -o$@ -c captcha_.c
          165  +
          166  +captcha_.c : $(SRCDIR)\captcha.c
          167  +	+translate$E $** > $@
          168  +
          169  +$(OBJDIR)\cgi$O : cgi_.c cgi.h
          170  +	$(TCC) -o$@ -c cgi_.c
          171  +
          172  +cgi_.c : $(SRCDIR)\cgi.c
          173  +	+translate$E $** > $@
          174  +
          175  +$(OBJDIR)\checkin$O : checkin_.c checkin.h
          176  +	$(TCC) -o$@ -c checkin_.c
          177  +
          178  +checkin_.c : $(SRCDIR)\checkin.c
          179  +	+translate$E $** > $@
          180  +
          181  +$(OBJDIR)\checkout$O : checkout_.c checkout.h
          182  +	$(TCC) -o$@ -c checkout_.c
          183  +
          184  +checkout_.c : $(SRCDIR)\checkout.c
          185  +	+translate$E $** > $@
          186  +
          187  +$(OBJDIR)\clearsign$O : clearsign_.c clearsign.h
          188  +	$(TCC) -o$@ -c clearsign_.c
          189  +
          190  +clearsign_.c : $(SRCDIR)\clearsign.c
          191  +	+translate$E $** > $@
          192  +
          193  +$(OBJDIR)\clone$O : clone_.c clone.h
          194  +	$(TCC) -o$@ -c clone_.c
          195  +
          196  +clone_.c : $(SRCDIR)\clone.c
          197  +	+translate$E $** > $@
          198  +
          199  +$(OBJDIR)\comformat$O : comformat_.c comformat.h
          200  +	$(TCC) -o$@ -c comformat_.c
          201  +
          202  +comformat_.c : $(SRCDIR)\comformat.c
          203  +	+translate$E $** > $@
          204  +
          205  +$(OBJDIR)\configure$O : configure_.c configure.h
          206  +	$(TCC) -o$@ -c configure_.c
          207  +
          208  +configure_.c : $(SRCDIR)\configure.c
          209  +	+translate$E $** > $@
          210  +
          211  +$(OBJDIR)\content$O : content_.c content.h
          212  +	$(TCC) -o$@ -c content_.c
          213  +
          214  +content_.c : $(SRCDIR)\content.c
          215  +	+translate$E $** > $@
          216  +
          217  +$(OBJDIR)\db$O : db_.c db.h
          218  +	$(TCC) -o$@ -c db_.c
          219  +
          220  +db_.c : $(SRCDIR)\db.c
          221  +	+translate$E $** > $@
          222  +
          223  +$(OBJDIR)\delta$O : delta_.c delta.h
          224  +	$(TCC) -o$@ -c delta_.c
          225  +
          226  +delta_.c : $(SRCDIR)\delta.c
          227  +	+translate$E $** > $@
          228  +
          229  +$(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h
          230  +	$(TCC) -o$@ -c deltacmd_.c
          231  +
          232  +deltacmd_.c : $(SRCDIR)\deltacmd.c
          233  +	+translate$E $** > $@
          234  +
          235  +$(OBJDIR)\descendants$O : descendants_.c descendants.h
          236  +	$(TCC) -o$@ -c descendants_.c
          237  +
          238  +descendants_.c : $(SRCDIR)\descendants.c
          239  +	+translate$E $** > $@
          240  +
          241  +$(OBJDIR)\diff$O : diff_.c diff.h
          242  +	$(TCC) -o$@ -c diff_.c
          243  +
          244  +diff_.c : $(SRCDIR)\diff.c
          245  +	+translate$E $** > $@
          246  +
          247  +$(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h
          248  +	$(TCC) -o$@ -c diffcmd_.c
          249  +
          250  +diffcmd_.c : $(SRCDIR)\diffcmd.c
          251  +	+translate$E $** > $@
          252  +
          253  +$(OBJDIR)\doc$O : doc_.c doc.h
          254  +	$(TCC) -o$@ -c doc_.c
          255  +
          256  +doc_.c : $(SRCDIR)\doc.c
          257  +	+translate$E $** > $@
          258  +
          259  +$(OBJDIR)\encode$O : encode_.c encode.h
          260  +	$(TCC) -o$@ -c encode_.c
          261  +
          262  +encode_.c : $(SRCDIR)\encode.c
          263  +	+translate$E $** > $@
          264  +
          265  +$(OBJDIR)\event$O : event_.c event.h
          266  +	$(TCC) -o$@ -c event_.c
          267  +
          268  +event_.c : $(SRCDIR)\event.c
          269  +	+translate$E $** > $@
          270  +
          271  +$(OBJDIR)\export$O : export_.c export.h
          272  +	$(TCC) -o$@ -c export_.c
          273  +
          274  +export_.c : $(SRCDIR)\export.c
          275  +	+translate$E $** > $@
          276  +
          277  +$(OBJDIR)\file$O : file_.c file.h
          278  +	$(TCC) -o$@ -c file_.c
          279  +
          280  +file_.c : $(SRCDIR)\file.c
          281  +	+translate$E $** > $@
          282  +
          283  +$(OBJDIR)\finfo$O : finfo_.c finfo.h
          284  +	$(TCC) -o$@ -c finfo_.c
          285  +
          286  +finfo_.c : $(SRCDIR)\finfo.c
          287  +	+translate$E $** > $@
          288  +
          289  +$(OBJDIR)\glob$O : glob_.c glob.h
          290  +	$(TCC) -o$@ -c glob_.c
          291  +
          292  +glob_.c : $(SRCDIR)\glob.c
          293  +	+translate$E $** > $@
          294  +
          295  +$(OBJDIR)\graph$O : graph_.c graph.h
          296  +	$(TCC) -o$@ -c graph_.c
          297  +
          298  +graph_.c : $(SRCDIR)\graph.c
          299  +	+translate$E $** > $@
          300  +
          301  +$(OBJDIR)\gzip$O : gzip_.c gzip.h
          302  +	$(TCC) -o$@ -c gzip_.c
          303  +
          304  +gzip_.c : $(SRCDIR)\gzip.c
          305  +	+translate$E $** > $@
          306  +
          307  +$(OBJDIR)\http$O : http_.c http.h
          308  +	$(TCC) -o$@ -c http_.c
          309  +
          310  +http_.c : $(SRCDIR)\http.c
          311  +	+translate$E $** > $@
          312  +
          313  +$(OBJDIR)\http_socket$O : http_socket_.c http_socket.h
          314  +	$(TCC) -o$@ -c http_socket_.c
          315  +
          316  +http_socket_.c : $(SRCDIR)\http_socket.c
          317  +	+translate$E $** > $@
          318  +
          319  +$(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h
          320  +	$(TCC) -o$@ -c http_ssl_.c
          321  +
          322  +http_ssl_.c : $(SRCDIR)\http_ssl.c
          323  +	+translate$E $** > $@
          324  +
          325  +$(OBJDIR)\http_transport$O : http_transport_.c http_transport.h
          326  +	$(TCC) -o$@ -c http_transport_.c
          327  +
          328  +http_transport_.c : $(SRCDIR)\http_transport.c
          329  +	+translate$E $** > $@
          330  +
          331  +$(OBJDIR)\import$O : import_.c import.h
          332  +	$(TCC) -o$@ -c import_.c
          333  +
          334  +import_.c : $(SRCDIR)\import.c
          335  +	+translate$E $** > $@
          336  +
          337  +$(OBJDIR)\info$O : info_.c info.h
          338  +	$(TCC) -o$@ -c info_.c
          339  +
          340  +info_.c : $(SRCDIR)\info.c
          341  +	+translate$E $** > $@
          342  +
          343  +$(OBJDIR)\json$O : json_.c json.h
          344  +	$(TCC) -o$@ -c json_.c
          345  +
          346  +json_.c : $(SRCDIR)\json.c
          347  +	+translate$E $** > $@
          348  +
          349  +$(OBJDIR)\json_artifact$O : json_artifact_.c json_artifact.h
          350  +	$(TCC) -o$@ -c json_artifact_.c
          351  +
          352  +json_artifact_.c : $(SRCDIR)\json_artifact.c
          353  +	+translate$E $** > $@
          354  +
          355  +$(OBJDIR)\json_branch$O : json_branch_.c json_branch.h
          356  +	$(TCC) -o$@ -c json_branch_.c
          357  +
          358  +json_branch_.c : $(SRCDIR)\json_branch.c
          359  +	+translate$E $** > $@
          360  +
          361  +$(OBJDIR)\json_config$O : json_config_.c json_config.h
          362  +	$(TCC) -o$@ -c json_config_.c
          363  +
          364  +json_config_.c : $(SRCDIR)\json_config.c
          365  +	+translate$E $** > $@
          366  +
          367  +$(OBJDIR)\json_diff$O : json_diff_.c json_diff.h
          368  +	$(TCC) -o$@ -c json_diff_.c
          369  +
          370  +json_diff_.c : $(SRCDIR)\json_diff.c
          371  +	+translate$E $** > $@
          372  +
          373  +$(OBJDIR)\json_dir$O : json_dir_.c json_dir.h
          374  +	$(TCC) -o$@ -c json_dir_.c
          375  +
          376  +json_dir_.c : $(SRCDIR)\json_dir.c
          377  +	+translate$E $** > $@
          378  +
          379  +$(OBJDIR)\json_finfo$O : json_finfo_.c json_finfo.h
          380  +	$(TCC) -o$@ -c json_finfo_.c
          381  +
          382  +json_finfo_.c : $(SRCDIR)\json_finfo.c
          383  +	+translate$E $** > $@
          384  +
          385  +$(OBJDIR)\json_login$O : json_login_.c json_login.h
          386  +	$(TCC) -o$@ -c json_login_.c
          387  +
          388  +json_login_.c : $(SRCDIR)\json_login.c
          389  +	+translate$E $** > $@
          390  +
          391  +$(OBJDIR)\json_query$O : json_query_.c json_query.h
          392  +	$(TCC) -o$@ -c json_query_.c
          393  +
          394  +json_query_.c : $(SRCDIR)\json_query.c
          395  +	+translate$E $** > $@
          396  +
          397  +$(OBJDIR)\json_report$O : json_report_.c json_report.h
          398  +	$(TCC) -o$@ -c json_report_.c
          399  +
          400  +json_report_.c : $(SRCDIR)\json_report.c
          401  +	+translate$E $** > $@
          402  +
          403  +$(OBJDIR)\json_tag$O : json_tag_.c json_tag.h
          404  +	$(TCC) -o$@ -c json_tag_.c
          405  +
          406  +json_tag_.c : $(SRCDIR)\json_tag.c
          407  +	+translate$E $** > $@
          408  +
          409  +$(OBJDIR)\json_timeline$O : json_timeline_.c json_timeline.h
          410  +	$(TCC) -o$@ -c json_timeline_.c
          411  +
          412  +json_timeline_.c : $(SRCDIR)\json_timeline.c
          413  +	+translate$E $** > $@
          414  +
          415  +$(OBJDIR)\json_user$O : json_user_.c json_user.h
          416  +	$(TCC) -o$@ -c json_user_.c
          417  +
          418  +json_user_.c : $(SRCDIR)\json_user.c
          419  +	+translate$E $** > $@
          420  +
          421  +$(OBJDIR)\json_wiki$O : json_wiki_.c json_wiki.h
          422  +	$(TCC) -o$@ -c json_wiki_.c
          423  +
          424  +json_wiki_.c : $(SRCDIR)\json_wiki.c
          425  +	+translate$E $** > $@
          426  +
          427  +$(OBJDIR)\leaf$O : leaf_.c leaf.h
          428  +	$(TCC) -o$@ -c leaf_.c
          429  +
          430  +leaf_.c : $(SRCDIR)\leaf.c
          431  +	+translate$E $** > $@
          432  +
          433  +$(OBJDIR)\login$O : login_.c login.h
          434  +	$(TCC) -o$@ -c login_.c
          435  +
          436  +login_.c : $(SRCDIR)\login.c
          437  +	+translate$E $** > $@
          438  +
          439  +$(OBJDIR)\main$O : main_.c main.h
          440  +	$(TCC) -o$@ -c main_.c
          441  +
          442  +main_.c : $(SRCDIR)\main.c
          443  +	+translate$E $** > $@
          444  +
          445  +$(OBJDIR)\manifest$O : manifest_.c manifest.h
          446  +	$(TCC) -o$@ -c manifest_.c
          447  +
          448  +manifest_.c : $(SRCDIR)\manifest.c
          449  +	+translate$E $** > $@
          450  +
          451  +$(OBJDIR)\markdown$O : markdown_.c markdown.h
          452  +	$(TCC) -o$@ -c markdown_.c
          453  +
          454  +markdown_.c : $(SRCDIR)\markdown.c
          455  +	+translate$E $** > $@
          456  +
          457  +$(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h
          458  +	$(TCC) -o$@ -c markdown_html_.c
          459  +
          460  +markdown_html_.c : $(SRCDIR)\markdown_html.c
          461  +	+translate$E $** > $@
          462  +
          463  +$(OBJDIR)\md5$O : md5_.c md5.h
          464  +	$(TCC) -o$@ -c md5_.c
          465  +
          466  +md5_.c : $(SRCDIR)\md5.c
          467  +	+translate$E $** > $@
          468  +
          469  +$(OBJDIR)\merge$O : merge_.c merge.h
          470  +	$(TCC) -o$@ -c merge_.c
          471  +
          472  +merge_.c : $(SRCDIR)\merge.c
          473  +	+translate$E $** > $@
          474  +
          475  +$(OBJDIR)\merge3$O : merge3_.c merge3.h
          476  +	$(TCC) -o$@ -c merge3_.c
          477  +
          478  +merge3_.c : $(SRCDIR)\merge3.c
          479  +	+translate$E $** > $@
          480  +
          481  +$(OBJDIR)\moderate$O : moderate_.c moderate.h
          482  +	$(TCC) -o$@ -c moderate_.c
          483  +
          484  +moderate_.c : $(SRCDIR)\moderate.c
          485  +	+translate$E $** > $@
          486  +
          487  +$(OBJDIR)\name$O : name_.c name.h
          488  +	$(TCC) -o$@ -c name_.c
          489  +
          490  +name_.c : $(SRCDIR)\name.c
          491  +	+translate$E $** > $@
          492  +
          493  +$(OBJDIR)\path$O : path_.c path.h
          494  +	$(TCC) -o$@ -c path_.c
          495  +
          496  +path_.c : $(SRCDIR)\path.c
          497  +	+translate$E $** > $@
          498  +
          499  +$(OBJDIR)\pivot$O : pivot_.c pivot.h
          500  +	$(TCC) -o$@ -c pivot_.c
          501  +
          502  +pivot_.c : $(SRCDIR)\pivot.c
          503  +	+translate$E $** > $@
          504  +
          505  +$(OBJDIR)\popen$O : popen_.c popen.h
          506  +	$(TCC) -o$@ -c popen_.c
          507  +
          508  +popen_.c : $(SRCDIR)\popen.c
          509  +	+translate$E $** > $@
          510  +
          511  +$(OBJDIR)\pqueue$O : pqueue_.c pqueue.h
          512  +	$(TCC) -o$@ -c pqueue_.c
          513  +
          514  +pqueue_.c : $(SRCDIR)\pqueue.c
          515  +	+translate$E $** > $@
          516  +
          517  +$(OBJDIR)\printf$O : printf_.c printf.h
          518  +	$(TCC) -o$@ -c printf_.c
          519  +
          520  +printf_.c : $(SRCDIR)\printf.c
          521  +	+translate$E $** > $@
          522  +
          523  +$(OBJDIR)\rebuild$O : rebuild_.c rebuild.h
          524  +	$(TCC) -o$@ -c rebuild_.c
          525  +
          526  +rebuild_.c : $(SRCDIR)\rebuild.c
          527  +	+translate$E $** > $@
          528  +
          529  +$(OBJDIR)\regexp$O : regexp_.c regexp.h
          530  +	$(TCC) -o$@ -c regexp_.c
          531  +
          532  +regexp_.c : $(SRCDIR)\regexp.c
          533  +	+translate$E $** > $@
          534  +
          535  +$(OBJDIR)\report$O : report_.c report.h
          536  +	$(TCC) -o$@ -c report_.c
          537  +
          538  +report_.c : $(SRCDIR)\report.c
          539  +	+translate$E $** > $@
          540  +
          541  +$(OBJDIR)\rss$O : rss_.c rss.h
          542  +	$(TCC) -o$@ -c rss_.c
          543  +
          544  +rss_.c : $(SRCDIR)\rss.c
          545  +	+translate$E $** > $@
          546  +
          547  +$(OBJDIR)\schema$O : schema_.c schema.h
          548  +	$(TCC) -o$@ -c schema_.c
          549  +
          550  +schema_.c : $(SRCDIR)\schema.c
          551  +	+translate$E $** > $@
          552  +
          553  +$(OBJDIR)\search$O : search_.c search.h
          554  +	$(TCC) -o$@ -c search_.c
          555  +
          556  +search_.c : $(SRCDIR)\search.c
          557  +	+translate$E $** > $@
          558  +
          559  +$(OBJDIR)\setup$O : setup_.c setup.h
          560  +	$(TCC) -o$@ -c setup_.c
          561  +
          562  +setup_.c : $(SRCDIR)\setup.c
          563  +	+translate$E $** > $@
          564  +
          565  +$(OBJDIR)\sha1$O : sha1_.c sha1.h
          566  +	$(TCC) -o$@ -c sha1_.c
          567  +
          568  +sha1_.c : $(SRCDIR)\sha1.c
          569  +	+translate$E $** > $@
          570  +
          571  +$(OBJDIR)\shun$O : shun_.c shun.h
          572  +	$(TCC) -o$@ -c shun_.c
          573  +
          574  +shun_.c : $(SRCDIR)\shun.c
          575  +	+translate$E $** > $@
          576  +
          577  +$(OBJDIR)\skins$O : skins_.c skins.h
          578  +	$(TCC) -o$@ -c skins_.c
          579  +
          580  +skins_.c : $(SRCDIR)\skins.c
          581  +	+translate$E $** > $@
          582  +
          583  +$(OBJDIR)\sqlcmd$O : sqlcmd_.c sqlcmd.h
          584  +	$(TCC) -o$@ -c sqlcmd_.c
          585  +
          586  +sqlcmd_.c : $(SRCDIR)\sqlcmd.c
          587  +	+translate$E $** > $@
          588  +
          589  +$(OBJDIR)\stash$O : stash_.c stash.h
          590  +	$(TCC) -o$@ -c stash_.c
          591  +
          592  +stash_.c : $(SRCDIR)\stash.c
          593  +	+translate$E $** > $@
          594  +
          595  +$(OBJDIR)\stat$O : stat_.c stat.h
          596  +	$(TCC) -o$@ -c stat_.c
          597  +
          598  +stat_.c : $(SRCDIR)\stat.c
          599  +	+translate$E $** > $@
          600  +
          601  +$(OBJDIR)\style$O : style_.c style.h
          602  +	$(TCC) -o$@ -c style_.c
          603  +
          604  +style_.c : $(SRCDIR)\style.c
          605  +	+translate$E $** > $@
          606  +
          607  +$(OBJDIR)\sync$O : sync_.c sync.h
          608  +	$(TCC) -o$@ -c sync_.c
          609  +
          610  +sync_.c : $(SRCDIR)\sync.c
          611  +	+translate$E $** > $@
          612  +
          613  +$(OBJDIR)\tag$O : tag_.c tag.h
          614  +	$(TCC) -o$@ -c tag_.c
          615  +
          616  +tag_.c : $(SRCDIR)\tag.c
          617  +	+translate$E $** > $@
          618  +
          619  +$(OBJDIR)\tar$O : tar_.c tar.h
          620  +	$(TCC) -o$@ -c tar_.c
          621  +
          622  +tar_.c : $(SRCDIR)\tar.c
          623  +	+translate$E $** > $@
          624  +
          625  +$(OBJDIR)\th_main$O : th_main_.c th_main.h
          626  +	$(TCC) -o$@ -c th_main_.c
          627  +
          628  +th_main_.c : $(SRCDIR)\th_main.c
          629  +	+translate$E $** > $@
          630  +
          631  +$(OBJDIR)\timeline$O : timeline_.c timeline.h
          632  +	$(TCC) -o$@ -c timeline_.c
          633  +
          634  +timeline_.c : $(SRCDIR)\timeline.c
          635  +	+translate$E $** > $@
          636  +
          637  +$(OBJDIR)\tkt$O : tkt_.c tkt.h
          638  +	$(TCC) -o$@ -c tkt_.c
          639  +
          640  +tkt_.c : $(SRCDIR)\tkt.c
          641  +	+translate$E $** > $@
          642  +
          643  +$(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h
          644  +	$(TCC) -o$@ -c tktsetup_.c
          645  +
          646  +tktsetup_.c : $(SRCDIR)\tktsetup.c
          647  +	+translate$E $** > $@
          648  +
          649  +$(OBJDIR)\undo$O : undo_.c undo.h
          650  +	$(TCC) -o$@ -c undo_.c
          651  +
          652  +undo_.c : $(SRCDIR)\undo.c
          653  +	+translate$E $** > $@
          654  +
          655  +$(OBJDIR)\unicode$O : unicode_.c unicode.h
          656  +	$(TCC) -o$@ -c unicode_.c
          657  +
          658  +unicode_.c : $(SRCDIR)\unicode.c
          659  +	+translate$E $** > $@
          660  +
          661  +$(OBJDIR)\update$O : update_.c update.h
          662  +	$(TCC) -o$@ -c update_.c
          663  +
          664  +update_.c : $(SRCDIR)\update.c
          665  +	+translate$E $** > $@
          666  +
          667  +$(OBJDIR)\url$O : url_.c url.h
          668  +	$(TCC) -o$@ -c url_.c
          669  +
          670  +url_.c : $(SRCDIR)\url.c
          671  +	+translate$E $** > $@
          672  +
          673  +$(OBJDIR)\user$O : user_.c user.h
          674  +	$(TCC) -o$@ -c user_.c
          675  +
          676  +user_.c : $(SRCDIR)\user.c
          677  +	+translate$E $** > $@
          678  +
          679  +$(OBJDIR)\utf8$O : utf8_.c utf8.h
          680  +	$(TCC) -o$@ -c utf8_.c
          681  +
          682  +utf8_.c : $(SRCDIR)\utf8.c
          683  +	+translate$E $** > $@
          684  +
          685  +$(OBJDIR)\verify$O : verify_.c verify.h
          686  +	$(TCC) -o$@ -c verify_.c
          687  +
          688  +verify_.c : $(SRCDIR)\verify.c
          689  +	+translate$E $** > $@
          690  +
          691  +$(OBJDIR)\vfile$O : vfile_.c vfile.h
          692  +	$(TCC) -o$@ -c vfile_.c
          693  +
          694  +vfile_.c : $(SRCDIR)\vfile.c
          695  +	+translate$E $** > $@
          696  +
          697  +$(OBJDIR)\wiki$O : wiki_.c wiki.h
          698  +	$(TCC) -o$@ -c wiki_.c
          699  +
          700  +wiki_.c : $(SRCDIR)\wiki.c
          701  +	+translate$E $** > $@
          702  +
          703  +$(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h
          704  +	$(TCC) -o$@ -c wikiformat_.c
          705  +
          706  +wikiformat_.c : $(SRCDIR)\wikiformat.c
          707  +	+translate$E $** > $@
          708  +
          709  +$(OBJDIR)\winhttp$O : winhttp_.c winhttp.h
          710  +	$(TCC) -o$@ -c winhttp_.c
          711  +
          712  +winhttp_.c : $(SRCDIR)\winhttp.c
          713  +	+translate$E $** > $@
          714  +
          715  +$(OBJDIR)\wysiwyg$O : wysiwyg_.c wysiwyg.h
          716  +	$(TCC) -o$@ -c wysiwyg_.c
          717  +
          718  +wysiwyg_.c : $(SRCDIR)\wysiwyg.c
          719  +	+translate$E $** > $@
          720  +
          721  +$(OBJDIR)\xfer$O : xfer_.c xfer.h
          722  +	$(TCC) -o$@ -c xfer_.c
          723  +
          724  +xfer_.c : $(SRCDIR)\xfer.c
          725  +	+translate$E $** > $@
          726  +
          727  +$(OBJDIR)\xfersetup$O : xfersetup_.c xfersetup.h
          728  +	$(TCC) -o$@ -c xfersetup_.c
          729  +
          730  +xfersetup_.c : $(SRCDIR)\xfersetup.c
          731  +	+translate$E $** > $@
          732  +
          733  +$(OBJDIR)\zip$O : zip_.c zip.h
          734  +	$(TCC) -o$@ -c zip_.c
          735  +
          736  +zip_.c : $(SRCDIR)\zip.c
          737  +	+translate$E $** > $@
          738  +
          739  +headers: makeheaders$E page_index.h VERSION.h
          740  +	 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
          741  +	@copy /Y nul: headers

Added win/Makefile.mingw.

            1  +#!/usr/bin/make
            2  +#
            3  +##############################################################################
            4  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            5  +##############################################################################
            6  +#
            7  +# This file is automatically generated.  Instead of editing this
            8  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
            9  +# to regenerate this file.
           10  +#
           11  +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using
           12  +# MinGW or MinGW-w64.
           13  +#
           14  +
           15  +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers.
           16  +#    By default, this is an empty string (i.e. use the native compiler).
           17  +#
           18  +PREFIX =
           19  +# PREFIX = mingw32-
           20  +# PREFIX = i686-pc-mingw32-
           21  +# PREFIX = i686-w64-mingw32-
           22  +# PREFIX = x86_64-w64-mingw32-
           23  +
           24  +#### The toplevel directory of the source tree.  Fossil can be built
           25  +#    in a directory that is separate from the source tree.  Just change
           26  +#    the following to point from the build directory to the src/ folder.
           27  +#
           28  +SRCDIR = src
           29  +
           30  +#### The directory into which object code files should be written.
           31  +#
           32  +OBJDIR = wbld
           33  +
           34  +#### C Compiler and options for use in building executables that
           35  +#    will run on the platform that is doing the build.  This is used
           36  +#    to compile code-generator programs as part of the build process.
           37  +#    See TCC below for the C compiler for building the finished binary.
           38  +#
           39  +BCC = gcc
           40  +
           41  +#### Enable compiling with debug symbols (much larger binary)
           42  +#
           43  +# FOSSIL_ENABLE_SYMBOLS = 1
           44  +
           45  +#### Enable JSON (http://www.json.org) support using "cson"
           46  +#
           47  +# FOSSIL_ENABLE_JSON = 1
           48  +
           49  +#### Enable markdown support
           50  +#
           51  +# FOSSIL_ENABLE_MARKDOWN = 1
           52  +
           53  +#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
           54  +#
           55  +# FOSSIL_ENABLE_SSL = 1
           56  +
           57  +#### Enable scripting support via Tcl/Tk
           58  +#
           59  +# FOSSIL_ENABLE_TCL = 1
           60  +
           61  +#### Load Tcl using the stubs mechanism
           62  +#
           63  +# FOSSIL_ENABLE_TCL_STUBS = 1
           64  +
           65  +#### Use the Tcl source directory instead of the install directory?
           66  +#    This is useful when Tcl has been compiled statically with MinGW.
           67  +#
           68  +FOSSIL_TCL_SOURCE = 1
           69  +
           70  +#### Check if the workaround for the MinGW command line handling needs to
           71  +#    be enabled by default.
           72  +#
           73  +ifndef BROKEN_MINGW_CMDLINE
           74  +ifeq (,$(findstring w64-mingw32,$(PREFIX)))
           75  +BROKEN_MINGW_CMDLINE = 1
           76  +endif
           77  +endif
           78  +
           79  +#### The directories where the zlib include and library files are located.
           80  +#
           81  +ZINCDIR = $(SRCDIR)/../compat/zlib
           82  +ZLIBDIR = $(SRCDIR)/../compat/zlib
           83  +
           84  +#### The directories where the OpenSSL include and library files are located.
           85  +#    The recommended usage here is to use the Sysinternals junction tool
           86  +#    to create a hard link between an "openssl-1.x" sub-directory of the
           87  +#    Fossil source code directory and the target OpenSSL source directory.
           88  +#
           89  +OPENSSLINCDIR = $(SRCDIR)/../openssl-1.0.1c/include
           90  +OPENSSLLIBDIR = $(SRCDIR)/../openssl-1.0.1c
           91  +
           92  +#### Either the directory where the Tcl library is installed or the Tcl
           93  +#    source code directory resides (depending on the value of the macro
           94  +#    FOSSIL_TCL_SOURCE).  If this points to the Tcl install directory,
           95  +#    this directory must have "include" and "lib" sub-directories.  If
           96  +#    this points to the Tcl source code directory, this directory must
           97  +#    have "generic" and "win" sub-directories.  The recommended usage
           98  +#    here is to use the Sysinternals junction tool to create a hard
           99  +#    link between a "tcl-8.x" sub-directory of the Fossil source code
          100  +#    directory and the target Tcl directory.  This removes the need to
          101  +#    hard-code the necessary paths in this Makefile.
          102  +#
          103  +TCLDIR = $(SRCDIR)/../tcl-8.6
          104  +
          105  +#### The Tcl source code directory.  This defaults to the same value as
          106  +#    TCLDIR macro (above), which may not be correct.  This value will
          107  +#    only be used if the FOSSIL_TCL_SOURCE macro is defined.
          108  +#
          109  +TCLSRCDIR = $(TCLDIR)
          110  +
          111  +#### The Tcl include and library directories.  These values will only be
          112  +#    used if the FOSSIL_TCL_SOURCE macro is not defined.
          113  +#
          114  +TCLINCDIR = $(TCLDIR)/include
          115  +TCLLIBDIR = $(TCLDIR)/lib
          116  +
          117  +#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
          118  +#
          119  +ifdef FOSSIL_ENABLE_TCL_STUBS
          120  +LIBTCL = -ltclstub86
          121  +else
          122  +LIBTCL = -ltcl86
          123  +endif
          124  +
          125  +#### C Compile and options for use in building executables that
          126  +#    will run on the target platform.  This is usually the same
          127  +#    as BCC, unless you are cross-compiling.  This C compiler builds
          128  +#    the finished binary for fossil.  The BCC compiler above is used
          129  +#    for building intermediate code-generator tools.
          130  +#
          131  +TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
          132  +
          133  +#### Add the necessary command line options to build with debugging
          134  +#    symbols, if enabled.
          135  +#
          136  +ifdef FOSSIL_ENABLE_SYMBOLS
          137  +TCC += -g
          138  +endif
          139  +
          140  +#### Compile resources for use in building executables that will run
          141  +#    on the target platform.
          142  +#
          143  +RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
          144  +
          145  +# With HTTPS support
          146  +ifdef FOSSIL_ENABLE_SSL
          147  +TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
          148  +RCC += -I$(OPENSSLINCDIR)
          149  +endif
          150  +
          151  +# With Tcl support
          152  +ifdef FOSSIL_ENABLE_TCL
          153  +ifdef FOSSIL_TCL_SOURCE
          154  +TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          155  +RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          156  +else
          157  +TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
          158  +RCC += -I$(TCLINCDIR)
          159  +endif
          160  +endif
          161  +
          162  +# With MinGW command line handling workaround
          163  +ifdef BROKEN_MINGW_CMDLINE
          164  +TCC += -DBROKEN_MINGW_CMDLINE=1
          165  +RCC += -DBROKEN_MINGW_CMDLINE=1
          166  +endif
          167  +
          168  +# With HTTPS support
          169  +ifdef FOSSIL_ENABLE_SSL
          170  +TCC += -DFOSSIL_ENABLE_SSL=1
          171  +RCC += -DFOSSIL_ENABLE_SSL=1
          172  +endif
          173  +
          174  +# With Tcl support
          175  +ifdef FOSSIL_ENABLE_TCL
          176  +TCC += -DFOSSIL_ENABLE_TCL=1
          177  +RCC += -DFOSSIL_ENABLE_TCL=1
          178  +# Either statically linked or via stubs
          179  +ifdef FOSSIL_ENABLE_TCL_STUBS
          180  +TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          181  +RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          182  +else
          183  +TCC += -DSTATIC_BUILD
          184  +RCC += -DSTATIC_BUILD
          185  +endif
          186  +endif
          187  +
          188  +# With JSON support
          189  +ifdef FOSSIL_ENABLE_JSON
          190  +TCC += -DFOSSIL_ENABLE_JSON=1
          191  +RCC += -DFOSSIL_ENABLE_JSON=1
          192  +endif
          193  +
          194  +# With markdown support
          195  +ifdef FOSSIL_ENABLE_MARKDOWN
          196  +TCC += -DFOSSIL_ENABLE_MARKDOWN=1
          197  +RCC += -DFOSSIL_ENABLE_MARKDOWN=1
          198  +endif
          199  +
          200  +#### We add the -static option here so that we can build a static
          201  +#    executable that will run in a chroot jail.
          202  +#
          203  +LIB = -static
          204  +
          205  +# MinGW: If available, use the Unicode capable runtime startup code.
          206  +ifndef BROKEN_MINGW_CMDLINE
          207  +LIB += -municode
          208  +endif
          209  +
          210  +# OpenSSL: Add the necessary libraries required, if enabled.
          211  +ifdef FOSSIL_ENABLE_SSL
          212  +LIB += -lssl -lcrypto -lgdi32
          213  +endif
          214  +
          215  +# Tcl: Add the necessary libraries required, if enabled.
          216  +ifdef FOSSIL_ENABLE_TCL
          217  +LIB += $(LIBTCL)
          218  +endif
          219  +
          220  +#### Extra arguments for linking the finished binary.  Fossil needs
          221  +#    to link against the Z-Lib compression library.  There are no
          222  +#    other mandatory dependencies.
          223  +#
          224  +LIB += -lmingwex -lz
          225  +
          226  +#### These libraries MUST appear in the same order as they do for Tcl
          227  +#    or linking with it will not work (exact reason unknown).
          228  +#
          229  +ifdef FOSSIL_ENABLE_TCL
          230  +ifdef FOSSIL_ENABLE_TCL_STUBS
          231  +LIB += -lkernel32 -lws2_32
          232  +else
          233  +LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
          234  +endif
          235  +else
          236  +LIB += -lkernel32 -lws2_32
          237  +endif
          238  +
          239  +#### Tcl shell for use in running the fossil test suite.  This is only
          240  +#    used for testing.
          241  +#
          242  +TCLSH = tclsh
          243  +
          244  +#### Nullsoft installer MakeNSIS location
          245  +#
          246  +MAKENSIS = "$(ProgramFiles)\NSIS\MakeNSIS.exe"
          247  +
          248  +#### Include a configuration file that can override any one of these settings.
          249  +#
          250  +-include config.w32
          251  +
          252  +# STOP HERE
          253  +# You should not need to change anything below this line
          254  +#--------------------------------------------------------
          255  +XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
          256  +
          257  +SRC = \
          258  +  $(SRCDIR)/add.c \
          259  +  $(SRCDIR)/allrepo.c \
          260  +  $(SRCDIR)/attach.c \
          261  +  $(SRCDIR)/bag.c \
          262  +  $(SRCDIR)/bisect.c \
          263  +  $(SRCDIR)/blob.c \
          264  +  $(SRCDIR)/branch.c \
          265  +  $(SRCDIR)/browse.c \
          266  +  $(SRCDIR)/captcha.c \
          267  +  $(SRCDIR)/cgi.c \
          268  +  $(SRCDIR)/checkin.c \
          269  +  $(SRCDIR)/checkout.c \
          270  +  $(SRCDIR)/clearsign.c \
          271  +  $(SRCDIR)/clone.c \
          272  +  $(SRCDIR)/comformat.c \
          273  +  $(SRCDIR)/configure.c \
          274  +  $(SRCDIR)/content.c \
          275  +  $(SRCDIR)/db.c \
          276  +  $(SRCDIR)/delta.c \
          277  +  $(SRCDIR)/deltacmd.c \
          278  +  $(SRCDIR)/descendants.c \
          279  +  $(SRCDIR)/diff.c \
          280  +  $(SRCDIR)/diffcmd.c \
          281  +  $(SRCDIR)/doc.c \
          282  +  $(SRCDIR)/encode.c \
          283  +  $(SRCDIR)/event.c \
          284  +  $(SRCDIR)/export.c \
          285  +  $(SRCDIR)/file.c \
          286  +  $(SRCDIR)/finfo.c \
          287  +  $(SRCDIR)/glob.c \
          288  +  $(SRCDIR)/graph.c \
          289  +  $(SRCDIR)/gzip.c \
          290  +  $(SRCDIR)/http.c \
          291  +  $(SRCDIR)/http_socket.c \
          292  +  $(SRCDIR)/http_ssl.c \
          293  +  $(SRCDIR)/http_transport.c \
          294  +  $(SRCDIR)/import.c \
          295  +  $(SRCDIR)/info.c \
          296  +  $(SRCDIR)/json.c \
          297  +  $(SRCDIR)/json_artifact.c \
          298  +  $(SRCDIR)/json_branch.c \
          299  +  $(SRCDIR)/json_config.c \
          300  +  $(SRCDIR)/json_diff.c \
          301  +  $(SRCDIR)/json_dir.c \
          302  +  $(SRCDIR)/json_finfo.c \
          303  +  $(SRCDIR)/json_login.c \
          304  +  $(SRCDIR)/json_query.c \
          305  +  $(SRCDIR)/json_report.c \
          306  +  $(SRCDIR)/json_tag.c \
          307  +  $(SRCDIR)/json_timeline.c \
          308  +  $(SRCDIR)/json_user.c \
          309  +  $(SRCDIR)/json_wiki.c \
          310  +  $(SRCDIR)/leaf.c \
          311  +  $(SRCDIR)/login.c \
          312  +  $(SRCDIR)/main.c \
          313  +  $(SRCDIR)/manifest.c \
          314  +  $(SRCDIR)/markdown.c \
          315  +  $(SRCDIR)/markdown_html.c \
          316  +  $(SRCDIR)/md5.c \
          317  +  $(SRCDIR)/merge.c \
          318  +  $(SRCDIR)/merge3.c \
          319  +  $(SRCDIR)/moderate.c \
          320  +  $(SRCDIR)/name.c \
          321  +  $(SRCDIR)/path.c \
          322  +  $(SRCDIR)/pivot.c \
          323  +  $(SRCDIR)/popen.c \
          324  +  $(SRCDIR)/pqueue.c \
          325  +  $(SRCDIR)/printf.c \
          326  +  $(SRCDIR)/rebuild.c \
          327  +  $(SRCDIR)/regexp.c \
          328  +  $(SRCDIR)/report.c \
          329  +  $(SRCDIR)/rss.c \
          330  +  $(SRCDIR)/schema.c \
          331  +  $(SRCDIR)/search.c \
          332  +  $(SRCDIR)/setup.c \
          333  +  $(SRCDIR)/sha1.c \
          334  +  $(SRCDIR)/shun.c \
          335  +  $(SRCDIR)/skins.c \
          336  +  $(SRCDIR)/sqlcmd.c \
          337  +  $(SRCDIR)/stash.c \
          338  +  $(SRCDIR)/stat.c \
          339  +  $(SRCDIR)/style.c \
          340  +  $(SRCDIR)/sync.c \
          341  +  $(SRCDIR)/tag.c \
          342  +  $(SRCDIR)/tar.c \
          343  +  $(SRCDIR)/th_main.c \
          344  +  $(SRCDIR)/timeline.c \
          345  +  $(SRCDIR)/tkt.c \
          346  +  $(SRCDIR)/tktsetup.c \
          347  +  $(SRCDIR)/undo.c \
          348  +  $(SRCDIR)/unicode.c \
          349  +  $(SRCDIR)/update.c \
          350  +  $(SRCDIR)/url.c \
          351  +  $(SRCDIR)/user.c \
          352  +  $(SRCDIR)/utf8.c \
          353  +  $(SRCDIR)/verify.c \
          354  +  $(SRCDIR)/vfile.c \
          355  +  $(SRCDIR)/wiki.c \
          356  +  $(SRCDIR)/wikiformat.c \
          357  +  $(SRCDIR)/winhttp.c \
          358  +  $(SRCDIR)/wysiwyg.c \
          359  +  $(SRCDIR)/xfer.c \
          360  +  $(SRCDIR)/xfersetup.c \
          361  +  $(SRCDIR)/zip.c
          362  +
          363  +TRANS_SRC = \
          364  +  $(OBJDIR)/add_.c \
          365  +  $(OBJDIR)/allrepo_.c \
          366  +  $(OBJDIR)/attach_.c \
          367  +  $(OBJDIR)/bag_.c \
          368  +  $(OBJDIR)/bisect_.c \
          369  +  $(OBJDIR)/blob_.c \
          370  +  $(OBJDIR)/branch_.c \
          371  +  $(OBJDIR)/browse_.c \
          372  +  $(OBJDIR)/captcha_.c \
          373  +  $(OBJDIR)/cgi_.c \
          374  +  $(OBJDIR)/checkin_.c \
          375  +  $(OBJDIR)/checkout_.c \
          376  +  $(OBJDIR)/clearsign_.c \
          377  +  $(OBJDIR)/clone_.c \
          378  +  $(OBJDIR)/comformat_.c \
          379  +  $(OBJDIR)/configure_.c \
          380  +  $(OBJDIR)/content_.c \
          381  +  $(OBJDIR)/db_.c \
          382  +  $(OBJDIR)/delta_.c \
          383  +  $(OBJDIR)/deltacmd_.c \
          384  +  $(OBJDIR)/descendants_.c \
          385  +  $(OBJDIR)/diff_.c \
          386  +  $(OBJDIR)/diffcmd_.c \
          387  +  $(OBJDIR)/doc_.c \
          388  +  $(OBJDIR)/encode_.c \
          389  +  $(OBJDIR)/event_.c \
          390  +  $(OBJDIR)/export_.c \
          391  +  $(OBJDIR)/file_.c \
          392  +  $(OBJDIR)/finfo_.c \
          393  +  $(OBJDIR)/glob_.c \
          394  +  $(OBJDIR)/graph_.c \
          395  +  $(OBJDIR)/gzip_.c \
          396  +  $(OBJDIR)/http_.c \
          397  +  $(OBJDIR)/http_socket_.c \
          398  +  $(OBJDIR)/http_ssl_.c \
          399  +  $(OBJDIR)/http_transport_.c \
          400  +  $(OBJDIR)/import_.c \
          401  +  $(OBJDIR)/info_.c \
          402  +  $(OBJDIR)/json_.c \
          403  +  $(OBJDIR)/json_artifact_.c \
          404  +  $(OBJDIR)/json_branch_.c \
          405  +  $(OBJDIR)/json_config_.c \
          406  +  $(OBJDIR)/json_diff_.c \
          407  +  $(OBJDIR)/json_dir_.c \
          408  +  $(OBJDIR)/json_finfo_.c \
          409  +  $(OBJDIR)/json_login_.c \
          410  +  $(OBJDIR)/json_query_.c \
          411  +  $(OBJDIR)/json_report_.c \
          412  +  $(OBJDIR)/json_tag_.c \
          413  +  $(OBJDIR)/json_timeline_.c \
          414  +  $(OBJDIR)/json_user_.c \
          415  +  $(OBJDIR)/json_wiki_.c \
          416  +  $(OBJDIR)/leaf_.c \
          417  +  $(OBJDIR)/login_.c \
          418  +  $(OBJDIR)/main_.c \
          419  +  $(OBJDIR)/manifest_.c \
          420  +  $(OBJDIR)/markdown_.c \
          421  +  $(OBJDIR)/markdown_html_.c \
          422  +  $(OBJDIR)/md5_.c \
          423  +  $(OBJDIR)/merge_.c \
          424  +  $(OBJDIR)/merge3_.c \
          425  +  $(OBJDIR)/moderate_.c \
          426  +  $(OBJDIR)/name_.c \
          427  +  $(OBJDIR)/path_.c \
          428  +  $(OBJDIR)/pivot_.c \
          429  +  $(OBJDIR)/popen_.c \
          430  +  $(OBJDIR)/pqueue_.c \
          431  +  $(OBJDIR)/printf_.c \
          432  +  $(OBJDIR)/rebuild_.c \
          433  +  $(OBJDIR)/regexp_.c \
          434  +  $(OBJDIR)/report_.c \
          435  +  $(OBJDIR)/rss_.c \
          436  +  $(OBJDIR)/schema_.c \
          437  +  $(OBJDIR)/search_.c \
          438  +  $(OBJDIR)/setup_.c \
          439  +  $(OBJDIR)/sha1_.c \
          440  +  $(OBJDIR)/shun_.c \
          441  +  $(OBJDIR)/skins_.c \
          442  +  $(OBJDIR)/sqlcmd_.c \
          443  +  $(OBJDIR)/stash_.c \
          444  +  $(OBJDIR)/stat_.c \
          445  +  $(OBJDIR)/style_.c \
          446  +  $(OBJDIR)/sync_.c \
          447  +  $(OBJDIR)/tag_.c \
          448  +  $(OBJDIR)/tar_.c \
          449  +  $(OBJDIR)/th_main_.c \
          450  +  $(OBJDIR)/timeline_.c \
          451  +  $(OBJDIR)/tkt_.c \
          452  +  $(OBJDIR)/tktsetup_.c \
          453  +  $(OBJDIR)/undo_.c \
          454  +  $(OBJDIR)/unicode_.c \
          455  +  $(OBJDIR)/update_.c \
          456  +  $(OBJDIR)/url_.c \
          457  +  $(OBJDIR)/user_.c \
          458  +  $(OBJDIR)/utf8_.c \
          459  +  $(OBJDIR)/verify_.c \
          460  +  $(OBJDIR)/vfile_.c \
          461  +  $(OBJDIR)/wiki_.c \
          462  +  $(OBJDIR)/wikiformat_.c \
          463  +  $(OBJDIR)/winhttp_.c \
          464  +  $(OBJDIR)/wysiwyg_.c \
          465  +  $(OBJDIR)/xfer_.c \
          466  +  $(OBJDIR)/xfersetup_.c \
          467  +  $(OBJDIR)/zip_.c
          468  +
          469  +OBJ = \
          470  + $(OBJDIR)/add.o \
          471  + $(OBJDIR)/allrepo.o \
          472  + $(OBJDIR)/attach.o \
          473  + $(OBJDIR)/bag.o \
          474  + $(OBJDIR)/bisect.o \
          475  + $(OBJDIR)/blob.o \
          476  + $(OBJDIR)/branch.o \
          477  + $(OBJDIR)/browse.o \
          478  + $(OBJDIR)/captcha.o \
          479  + $(OBJDIR)/cgi.o \
          480  + $(OBJDIR)/checkin.o \
          481  + $(OBJDIR)/checkout.o \
          482  + $(OBJDIR)/clearsign.o \
          483  + $(OBJDIR)/clone.o \
          484  + $(OBJDIR)/comformat.o \
          485  + $(OBJDIR)/configure.o \
          486  + $(OBJDIR)/content.o \
          487  + $(OBJDIR)/db.o \
          488  + $(OBJDIR)/delta.o \
          489  + $(OBJDIR)/deltacmd.o \
          490  + $(OBJDIR)/descendants.o \
          491  + $(OBJDIR)/diff.o \
          492  + $(OBJDIR)/diffcmd.o \
          493  + $(OBJDIR)/doc.o \
          494  + $(OBJDIR)/encode.o \
          495  + $(OBJDIR)/event.o \
          496  + $(OBJDIR)/export.o \
          497  + $(OBJDIR)/file.o \
          498  + $(OBJDIR)/finfo.o \
          499  + $(OBJDIR)/glob.o \
          500  + $(OBJDIR)/graph.o \
          501  + $(OBJDIR)/gzip.o \
          502  + $(OBJDIR)/http.o \
          503  + $(OBJDIR)/http_socket.o \
          504  + $(OBJDIR)/http_ssl.o \
          505  + $(OBJDIR)/http_transport.o \
          506  + $(OBJDIR)/import.o \
          507  + $(OBJDIR)/info.o \
          508  + $(OBJDIR)/json.o \
          509  + $(OBJDIR)/json_artifact.o \
          510  + $(OBJDIR)/json_branch.o \
          511  + $(OBJDIR)/json_config.o \
          512  + $(OBJDIR)/json_diff.o \
          513  + $(OBJDIR)/json_dir.o \
          514  + $(OBJDIR)/json_finfo.o \
          515  + $(OBJDIR)/json_login.o \
          516  + $(OBJDIR)/json_query.o \
          517  + $(OBJDIR)/json_report.o \
          518  + $(OBJDIR)/json_tag.o \
          519  + $(OBJDIR)/json_timeline.o \
          520  + $(OBJDIR)/json_user.o \
          521  + $(OBJDIR)/json_wiki.o \
          522  + $(OBJDIR)/leaf.o \
          523  + $(OBJDIR)/login.o \
          524  + $(OBJDIR)/main.o \
          525  + $(OBJDIR)/manifest.o \
          526  + $(OBJDIR)/markdown.o \
          527  + $(OBJDIR)/markdown_html.o \
          528  + $(OBJDIR)/md5.o \
          529  + $(OBJDIR)/merge.o \
          530  + $(OBJDIR)/merge3.o \
          531  + $(OBJDIR)/moderate.o \
          532  + $(OBJDIR)/name.o \
          533  + $(OBJDIR)/path.o \
          534  + $(OBJDIR)/pivot.o \
          535  + $(OBJDIR)/popen.o \
          536  + $(OBJDIR)/pqueue.o \
          537  + $(OBJDIR)/printf.o \
          538  + $(OBJDIR)/rebuild.o \
          539  + $(OBJDIR)/regexp.o \
          540  + $(OBJDIR)/report.o \
          541  + $(OBJDIR)/rss.o \
          542  + $(OBJDIR)/schema.o \
          543  + $(OBJDIR)/search.o \
          544  + $(OBJDIR)/setup.o \
          545  + $(OBJDIR)/sha1.o \
          546  + $(OBJDIR)/shun.o \
          547  + $(OBJDIR)/skins.o \
          548  + $(OBJDIR)/sqlcmd.o \
          549  + $(OBJDIR)/stash.o \
          550  + $(OBJDIR)/stat.o \
          551  + $(OBJDIR)/style.o \
          552  + $(OBJDIR)/sync.o \
          553  + $(OBJDIR)/tag.o \
          554  + $(OBJDIR)/tar.o \
          555  + $(OBJDIR)/th_main.o \
          556  + $(OBJDIR)/timeline.o \
          557  + $(OBJDIR)/tkt.o \
          558  + $(OBJDIR)/tktsetup.o \
          559  + $(OBJDIR)/undo.o \
          560  + $(OBJDIR)/unicode.o \
          561  + $(OBJDIR)/update.o \
          562  + $(OBJDIR)/url.o \
          563  + $(OBJDIR)/user.o \
          564  + $(OBJDIR)/utf8.o \
          565  + $(OBJDIR)/verify.o \
          566  + $(OBJDIR)/vfile.o \
          567  + $(OBJDIR)/wiki.o \
          568  + $(OBJDIR)/wikiformat.o \
          569  + $(OBJDIR)/winhttp.o \
          570  + $(OBJDIR)/wysiwyg.o \
          571  + $(OBJDIR)/xfer.o \
          572  + $(OBJDIR)/xfersetup.o \
          573  + $(OBJDIR)/zip.o
          574  +
          575  +APPNAME = fossil.exe
          576  +
          577  +#### If the USE_WINDOWS variable exists, it is assumed that we are building
          578  +#    inside of a Windows-style shell; otherwise, it is assumed that we are
          579  +#    building inside of a Unix-style shell.  Note that the "move" command is
          580  +#    broken when attempting to use it from the Windows shell via MinGW make
          581  +#    because the SHELL variable is only used for certain commands that are
          582  +#    recognized internally by make.
          583  +#
          584  +ifdef USE_WINDOWS
          585  +TRANSLATE   = $(subst /,\,$(OBJDIR)/translate)
          586  +MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders)
          587  +MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex)
          588  +VERSION     = $(subst /,\,$(OBJDIR)/version)
          589  +CP          = copy
          590  +MV          = copy
          591  +RM          = del /Q
          592  +MKDIR       = -mkdir
          593  +RMDIR       = rmdir /S /Q
          594  +else
          595  +TRANSLATE   = $(OBJDIR)/translate
          596  +MAKEHEADERS = $(OBJDIR)/makeheaders
          597  +MKINDEX     = $(OBJDIR)/mkindex
          598  +VERSION     = $(OBJDIR)/version
          599  +CP          = cp
          600  +MV          = mv
          601  +RM          = rm -f
          602  +MKDIR       = -mkdir -p
          603  +RMDIR       = rm -rf
          604  +endif
          605  +
          606  +all:	$(OBJDIR) $(APPNAME)
          607  +
          608  +$(OBJDIR)/fossil.o:	$(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h
          609  +ifdef USE_WINDOWS
          610  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
          611  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
          612  +else
          613  +	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
          614  +	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
          615  +endif
          616  +	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o
          617  +
          618  +install:	$(OBJDIR) $(APPNAME)
          619  +ifdef USE_WINDOWS
          620  +	$(MKDIR) $(subst /,\,$(INSTALLDIR))
          621  +	$(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR))
          622  +else
          623  +	$(MKDIR) $(INSTALLDIR)
          624  +	$(MV) $(APPNAME) $(INSTALLDIR)
          625  +endif
          626  +
          627  +$(OBJDIR):
          628  +ifdef USE_WINDOWS
          629  +	$(MKDIR) $(subst /,\,$(OBJDIR))
          630  +else
          631  +	$(MKDIR) $(OBJDIR)
          632  +endif
          633  +
          634  +$(OBJDIR)/translate:	$(SRCDIR)/translate.c
          635  +	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
          636  +
          637  +$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
          638  +	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
          639  +
          640  +$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
          641  +	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
          642  +
          643  +$(VERSION): $(SRCDIR)/mkversion.c
          644  +	$(BCC) -o $(OBJDIR)/version $(SRCDIR)/mkversion.c
          645  +
          646  +# WARNING. DANGER. Running the test suite modifies the repository the
          647  +# build is done from, i.e. the checkout belongs to. Do not sync/push
          648  +# the repository after running the tests.
          649  +test:	$(OBJDIR) $(APPNAME)
          650  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
          651  +
          652  +$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
          653  +	$(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
          654  +
          655  +EXTRAOBJ =  $(OBJDIR)/sqlite3.o  $(OBJDIR)/shell.o  $(OBJDIR)/th.o  $(OBJDIR)/th_lang.o  $(OBJDIR)/cson_amalgamation.o
          656  +
          657  +ifdef FOSSIL_ENABLE_TCL
          658  +EXTRAOBJ +=  $(OBJDIR)/th_tcl.o
          659  +endif
          660  +
          661  +zlib:
          662  +	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a
          663  +
          664  +$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib
          665  +	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o
          666  +
          667  +# This rule prevents make from using its default rules to try build
          668  +# an executable named "manifest" out of the file named "manifest.c"
          669  +#
          670  +$(SRCDIR)/../manifest:
          671  +	# noop
          672  +
          673  +clean:
          674  +ifdef USE_WINDOWS
          675  +	$(RM) $(subst /,\,$(APPNAME))
          676  +	$(RMDIR) $(subst /,\,$(OBJDIR))
          677  +else
          678  +	$(RM) $(APPNAME)
          679  +	$(RMDIR) $(OBJDIR)
          680  +endif
          681  +
          682  +setup: $(OBJDIR) $(APPNAME)
          683  +	$(MAKENSIS) ./fossil.nsi
          684  +
          685  +$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
          686  +	$(MKINDEX) $(TRANS_SRC) >$@
          687  +
          688  +$(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
          689  +	$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
          690  +		$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
          691  +		$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
          692  +		$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
          693  +		$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
          694  +		$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
          695  +		$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
          696  +		$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
          697  +		$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
          698  +		$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
          699  +		$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
          700  +		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
          701  +		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
          702  +		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
          703  +		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
          704  +		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
          705  +		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
          706  +		$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
          707  +		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
          708  +		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
          709  +		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
          710  +		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
          711  +		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
          712  +		$(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \
          713  +		$(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \
          714  +		$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
          715  +		$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
          716  +		$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
          717  +		$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
          718  +		$(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \
          719  +		$(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \
          720  +		$(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \
          721  +		$(OBJDIR)/http_.c:$(OBJDIR)/http.h \
          722  +		$(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \
          723  +		$(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \
          724  +		$(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \
          725  +		$(OBJDIR)/import_.c:$(OBJDIR)/import.h \
          726  +		$(OBJDIR)/info_.c:$(OBJDIR)/info.h \
          727  +		$(OBJDIR)/json_.c:$(OBJDIR)/json.h \
          728  +		$(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h \
          729  +		$(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h \
          730  +		$(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h \
          731  +		$(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h \
          732  +		$(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h \
          733  +		$(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h \
          734  +		$(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h \
          735  +		$(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h \
          736  +		$(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \
          737  +		$(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \
          738  +		$(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \
          739  +		$(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \
          740  +		$(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \
          741  +		$(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \
          742  +		$(OBJDIR)/login_.c:$(OBJDIR)/login.h \
          743  +		$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
          744  +		$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
          745  +		$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
          746  +		$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
          747  +		$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
          748  +		$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
          749  +		$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
          750  +		$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
          751  +		$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
          752  +		$(OBJDIR)/path_.c:$(OBJDIR)/path.h \
          753  +		$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
          754  +		$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
          755  +		$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
          756  +		$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
          757  +		$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
          758  +		$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
          759  +		$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
          760  +		$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
          761  +		$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
          762  +		$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
          763  +		$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
          764  +		$(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \
          765  +		$(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \
          766  +		$(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \
          767  +		$(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \
          768  +		$(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \
          769  +		$(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \
          770  +		$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
          771  +		$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
          772  +		$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
          773  +		$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
          774  +		$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
          775  +		$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
          776  +		$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
          777  +		$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
          778  +		$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
          779  +		$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
          780  +		$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
          781  +		$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
          782  +		$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
          783  +		$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
          784  +		$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
          785  +		$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
          786  +		$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
          787  +		$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
          788  +		$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
          789  +		$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
          790  +		$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
          791  +		$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
          792  +		$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
          793  +		$(SRCDIR)/sqlite3.h \
          794  +		$(SRCDIR)/th.h \
          795  +		$(OBJDIR)/VERSION.h
          796  +	echo Done >$(OBJDIR)/headers
          797  +
          798  +$(OBJDIR)/headers: Makefile
          799  +
          800  +Makefile:
          801  +
          802  +$(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
          803  +	$(TRANSLATE) $(SRCDIR)/add.c >$(OBJDIR)/add_.c
          804  +
          805  +$(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h  $(SRCDIR)/config.h
          806  +	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
          807  +
          808  +$(OBJDIR)/add.h:	$(OBJDIR)/headers
          809  +
          810  +$(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(OBJDIR)/translate
          811  +	$(TRANSLATE) $(SRCDIR)/allrepo.c >$(OBJDIR)/allrepo_.c
          812  +
          813  +$(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h  $(SRCDIR)/config.h
          814  +	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
          815  +
          816  +$(OBJDIR)/allrepo.h:	$(OBJDIR)/headers
          817  +
          818  +$(OBJDIR)/attach_.c:	$(SRCDIR)/attach.c $(OBJDIR)/translate
          819  +	$(TRANSLATE) $(SRCDIR)/attach.c >$(OBJDIR)/attach_.c
          820  +
          821  +$(OBJDIR)/attach.o:	$(OBJDIR)/attach_.c $(OBJDIR)/attach.h  $(SRCDIR)/config.h
          822  +	$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
          823  +
          824  +$(OBJDIR)/attach.h:	$(OBJDIR)/headers
          825  +
          826  +$(OBJDIR)/bag_.c:	$(SRCDIR)/bag.c $(OBJDIR)/translate
          827  +	$(TRANSLATE) $(SRCDIR)/bag.c >$(OBJDIR)/bag_.c
          828  +
          829  +$(OBJDIR)/bag.o:	$(OBJDIR)/bag_.c $(OBJDIR)/bag.h  $(SRCDIR)/config.h
          830  +	$(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c
          831  +
          832  +$(OBJDIR)/bag.h:	$(OBJDIR)/headers
          833  +
          834  +$(OBJDIR)/bisect_.c:	$(SRCDIR)/bisect.c $(OBJDIR)/translate
          835  +	$(TRANSLATE) $(SRCDIR)/bisect.c >$(OBJDIR)/bisect_.c
          836  +
          837  +$(OBJDIR)/bisect.o:	$(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h  $(SRCDIR)/config.h
          838  +	$(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c
          839  +
          840  +$(OBJDIR)/bisect.h:	$(OBJDIR)/headers
          841  +
          842  +$(OBJDIR)/blob_.c:	$(SRCDIR)/blob.c $(OBJDIR)/translate
          843  +	$(TRANSLATE) $(SRCDIR)/blob.c >$(OBJDIR)/blob_.c
          844  +
          845  +$(OBJDIR)/blob.o:	$(OBJDIR)/blob_.c $(OBJDIR)/blob.h  $(SRCDIR)/config.h
          846  +	$(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c
          847  +
          848  +$(OBJDIR)/blob.h:	$(OBJDIR)/headers
          849  +
          850  +$(OBJDIR)/branch_.c:	$(SRCDIR)/branch.c $(OBJDIR)/translate
          851  +	$(TRANSLATE) $(SRCDIR)/branch.c >$(OBJDIR)/branch_.c
          852  +
          853  +$(OBJDIR)/branch.o:	$(OBJDIR)/branch_.c $(OBJDIR)/branch.h  $(SRCDIR)/config.h
          854  +	$(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c
          855  +
          856  +$(OBJDIR)/branch.h:	$(OBJDIR)/headers
          857  +
          858  +$(OBJDIR)/browse_.c:	$(SRCDIR)/browse.c $(OBJDIR)/translate
          859  +	$(TRANSLATE) $(SRCDIR)/browse.c >$(OBJDIR)/browse_.c
          860  +
          861  +$(OBJDIR)/browse.o:	$(OBJDIR)/browse_.c $(OBJDIR)/browse.h  $(SRCDIR)/config.h
          862  +	$(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c
          863  +
          864  +$(OBJDIR)/browse.h:	$(OBJDIR)/headers
          865  +
          866  +$(OBJDIR)/captcha_.c:	$(SRCDIR)/captcha.c $(OBJDIR)/translate
          867  +	$(TRANSLATE) $(SRCDIR)/captcha.c >$(OBJDIR)/captcha_.c
          868  +
          869  +$(OBJDIR)/captcha.o:	$(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h  $(SRCDIR)/config.h
          870  +	$(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c
          871  +
          872  +$(OBJDIR)/captcha.h:	$(OBJDIR)/headers
          873  +
          874  +$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(OBJDIR)/translate
          875  +	$(TRANSLATE) $(SRCDIR)/cgi.c >$(OBJDIR)/cgi_.c
          876  +
          877  +$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h  $(SRCDIR)/config.h
          878  +	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
          879  +
          880  +$(OBJDIR)/cgi.h:	$(OBJDIR)/headers
          881  +
          882  +$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(OBJDIR)/translate
          883  +	$(TRANSLATE) $(SRCDIR)/checkin.c >$(OBJDIR)/checkin_.c
          884  +
          885  +$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h  $(SRCDIR)/config.h
          886  +	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c
          887  +
          888  +$(OBJDIR)/checkin.h:	$(OBJDIR)/headers
          889  +
          890  +$(OBJDIR)/checkout_.c:	$(SRCDIR)/checkout.c $(OBJDIR)/translate
          891  +	$(TRANSLATE) $(SRCDIR)/checkout.c >$(OBJDIR)/checkout_.c
          892  +
          893  +$(OBJDIR)/checkout.o:	$(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h  $(SRCDIR)/config.h
          894  +	$(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c
          895  +
          896  +$(OBJDIR)/checkout.h:	$(OBJDIR)/headers
          897  +
          898  +$(OBJDIR)/clearsign_.c:	$(SRCDIR)/clearsign.c $(OBJDIR)/translate
          899  +	$(TRANSLATE) $(SRCDIR)/clearsign.c >$(OBJDIR)/clearsign_.c
          900  +
          901  +$(OBJDIR)/clearsign.o:	$(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h  $(SRCDIR)/config.h
          902  +	$(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c
          903  +
          904  +$(OBJDIR)/clearsign.h:	$(OBJDIR)/headers
          905  +
          906  +$(OBJDIR)/clone_.c:	$(SRCDIR)/clone.c $(OBJDIR)/translate
          907  +	$(TRANSLATE) $(SRCDIR)/clone.c >$(OBJDIR)/clone_.c
          908  +
          909  +$(OBJDIR)/clone.o:	$(OBJDIR)/clone_.c $(OBJDIR)/clone.h  $(SRCDIR)/config.h
          910  +	$(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
          911  +
          912  +$(OBJDIR)/clone.h:	$(OBJDIR)/headers
          913  +
          914  +$(OBJDIR)/comformat_.c:	$(SRCDIR)/comformat.c $(OBJDIR)/translate
          915  +	$(TRANSLATE) $(SRCDIR)/comformat.c >$(OBJDIR)/comformat_.c
          916  +
          917  +$(OBJDIR)/comformat.o:	$(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h  $(SRCDIR)/config.h
          918  +	$(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c
          919  +
          920  +$(OBJDIR)/comformat.h:	$(OBJDIR)/headers
          921  +
          922  +$(OBJDIR)/configure_.c:	$(SRCDIR)/configure.c $(OBJDIR)/translate
          923  +	$(TRANSLATE) $(SRCDIR)/configure.c >$(OBJDIR)/configure_.c
          924  +
          925  +$(OBJDIR)/configure.o:	$(OBJDIR)/configure_.c $(OBJDIR)/configure.h  $(SRCDIR)/config.h
          926  +	$(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c
          927  +
          928  +$(OBJDIR)/configure.h:	$(OBJDIR)/headers
          929  +
          930  +$(OBJDIR)/content_.c:	$(SRCDIR)/content.c $(OBJDIR)/translate
          931  +	$(TRANSLATE) $(SRCDIR)/content.c >$(OBJDIR)/content_.c
          932  +
          933  +$(OBJDIR)/content.o:	$(OBJDIR)/content_.c $(OBJDIR)/content.h  $(SRCDIR)/config.h
          934  +	$(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c
          935  +
          936  +$(OBJDIR)/content.h:	$(OBJDIR)/headers
          937  +
          938  +$(OBJDIR)/db_.c:	$(SRCDIR)/db.c $(OBJDIR)/translate
          939  +	$(TRANSLATE) $(SRCDIR)/db.c >$(OBJDIR)/db_.c
          940  +
          941  +$(OBJDIR)/db.o:	$(OBJDIR)/db_.c $(OBJDIR)/db.h  $(SRCDIR)/config.h
          942  +	$(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c
          943  +
          944  +$(OBJDIR)/db.h:	$(OBJDIR)/headers
          945  +
          946  +$(OBJDIR)/delta_.c:	$(SRCDIR)/delta.c $(OBJDIR)/translate
          947  +	$(TRANSLATE) $(SRCDIR)/delta.c >$(OBJDIR)/delta_.c
          948  +
          949  +$(OBJDIR)/delta.o:	$(OBJDIR)/delta_.c $(OBJDIR)/delta.h  $(SRCDIR)/config.h
          950  +	$(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c
          951  +
          952  +$(OBJDIR)/delta.h:	$(OBJDIR)/headers
          953  +
          954  +$(OBJDIR)/deltacmd_.c:	$(SRCDIR)/deltacmd.c $(OBJDIR)/translate
          955  +	$(TRANSLATE) $(SRCDIR)/deltacmd.c >$(OBJDIR)/deltacmd_.c
          956  +
          957  +$(OBJDIR)/deltacmd.o:	$(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h  $(SRCDIR)/config.h
          958  +	$(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c
          959  +
          960  +$(OBJDIR)/deltacmd.h:	$(OBJDIR)/headers
          961  +
          962  +$(OBJDIR)/descendants_.c:	$(SRCDIR)/descendants.c $(OBJDIR)/translate
          963  +	$(TRANSLATE) $(SRCDIR)/descendants.c >$(OBJDIR)/descendants_.c
          964  +
          965  +$(OBJDIR)/descendants.o:	$(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h  $(SRCDIR)/config.h
          966  +	$(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c
          967  +
          968  +$(OBJDIR)/descendants.h:	$(OBJDIR)/headers
          969  +
          970  +$(OBJDIR)/diff_.c:	$(SRCDIR)/diff.c $(OBJDIR)/translate
          971  +	$(TRANSLATE) $(SRCDIR)/diff.c >$(OBJDIR)/diff_.c
          972  +
          973  +$(OBJDIR)/diff.o:	$(OBJDIR)/diff_.c $(OBJDIR)/diff.h  $(SRCDIR)/config.h
          974  +	$(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c
          975  +
          976  +$(OBJDIR)/diff.h:	$(OBJDIR)/headers
          977  +
          978  +$(OBJDIR)/diffcmd_.c:	$(SRCDIR)/diffcmd.c $(OBJDIR)/translate
          979  +	$(TRANSLATE) $(SRCDIR)/diffcmd.c >$(OBJDIR)/diffcmd_.c
          980  +
          981  +$(OBJDIR)/diffcmd.o:	$(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h  $(SRCDIR)/config.h
          982  +	$(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c
          983  +
          984  +$(OBJDIR)/diffcmd.h:	$(OBJDIR)/headers
          985  +
          986  +$(OBJDIR)/doc_.c:	$(SRCDIR)/doc.c $(OBJDIR)/translate
          987  +	$(TRANSLATE) $(SRCDIR)/doc.c >$(OBJDIR)/doc_.c
          988  +
          989  +$(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h  $(SRCDIR)/config.h
          990  +	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
          991  +
          992  +$(OBJDIR)/doc.h:	$(OBJDIR)/headers
          993  +
          994  +$(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(OBJDIR)/translate
          995  +	$(TRANSLATE) $(SRCDIR)/encode.c >$(OBJDIR)/encode_.c
          996  +
          997  +$(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h  $(SRCDIR)/config.h
          998  +	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
          999  +
         1000  +$(OBJDIR)/encode.h:	$(OBJDIR)/headers
         1001  +
         1002  +$(OBJDIR)/event_.c:	$(SRCDIR)/event.c $(OBJDIR)/translate
         1003  +	$(TRANSLATE) $(SRCDIR)/event.c >$(OBJDIR)/event_.c
         1004  +
         1005  +$(OBJDIR)/event.o:	$(OBJDIR)/event_.c $(OBJDIR)/event.h  $(SRCDIR)/config.h
         1006  +	$(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c
         1007  +
         1008  +$(OBJDIR)/event.h:	$(OBJDIR)/headers
         1009  +
         1010  +$(OBJDIR)/export_.c:	$(SRCDIR)/export.c $(OBJDIR)/translate
         1011  +	$(TRANSLATE) $(SRCDIR)/export.c >$(OBJDIR)/export_.c
         1012  +
         1013  +$(OBJDIR)/export.o:	$(OBJDIR)/export_.c $(OBJDIR)/export.h  $(SRCDIR)/config.h
         1014  +	$(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c
         1015  +
         1016  +$(OBJDIR)/export.h:	$(OBJDIR)/headers
         1017  +
         1018  +$(OBJDIR)/file_.c:	$(SRCDIR)/file.c $(OBJDIR)/translate
         1019  +	$(TRANSLATE) $(SRCDIR)/file.c >$(OBJDIR)/file_.c
         1020  +
         1021  +$(OBJDIR)/file.o:	$(OBJDIR)/file_.c $(OBJDIR)/file.h  $(SRCDIR)/config.h
         1022  +	$(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
         1023  +
         1024  +$(OBJDIR)/file.h:	$(OBJDIR)/headers
         1025  +
         1026  +$(OBJDIR)/finfo_.c:	$(SRCDIR)/finfo.c $(OBJDIR)/translate
         1027  +	$(TRANSLATE) $(SRCDIR)/finfo.c >$(OBJDIR)/finfo_.c
         1028  +
         1029  +$(OBJDIR)/finfo.o:	$(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h  $(SRCDIR)/config.h
         1030  +	$(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c
         1031  +
         1032  +$(OBJDIR)/finfo.h:	$(OBJDIR)/headers
         1033  +
         1034  +$(OBJDIR)/glob_.c:	$(SRCDIR)/glob.c $(OBJDIR)/translate
         1035  +	$(TRANSLATE) $(SRCDIR)/glob.c >$(OBJDIR)/glob_.c
         1036  +
         1037  +$(OBJDIR)/glob.o:	$(OBJDIR)/glob_.c $(OBJDIR)/glob.h  $(SRCDIR)/config.h
         1038  +	$(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c
         1039  +
         1040  +$(OBJDIR)/glob.h:	$(OBJDIR)/headers
         1041  +
         1042  +$(OBJDIR)/graph_.c:	$(SRCDIR)/graph.c $(OBJDIR)/translate
         1043  +	$(TRANSLATE) $(SRCDIR)/graph.c >$(OBJDIR)/graph_.c
         1044  +
         1045  +$(OBJDIR)/graph.o:	$(OBJDIR)/graph_.c $(OBJDIR)/graph.h  $(SRCDIR)/config.h
         1046  +	$(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c
         1047  +
         1048  +$(OBJDIR)/graph.h:	$(OBJDIR)/headers
         1049  +
         1050  +$(OBJDIR)/gzip_.c:	$(SRCDIR)/gzip.c $(OBJDIR)/translate
         1051  +	$(TRANSLATE) $(SRCDIR)/gzip.c >$(OBJDIR)/gzip_.c
         1052  +
         1053  +$(OBJDIR)/gzip.o:	$(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h  $(SRCDIR)/config.h
         1054  +	$(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c
         1055  +
         1056  +$(OBJDIR)/gzip.h:	$(OBJDIR)/headers
         1057  +
         1058  +$(OBJDIR)/http_.c:	$(SRCDIR)/http.c $(OBJDIR)/translate
         1059  +	$(TRANSLATE) $(SRCDIR)/http.c >$(OBJDIR)/http_.c
         1060  +
         1061  +$(OBJDIR)/http.o:	$(OBJDIR)/http_.c $(OBJDIR)/http.h  $(SRCDIR)/config.h
         1062  +	$(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c
         1063  +
         1064  +$(OBJDIR)/http.h:	$(OBJDIR)/headers
         1065  +
         1066  +$(OBJDIR)/http_socket_.c:	$(SRCDIR)/http_socket.c $(OBJDIR)/translate
         1067  +	$(TRANSLATE) $(SRCDIR)/http_socket.c >$(OBJDIR)/http_socket_.c
         1068  +
         1069  +$(OBJDIR)/http_socket.o:	$(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h  $(SRCDIR)/config.h
         1070  +	$(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c
         1071  +
         1072  +$(OBJDIR)/http_socket.h:	$(OBJDIR)/headers
         1073  +
         1074  +$(OBJDIR)/http_ssl_.c:	$(SRCDIR)/http_ssl.c $(OBJDIR)/translate
         1075  +	$(TRANSLATE) $(SRCDIR)/http_ssl.c >$(OBJDIR)/http_ssl_.c
         1076  +
         1077  +$(OBJDIR)/http_ssl.o:	$(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h  $(SRCDIR)/config.h
         1078  +	$(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c
         1079  +
         1080  +$(OBJDIR)/http_ssl.h:	$(OBJDIR)/headers
         1081  +
         1082  +$(OBJDIR)/http_transport_.c:	$(SRCDIR)/http_transport.c $(OBJDIR)/translate
         1083  +	$(TRANSLATE) $(SRCDIR)/http_transport.c >$(OBJDIR)/http_transport_.c
         1084  +
         1085  +$(OBJDIR)/http_transport.o:	$(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h  $(SRCDIR)/config.h
         1086  +	$(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c
         1087  +
         1088  +$(OBJDIR)/http_transport.h:	$(OBJDIR)/headers
         1089  +
         1090  +$(OBJDIR)/import_.c:	$(SRCDIR)/import.c $(OBJDIR)/translate
         1091  +	$(TRANSLATE) $(SRCDIR)/import.c >$(OBJDIR)/import_.c
         1092  +
         1093  +$(OBJDIR)/import.o:	$(OBJDIR)/import_.c $(OBJDIR)/import.h  $(SRCDIR)/config.h
         1094  +	$(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c
         1095  +
         1096  +$(OBJDIR)/import.h:	$(OBJDIR)/headers
         1097  +
         1098  +$(OBJDIR)/info_.c:	$(SRCDIR)/info.c $(OBJDIR)/translate
         1099  +	$(TRANSLATE) $(SRCDIR)/info.c >$(OBJDIR)/info_.c
         1100  +
         1101  +$(OBJDIR)/info.o:	$(OBJDIR)/info_.c $(OBJDIR)/info.h  $(SRCDIR)/config.h
         1102  +	$(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
         1103  +
         1104  +$(OBJDIR)/info.h:	$(OBJDIR)/headers
         1105  +
         1106  +$(OBJDIR)/json_.c:	$(SRCDIR)/json.c $(OBJDIR)/translate
         1107  +	$(TRANSLATE) $(SRCDIR)/json.c >$(OBJDIR)/json_.c
         1108  +
         1109  +$(OBJDIR)/json.o:	$(OBJDIR)/json_.c $(OBJDIR)/json.h  $(SRCDIR)/config.h
         1110  +	$(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
         1111  +
         1112  +$(OBJDIR)/json.h:	$(OBJDIR)/headers
         1113  +
         1114  +$(OBJDIR)/json_artifact_.c:	$(SRCDIR)/json_artifact.c $(OBJDIR)/translate
         1115  +	$(TRANSLATE) $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
         1116  +
         1117  +$(OBJDIR)/json_artifact.o:	$(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h  $(SRCDIR)/config.h
         1118  +	$(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
         1119  +
         1120  +$(OBJDIR)/json_artifact.h:	$(OBJDIR)/headers
         1121  +
         1122  +$(OBJDIR)/json_branch_.c:	$(SRCDIR)/json_branch.c $(OBJDIR)/translate
         1123  +	$(TRANSLATE) $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
         1124  +
         1125  +$(OBJDIR)/json_branch.o:	$(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h  $(SRCDIR)/config.h
         1126  +	$(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
         1127  +
         1128  +$(OBJDIR)/json_branch.h:	$(OBJDIR)/headers
         1129  +
         1130  +$(OBJDIR)/json_config_.c:	$(SRCDIR)/json_config.c $(OBJDIR)/translate
         1131  +	$(TRANSLATE) $(SRCDIR)/json_config.c >$(OBJDIR)/json_config_.c
         1132  +
         1133  +$(OBJDIR)/json_config.o:	$(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h  $(SRCDIR)/config.h
         1134  +	$(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c
         1135  +
         1136  +$(OBJDIR)/json_config.h:	$(OBJDIR)/headers
         1137  +
         1138  +$(OBJDIR)/json_diff_.c:	$(SRCDIR)/json_diff.c $(OBJDIR)/translate
         1139  +	$(TRANSLATE) $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
         1140  +
         1141  +$(OBJDIR)/json_diff.o:	$(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h  $(SRCDIR)/config.h
         1142  +	$(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
         1143  +
         1144  +$(OBJDIR)/json_diff.h:	$(OBJDIR)/headers
         1145  +
         1146  +$(OBJDIR)/json_dir_.c:	$(SRCDIR)/json_dir.c $(OBJDIR)/translate
         1147  +	$(TRANSLATE) $(SRCDIR)/json_dir.c >$(OBJDIR)/json_dir_.c
         1148  +
         1149  +$(OBJDIR)/json_dir.o:	$(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h  $(SRCDIR)/config.h
         1150  +	$(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c
         1151  +
         1152  +$(OBJDIR)/json_dir.h:	$(OBJDIR)/headers
         1153  +
         1154  +$(OBJDIR)/json_finfo_.c:	$(SRCDIR)/json_finfo.c $(OBJDIR)/translate
         1155  +	$(TRANSLATE) $(SRCDIR)/json_finfo.c >$(OBJDIR)/json_finfo_.c
         1156  +
         1157  +$(OBJDIR)/json_finfo.o:	$(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h  $(SRCDIR)/config.h
         1158  +	$(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c
         1159  +
         1160  +$(OBJDIR)/json_finfo.h:	$(OBJDIR)/headers
         1161  +
         1162  +$(OBJDIR)/json_login_.c:	$(SRCDIR)/json_login.c $(OBJDIR)/translate
         1163  +	$(TRANSLATE) $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
         1164  +
         1165  +$(OBJDIR)/json_login.o:	$(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h  $(SRCDIR)/config.h
         1166  +	$(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
         1167  +
         1168  +$(OBJDIR)/json_login.h:	$(OBJDIR)/headers
         1169  +
         1170  +$(OBJDIR)/json_query_.c:	$(SRCDIR)/json_query.c $(OBJDIR)/translate
         1171  +	$(TRANSLATE) $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
         1172  +
         1173  +$(OBJDIR)/json_query.o:	$(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h  $(SRCDIR)/config.h
         1174  +	$(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
         1175  +
         1176  +$(OBJDIR)/json_query.h:	$(OBJDIR)/headers
         1177  +
         1178  +$(OBJDIR)/json_report_.c:	$(SRCDIR)/json_report.c $(OBJDIR)/translate
         1179  +	$(TRANSLATE) $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
         1180  +
         1181  +$(OBJDIR)/json_report.o:	$(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h  $(SRCDIR)/config.h
         1182  +	$(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
         1183  +
         1184  +$(OBJDIR)/json_report.h:	$(OBJDIR)/headers
         1185  +
         1186  +$(OBJDIR)/json_tag_.c:	$(SRCDIR)/json_tag.c $(OBJDIR)/translate
         1187  +	$(TRANSLATE) $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
         1188  +
         1189  +$(OBJDIR)/json_tag.o:	$(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h  $(SRCDIR)/config.h
         1190  +	$(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
         1191  +
         1192  +$(OBJDIR)/json_tag.h:	$(OBJDIR)/headers
         1193  +
         1194  +$(OBJDIR)/json_timeline_.c:	$(SRCDIR)/json_timeline.c $(OBJDIR)/translate
         1195  +	$(TRANSLATE) $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
         1196  +
         1197  +$(OBJDIR)/json_timeline.o:	$(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h  $(SRCDIR)/config.h
         1198  +	$(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
         1199  +
         1200  +$(OBJDIR)/json_timeline.h:	$(OBJDIR)/headers
         1201  +
         1202  +$(OBJDIR)/json_user_.c:	$(SRCDIR)/json_user.c $(OBJDIR)/translate
         1203  +	$(TRANSLATE) $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
         1204  +
         1205  +$(OBJDIR)/json_user.o:	$(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h  $(SRCDIR)/config.h
         1206  +	$(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
         1207  +
         1208  +$(OBJDIR)/json_user.h:	$(OBJDIR)/headers
         1209  +
         1210  +$(OBJDIR)/json_wiki_.c:	$(SRCDIR)/json_wiki.c $(OBJDIR)/translate
         1211  +	$(TRANSLATE) $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
         1212  +
         1213  +$(OBJDIR)/json_wiki.o:	$(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h  $(SRCDIR)/config.h
         1214  +	$(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
         1215  +
         1216  +$(OBJDIR)/json_wiki.h:	$(OBJDIR)/headers
         1217  +
         1218  +$(OBJDIR)/leaf_.c:	$(SRCDIR)/leaf.c $(OBJDIR)/translate
         1219  +	$(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
         1220  +
         1221  +$(OBJDIR)/leaf.o:	$(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h  $(SRCDIR)/config.h
         1222  +	$(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
         1223  +
         1224  +$(OBJDIR)/leaf.h:	$(OBJDIR)/headers
         1225  +
         1226  +$(OBJDIR)/login_.c:	$(SRCDIR)/login.c $(OBJDIR)/translate
         1227  +	$(TRANSLATE) $(SRCDIR)/login.c >$(OBJDIR)/login_.c
         1228  +
         1229  +$(OBJDIR)/login.o:	$(OBJDIR)/login_.c $(OBJDIR)/login.h  $(SRCDIR)/config.h
         1230  +	$(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c
         1231  +
         1232  +$(OBJDIR)/login.h:	$(OBJDIR)/headers
         1233  +
         1234  +$(OBJDIR)/main_.c:	$(SRCDIR)/main.c $(OBJDIR)/translate
         1235  +	$(TRANSLATE) $(SRCDIR)/main.c >$(OBJDIR)/main_.c
         1236  +
         1237  +$(OBJDIR)/main.o:	$(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h
         1238  +	$(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c
         1239  +
         1240  +$(OBJDIR)/main.h:	$(OBJDIR)/headers
         1241  +
         1242  +$(OBJDIR)/manifest_.c:	$(SRCDIR)/manifest.c $(OBJDIR)/translate
         1243  +	$(TRANSLATE) $(SRCDIR)/manifest.c >$(OBJDIR)/manifest_.c
         1244  +
         1245  +$(OBJDIR)/manifest.o:	$(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h  $(SRCDIR)/config.h
         1246  +	$(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c
         1247  +
         1248  +$(OBJDIR)/manifest.h:	$(OBJDIR)/headers
         1249  +
         1250  +$(OBJDIR)/markdown_.c:	$(SRCDIR)/markdown.c $(OBJDIR)/translate
         1251  +	$(TRANSLATE) $(SRCDIR)/markdown.c >$(OBJDIR)/markdown_.c
         1252  +
         1253  +$(OBJDIR)/markdown.o:	$(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h  $(SRCDIR)/config.h
         1254  +	$(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c
         1255  +
         1256  +$(OBJDIR)/markdown.h:	$(OBJDIR)/headers
         1257  +
         1258  +$(OBJDIR)/markdown_html_.c:	$(SRCDIR)/markdown_html.c $(OBJDIR)/translate
         1259  +	$(TRANSLATE) $(SRCDIR)/markdown_html.c >$(OBJDIR)/markdown_html_.c
         1260  +
         1261  +$(OBJDIR)/markdown_html.o:	$(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h  $(SRCDIR)/config.h
         1262  +	$(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
         1263  +
         1264  +$(OBJDIR)/markdown_html.h:	$(OBJDIR)/headers
         1265  +
         1266  +$(OBJDIR)/md5_.c:	$(SRCDIR)/md5.c $(OBJDIR)/translate
         1267  +	$(TRANSLATE) $(SRCDIR)/md5.c >$(OBJDIR)/md5_.c
         1268  +
         1269  +$(OBJDIR)/md5.o:	$(OBJDIR)/md5_.c $(OBJDIR)/md5.h  $(SRCDIR)/config.h
         1270  +	$(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c
         1271  +
         1272  +$(OBJDIR)/md5.h:	$(OBJDIR)/headers
         1273  +
         1274  +$(OBJDIR)/merge_.c:	$(SRCDIR)/merge.c $(OBJDIR)/translate
         1275  +	$(TRANSLATE) $(SRCDIR)/merge.c >$(OBJDIR)/merge_.c
         1276  +
         1277  +$(OBJDIR)/merge.o:	$(OBJDIR)/merge_.c $(OBJDIR)/merge.h  $(SRCDIR)/config.h
         1278  +	$(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c
         1279  +
         1280  +$(OBJDIR)/merge.h:	$(OBJDIR)/headers
         1281  +
         1282  +$(OBJDIR)/merge3_.c:	$(SRCDIR)/merge3.c $(OBJDIR)/translate
         1283  +	$(TRANSLATE) $(SRCDIR)/merge3.c >$(OBJDIR)/merge3_.c
         1284  +
         1285  +$(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
         1286  +	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
         1287  +
         1288  +$(OBJDIR)/merge3.h:	$(OBJDIR)/headers
         1289  +
         1290  +$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
         1291  +	$(TRANSLATE) $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
         1292  +
         1293  +$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
         1294  +	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
         1295  +
         1296  +$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
         1297  +
         1298  +$(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
         1299  +	$(TRANSLATE) $(SRCDIR)/name.c >$(OBJDIR)/name_.c
         1300  +
         1301  +$(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
         1302  +	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c
         1303  +
         1304  +$(OBJDIR)/name.h:	$(OBJDIR)/headers
         1305  +
         1306  +$(OBJDIR)/path_.c:	$(SRCDIR)/path.c $(OBJDIR)/translate
         1307  +	$(TRANSLATE) $(SRCDIR)/path.c >$(OBJDIR)/path_.c
         1308  +
         1309  +$(OBJDIR)/path.o:	$(OBJDIR)/path_.c $(OBJDIR)/path.h  $(SRCDIR)/config.h
         1310  +	$(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c
         1311  +
         1312  +$(OBJDIR)/path.h:	$(OBJDIR)/headers
         1313  +
         1314  +$(OBJDIR)/pivot_.c:	$(SRCDIR)/pivot.c $(OBJDIR)/translate
         1315  +	$(TRANSLATE) $(SRCDIR)/pivot.c >$(OBJDIR)/pivot_.c
         1316  +
         1317  +$(OBJDIR)/pivot.o:	$(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h  $(SRCDIR)/config.h
         1318  +	$(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c
         1319  +
         1320  +$(OBJDIR)/pivot.h:	$(OBJDIR)/headers
         1321  +
         1322  +$(OBJDIR)/popen_.c:	$(SRCDIR)/popen.c $(OBJDIR)/translate
         1323  +	$(TRANSLATE) $(SRCDIR)/popen.c >$(OBJDIR)/popen_.c
         1324  +
         1325  +$(OBJDIR)/popen.o:	$(OBJDIR)/popen_.c $(OBJDIR)/popen.h  $(SRCDIR)/config.h
         1326  +	$(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c
         1327  +
         1328  +$(OBJDIR)/popen.h:	$(OBJDIR)/headers
         1329  +
         1330  +$(OBJDIR)/pqueue_.c:	$(SRCDIR)/pqueue.c $(OBJDIR)/translate
         1331  +	$(TRANSLATE) $(SRCDIR)/pqueue.c >$(OBJDIR)/pqueue_.c
         1332  +
         1333  +$(OBJDIR)/pqueue.o:	$(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h  $(SRCDIR)/config.h
         1334  +	$(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c
         1335  +
         1336  +$(OBJDIR)/pqueue.h:	$(OBJDIR)/headers
         1337  +
         1338  +$(OBJDIR)/printf_.c:	$(SRCDIR)/printf.c $(OBJDIR)/translate
         1339  +	$(TRANSLATE) $(SRCDIR)/printf.c >$(OBJDIR)/printf_.c
         1340  +
         1341  +$(OBJDIR)/printf.o:	$(OBJDIR)/printf_.c $(OBJDIR)/printf.h  $(SRCDIR)/config.h
         1342  +	$(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c
         1343  +
         1344  +$(OBJDIR)/printf.h:	$(OBJDIR)/headers
         1345  +
         1346  +$(OBJDIR)/rebuild_.c:	$(SRCDIR)/rebuild.c $(OBJDIR)/translate
         1347  +	$(TRANSLATE) $(SRCDIR)/rebuild.c >$(OBJDIR)/rebuild_.c
         1348  +
         1349  +$(OBJDIR)/rebuild.o:	$(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h  $(SRCDIR)/config.h
         1350  +	$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
         1351  +
         1352  +$(OBJDIR)/rebuild.h:	$(OBJDIR)/headers
         1353  +
         1354  +$(OBJDIR)/regexp_.c:	$(SRCDIR)/regexp.c $(OBJDIR)/translate
         1355  +	$(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
         1356  +
         1357  +$(OBJDIR)/regexp.o:	$(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h  $(SRCDIR)/config.h
         1358  +	$(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
         1359  +
         1360  +$(OBJDIR)/regexp.h:	$(OBJDIR)/headers
         1361  +
         1362  +$(OBJDIR)/report_.c:	$(SRCDIR)/report.c $(OBJDIR)/translate
         1363  +	$(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
         1364  +
         1365  +$(OBJDIR)/report.o:	$(OBJDIR)/report_.c $(OBJDIR)/report.h  $(SRCDIR)/config.h
         1366  +	$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
         1367  +
         1368  +$(OBJDIR)/report.h:	$(OBJDIR)/headers
         1369  +
         1370  +$(OBJDIR)/rss_.c:	$(SRCDIR)/rss.c $(OBJDIR)/translate
         1371  +	$(TRANSLATE) $(SRCDIR)/rss.c >$(OBJDIR)/rss_.c
         1372  +
         1373  +$(OBJDIR)/rss.o:	$(OBJDIR)/rss_.c $(OBJDIR)/rss.h  $(SRCDIR)/config.h
         1374  +	$(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c
         1375  +
         1376  +$(OBJDIR)/rss.h:	$(OBJDIR)/headers
         1377  +
         1378  +$(OBJDIR)/schema_.c:	$(SRCDIR)/schema.c $(OBJDIR)/translate
         1379  +	$(TRANSLATE) $(SRCDIR)/schema.c >$(OBJDIR)/schema_.c
         1380  +
         1381  +$(OBJDIR)/schema.o:	$(OBJDIR)/schema_.c $(OBJDIR)/schema.h  $(SRCDIR)/config.h
         1382  +	$(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c
         1383  +
         1384  +$(OBJDIR)/schema.h:	$(OBJDIR)/headers
         1385  +
         1386  +$(OBJDIR)/search_.c:	$(SRCDIR)/search.c $(OBJDIR)/translate
         1387  +	$(TRANSLATE) $(SRCDIR)/search.c >$(OBJDIR)/search_.c
         1388  +
         1389  +$(OBJDIR)/search.o:	$(OBJDIR)/search_.c $(OBJDIR)/search.h  $(SRCDIR)/config.h
         1390  +	$(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c
         1391  +
         1392  +$(OBJDIR)/search.h:	$(OBJDIR)/headers
         1393  +
         1394  +$(OBJDIR)/setup_.c:	$(SRCDIR)/setup.c $(OBJDIR)/translate
         1395  +	$(TRANSLATE) $(SRCDIR)/setup.c >$(OBJDIR)/setup_.c
         1396  +
         1397  +$(OBJDIR)/setup.o:	$(OBJDIR)/setup_.c $(OBJDIR)/setup.h  $(SRCDIR)/config.h
         1398  +	$(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c
         1399  +
         1400  +$(OBJDIR)/setup.h:	$(OBJDIR)/headers
         1401  +
         1402  +$(OBJDIR)/sha1_.c:	$(SRCDIR)/sha1.c $(OBJDIR)/translate
         1403  +	$(TRANSLATE) $(SRCDIR)/sha1.c >$(OBJDIR)/sha1_.c
         1404  +
         1405  +$(OBJDIR)/sha1.o:	$(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h  $(SRCDIR)/config.h
         1406  +	$(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c
         1407  +
         1408  +$(OBJDIR)/sha1.h:	$(OBJDIR)/headers
         1409  +
         1410  +$(OBJDIR)/shun_.c:	$(SRCDIR)/shun.c $(OBJDIR)/translate
         1411  +	$(TRANSLATE) $(SRCDIR)/shun.c >$(OBJDIR)/shun_.c
         1412  +
         1413  +$(OBJDIR)/shun.o:	$(OBJDIR)/shun_.c $(OBJDIR)/shun.h  $(SRCDIR)/config.h
         1414  +	$(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c
         1415  +
         1416  +$(OBJDIR)/shun.h:	$(OBJDIR)/headers
         1417  +
         1418  +$(OBJDIR)/skins_.c:	$(SRCDIR)/skins.c $(OBJDIR)/translate
         1419  +	$(TRANSLATE) $(SRCDIR)/skins.c >$(OBJDIR)/skins_.c
         1420  +
         1421  +$(OBJDIR)/skins.o:	$(OBJDIR)/skins_.c $(OBJDIR)/skins.h  $(SRCDIR)/config.h
         1422  +	$(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c
         1423  +
         1424  +$(OBJDIR)/skins.h:	$(OBJDIR)/headers
         1425  +
         1426  +$(OBJDIR)/sqlcmd_.c:	$(SRCDIR)/sqlcmd.c $(OBJDIR)/translate
         1427  +	$(TRANSLATE) $(SRCDIR)/sqlcmd.c >$(OBJDIR)/sqlcmd_.c
         1428  +
         1429  +$(OBJDIR)/sqlcmd.o:	$(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h  $(SRCDIR)/config.h
         1430  +	$(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c
         1431  +
         1432  +$(OBJDIR)/sqlcmd.h:	$(OBJDIR)/headers
         1433  +
         1434  +$(OBJDIR)/stash_.c:	$(SRCDIR)/stash.c $(OBJDIR)/translate
         1435  +	$(TRANSLATE) $(SRCDIR)/stash.c >$(OBJDIR)/stash_.c
         1436  +
         1437  +$(OBJDIR)/stash.o:	$(OBJDIR)/stash_.c $(OBJDIR)/stash.h  $(SRCDIR)/config.h
         1438  +	$(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c
         1439  +
         1440  +$(OBJDIR)/stash.h:	$(OBJDIR)/headers
         1441  +
         1442  +$(OBJDIR)/stat_.c:	$(SRCDIR)/stat.c $(OBJDIR)/translate
         1443  +	$(TRANSLATE) $(SRCDIR)/stat.c >$(OBJDIR)/stat_.c
         1444  +
         1445  +$(OBJDIR)/stat.o:	$(OBJDIR)/stat_.c $(OBJDIR)/stat.h  $(SRCDIR)/config.h
         1446  +	$(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c
         1447  +
         1448  +$(OBJDIR)/stat.h:	$(OBJDIR)/headers
         1449  +
         1450  +$(OBJDIR)/style_.c:	$(SRCDIR)/style.c $(OBJDIR)/translate
         1451  +	$(TRANSLATE) $(SRCDIR)/style.c >$(OBJDIR)/style_.c
         1452  +
         1453  +$(OBJDIR)/style.o:	$(OBJDIR)/style_.c $(OBJDIR)/style.h  $(SRCDIR)/config.h
         1454  +	$(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c
         1455  +
         1456  +$(OBJDIR)/style.h:	$(OBJDIR)/headers
         1457  +
         1458  +$(OBJDIR)/sync_.c:	$(SRCDIR)/sync.c $(OBJDIR)/translate
         1459  +	$(TRANSLATE) $(SRCDIR)/sync.c >$(OBJDIR)/sync_.c
         1460  +
         1461  +$(OBJDIR)/sync.o:	$(OBJDIR)/sync_.c $(OBJDIR)/sync.h  $(SRCDIR)/config.h
         1462  +	$(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c
         1463  +
         1464  +$(OBJDIR)/sync.h:	$(OBJDIR)/headers
         1465  +
         1466  +$(OBJDIR)/tag_.c:	$(SRCDIR)/tag.c $(OBJDIR)/translate
         1467  +	$(TRANSLATE) $(SRCDIR)/tag.c >$(OBJDIR)/tag_.c
         1468  +
         1469  +$(OBJDIR)/tag.o:	$(OBJDIR)/tag_.c $(OBJDIR)/tag.h  $(SRCDIR)/config.h
         1470  +	$(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c
         1471  +
         1472  +$(OBJDIR)/tag.h:	$(OBJDIR)/headers
         1473  +
         1474  +$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(OBJDIR)/translate
         1475  +	$(TRANSLATE) $(SRCDIR)/tar.c >$(OBJDIR)/tar_.c
         1476  +
         1477  +$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h  $(SRCDIR)/config.h
         1478  +	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
         1479  +
         1480  +$(OBJDIR)/tar.h:	$(OBJDIR)/headers
         1481  +
         1482  +$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(OBJDIR)/translate
         1483  +	$(TRANSLATE) $(SRCDIR)/th_main.c >$(OBJDIR)/th_main_.c
         1484  +
         1485  +$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h  $(SRCDIR)/config.h
         1486  +	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c
         1487  +
         1488  +$(OBJDIR)/th_main.h:	$(OBJDIR)/headers
         1489  +
         1490  +$(OBJDIR)/timeline_.c:	$(SRCDIR)/timeline.c $(OBJDIR)/translate
         1491  +	$(TRANSLATE) $(SRCDIR)/timeline.c >$(OBJDIR)/timeline_.c
         1492  +
         1493  +$(OBJDIR)/timeline.o:	$(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h  $(SRCDIR)/config.h
         1494  +	$(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c
         1495  +
         1496  +$(OBJDIR)/timeline.h:	$(OBJDIR)/headers
         1497  +
         1498  +$(OBJDIR)/tkt_.c:	$(SRCDIR)/tkt.c $(OBJDIR)/translate
         1499  +	$(TRANSLATE) $(SRCDIR)/tkt.c >$(OBJDIR)/tkt_.c
         1500  +
         1501  +$(OBJDIR)/tkt.o:	$(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h  $(SRCDIR)/config.h
         1502  +	$(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c
         1503  +
         1504  +$(OBJDIR)/tkt.h:	$(OBJDIR)/headers
         1505  +
         1506  +$(OBJDIR)/tktsetup_.c:	$(SRCDIR)/tktsetup.c $(OBJDIR)/translate
         1507  +	$(TRANSLATE) $(SRCDIR)/tktsetup.c >$(OBJDIR)/tktsetup_.c
         1508  +
         1509  +$(OBJDIR)/tktsetup.o:	$(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h  $(SRCDIR)/config.h
         1510  +	$(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c
         1511  +
         1512  +$(OBJDIR)/tktsetup.h:	$(OBJDIR)/headers
         1513  +
         1514  +$(OBJDIR)/undo_.c:	$(SRCDIR)/undo.c $(OBJDIR)/translate
         1515  +	$(TRANSLATE) $(SRCDIR)/undo.c >$(OBJDIR)/undo_.c
         1516  +
         1517  +$(OBJDIR)/undo.o:	$(OBJDIR)/undo_.c $(OBJDIR)/undo.h  $(SRCDIR)/config.h
         1518  +	$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
         1519  +
         1520  +$(OBJDIR)/undo.h:	$(OBJDIR)/headers
         1521  +
         1522  +$(OBJDIR)/unicode_.c:	$(SRCDIR)/unicode.c $(OBJDIR)/translate
         1523  +	$(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
         1524  +
         1525  +$(OBJDIR)/unicode.o:	$(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h  $(SRCDIR)/config.h
         1526  +	$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
         1527  +
         1528  +$(OBJDIR)/unicode.h:	$(OBJDIR)/headers
         1529  +
         1530  +$(OBJDIR)/update_.c:	$(SRCDIR)/update.c $(OBJDIR)/translate
         1531  +	$(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
         1532  +
         1533  +$(OBJDIR)/update.o:	$(OBJDIR)/update_.c $(OBJDIR)/update.h  $(SRCDIR)/config.h
         1534  +	$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
         1535  +
         1536  +$(OBJDIR)/update.h:	$(OBJDIR)/headers
         1537  +
         1538  +$(OBJDIR)/url_.c:	$(SRCDIR)/url.c $(OBJDIR)/translate
         1539  +	$(TRANSLATE) $(SRCDIR)/url.c >$(OBJDIR)/url_.c
         1540  +
         1541  +$(OBJDIR)/url.o:	$(OBJDIR)/url_.c $(OBJDIR)/url.h  $(SRCDIR)/config.h
         1542  +	$(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c
         1543  +
         1544  +$(OBJDIR)/url.h:	$(OBJDIR)/headers
         1545  +
         1546  +$(OBJDIR)/user_.c:	$(SRCDIR)/user.c $(OBJDIR)/translate
         1547  +	$(TRANSLATE) $(SRCDIR)/user.c >$(OBJDIR)/user_.c
         1548  +
         1549  +$(OBJDIR)/user.o:	$(OBJDIR)/user_.c $(OBJDIR)/user.h  $(SRCDIR)/config.h
         1550  +	$(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c
         1551  +
         1552  +$(OBJDIR)/user.h:	$(OBJDIR)/headers
         1553  +
         1554  +$(OBJDIR)/utf8_.c:	$(SRCDIR)/utf8.c $(OBJDIR)/translate
         1555  +	$(TRANSLATE) $(SRCDIR)/utf8.c >$(OBJDIR)/utf8_.c
         1556  +
         1557  +$(OBJDIR)/utf8.o:	$(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h  $(SRCDIR)/config.h
         1558  +	$(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c
         1559  +
         1560  +$(OBJDIR)/utf8.h:	$(OBJDIR)/headers
         1561  +
         1562  +$(OBJDIR)/verify_.c:	$(SRCDIR)/verify.c $(OBJDIR)/translate
         1563  +	$(TRANSLATE) $(SRCDIR)/verify.c >$(OBJDIR)/verify_.c
         1564  +
         1565  +$(OBJDIR)/verify.o:	$(OBJDIR)/verify_.c $(OBJDIR)/verify.h  $(SRCDIR)/config.h
         1566  +	$(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c
         1567  +
         1568  +$(OBJDIR)/verify.h:	$(OBJDIR)/headers
         1569  +
         1570  +$(OBJDIR)/vfile_.c:	$(SRCDIR)/vfile.c $(OBJDIR)/translate
         1571  +	$(TRANSLATE) $(SRCDIR)/vfile.c >$(OBJDIR)/vfile_.c
         1572  +
         1573  +$(OBJDIR)/vfile.o:	$(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h  $(SRCDIR)/config.h
         1574  +	$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
         1575  +
         1576  +$(OBJDIR)/vfile.h:	$(OBJDIR)/headers
         1577  +
         1578  +$(OBJDIR)/wiki_.c:	$(SRCDIR)/wiki.c $(OBJDIR)/translate
         1579  +	$(TRANSLATE) $(SRCDIR)/wiki.c >$(OBJDIR)/wiki_.c
         1580  +
         1581  +$(OBJDIR)/wiki.o:	$(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h  $(SRCDIR)/config.h
         1582  +	$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
         1583  +
         1584  +$(OBJDIR)/wiki.h:	$(OBJDIR)/headers
         1585  +
         1586  +$(OBJDIR)/wikiformat_.c:	$(SRCDIR)/wikiformat.c $(OBJDIR)/translate
         1587  +	$(TRANSLATE) $(SRCDIR)/wikiformat.c >$(OBJDIR)/wikiformat_.c
         1588  +
         1589  +$(OBJDIR)/wikiformat.o:	$(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h  $(SRCDIR)/config.h
         1590  +	$(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c
         1591  +
         1592  +$(OBJDIR)/wikiformat.h:	$(OBJDIR)/headers
         1593  +
         1594  +$(OBJDIR)/winhttp_.c:	$(SRCDIR)/winhttp.c $(OBJDIR)/translate
         1595  +	$(TRANSLATE) $(SRCDIR)/winhttp.c >$(OBJDIR)/winhttp_.c
         1596  +
         1597  +$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h  $(SRCDIR)/config.h
         1598  +	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c
         1599  +
         1600  +$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers
         1601  +
         1602  +$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(OBJDIR)/translate
         1603  +	$(TRANSLATE) $(SRCDIR)/wysiwyg.c >$(OBJDIR)/wysiwyg_.c
         1604  +
         1605  +$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h  $(SRCDIR)/config.h
         1606  +	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c
         1607  +
         1608  +$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers
         1609  +
         1610  +$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(OBJDIR)/translate
         1611  +	$(TRANSLATE) $(SRCDIR)/xfer.c >$(OBJDIR)/xfer_.c
         1612  +
         1613  +$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h  $(SRCDIR)/config.h
         1614  +	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c
         1615  +
         1616  +$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
         1617  +
         1618  +$(OBJDIR)/xfersetup_.c:	$(SRCDIR)/xfersetup.c $(OBJDIR)/translate
         1619  +	$(TRANSLATE) $(SRCDIR)/xfersetup.c >$(OBJDIR)/xfersetup_.c
         1620  +
         1621  +$(OBJDIR)/xfersetup.o:	$(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h  $(SRCDIR)/config.h
         1622  +	$(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c
         1623  +
         1624  +$(OBJDIR)/xfersetup.h:	$(OBJDIR)/headers
         1625  +
         1626  +$(OBJDIR)/zip_.c:	$(SRCDIR)/zip.c $(OBJDIR)/translate
         1627  +	$(TRANSLATE) $(SRCDIR)/zip.c >$(OBJDIR)/zip_.c
         1628  +
         1629  +$(OBJDIR)/zip.o:	$(OBJDIR)/zip_.c $(OBJDIR)/zip.h  $(SRCDIR)/config.h
         1630  +	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
         1631  +
         1632  +$(OBJDIR)/zip.h:	$(OBJDIR)/headers
         1633  +
         1634  +$(OBJDIR)/sqlite3.o:	$(SRCDIR)/sqlite3.c
         1635  +	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
         1636  +
         1637  +$(OBJDIR)/cson_amalgamation.o:	$(SRCDIR)/cson_amalgamation.c
         1638  +	$(XTCC)  -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
         1639  +
         1640  +$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
         1641  +
         1642  +$(OBJDIR)/shell.o:	$(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
         1643  +	$(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
         1644  +
         1645  +$(OBJDIR)/th.o:	$(SRCDIR)/th.c
         1646  +	$(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
         1647  +
         1648  +$(OBJDIR)/th_lang.o:	$(SRCDIR)/th_lang.c
         1649  +	$(XTCC) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
         1650  +
         1651  +ifdef FOSSIL_ENABLE_TCL
         1652  +$(OBJDIR)/th_tcl.o:	$(SRCDIR)/th_tcl.c
         1653  +	$(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
         1654  +endif

Added win/Makefile.mingw.mistachkin.

            1  +#!/usr/bin/make
            2  +#
            3  +##############################################################################
            4  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            5  +##############################################################################
            6  +#
            7  +# This file is automatically generated.  Instead of editing this
            8  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
            9  +# to regenerate this file.
           10  +#
           11  +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using
           12  +# MinGW or MinGW-w64.
           13  +#
           14  +
           15  +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers.
           16  +#    By default, this is an empty string (i.e. use the native compiler).
           17  +#
           18  +PREFIX =
           19  +# PREFIX = mingw32-
           20  +# PREFIX = i686-pc-mingw32-
           21  +# PREFIX = i686-w64-mingw32-
           22  +# PREFIX = x86_64-w64-mingw32-
           23  +
           24  +#### The toplevel directory of the source tree.  Fossil can be built
           25  +#    in a directory that is separate from the source tree.  Just change
           26  +#    the following to point from the build directory to the src/ folder.
           27  +#
           28  +SRCDIR = src
           29  +
           30  +#### The directory into which object code files should be written.
           31  +#
           32  +OBJDIR = wbld
           33  +
           34  +#### C Compiler and options for use in building executables that
           35  +#    will run on the platform that is doing the build.  This is used
           36  +#    to compile code-generator programs as part of the build process.
           37  +#    See TCC below for the C compiler for building the finished binary.
           38  +#
           39  +BCC = gcc
           40  +
           41  +#### Enable compiling with debug symbols (much larger binary)
           42  +#
           43  +# FOSSIL_ENABLE_SYMBOLS = 1
           44  +
           45  +#### Enable JSON (http://www.json.org) support using "cson"
           46  +#
           47  +FOSSIL_ENABLE_JSON = 1
           48  +
           49  +#### Enable markdown support
           50  +#
           51  +FOSSIL_ENABLE_MARKDOWN = 1
           52  +
           53  +#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
           54  +#
           55  +FOSSIL_ENABLE_SSL = 1
           56  +
           57  +#### Enable scripting support via Tcl/Tk
           58  +#
           59  +FOSSIL_ENABLE_TCL = 1
           60  +
           61  +#### Load Tcl using the stubs mechanism
           62  +#
           63  +FOSSIL_ENABLE_TCL_STUBS = 1
           64  +
           65  +#### Use the Tcl source directory instead of the install directory?
           66  +#    This is useful when Tcl has been compiled statically with MinGW.
           67  +#
           68  +FOSSIL_TCL_SOURCE = 1
           69  +
           70  +#### Check if the workaround for the MinGW command line handling needs to
           71  +#    be enabled by default.
           72  +#
           73  +ifndef BROKEN_MINGW_CMDLINE
           74  +ifeq (,$(findstring w64-mingw32,$(PREFIX)))
           75  +BROKEN_MINGW_CMDLINE = 1
           76  +endif
           77  +endif
           78  +
           79  +#### The directories where the zlib include and library files are located.
           80  +#
           81  +ZINCDIR = $(SRCDIR)/../compat/zlib
           82  +ZLIBDIR = $(SRCDIR)/../compat/zlib
           83  +
           84  +#### The directories where the OpenSSL include and library files are located.
           85  +#    The recommended usage here is to use the Sysinternals junction tool
           86  +#    to create a hard link between an "openssl-1.x" sub-directory of the
           87  +#    Fossil source code directory and the target OpenSSL source directory.
           88  +#
           89  +OPENSSLINCDIR = $(SRCDIR)/../openssl-1.0.1c/include
           90  +OPENSSLLIBDIR = $(SRCDIR)/../openssl-1.0.1c
           91  +
           92  +#### Either the directory where the Tcl library is installed or the Tcl
           93  +#    source code directory resides (depending on the value of the macro
           94  +#    FOSSIL_TCL_SOURCE).  If this points to the Tcl install directory,
           95  +#    this directory must have "include" and "lib" sub-directories.  If
           96  +#    this points to the Tcl source code directory, this directory must
           97  +#    have "generic" and "win" sub-directories.  The recommended usage
           98  +#    here is to use the Sysinternals junction tool to create a hard
           99  +#    link between a "tcl-8.x" sub-directory of the Fossil source code
          100  +#    directory and the target Tcl directory.  This removes the need to
          101  +#    hard-code the necessary paths in this Makefile.
          102  +#
          103  +TCLDIR = $(SRCDIR)/../tcl-8.6
          104  +
          105  +#### The Tcl source code directory.  This defaults to the same value as
          106  +#    TCLDIR macro (above), which may not be correct.  This value will
          107  +#    only be used if the FOSSIL_TCL_SOURCE macro is defined.
          108  +#
          109  +TCLSRCDIR = $(TCLDIR)
          110  +
          111  +#### The Tcl include and library directories.  These values will only be
          112  +#    used if the FOSSIL_TCL_SOURCE macro is not defined.
          113  +#
          114  +TCLINCDIR = $(TCLDIR)/include
          115  +TCLLIBDIR = $(TCLDIR)/lib
          116  +
          117  +#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
          118  +#
          119  +ifdef FOSSIL_ENABLE_TCL_STUBS
          120  +LIBTCL = -ltclstub86
          121  +else
          122  +LIBTCL = -ltcl86
          123  +endif
          124  +
          125  +#### C Compile and options for use in building executables that
          126  +#    will run on the target platform.  This is usually the same
          127  +#    as BCC, unless you are cross-compiling.  This C compiler builds
          128  +#    the finished binary for fossil.  The BCC compiler above is used
          129  +#    for building intermediate code-generator tools.
          130  +#
          131  +TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
          132  +
          133  +#### Add the necessary command line options to build with debugging
          134  +#    symbols, if enabled.
          135  +#
          136  +ifdef FOSSIL_ENABLE_SYMBOLS
          137  +TCC += -g
          138  +endif
          139  +
          140  +#### Compile resources for use in building executables that will run
          141  +#    on the target platform.
          142  +#
          143  +RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
          144  +
          145  +# With HTTPS support
          146  +ifdef FOSSIL_ENABLE_SSL
          147  +TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
          148  +RCC += -I$(OPENSSLINCDIR)
          149  +endif
          150  +
          151  +# With Tcl support
          152  +ifdef FOSSIL_ENABLE_TCL
          153  +ifdef FOSSIL_TCL_SOURCE
          154  +TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          155  +RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
          156  +else
          157  +TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
          158  +RCC += -I$(TCLINCDIR)
          159  +endif
          160  +endif
          161  +
          162  +# With MinGW command line handling workaround
          163  +ifdef BROKEN_MINGW_CMDLINE
          164  +TCC += -DBROKEN_MINGW_CMDLINE=1
          165  +RCC += -DBROKEN_MINGW_CMDLINE=1
          166  +endif
          167  +
          168  +# With HTTPS support
          169  +ifdef FOSSIL_ENABLE_SSL
          170  +TCC += -DFOSSIL_ENABLE_SSL=1
          171  +RCC += -DFOSSIL_ENABLE_SSL=1
          172  +endif
          173  +
          174  +# With Tcl support
          175  +ifdef FOSSIL_ENABLE_TCL
          176  +TCC += -DFOSSIL_ENABLE_TCL=1
          177  +RCC += -DFOSSIL_ENABLE_TCL=1
          178  +# Either statically linked or via stubs
          179  +ifdef FOSSIL_ENABLE_TCL_STUBS
          180  +TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          181  +RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
          182  +else
          183  +TCC += -DSTATIC_BUILD
          184  +RCC += -DSTATIC_BUILD
          185  +endif
          186  +endif
          187  +
          188  +# With JSON support
          189  +ifdef FOSSIL_ENABLE_JSON
          190  +TCC += -DFOSSIL_ENABLE_JSON=1
          191  +RCC += -DFOSSIL_ENABLE_JSON=1
          192  +endif
          193  +
          194  +# With markdown support
          195  +ifdef FOSSIL_ENABLE_MARKDOWN
          196  +TCC += -DFOSSIL_ENABLE_MARKDOWN=1
          197  +RCC += -DFOSSIL_ENABLE_MARKDOWN=1
          198  +endif
          199  +
          200  +#### We add the -static option here so that we can build a static
          201  +#    executable that will run in a chroot jail.
          202  +#
          203  +LIB = -static
          204  +
          205  +# MinGW: If available, use the Unicode capable runtime startup code.
          206  +ifndef BROKEN_MINGW_CMDLINE
          207  +LIB += -municode
          208  +endif
          209  +
          210  +# OpenSSL: Add the necessary libraries required, if enabled.
          211  +ifdef FOSSIL_ENABLE_SSL
          212  +LIB += -lssl -lcrypto -lgdi32
          213  +endif
          214  +
          215  +# Tcl: Add the necessary libraries required, if enabled.
          216  +ifdef FOSSIL_ENABLE_TCL
          217  +LIB += $(LIBTCL)
          218  +endif
          219  +
          220  +#### Extra arguments for linking the finished binary.  Fossil needs
          221  +#    to link against the Z-Lib compression library.  There are no
          222  +#    other mandatory dependencies.
          223  +#
          224  +LIB += -lmingwex -lz
          225  +
          226  +#### These libraries MUST appear in the same order as they do for Tcl
          227  +#    or linking with it will not work (exact reason unknown).
          228  +#
          229  +ifdef FOSSIL_ENABLE_TCL
          230  +ifdef FOSSIL_ENABLE_TCL_STUBS
          231  +LIB += -lkernel32 -lws2_32
          232  +else
          233  +LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
          234  +endif
          235  +else
          236  +LIB += -lkernel32 -lws2_32
          237  +endif
          238  +
          239  +#### Tcl shell for use in running the fossil test suite.  This is only
          240  +#    used for testing.
          241  +#
          242  +TCLSH = tclsh
          243  +
          244  +#### Nullsoft installer MakeNSIS location
          245  +#
          246  +MAKENSIS = "$(ProgramFiles)\NSIS\MakeNSIS.exe"
          247  +
          248  +#### Include a configuration file that can override any one of these settings.
          249  +#
          250  +-include config.w32
          251  +
          252  +# STOP HERE
          253  +# You should not need to change anything below this line
          254  +#--------------------------------------------------------
          255  +XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
          256  +
          257  +SRC = \
          258  +  $(SRCDIR)/add.c \
          259  +  $(SRCDIR)/allrepo.c \
          260  +  $(SRCDIR)/attach.c \
          261  +  $(SRCDIR)/bag.c \
          262  +  $(SRCDIR)/bisect.c \
          263  +  $(SRCDIR)/blob.c \
          264  +  $(SRCDIR)/branch.c \
          265  +  $(SRCDIR)/browse.c \
          266  +  $(SRCDIR)/captcha.c \
          267  +  $(SRCDIR)/cgi.c \
          268  +  $(SRCDIR)/checkin.c \
          269  +  $(SRCDIR)/checkout.c \
          270  +  $(SRCDIR)/clearsign.c \
          271  +  $(SRCDIR)/clone.c \
          272  +  $(SRCDIR)/comformat.c \
          273  +  $(SRCDIR)/configure.c \
          274  +  $(SRCDIR)/content.c \
          275  +  $(SRCDIR)/db.c \
          276  +  $(SRCDIR)/delta.c \
          277  +  $(SRCDIR)/deltacmd.c \
          278  +  $(SRCDIR)/descendants.c \
          279  +  $(SRCDIR)/diff.c \
          280  +  $(SRCDIR)/diffcmd.c \
          281  +  $(SRCDIR)/doc.c \
          282  +  $(SRCDIR)/encode.c \
          283  +  $(SRCDIR)/event.c \
          284  +  $(SRCDIR)/export.c \
          285  +  $(SRCDIR)/file.c \
          286  +  $(SRCDIR)/finfo.c \
          287  +  $(SRCDIR)/glob.c \
          288  +  $(SRCDIR)/graph.c \
          289  +  $(SRCDIR)/gzip.c \
          290  +  $(SRCDIR)/http.c \
          291  +  $(SRCDIR)/http_socket.c \
          292  +  $(SRCDIR)/http_ssl.c \
          293  +  $(SRCDIR)/http_transport.c \
          294  +  $(SRCDIR)/import.c \
          295  +  $(SRCDIR)/info.c \
          296  +  $(SRCDIR)/json.c \
          297  +  $(SRCDIR)/json_artifact.c \
          298  +  $(SRCDIR)/json_branch.c \
          299  +  $(SRCDIR)/json_config.c \
          300  +  $(SRCDIR)/json_diff.c \
          301  +  $(SRCDIR)/json_dir.c \
          302  +  $(SRCDIR)/json_finfo.c \
          303  +  $(SRCDIR)/json_login.c \
          304  +  $(SRCDIR)/json_query.c \
          305  +  $(SRCDIR)/json_report.c \
          306  +  $(SRCDIR)/json_tag.c \
          307  +  $(SRCDIR)/json_timeline.c \
          308  +  $(SRCDIR)/json_user.c \
          309  +  $(SRCDIR)/json_wiki.c \
          310  +  $(SRCDIR)/leaf.c \
          311  +  $(SRCDIR)/login.c \
          312  +  $(SRCDIR)/main.c \
          313  +  $(SRCDIR)/manifest.c \
          314  +  $(SRCDIR)/markdown.c \
          315  +  $(SRCDIR)/markdown_html.c \
          316  +  $(SRCDIR)/md5.c \
          317  +  $(SRCDIR)/merge.c \
          318  +  $(SRCDIR)/merge3.c \
          319  +  $(SRCDIR)/moderate.c \
          320  +  $(SRCDIR)/name.c \
          321  +  $(SRCDIR)/path.c \
          322  +  $(SRCDIR)/pivot.c \
          323  +  $(SRCDIR)/popen.c \
          324  +  $(SRCDIR)/pqueue.c \
          325  +  $(SRCDIR)/printf.c \
          326  +  $(SRCDIR)/rebuild.c \
          327  +  $(SRCDIR)/regexp.c \
          328  +  $(SRCDIR)/report.c \
          329  +  $(SRCDIR)/rss.c \
          330  +  $(SRCDIR)/schema.c \
          331  +  $(SRCDIR)/search.c \
          332  +  $(SRCDIR)/setup.c \
          333  +  $(SRCDIR)/sha1.c \
          334  +  $(SRCDIR)/shun.c \
          335  +  $(SRCDIR)/skins.c \
          336  +  $(SRCDIR)/sqlcmd.c \
          337  +  $(SRCDIR)/stash.c \
          338  +  $(SRCDIR)/stat.c \
          339  +  $(SRCDIR)/style.c \
          340  +  $(SRCDIR)/sync.c \
          341  +  $(SRCDIR)/tag.c \
          342  +  $(SRCDIR)/tar.c \
          343  +  $(SRCDIR)/th_main.c \
          344  +  $(SRCDIR)/timeline.c \
          345  +  $(SRCDIR)/tkt.c \
          346  +  $(SRCDIR)/tktsetup.c \
          347  +  $(SRCDIR)/undo.c \
          348  +  $(SRCDIR)/unicode.c \
          349  +  $(SRCDIR)/update.c \
          350  +  $(SRCDIR)/url.c \
          351  +  $(SRCDIR)/user.c \
          352  +  $(SRCDIR)/utf8.c \
          353  +  $(SRCDIR)/verify.c \
          354  +  $(SRCDIR)/vfile.c \
          355  +  $(SRCDIR)/wiki.c \
          356  +  $(SRCDIR)/wikiformat.c \
          357  +  $(SRCDIR)/winhttp.c \
          358  +  $(SRCDIR)/wysiwyg.c \
          359  +  $(SRCDIR)/xfer.c \
          360  +  $(SRCDIR)/xfersetup.c \
          361  +  $(SRCDIR)/zip.c
          362  +
          363  +TRANS_SRC = \
          364  +  $(OBJDIR)/add_.c \
          365  +  $(OBJDIR)/allrepo_.c \
          366  +  $(OBJDIR)/attach_.c \
          367  +  $(OBJDIR)/bag_.c \
          368  +  $(OBJDIR)/bisect_.c \
          369  +  $(OBJDIR)/blob_.c \
          370  +  $(OBJDIR)/branch_.c \
          371  +  $(OBJDIR)/browse_.c \
          372  +  $(OBJDIR)/captcha_.c \
          373  +  $(OBJDIR)/cgi_.c \
          374  +  $(OBJDIR)/checkin_.c \
          375  +  $(OBJDIR)/checkout_.c \
          376  +  $(OBJDIR)/clearsign_.c \
          377  +  $(OBJDIR)/clone_.c \
          378  +  $(OBJDIR)/comformat_.c \
          379  +  $(OBJDIR)/configure_.c \
          380  +  $(OBJDIR)/content_.c \
          381  +  $(OBJDIR)/db_.c \
          382  +  $(OBJDIR)/delta_.c \
          383  +  $(OBJDIR)/deltacmd_.c \
          384  +  $(OBJDIR)/descendants_.c \
          385  +  $(OBJDIR)/diff_.c \
          386  +  $(OBJDIR)/diffcmd_.c \
          387  +  $(OBJDIR)/doc_.c \
          388  +  $(OBJDIR)/encode_.c \
          389  +  $(OBJDIR)/event_.c \
          390  +  $(OBJDIR)/export_.c \
          391  +  $(OBJDIR)/file_.c \
          392  +  $(OBJDIR)/finfo_.c \
          393  +  $(OBJDIR)/glob_.c \
          394  +  $(OBJDIR)/graph_.c \
          395  +  $(OBJDIR)/gzip_.c \
          396  +  $(OBJDIR)/http_.c \
          397  +  $(OBJDIR)/http_socket_.c \
          398  +  $(OBJDIR)/http_ssl_.c \
          399  +  $(OBJDIR)/http_transport_.c \
          400  +  $(OBJDIR)/import_.c \
          401  +  $(OBJDIR)/info_.c \
          402  +  $(OBJDIR)/json_.c \
          403  +  $(OBJDIR)/json_artifact_.c \
          404  +  $(OBJDIR)/json_branch_.c \
          405  +  $(OBJDIR)/json_config_.c \
          406  +  $(OBJDIR)/json_diff_.c \
          407  +  $(OBJDIR)/json_dir_.c \
          408  +  $(OBJDIR)/json_finfo_.c \
          409  +  $(OBJDIR)/json_login_.c \
          410  +  $(OBJDIR)/json_query_.c \
          411  +  $(OBJDIR)/json_report_.c \
          412  +  $(OBJDIR)/json_tag_.c \
          413  +  $(OBJDIR)/json_timeline_.c \
          414  +  $(OBJDIR)/json_user_.c \
          415  +  $(OBJDIR)/json_wiki_.c \
          416  +  $(OBJDIR)/leaf_.c \
          417  +  $(OBJDIR)/login_.c \
          418  +  $(OBJDIR)/main_.c \
          419  +  $(OBJDIR)/manifest_.c \
          420  +  $(OBJDIR)/markdown_.c \
          421  +  $(OBJDIR)/markdown_html_.c \
          422  +  $(OBJDIR)/md5_.c \
          423  +  $(OBJDIR)/merge_.c \
          424  +  $(OBJDIR)/merge3_.c \
          425  +  $(OBJDIR)/moderate_.c \
          426  +  $(OBJDIR)/name_.c \
          427  +  $(OBJDIR)/path_.c \
          428  +  $(OBJDIR)/pivot_.c \
          429  +  $(OBJDIR)/popen_.c \
          430  +  $(OBJDIR)/pqueue_.c \
          431  +  $(OBJDIR)/printf_.c \
          432  +  $(OBJDIR)/rebuild_.c \
          433  +  $(OBJDIR)/regexp_.c \
          434  +  $(OBJDIR)/report_.c \
          435  +  $(OBJDIR)/rss_.c \
          436  +  $(OBJDIR)/schema_.c \
          437  +  $(OBJDIR)/search_.c \
          438  +  $(OBJDIR)/setup_.c \
          439  +  $(OBJDIR)/sha1_.c \
          440  +  $(OBJDIR)/shun_.c \
          441  +  $(OBJDIR)/skins_.c \
          442  +  $(OBJDIR)/sqlcmd_.c \
          443  +  $(OBJDIR)/stash_.c \
          444  +  $(OBJDIR)/stat_.c \
          445  +  $(OBJDIR)/style_.c \
          446  +  $(OBJDIR)/sync_.c \
          447  +  $(OBJDIR)/tag_.c \
          448  +  $(OBJDIR)/tar_.c \
          449  +  $(OBJDIR)/th_main_.c \
          450  +  $(OBJDIR)/timeline_.c \
          451  +  $(OBJDIR)/tkt_.c \
          452  +  $(OBJDIR)/tktsetup_.c \
          453  +  $(OBJDIR)/undo_.c \
          454  +  $(OBJDIR)/unicode_.c \
          455  +  $(OBJDIR)/update_.c \
          456  +  $(OBJDIR)/url_.c \
          457  +  $(OBJDIR)/user_.c \
          458  +  $(OBJDIR)/utf8_.c \
          459  +  $(OBJDIR)/verify_.c \
          460  +  $(OBJDIR)/vfile_.c \
          461  +  $(OBJDIR)/wiki_.c \
          462  +  $(OBJDIR)/wikiformat_.c \
          463  +  $(OBJDIR)/winhttp_.c \
          464  +  $(OBJDIR)/wysiwyg_.c \
          465  +  $(OBJDIR)/xfer_.c \
          466  +  $(OBJDIR)/xfersetup_.c \
          467  +  $(OBJDIR)/zip_.c
          468  +
          469  +OBJ = \
          470  + $(OBJDIR)/add.o \
          471  + $(OBJDIR)/allrepo.o \
          472  + $(OBJDIR)/attach.o \
          473  + $(OBJDIR)/bag.o \
          474  + $(OBJDIR)/bisect.o \
          475  + $(OBJDIR)/blob.o \
          476  + $(OBJDIR)/branch.o \
          477  + $(OBJDIR)/browse.o \
          478  + $(OBJDIR)/captcha.o \
          479  + $(OBJDIR)/cgi.o \
          480  + $(OBJDIR)/checkin.o \
          481  + $(OBJDIR)/checkout.o \
          482  + $(OBJDIR)/clearsign.o \
          483  + $(OBJDIR)/clone.o \
          484  + $(OBJDIR)/comformat.o \
          485  + $(OBJDIR)/configure.o \
          486  + $(OBJDIR)/content.o \
          487  + $(OBJDIR)/db.o \
          488  + $(OBJDIR)/delta.o \
          489  + $(OBJDIR)/deltacmd.o \
          490  + $(OBJDIR)/descendants.o \
          491  + $(OBJDIR)/diff.o \
          492  + $(OBJDIR)/diffcmd.o \
          493  + $(OBJDIR)/doc.o \
          494  + $(OBJDIR)/encode.o \
          495  + $(OBJDIR)/event.o \
          496  + $(OBJDIR)/export.o \
          497  + $(OBJDIR)/file.o \
          498  + $(OBJDIR)/finfo.o \
          499  + $(OBJDIR)/glob.o \
          500  + $(OBJDIR)/graph.o \
          501  + $(OBJDIR)/gzip.o \
          502  + $(OBJDIR)/http.o \
          503  + $(OBJDIR)/http_socket.o \
          504  + $(OBJDIR)/http_ssl.o \
          505  + $(OBJDIR)/http_transport.o \
          506  + $(OBJDIR)/import.o \
          507  + $(OBJDIR)/info.o \
          508  + $(OBJDIR)/json.o \
          509  + $(OBJDIR)/json_artifact.o \
          510  + $(OBJDIR)/json_branch.o \
          511  + $(OBJDIR)/json_config.o \
          512  + $(OBJDIR)/json_diff.o \
          513  + $(OBJDIR)/json_dir.o \
          514  + $(OBJDIR)/json_finfo.o \
          515  + $(OBJDIR)/json_login.o \
          516  + $(OBJDIR)/json_query.o \
          517  + $(OBJDIR)/json_report.o \
          518  + $(OBJDIR)/json_tag.o \
          519  + $(OBJDIR)/json_timeline.o \
          520  + $(OBJDIR)/json_user.o \
          521  + $(OBJDIR)/json_wiki.o \
          522  + $(OBJDIR)/leaf.o \
          523  + $(OBJDIR)/login.o \
          524  + $(OBJDIR)/main.o \
          525  + $(OBJDIR)/manifest.o \
          526  + $(OBJDIR)/markdown.o \
          527  + $(OBJDIR)/markdown_html.o \
          528  + $(OBJDIR)/md5.o \
          529  + $(OBJDIR)/merge.o \
          530  + $(OBJDIR)/merge3.o \
          531  + $(OBJDIR)/moderate.o \
          532  + $(OBJDIR)/name.o \
          533  + $(OBJDIR)/path.o \
          534  + $(OBJDIR)/pivot.o \
          535  + $(OBJDIR)/popen.o \
          536  + $(OBJDIR)/pqueue.o \
          537  + $(OBJDIR)/printf.o \
          538  + $(OBJDIR)/rebuild.o \
          539  + $(OBJDIR)/regexp.o \
          540  + $(OBJDIR)/report.o \
          541  + $(OBJDIR)/rss.o \
          542  + $(OBJDIR)/schema.o \
          543  + $(OBJDIR)/search.o \
          544  + $(OBJDIR)/setup.o \
          545  + $(OBJDIR)/sha1.o \
          546  + $(OBJDIR)/shun.o \
          547  + $(OBJDIR)/skins.o \
          548  + $(OBJDIR)/sqlcmd.o \
          549  + $(OBJDIR)/stash.o \
          550  + $(OBJDIR)/stat.o \
          551  + $(OBJDIR)/style.o \
          552  + $(OBJDIR)/sync.o \
          553  + $(OBJDIR)/tag.o \
          554  + $(OBJDIR)/tar.o \
          555  + $(OBJDIR)/th_main.o \
          556  + $(OBJDIR)/timeline.o \
          557  + $(OBJDIR)/tkt.o \
          558  + $(OBJDIR)/tktsetup.o \
          559  + $(OBJDIR)/undo.o \
          560  + $(OBJDIR)/unicode.o \
          561  + $(OBJDIR)/update.o \
          562  + $(OBJDIR)/url.o \
          563  + $(OBJDIR)/user.o \
          564  + $(OBJDIR)/utf8.o \
          565  + $(OBJDIR)/verify.o \
          566  + $(OBJDIR)/vfile.o \
          567  + $(OBJDIR)/wiki.o \
          568  + $(OBJDIR)/wikiformat.o \
          569  + $(OBJDIR)/winhttp.o \
          570  + $(OBJDIR)/wysiwyg.o \
          571  + $(OBJDIR)/xfer.o \
          572  + $(OBJDIR)/xfersetup.o \
          573  + $(OBJDIR)/zip.o
          574  +
          575  +APPNAME = fossil.exe
          576  +
          577  +#### If the USE_WINDOWS variable exists, it is assumed that we are building
          578  +#    inside of a Windows-style shell; otherwise, it is assumed that we are
          579  +#    building inside of a Unix-style shell.  Note that the "move" command is
          580  +#    broken when attempting to use it from the Windows shell via MinGW make
          581  +#    because the SHELL variable is only used for certain commands that are
          582  +#    recognized internally by make.
          583  +#
          584  +ifdef USE_WINDOWS
          585  +TRANSLATE   = $(subst /,\,$(OBJDIR)/translate)
          586  +MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders)
          587  +MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex)
          588  +VERSION     = $(subst /,\,$(OBJDIR)/version)
          589  +CP          = copy
          590  +MV          = copy
          591  +RM          = del /Q
          592  +MKDIR       = -mkdir
          593  +RMDIR       = rmdir /S /Q
          594  +else
          595  +TRANSLATE   = $(OBJDIR)/translate
          596  +MAKEHEADERS = $(OBJDIR)/makeheaders
          597  +MKINDEX     = $(OBJDIR)/mkindex
          598  +VERSION     = $(OBJDIR)/version
          599  +CP          = cp
          600  +MV          = mv
          601  +RM          = rm -f
          602  +MKDIR       = -mkdir -p
          603  +RMDIR       = rm -rf
          604  +endif
          605  +
          606  +all:	$(OBJDIR) $(APPNAME)
          607  +
          608  +$(OBJDIR)/fossil.o:	$(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h
          609  +ifdef USE_WINDOWS
          610  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
          611  +	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
          612  +else
          613  +	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
          614  +	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
          615  +endif
          616  +	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o
          617  +
          618  +install:	$(OBJDIR) $(APPNAME)
          619  +ifdef USE_WINDOWS
          620  +	$(MKDIR) $(subst /,\,$(INSTALLDIR))
          621  +	$(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR))
          622  +else
          623  +	$(MKDIR) $(INSTALLDIR)
          624  +	$(MV) $(APPNAME) $(INSTALLDIR)
          625  +endif
          626  +
          627  +$(OBJDIR):
          628  +ifdef USE_WINDOWS
          629  +	$(MKDIR) $(subst /,\,$(OBJDIR))
          630  +else
          631  +	$(MKDIR) $(OBJDIR)
          632  +endif
          633  +
          634  +$(OBJDIR)/translate:	$(SRCDIR)/translate.c
          635  +	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
          636  +
          637  +$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
          638  +	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
          639  +
          640  +$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
          641  +	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
          642  +
          643  +$(VERSION): $(SRCDIR)/mkversion.c
          644  +	$(BCC) -o $(OBJDIR)/version $(SRCDIR)/mkversion.c
          645  +
          646  +# WARNING. DANGER. Running the test suite modifies the repository the
          647  +# build is done from, i.e. the checkout belongs to. Do not sync/push
          648  +# the repository after running the tests.
          649  +test:	$(OBJDIR) $(APPNAME)
          650  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
          651  +
          652  +$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
          653  +	$(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
          654  +
          655  +EXTRAOBJ =  $(OBJDIR)/sqlite3.o  $(OBJDIR)/shell.o  $(OBJDIR)/th.o  $(OBJDIR)/th_lang.o  $(OBJDIR)/cson_amalgamation.o
          656  +
          657  +ifdef FOSSIL_ENABLE_TCL
          658  +EXTRAOBJ +=  $(OBJDIR)/th_tcl.o
          659  +endif
          660  +
          661  +zlib:
          662  +	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a
          663  +
          664  +$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib
          665  +	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o
          666  +
          667  +# This rule prevents make from using its default rules to try build
          668  +# an executable named "manifest" out of the file named "manifest.c"
          669  +#
          670  +$(SRCDIR)/../manifest:
          671  +	# noop
          672  +
          673  +clean:
          674  +ifdef USE_WINDOWS
          675  +	$(RM) $(subst /,\,$(APPNAME))
          676  +	$(RMDIR) $(subst /,\,$(OBJDIR))
          677  +else
          678  +	$(RM) $(APPNAME)
          679  +	$(RMDIR) $(OBJDIR)
          680  +endif
          681  +
          682  +setup: $(OBJDIR) $(APPNAME)
          683  +	$(MAKENSIS) ./fossil.nsi
          684  +
          685  +$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
          686  +	$(MKINDEX) $(TRANS_SRC) >$@
          687  +
          688  +$(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
          689  +	$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
          690  +		$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
          691  +		$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
          692  +		$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
          693  +		$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
          694  +		$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
          695  +		$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
          696  +		$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
          697  +		$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
          698  +		$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
          699  +		$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
          700  +		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
          701  +		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
          702  +		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
          703  +		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
          704  +		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
          705  +		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
          706  +		$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
          707  +		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
          708  +		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
          709  +		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
          710  +		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
          711  +		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
          712  +		$(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \
          713  +		$(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \
          714  +		$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
          715  +		$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
          716  +		$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
          717  +		$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
          718  +		$(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \
          719  +		$(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \
          720  +		$(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \
          721  +		$(OBJDIR)/http_.c:$(OBJDIR)/http.h \
          722  +		$(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \
          723  +		$(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \
          724  +		$(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \
          725  +		$(OBJDIR)/import_.c:$(OBJDIR)/import.h \
          726  +		$(OBJDIR)/info_.c:$(OBJDIR)/info.h \
          727  +		$(OBJDIR)/json_.c:$(OBJDIR)/json.h \
          728  +		$(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h \
          729  +		$(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h \
          730  +		$(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h \
          731  +		$(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h \
          732  +		$(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h \
          733  +		$(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h \
          734  +		$(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h \
          735  +		$(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h \
          736  +		$(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \
          737  +		$(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \
          738  +		$(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \
          739  +		$(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \
          740  +		$(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \
          741  +		$(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \
          742  +		$(OBJDIR)/login_.c:$(OBJDIR)/login.h \
          743  +		$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
          744  +		$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
          745  +		$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
          746  +		$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
          747  +		$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
          748  +		$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
          749  +		$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
          750  +		$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
          751  +		$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
          752  +		$(OBJDIR)/path_.c:$(OBJDIR)/path.h \
          753  +		$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
          754  +		$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
          755  +		$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
          756  +		$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
          757  +		$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
          758  +		$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
          759  +		$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
          760  +		$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
          761  +		$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
          762  +		$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
          763  +		$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
          764  +		$(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \
          765  +		$(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \
          766  +		$(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \
          767  +		$(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \
          768  +		$(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \
          769  +		$(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \
          770  +		$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
          771  +		$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
          772  +		$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
          773  +		$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
          774  +		$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
          775  +		$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
          776  +		$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
          777  +		$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
          778  +		$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
          779  +		$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
          780  +		$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
          781  +		$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
          782  +		$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
          783  +		$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
          784  +		$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
          785  +		$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
          786  +		$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
          787  +		$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
          788  +		$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
          789  +		$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
          790  +		$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
          791  +		$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
          792  +		$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
          793  +		$(SRCDIR)/sqlite3.h \
          794  +		$(SRCDIR)/th.h \
          795  +		$(OBJDIR)/VERSION.h
          796  +	echo Done >$(OBJDIR)/headers
          797  +
          798  +$(OBJDIR)/headers: Makefile
          799  +
          800  +Makefile:
          801  +
          802  +$(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
          803  +	$(TRANSLATE) $(SRCDIR)/add.c >$(OBJDIR)/add_.c
          804  +
          805  +$(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h  $(SRCDIR)/config.h
          806  +	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
          807  +
          808  +$(OBJDIR)/add.h:	$(OBJDIR)/headers
          809  +
          810  +$(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(OBJDIR)/translate
          811  +	$(TRANSLATE) $(SRCDIR)/allrepo.c >$(OBJDIR)/allrepo_.c
          812  +
          813  +$(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h  $(SRCDIR)/config.h
          814  +	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
          815  +
          816  +$(OBJDIR)/allrepo.h:	$(OBJDIR)/headers
          817  +
          818  +$(OBJDIR)/attach_.c:	$(SRCDIR)/attach.c $(OBJDIR)/translate
          819  +	$(TRANSLATE) $(SRCDIR)/attach.c >$(OBJDIR)/attach_.c
          820  +
          821  +$(OBJDIR)/attach.o:	$(OBJDIR)/attach_.c $(OBJDIR)/attach.h  $(SRCDIR)/config.h
          822  +	$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
          823  +
          824  +$(OBJDIR)/attach.h:	$(OBJDIR)/headers
          825  +
          826  +$(OBJDIR)/bag_.c:	$(SRCDIR)/bag.c $(OBJDIR)/translate
          827  +	$(TRANSLATE) $(SRCDIR)/bag.c >$(OBJDIR)/bag_.c
          828  +
          829  +$(OBJDIR)/bag.o:	$(OBJDIR)/bag_.c $(OBJDIR)/bag.h  $(SRCDIR)/config.h
          830  +	$(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c
          831  +
          832  +$(OBJDIR)/bag.h:	$(OBJDIR)/headers
          833  +
          834  +$(OBJDIR)/bisect_.c:	$(SRCDIR)/bisect.c $(OBJDIR)/translate
          835  +	$(TRANSLATE) $(SRCDIR)/bisect.c >$(OBJDIR)/bisect_.c
          836  +
          837  +$(OBJDIR)/bisect.o:	$(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h  $(SRCDIR)/config.h
          838  +	$(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c
          839  +
          840  +$(OBJDIR)/bisect.h:	$(OBJDIR)/headers
          841  +
          842  +$(OBJDIR)/blob_.c:	$(SRCDIR)/blob.c $(OBJDIR)/translate
          843  +	$(TRANSLATE) $(SRCDIR)/blob.c >$(OBJDIR)/blob_.c
          844  +
          845  +$(OBJDIR)/blob.o:	$(OBJDIR)/blob_.c $(OBJDIR)/blob.h  $(SRCDIR)/config.h
          846  +	$(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c
          847  +
          848  +$(OBJDIR)/blob.h:	$(OBJDIR)/headers
          849  +
          850  +$(OBJDIR)/branch_.c:	$(SRCDIR)/branch.c $(OBJDIR)/translate
          851  +	$(TRANSLATE) $(SRCDIR)/branch.c >$(OBJDIR)/branch_.c
          852  +
          853  +$(OBJDIR)/branch.o:	$(OBJDIR)/branch_.c $(OBJDIR)/branch.h  $(SRCDIR)/config.h
          854  +	$(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c
          855  +
          856  +$(OBJDIR)/branch.h:	$(OBJDIR)/headers
          857  +
          858  +$(OBJDIR)/browse_.c:	$(SRCDIR)/browse.c $(OBJDIR)/translate
          859  +	$(TRANSLATE) $(SRCDIR)/browse.c >$(OBJDIR)/browse_.c
          860  +
          861  +$(OBJDIR)/browse.o:	$(OBJDIR)/browse_.c $(OBJDIR)/browse.h  $(SRCDIR)/config.h
          862  +	$(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c
          863  +
          864  +$(OBJDIR)/browse.h:	$(OBJDIR)/headers
          865  +
          866  +$(OBJDIR)/captcha_.c:	$(SRCDIR)/captcha.c $(OBJDIR)/translate
          867  +	$(TRANSLATE) $(SRCDIR)/captcha.c >$(OBJDIR)/captcha_.c
          868  +
          869  +$(OBJDIR)/captcha.o:	$(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h  $(SRCDIR)/config.h
          870  +	$(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c
          871  +
          872  +$(OBJDIR)/captcha.h:	$(OBJDIR)/headers
          873  +
          874  +$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(OBJDIR)/translate
          875  +	$(TRANSLATE) $(SRCDIR)/cgi.c >$(OBJDIR)/cgi_.c
          876  +
          877  +$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h  $(SRCDIR)/config.h
          878  +	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
          879  +
          880  +$(OBJDIR)/cgi.h:	$(OBJDIR)/headers
          881  +
          882  +$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(OBJDIR)/translate
          883  +	$(TRANSLATE) $(SRCDIR)/checkin.c >$(OBJDIR)/checkin_.c
          884  +
          885  +$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h  $(SRCDIR)/config.h
          886  +	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c
          887  +
          888  +$(OBJDIR)/checkin.h:	$(OBJDIR)/headers
          889  +
          890  +$(OBJDIR)/checkout_.c:	$(SRCDIR)/checkout.c $(OBJDIR)/translate
          891  +	$(TRANSLATE) $(SRCDIR)/checkout.c >$(OBJDIR)/checkout_.c
          892  +
          893  +$(OBJDIR)/checkout.o:	$(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h  $(SRCDIR)/config.h
          894  +	$(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c
          895  +
          896  +$(OBJDIR)/checkout.h:	$(OBJDIR)/headers
          897  +
          898  +$(OBJDIR)/clearsign_.c:	$(SRCDIR)/clearsign.c $(OBJDIR)/translate
          899  +	$(TRANSLATE) $(SRCDIR)/clearsign.c >$(OBJDIR)/clearsign_.c
          900  +
          901  +$(OBJDIR)/clearsign.o:	$(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h  $(SRCDIR)/config.h
          902  +	$(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c
          903  +
          904  +$(OBJDIR)/clearsign.h:	$(OBJDIR)/headers
          905  +
          906  +$(OBJDIR)/clone_.c:	$(SRCDIR)/clone.c $(OBJDIR)/translate
          907  +	$(TRANSLATE) $(SRCDIR)/clone.c >$(OBJDIR)/clone_.c
          908  +
          909  +$(OBJDIR)/clone.o:	$(OBJDIR)/clone_.c $(OBJDIR)/clone.h  $(SRCDIR)/config.h
          910  +	$(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
          911  +
          912  +$(OBJDIR)/clone.h:	$(OBJDIR)/headers
          913  +
          914  +$(OBJDIR)/comformat_.c:	$(SRCDIR)/comformat.c $(OBJDIR)/translate
          915  +	$(TRANSLATE) $(SRCDIR)/comformat.c >$(OBJDIR)/comformat_.c
          916  +
          917  +$(OBJDIR)/comformat.o:	$(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h  $(SRCDIR)/config.h
          918  +	$(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c
          919  +
          920  +$(OBJDIR)/comformat.h:	$(OBJDIR)/headers
          921  +
          922  +$(OBJDIR)/configure_.c:	$(SRCDIR)/configure.c $(OBJDIR)/translate
          923  +	$(TRANSLATE) $(SRCDIR)/configure.c >$(OBJDIR)/configure_.c
          924  +
          925  +$(OBJDIR)/configure.o:	$(OBJDIR)/configure_.c $(OBJDIR)/configure.h  $(SRCDIR)/config.h
          926  +	$(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c
          927  +
          928  +$(OBJDIR)/configure.h:	$(OBJDIR)/headers
          929  +
          930  +$(OBJDIR)/content_.c:	$(SRCDIR)/content.c $(OBJDIR)/translate
          931  +	$(TRANSLATE) $(SRCDIR)/content.c >$(OBJDIR)/content_.c
          932  +
          933  +$(OBJDIR)/content.o:	$(OBJDIR)/content_.c $(OBJDIR)/content.h  $(SRCDIR)/config.h
          934  +	$(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c
          935  +
          936  +$(OBJDIR)/content.h:	$(OBJDIR)/headers
          937  +
          938  +$(OBJDIR)/db_.c:	$(SRCDIR)/db.c $(OBJDIR)/translate
          939  +	$(TRANSLATE) $(SRCDIR)/db.c >$(OBJDIR)/db_.c
          940  +
          941  +$(OBJDIR)/db.o:	$(OBJDIR)/db_.c $(OBJDIR)/db.h  $(SRCDIR)/config.h
          942  +	$(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c
          943  +
          944  +$(OBJDIR)/db.h:	$(OBJDIR)/headers
          945  +
          946  +$(OBJDIR)/delta_.c:	$(SRCDIR)/delta.c $(OBJDIR)/translate
          947  +	$(TRANSLATE) $(SRCDIR)/delta.c >$(OBJDIR)/delta_.c
          948  +
          949  +$(OBJDIR)/delta.o:	$(OBJDIR)/delta_.c $(OBJDIR)/delta.h  $(SRCDIR)/config.h
          950  +	$(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c
          951  +
          952  +$(OBJDIR)/delta.h:	$(OBJDIR)/headers
          953  +
          954  +$(OBJDIR)/deltacmd_.c:	$(SRCDIR)/deltacmd.c $(OBJDIR)/translate
          955  +	$(TRANSLATE) $(SRCDIR)/deltacmd.c >$(OBJDIR)/deltacmd_.c
          956  +
          957  +$(OBJDIR)/deltacmd.o:	$(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h  $(SRCDIR)/config.h
          958  +	$(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c
          959  +
          960  +$(OBJDIR)/deltacmd.h:	$(OBJDIR)/headers
          961  +
          962  +$(OBJDIR)/descendants_.c:	$(SRCDIR)/descendants.c $(OBJDIR)/translate
          963  +	$(TRANSLATE) $(SRCDIR)/descendants.c >$(OBJDIR)/descendants_.c
          964  +
          965  +$(OBJDIR)/descendants.o:	$(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h  $(SRCDIR)/config.h
          966  +	$(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c
          967  +
          968  +$(OBJDIR)/descendants.h:	$(OBJDIR)/headers
          969  +
          970  +$(OBJDIR)/diff_.c:	$(SRCDIR)/diff.c $(OBJDIR)/translate
          971  +	$(TRANSLATE) $(SRCDIR)/diff.c >$(OBJDIR)/diff_.c
          972  +
          973  +$(OBJDIR)/diff.o:	$(OBJDIR)/diff_.c $(OBJDIR)/diff.h  $(SRCDIR)/config.h
          974  +	$(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c
          975  +
          976  +$(OBJDIR)/diff.h:	$(OBJDIR)/headers
          977  +
          978  +$(OBJDIR)/diffcmd_.c:	$(SRCDIR)/diffcmd.c $(OBJDIR)/translate
          979  +	$(TRANSLATE) $(SRCDIR)/diffcmd.c >$(OBJDIR)/diffcmd_.c
          980  +
          981  +$(OBJDIR)/diffcmd.o:	$(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h  $(SRCDIR)/config.h
          982  +	$(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c
          983  +
          984  +$(OBJDIR)/diffcmd.h:	$(OBJDIR)/headers
          985  +
          986  +$(OBJDIR)/doc_.c:	$(SRCDIR)/doc.c $(OBJDIR)/translate
          987  +	$(TRANSLATE) $(SRCDIR)/doc.c >$(OBJDIR)/doc_.c
          988  +
          989  +$(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h  $(SRCDIR)/config.h
          990  +	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
          991  +
          992  +$(OBJDIR)/doc.h:	$(OBJDIR)/headers
          993  +
          994  +$(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(OBJDIR)/translate
          995  +	$(TRANSLATE) $(SRCDIR)/encode.c >$(OBJDIR)/encode_.c
          996  +
          997  +$(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h  $(SRCDIR)/config.h
          998  +	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
          999  +
         1000  +$(OBJDIR)/encode.h:	$(OBJDIR)/headers
         1001  +
         1002  +$(OBJDIR)/event_.c:	$(SRCDIR)/event.c $(OBJDIR)/translate
         1003  +	$(TRANSLATE) $(SRCDIR)/event.c >$(OBJDIR)/event_.c
         1004  +
         1005  +$(OBJDIR)/event.o:	$(OBJDIR)/event_.c $(OBJDIR)/event.h  $(SRCDIR)/config.h
         1006  +	$(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c
         1007  +
         1008  +$(OBJDIR)/event.h:	$(OBJDIR)/headers
         1009  +
         1010  +$(OBJDIR)/export_.c:	$(SRCDIR)/export.c $(OBJDIR)/translate
         1011  +	$(TRANSLATE) $(SRCDIR)/export.c >$(OBJDIR)/export_.c
         1012  +
         1013  +$(OBJDIR)/export.o:	$(OBJDIR)/export_.c $(OBJDIR)/export.h  $(SRCDIR)/config.h
         1014  +	$(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c
         1015  +
         1016  +$(OBJDIR)/export.h:	$(OBJDIR)/headers
         1017  +
         1018  +$(OBJDIR)/file_.c:	$(SRCDIR)/file.c $(OBJDIR)/translate
         1019  +	$(TRANSLATE) $(SRCDIR)/file.c >$(OBJDIR)/file_.c
         1020  +
         1021  +$(OBJDIR)/file.o:	$(OBJDIR)/file_.c $(OBJDIR)/file.h  $(SRCDIR)/config.h
         1022  +	$(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
         1023  +
         1024  +$(OBJDIR)/file.h:	$(OBJDIR)/headers
         1025  +
         1026  +$(OBJDIR)/finfo_.c:	$(SRCDIR)/finfo.c $(OBJDIR)/translate
         1027  +	$(TRANSLATE) $(SRCDIR)/finfo.c >$(OBJDIR)/finfo_.c
         1028  +
         1029  +$(OBJDIR)/finfo.o:	$(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h  $(SRCDIR)/config.h
         1030  +	$(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c
         1031  +
         1032  +$(OBJDIR)/finfo.h:	$(OBJDIR)/headers
         1033  +
         1034  +$(OBJDIR)/glob_.c:	$(SRCDIR)/glob.c $(OBJDIR)/translate
         1035  +	$(TRANSLATE) $(SRCDIR)/glob.c >$(OBJDIR)/glob_.c
         1036  +
         1037  +$(OBJDIR)/glob.o:	$(OBJDIR)/glob_.c $(OBJDIR)/glob.h  $(SRCDIR)/config.h
         1038  +	$(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c
         1039  +
         1040  +$(OBJDIR)/glob.h:	$(OBJDIR)/headers
         1041  +
         1042  +$(OBJDIR)/graph_.c:	$(SRCDIR)/graph.c $(OBJDIR)/translate
         1043  +	$(TRANSLATE) $(SRCDIR)/graph.c >$(OBJDIR)/graph_.c
         1044  +
         1045  +$(OBJDIR)/graph.o:	$(OBJDIR)/graph_.c $(OBJDIR)/graph.h  $(SRCDIR)/config.h
         1046  +	$(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c
         1047  +
         1048  +$(OBJDIR)/graph.h:	$(OBJDIR)/headers
         1049  +
         1050  +$(OBJDIR)/gzip_.c:	$(SRCDIR)/gzip.c $(OBJDIR)/translate
         1051  +	$(TRANSLATE) $(SRCDIR)/gzip.c >$(OBJDIR)/gzip_.c
         1052  +
         1053  +$(OBJDIR)/gzip.o:	$(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h  $(SRCDIR)/config.h
         1054  +	$(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c
         1055  +
         1056  +$(OBJDIR)/gzip.h:	$(OBJDIR)/headers
         1057  +
         1058  +$(OBJDIR)/http_.c:	$(SRCDIR)/http.c $(OBJDIR)/translate
         1059  +	$(TRANSLATE) $(SRCDIR)/http.c >$(OBJDIR)/http_.c
         1060  +
         1061  +$(OBJDIR)/http.o:	$(OBJDIR)/http_.c $(OBJDIR)/http.h  $(SRCDIR)/config.h
         1062  +	$(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c
         1063  +
         1064  +$(OBJDIR)/http.h:	$(OBJDIR)/headers
         1065  +
         1066  +$(OBJDIR)/http_socket_.c:	$(SRCDIR)/http_socket.c $(OBJDIR)/translate
         1067  +	$(TRANSLATE) $(SRCDIR)/http_socket.c >$(OBJDIR)/http_socket_.c
         1068  +
         1069  +$(OBJDIR)/http_socket.o:	$(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h  $(SRCDIR)/config.h
         1070  +	$(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c
         1071  +
         1072  +$(OBJDIR)/http_socket.h:	$(OBJDIR)/headers
         1073  +
         1074  +$(OBJDIR)/http_ssl_.c:	$(SRCDIR)/http_ssl.c $(OBJDIR)/translate
         1075  +	$(TRANSLATE) $(SRCDIR)/http_ssl.c >$(OBJDIR)/http_ssl_.c
         1076  +
         1077  +$(OBJDIR)/http_ssl.o:	$(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h  $(SRCDIR)/config.h
         1078  +	$(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c
         1079  +
         1080  +$(OBJDIR)/http_ssl.h:	$(OBJDIR)/headers
         1081  +
         1082  +$(OBJDIR)/http_transport_.c:	$(SRCDIR)/http_transport.c $(OBJDIR)/translate
         1083  +	$(TRANSLATE) $(SRCDIR)/http_transport.c >$(OBJDIR)/http_transport_.c
         1084  +
         1085  +$(OBJDIR)/http_transport.o:	$(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h  $(SRCDIR)/config.h
         1086  +	$(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c
         1087  +
         1088  +$(OBJDIR)/http_transport.h:	$(OBJDIR)/headers
         1089  +
         1090  +$(OBJDIR)/import_.c:	$(SRCDIR)/import.c $(OBJDIR)/translate
         1091  +	$(TRANSLATE) $(SRCDIR)/import.c >$(OBJDIR)/import_.c
         1092  +
         1093  +$(OBJDIR)/import.o:	$(OBJDIR)/import_.c $(OBJDIR)/import.h  $(SRCDIR)/config.h
         1094  +	$(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c
         1095  +
         1096  +$(OBJDIR)/import.h:	$(OBJDIR)/headers
         1097  +
         1098  +$(OBJDIR)/info_.c:	$(SRCDIR)/info.c $(OBJDIR)/translate
         1099  +	$(TRANSLATE) $(SRCDIR)/info.c >$(OBJDIR)/info_.c
         1100  +
         1101  +$(OBJDIR)/info.o:	$(OBJDIR)/info_.c $(OBJDIR)/info.h  $(SRCDIR)/config.h
         1102  +	$(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
         1103  +
         1104  +$(OBJDIR)/info.h:	$(OBJDIR)/headers
         1105  +
         1106  +$(OBJDIR)/json_.c:	$(SRCDIR)/json.c $(OBJDIR)/translate
         1107  +	$(TRANSLATE) $(SRCDIR)/json.c >$(OBJDIR)/json_.c
         1108  +
         1109  +$(OBJDIR)/json.o:	$(OBJDIR)/json_.c $(OBJDIR)/json.h  $(SRCDIR)/config.h
         1110  +	$(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
         1111  +
         1112  +$(OBJDIR)/json.h:	$(OBJDIR)/headers
         1113  +
         1114  +$(OBJDIR)/json_artifact_.c:	$(SRCDIR)/json_artifact.c $(OBJDIR)/translate
         1115  +	$(TRANSLATE) $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
         1116  +
         1117  +$(OBJDIR)/json_artifact.o:	$(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h  $(SRCDIR)/config.h
         1118  +	$(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
         1119  +
         1120  +$(OBJDIR)/json_artifact.h:	$(OBJDIR)/headers
         1121  +
         1122  +$(OBJDIR)/json_branch_.c:	$(SRCDIR)/json_branch.c $(OBJDIR)/translate
         1123  +	$(TRANSLATE) $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
         1124  +
         1125  +$(OBJDIR)/json_branch.o:	$(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h  $(SRCDIR)/config.h
         1126  +	$(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
         1127  +
         1128  +$(OBJDIR)/json_branch.h:	$(OBJDIR)/headers
         1129  +
         1130  +$(OBJDIR)/json_config_.c:	$(SRCDIR)/json_config.c $(OBJDIR)/translate
         1131  +	$(TRANSLATE) $(SRCDIR)/json_config.c >$(OBJDIR)/json_config_.c
         1132  +
         1133  +$(OBJDIR)/json_config.o:	$(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h  $(SRCDIR)/config.h
         1134  +	$(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c
         1135  +
         1136  +$(OBJDIR)/json_config.h:	$(OBJDIR)/headers
         1137  +
         1138  +$(OBJDIR)/json_diff_.c:	$(SRCDIR)/json_diff.c $(OBJDIR)/translate
         1139  +	$(TRANSLATE) $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
         1140  +
         1141  +$(OBJDIR)/json_diff.o:	$(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h  $(SRCDIR)/config.h
         1142  +	$(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
         1143  +
         1144  +$(OBJDIR)/json_diff.h:	$(OBJDIR)/headers
         1145  +
         1146  +$(OBJDIR)/json_dir_.c:	$(SRCDIR)/json_dir.c $(OBJDIR)/translate
         1147  +	$(TRANSLATE) $(SRCDIR)/json_dir.c >$(OBJDIR)/json_dir_.c
         1148  +
         1149  +$(OBJDIR)/json_dir.o:	$(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h  $(SRCDIR)/config.h
         1150  +	$(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c
         1151  +
         1152  +$(OBJDIR)/json_dir.h:	$(OBJDIR)/headers
         1153  +
         1154  +$(OBJDIR)/json_finfo_.c:	$(SRCDIR)/json_finfo.c $(OBJDIR)/translate
         1155  +	$(TRANSLATE) $(SRCDIR)/json_finfo.c >$(OBJDIR)/json_finfo_.c
         1156  +
         1157  +$(OBJDIR)/json_finfo.o:	$(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h  $(SRCDIR)/config.h
         1158  +	$(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c
         1159  +
         1160  +$(OBJDIR)/json_finfo.h:	$(OBJDIR)/headers
         1161  +
         1162  +$(OBJDIR)/json_login_.c:	$(SRCDIR)/json_login.c $(OBJDIR)/translate
         1163  +	$(TRANSLATE) $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
         1164  +
         1165  +$(OBJDIR)/json_login.o:	$(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h  $(SRCDIR)/config.h
         1166  +	$(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
         1167  +
         1168  +$(OBJDIR)/json_login.h:	$(OBJDIR)/headers
         1169  +
         1170  +$(OBJDIR)/json_query_.c:	$(SRCDIR)/json_query.c $(OBJDIR)/translate
         1171  +	$(TRANSLATE) $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
         1172  +
         1173  +$(OBJDIR)/json_query.o:	$(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h  $(SRCDIR)/config.h
         1174  +	$(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
         1175  +
         1176  +$(OBJDIR)/json_query.h:	$(OBJDIR)/headers
         1177  +
         1178  +$(OBJDIR)/json_report_.c:	$(SRCDIR)/json_report.c $(OBJDIR)/translate
         1179  +	$(TRANSLATE) $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
         1180  +
         1181  +$(OBJDIR)/json_report.o:	$(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h  $(SRCDIR)/config.h
         1182  +	$(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
         1183  +
         1184  +$(OBJDIR)/json_report.h:	$(OBJDIR)/headers
         1185  +
         1186  +$(OBJDIR)/json_tag_.c:	$(SRCDIR)/json_tag.c $(OBJDIR)/translate
         1187  +	$(TRANSLATE) $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
         1188  +
         1189  +$(OBJDIR)/json_tag.o:	$(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h  $(SRCDIR)/config.h
         1190  +	$(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
         1191  +
         1192  +$(OBJDIR)/json_tag.h:	$(OBJDIR)/headers
         1193  +
         1194  +$(OBJDIR)/json_timeline_.c:	$(SRCDIR)/json_timeline.c $(OBJDIR)/translate
         1195  +	$(TRANSLATE) $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
         1196  +
         1197  +$(OBJDIR)/json_timeline.o:	$(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h  $(SRCDIR)/config.h
         1198  +	$(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
         1199  +
         1200  +$(OBJDIR)/json_timeline.h:	$(OBJDIR)/headers
         1201  +
         1202  +$(OBJDIR)/json_user_.c:	$(SRCDIR)/json_user.c $(OBJDIR)/translate
         1203  +	$(TRANSLATE) $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
         1204  +
         1205  +$(OBJDIR)/json_user.o:	$(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h  $(SRCDIR)/config.h
         1206  +	$(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
         1207  +
         1208  +$(OBJDIR)/json_user.h:	$(OBJDIR)/headers
         1209  +
         1210  +$(OBJDIR)/json_wiki_.c:	$(SRCDIR)/json_wiki.c $(OBJDIR)/translate
         1211  +	$(TRANSLATE) $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
         1212  +
         1213  +$(OBJDIR)/json_wiki.o:	$(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h  $(SRCDIR)/config.h
         1214  +	$(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
         1215  +
         1216  +$(OBJDIR)/json_wiki.h:	$(OBJDIR)/headers
         1217  +
         1218  +$(OBJDIR)/leaf_.c:	$(SRCDIR)/leaf.c $(OBJDIR)/translate
         1219  +	$(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
         1220  +
         1221  +$(OBJDIR)/leaf.o:	$(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h  $(SRCDIR)/config.h
         1222  +	$(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
         1223  +
         1224  +$(OBJDIR)/leaf.h:	$(OBJDIR)/headers
         1225  +
         1226  +$(OBJDIR)/login_.c:	$(SRCDIR)/login.c $(OBJDIR)/translate
         1227  +	$(TRANSLATE) $(SRCDIR)/login.c >$(OBJDIR)/login_.c
         1228  +
         1229  +$(OBJDIR)/login.o:	$(OBJDIR)/login_.c $(OBJDIR)/login.h  $(SRCDIR)/config.h
         1230  +	$(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c
         1231  +
         1232  +$(OBJDIR)/login.h:	$(OBJDIR)/headers
         1233  +
         1234  +$(OBJDIR)/main_.c:	$(SRCDIR)/main.c $(OBJDIR)/translate
         1235  +	$(TRANSLATE) $(SRCDIR)/main.c >$(OBJDIR)/main_.c
         1236  +
         1237  +$(OBJDIR)/main.o:	$(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h
         1238  +	$(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c
         1239  +
         1240  +$(OBJDIR)/main.h:	$(OBJDIR)/headers
         1241  +
         1242  +$(OBJDIR)/manifest_.c:	$(SRCDIR)/manifest.c $(OBJDIR)/translate
         1243  +	$(TRANSLATE) $(SRCDIR)/manifest.c >$(OBJDIR)/manifest_.c
         1244  +
         1245  +$(OBJDIR)/manifest.o:	$(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h  $(SRCDIR)/config.h
         1246  +	$(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c
         1247  +
         1248  +$(OBJDIR)/manifest.h:	$(OBJDIR)/headers
         1249  +
         1250  +$(OBJDIR)/markdown_.c:	$(SRCDIR)/markdown.c $(OBJDIR)/translate
         1251  +	$(TRANSLATE) $(SRCDIR)/markdown.c >$(OBJDIR)/markdown_.c
         1252  +
         1253  +$(OBJDIR)/markdown.o:	$(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h  $(SRCDIR)/config.h
         1254  +	$(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c
         1255  +
         1256  +$(OBJDIR)/markdown.h:	$(OBJDIR)/headers
         1257  +
         1258  +$(OBJDIR)/markdown_html_.c:	$(SRCDIR)/markdown_html.c $(OBJDIR)/translate
         1259  +	$(TRANSLATE) $(SRCDIR)/markdown_html.c >$(OBJDIR)/markdown_html_.c
         1260  +
         1261  +$(OBJDIR)/markdown_html.o:	$(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h  $(SRCDIR)/config.h
         1262  +	$(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
         1263  +
         1264  +$(OBJDIR)/markdown_html.h:	$(OBJDIR)/headers
         1265  +
         1266  +$(OBJDIR)/md5_.c:	$(SRCDIR)/md5.c $(OBJDIR)/translate
         1267  +	$(TRANSLATE) $(SRCDIR)/md5.c >$(OBJDIR)/md5_.c
         1268  +
         1269  +$(OBJDIR)/md5.o:	$(OBJDIR)/md5_.c $(OBJDIR)/md5.h  $(SRCDIR)/config.h
         1270  +	$(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c
         1271  +
         1272  +$(OBJDIR)/md5.h:	$(OBJDIR)/headers
         1273  +
         1274  +$(OBJDIR)/merge_.c:	$(SRCDIR)/merge.c $(OBJDIR)/translate
         1275  +	$(TRANSLATE) $(SRCDIR)/merge.c >$(OBJDIR)/merge_.c
         1276  +
         1277  +$(OBJDIR)/merge.o:	$(OBJDIR)/merge_.c $(OBJDIR)/merge.h  $(SRCDIR)/config.h
         1278  +	$(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c
         1279  +
         1280  +$(OBJDIR)/merge.h:	$(OBJDIR)/headers
         1281  +
         1282  +$(OBJDIR)/merge3_.c:	$(SRCDIR)/merge3.c $(OBJDIR)/translate
         1283  +	$(TRANSLATE) $(SRCDIR)/merge3.c >$(OBJDIR)/merge3_.c
         1284  +
         1285  +$(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
         1286  +	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
         1287  +
         1288  +$(OBJDIR)/merge3.h:	$(OBJDIR)/headers
         1289  +
         1290  +$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
         1291  +	$(TRANSLATE) $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
         1292  +
         1293  +$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
         1294  +	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
         1295  +
         1296  +$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
         1297  +
         1298  +$(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
         1299  +	$(TRANSLATE) $(SRCDIR)/name.c >$(OBJDIR)/name_.c
         1300  +
         1301  +$(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
         1302  +	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c
         1303  +
         1304  +$(OBJDIR)/name.h:	$(OBJDIR)/headers
         1305  +
         1306  +$(OBJDIR)/path_.c:	$(SRCDIR)/path.c $(OBJDIR)/translate
         1307  +	$(TRANSLATE) $(SRCDIR)/path.c >$(OBJDIR)/path_.c
         1308  +
         1309  +$(OBJDIR)/path.o:	$(OBJDIR)/path_.c $(OBJDIR)/path.h  $(SRCDIR)/config.h
         1310  +	$(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c
         1311  +
         1312  +$(OBJDIR)/path.h:	$(OBJDIR)/headers
         1313  +
         1314  +$(OBJDIR)/pivot_.c:	$(SRCDIR)/pivot.c $(OBJDIR)/translate
         1315  +	$(TRANSLATE) $(SRCDIR)/pivot.c >$(OBJDIR)/pivot_.c
         1316  +
         1317  +$(OBJDIR)/pivot.o:	$(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h  $(SRCDIR)/config.h
         1318  +	$(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c
         1319  +
         1320  +$(OBJDIR)/pivot.h:	$(OBJDIR)/headers
         1321  +
         1322  +$(OBJDIR)/popen_.c:	$(SRCDIR)/popen.c $(OBJDIR)/translate
         1323  +	$(TRANSLATE) $(SRCDIR)/popen.c >$(OBJDIR)/popen_.c
         1324  +
         1325  +$(OBJDIR)/popen.o:	$(OBJDIR)/popen_.c $(OBJDIR)/popen.h  $(SRCDIR)/config.h
         1326  +	$(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c
         1327  +
         1328  +$(OBJDIR)/popen.h:	$(OBJDIR)/headers
         1329  +
         1330  +$(OBJDIR)/pqueue_.c:	$(SRCDIR)/pqueue.c $(OBJDIR)/translate
         1331  +	$(TRANSLATE) $(SRCDIR)/pqueue.c >$(OBJDIR)/pqueue_.c
         1332  +
         1333  +$(OBJDIR)/pqueue.o:	$(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h  $(SRCDIR)/config.h
         1334  +	$(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c
         1335  +
         1336  +$(OBJDIR)/pqueue.h:	$(OBJDIR)/headers
         1337  +
         1338  +$(OBJDIR)/printf_.c:	$(SRCDIR)/printf.c $(OBJDIR)/translate
         1339  +	$(TRANSLATE) $(SRCDIR)/printf.c >$(OBJDIR)/printf_.c
         1340  +
         1341  +$(OBJDIR)/printf.o:	$(OBJDIR)/printf_.c $(OBJDIR)/printf.h  $(SRCDIR)/config.h
         1342  +	$(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c
         1343  +
         1344  +$(OBJDIR)/printf.h:	$(OBJDIR)/headers
         1345  +
         1346  +$(OBJDIR)/rebuild_.c:	$(SRCDIR)/rebuild.c $(OBJDIR)/translate
         1347  +	$(TRANSLATE) $(SRCDIR)/rebuild.c >$(OBJDIR)/rebuild_.c
         1348  +
         1349  +$(OBJDIR)/rebuild.o:	$(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h  $(SRCDIR)/config.h
         1350  +	$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
         1351  +
         1352  +$(OBJDIR)/rebuild.h:	$(OBJDIR)/headers
         1353  +
         1354  +$(OBJDIR)/regexp_.c:	$(SRCDIR)/regexp.c $(OBJDIR)/translate
         1355  +	$(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
         1356  +
         1357  +$(OBJDIR)/regexp.o:	$(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h  $(SRCDIR)/config.h
         1358  +	$(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
         1359  +
         1360  +$(OBJDIR)/regexp.h:	$(OBJDIR)/headers
         1361  +
         1362  +$(OBJDIR)/report_.c:	$(SRCDIR)/report.c $(OBJDIR)/translate
         1363  +	$(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
         1364  +
         1365  +$(OBJDIR)/report.o:	$(OBJDIR)/report_.c $(OBJDIR)/report.h  $(SRCDIR)/config.h
         1366  +	$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
         1367  +
         1368  +$(OBJDIR)/report.h:	$(OBJDIR)/headers
         1369  +
         1370  +$(OBJDIR)/rss_.c:	$(SRCDIR)/rss.c $(OBJDIR)/translate
         1371  +	$(TRANSLATE) $(SRCDIR)/rss.c >$(OBJDIR)/rss_.c
         1372  +
         1373  +$(OBJDIR)/rss.o:	$(OBJDIR)/rss_.c $(OBJDIR)/rss.h  $(SRCDIR)/config.h
         1374  +	$(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c
         1375  +
         1376  +$(OBJDIR)/rss.h:	$(OBJDIR)/headers
         1377  +
         1378  +$(OBJDIR)/schema_.c:	$(SRCDIR)/schema.c $(OBJDIR)/translate
         1379  +	$(TRANSLATE) $(SRCDIR)/schema.c >$(OBJDIR)/schema_.c
         1380  +
         1381  +$(OBJDIR)/schema.o:	$(OBJDIR)/schema_.c $(OBJDIR)/schema.h  $(SRCDIR)/config.h
         1382  +	$(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c
         1383  +
         1384  +$(OBJDIR)/schema.h:	$(OBJDIR)/headers
         1385  +
         1386  +$(OBJDIR)/search_.c:	$(SRCDIR)/search.c $(OBJDIR)/translate
         1387  +	$(TRANSLATE) $(SRCDIR)/search.c >$(OBJDIR)/search_.c
         1388  +
         1389  +$(OBJDIR)/search.o:	$(OBJDIR)/search_.c $(OBJDIR)/search.h  $(SRCDIR)/config.h
         1390  +	$(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c
         1391  +
         1392  +$(OBJDIR)/search.h:	$(OBJDIR)/headers
         1393  +
         1394  +$(OBJDIR)/setup_.c:	$(SRCDIR)/setup.c $(OBJDIR)/translate
         1395  +	$(TRANSLATE) $(SRCDIR)/setup.c >$(OBJDIR)/setup_.c
         1396  +
         1397  +$(OBJDIR)/setup.o:	$(OBJDIR)/setup_.c $(OBJDIR)/setup.h  $(SRCDIR)/config.h
         1398  +	$(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c
         1399  +
         1400  +$(OBJDIR)/setup.h:	$(OBJDIR)/headers
         1401  +
         1402  +$(OBJDIR)/sha1_.c:	$(SRCDIR)/sha1.c $(OBJDIR)/translate
         1403  +	$(TRANSLATE) $(SRCDIR)/sha1.c >$(OBJDIR)/sha1_.c
         1404  +
         1405  +$(OBJDIR)/sha1.o:	$(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h  $(SRCDIR)/config.h
         1406  +	$(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c
         1407  +
         1408  +$(OBJDIR)/sha1.h:	$(OBJDIR)/headers
         1409  +
         1410  +$(OBJDIR)/shun_.c:	$(SRCDIR)/shun.c $(OBJDIR)/translate
         1411  +	$(TRANSLATE) $(SRCDIR)/shun.c >$(OBJDIR)/shun_.c
         1412  +
         1413  +$(OBJDIR)/shun.o:	$(OBJDIR)/shun_.c $(OBJDIR)/shun.h  $(SRCDIR)/config.h
         1414  +	$(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c
         1415  +
         1416  +$(OBJDIR)/shun.h:	$(OBJDIR)/headers
         1417  +
         1418  +$(OBJDIR)/skins_.c:	$(SRCDIR)/skins.c $(OBJDIR)/translate
         1419  +	$(TRANSLATE) $(SRCDIR)/skins.c >$(OBJDIR)/skins_.c
         1420  +
         1421  +$(OBJDIR)/skins.o:	$(OBJDIR)/skins_.c $(OBJDIR)/skins.h  $(SRCDIR)/config.h
         1422  +	$(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c
         1423  +
         1424  +$(OBJDIR)/skins.h:	$(OBJDIR)/headers
         1425  +
         1426  +$(OBJDIR)/sqlcmd_.c:	$(SRCDIR)/sqlcmd.c $(OBJDIR)/translate
         1427  +	$(TRANSLATE) $(SRCDIR)/sqlcmd.c >$(OBJDIR)/sqlcmd_.c
         1428  +
         1429  +$(OBJDIR)/sqlcmd.o:	$(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h  $(SRCDIR)/config.h
         1430  +	$(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c
         1431  +
         1432  +$(OBJDIR)/sqlcmd.h:	$(OBJDIR)/headers
         1433  +
         1434  +$(OBJDIR)/stash_.c:	$(SRCDIR)/stash.c $(OBJDIR)/translate
         1435  +	$(TRANSLATE) $(SRCDIR)/stash.c >$(OBJDIR)/stash_.c
         1436  +
         1437  +$(OBJDIR)/stash.o:	$(OBJDIR)/stash_.c $(OBJDIR)/stash.h  $(SRCDIR)/config.h
         1438  +	$(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c
         1439  +
         1440  +$(OBJDIR)/stash.h:	$(OBJDIR)/headers
         1441  +
         1442  +$(OBJDIR)/stat_.c:	$(SRCDIR)/stat.c $(OBJDIR)/translate
         1443  +	$(TRANSLATE) $(SRCDIR)/stat.c >$(OBJDIR)/stat_.c
         1444  +
         1445  +$(OBJDIR)/stat.o:	$(OBJDIR)/stat_.c $(OBJDIR)/stat.h  $(SRCDIR)/config.h
         1446  +	$(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c
         1447  +
         1448  +$(OBJDIR)/stat.h:	$(OBJDIR)/headers
         1449  +
         1450  +$(OBJDIR)/style_.c:	$(SRCDIR)/style.c $(OBJDIR)/translate
         1451  +	$(TRANSLATE) $(SRCDIR)/style.c >$(OBJDIR)/style_.c
         1452  +
         1453  +$(OBJDIR)/style.o:	$(OBJDIR)/style_.c $(OBJDIR)/style.h  $(SRCDIR)/config.h
         1454  +	$(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c
         1455  +
         1456  +$(OBJDIR)/style.h:	$(OBJDIR)/headers
         1457  +
         1458  +$(OBJDIR)/sync_.c:	$(SRCDIR)/sync.c $(OBJDIR)/translate
         1459  +	$(TRANSLATE) $(SRCDIR)/sync.c >$(OBJDIR)/sync_.c
         1460  +
         1461  +$(OBJDIR)/sync.o:	$(OBJDIR)/sync_.c $(OBJDIR)/sync.h  $(SRCDIR)/config.h
         1462  +	$(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c
         1463  +
         1464  +$(OBJDIR)/sync.h:	$(OBJDIR)/headers
         1465  +
         1466  +$(OBJDIR)/tag_.c:	$(SRCDIR)/tag.c $(OBJDIR)/translate
         1467  +	$(TRANSLATE) $(SRCDIR)/tag.c >$(OBJDIR)/tag_.c
         1468  +
         1469  +$(OBJDIR)/tag.o:	$(OBJDIR)/tag_.c $(OBJDIR)/tag.h  $(SRCDIR)/config.h
         1470  +	$(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c
         1471  +
         1472  +$(OBJDIR)/tag.h:	$(OBJDIR)/headers
         1473  +
         1474  +$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(OBJDIR)/translate
         1475  +	$(TRANSLATE) $(SRCDIR)/tar.c >$(OBJDIR)/tar_.c
         1476  +
         1477  +$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h  $(SRCDIR)/config.h
         1478  +	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
         1479  +
         1480  +$(OBJDIR)/tar.h:	$(OBJDIR)/headers
         1481  +
         1482  +$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(OBJDIR)/translate
         1483  +	$(TRANSLATE) $(SRCDIR)/th_main.c >$(OBJDIR)/th_main_.c
         1484  +
         1485  +$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h  $(SRCDIR)/config.h
         1486  +	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c
         1487  +
         1488  +$(OBJDIR)/th_main.h:	$(OBJDIR)/headers
         1489  +
         1490  +$(OBJDIR)/timeline_.c:	$(SRCDIR)/timeline.c $(OBJDIR)/translate
         1491  +	$(TRANSLATE) $(SRCDIR)/timeline.c >$(OBJDIR)/timeline_.c
         1492  +
         1493  +$(OBJDIR)/timeline.o:	$(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h  $(SRCDIR)/config.h
         1494  +	$(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c
         1495  +
         1496  +$(OBJDIR)/timeline.h:	$(OBJDIR)/headers
         1497  +
         1498  +$(OBJDIR)/tkt_.c:	$(SRCDIR)/tkt.c $(OBJDIR)/translate
         1499  +	$(TRANSLATE) $(SRCDIR)/tkt.c >$(OBJDIR)/tkt_.c
         1500  +
         1501  +$(OBJDIR)/tkt.o:	$(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h  $(SRCDIR)/config.h
         1502  +	$(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c
         1503  +
         1504  +$(OBJDIR)/tkt.h:	$(OBJDIR)/headers
         1505  +
         1506  +$(OBJDIR)/tktsetup_.c:	$(SRCDIR)/tktsetup.c $(OBJDIR)/translate
         1507  +	$(TRANSLATE) $(SRCDIR)/tktsetup.c >$(OBJDIR)/tktsetup_.c
         1508  +
         1509  +$(OBJDIR)/tktsetup.o:	$(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h  $(SRCDIR)/config.h
         1510  +	$(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c
         1511  +
         1512  +$(OBJDIR)/tktsetup.h:	$(OBJDIR)/headers
         1513  +
         1514  +$(OBJDIR)/undo_.c:	$(SRCDIR)/undo.c $(OBJDIR)/translate
         1515  +	$(TRANSLATE) $(SRCDIR)/undo.c >$(OBJDIR)/undo_.c
         1516  +
         1517  +$(OBJDIR)/undo.o:	$(OBJDIR)/undo_.c $(OBJDIR)/undo.h  $(SRCDIR)/config.h
         1518  +	$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
         1519  +
         1520  +$(OBJDIR)/undo.h:	$(OBJDIR)/headers
         1521  +
         1522  +$(OBJDIR)/unicode_.c:	$(SRCDIR)/unicode.c $(OBJDIR)/translate
         1523  +	$(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
         1524  +
         1525  +$(OBJDIR)/unicode.o:	$(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h  $(SRCDIR)/config.h
         1526  +	$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
         1527  +
         1528  +$(OBJDIR)/unicode.h:	$(OBJDIR)/headers
         1529  +
         1530  +$(OBJDIR)/update_.c:	$(SRCDIR)/update.c $(OBJDIR)/translate
         1531  +	$(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
         1532  +
         1533  +$(OBJDIR)/update.o:	$(OBJDIR)/update_.c $(OBJDIR)/update.h  $(SRCDIR)/config.h
         1534  +	$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
         1535  +
         1536  +$(OBJDIR)/update.h:	$(OBJDIR)/headers
         1537  +
         1538  +$(OBJDIR)/url_.c:	$(SRCDIR)/url.c $(OBJDIR)/translate
         1539  +	$(TRANSLATE) $(SRCDIR)/url.c >$(OBJDIR)/url_.c
         1540  +
         1541  +$(OBJDIR)/url.o:	$(OBJDIR)/url_.c $(OBJDIR)/url.h  $(SRCDIR)/config.h
         1542  +	$(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c
         1543  +
         1544  +$(OBJDIR)/url.h:	$(OBJDIR)/headers
         1545  +
         1546  +$(OBJDIR)/user_.c:	$(SRCDIR)/user.c $(OBJDIR)/translate
         1547  +	$(TRANSLATE) $(SRCDIR)/user.c >$(OBJDIR)/user_.c
         1548  +
         1549  +$(OBJDIR)/user.o:	$(OBJDIR)/user_.c $(OBJDIR)/user.h  $(SRCDIR)/config.h
         1550  +	$(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c
         1551  +
         1552  +$(OBJDIR)/user.h:	$(OBJDIR)/headers
         1553  +
         1554  +$(OBJDIR)/utf8_.c:	$(SRCDIR)/utf8.c $(OBJDIR)/translate
         1555  +	$(TRANSLATE) $(SRCDIR)/utf8.c >$(OBJDIR)/utf8_.c
         1556  +
         1557  +$(OBJDIR)/utf8.o:	$(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h  $(SRCDIR)/config.h
         1558  +	$(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c
         1559  +
         1560  +$(OBJDIR)/utf8.h:	$(OBJDIR)/headers
         1561  +
         1562  +$(OBJDIR)/verify_.c:	$(SRCDIR)/verify.c $(OBJDIR)/translate
         1563  +	$(TRANSLATE) $(SRCDIR)/verify.c >$(OBJDIR)/verify_.c
         1564  +
         1565  +$(OBJDIR)/verify.o:	$(OBJDIR)/verify_.c $(OBJDIR)/verify.h  $(SRCDIR)/config.h
         1566  +	$(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c
         1567  +
         1568  +$(OBJDIR)/verify.h:	$(OBJDIR)/headers
         1569  +
         1570  +$(OBJDIR)/vfile_.c:	$(SRCDIR)/vfile.c $(OBJDIR)/translate
         1571  +	$(TRANSLATE) $(SRCDIR)/vfile.c >$(OBJDIR)/vfile_.c
         1572  +
         1573  +$(OBJDIR)/vfile.o:	$(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h  $(SRCDIR)/config.h
         1574  +	$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
         1575  +
         1576  +$(OBJDIR)/vfile.h:	$(OBJDIR)/headers
         1577  +
         1578  +$(OBJDIR)/wiki_.c:	$(SRCDIR)/wiki.c $(OBJDIR)/translate
         1579  +	$(TRANSLATE) $(SRCDIR)/wiki.c >$(OBJDIR)/wiki_.c
         1580  +
         1581  +$(OBJDIR)/wiki.o:	$(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h  $(SRCDIR)/config.h
         1582  +	$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
         1583  +
         1584  +$(OBJDIR)/wiki.h:	$(OBJDIR)/headers
         1585  +
         1586  +$(OBJDIR)/wikiformat_.c:	$(SRCDIR)/wikiformat.c $(OBJDIR)/translate
         1587  +	$(TRANSLATE) $(SRCDIR)/wikiformat.c >$(OBJDIR)/wikiformat_.c
         1588  +
         1589  +$(OBJDIR)/wikiformat.o:	$(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h  $(SRCDIR)/config.h
         1590  +	$(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c
         1591  +
         1592  +$(OBJDIR)/wikiformat.h:	$(OBJDIR)/headers
         1593  +
         1594  +$(OBJDIR)/winhttp_.c:	$(SRCDIR)/winhttp.c $(OBJDIR)/translate
         1595  +	$(TRANSLATE) $(SRCDIR)/winhttp.c >$(OBJDIR)/winhttp_.c
         1596  +
         1597  +$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h  $(SRCDIR)/config.h
         1598  +	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c
         1599  +
         1600  +$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers
         1601  +
         1602  +$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(OBJDIR)/translate
         1603  +	$(TRANSLATE) $(SRCDIR)/wysiwyg.c >$(OBJDIR)/wysiwyg_.c
         1604  +
         1605  +$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h  $(SRCDIR)/config.h
         1606  +	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c
         1607  +
         1608  +$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers
         1609  +
         1610  +$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(OBJDIR)/translate
         1611  +	$(TRANSLATE) $(SRCDIR)/xfer.c >$(OBJDIR)/xfer_.c
         1612  +
         1613  +$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h  $(SRCDIR)/config.h
         1614  +	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c
         1615  +
         1616  +$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
         1617  +
         1618  +$(OBJDIR)/xfersetup_.c:	$(SRCDIR)/xfersetup.c $(OBJDIR)/translate
         1619  +	$(TRANSLATE) $(SRCDIR)/xfersetup.c >$(OBJDIR)/xfersetup_.c
         1620  +
         1621  +$(OBJDIR)/xfersetup.o:	$(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h  $(SRCDIR)/config.h
         1622  +	$(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c
         1623  +
         1624  +$(OBJDIR)/xfersetup.h:	$(OBJDIR)/headers
         1625  +
         1626  +$(OBJDIR)/zip_.c:	$(SRCDIR)/zip.c $(OBJDIR)/translate
         1627  +	$(TRANSLATE) $(SRCDIR)/zip.c >$(OBJDIR)/zip_.c
         1628  +
         1629  +$(OBJDIR)/zip.o:	$(OBJDIR)/zip_.c $(OBJDIR)/zip.h  $(SRCDIR)/config.h
         1630  +	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
         1631  +
         1632  +$(OBJDIR)/zip.h:	$(OBJDIR)/headers
         1633  +
         1634  +$(OBJDIR)/sqlite3.o:	$(SRCDIR)/sqlite3.c
         1635  +	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
         1636  +
         1637  +$(OBJDIR)/cson_amalgamation.o:	$(SRCDIR)/cson_amalgamation.c
         1638  +	$(XTCC)  -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
         1639  +
         1640  +$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
         1641  +
         1642  +$(OBJDIR)/shell.o:	$(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
         1643  +	$(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
         1644  +
         1645  +$(OBJDIR)/th.o:	$(SRCDIR)/th.c
         1646  +	$(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
         1647  +
         1648  +$(OBJDIR)/th_lang.o:	$(SRCDIR)/th_lang.c
         1649  +	$(XTCC) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
         1650  +
         1651  +ifdef FOSSIL_ENABLE_TCL
         1652  +$(OBJDIR)/th_tcl.o:	$(SRCDIR)/th_tcl.c
         1653  +	$(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
         1654  +endif

Added win/Makefile.msc.

            1  +#
            2  +##############################################################################
            3  +# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
            4  +##############################################################################
            5  +#
            6  +# This file is automatically generated.  Instead of editing this
            7  +# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
            8  +# to regenerate this file.
            9  +#
           10  +B      = ..
           11  +SRCDIR = $B\src
           12  +OBJDIR = .
           13  +OX     = .
           14  +O      = .obj
           15  +E      = .exe
           16  +
           17  +# Uncomment below for SSL support
           18  +SSL =
           19  +SSLLIB =
           20  +# SSL = -DFOSSIL_ENABLE_SSL=1
           21  +# SSLLIB  = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
           22  +
           23  +# zlib options
           24  +ZINCDIR = $(B)\compat\zlib
           25  +ZLIBDIR = $(B)\compat\zlib
           26  +ZLIB    = zlib.lib
           27  +
           28  +# Uncomment to enable JSON API
           29  +# FOSSIL_ENABLE_JSON = 1
           30  +
           31  +# Uncomment to enable markdown support
           32  +# FOSSIL_ENABLE_MARKDOWN = 1
           33  +
           34  +INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
           35  +
           36  +CFLAGS = -nologo -MT -O2
           37  +BCC    = $(CC) $(CFLAGS)
           38  +TCC    = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
           39  +RCC    = rc -D_WIN32 -D_MSC_VER $(INCL)
           40  +LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
           41  +LIBDIR = -LIBPATH:$(ZLIBDIR)
           42  +
           43  +!ifdef FOSSIL_ENABLE_JSON
           44  +TCC = $(TCC) -DFOSSIL_ENABLE_JSON
           45  +RCC = $(RCC) -DFOSSIL_ENABLE_JSON
           46  +!endif
           47  +
           48  +!ifdef FOSSIL_ENABLE_MARKDOWN
           49  +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN
           50  +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN
           51  +!endif
           52  +
           53  +SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \
           54  +                 /DSQLITE_THREADSAFE=0 \
           55  +                 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
           56  +                 /DSQLITE_ENABLE_STAT3 \
           57  +                 /Dlocaltime=fossil_localtime \
           58  +                 /DSQLITE_ENABLE_LOCKING_STYLE=0
           59  +
           60  +SRC   = add_.c \
           61  +        allrepo_.c \
           62  +        attach_.c \
           63  +        bag_.c \
           64  +        bisect_.c \
           65  +        blob_.c \
           66  +        branch_.c \
           67  +        browse_.c \
           68  +        captcha_.c \
           69  +        cgi_.c \
           70  +        checkin_.c \
           71  +        checkout_.c \
           72  +        clearsign_.c \
           73  +        clone_.c \
           74  +        comformat_.c \
           75  +        configure_.c \
           76  +        content_.c \
           77  +        db_.c \
           78  +        delta_.c \
           79  +        deltacmd_.c \
           80  +        descendants_.c \
           81  +        diff_.c \
           82  +        diffcmd_.c \
           83  +        doc_.c \
           84  +        encode_.c \
           85  +        event_.c \
           86  +        export_.c \
           87  +        file_.c \
           88  +        finfo_.c \
           89  +        glob_.c \
           90  +        graph_.c \
           91  +        gzip_.c \
           92  +        http_.c \
           93  +        http_socket_.c \
           94  +        http_ssl_.c \
           95  +        http_transport_.c \
           96  +        import_.c \
           97  +        info_.c \
           98  +        json_.c \
           99  +        json_artifact_.c \
          100  +        json_branch_.c \
          101  +        json_config_.c \
          102  +        json_diff_.c \
          103  +        json_dir_.c \
          104  +        json_finfo_.c \
          105  +        json_login_.c \
          106  +        json_query_.c \
          107  +        json_report_.c \
          108  +        json_tag_.c \
          109  +        json_timeline_.c \
          110  +        json_user_.c \
          111  +        json_wiki_.c \
          112  +        leaf_.c \
          113  +        login_.c \
          114  +        main_.c \
          115  +        manifest_.c \
          116  +        markdown_.c \
          117  +        markdown_html_.c \
          118  +        md5_.c \
          119  +        merge_.c \
          120  +        merge3_.c \
          121  +        moderate_.c \
          122  +        name_.c \
          123  +        path_.c \
          124  +        pivot_.c \
          125  +        popen_.c \
          126  +        pqueue_.c \
          127  +        printf_.c \
          128  +        rebuild_.c \
          129  +        regexp_.c \
          130  +        report_.c \
          131  +        rss_.c \
          132  +        schema_.c \
          133  +        search_.c \
          134  +        setup_.c \
          135  +        sha1_.c \
          136  +        shun_.c \
          137  +        skins_.c \
          138  +        sqlcmd_.c \
          139  +        stash_.c \
          140  +        stat_.c \
          141  +        style_.c \
          142  +        sync_.c \
          143  +        tag_.c \
          144  +        tar_.c \
          145  +        th_main_.c \
          146  +        timeline_.c \
          147  +        tkt_.c \
          148  +        tktsetup_.c \
          149  +        undo_.c \
          150  +        unicode_.c \
          151  +        update_.c \
          152  +        url_.c \
          153  +        user_.c \
          154  +        utf8_.c \
          155  +        verify_.c \
          156  +        vfile_.c \
          157  +        wiki_.c \
          158  +        wikiformat_.c \
          159  +        winhttp_.c \
          160  +        wysiwyg_.c \
          161  +        xfer_.c \
          162  +        xfersetup_.c \
          163  +        zip_.c
          164  +
          165  +OBJ   = $(OX)\add$O \
          166  +        $(OX)\allrepo$O \
          167  +        $(OX)\attach$O \
          168  +        $(OX)\bag$O \
          169  +        $(OX)\bisect$O \
          170  +        $(OX)\blob$O \
          171  +        $(OX)\branch$O \
          172  +        $(OX)\browse$O \
          173  +        $(OX)\captcha$O \
          174  +        $(OX)\cgi$O \
          175  +        $(OX)\checkin$O \
          176  +        $(OX)\checkout$O \
          177  +        $(OX)\clearsign$O \
          178  +        $(OX)\clone$O \
          179  +        $(OX)\comformat$O \
          180  +        $(OX)\configure$O \
          181  +        $(OX)\content$O \
          182  +        $(OX)\cson_amalgamation$O \
          183  +        $(OX)\db$O \
          184  +        $(OX)\delta$O \
          185  +        $(OX)\deltacmd$O \
          186  +        $(OX)\descendants$O \
          187  +        $(OX)\diff$O \
          188  +        $(OX)\diffcmd$O \
          189  +        $(OX)\doc$O \
          190  +        $(OX)\encode$O \
          191  +        $(OX)\event$O \
          192  +        $(OX)\export$O \
          193  +        $(OX)\file$O \
          194  +        $(OX)\finfo$O \
          195  +        $(OX)\glob$O \
          196  +        $(OX)\graph$O \
          197  +        $(OX)\gzip$O \
          198  +        $(OX)\http$O \
          199  +        $(OX)\http_socket$O \
          200  +        $(OX)\http_ssl$O \
          201  +        $(OX)\http_transport$O \
          202  +        $(OX)\import$O \
          203  +        $(OX)\info$O \
          204  +        $(OX)\json$O \
          205  +        $(OX)\json_artifact$O \
          206  +        $(OX)\json_branch$O \
          207  +        $(OX)\json_config$O \
          208  +        $(OX)\json_diff$O \
          209  +        $(OX)\json_dir$O \
          210  +        $(OX)\json_finfo$O \
          211  +        $(OX)\json_login$O \
          212  +        $(OX)\json_query$O \
          213  +        $(OX)\json_report$O \
          214  +        $(OX)\json_tag$O \
          215  +        $(OX)\json_timeline$O \
          216  +        $(OX)\json_user$O \
          217  +        $(OX)\json_wiki$O \
          218  +        $(OX)\leaf$O \
          219  +        $(OX)\login$O \
          220  +        $(OX)\main$O \
          221  +        $(OX)\manifest$O \
          222  +        $(OX)\markdown$O \
          223  +        $(OX)\markdown_html$O \
          224  +        $(OX)\md5$O \
          225  +        $(OX)\merge$O \
          226  +        $(OX)\merge3$O \
          227  +        $(OX)\moderate$O \
          228  +        $(OX)\name$O \
          229  +        $(OX)\path$O \
          230  +        $(OX)\pivot$O \
          231  +        $(OX)\popen$O \
          232  +        $(OX)\pqueue$O \
          233  +        $(OX)\printf$O \
          234  +        $(OX)\rebuild$O \
          235  +        $(OX)\regexp$O \
          236  +        $(OX)\report$O \
          237  +        $(OX)\rss$O \
          238  +        $(OX)\schema$O \
          239  +        $(OX)\search$O \
          240  +        $(OX)\setup$O \
          241  +        $(OX)\sha1$O \
          242  +        $(OX)\shell$O \
          243  +        $(OX)\shun$O \
          244  +        $(OX)\skins$O \
          245  +        $(OX)\sqlcmd$O \
          246  +        $(OX)\sqlite3$O \
          247  +        $(OX)\stash$O \
          248  +        $(OX)\stat$O \
          249  +        $(OX)\style$O \
          250  +        $(OX)\sync$O \
          251  +        $(OX)\tag$O \
          252  +        $(OX)\tar$O \
          253  +        $(OX)\th$O \
          254  +        $(OX)\th_lang$O \
          255  +        $(OX)\th_main$O \
          256  +        $(OX)\timeline$O \
          257  +        $(OX)\tkt$O \
          258  +        $(OX)\tktsetup$O \
          259  +        $(OX)\undo$O \
          260  +        $(OX)\unicode$O \
          261  +        $(OX)\update$O \
          262  +        $(OX)\url$O \
          263  +        $(OX)\user$O \
          264  +        $(OX)\utf8$O \
          265  +        $(OX)\verify$O \
          266  +        $(OX)\vfile$O \
          267  +        $(OX)\wiki$O \
          268  +        $(OX)\wikiformat$O \
          269  +        $(OX)\winhttp$O \
          270  +        $(OX)\wysiwyg$O \
          271  +        $(OX)\xfer$O \
          272  +        $(OX)\xfersetup$O \
          273  +        $(OX)\zip$O \
          274  +        $(OX)\fossil.res
          275  +
          276  +APPNAME = $(OX)\fossil$(E)
          277  +
          278  +all: $(OX) $(APPNAME)
          279  +
          280  +zlib:
          281  +	@echo Building zlib from "$(ZLIBDIR)"...
          282  +	@pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd
          283  +
          284  +$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib
          285  +	cd $(OX) 
          286  +	link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
          287  +
          288  +$(OX)\linkopts: $B\win\Makefile.msc
          289  +	echo $(OX)\add.obj > $@
          290  +	echo $(OX)\allrepo.obj >> $@
          291  +	echo $(OX)\attach.obj >> $@
          292  +	echo $(OX)\bag.obj >> $@
          293  +	echo $(OX)\bisect.obj >> $@
          294  +	echo $(OX)\blob.obj >> $@
          295  +	echo $(OX)\branch.obj >> $@
          296  +	echo $(OX)\browse.obj >> $@
          297  +	echo $(OX)\captcha.obj >> $@
          298  +	echo $(OX)\cgi.obj >> $@
          299  +	echo $(OX)\checkin.obj >> $@
          300  +	echo $(OX)\checkout.obj >> $@
          301  +	echo $(OX)\clearsign.obj >> $@
          302  +	echo $(OX)\clone.obj >> $@
          303  +	echo $(OX)\comformat.obj >> $@
          304  +	echo $(OX)\configure.obj >> $@
          305  +	echo $(OX)\content.obj >> $@
          306  +	echo $(OX)\cson_amalgamation.obj >> $@
          307  +	echo $(OX)\db.obj >> $@
          308  +	echo $(OX)\delta.obj >> $@
          309  +	echo $(OX)\deltacmd.obj >> $@
          310  +	echo $(OX)\descendants.obj >> $@
          311  +	echo $(OX)\diff.obj >> $@
          312  +	echo $(OX)\diffcmd.obj >> $@
          313  +	echo $(OX)\doc.obj >> $@
          314  +	echo $(OX)\encode.obj >> $@
          315  +	echo $(OX)\event.obj >> $@
          316  +	echo $(OX)\export.obj >> $@
          317  +	echo $(OX)\file.obj >> $@
          318  +	echo $(OX)\finfo.obj >> $@
          319  +	echo $(OX)\glob.obj >> $@
          320  +	echo $(OX)\graph.obj >> $@
          321  +	echo $(OX)\gzip.obj >> $@
          322  +	echo $(OX)\http.obj >> $@
          323  +	echo $(OX)\http_socket.obj >> $@
          324  +	echo $(OX)\http_ssl.obj >> $@
          325  +	echo $(OX)\http_transport.obj >> $@
          326  +	echo $(OX)\import.obj >> $@
          327  +	echo $(OX)\info.obj >> $@
          328  +	echo $(OX)\json.obj >> $@
          329  +	echo $(OX)\json_artifact.obj >> $@
          330  +	echo $(OX)\json_branch.obj >> $@
          331  +	echo $(OX)\json_config.obj >> $@
          332  +	echo $(OX)\json_diff.obj >> $@
          333  +	echo $(OX)\json_dir.obj >> $@
          334  +	echo $(OX)\json_finfo.obj >> $@
          335  +	echo $(OX)\json_login.obj >> $@
          336  +	echo $(OX)\json_query.obj >> $@
          337  +	echo $(OX)\json_report.obj >> $@
          338  +	echo $(OX)\json_tag.obj >> $@
          339  +	echo $(OX)\json_timeline.obj >> $@
          340  +	echo $(OX)\json_user.obj >> $@
          341  +	echo $(OX)\json_wiki.obj >> $@
          342  +	echo $(OX)\leaf.obj >> $@
          343  +	echo $(OX)\login.obj >> $@
          344  +	echo $(OX)\main.obj >> $@
          345  +	echo $(OX)\manifest.obj >> $@
          346  +	echo $(OX)\markdown.obj >> $@
          347  +	echo $(OX)\markdown_html.obj >> $@
          348  +	echo $(OX)\md5.obj >> $@
          349  +	echo $(OX)\merge.obj >> $@
          350  +	echo $(OX)\merge3.obj >> $@
          351  +	echo $(OX)\moderate.obj >> $@
          352  +	echo $(OX)\name.obj >> $@
          353  +	echo $(OX)\path.obj >> $@
          354  +	echo $(OX)\pivot.obj >> $@
          355  +	echo $(OX)\popen.obj >> $@
          356  +	echo $(OX)\pqueue.obj >> $@
          357  +	echo $(OX)\printf.obj >> $@
          358  +	echo $(OX)\rebuild.obj >> $@
          359  +	echo $(OX)\regexp.obj >> $@
          360  +	echo $(OX)\report.obj >> $@
          361  +	echo $(OX)\rss.obj >> $@
          362  +	echo $(OX)\schema.obj >> $@
          363  +	echo $(OX)\search.obj >> $@
          364  +	echo $(OX)\setup.obj >> $@
          365  +	echo $(OX)\sha1.obj >> $@
          366  +	echo $(OX)\shell.obj >> $@
          367  +	echo $(OX)\shun.obj >> $@
          368  +	echo $(OX)\skins.obj >> $@
          369  +	echo $(OX)\sqlcmd.obj >> $@
          370  +	echo $(OX)\sqlite3.obj >> $@
          371  +	echo $(OX)\stash.obj >> $@
          372  +	echo $(OX)\stat.obj >> $@
          373  +	echo $(OX)\style.obj >> $@
          374  +	echo $(OX)\sync.obj >> $@
          375  +	echo $(OX)\tag.obj >> $@
          376  +	echo $(OX)\tar.obj >> $@
          377  +	echo $(OX)\th.obj >> $@
          378  +	echo $(OX)\th_lang.obj >> $@
          379  +	echo $(OX)\th_main.obj >> $@
          380  +	echo $(OX)\timeline.obj >> $@
          381  +	echo $(OX)\tkt.obj >> $@
          382  +	echo $(OX)\tktsetup.obj >> $@
          383  +	echo $(OX)\undo.obj >> $@
          384  +	echo $(OX)\unicode.obj >> $@
          385  +	echo $(OX)\update.obj >> $@
          386  +	echo $(OX)\url.obj >> $@
          387  +	echo $(OX)\user.obj >> $@
          388  +	echo $(OX)\utf8.obj >> $@
          389  +	echo $(OX)\verify.obj >> $@
          390  +	echo $(OX)\vfile.obj >> $@
          391  +	echo $(OX)\wiki.obj >> $@
          392  +	echo $(OX)\wikiformat.obj >> $@
          393  +	echo $(OX)\winhttp.obj >> $@
          394  +	echo $(OX)\wysiwyg.obj >> $@
          395  +	echo $(OX)\xfer.obj >> $@
          396  +	echo $(OX)\xfersetup.obj >> $@
          397  +	echo $(OX)\zip.obj >> $@
          398  +	echo $(LIBS) >> $@
          399  +
          400  +
          401  +
          402  +
          403  +$(OX):
          404  +	@-mkdir $@
          405  +
          406  +translate$E: $(SRCDIR)\translate.c
          407  +	$(BCC) $**
          408  +
          409  +makeheaders$E: $(SRCDIR)\makeheaders.c
          410  +	$(BCC) $**
          411  +
          412  +mkindex$E: $(SRCDIR)\mkindex.c
          413  +	$(BCC) $**
          414  +
          415  +mkversion$E: $B\src\mkversion.c
          416  +	$(BCC) $**
          417  +
          418  +$(OX)\shell$O : $(SRCDIR)\shell.c
          419  +	$(TCC) /Fo$@ /Dmain=sqlite3_shell $(SQLITE_OPTIONS) -c $(SRCDIR)\shell.c
          420  +
          421  +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
          422  +	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $**
          423  +
          424  +$(OX)\th$O : $(SRCDIR)\th.c
          425  +	$(TCC) /Fo$@ -c $**
          426  +
          427  +$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
          428  +	$(TCC) /Fo$@ -c $**
          429  +
          430  +VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
          431  +	$** > $@
          432  +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c
          433  +	$(TCC) /Fo$@ -c $**
          434  +
          435  +page_index.h: mkindex$E $(SRC) 
          436  +	$** > $@
          437  +
          438  +clean:
          439  +	-del $(OX)\*.obj
          440  +	-del *.obj
          441  +	-del *_.c
          442  +	-del *.h
          443  +	-del *.map
          444  +	-del *.manifest
          445  +	-del headers
          446  +	-del linkopts
          447  +	-del *.res
          448  +
          449  +realclean: clean
          450  +	-del $(APPNAME)
          451  +	-del translate$E
          452  +	-del mkindex$E
          453  +	-del makeheaders$E
          454  +	-del mkversion$E
          455  +
          456  +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
          457  +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
          458  +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
          459  +$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
          460  +$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
          461  +$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
          462  +$(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h
          463  +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
          464  +$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
          465  +$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
          466  +$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
          467  +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
          468  +$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
          469  +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
          470  +
          471  +
          472  +$(OX)\add$O : add_.c add.h
          473  +	$(TCC) /Fo$@ -c add_.c
          474  +
          475  +add_.c : $(SRCDIR)\add.c
          476  +	translate$E $** > $@
          477  +
          478  +$(OX)\allrepo$O : allrepo_.c allrepo.h
          479  +	$(TCC) /Fo$@ -c allrepo_.c
          480  +
          481  +allrepo_.c : $(SRCDIR)\allrepo.c
          482  +	translate$E $** > $@
          483  +
          484  +$(OX)\attach$O : attach_.c attach.h
          485  +	$(TCC) /Fo$@ -c attach_.c
          486  +
          487  +attach_.c : $(SRCDIR)\attach.c
          488  +	translate$E $** > $@
          489  +
          490  +$(OX)\bag$O : bag_.c bag.h
          491  +	$(TCC) /Fo$@ -c bag_.c
          492  +
          493  +bag_.c : $(SRCDIR)\bag.c
          494  +	translate$E $** > $@
          495  +
          496  +$(OX)\bisect$O : bisect_.c bisect.h
          497  +	$(TCC) /Fo$@ -c bisect_.c
          498  +
          499  +bisect_.c : $(SRCDIR)\bisect.c
          500  +	translate$E $** > $@
          501  +
          502  +$(OX)\blob$O : blob_.c blob.h
          503  +	$(TCC) /Fo$@ -c blob_.c
          504  +
          505  +blob_.c : $(SRCDIR)\blob.c
          506  +	translate$E $** > $@
          507  +
          508  +$(OX)\branch$O : branch_.c branch.h
          509  +	$(TCC) /Fo$@ -c branch_.c
          510  +
          511  +branch_.c : $(SRCDIR)\branch.c
          512  +	translate$E $** > $@
          513  +
          514  +$(OX)\browse$O : browse_.c browse.h
          515  +	$(TCC) /Fo$@ -c browse_.c
          516  +
          517  +browse_.c : $(SRCDIR)\browse.c
          518  +	translate$E $** > $@
          519  +
          520  +$(OX)\captcha$O : captcha_.c captcha.h
          521  +	$(TCC) /Fo$@ -c captcha_.c
          522  +
          523  +captcha_.c : $(SRCDIR)\captcha.c
          524  +	translate$E $** > $@
          525  +
          526  +$(OX)\cgi$O : cgi_.c cgi.h
          527  +	$(TCC) /Fo$@ -c cgi_.c
          528  +
          529  +cgi_.c : $(SRCDIR)\cgi.c
          530  +	translate$E $** > $@
          531  +
          532  +$(OX)\checkin$O : checkin_.c checkin.h
          533  +	$(TCC) /Fo$@ -c checkin_.c
          534  +
          535  +checkin_.c : $(SRCDIR)\checkin.c
          536  +	translate$E $** > $@
          537  +
          538  +$(OX)\checkout$O : checkout_.c checkout.h
          539  +	$(TCC) /Fo$@ -c checkout_.c
          540  +
          541  +checkout_.c : $(SRCDIR)\checkout.c
          542  +	translate$E $** > $@
          543  +
          544  +$(OX)\clearsign$O : clearsign_.c clearsign.h
          545  +	$(TCC) /Fo$@ -c clearsign_.c
          546  +
          547  +clearsign_.c : $(SRCDIR)\clearsign.c
          548  +	translate$E $** > $@
          549  +
          550  +$(OX)\clone$O : clone_.c clone.h
          551  +	$(TCC) /Fo$@ -c clone_.c
          552  +
          553  +clone_.c : $(SRCDIR)\clone.c
          554  +	translate$E $** > $@
          555  +
          556  +$(OX)\comformat$O : comformat_.c comformat.h
          557  +	$(TCC) /Fo$@ -c comformat_.c
          558  +
          559  +comformat_.c : $(SRCDIR)\comformat.c
          560  +	translate$E $** > $@
          561  +
          562  +$(OX)\configure$O : configure_.c configure.h
          563  +	$(TCC) /Fo$@ -c configure_.c
          564  +
          565  +configure_.c : $(SRCDIR)\configure.c
          566  +	translate$E $** > $@
          567  +
          568  +$(OX)\content$O : content_.c content.h
          569  +	$(TCC) /Fo$@ -c content_.c
          570  +
          571  +content_.c : $(SRCDIR)\content.c
          572  +	translate$E $** > $@
          573  +
          574  +$(OX)\db$O : db_.c db.h
          575  +	$(TCC) /Fo$@ -c db_.c
          576  +
          577  +db_.c : $(SRCDIR)\db.c
          578  +	translate$E $** > $@
          579  +
          580  +$(OX)\delta$O : delta_.c delta.h
          581  +	$(TCC) /Fo$@ -c delta_.c
          582  +
          583  +delta_.c : $(SRCDIR)\delta.c
          584  +	translate$E $** > $@
          585  +
          586  +$(OX)\deltacmd$O : deltacmd_.c deltacmd.h
          587  +	$(TCC) /Fo$@ -c deltacmd_.c
          588  +
          589  +deltacmd_.c : $(SRCDIR)\deltacmd.c
          590  +	translate$E $** > $@
          591  +
          592  +$(OX)\descendants$O : descendants_.c descendants.h
          593  +	$(TCC) /Fo$@ -c descendants_.c
          594  +
          595  +descendants_.c : $(SRCDIR)\descendants.c
          596  +	translate$E $** > $@
          597  +
          598  +$(OX)\diff$O : diff_.c diff.h
          599  +	$(TCC) /Fo$@ -c diff_.c
          600  +
          601  +diff_.c : $(SRCDIR)\diff.c
          602  +	translate$E $** > $@
          603  +
          604  +$(OX)\diffcmd$O : diffcmd_.c diffcmd.h
          605  +	$(TCC) /Fo$@ -c diffcmd_.c
          606  +
          607  +diffcmd_.c : $(SRCDIR)\diffcmd.c
          608  +	translate$E $** > $@
          609  +
          610  +$(OX)\doc$O : doc_.c doc.h
          611  +	$(TCC) /Fo$@ -c doc_.c
          612  +
          613  +doc_.c : $(SRCDIR)\doc.c
          614  +	translate$E $** > $@
          615  +
          616  +$(OX)\encode$O : encode_.c encode.h
          617  +	$(TCC) /Fo$@ -c encode_.c
          618  +
          619  +encode_.c : $(SRCDIR)\encode.c
          620  +	translate$E $** > $@
          621  +
          622  +$(OX)\event$O : event_.c event.h
          623  +	$(TCC) /Fo$@ -c event_.c
          624  +
          625  +event_.c : $(SRCDIR)\event.c
          626  +	translate$E $** > $@
          627  +
          628  +$(OX)\export$O : export_.c export.h
          629  +	$(TCC) /Fo$@ -c export_.c
          630  +
          631  +export_.c : $(SRCDIR)\export.c
          632  +	translate$E $** > $@
          633  +
          634  +$(OX)\file$O : file_.c file.h
          635  +	$(TCC) /Fo$@ -c file_.c
          636  +
          637  +file_.c : $(SRCDIR)\file.c
          638  +	translate$E $** > $@
          639  +
          640  +$(OX)\finfo$O : finfo_.c finfo.h
          641  +	$(TCC) /Fo$@ -c finfo_.c
          642  +
          643  +finfo_.c : $(SRCDIR)\finfo.c
          644  +	translate$E $** > $@
          645  +
          646  +$(OX)\glob$O : glob_.c glob.h
          647  +	$(TCC) /Fo$@ -c glob_.c
          648  +
          649  +glob_.c : $(SRCDIR)\glob.c
          650  +	translate$E $** > $@
          651  +
          652  +$(OX)\graph$O : graph_.c graph.h
          653  +	$(TCC) /Fo$@ -c graph_.c
          654  +
          655  +graph_.c : $(SRCDIR)\graph.c
          656  +	translate$E $** > $@
          657  +
          658  +$(OX)\gzip$O : gzip_.c gzip.h
          659  +	$(TCC) /Fo$@ -c gzip_.c
          660  +
          661  +gzip_.c : $(SRCDIR)\gzip.c
          662  +	translate$E $** > $@
          663  +
          664  +$(OX)\http$O : http_.c http.h
          665  +	$(TCC) /Fo$@ -c http_.c
          666  +
          667  +http_.c : $(SRCDIR)\http.c
          668  +	translate$E $** > $@
          669  +
          670  +$(OX)\http_socket$O : http_socket_.c http_socket.h
          671  +	$(TCC) /Fo$@ -c http_socket_.c
          672  +
          673  +http_socket_.c : $(SRCDIR)\http_socket.c
          674  +	translate$E $** > $@
          675  +
          676  +$(OX)\http_ssl$O : http_ssl_.c http_ssl.h
          677  +	$(TCC) /Fo$@ -c http_ssl_.c
          678  +
          679  +http_ssl_.c : $(SRCDIR)\http_ssl.c
          680  +	translate$E $** > $@
          681  +
          682  +$(OX)\http_transport$O : http_transport_.c http_transport.h
          683  +	$(TCC) /Fo$@ -c http_transport_.c
          684  +
          685  +http_transport_.c : $(SRCDIR)\http_transport.c
          686  +	translate$E $** > $@
          687  +
          688  +$(OX)\import$O : import_.c import.h
          689  +	$(TCC) /Fo$@ -c import_.c
          690  +
          691  +import_.c : $(SRCDIR)\import.c
          692  +	translate$E $** > $@
          693  +
          694  +$(OX)\info$O : info_.c info.h
          695  +	$(TCC) /Fo$@ -c info_.c
          696  +
          697  +info_.c : $(SRCDIR)\info.c
          698  +	translate$E $** > $@
          699  +
          700  +$(OX)\json$O : json_.c json.h
          701  +	$(TCC) /Fo$@ -c json_.c
          702  +
          703  +json_.c : $(SRCDIR)\json.c
          704  +	translate$E $** > $@
          705  +
          706  +$(OX)\json_artifact$O : json_artifact_.c json_artifact.h
          707  +	$(TCC) /Fo$@ -c json_artifact_.c
          708  +
          709  +json_artifact_.c : $(SRCDIR)\json_artifact.c
          710  +	translate$E $** > $@
          711  +
          712  +$(OX)\json_branch$O : json_branch_.c json_branch.h
          713  +	$(TCC) /Fo$@ -c json_branch_.c
          714  +
          715  +json_branch_.c : $(SRCDIR)\json_branch.c
          716  +	translate$E $** > $@
          717  +
          718  +$(OX)\json_config$O : json_config_.c json_config.h
          719  +	$(TCC) /Fo$@ -c json_config_.c
          720  +
          721  +json_config_.c : $(SRCDIR)\json_config.c
          722  +	translate$E $** > $@
          723  +
          724  +$(OX)\json_diff$O : json_diff_.c json_diff.h
          725  +	$(TCC) /Fo$@ -c json_diff_.c
          726  +
          727  +json_diff_.c : $(SRCDIR)\json_diff.c
          728  +	translate$E $** > $@
          729  +
          730  +$(OX)\json_dir$O : json_dir_.c json_dir.h
          731  +	$(TCC) /Fo$@ -c json_dir_.c
          732  +
          733  +json_dir_.c : $(SRCDIR)\json_dir.c
          734  +	translate$E $** > $@
          735  +
          736  +$(OX)\json_finfo$O : json_finfo_.c json_finfo.h
          737  +	$(TCC) /Fo$@ -c json_finfo_.c
          738  +
          739  +json_finfo_.c : $(SRCDIR)\json_finfo.c
          740  +	translate$E $** > $@
          741  +
          742  +$(OX)\json_login$O : json_login_.c json_login.h
          743  +	$(TCC) /Fo$@ -c json_login_.c
          744  +
          745  +json_login_.c : $(SRCDIR)\json_login.c
          746  +	translate$E $** > $@
          747  +
          748  +$(OX)\json_query$O : json_query_.c json_query.h
          749  +	$(TCC) /Fo$@ -c json_query_.c
          750  +
          751  +json_query_.c : $(SRCDIR)\json_query.c
          752  +	translate$E $** > $@
          753  +
          754  +$(OX)\json_report$O : json_report_.c json_report.h
          755  +	$(TCC) /Fo$@ -c json_report_.c
          756  +
          757  +json_report_.c : $(SRCDIR)\json_report.c
          758  +	translate$E $** > $@
          759  +
          760  +$(OX)\json_tag$O : json_tag_.c json_tag.h
          761  +	$(TCC) /Fo$@ -c json_tag_.c
          762  +
          763  +json_tag_.c : $(SRCDIR)\json_tag.c
          764  +	translate$E $** > $@
          765  +
          766  +$(OX)\json_timeline$O : json_timeline_.c json_timeline.h
          767  +	$(TCC) /Fo$@ -c json_timeline_.c
          768  +
          769  +json_timeline_.c : $(SRCDIR)\json_timeline.c
          770  +	translate$E $** > $@
          771  +
          772  +$(OX)\json_user$O : json_user_.c json_user.h
          773  +	$(TCC) /Fo$@ -c json_user_.c
          774  +
          775  +json_user_.c : $(SRCDIR)\json_user.c
          776  +	translate$E $** > $@
          777  +
          778  +$(OX)\json_wiki$O : json_wiki_.c json_wiki.h
          779  +	$(TCC) /Fo$@ -c json_wiki_.c
          780  +
          781  +json_wiki_.c : $(SRCDIR)\json_wiki.c
          782  +	translate$E $** > $@
          783  +
          784  +$(OX)\leaf$O : leaf_.c leaf.h
          785  +	$(TCC) /Fo$@ -c leaf_.c
          786  +
          787  +leaf_.c : $(SRCDIR)\leaf.c
          788  +	translate$E $** > $@
          789  +
          790  +$(OX)\login$O : login_.c login.h
          791  +	$(TCC) /Fo$@ -c login_.c
          792  +
          793  +login_.c : $(SRCDIR)\login.c
          794  +	translate$E $** > $@
          795  +
          796  +$(OX)\main$O : main_.c main.h
          797  +	$(TCC) /Fo$@ -c main_.c
          798  +
          799  +main_.c : $(SRCDIR)\main.c
          800  +	translate$E $** > $@
          801  +
          802  +$(OX)\manifest$O : manifest_.c manifest.h
          803  +	$(TCC) /Fo$@ -c manifest_.c
          804  +
          805  +manifest_.c : $(SRCDIR)\manifest.c
          806  +	translate$E $** > $@
          807  +
          808  +$(OX)\markdown$O : markdown_.c markdown.h
          809  +	$(TCC) /Fo$@ -c markdown_.c
          810  +
          811  +markdown_.c : $(SRCDIR)\markdown.c
          812  +	translate$E $** > $@
          813  +
          814  +$(OX)\markdown_html$O : markdown_html_.c markdown_html.h
          815  +	$(TCC) /Fo$@ -c markdown_html_.c
          816  +
          817  +markdown_html_.c : $(SRCDIR)\markdown_html.c
          818  +	translate$E $** > $@
          819  +
          820  +$(OX)\md5$O : md5_.c md5.h
          821  +	$(TCC) /Fo$@ -c md5_.c
          822  +
          823  +md5_.c : $(SRCDIR)\md5.c
          824  +	translate$E $** > $@
          825  +
          826  +$(OX)\merge$O : merge_.c merge.h
          827  +	$(TCC) /Fo$@ -c merge_.c
          828  +
          829  +merge_.c : $(SRCDIR)\merge.c
          830  +	translate$E $** > $@
          831  +
          832  +$(OX)\merge3$O : merge3_.c merge3.h
          833  +	$(TCC) /Fo$@ -c merge3_.c
          834  +
          835  +merge3_.c : $(SRCDIR)\merge3.c
          836  +	translate$E $** > $@
          837  +
          838  +$(OX)\moderate$O : moderate_.c moderate.h
          839  +	$(TCC) /Fo$@ -c moderate_.c
          840  +
          841  +moderate_.c : $(SRCDIR)\moderate.c
          842  +	translate$E $** > $@
          843  +
          844  +$(OX)\name$O : name_.c name.h
          845  +	$(TCC) /Fo$@ -c name_.c
          846  +
          847  +name_.c : $(SRCDIR)\name.c
          848  +	translate$E $** > $@
          849  +
          850  +$(OX)\path$O : path_.c path.h
          851  +	$(TCC) /Fo$@ -c path_.c
          852  +
          853  +path_.c : $(SRCDIR)\path.c
          854  +	translate$E $** > $@
          855  +
          856  +$(OX)\pivot$O : pivot_.c pivot.h
          857  +	$(TCC) /Fo$@ -c pivot_.c
          858  +
          859  +pivot_.c : $(SRCDIR)\pivot.c
          860  +	translate$E $** > $@
          861  +
          862  +$(OX)\popen$O : popen_.c popen.h
          863  +	$(TCC) /Fo$@ -c popen_.c
          864  +
          865  +popen_.c : $(SRCDIR)\popen.c
          866  +	translate$E $** > $@
          867  +
          868  +$(OX)\pqueue$O : pqueue_.c pqueue.h
          869  +	$(TCC) /Fo$@ -c pqueue_.c
          870  +
          871  +pqueue_.c : $(SRCDIR)\pqueue.c
          872  +	translate$E $** > $@
          873  +
          874  +$(OX)\printf$O : printf_.c printf.h
          875  +	$(TCC) /Fo$@ -c printf_.c
          876  +
          877  +printf_.c : $(SRCDIR)\printf.c
          878  +	translate$E $** > $@
          879  +
          880  +$(OX)\rebuild$O : rebuild_.c rebuild.h
          881  +	$(TCC) /Fo$@ -c rebuild_.c
          882  +
          883  +rebuild_.c : $(SRCDIR)\rebuild.c
          884  +	translate$E $** > $@
          885  +
          886  +$(OX)\regexp$O : regexp_.c regexp.h
          887  +	$(TCC) /Fo$@ -c regexp_.c
          888  +
          889  +regexp_.c : $(SRCDIR)\regexp.c
          890  +	translate$E $** > $@
          891  +
          892  +$(OX)\report$O : report_.c report.h
          893  +	$(TCC) /Fo$@ -c report_.c
          894  +
          895  +report_.c : $(SRCDIR)\report.c
          896  +	translate$E $** > $@
          897  +
          898  +$(OX)\rss$O : rss_.c rss.h
          899  +	$(TCC) /Fo$@ -c rss_.c
          900  +
          901  +rss_.c : $(SRCDIR)\rss.c
          902  +	translate$E $** > $@
          903  +
          904  +$(OX)\schema$O : schema_.c schema.h
          905  +	$(TCC) /Fo$@ -c schema_.c
          906  +
          907  +schema_.c : $(SRCDIR)\schema.c
          908  +	translate$E $** > $@
          909  +
          910  +$(OX)\search$O : search_.c search.h
          911  +	$(TCC) /Fo$@ -c search_.c
          912  +
          913  +search_.c : $(SRCDIR)\search.c
          914  +	translate$E $** > $@
          915  +
          916  +$(OX)\setup$O : setup_.c setup.h
          917  +	$(TCC) /Fo$@ -c setup_.c
          918  +
          919  +setup_.c : $(SRCDIR)\setup.c
          920  +	translate$E $** > $@
          921  +
          922  +$(OX)\sha1$O : sha1_.c sha1.h
          923  +	$(TCC) /Fo$@ -c sha1_.c
          924  +
          925  +sha1_.c : $(SRCDIR)\sha1.c
          926  +	translate$E $** > $@
          927  +
          928  +$(OX)\shun$O : shun_.c shun.h
          929  +	$(TCC) /Fo$@ -c shun_.c
          930  +
          931  +shun_.c : $(SRCDIR)\shun.c
          932  +	translate$E $** > $@
          933  +
          934  +$(OX)\skins$O : skins_.c skins.h
          935  +	$(TCC) /Fo$@ -c skins_.c
          936  +
          937  +skins_.c : $(SRCDIR)\skins.c
          938  +	translate$E $** > $@
          939  +
          940  +$(OX)\sqlcmd$O : sqlcmd_.c sqlcmd.h
          941  +	$(TCC) /Fo$@ -c sqlcmd_.c
          942  +
          943  +sqlcmd_.c : $(SRCDIR)\sqlcmd.c
          944  +	translate$E $** > $@
          945  +
          946  +$(OX)\stash$O : stash_.c stash.h
          947  +	$(TCC) /Fo$@ -c stash_.c
          948  +
          949  +stash_.c : $(SRCDIR)\stash.c
          950  +	translate$E $** > $@
          951  +
          952  +$(OX)\stat$O : stat_.c stat.h
          953  +	$(TCC) /Fo$@ -c stat_.c
          954  +
          955  +stat_.c : $(SRCDIR)\stat.c
          956  +	translate$E $** > $@
          957  +
          958  +$(OX)\style$O : style_.c style.h
          959  +	$(TCC) /Fo$@ -c style_.c
          960  +
          961  +style_.c : $(SRCDIR)\style.c
          962  +	translate$E $** > $@
          963  +
          964  +$(OX)\sync$O : sync_.c sync.h
          965  +	$(TCC) /Fo$@ -c sync_.c
          966  +
          967  +sync_.c : $(SRCDIR)\sync.c
          968  +	translate$E $** > $@
          969  +
          970  +$(OX)\tag$O : tag_.c tag.h
          971  +	$(TCC) /Fo$@ -c tag_.c
          972  +
          973  +tag_.c : $(SRCDIR)\tag.c
          974  +	translate$E $** > $@
          975  +
          976  +$(OX)\tar$O : tar_.c tar.h
          977  +	$(TCC) /Fo$@ -c tar_.c
          978  +
          979  +tar_.c : $(SRCDIR)\tar.c
          980  +	translate$E $** > $@
          981  +
          982  +$(OX)\th_main$O : th_main_.c th_main.h
          983  +	$(TCC) /Fo$@ -c th_main_.c
          984  +
          985  +th_main_.c : $(SRCDIR)\th_main.c
          986  +	translate$E $** > $@
          987  +
          988  +$(OX)\timeline$O : timeline_.c timeline.h
          989  +	$(TCC) /Fo$@ -c timeline_.c
          990  +
          991  +timeline_.c : $(SRCDIR)\timeline.c
          992  +	translate$E $** > $@
          993  +
          994  +$(OX)\tkt$O : tkt_.c tkt.h
          995  +	$(TCC) /Fo$@ -c tkt_.c
          996  +
          997  +tkt_.c : $(SRCDIR)\tkt.c
          998  +	translate$E $** > $@
          999  +
         1000  +$(OX)\tktsetup$O : tktsetup_.c tktsetup.h
         1001  +	$(TCC) /Fo$@ -c tktsetup_.c
         1002  +
         1003  +tktsetup_.c : $(SRCDIR)\tktsetup.c
         1004  +	translate$E $** > $@
         1005  +
         1006  +$(OX)\undo$O : undo_.c undo.h
         1007  +	$(TCC) /Fo$@ -c undo_.c
         1008  +
         1009  +undo_.c : $(SRCDIR)\undo.c
         1010  +	translate$E $** > $@
         1011  +
         1012  +$(OX)\unicode$O : unicode_.c unicode.h
         1013  +	$(TCC) /Fo$@ -c unicode_.c
         1014  +
         1015  +unicode_.c : $(SRCDIR)\unicode.c
         1016  +	translate$E $** > $@
         1017  +
         1018  +$(OX)\update$O : update_.c update.h
         1019  +	$(TCC) /Fo$@ -c update_.c
         1020  +
         1021  +update_.c : $(SRCDIR)\update.c
         1022  +	translate$E $** > $@
         1023  +
         1024  +$(OX)\url$O : url_.c url.h
         1025  +	$(TCC) /Fo$@ -c url_.c
         1026  +
         1027  +url_.c : $(SRCDIR)\url.c
         1028  +	translate$E $** > $@
         1029  +
         1030  +$(OX)\user$O : user_.c user.h
         1031  +	$(TCC) /Fo$@ -c user_.c
         1032  +
         1033  +user_.c : $(SRCDIR)\user.c
         1034  +	translate$E $** > $@
         1035  +
         1036  +$(OX)\utf8$O : utf8_.c utf8.h
         1037  +	$(TCC) /Fo$@ -c utf8_.c
         1038  +
         1039  +utf8_.c : $(SRCDIR)\utf8.c
         1040  +	translate$E $** > $@
         1041  +
         1042  +$(OX)\verify$O : verify_.c verify.h
         1043  +	$(TCC) /Fo$@ -c verify_.c
         1044  +
         1045  +verify_.c : $(SRCDIR)\verify.c
         1046  +	translate$E $** > $@
         1047  +
         1048  +$(OX)\vfile$O : vfile_.c vfile.h
         1049  +	$(TCC) /Fo$@ -c vfile_.c
         1050  +
         1051  +vfile_.c : $(SRCDIR)\vfile.c
         1052  +	translate$E $** > $@
         1053  +
         1054  +$(OX)\wiki$O : wiki_.c wiki.h
         1055  +	$(TCC) /Fo$@ -c wiki_.c
         1056  +
         1057  +wiki_.c : $(SRCDIR)\wiki.c
         1058  +	translate$E $** > $@
         1059  +
         1060  +$(OX)\wikiformat$O : wikiformat_.c wikiformat.h
         1061  +	$(TCC) /Fo$@ -c wikiformat_.c
         1062  +
         1063  +wikiformat_.c : $(SRCDIR)\wikiformat.c
         1064  +	translate$E $** > $@
         1065  +
         1066  +$(OX)\winhttp$O : winhttp_.c winhttp.h
         1067  +	$(TCC) /Fo$@ -c winhttp_.c
         1068  +
         1069  +winhttp_.c : $(SRCDIR)\winhttp.c
         1070  +	translate$E $** > $@
         1071  +
         1072  +$(OX)\wysiwyg$O : wysiwyg_.c wysiwyg.h
         1073  +	$(TCC) /Fo$@ -c wysiwyg_.c
         1074  +
         1075  +wysiwyg_.c : $(SRCDIR)\wysiwyg.c
         1076  +	translate$E $** > $@
         1077  +
         1078  +$(OX)\xfer$O : xfer_.c xfer.h
         1079  +	$(TCC) /Fo$@ -c xfer_.c
         1080  +
         1081  +xfer_.c : $(SRCDIR)\xfer.c
         1082  +	translate$E $** > $@
         1083  +
         1084  +$(OX)\xfersetup$O : xfersetup_.c xfersetup.h
         1085  +	$(TCC) /Fo$@ -c xfersetup_.c
         1086  +
         1087  +xfersetup_.c : $(SRCDIR)\xfersetup.c
         1088  +	translate$E $** > $@
         1089  +
         1090  +$(OX)\zip$O : zip_.c zip.h
         1091  +	$(TCC) /Fo$@ -c zip_.c
         1092  +
         1093  +zip_.c : $(SRCDIR)\zip.c
         1094  +	translate$E $** > $@
         1095  +
         1096  +fossil.res : $B\win\fossil.rc
         1097  +	$(RCC)  -fo $@ $**
         1098  +headers: makeheaders$E page_index.h VERSION.h
         1099  +	makeheaders$E add_.c:add.h \
         1100  +			allrepo_.c:allrepo.h \
         1101  +			attach_.c:attach.h \
         1102  +			bag_.c:bag.h \
         1103  +			bisect_.c:bisect.h \
         1104  +			blob_.c:blob.h \
         1105  +			branch_.c:branch.h \
         1106  +			browse_.c:browse.h \
         1107  +			captcha_.c:captcha.h \
         1108  +			cgi_.c:cgi.h \
         1109  +			checkin_.c:checkin.h \
         1110  +			checkout_.c:checkout.h \
         1111  +			clearsign_.c:clearsign.h \
         1112  +			clone_.c:clone.h \
         1113  +			comformat_.c:comformat.h \
         1114  +			configure_.c:configure.h \
         1115  +			content_.c:content.h \
         1116  +			db_.c:db.h \
         1117  +			delta_.c:delta.h \
         1118  +			deltacmd_.c:deltacmd.h \
         1119  +			descendants_.c:descendants.h \
         1120  +			diff_.c:diff.h \
         1121  +			diffcmd_.c:diffcmd.h \
         1122  +			doc_.c:doc.h \
         1123  +			encode_.c:encode.h \
         1124  +			event_.c:event.h \
         1125  +			export_.c:export.h \
         1126  +			file_.c:file.h \
         1127  +			finfo_.c:finfo.h \
         1128  +			glob_.c:glob.h \
         1129  +			graph_.c:graph.h \
         1130  +			gzip_.c:gzip.h \
         1131  +			http_.c:http.h \
         1132  +			http_socket_.c:http_socket.h \
         1133  +			http_ssl_.c:http_ssl.h \
         1134  +			http_transport_.c:http_transport.h \
         1135  +			import_.c:import.h \
         1136  +			info_.c:info.h \
         1137  +			json_.c:json.h \
         1138  +			json_artifact_.c:json_artifact.h \
         1139  +			json_branch_.c:json_branch.h \
         1140  +			json_config_.c:json_config.h \
         1141  +			json_diff_.c:json_diff.h \
         1142  +			json_dir_.c:json_dir.h \
         1143  +			json_finfo_.c:json_finfo.h \
         1144  +			json_login_.c:json_login.h \
         1145  +			json_query_.c:json_query.h \
         1146  +			json_report_.c:json_report.h \
         1147  +			json_tag_.c:json_tag.h \
         1148  +			json_timeline_.c:json_timeline.h \
         1149  +			json_user_.c:json_user.h \
         1150  +			json_wiki_.c:json_wiki.h \
         1151  +			leaf_.c:leaf.h \
         1152  +			login_.c:login.h \
         1153  +			main_.c:main.h \
         1154  +			manifest_.c:manifest.h \
         1155  +			markdown_.c:markdown.h \
         1156  +			markdown_html_.c:markdown_html.h \
         1157  +			md5_.c:md5.h \
         1158  +			merge_.c:merge.h \
         1159  +			merge3_.c:merge3.h \
         1160  +			moderate_.c:moderate.h \
         1161  +			name_.c:name.h \
         1162  +			path_.c:path.h \
         1163  +			pivot_.c:pivot.h \
         1164  +			popen_.c:popen.h \
         1165  +			pqueue_.c:pqueue.h \
         1166  +			printf_.c:printf.h \
         1167  +			rebuild_.c:rebuild.h \
         1168  +			regexp_.c:regexp.h \
         1169  +			report_.c:report.h \
         1170  +			rss_.c:rss.h \
         1171  +			schema_.c:schema.h \
         1172  +			search_.c:search.h \
         1173  +			setup_.c:setup.h \
         1174  +			sha1_.c:sha1.h \
         1175  +			shun_.c:shun.h \
         1176  +			skins_.c:skins.h \
         1177  +			sqlcmd_.c:sqlcmd.h \
         1178  +			stash_.c:stash.h \
         1179  +			stat_.c:stat.h \
         1180  +			style_.c:style.h \
         1181  +			sync_.c:sync.h \
         1182  +			tag_.c:tag.h \
         1183  +			tar_.c:tar.h \
         1184  +			th_main_.c:th_main.h \
         1185  +			timeline_.c:timeline.h \
         1186  +			tkt_.c:tkt.h \
         1187  +			tktsetup_.c:tktsetup.h \
         1188  +			undo_.c:undo.h \
         1189  +			unicode_.c:unicode.h \
         1190  +			update_.c:update.h \
         1191  +			url_.c:url.h \
         1192  +			user_.c:user.h \
         1193  +			utf8_.c:utf8.h \
         1194  +			verify_.c:verify.h \
         1195  +			vfile_.c:vfile.h \
         1196  +			wiki_.c:wiki.h \
         1197  +			wikiformat_.c:wikiformat.h \
         1198  +			winhttp_.c:winhttp.h \
         1199  +			wysiwyg_.c:wysiwyg.h \
         1200  +			xfer_.c:xfer.h \
         1201  +			xfersetup_.c:xfersetup.h \
         1202  +			zip_.c:zip.h \
         1203  +			$(SRCDIR)\sqlite3.h \
         1204  +			$(SRCDIR)\th.h \
         1205  +			VERSION.h \
         1206  +			$(SRCDIR)\cson_amalgamation.h
         1207  +	@copy /Y nul: headers

Added win/fossil.ico.

cannot compute difference between binary files

Added win/fossil.rc.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains resource information for the executable on Windows.
           19  +*/
           20  +
           21  +#if !defined(_WIN32_WCE)
           22  +#include "winresrc.h"
           23  +#else
           24  +#include "windows.h"
           25  +#endif
           26  +
           27  +#include "VERSION.h"
           28  +#define _RC_COMPILE_
           29  +#include "config.h"
           30  +#include "sqlite3.h"
           31  +#include "zlib.h"
           32  +
           33  +#ifdef FOSSIL_ENABLE_SSL
           34  +#include "openssl/opensslv.h"
           35  +#endif
           36  +
           37  +#ifdef FOSSIL_ENABLE_TCL
           38  +#include "tcl.h"
           39  +#endif
           40  +
           41  +/*
           42  + * English (U.S.) resources
           43  + */
           44  +
           45  +#ifdef _WIN32
           46  +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
           47  +#pragma code_page(1252)
           48  +#endif /* _WIN32 */
           49  +
           50  +/*
           51  + * Icon
           52  + */
           53  +
           54  +#define IDI_FOSSIL 8001
           55  +
           56  +IDI_FOSSIL ICON "fossil.ico"
           57  +
           58  +/*
           59  + * Version
           60  + */
           61  +
           62  +VS_VERSION_INFO VERSIONINFO
           63  +  FILEVERSION RELEASE_RESOURCE_VERSION
           64  +  PRODUCTVERSION RELEASE_RESOURCE_VERSION
           65  +  FILEFLAGSMASK 0x3F
           66  +#if defined(_DEBUG)
           67  +  FILEFLAGS 0x1L
           68  +#else
           69  +  FILEFLAGS 0x0L
           70  +#endif
           71  +  FILEOS VOS__WINDOWS32
           72  +  FILETYPE VFT_APP
           73  +  FILESUBTYPE VFT2_UNKNOWN
           74  +BEGIN
           75  +  BLOCK "StringFileInfo"
           76  +  BEGIN
           77  +    BLOCK "040904B0"
           78  +    BEGIN
           79  +      VALUE "CompanyName", "Fossil Development Team\0"
           80  +      VALUE "FileDescription", "Simple, high-reliability, distributed software configuration management system.\0"
           81  +      VALUE "ProductName", "Fossil\0"
           82  +      VALUE "ProductVersion", "Fossil " RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE " UTC\0"
           83  +      VALUE "FileVersion", "Fossil " RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE " UTC\0"
           84  +      VALUE "InternalName", "fossil\0"
           85  +      VALUE "LegalCopyright", "Copyright © " MANIFEST_YEAR " by D. Richard Hipp.  All rights reserved.\0"
           86  +      VALUE "OriginalFilename", "fossil.exe\0"
           87  +      VALUE "CompilerName", COMPILER_NAME "\0"
           88  +      VALUE "SQLiteVersion", "SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\0"
           89  +      VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0"
           90  +#ifdef BROKEN_MINGW_CMDLINE
           91  +      VALUE "CommandLineIsUnicode", "No\0"
           92  +#else
           93  +      VALUE "CommandLineIsUnicode", "Yes\0"
           94  +#endif
           95  +#ifdef FOSSIL_ENABLE_SSL
           96  +      VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
           97  +#endif
           98  +#ifdef FOSSIL_ENABLE_TCL
           99  +      VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
          100  +#ifdef FOSSIL_ENABLE_TCL_STUBS
          101  +      VALUE "TclStubsEnabled", "Yes\0"
          102  +#else
          103  +      VALUE "TclStubsEnabled", "No\0"
          104  +#endif
          105  +#endif
          106  +#ifdef FOSSIL_ENABLE_JSON
          107  +      VALUE "JsonEnabled", "Yes, cson\0"
          108  +#endif
          109  +#ifdef FOSSIL_ENABLE_MARKDOWN
          110  +      VALUE "MarkdownEnabled", "Yes\0"
          111  +#endif
          112  +    END
          113  +  END
          114  +  BLOCK "VarFileInfo"
          115  +  BEGIN
          116  +    VALUE "Translation", 0x409, 0x4B0
          117  +  END
          118  +END

Added win/include/dirent.h.

            1  +/*
            2  + * dirent.h - dirent API for Microsoft Visual Studio
            3  + *
            4  + * Copyright (C) 2006-2012 Toni Ronkko
            5  + *
            6  + * Permission is hereby granted, free of charge, to any person obtaining
            7  + * a copy of this software and associated documentation files (the
            8  + * ``Software''), to deal in the Software without restriction, including
            9  + * without limitation the rights to use, copy, modify, merge, publish,
           10  + * distribute, sublicense, and/or sell copies of the Software, and to
           11  + * permit persons to whom the Software is furnished to do so, subject to
           12  + * the following conditions:
           13  + *
           14  + * The above copyright notice and this permission notice shall be included
           15  + * in all copies or substantial portions of the Software.
           16  + *
           17  + * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
           18  + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           19  + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
           20  + * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
           21  + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
           22  + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
           23  + * OTHER DEALINGS IN THE SOFTWARE.
           24  + *
           25  + *
           26  + * Version 1.13, Dec 12 2012, Toni Ronkko
           27  + * Use traditional 8+3 file name if the name cannot be represented in the
           28  + * default ANSI code page.  Now compiles again with MSVC 6.0.  Thanks to
           29  + * Konstantin Khomoutov for testing.
           30  + *
           31  + * Version 1.12.1, Oct 1 2012, Toni Ronkko
           32  + * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
           33  + * capital W) in order to maintain compatibility with MingW.
           34  + *
           35  + * Version 1.12, Sep 30 2012, Toni Ronkko
           36  + * Define PATH_MAX and NAME_MAX.  Added wide-character variants _wDIR, 
           37  + * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
           38  + * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
           39  + *
           40  + * Do not include windows.h.  This allows dirent.h to be integrated more
           41  + * easily into programs using winsock.  Thanks to Fernando Azaldegui.
           42  + *
           43  + * Version 1.11, Mar 15, 2011, Toni Ronkko
           44  + * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
           45  + *
           46  + * Version 1.10, Aug 11, 2010, Toni Ronkko
           47  + * Added d_type and d_namlen fields to dirent structure.  The former is
           48  + * especially useful for determining whether directory entry represents a
           49  + * file or a directory.  For more information, see
           50  + * http://www.delorie.com/gnu/docs/glibc/libc_270.html
           51  + *
           52  + * Improved conformance to the standards.  For example, errno is now set
           53  + * properly on failure and assert() is never used.  Thanks to Peter Brockam
           54  + * for suggestions.
           55  + *
           56  + * Fixed a bug in rewinddir(): when using relative directory names, change
           57  + * of working directory no longer causes rewinddir() to fail.
           58  + *
           59  + * Version 1.9, Dec 15, 2009, John Cunningham
           60  + * Added rewinddir member function
           61  + *
           62  + * Version 1.8, Jan 18, 2008, Toni Ronkko
           63  + * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
           64  + * between multi-byte and unicode representations.  This makes the
           65  + * code simpler and also allows the code to be compiled under MingW.  Thanks
           66  + * to Azriel Fasten for the suggestion.
           67  + *
           68  + * Mar 4, 2007, Toni Ronkko
           69  + * Bug fix: due to the strncpy_s() function this file only compiled in
           70  + * Visual Studio 2005.  Using the new string functions only when the
           71  + * compiler version allows.
           72  + *
           73  + * Nov  2, 2006, Toni Ronkko
           74  + * Major update: removed support for Watcom C, MS-DOS and Turbo C to
           75  + * simplify the file, updated the code to compile cleanly on Visual
           76  + * Studio 2005 with both unicode and multi-byte character strings,
           77  + * removed rewinddir() as it had a bug.
           78  + *
           79  + * Aug 20, 2006, Toni Ronkko
           80  + * Removed all remarks about MSVC 1.0, which is antiqued now.  Simplified
           81  + * comments by removing SGML tags.
           82  + *
           83  + * May 14 2002, Toni Ronkko
           84  + * Embedded the function definitions directly to the header so that no
           85  + * source modules need to be included in the Visual Studio project.  Removed
           86  + * all the dependencies to other projects so that this header file can be
           87  + * used independently.
           88  + *
           89  + * May 28 1998, Toni Ronkko
           90  + * First version.
           91  + *****************************************************************************/
           92  +#ifndef DIRENT_H
           93  +#define DIRENT_H
           94  +
           95  +#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
           96  +#   define _X86_
           97  +#endif
           98  +#include <stdio.h>
           99  +#include <stdarg.h>
          100  +#include <windef.h>
          101  +#include <winbase.h>
          102  +#include <wchar.h>
          103  +#include <string.h>
          104  +#include <stdlib.h>
          105  +#include <malloc.h>
          106  +#include <sys/types.h>
          107  +#include <sys/stat.h>
          108  +#include <errno.h>
          109  +
          110  +/* Indicates that d_type field is available in dirent structure */
          111  +#define _DIRENT_HAVE_D_TYPE
          112  +
          113  +/* Indicates that d_namlen field is available in dirent structure */
          114  +#define _DIRENT_HAVE_D_NAMLEN
          115  +
          116  +/* Entries missing from MSVC 6.0 */
          117  +#if !defined(FILE_ATTRIBUTE_DEVICE)
          118  +#   define FILE_ATTRIBUTE_DEVICE 0x40
          119  +#endif
          120  +
          121  +/* File type and permission flags for stat() */
          122  +#if !defined(S_IFMT)
          123  +#   define S_IFMT   _S_IFMT                     /* File type mask */
          124  +#endif
          125  +#if !defined(S_IFDIR)
          126  +#   define S_IFDIR  _S_IFDIR                    /* Directory */
          127  +#endif
          128  +#if !defined(S_IFCHR)
          129  +#   define S_IFCHR  _S_IFCHR                    /* Character device */
          130  +#endif
          131  +#if !defined(S_IFFIFO)
          132  +#   define S_IFFIFO _S_IFFIFO                   /* Pipe */
          133  +#endif
          134  +#if !defined(S_IFREG)
          135  +#   define S_IFREG  _S_IFREG                    /* Regular file */
          136  +#endif
          137  +#if !defined(S_IREAD)
          138  +#   define S_IREAD  _S_IREAD                    /* Read permission */
          139  +#endif
          140  +#if !defined(S_IWRITE)
          141  +#   define S_IWRITE _S_IWRITE                   /* Write permission */
          142  +#endif
          143  +#if !defined(S_IEXEC)
          144  +#   define S_IEXEC  _S_IEXEC                    /* Execute permission */
          145  +#endif
          146  +#if !defined(S_IFIFO)
          147  +#   define S_IFIFO _S_IFIFO                     /* Pipe */
          148  +#endif
          149  +#if !defined(S_IFBLK)
          150  +#   define S_IFBLK   0                          /* Block device */
          151  +#endif
          152  +#if !defined(S_IFLNK)
          153  +#   define S_IFLNK   0                          /* Link */
          154  +#endif
          155  +#if !defined(S_IFSOCK)
          156  +#   define S_IFSOCK  0                          /* Socket */
          157  +#endif
          158  +
          159  +#if defined(_MSC_VER)
          160  +#   define S_IRUSR  S_IREAD                     /* Read user */
          161  +#   define S_IWUSR  S_IWRITE                    /* Write user */
          162  +#   define S_IXUSR  0                           /* Execute user */
          163  +#   define S_IRGRP  0                           /* Read group */
          164  +#   define S_IWGRP  0                           /* Write group */
          165  +#   define S_IXGRP  0                           /* Execute group */
          166  +#   define S_IROTH  0                           /* Read others */
          167  +#   define S_IWOTH  0                           /* Write others */
          168  +#   define S_IXOTH  0                           /* Execute others */
          169  +#endif
          170  +
          171  +/* Maximum length of file name */
          172  +#if !defined(PATH_MAX)
          173  +#   define PATH_MAX MAX_PATH
          174  +#endif
          175  +#if !defined(FILENAME_MAX)
          176  +#   define FILENAME_MAX MAX_PATH
          177  +#endif
          178  +#if !defined(NAME_MAX)
          179  +#   define NAME_MAX FILENAME_MAX
          180  +#endif
          181  +
          182  +/* File type flags for d_type */
          183  +#define DT_UNKNOWN  0
          184  +#define DT_REG      S_IFREG
          185  +#define DT_DIR      S_IFDIR
          186  +#define DT_FIFO     S_IFIFO
          187  +#define DT_SOCK     S_IFSOCK
          188  +#define DT_CHR      S_IFCHR
          189  +#define DT_BLK      S_IFBLK
          190  +
          191  +/* Macros for converting between st_mode and d_type */
          192  +#define IFTODT(mode) ((mode) & S_IFMT)
          193  +#define DTTOIF(type) (type)
          194  +
          195  +/*
          196  + * File type macros.  Note that block devices, sockets and links cannot be
          197  + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
          198  + * only defined for compatibility.  These macros should always return false
          199  + * on Windows.
          200  + */
          201  +#define	S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
          202  +#define	S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
          203  +#define	S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
          204  +#define	S_ISLNK(mode)  (((mode) & S_IFMT) == S_IFLNK)
          205  +#define	S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
          206  +#define	S_ISCHR(mode)  (((mode) & S_IFMT) == S_IFCHR)
          207  +#define	S_ISBLK(mode)  (((mode) & S_IFMT) == S_IFBLK)
          208  +
          209  +/* Return the exact length of d_namlen without zero terminator */
          210  +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
          211  +
          212  +/* Return number of bytes needed to store d_namlen */
          213  +#define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
          214  +
          215  +
          216  +#ifdef __cplusplus
          217  +extern "C" {
          218  +#endif
          219  +
          220  +
          221  +/* Wide-character version */
          222  +struct _wdirent {
          223  +    long d_ino;                                 /* Always zero */
          224  +    unsigned short d_reclen;                    /* Structure size */
          225  +    size_t d_namlen;                            /* Length of name without \0 */
          226  +    int d_type;                                 /* File type */
          227  +    wchar_t d_name[PATH_MAX + 1];               /* File name */
          228  +};
          229  +typedef struct _wdirent _wdirent;
          230  +
          231  +struct _WDIR {
          232  +    struct _wdirent ent;                        /* Current directory entry */
          233  +    WIN32_FIND_DATAW data;                      /* Private file data */
          234  +    int cached;                                 /* True if data is valid */
          235  +    HANDLE handle;                              /* Win32 search handle */
          236  +    wchar_t *patt;                              /* Initial directory name */
          237  +};
          238  +typedef struct _WDIR _WDIR;
          239  +
          240  +static _WDIR *_wopendir (const wchar_t *dirname);
          241  +static struct _wdirent *_wreaddir (_WDIR *dirp);
          242  +static int _wclosedir (_WDIR *dirp);
          243  +static void _wrewinddir (_WDIR* dirp);
          244  +
          245  +
          246  +/* For compatibility with Symbian */
          247  +#define wdirent _wdirent
          248  +#define WDIR _WDIR
          249  +#define wopendir _wopendir
          250  +#define wreaddir _wreaddir
          251  +#define wclosedir _wclosedir
          252  +#define wrewinddir _wrewinddir
          253  +
          254  +
          255  +/* Multi-byte character versions */
          256  +struct dirent {
          257  +    long d_ino;                                 /* Always zero */
          258  +    unsigned short d_reclen;                    /* Structure size */
          259  +    size_t d_namlen;                            /* Length of name without \0 */
          260  +    int d_type;                                 /* File type */
          261  +    char d_name[PATH_MAX + 1];                  /* File name */
          262  +};
          263  +typedef struct dirent dirent;
          264  +
          265  +struct DIR {
          266  +    struct dirent ent;
          267  +    struct _WDIR *wdirp;
          268  +};
          269  +typedef struct DIR DIR;
          270  +
          271  +static DIR *opendir (const char *dirname);
          272  +static struct dirent *readdir (DIR *dirp);
          273  +static int closedir (DIR *dirp);
          274  +static void rewinddir (DIR* dirp);
          275  +
          276  +
          277  +/* Internal utility functions */
          278  +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
          279  +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
          280  +
          281  +static int dirent_mbstowcs_s(
          282  +    size_t *pReturnValue,
          283  +    wchar_t *wcstr,
          284  +    size_t sizeInWords,
          285  +    const char *mbstr,
          286  +    size_t count);
          287  +
          288  +static int dirent_wcstombs_s(
          289  +    size_t *pReturnValue,
          290  +    char *mbstr,
          291  +    size_t sizeInBytes,
          292  +    const wchar_t *wcstr,
          293  +    size_t count);
          294  +
          295  +static void dirent_set_errno (int error);
          296  +
          297  +/*
          298  + * Open directory stream DIRNAME for read and return a pointer to the
          299  + * internal working area that is used to retrieve individual directory
          300  + * entries.
          301  + */
          302  +static _WDIR*
          303  +_wopendir(
          304  +    const wchar_t *dirname)
          305  +{
          306  +    _WDIR *dirp = NULL;
          307  +    int error;
          308  +
          309  +    /* Must have directory name */
          310  +    if (dirname == NULL  ||  dirname[0] == '\0') {
          311  +        dirent_set_errno (ENOENT);
          312  +        return NULL;
          313  +    }
          314  +
          315  +    /* Allocate new _WDIR structure */
          316  +    dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
          317  +    if (dirp != NULL) {
          318  +        DWORD n;
          319  +
          320  +        /* Reset _WDIR structure */
          321  +        dirp->handle = INVALID_HANDLE_VALUE;
          322  +        dirp->patt = NULL;
          323  +        dirp->cached = 0;
          324  +
          325  +        /* Compute the length of full path plus zero terminator */
          326  +        n = GetFullPathNameW (dirname, 0, NULL, NULL);
          327  +
          328  +        /* Allocate room for absolute directory name and search pattern */
          329  +        dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
          330  +        if (dirp->patt) {
          331  +
          332  +            /*
          333  +             * Convert relative directory name to an absolute one.  This
          334  +             * allows rewinddir() to function correctly even when current
          335  +             * working directory is changed between opendir() and rewinddir().
          336  +             */
          337  +            n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
          338  +            if (n > 0) {
          339  +                wchar_t *p;
          340  +
          341  +                /* Append search pattern \* to the directory name */
          342  +                p = dirp->patt + n;
          343  +                if (dirp->patt < p) {
          344  +                    switch (p[-1]) {
          345  +                    case '\\':
          346  +                    case '/':
          347  +                    case ':':
          348  +                        /* Directory ends in path separator, e.g. c:\temp\ */
          349  +                        /*NOP*/;
          350  +                        break;
          351  +
          352  +                    default:
          353  +                        /* Directory name doesn't end in path separator */
          354  +                        *p++ = '\\';
          355  +                    }
          356  +                }
          357  +                *p++ = '*';
          358  +                *p = '\0';
          359  +
          360  +                /* Open directory stream and retrieve the first entry */
          361  +                if (dirent_first (dirp)) {
          362  +                    /* Directory stream opened successfully */
          363  +                    error = 0;
          364  +                } else {
          365  +                    /* Cannot retrieve first entry */
          366  +                    error = 1;
          367  +                    dirent_set_errno (ENOENT);
          368  +                }
          369  +
          370  +            } else {
          371  +                /* Cannot retrieve full path name */
          372  +                dirent_set_errno (ENOENT);
          373  +                error = 1;
          374  +            }
          375  +
          376  +        } else {
          377  +            /* Cannot allocate memory for search pattern */
          378  +            error = 1;
          379  +        }
          380  +
          381  +    } else {
          382  +        /* Cannot allocate _WDIR structure */
          383  +        error = 1;
          384  +    }
          385  +
          386  +    /* Clean up in case of error */
          387  +    if (error  &&  dirp) {
          388  +        _wclosedir (dirp);
          389  +        dirp = NULL;
          390  +    }
          391  +
          392  +    return dirp;
          393  +}
          394  +
          395  +/*
          396  + * Read next directory entry.  The directory entry is returned in dirent
          397  + * structure in the d_name field.  Individual directory entries returned by
          398  + * this function include regular files, sub-directories, pseudo-directories
          399  + * "." and ".." as well as volume labels, hidden files and system files.
          400  + */
          401  +static struct _wdirent*
          402  +_wreaddir(
          403  +    _WDIR *dirp)
          404  +{
          405  +    WIN32_FIND_DATAW *datap;
          406  +    struct _wdirent *entp;
          407  +
          408  +    /* Read next directory entry */
          409  +    datap = dirent_next (dirp);
          410  +    if (datap) {
          411  +        size_t n;
          412  +        DWORD attr;
          413  +        
          414  +        /* Pointer to directory entry to return */
          415  +        entp = &dirp->ent;
          416  +
          417  +        /* 
          418  +         * Copy file name as wide-character string.  If the file name is too
          419  +         * long to fit in to the destination buffer, then truncate file name
          420  +         * to PATH_MAX characters and zero-terminate the buffer.
          421  +         */
          422  +        n = 0;
          423  +        while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
          424  +            entp->d_name[n] = datap->cFileName[n];
          425  +            n++;
          426  +        }
          427  +        dirp->ent.d_name[n] = 0;
          428  +
          429  +        /* Length of file name excluding zero terminator */
          430  +        entp->d_namlen = n;
          431  +
          432  +        /* File type */
          433  +        attr = datap->dwFileAttributes;
          434  +        if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
          435  +            entp->d_type = DT_CHR;
          436  +        } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
          437  +            entp->d_type = DT_DIR;
          438  +        } else {
          439  +            entp->d_type = DT_REG;
          440  +        }
          441  +
          442  +        /* Reset dummy fields */
          443  +        entp->d_ino = 0;
          444  +        entp->d_reclen = sizeof (struct _wdirent);
          445  +
          446  +    } else {
          447  +
          448  +        /* Last directory entry read */
          449  +        entp = NULL;
          450  +
          451  +    }
          452  +
          453  +    return entp;
          454  +}
          455  +
          456  +/*
          457  + * Close directory stream opened by opendir() function.  This invalidates the
          458  + * DIR structure as well as any directory entry read previously by
          459  + * _wreaddir().
          460  + */
          461  +static int
          462  +_wclosedir(
          463  +    _WDIR *dirp)
          464  +{
          465  +    int ok;
          466  +    if (dirp) {
          467  +
          468  +        /* Release search handle */
          469  +        if (dirp->handle != INVALID_HANDLE_VALUE) {
          470  +            FindClose (dirp->handle);
          471  +            dirp->handle = INVALID_HANDLE_VALUE;
          472  +        }
          473  +
          474  +        /* Release search pattern */
          475  +        if (dirp->patt) {
          476  +            free (dirp->patt);
          477  +            dirp->patt = NULL;
          478  +        }
          479  +
          480  +        /* Release directory structure */
          481  +        free (dirp);
          482  +        ok = /*success*/0;
          483  +
          484  +    } else {
          485  +        /* Invalid directory stream */
          486  +        dirent_set_errno (EBADF);
          487  +        ok = /*failure*/-1;
          488  +    }
          489  +    return ok;
          490  +}
          491  +
          492  +/*
          493  + * Rewind directory stream such that _wreaddir() returns the very first
          494  + * file name again.
          495  + */
          496  +static void
          497  +_wrewinddir(
          498  +    _WDIR* dirp)
          499  +{
          500  +    if (dirp) {
          501  +        /* Release existing search handle */
          502  +        if (dirp->handle != INVALID_HANDLE_VALUE) {
          503  +            FindClose (dirp->handle);
          504  +        }
          505  +
          506  +        /* Open new search handle */
          507  +        dirent_first (dirp);
          508  +    }
          509  +}
          510  +
          511  +/* Get first directory entry (internal) */
          512  +static WIN32_FIND_DATAW*
          513  +dirent_first(
          514  +    _WDIR *dirp)
          515  +{
          516  +    WIN32_FIND_DATAW *datap;
          517  +
          518  +    /* Open directory and retrieve the first entry */
          519  +    dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
          520  +    if (dirp->handle != INVALID_HANDLE_VALUE) {
          521  +
          522  +        /* a directory entry is now waiting in memory */
          523  +        datap = &dirp->data;
          524  +        dirp->cached = 1;
          525  +
          526  +    } else {
          527  +
          528  +        /* Failed to re-open directory: no directory entry in memory */
          529  +        dirp->cached = 0;
          530  +        datap = NULL;
          531  +
          532  +    }
          533  +    return datap;
          534  +}
          535  +
          536  +/* Get next directory entry (internal) */
          537  +static WIN32_FIND_DATAW*
          538  +dirent_next(
          539  +    _WDIR *dirp)
          540  +{
          541  +    WIN32_FIND_DATAW *p;
          542  +
          543  +    /* Get next directory entry */
          544  +    if (dirp->cached != 0) {
          545  +
          546  +        /* A valid directory entry already in memory */
          547  +        p = &dirp->data;
          548  +        dirp->cached = 0;
          549  +
          550  +    } else if (dirp->handle != INVALID_HANDLE_VALUE) {
          551  +
          552  +        /* Get the next directory entry from stream */
          553  +        if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
          554  +            /* Got a file */
          555  +            p = &dirp->data;
          556  +        } else {
          557  +            /* The very last entry has been processed or an error occured */
          558  +            FindClose (dirp->handle);
          559  +            dirp->handle = INVALID_HANDLE_VALUE;
          560  +            p = NULL;
          561  +        }
          562  +
          563  +    } else {
          564  +
          565  +        /* End of directory stream reached */
          566  +        p = NULL;
          567  +
          568  +    }
          569  +
          570  +    return p;
          571  +}
          572  +
          573  +/* 
          574  + * Open directory stream using plain old C-string.
          575  + */
          576  +static DIR*
          577  +opendir(
          578  +    const char *dirname) 
          579  +{
          580  +    struct DIR *dirp;
          581  +    int error;
          582  +
          583  +    /* Must have directory name */
          584  +    if (dirname == NULL  ||  dirname[0] == '\0') {
          585  +        dirent_set_errno (ENOENT);
          586  +        return NULL;
          587  +    }
          588  +
          589  +    /* Allocate memory for DIR structure */
          590  +    dirp = (DIR*) malloc (sizeof (struct DIR));
          591  +    if (dirp) {
          592  +        wchar_t wname[PATH_MAX + 1];
          593  +        size_t n;
          594  +
          595  +        /* Convert directory name to wide-character string */
          596  +        error = dirent_mbstowcs_s(
          597  +            &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
          598  +        if (!error) {
          599  +
          600  +            /* Open directory stream using wide-character name */
          601  +            dirp->wdirp = _wopendir (wname);
          602  +            if (dirp->wdirp) {
          603  +                /* Directory stream opened */
          604  +                error = 0;
          605  +            } else {
          606  +                /* Failed to open directory stream */
          607  +                error = 1;
          608  +            }
          609  +
          610  +        } else {
          611  +            /* 
          612  +             * Cannot convert file name to wide-character string.  This
          613  +             * occurs if the string contains invalid multi-byte sequences or
          614  +             * the output buffer is too small to contain the resulting
          615  +             * string.
          616  +             */
          617  +            error = 1;
          618  +        }
          619  +
          620  +    } else {
          621  +        /* Cannot allocate DIR structure */
          622  +        error = 1;
          623  +    }
          624  +
          625  +    /* Clean up in case of error */
          626  +    if (error  &&  dirp) {
          627  +        free (dirp);
          628  +        dirp = NULL;
          629  +    }
          630  +
          631  +    return dirp;
          632  +}
          633  +
          634  +/*
          635  + * Read next directory entry.
          636  + *
          637  + * When working with text consoles, please note that file names returned by
          638  + * readdir() are represented in the default ANSI code page while any output to
          639  + * console is typically formatted on another code page.  Thus, non-ASCII
          640  + * characters in file names will not usually display correctly on console.  The
          641  + * problem can be fixed in two ways: (1) change the character set of console
          642  + * to 1252 using chcp utility and use Lucida Console font, or (2) use
          643  + * _cprintf function when writing to console.  The _cprinf() will re-encode
          644  + * ANSI strings to the console code page so many non-ASCII characters will
          645  + * display correcly.
          646  + */
          647  +static struct dirent*
          648  +readdir(
          649  +    DIR *dirp) 
          650  +{
          651  +    WIN32_FIND_DATAW *datap;
          652  +    struct dirent *entp;
          653  +
          654  +    /* Read next directory entry */
          655  +    datap = dirent_next (dirp->wdirp);
          656  +    if (datap) {
          657  +        size_t n;
          658  +        int error;
          659  +
          660  +        /* Attempt to convert file name to multi-byte string */
          661  +        error = dirent_wcstombs_s(
          662  +            &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
          663  +
          664  +        /* 
          665  +         * If the file name cannot be represented by a multi-byte string,
          666  +         * then attempt to use old 8+3 file name.  This allows traditional
          667  +         * Unix-code to access some file names despite of unicode
          668  +         * characters, although file names may seem unfamiliar to the user.
          669  +         *
          670  +         * Be ware that the code below cannot come up with a short file
          671  +         * name unless the file system provides one.  At least
          672  +         * VirtualBox shared folders fail to do this.
          673  +         */
          674  +        if (error  &&  datap->cAlternateFileName[0] != '\0') {
          675  +            error = dirent_wcstombs_s(
          676  +                &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
          677  +                sizeof (datap->cAlternateFileName) / 
          678  +                    sizeof (datap->cAlternateFileName[0]));
          679  +        }
          680  +
          681  +        if (!error) {
          682  +            DWORD attr;
          683  +
          684  +            /* Initialize directory entry for return */
          685  +            entp = &dirp->ent;
          686  +
          687  +            /* Length of file name excluding zero terminator */
          688  +            entp->d_namlen = n - 1;
          689  +
          690  +            /* File attributes */
          691  +            attr = datap->dwFileAttributes;
          692  +            if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
          693  +                entp->d_type = DT_CHR;
          694  +            } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
          695  +                entp->d_type = DT_DIR;
          696  +            } else {
          697  +                entp->d_type = DT_REG;
          698  +            }
          699  +
          700  +            /* Reset dummy fields */
          701  +            entp->d_ino = 0;
          702  +            entp->d_reclen = sizeof (struct dirent);
          703  +
          704  +        } else {
          705  +            /* 
          706  +             * Cannot convert file name to multi-byte string so construct
          707  +             * an errornous directory entry and return that.  Note that
          708  +             * we cannot return NULL as that would stop the processing
          709  +             * of directory entries completely.
          710  +             */
          711  +            entp = &dirp->ent;
          712  +            entp->d_name[0] = '?';
          713  +            entp->d_name[1] = '\0';
          714  +            entp->d_namlen = 1;
          715  +            entp->d_type = DT_UNKNOWN;
          716  +            entp->d_ino = 0;
          717  +            entp->d_reclen = 0;
          718  +        }
          719  +
          720  +    } else {
          721  +        /* No more directory entries */
          722  +        entp = NULL;
          723  +    }
          724  +
          725  +    return entp;
          726  +}
          727  +
          728  +/*
          729  + * Close directory stream.
          730  + */
          731  +static int
          732  +closedir(
          733  +    DIR *dirp) 
          734  +{
          735  +    int ok;
          736  +    if (dirp) {
          737  +
          738  +        /* Close wide-character directory stream */
          739  +        ok = _wclosedir (dirp->wdirp);
          740  +        dirp->wdirp = NULL;
          741  +
          742  +        /* Release multi-byte character version */
          743  +        free (dirp);
          744  +
          745  +    } else {
          746  +
          747  +        /* Invalid directory stream */
          748  +        dirent_set_errno (EBADF);
          749  +        ok = /*failure*/-1;
          750  +
          751  +    }
          752  +    return ok;
          753  +}
          754  +
          755  +/*
          756  + * Rewind directory stream to beginning.
          757  + */
          758  +static void
          759  +rewinddir(
          760  +    DIR* dirp) 
          761  +{
          762  +    /* Rewind wide-character string directory stream */
          763  +    _wrewinddir (dirp->wdirp);
          764  +}
          765  +
          766  +/* Convert multi-byte string to wide character string */
          767  +static int
          768  +dirent_mbstowcs_s(
          769  +    size_t *pReturnValue,
          770  +    wchar_t *wcstr,
          771  +    size_t sizeInWords,
          772  +    const char *mbstr,
          773  +    size_t count)
          774  +{
          775  +    int error;
          776  +
          777  +#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
          778  +
          779  +    /* Microsoft Visual Studio 2005 or later */
          780  +    error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
          781  +
          782  +#else
          783  +
          784  +    /* Older Visual Studio or non-Microsoft compiler */
          785  +    size_t n;
          786  +
          787  +    /* Convert to wide-character string */
          788  +    n = mbstowcs (wcstr, mbstr, count);
          789  +    if (n < sizeInWords) {
          790  +
          791  +        /* Zero-terminate output buffer */
          792  +        if (wcstr) {
          793  +            wcstr[n] = 0;
          794  +        }
          795  +
          796  +        /* Length of resuting multi-byte string WITH zero terminator */
          797  +        if (pReturnValue) {
          798  +            *pReturnValue = n + 1;
          799  +        }
          800  +
          801  +        /* Success */
          802  +        error = 0;
          803  +
          804  +    } else {
          805  +
          806  +        /* Could not convert string */
          807  +        error = 1;
          808  +
          809  +    }
          810  +
          811  +#endif
          812  +
          813  +    return error;
          814  +}
          815  +
          816  +/* Convert wide-character string to multi-byte string */
          817  +static int
          818  +dirent_wcstombs_s(
          819  +    size_t *pReturnValue,
          820  +    char *mbstr,
          821  +    size_t sizeInBytes,
          822  +    const wchar_t *wcstr,
          823  +    size_t count)
          824  +{
          825  +    int error;
          826  +
          827  +#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
          828  +
          829  +    /* Microsoft Visual Studio 2005 or later */
          830  +    error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
          831  +
          832  +#else
          833  +
          834  +    /* Older Visual Studio or non-Microsoft compiler */
          835  +    size_t n;
          836  +
          837  +    /* Convert to multi-byte string */
          838  +    n = wcstombs (mbstr, wcstr, count);
          839  +    if (n < sizeInBytes) {
          840  +
          841  +        /* Zero-terminate output buffer */
          842  +        if (mbstr) {
          843  +            mbstr[n] = '\0';
          844  +        }
          845  +
          846  +        /* Lenght of resulting multi-bytes string WITH zero-terminator */
          847  +        if (pReturnValue) {
          848  +            *pReturnValue = n + 1;
          849  +        }
          850  +
          851  +        /* Success */
          852  +        error = 0;
          853  +
          854  +    } else {
          855  +
          856  +        /* Cannot convert string */
          857  +        error = 1;
          858  +
          859  +    }
          860  +
          861  +#endif
          862  +
          863  +    return error;
          864  +}
          865  +
          866  +/* Set errno variable */
          867  +static void
          868  +dirent_set_errno(
          869  +    int error)
          870  +{
          871  +#if defined(_MSC_VER) && _MSC_VER >= 1400
          872  +
          873  +    /* Microsoft Visual Studio 2005 and later */
          874  +    _set_errno (error);
          875  +
          876  +#else
          877  +
          878  +    /* Non-Microsoft compiler or older Microsoft compiler */
          879  +    errno = error;
          880  +
          881  +#endif
          882  +}
          883  +
          884  +
          885  +#ifdef __cplusplus
          886  +}
          887  +#endif
          888  +#endif /*DIRENT_H*/
          889  +

Added win/include/unistd.h.

            1  +#ifndef _UNISTD_H
            2  +#define _UNISTD_H	 1
            3  +
            4  +/* This file intended to serve as a drop-in replacement for 
            5  + *  unistd.h on Windows
            6  + *  Please add functionality as neeeded 
            7  + */
            8  +
            9  +#include <stdlib.h>
           10  +#include <io.h>
           11  +#define srandom srand
           12  +#define random rand
           13  +#if defined(__DMC__)
           14  +#endif
           15  +
           16  +#if defined(_WIN32)
           17  +#define _CRT_SECURE_NO_WARNINGS 1
           18  +
           19  +#ifndef F_OK
           20  +#define F_OK 0
           21  +#endif /* not F_OK */
           22  +
           23  +#ifndef X_OK
           24  +#define X_OK 1
           25  +#endif /* not X_OK */
           26  +
           27  +#ifndef R_OK
           28  +#define R_OK 2
           29  +#endif /* not R_OK */
           30  +
           31  +#ifndef W_OK
           32  +#define W_OK 4
           33  +#endif /* not W_OK */
           34  +
           35  +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
           36  +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
           37  +
           38  +
           39  +
           40  +#endif
           41  +
           42  +#define access _access
           43  +#define ftruncate _chsize
           44  +
           45  +#define ssize_t int
           46  +
           47  +#endif /* unistd.h  */

Added www/apple-touch-icon.png.

cannot compute difference between binary files

Added www/background.jpg.

cannot compute difference between binary files

Changes to www/branching.wiki.

     1         -<title>Fossil Documentation</title>
     2         -<h1 align="center">
     3         -Branching, Forking, Merging, and Tagging
     4         -</h1>
            1  +<title>Branching, Forking, Merging, and Tagging</title>
            2  +<h2>Background</h2>
     5      3   
     6      4   In a simple and perfect world, the development of a project would proceed
     7      5   linearly, as shown in figure 1.
     8      6   
     9      7   <center><table border=1 cellpadding=10 hspace=10 vspace=10>
    10      8   <tr><td align="center">
    11      9   <img src="branch01.gif" width=280 height=68><br>
    12     10   Figure 1
    13     11   </td></tr></table></center>
    14     12   
    15     13   Each circle represents a check-in.  For the sake of clarity, the check-ins
    16     14   are given small consecutive numbers.  In a real system, of course, the
    17     15   check-in numbers would be 40-character SHA1 hashes since it is not possible
    18         -to allocate collision-free sequential numbers is a distributed system.
    19         -But sequential numbers are easier to read, so we will substitute them for
           16  +to allocate collision-free sequential numbers in a distributed system.
           17  +But as sequential numbers are easier to read, we will substitute them for
    20     18   the 40-character SHA1 hashes in this document.
    21     19   
    22         -The arrows in figure 1 show evolution of the project.  The initial
           20  +The arrows in figure 1 show the evolution of a project.  The initial
    23     21   check-in is 1.  Check-in 2 is derived from 1.  In other words, check-in 2
    24     22   was created by making edits to check-in 1 and then committing those edits.
    25     23   We say that 2 is a <i>child</i> of 1
    26     24   and that 1 is a <i>parent</i> of 2.  
    27     25   Check-in 3 is derived from check-in 2, making
    28     26   3 a child of 2.  We say that 3 is a <i>descendant</i> of both 1 and 2 and that 1
    29     27   and 2 are both <i>ancestors</i> of 3.  
    30     28   
    31         -We call the graph of check-ins a <i>tree</i>.  Check-in 1 is the <i>root</i>
    32         -since it has no ancestors.  Check-in 4 is a <i>leaf</i> of the tree since
    33         -it has no descendants.  (We will give a more precise in the definition of
    34         -"leaf" later.)
           29  +<a name="dag"></a>
           30  +<h2>DAGs</h2>
           31  +
           32  +The graph of check-ins is a 
           33  +[http://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic graph]
           34  +commonly shortened to <i>DAG</i>.  Check-in 1 is the <i>root</i> of the DAG
           35  +since it has no ancestors.  Check-in 4 is a <i>leaf</i> of the DAG since
           36  +it has no descendants.  (We will give a more precise definition later of
           37  +"leaf.")
    35     38   
    36     39   Alas, reality often interferes with the simple linear development of a
    37     40   project.  Suppose two programmers make independent modifications to check-in 2.
    38         -After both changes are checked in, we have a check-in graph that looks 
    39         -like figure 2:
           41  +After both changes are committed, the check-in graph looks like figure 2:
    40     42   
    41     43   <center><table border=1 cellpadding=10 hspace=10 vspace=10>
    42     44   <tr><td align="center">
    43     45   <img src="branch02.gif" width=210 height=140><br>
    44     46   Figure 2
    45     47   </td></tr></table></center>
    46     48   
    47     49   The graph in figure 2 has two leaves: check-ins 3 and 4.  Check-in 2 has
    48         -two children, check-ins 3 and 4.  We call this stituation a <i>fork</i>.
           50  +two children, check-ins 3 and 4.  We call this state a <i>fork</i>.
    49     51   
    50         -Fossil tries to prevent forks.  Suppose the two programmers who were
    51         -editing check-in 2 are named Alice and Bob.  Suppose Alice finished her
    52         -edits first and did a commit, resulting in check-in 3.  Later, when Bob
    53         -tried to commit his changes, fossil would try to verify that check-in 2
    54         -was still a leaf.  Fossil would see that check-in 3 had occurred and would
    55         -abort Bob's commit attempt with a message "would fork".  This allows Bob
    56         -to do a "fossil update" which would pull in Alices changes and merge them
    57         -together with his own changes.  After merging, Bob could then commit
    58         -check-in 4 as a child of check-in 3 and the result would be a linear graph
    59         -as shown in figure 1.  This is how CVS works.  This is also how fossil
    60         -works in "autosync" mode.
           52  +Fossil tries to prevent forks. Suppose two programmers named Alice and 
           53  +Bob are each editing check-in 2 separately. Alice finishes her edits 
           54  +first and commits her changes, resulting in check-in 3. Later, when Bob 
           55  +attempts to commit his changes, fossil verifies that check-in 2 is still 
           56  +a leaf. Fossil sees that check-in 3 has occurred and aborts Bob's commit 
           57  +attempt with a message "would fork." This allows Bob to do a "fossil 
           58  +update" which pulls in Alice's changes, merging them into his own 
           59  +changes. After merging, Bob commits check-in 4 as a child of check-in 3. 
           60  +The result is a linear graph as shown in figure 1. This is how CVS 
           61  +works. This is also how fossil works in [concepts.wiki#workflow | 
           62  +"autosync"] mode. 
    61     63   
    62         -But it might be that Bob is off-network when he does his commit, so he
           64  +But perhaps Bob is off-network when he does his commit, so he
    63     65   has no way of knowing that Alice has already committed her changes.
    64         -Or, it could be that Bob has turned off "autosync" mode in SQLite.  Or,
    65         -maybe Bob just doesn't want to merge in Alices changes before he has
           66  +Or, it could be that Bob has turned off "autosync" mode in Fossil.  Or,
           67  +maybe Bob just doesn't want to merge in Alice's changes before he has
    66     68   saved his own, so he forces the commit to occur using the "--force" option
    67         -to the fossil <b>commit</b> command.  For whatever reason, two commits against
    68         -check-in 2 have occurred and now the tree has two leaves.
           69  +to the fossil <b>commit</b> command.  For any of these reasons, two commits against
           70  +check-in 2 have occurred and now the DAG has two leaves.
    69     71   
    70     72   So which version of the project is the "latest" in the sense of having
    71     73   the most features and the most bug fixes?  When there is more than
    72     74   one leaf in the graph, you don't really know.  So we like to have
    73     75   graphs with a single leaf.
    74     76   
    75     77   To resolve this situation, Alice can use the fossil <b>merge</b> command
    76     78   to merge in Bob's changes in her local copy of check-in 3.  Then she
    77         -can commit the results as check-in 5.  This results in a tree as shown
           79  +can commit the results as check-in 5.  This results in a DAG as shown
    78     80   in figure 3.
    79     81   
    80     82   <center><table border=1 cellpadding=10 hspace=10 vspace=10>
    81     83   <tr><td align="center">
    82     84   <img src="branch03.gif" width=282 height=152><br>
    83     85   Figure 3
    84     86   </td></tr></table></center>
................................................................................
    95     97   Alice's check-in 3 changes, then committed, then the fork would have
    96     98   never occurred.  The resulting graph would have been linear, as shown
    97     99   in figure 1.  Really the graph of figure 1 is a subset of figure 3.
    98    100   Hold your hand over the check-in 4 circle of figure 3 and then figure
    99    101   3 looks exactly like figure 1 (except that the leaf has a different check-in
   100    102   number, but that is just a notational difference - the two check-ins have
   101    103   exactly the same content).  In other words, figure 3 is really a superset
   102         -of figure 1.  The check-in 4 of figure 3 captures addition state which
          104  +of figure 1.  The check-in 4 of figure 3 captures additional state which
   103    105   is omitted from figure 1.  Check-in 4 of figure 3 holds a copy
   104    106   of Bob's local checkout before he merged in Alice's changes.  That snapshot
   105         -of Bob's changes independent of Alice's changes is omitted from figure 1.
          107  +of Bob's changes, which is independent of Alice's changes, is omitted from figure 1.
   106    108   Some people say that the approach taken in figure 3 is better because it
   107    109   preserves this extra intermediate state.  Others say that the approach
   108    110   taken in figure 1 is better because it is much easier to visualize a
   109         -linear line of development and because the the merging happens automatically
          111  +linear line of development and because the merging happens automatically
   110    112   instead of as a separate manual step.  We will not take sides in that
   111    113   debate.  We will simply point out that fossil enables you to do it either way.
   112    114   
   113    115   <h2>Forking Versus Branching</h2>
   114    116   
   115         -Having more than one leaf in the check-in tree is usually
   116         -considered undesirable, and so forks are usually either avoided entirely,
          117  +Having more than one leaf in the check-in DAG is called a "fork." This 
          118  +is usually undesirable and either avoided entirely,
   117    119   as in figure 1, or else quickly resolved as shown in figure 3.
   118    120   But sometimes, one does want to have multiple leaves.  For example, a project
   119    121   might have one leaf that is the latest version of the project under
   120    122   development and another leaf that is the latest version that has been
   121    123   tested.
   122         -When multiple leaves are desirable, we call the phenomenon <i>branching</i>
          124  +When multiple leaves are desirable, we call this <i>branching</i>
   123    125   instead of <i>forking</i>.
   124    126   Figure 4 shows an example of a project where there are two branches, one
   125    127   for development work and another for testing.
   126    128   
   127    129   <center><table border=1 cellpadding=10 hspace=10 vspace=10>
   128    130   <tr><td align="center">
   129    131   <img src="branch04.gif" width=426 height=123><br>
................................................................................
   144    146   release.  Of course, the development team would like to take advantage of
   145    147   the bug fixes implemented by the testing team.  So periodically, the
   146    148   changes in the test branch are merged into the dev branch.  This is
   147    149   shown by the dashed merge arrows between check-ins 6 and 7 and between
   148    150   check-ins 9 and 10.
   149    151   
   150    152   In both figures 2 and 4, check-in 2 has two children.  In figure 2,
   151         -we called this a "fork".  In diagram 4, we call it a "branch".  What is
          153  +we call this a "fork."  In diagram 4, we call it a "branch."  What is
   152    154   the difference?  As far as the internal fossil data structures are
   153    155   concerned, there is no difference.  The distinction is in the intent.
   154    156   In figure 2, the fact that check-in 2 has multiple children is an
   155    157   accident that stems from concurrent development.  In figure 4, giving
   156    158   check-in 2 multiple children is a deliberate act.  So, to a good
   157    159   approximation, we define forking to be by accident and branching to
   158    160   be by intent.  Apart from that, they are the same.
   159    161   
          162  +<a name="tags"></a>
   160    163   <h2>Tags And Properties</h2>
   161    164   
   162    165   Tags and properties are used in fossil to help express the intent, and
   163    166   thus to distinguish between forks and branches.  Figure 5 shows the
   164    167   same scenario as figure 4 but with tags and properties added:
   165    168   
   166    169   <center><table border=1 cellpadding=10 hspace=10 vspace=10>
................................................................................
   186    189   tag that was previously placed on that same check-in, or to block
   187    190   tag propagation from an ancestor.
   188    191   
   189    192   Every repository is created with a single empty check-in that has two
   190    193   propagating tags.  In figure 5, that initial empty check-in is check-in 1.
   191    194   The <b>branch</b> tag tells (by its value) 
   192    195   what branch the check-in is a member of.
   193         -The default branch is called "trunk".  All tags that begin with "<b>sym-</b>"
          196  +The default branch is called "trunk."  All tags that begin with "<b>sym-</b>"
   194    197   are symbolic name tags.  When a symbolic name tag is attached to a
   195    198   check-in, that allows you to refer to that check-in by its symbolic
   196    199   name rather than by its 40-character SHA1 hash name.  When a symbolic name
   197    200   tag propagates (as does the <b>sym-trunk</b> tag) then referring to that
   198    201   name is the same as referring to the most recent check-in with that name.
   199         -Thus the two tags on check-in one cause all decendents to be in the
   200         -"trunk" branch and to have the symbolic name "trunk".
          202  +Thus the two tags on check-in 1 cause all descendants to be in the
          203  +"trunk" branch and to have the symbolic name "trunk."
   201    204   
   202    205   Check-in 4 has a <b>branch</b> tag which changes the name of the branch
   203         -to "test".  The branch tag on check-in 4 propagates to check-ins 6 and 9.
          206  +to "test."  The branch tag on check-in 4 propagates to check-ins 6 and 9.
   204    207   But because tag propagation does not follow merge links, the <b>branch=test</b>
   205    208   tag does not propagate to check-ins 7, 8, or 10.  Note also that the
   206    209   <b>branch</b> tag on check-in 4 blocks the propagation of <b>branch=trunk</b>
   207    210   so that it cannot reach check-ins 6 or 9.  This causes check-ins 4, 6, and
   208    211   9 to be in the "test" branch and all others to be in the "trunk" branch.
   209    212   
   210    213   Check-in 4 also has a <b>sym-test</b> tag, which gives the symbolic name
   211    214   "test" to check-ins 4, 6, and 9.  Because tags do not propagate across
   212    215   merges, check-ins 7, 8, and 10 do not inherit the <b>sym-test</b> tag and
   213         -are hence not known by the name "test".
          216  +are hence not known by the name "test."
   214    217   To prevent the <b>sym-trunk</b> tag from propagating from check-in 1 
   215    218   into check-ins 4, 6, and 9, there is a cancellation tag for 
   216         -<b>sym-trunk</b> on check-in 4.  The net effect of all of this is that
          219  +<b>sym-trunk</b> on check-in 4.  The net effect is that
   217    220   check-ins on the trunk go by the symbolic name of "trunk" and check-ins
   218         -that are on the test branch go by the symbolic name "test".
          221  +on the test branch go by the symbolic name "test."
   219    222   
   220    223   The <b>bgcolor=blue</b> tag on check-in 4 causes the background color
   221    224   of timelines to be blue for check-in 4 and its direct descendants.
   222    225   
   223    226   Figure 5 also shows two one-time tags on check-in 9.  (The diagram does
   224    227   not make a graphical distinction between one-time and propagating tags.)
   225    228   The <b>sym-release-1.0</b> tag means that check-in 9 can be referred to
   226         -using the more meaningful name "release-1.0".  The <b>closed</b> tag means
   227         -that check-in 9 is a "closed leaf".  A closed leaf is a leaf that intended
   228         -to never have any direct children.
          229  +using the more meaningful name "release-1.0."  The <b>closed</b> tag means
          230  +that check-in 9 is a "closed leaf."  A closed leaf is a leaf that should 
          231  +never have direct children.
   229    232   
   230    233   <h2>Review Of Terminology</h2>
   231         -
   232         -Here is a list of definitions of key terms:
   233         -
   234    234   
   235    235   <blockquote><dl>
   236    236   <dt><b>Branch</b></dt>
   237         -<dd><p>A branch is a set of check-ins that have the same value for their
   238         -branch property.</p></dd>
          237  +<dd><p>A branch is a set of check-ins with the same value for their
          238  +"branch" property.</p></dd>
   239    239   <dt><b>Leaf</b></dt>
   240         -<dd><p>A leaf is a check-in that has no children in the same branch.</p></dd>
          240  +<dd><p>A leaf is a check-in with no children in the same branch.</p></dd>
   241    241   <dt><b>Closed Leaf</b></dt>
   242         -<dd><p>A closed leaf is leaf that has the <b>closed</b> tag.  Such leaves
   243         -are intented to never be extended with descendents and hence are omitted
          242  +<dd><p>A closed leaf is any leaf with the <b>closed</b> tag.  These leaves
          243  +are intended to never be extended with descendants and hence are omitted
   244    244   from lists of leaves in the command-line and web interface.</p></dd>
   245    245   <dt><b>Open Leaf</b></dt>
   246    246   <dd><p>A open leaf is a leaf that is not closed.</p></dd>
   247    247   <dt><b>Fork</b></dt>
   248         -<dd><p>A fork occurs when a check-in has two or more direct (non-merge)
          248  +<dd><p>A fork is when a check-in has two or more direct (non-merge)
   249    249   children in the same branch.</p></dd>
   250    250   <dt><b>Branch Point</b></dt>
   251    251   <dd><p>A branch point occurs when a check-in has two or more direct (non-merge)
   252         -children in the different branches.  A branch point is similar to a fork,
          252  +children in different branches.  A branch point is similar to a fork,
   253    253   except that the children are in different branches.</p></dd>
   254    254   </dl></blockquote>
   255    255   
   256    256   Check-in 4 of figure 3 is not a leaf because it has a child (check-in 5)
   257    257   in the same branch.  Check-in 9 of figure 5 also has a child (check-in 10)
   258    258   but that child is in a different branch, so check-in 9 is a leaf.  Because
   259         -of the <b>closed</b> tag check-in 9, it is a closed leaf.
          259  +of the <b>closed</b> tag on check-in 9, it is a closed leaf.
   260    260   
   261    261   Check-in 2 of figure 3 is considered a "fork"
   262    262   because it has two children in the same branch.  Check-in 2 of figure 5
   263    263   also has two children, but each child is in a different branch, hence in
   264         -figure 5, check-in 2 is considered a "branch point".
          264  +figure 5, check-in 2 is considered a "branch point."
          265  +
          266  +<h2>Differences With Other DVCSes</h2>
          267  +
          268  +Fossil keeps all check-ins on a single DAG.  Branches are identified with
          269  +tags.  This means that check-ins can be freely moved between branches
          270  +simply by altering their tags.
          271  +
          272  +Most other DVCSes maintain a separate DAG for each branch.

Changes to www/bugtheory.wiki.

     1         -<title>Fossil Documentation</title>
     2         -<h1>Bug-Tracking In <a href="index.wiki">Fossil</a></h1>
            1  +<title>Bug-Tracking In Fossil</title>
            2  +<h2>Introduction</h2>
     3      3   
     4      4   A bug-report in fossil is called a "ticket".  Tickets are tracked
     5      5   separately from code check-ins.
     6      6   
     7      7   Some other distributed bug-tracking systems store tickets as files within
     8      8   the source tree and thereby leverage the syncing and merging capabilities
     9      9   of the versioning system to sync and merge tickets.  This approach is

Changes to www/build-icons/mac.gif.

cannot compute difference between binary files

Added www/build-icons/openbsd.gif.

cannot compute difference between binary files

Changes to www/build.wiki.

     1         -<title>Building and Installing Fossil</title>
     2         -<nowiki>
     3         -
     4         -<p>This page describes how to build and install Fossil.  The
     5         -whole process is designed to be very easy.</p>
            1  +<title>Compiling and Installing Fossil</title>
     6      2   
     7      3   <h2>0.0 Using A Pre-compiled Binary</h2>
     8      4   
     9         -<p>You can skip steps 1.0 and 2.0 below by downloading
    10         -a <a href="http://www.fossil-scm.org/download.html">pre-compiled binary</a>
    11         -appropriate for your platform.  If you use a pre-compiled binary
    12         -jump immediate to step 3.0.</p>
            5  +<p>Released versions of fossil come with
            6  +<a href="http://www.fossil-scm.org/download.html">pre-compiled binaries and
            7  +a source archive</a> for that release. You can thus skip the following if you
            8  +want to run or build a release version of fossil. Just download
            9  +the appropriate package from the <a href="http://www.fossil-scm.org/download.html">downloads page</a>
           10  +and put it on your $PATH.
           11  +To uninstall, simply delete the binary.
           12  +To upgrade from an older release, just overwrite the older binary with
           13  +the newer one.</p>
           14  +
           15  +<h2>0.1 Executive Summary</h2>
           16  +
           17  +<p>Building and installing is very simple.  Three steps:</p>
           18  +
           19  +<ol>
           20  +<li> Download and unpack a source tarball or ZIP.
           21  +<li> <b>./configure; make</b>
           22  +<li> Move or copy the resulting "fossil" executable to someplace
           23  +     on your $PATH.
           24  +</ol>
           25  +
           26  +<p><hr>
    13     27   
    14     28   <h2>1.0 Obtaining The Source Code</h2>
    15     29   
    16         -<p>Fossil is self-hosting, so you can obtain a ZIP archive containing
    17         -a snapshot of the latest version directly from fossil's own fossil
    18         -repository.  Follow these steps:</p>
           30  +<p>Fossil is self-hosting, so you can obtain a ZIP archive or tarball
           31  +containing a snapshot of the <em>latest</em> version directly from 
           32  +Fossil's own fossil repository. Additionally, source archives of 
           33  +<em>released</em> versions of
           34  +fossil are available from the <a href="http://www.fossil-scm.org/download.html">downloads page</a>.
           35  +To obtain a development version of fossil, follow these steps:</p>
    19     36   
    20     37   <ol>
    21         -<li><p>Pointer your web browser at
           38  +<li><p>Point your web browser at
    22     39   <a href="http://www.fossil-scm.org/">
    23         -http://www.fossil-scm.org/</a>.  Click on the "Login" menu button.</p></li>
    24         -
    25         -<li><p>Log in as anonymous.  The password is shown on screen.
    26         -The reason for requiring this login is to prevent spiders from
    27         -walking the entire website, downloading ZIP archives
    28         -of every historical version, and thereby soaking up all our bandwidth.</p></li>
           40  +http://www.fossil-scm.org/</a>.</p></li>
    29     41   
    30     42   <li><p>Click on the 
    31         -<a href="http://www.fossil-scm.org/fossil/timeline">Timeline</a> or
    32         -<a href="http://www.fossil-scm.org/fossil/leaves">Leaves</a> link at
    33         -the top of the page.</p></li>
           43  +<a href="http://www.fossil-scm.org/fossil/timeline">Timeline</a> 
           44  +link at the top of the page.</p></li>
    34     45   
    35         -<li><p>Select a version of of fossil you want to download.  Click on its
    36         -link.  Note that you must successfully log in as "anonymous" in step 1
    37         -above in order to see the link to the detailed version information.</p></li>
           46  +<li><p>Select a version of of Fossil you want to download.  The latest
           47  +version on the trunk branch is usually a good choice.  Click on its
           48  +link.</p></li>
    38     49   
    39         -<li><p>Finally, click on the
    40         -"Zip Archive" link.  This link will build a ZIP archive of the 
           50  +<li><p>Finally, click on one of the
           51  +"Zip Archive" or "Tarball" links, according to your preference.
           52  +These link will build a ZIP archive or a gzip-compressed tarball of the 
    41     53   complete source code and download it to your browser.
    42     54   </ol>
    43     55   
    44     56   <h2>2.0 Compiling</h2>
    45     57   
    46         -<p>Follow these steps to compile:</p>
    47         -
    48     58   <ol>
    49         -<li value="6">
    50         -<p>Create a directory to hold the source code.  Then unzip the
    51         -ZIP archive you downloaded into that directory.  You should be
    52         -in the top-level folder of that directory</p></li>
           59  +<li value="5">
           60  +<p>Unpack the ZIP or tarball you downloaded then
           61  +<b>cd</b> into the directory created.</p></li>
           62  +
           63  +<li><i>(Optional, unix only)</i>
           64  +Run <b>./configure</b> to construct a makefile.
           65  +
           66  +<ol type="a">
           67  +<li><p>
           68  +If you do not have the OpenSSL library installed on your system, then
           69  +add <b>--with-openssl=none</b> to omit the https functionality.
           70  +
           71  +<li><p>
           72  +To build a statically linked binary (suitable for use inside a chroot
           73  +jail) add the <b>--static</b> option.
           74  +
           75  +<li><p>
           76  +Other configuration options can be seen by running
           77  +<b>./configure --help</b>
           78  +</ol>
           79  +
           80  +<li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable.
           81  +The details depend on your platform and compiler.
           82  +
           83  +<ol type="a">
           84  +<li><p><i>Unix</i> → the configure-generated Makefile should work on
           85  +all unix and unix-like systems.  Simply type "<b>make</b>".
           86  +
           87  +<li><p><i>Unix without running "configure"</i> → if you prefer to avoid running configure, you
           88  +can also use: <b>make -f Makefile.classic</b>.  You may want to make minor
           89  +edits to Makefile.classic to configure the build for your system.
    53     90   
    54         -<li><p><b>(Optional:)</b>
    55         -Edit the Makefile to set it up like you want.  You probably do not
    56         -need to do anything.  Do not be intimidated:  There are less than 10
    57         -variables in the makefile that can be changed.  The whole Makefile
    58         -is only a few dozen lines long and most of those lines are comments.</p>
           91  +<li><p><i>MinGW/MinGW-w64</i> → Use the mingw makefile:
           92  +"<b>make -f win/Makefile.mingw</b>". On a Windows box you will
           93  +need either Cygwin or Msys as build environment. On Cygwin, Linux
           94  +or Darwin you may want to make minor edits to win/Makefile.mingw
           95  +to configure the cross-compile environment.
    59     96   
    60         -<li><p>Type "<b>make</b>"
           97  +<li><p><i>VC++</i> → Use the msc makefile.  First
           98  +change to the "win/" subdirectory ("<b>cd win</b>") then run
           99  +"<b>nmake /f Makefile.msc</b>".
          100  +</ol>
    61    101   </ol>
    62    102   
    63    103   <h2>3.0 Installing</h2>
    64    104   
    65    105   <ol>
    66         -<li value="9">
    67         -<p>The finished binary is named "fossil".  Put this binary in a 
          106  +<li value="8">
          107  +<p>The finished binary is named "fossil" (or "fossil.exe" on windows).  
          108  +Put this binary in a 
    68    109   directory that is somewhere on your PATH environment variable.
    69    110   It does not matter where.</p>
    70    111   
    71    112   <li>
    72    113   <p><b>(Optional:)</b>
    73    114   To uninstall, just delete the binary.</p>
    74    115   </ol>
          116  +
          117  +<h2>4.0 Additional Considerations</h2>
          118  +
          119  +<ul>
          120  +<li><p>
          121  +  If the makefiles that come with Fossil do not work for
          122  +  you, or for some other reason you want to know how to build
          123  +  Fossil manually, then refer to the
          124  +  [./makefile.wiki | Fossil Build Process] document which describes
          125  +  in detail what the makefiles do behind the scenes.
          126  +
          127  +<li><p>
          128  +  To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile
          129  +  generated by configure to add the following lines:
          130  +  <blockquote><pre>
          131  +  TCC += -DSQLITE_WITHOUT_ZONEMALLOC
          132  +  TCC += -DWITHOUT_ICONV
          133  +  TCC += -Dsocketlen_t=int
          134  +  </pre></blockquote>
          135  +</ul>

Added www/changes.wiki.

            1  +<title>Change Log</title>
            2  +
            3  +<h2>Changes For Version 1.25 (2013-02-16)</h2>
            4  +  *  Enhancements to ticket processing. There are now two tables: TICKET and
            5  +     TICKETCHNG. There is one row in TICKETCHNG for each ticket artifact.
            6  +     Fields from ticket artifacts go into either or both of TICKET and
            7  +     TICKETCHNG, whichever contain matching column names. Default ticket 
            8  +     edit and viewing scripts are updated to use TICKETCHNG. The TH1
            9  +     scripting language is enhanced to support this, including the new
           10  +     "query" command for doing SQL queries against the repository database.
           11  +     All changes should be backwards compatible. 
           12  +  *  Add the ability to moderate ticket and wiki changes.  Unmoderated changes
           13  +     do not sync and may be deleted by the moderator if found to contain spam
           14  +     or other objectionable content.
           15  +  *  Add javascript so that clicking on a node of the timeline graph selects
           16  +     that node.  Then clicking on a second node shows a diff between the
           17  +     two nodes.  Clicking on the selected node unselects it.
           18  +  *  Warn of unresolved merge conflicts in "fossil status" and disallow
           19  +     commits of unresolved conflicts unless the --allow-conflict option
           20  +     is used.
           21  +  *  Add javascript so that clicking on column headers in a ticket report
           22  +     sorts by the indicated column.
           23  +  *  Add the "fossil cat" command which is basically an alias for
           24  +     "fossil finfo -p".
           25  +  *  Hyperlinks with the class "button" are rendered as submenu buttons
           26  +     on embedded documentation.
           27  +  *  The check-in comment editor on windows now defaults to NotePad.exe.
           28  +  *  Correctly deal with BOMs in check-in comments.  Also attempt to convert 
           29  +     check-in comments to UTF8 from other encodings.
           30  +  *  Allow the deletion of multiple stash entries using multiple arguments
           31  +     to the "fossil stash rm" command.
           32  +  *  Enhance the "fossil server DIRECTORY" command to serve static content
           33  +     files contained in DIRECTORY.  For security, only files with a 
           34  +     recognized suffix (such as *.html, *.jpg, *.txt, etc) will be delivered
           35  +     as static content, and *.fossil files are not on the list of recognized
           36  +     suffixes.  There are additional restrictions on the names of the files.
           37  +  *  Allow the "fossil ui" command to specify a directory as long as the
           38  +     the --notfound option is used.
           39  +  *  Add a configuration option that causes timeline messages to be rendered
           40  +     as text/x-fossil-plain (which is the same as text/plain except that
           41  +     hyperlinks inside of <nowiki>[...]</nowiki> are decorated.)
           42  +  *  Only decorate <nowiki>[...]</nowiki> in check-in comments and tickets
           43  +     if the contented text really is a valid hyperlink target.
           44  +  *  Improvements to the side-by-side diff algorithm, for a more
           45  +     human-friendly display in some complex cases.
           46  +  *  Added <nowiki>[utime] and [stime]</nowiki> commands to TH1.  These
           47  +     commands can be used for things such as displaying the page rendering
           48  +     time in the footer.
           49  +  *  Add the ability to pass command-line options of "fossil rebuild" to
           50  +     "fossil all rebuild".
           51  +  *  Add the --deanalyze option to "fossil rebuild" (and "fossil all rebuild")
           52  +  *  Do not run the graphical merging tool nor leave merge-droppings after a
           53  +     dry-run merge.  Display an improved merge-summary message at the end of
           54  +     the merge.
           55  +  *  Add options to "fossil commit" to override the various sanity checks.
           56  +     Options added: --allow-empty, --allow-fork, --allow-older, and
           57  +     --allow-conflict.
           58  +  *  Optionally require a CAPTCHA (controlled by a setting on the
           59  +     Admin/Access webpage) when a user who is not logged in tries to
           60  +     edit wiki, or a ticket, or an attachment. 
           61  +  *  Improvements to the "ssh://" sync protocol, to help it move past
           62  +     noisey motd comments.
           63  +  *  Add the uf=FILE-SHA1-HASH query parameter to the timeline, causing the
           64  +     timeline to show only check-ins that contain the specific file identified
           65  +     by FILE-SHA1-HASH.  ("uf" stands for "uses file".)
           66  +  *  Enhance the file change annotator so that it follows the file across
           67  +     name changes.
           68  +  *  Fix the server-side of the sync protocol so that it will not generate
           69  +     a delta loop when a file changes from its original state, through two
           70  +     or more intermediate states, and back to the original state, all within
           71  +     a single sync.
           72  +  *  Show much less output during a sync operation, unless the --verbose
           73  +     option is used.
           74  +  *  Set the action= attribute of &lt;form&gt; elements using javascript,
           75  +     as an addition defense against spam-bots.
           76  +  *  Disallow invalid UTF8 characters (such as characters in the surrogate
           77  +     pair range) in filenames.
           78  +  *  Judge the UserAgent strings issued by the NetSurf webbrowser to be
           79  +     coming from a human, not from a bot.
           80  +  *  Add the zlib sources to the Fossil source tree (under compat/zlib) and
           81  +     use those sources when compiling on (windows) systems that do not have
           82  +     a zlib library installed by default.
           83  +  *  Prompt the user with the option to convert non-UTF8 files into UTF8
           84  +     when committing.
           85  +  *  Allow the characters <nowiki>*[]?</nowiki> in filenames.
           86  +  *  Allow the --context option on diff commands to have a value of 0.
           87  +  *  Added the "dbstat" command.
           88  +  *  Enhanced "fossil merge" so that if the VERSION argument is omitted, Fossil
           89  +     tries to merge any forks of the current branch.
           90  +  *  Improved detection of forks in a commit race.
           91  +  *  Added the --analyze option to "fossil rebuild".
           92  +
           93  +<h2>Changes For Version 1.24 (2012-10-22)</h2>
           94  +  *  Added support for WYSIWYG editing of wiki pages. WYSIWYG is turned off
           95  +     by default and can be turned on by setting a configuration option.
           96  +  *  Allow style= attribute to occur in HTML markup on wiki pages.
           97  +  *  Added the --tk option to the "fossi diff" and "fossil stash diff"
           98  +     commands, causing color-coded diff output to be displayed in a Tcl/Tk
           99  +     GUI window.  This option only works if Tcl/Tk is installed on the
          100  +     host.
          101  +  *  On windows, make the "gdiff" command default to use WinDiff.exe.
          102  +  *  Update the "fossil stash" command so that it always prompts for a
          103  +     comment if the -m option is omitted.
          104  +  *  Enhance the timeline webpages so that a=, b=, c=, d=, p=, and dp=
          105  +     query parameters (and others) can all accept any valid checkin name
          106  +     (such as branch names or labels) instead of just SHA1 hashes.
          107  +  *  Added the "fossil stash show" command.
          108  +  *  Added the "fileage" webpage with links to this page from the check-in
          109  +     information page and from the file browser.
          110  +  *  Added --age and -t options to the "fossil ls" command.
          111  +  *  Added the --setmtime option to "fossil update".  When used, the mtime
          112  +     of all mananged files is set to the time when the most recent version of
          113  +     the file was checked in.
          114  +  *  Changed the "vdiff" webpage to show the complete text of files that
          115  +     were added or removed (the equivelent of using the -N or --newfile
          116  +     options with the "fossil diff" command-line.)
          117  +  *  Added the --temp option to "fossil clean" and "fossil extra", causing
          118  +     those commands to only look at temporary files generated by Fossil,
          119  +     such as merge-conflict reports or aborted check-in messages.
          120  +  *  Enhance the raw page download so that it can guess the mimetype of
          121  +     attachments based on the filename.
          122  +  *  Change the behavior of the from= and to= query parameters on the
          123  +     timeline page so that by default the path between the two specified
          124  +     check-ins avoids merges.
          125  +  *  Add the --baseurl option to "fossil server" and "fossil http" commands,
          126  +     so that those commands can be used with reverse proxies.
          127  +  *  If unable to determine the command-line user, do not guess.  Instead
          128  +     issue an error message.  This helps prevent check-ins from accidentally
          129  +     occurring under the wrong username.
          130  +  *  Include branch information in the output of file change listings
          131  +     (the "finfo" webpage).
          132  +  *  Make the simplified view of file history, rather than the full view,
          133  +     the default.
          134  +  *  In the "fossil configuration" command, allow the "css" option for
          135  +     synchronizing, importing, or exporting just the CSS file.  This makes
          136  +     it easier to share CSS files across repositories by exporting from
          137  +     one and importing to another.
          138  +  *  Add the (unsupported) "fossil test-orphans" command.
          139  +  *  Add the --template option to the "fossil init" command, to facilitate
          140  +     creating new repositories based on a template repository.
          141  +  *  Add the diff-binary setting, which if enabled causes binary files to
          142  +     be passed to the "gdiff" command for it to deal with, rather than simply
          143  +     printing a "cannot diff binary files" error.
          144  +  *  Add the --unified option to the "fossil diff" command to force a unified
          145  +     diff even if the --tk option (which normally implies a side-by-side diff)
          146  +     is used.
          147  +  *  Present a choice of nearby branches and versions to diff against on the
          148  +     check-in information page.
          149  +  *  Add the --force option to the "fossil merge" command that will force the
          150  +     merge to occur even if it would be a no-op.  This is sometimes useful for
          151  +     documentation purposes.
          152  +  *  Add another built-in skin:  "Enhanced Default".  Other minor tweaks to
          153  +     the existing skins.
          154  +  *  Add the "urllist" webpage, showing a list of URLs by which a server
          155  +     instance of Fossil has been accessed.  Requires "Administrator" privileges.
          156  +     A link is on the "Setup" main page.
          157  +  *  Enable dynamic loading of the Tcl runtime for installations that want
          158  +     to use Tcl as part of their configuration.  This reduces the size of
          159  +     the Fossil binary and allows any version of Tcl 8.4 or later to be used.
          160  +  *  Merge the latest SQLite changes from upstream.
          161  +  *  Lots of minor bug fixes.
          162  +
          163  +<h2>Changes For Version 1.23 (2012-08-08)</h2>
          164  +  *  The default checkout database name is now ".fslckout" instead of
          165  +     "_FOSSIL_" on unix.  Both names continue to work.
          166  +  *  Added the "fossil all changes" command
          167  +  *  Added the --ckout option to the "fossil all list" command
          168  +  *  Added the "public-pages" glob pattern that can be configured to allow
          169  +     anonymous users to see embedded documentation on sites where source
          170  +     code should not be accessible to anonymous users.
          171  +  *  Allow multiple --tag options on the same "fossil commit" command.
          172  +  *  Change the meaning of the --bgcolor option for "fossil commit" to only
          173  +     change the color for that one commit.  The new --branchcolor option 
          174  +     is available to set a persistent background color.
          175  +  *  Add the branch= query parameter to the vdiff page and the --branch option
          176  +     to the "fossil diff" command.
          177  +  *  Check-in names of the form "root:BRANCH" now refer to the origin of
          178  +     the branch.  Hence to see all changes in a branch, use 
          179  +     "fossil diff --from root:BRANCH --to BRANCH".  The --branch option on
          180  +     the diff command is an alias for the same.
          181  +  *  Add the ability to configure ad-units to be displayed between the menu
          182  +     bar and the content.
          183  +  *  Add the ability to set a background image as part of server configuration.
          184  +  *  Allow partial commits of cherrypick merges.
          185  +  *  Updates against an uncommitted merge are now a warning, not a fatal error.
          186  +  *  Prompt the user to continue if a check-in comment is unedited.
          187  +  *  Fixes to case sensitivity settings with the /dir webpage.
          188  +  *  Repositories now try to remember the locations of all checkouts and
          189  +     web-access URLs and display this information with the 
          190  +     "fossil info $REPO" command.
          191  +  *  Improved defense against spiders:  The src= attribute of
          192  +     &lt;a&gt; elements is set using javascript after the page loads.
          193  +  *  Enhanced formatting of the user list page.
          194  +  *  If a file named in "fossil add" is missing, that is now a warning instead
          195  +     of a fatal error.
          196  +  *  Fix side-by-side diff so that it displays correctly with 
          197  +     multi-byte UTF8 characters.
          198  +  *  Performance improvements in the diff logic.
          199  +  *  Other performance tweaks and documentation updates.
          200  +
          201  +<h2>Changes For Version 1.22 (2012-03-17)</h2>
          202  +  *  Greatly improved "diff" processing including the new --brief option,
          203  +     partial line matching, colorized in-line diffs, and better performance.
          204  +  *  Promote "allow-symlinks" to a versionable setting
          205  +  *  Harden the CGI processing logic against DOS attacks
          206  +  *  Add the ability to run TH1 scripts after sync requests
          207  +  *  Store the repository name in _FOSSIL_ as it is type in the "open" command,
          208  +     possibly as a relative pathname.
          209  +  *  Make ".fslckout" the alternative name for the "_FOSSIL_" file. 
          210  +  *  Change the "ssh:" transfer method to allow all access regardless of
          211  +     user permission.
          212  +  *  Improvements to the timeline messages associated with tag changes.
          213  +     (Requires a "[/help/rebuild | fossil rebuild]" to take effect.)
          214  +  *  Various additions and fixes for the JSON API.
          215  +  *  Improved merge-with-rename handling.
          216  +  *  --cherrypick merges use their origin's commit message by default.
          217  +  *  Added support for multiple concurrent logins per user.
          218  +  *  Update to use SQLite version 3.7.11.
          219  +  *  Various minor bug fixes.
          220  +
          221  +<h2>Changes For Version 1.21 (2011-12-13)</h2>
          222  +  *  Added side-by-side diffs in the command-line interface
          223  +  *  Automatically enable hyperlinks if the UserAgent string in the 
          224  +     HTTP header suggests that the requestor is a human and not a bot.
          225  +  *  Show only commonly used commands with "fossil help".  Use
          226  +     "fossil help --all" to see the complete list now.
          227  +  *  Improvements to the "stash" command:  (1) Stash all files, not just 
          228  +     those below the working directory. (2) Add the --detail option to 
          229  +     "list". (3) Confirm before "drop --all". (4) Add the "help" 
          230  +     subcommand.
          231  +  *  Add an Admin/Access setting to change the number of octets of the
          232  +     IP address that are saved in login cookies - allowing this setting
          233  +     to be changed to zero
          234  +  *  Promote the "test-md5sum" command to "md5sum".
          235  +  *  Added the "whatis" command.
          236  +  *  Stop showing the server-code in status outputs - it is no longer used
          237  +     for anything.
          238  +  *  Added a compile-time option (--with-tcl) to build in the full
          239  +     TCL interpreter to augment TH1.
          240  +  *  Merged the JSON branch into trunk.  Disabled by default.  Enabled
          241  +     by a compile-time option.  Probably it will be enabled by default
          242  +     in some future release.
          243  +  *  Update to use SQLite version 3.7.9 plus the alignment fix for Sparc.
          244  +     align
          245  +
          246  +<h2>Changes For Version 1.20 (2011-10-21)</h2>
          247  +  *  Added side-by-side diffs in HTML interface. [0bde74ea1e]
          248  +  *  Added support for symlinks. (Controlled by "allow-symlinks" setting,
          249  +     off by default). [e4f1c1fe95]
          250  +  *  Fixed CLI annotate to show the proper file version in case there
          251  +     are multiple equal versions in history. [e161670939]
          252  +  *  Timeline now shows tag changes (requires rebuild).[87540ed6e6]  
          253  +  *  Fixed annotate to show "more relevant" versions of lines in
          254  +     some cases. [e161670939]
          255  +  *  New command: ticket history. [98a855c508]
          256  +  *  Disabled SSLv2 in HTTPS client.[ea1d369d23]
          257  +  *  Fixed constant prompting regarding previously-saved SSL
          258  +     certificates. [636804745b]
          259  +  *  Other SSL improvements.
          260  +  *  Added -R REPOFILE support to several more CLI commands. [e080560378]
          261  +  *  Generated tarballs now have constant timestamps, so they are
          262  +     always identical for any given checkin. [e080560378]
          263  +  *  A number of minor HTML-related tweaks and fixes.
          264  +  *  Added --args FILENAME global CLI argument to import arbitrary
          265  +     CLI arguments from a file (e.g. long file lists). [e080560378]
          266  +  *  Fixed significant memory leak in annotation of files with long
          267  +     histories.[9929bab702] 
          268  +  *  Added warnings when a merge operation overwrites local copies
          269  +     (UNDO is available, but previously this condition normally went
          270  +     silently unnoticed). [39f979b08c]
          271  +  *  Improved performance when adding many files. [a369dc7721]
          272  +  *  Improve merges which contain many file renames. [0b93b0f958]
          273  +  *  Added protection against timing attacks. [d4a341b49d]
          274  +  *  Firefox now remembers filled fields when returning to forms. [3fac77d7b0]
          275  +  *  Added the --stats option to the rebuild command. [f25e5e53c4]
          276  +  *  RSS feed now passes validation. [ce354d0a9f]
          277  +  *  Show overridden user when entering commit comment. [ce354d0a9f]
          278  +  *  Made rebuilding from web interface silent. [ce354d0a9f]
          279  +  *  Now works on MSVC with repos >2GB. [6092935ff2]
          280  +  *  A number of code cleanups to resolve warnings from various compilers.
          281  +  *  Update the built-in SQLite to version 3.7.9 beta.
          282  +
          283  +<h2>Changes For Version 1.19 (2011-09-02)</h2>
          284  +
          285  +  *  Added a ./configure script based on autosetup.
          286  +  *  Added the "[/help/winsrv | fossil winsrv]" command
          287  +     for creating a Fossil service on windows systems.
          288  +  *  Added "versionable settings" where settings that affect
          289  +     the local tree can be stored in versioned files in the
          290  +     .fossil-settings directory.
          291  +  *  Background colors for branches are choosen automatically if no
          292  +     color is specified by the user.
          293  +  *  The status, changes and extras commands now show
          294  +     pathnames relative to the current working directory,
          295  +     unless overridden by command line options or the
          296  +     "relative-paths" setting.<br><b>WARNING:</b> This
          297  +     change will break scripts which rely on the current
          298  +     output when the current working directory is not the
          299  +     repository root.
          300  +  *  Added "empty-dirs" versionable setting.
          301  +  *  Added support for client-side SSL certificates with "ssl-identity"
          302  +     setting and --ssl-identity option.
          303  +  *  Added "ssl-ca-location" setting to specify trusted root
          304  +     SSL certificates.
          305  +  *  Added the --case-sensitive BOOLEAN command-line option to many commands.
          306  +     Default to true for unix and false for windows.
          307  +  *  Added the "Color-Test" submenu button on the branch list web page.
          308  +  *  Compatibility improvements to the git-export feature.
          309  +  *  Performance improvements on SHA1 checksums
          310  +  *  Update to the latest SQLite version 3.7.8 alpha.
          311  +  *  Fix the tarball generator to work with very log pathnames
          312  +
          313  +<h2>Changes For Version 1.18 (2011-07-14)</h2>
          314  +
          315  +  *  Added this Change Log
          316  +  *  Added sequential version numbering
          317  +  *  Added a optional configure script - the Makefile still works for most
          318  +     systems.
          319  +  *  Improvements to the "annotate" algorithm: only search primary
          320  +     ancestors and ignore branches.
          321  +  *  Update the "scrub" command to remove traces of login-groups and
          322  +     subrepositories.
          323  +  *  Added the --type option to the "fossil tag find" command.
          324  +  *  In contexts where only a check-in makes sense, resolve branch and
          325  +     tag names to checkins only, never events or other artifacts.
          326  +  *  Improved display of file renames on a diff.  A rebuild is required
          327  +     to take full advantage of this change.
          328  +  *  Update the built-in SQLite to version 3.7.7.

Added www/checkin.wiki.

            1  +<title>Check-in Checklist</title>
            2  +
            3  +<h2><u>Always</u> run the following checklist prior to <u>every</u>
            4  +check-in or commit to the Fossil repository:</h2>
            5  +
            6  +Before every check-in:
            7  +
            8  +  0.   <b>fossil user default</b> → your username is correct.
            9  +
           10  +  1.   <b>fossil diff</b> →
           11  +       <ol type="a">
           12  +       <li> No stray changes
           13  +       <li> All changes comply with the license
           14  +       <li> All inputs are scrubbed before use
           15  +       <li> No injection attacks via %s formats
           16  +       </ol>
           17  +
           18  +  2.   <b>fossil extra</b> → no unmanaged files need to be added.
           19  +
           20  +  3.   The check-in will go onto the desired branch.
           21  +       →  Check-ins to trunk normally require approval from
           22  +       the lead programmer (drh).
           23  +
           24  +  4.   auto-sync is on, or the system clock is verified
           25  +
           26  +  5.   If sources files have been added or removed, ensure all makefiles
           27  +       and configure scripts have been updated accordingly.
           28  +
           29  +Before every check-in to <b>trunk</b>:
           30  +
           31  +  6.   No compiler warnings on the development machine.
           32  +
           33  +  7.   The fossil executable that results from a build actually works.
           34  +
           35  +
           36  +<hr>
           37  +<h2>Commentary</h2>
           38  +
           39  +Before you go ahead and push content back to the servers, make sure
           40  +that the username you are using by default matches your username
           41  +within the project. Also remember to enable the localauth setting
           42  +if you intend to make changes via a locally served web UI.
           43  +
           44  +Item 1 is the most important step.  Consider using <b>gdiff</b>
           45  +instead of <b>diff</b> if you have a graphical differ configured.  Or
           46  +use the command-line option <b>--tk</b>.  Also consider the <b>-N</b>
           47  +command-line option to show the complete text newly added files.
           48  +The recommended command for completing checklist item 1 is:
           49  +
           50  +   <b>fossil diff --tk -N</b>
           51  +
           52  +Look carefully at every changed line in item 1.
           53  +Make sure that you are not about to commit unrelated changes.
           54  +If there are two or more unrelated changes present, consider
           55  +breaking up the commit into two or more separate commits.
           56  +Always make 100% sure that all changes are compatible with the
           57  +BSD license, that you have the authority to commit the code in accordance
           58  +with the [/doc/trunk/www/copyright-release.html | CLA] that you have
           59  +signed and have on file, and that
           60  +no NDAs, copyrights, patents, or trademarks are infringed by the check-in.
           61  +Also check very carefully to make sure that
           62  +you are not introducing security vulnerabilities.  Pay particular attention
           63  +to the possibility of SQL or HTML injection attacks.
           64  +
           65  +Item 2 verifies that you have not added source files but failed to
           66  +do the necessary "<b>fossil add</b>" to manage them.  If you commit
           67  +without bringing the new file under source control, the check-in will
           68  +be broken.  That, in turn, can cause complications far in the future
           69  +when we are bisecting for a bug.
           70  +
           71  +For item 3, Run "<b>fossil status</b>" or the equivalent to
           72  +make sure your changes are going into the branch you think they are.
           73  +Also double-check the branch name when entering change comments.
           74  +Never check into trunk unless you are expressly authorized to do so.
           75  +
           76  +For Item 4, if you are on-network, make sure auto-sync is enabled.  This
           77  +will minimize the risk of an unintended fork.  It will also give you a
           78  +warning if you system clock is set incorrectly.  If you are off-network,
           79  +make sure that your system clock is correct or at least close to correct
           80  +so that your check-in does not appear out-of-sequence on timelines.
           81  +On-network commits are preferred, especially for trunk commits.
           82  +
           83  +Items 6 and 7 help to ensure that check-ins on the trunk always work.
           84  +Knowing that the trunk always works makes bisecting much easier.  Items
           85  +6 and 7 are recommended for all check-ins, even those that are on a branch.
           86  +But they are especially important for trunk check-ins.  We desire that
           87  +all trunk check-ins work at all times.  Any experimental, unstable, or
           88  +questionable changes should go on a branch and be merged into trunk after
           89  +further testing.

Added www/checkin_names.wiki.

            1  +<title>Check-in Names</title>
            2  +
            3  +<table align="right" border="1" width="33%" cellpadding="10">
            4  +<tr><td>
            5  +<h3>Executive Summary</h3>
            6  +<p>A check-in can be identified using any of the following
            7  +names:
            8  +<ul>
            9  +<li> SHA1 hash prefix
           10  +<li> Tag or branchname
           11  +<li> Timestamp:  <i>YYYY-MM-DD HH:MM:SS</i>
           12  +<li> <i>tag-name</i> <big><b>:</b></big> <i>timestamp</i>
           13  +<li> <b>root :</b> <i>branchname</i>
           14  +<li> Special names:
           15  +<ul>
           16  +<li> <b>tip</b>
           17  +<li> <b>current</b>
           18  +<li> <b>next</b>
           19  +<li> <b>previous</b> or <b>prev</b>
           20  +</ul>
           21  +</ul>
           22  +</td></tr>
           23  +</table>
           24  +Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept
           25  +check-in names as an argument.  For example, the "[/help/info|info]" command
           26  +accepts an optional check-in name to identify the specific checkout
           27  +about which information is desired:
           28  +
           29  +<blockquote>
           30  +<tt>fossil info</tt> <i>checkin-name</i>
           31  +</blockquote>
           32  +
           33  +You are perhaps reading this page from the following URL:
           34  +
           35  +<blockquote>
           36  +http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/checkin_names.wiki
           37  +</blockquote>
           38  +
           39  +The URL above is an example of an [./embeddeddoc.wiki | embedded documentation]
           40  +page in Fossil.  The bold term of the pathname is a check-in name that
           41  +determines which version of the documentation to display.
           42  +
           43  +Fossil provides a variety of ways to specify a check-in.  This
           44  +document describes the various methods.
           45  +
           46  +<h2>Canonical Check-in Name</h2>
           47  +
           48  +The canonical name of a checkin is the SHA1 hash of its
           49  +[./fileformat.wiki#manifest | manifest] expressed as a 40-character
           50  +lowercase hexadecimal number.  For example:
           51  +
           52  +<blockquote><pre>
           53  +fossil info e5a734a19a9826973e1d073b49dc2a16aa2308f9
           54  +</pre></blockquote>
           55  +
           56  +The full 40-character SHA1 hash is unwieldy to remember and type, though,
           57  +so Fossil also accepts a unique prefix of the hash, using any combination
           58  +of upper and lower case letters, as long as the prefix is at least 4
           59  +characters long.  Hence the following commands all 
           60  +accomplish the same thing as the above:
           61  +
           62  +<blockquote><pre>
           63  +fossil info e5a734a19a9
           64  +fossil info E5a734A
           65  +fossil info e5a7
           66  +</blockquote>
           67  +
           68  +Many web-interface screens identify check-ins by 10- or 16-character 
           69  +prefix of canonical name.
           70  +
           71  +<h2>Tags And Branch Names</h2>
           72  +
           73  +Using a tag or branch name where a check-in name is expected causes
           74  +Fossil to choose the most recent check-in with that tag or branch name.
           75  +So, for example, as of this writing the most recent check-in that
           76  +is tagged with "release" is [d0753799e44].
           77  +So the command:
           78  +
           79  +<blockquote><pre>
           80  +fossil info release
           81  +</pre></blockquote>
           82  +
           83  +Results in the following input:
           84  +
           85  +<blockquote><pre>
           86  +uuid:         d0753799e447b795933e9f266233767d84aa1d84 2010-11-01 14:23:35 UTC
           87  +parent:       4e1241f3236236187ad2a8f205323c05b98c9895 2010-10-31 21:51:11 UTC
           88  +child:        4a094f46ade70bd9d1e4ffa48cbe94b4d3750aef 2010-11-01 18:52:37 UTC
           89  +child:        f4033ec09ee6bb2a73fa588c217527a1f311bd27 2010-11-01 23:38:34 UTC
           90  +tags:         trunk, release
           91  +comment:      Fix a typo in the file format documentation reported on the
           92  +              Tcl/Tk chatroom. (user: drh)
           93  +</pre></blockquote>
           94  +
           95  +There are multiple check-ins that are tagged with "release" but
           96  +(as of this writing) the [d0753799e44]
           97  +check-in is the most recent so it is the one that is selected.
           98  +
           99  +Note that unlike other command DVCSes, a "branch" in Fossil
          100  +is not anything special; it is simply a sequence of check-ins that
          101  +share a common tag.  So the same mechanism that resolves tag names
          102  +also resolves branch names.
          103  +
          104  +Note also that there can (in theory) be an ambiguity between tag names
          105  +and canonical names.  Suppose, for example, you had a check-in with
          106  +the canonical name deed28aa99a835f01fa06d5b4a41ecc2121bf419 and you
          107  +also happened to have tagged a different check-in with "deed2".  If
          108  +you use the "deed2" name, does it choose the canonical name or the tag
          109  +name?  In such cases, you can prefix the tag name with "tag:".
          110  +For example:
          111  +
          112  +<blockquote><tt>
          113  +fossil info tag:deed2
          114  +</tt></blockquote>
          115  +
          116  +The "tag:deed2" name will refer to the most recent check-in 
          117  +tagged with "deed2" not to the
          118  +check-in whose canonical name begins with "deed2".
          119  +
          120  +<h2>Timestamps</h2>
          121  +
          122  +A timestamp in one of the formats shown below means the most recent
          123  +check-in that occurs no later than the timestamp given:
          124  +
          125  +  *   <i>YYYY-MM-DD</i>
          126  +  *   <i>YYYY-MM-DD HH:MM</i>
          127  +  *   <i>YYYY-MM-DD HH:MM:SS</i>
          128  +  *   <i>YYYY-MM-DD HH:MM:SS.SSS</i>
          129  +
          130  +The space between the day and the year can optionally be 
          131  +replaced by an uppercase <b>T</b> and the entire timestamp can
          132  +optionally be followed by "<b>z</b>" or "<b>Z</b>".  In the fourth
          133  +form with fractional seconds, any number of digits may follow the
          134  +decimal point, though due to precision limits only the first three
          135  +digits will be significant.
          136  +
          137  +In its default configuration, Fossil interprets and displays all dates
          138  +in Universal Coordinated Time (UTC).  This tends to work the best for
          139  +distributed projects where participants are scattered around the globe.
          140  +But there is an option on the Admin/Timeline page of the web-interface to
          141  +switch to local time.  The "<b>Z</b>" suffix on an timestamp check-in
          142  +name is meaningless if Fossil is in the default mode of using UTC for
          143  +everything, but if Fossil has been switched to localtime mode, then the
          144  +"<b>Z</b>" suffix means to interpret that particular timestamp using 
          145  +UTC instead localtime.
          146  +
          147  +For an example of how timestamps are useful, 
          148  +consider the homepage for the Fossil website itself:
          149  +
          150  +<blockquote>
          151  +http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki
          152  +</blockquote>
          153  +
          154  +The bold component of that URL is a check-in name.  To see what the
          155  +Fossil website looked like on January 1, 2009, one has merely to change
          156  +the URL to the following:
          157  +
          158  +<blockquote>
          159  +http://www.fossil-scm.org/fossil/doc/<b>2009-01-01</b>/www/index.wiki
          160  +</blockquote>
          161  +
          162  +<h2>Tag And Timestamp</h2>
          163  +
          164  +A check-in name can also take the form of a tag or branch name followed by
          165  +a colon and then a timestamp.  The combination means to take the most
          166  +recent check-in with the given tag or branch which is not more recent than
          167  +the timestamp.  So, for example:
          168  +
          169  +<blockquote>
          170  +fossil update trunk:2010-07-01T14:30
          171  +</blockquote>
          172  +
          173  +Would cause Fossil to update the working check-out to be the most recent
          174  +check-in on the trunk that is not more recent that 14:30 (UTC) on 
          175  +July 1, 2010.
          176  +
          177  +<h2>Root Of A Branch</h2>
          178  +
          179  +A branch name that begins with the "<tt>root:</tt>" prefix refers to the
          180  +last check-in in the parent branch prior to the beginning of the branch.
          181  +Such a label is useful, for example, in computing all diffs for a single
          182  +branch.  The following example will show all changes in the hypothetical
          183  +branch "xyzzy":
          184  +
          185  +<blockquote>
          186  +fossil diff --from root:xyzzy --to xyzzy
          187  +</blockquote>
          188  +
          189  +
          190  +<h2>Special Tags</h2>
          191  +
          192  +The tag "tip" means the most recent check-in.  The "tip" tag is roughly
          193  +equivalent to the timestamp tag "5000-01-01".
          194  +
          195  +If the command is being run from a working check-out (not against a bare
          196  +repository) then a few extra tags apply.  The "current" tag means the
          197  +current check-out.  The "next" tag means the youngest child of the
          198  +current check-out.  And the "previous" or "prev" tag means the primary
          199  +(non-merge) parent of the current check-out.
          200  +
          201  +<h2>Additional Examples</h2>
          202  +
          203  +To view the changes in the most recent check-in prior to the version currently
          204  +checked out:
          205  +
          206  +<blockquote><pre>
          207  +fossil diff --from previous --to current
          208  +</pre></blockquote>
          209  +
          210  +Suppose you are of the habit of tagging each release with a "release" tag.
          211  +Then to see everything that has changed on the trunk since the last release:
          212  +
          213  +<blockquote><pre>
          214  +fossil diff --from release --to trunk
          215  +</pre></blockquote>

Deleted www/cmd_add.wiki.

     1         -<h2>add</h2>
     2         -
     3         -The often used <code>add</code> command is how you tell <b>fossil</b> to
     4         -include a (usually new) file in the repository.
     5         -
     6         -<b>fossil</b> is designed to manage artifacts whose role is being
     7         -"source" for something, most probably software program code or other
     8         -text.  One can imagine all kinds of ways to let fossil know just what
     9         -constitutes a source; the simplest and most direct way it
    10         -<i>actually</i> finds out is when you give it the
    11         -<code> fossil add <i>path</i> </code> command.
    12         -
    13         -It's reasonable to think of
    14         -the [./cmd_import.wiki | <code>import</code>]
    15         -and [./cmd_clone.wiki | <code>clone</code>]
    16         -commands as very high-powered versions of the <code>add</code>
    17         -command that are combined with system level file movement and
    18         -networking functions.  Not particularly accurate, but reasonable.
    19         -
    20         -Typing <code>&nbsp;fossil add myfile</code> causes fossil to put
    21         -<i>myfile</i> into the repository at the next
    22         -<code>commit</code>&#8212;provided you issue it from within the source
    23         -tree, of course.
    24         -
    25         -By contrast, <code>&nbsp;fossil add mydirectory</code> will add
    26         -<em><strong>all</strong></em> of the files in <i>mydirectory</i>, and
    27         -all of its sub-directories.  In other words, adding a directory will
    28         -recursively add all of the directory's file system descendants to the
    29         -repository.  This was an oft-requested feature, recently implemented.
    30         -It is very flexible.  Only when you add a directory do you get the
    31         -recursive behavior.  If you are globbing a subset of files, you won't
    32         -get the recursion.
    33         -
    34         -Realize that the repository is <u>not</u> changed by the
    35         -<code>add</code> command, but by the <code>&nbsp;commit</code> command.
    36         -<code>add</code> <i>myfile</i> tells <b>fossil</b> to "mark"
    37         -<i>myfile</i> as part of the repository.  Only commands which actually
    38         -manipulate the content of the repository can physically put source
    39         -artifacts into (or remove them from) the repository.
    40         -
    41         -Just to keep things symmetric, there are also commands that can
    42         -manipulate the repository without affecting the checked-out sources
    43         -(see [./cmd_pull.wiki | fossil pull], for instance.)
    44         -
    45         -It's worthwhile reiterating that <b>fossil</b> is storing the content
    46         -of source artifacts and the names of the artifacts in their "native
    47         -habitat", a sequence of "temporal slices" (aka "versions") of the
    48         -state of the whole system, and a set of unique identifiers.  When you
    49         -add a file to a repository, the <i>path</i> to the file is a part of
    50         -the <i>name</i> of the file.  There is a mis-match between the file
    51         -system's idea of a directory (a file containing pointers to files) and
    52         -fossil's idea (a substring of the name of the artifact.)  The names of
    53         -the artifacts specify their relative locations because of the way the
    54         -file system interprets them.  If you don't keep this in mind, you may
    55         -fool yourself into thinking <b>fossil</b> somehow "stores
    56         -directories."  It doesn't, and believing it does will eventually
    57         -confuse you.
    58         -
    59         -See also: [./cmd_rm.wiki | fossil rm],
    60         -[./cmd_import.wiki | fossil import],
    61         -[./cmd_clone.wiki | fossil clone],
    62         -[./cmd_commit.wiki | fossil commit],
    63         -[./cmd_pull.wiki | fossil pull],
    64         -[./cmd_settings.wiki | fossil setting] (async),
    65         -[./reference.wiki | Reference]

Deleted www/cmd_all.wiki.

     1         -<h2>all</h2>
     2         -
     3         -The <code>all</code> command will let you perform (some) commands on
     4         -<em>all</em> of your repositories, and provides a way of finding all
     5         -of your repositories as well.
     6         -
     7         -There are some commands you might especially want to perform on every
     8         -repository you've got, once in a while.  <code> fossil&nbsp;all
     9         -</code> includes four of the most likely as sub-commands:
    10         -[./cmd_pull.wiki | <code>pull</code>],
    11         -[./cmd_push.wiki | <code>push</code>],
    12         -[./cmd_rebuild.wiki | <code>rebuild</code>] and
    13         -[./cmd_sync.wiki | <code>sync</code>].
    14         -
    15         -Follow the links to find out what each of those do, and then a moment
    16         -of thought will tell you why you might want to have them available for
    17         -all repositories.
    18         -
    19         -Certainly you'll want your repositories all rebuilt when you upgrade
    20         -<b>fossil</b> after there has been a change in the repository
    21         -structure.  For the others, it depends.  Usually you would want
    22         -across-the-board versions if you've been "off Net" for a while, and
    23         -have commits to multiple repositories than you need to share, or want
    24         -to get the repository changes that have been made by others, or both.
    25         -
    26         -The last sub-command provided by <code>all</code> is "<code>list</code>."
    27         -
    28         -While the other sub-commands give you a way to conveniently take care
    29         -of all of your repositories for some common tasks, the
    30         -<code>list</code> provides a way to take care of any subset of your
    31         -repositories in any way you want.  It provides a list of all of your
    32         -repositories' locations.  <code>fossil&nbsp;all&nbsp;list</code>
    33         -outputs a one-per-line listing of the path for each of your
    34         -repositories.  With that in hand, you can easily script just about any
    35         -repository manipulations you want.
    36         -
    37         -Or, you could just jog your memory.
    38         -
    39         -The <code>all</code> command uses the <i>.fossil</i> file in the home
    40         -directory to find all of your repositories, so you can mess it up by
    41         -moving your repositories around.  This is easy to do inadvertently if
    42         -you have a cavalier attitude about repos, but you'll know pretty
    43         -quickly that you've done it&#8212;many commands you try to use from
    44         -inside of a checkout won't work correctly.  The <i>.fossil</i> file is
    45         -an <b>sqlite</b> db file which fossil uses to keeping track of
    46         -repository locations. Advice: if you move your repositories around,
    47         -let fossil know you did;
    48         -[./cmd_close.wiki | <code>close</code>] them before you move
    49         -them, and then [./cmd_open.wiki | <code>open</code>] them from
    50         -their new locations.
    51         -
    52         -See also: [./cmd_pull.wiki | fossil pull],
    53         -[./cmd_push.wiki | fossil push],
    54         -[./cmd_rebuild.wiki | fossil rebuild],
    55         -[./cmd_sync.wiki | fossil sync],
    56         -[./cmd_open.wiki | fossil open],
    57         -[./cmd_close.wiki | fossil close],
    58         -[./reference.wiki | Reference],
    59         -[http://www.sqlite.org | <b>SQLite</b>]

Deleted www/cmd_cgi.wiki.

     1         -<h2>cgi</h2>
     2         -
     3         -<code>cgi</code> is the command that tells fossil it is running as a
     4         -web-page supplier for an external http server.  (For you web-miesters,
     5         -the "cgi" is actually unnecessary if your web environment is set up in
     6         -a normal fashion.)
     7         -
     8         -This is the command you will probably use if you want to make a
     9         -moderate-to-high hit rate public repository (like the <b>fossil</b>
    10         -project's self-hosted repository) but you'll be using it in the
    11         -shebang line.
    12         -
    13         -If you need lower level access to the pages <b>fossil</b> generates,
    14         -you'll want to look at the [./cmd_http.wiki | <code>http</code>]
    15         -command.
    16         -
    17         -See also: [./cmd_http.wiki | fossil http],
    18         -[./concepts.wiki#saserv | <i>Concepts (setting up a server)</i>],
    19         -[./reference.wiki | Reference]

Deleted www/cmd_changes.wiki.

     1         -<h2>changes</h2>
     2         -
     3         -The <code>changes</code> command is informational, it doesn't do
     4         -anything to a checked-out project, but it tells you something about
     5         -it.
     6         -
     7         -This is simply a quick way to get a list of the files which are
     8         -different in the source tree (the checkout) and the repository.
     9         -
    10         -There is a bit more information (was a file edited, added or
    11         -removed?, for instance).
    12         -
    13         -The same information will be displayed if you
    14         -[./cmd_status.wiki | <code>&nbsp;fossil&nbsp;status&nbsp;</code>],
    15         -except there will be some additional repository information displayed
    16         -first.
    17         -
    18         -See also: [./cmd_status.wiki | fossil status],
    19         -[./reference.wiki | Reference]

Deleted www/cmd_checkout.wiki.

     1         -<h2>checkout</h2>
     2         -
     3         -The <code>checkout</code> command is how a project version goes from
     4         -the repository to the chosen project directory.
     5         -
     6         -Without going into detail about getting/opening a repository, once you
     7         -have a repository and a place in which the repository has been
     8         -opened, you can "check out" a "version" of the files which make up the
     9         -repository at somewhen.
    10         -
    11         -The term "checkout" is traditional in source management systems, but a
    12         -bit of an anachronism in a distributed system like <b>fossil</b>.
    13         -"Checking out" a version of a project means getting all of the source
    14         -artifacts out into the standard environment---currently the
    15         -shell/file-system.
    16         -
    17         -Traditionally, the version is some "incrementing" code like
    18         -v1.3.2rcQuink or f451 or something.  In distributed SCM systems it's
    19         -some absolutely unique identifier, usually the result of a one-way
    20         -hash (SHA1, in fossil's case.)  The <b>fossil</b> term for these is
    21         -<em>artifact IDs</em>.
    22         -
    23         -<code>fossil&nbsp;checkout&nbsp;</code> <i>id</i> will check out the
    24         -version corresponding to <i>id</i> into the source tree.
    25         -
    26         -<code>checkout</code> requires you to pick a precise version to put into
    27         -the "on-disk" source tree, and leaves any edited files which are already
    28         -in the tree intact.
    29         -
    30         -<code>update</code>, on the other hand, <em>merges</em> edits into the
    31         -version you choose (if you choose one; you can default the version.)
    32         -
    33         -Since a version is required, and <b>fossil</b>'s artifact IDs are
    34         -fairly long, there are two good ways to refer to the version.  You can
    35         -use a unique proper prefix of the version (six or eight characters is
    36         -more than enough in most cases) <em>or</em> you can [./cmd_tag.wiki |
    37         -tag] your check-ins and use the tags for checkouts, reverting,
    38         -branching (tags are the best way to branch) and so forth.  Both
    39         -methods work throughout fossil.
    40         -
    41         -See also [./cmd_tag.wiki | fossil tag],
    42         -[./cmd_revert.wiki | fossil revert],
    43         -[./cmd_update.wiki | fossil update],
    44         -[./cmd_push.wiki | fossil push],
    45         -[./cmd_pull.wiki | fossil pull],
    46         -[./cmd_clone.wiki | fossil clone],
    47         -[./cmd_open.wiki | fossil open],
    48         -[./cmd_close.wiki | fossil close],
    49         -[./cmd_new.wiki | fossil new],
    50         -[./reference.wiki | Reference]

Deleted www/cmd_extra.wiki.

     1         -<h2>extra</h2>
     2         -
     3         -The <code>extra</code> command is informational, it doesn't do anything to
     4         -a checked-out project, but it tells you something about it.
     5         -
     6         -Extra files are files that exist in a checked-out project, but don't belong to
     7         -the repository.
     8         -
     9         -The <code>fossil extra</code> command will get you a list of these files.
    10         -
    11         -This is convenient for figuring out if you've <code>
    12         -[./cmd_add.wiki | add]</code>ed every file that needs to be
    13         -
    14         -in the repository before you do a commit.  It will also tell you what
    15         -will be removed if you [./cmd_clean.wiki | <code>clean</code>]
    16         -the project.
    17         -
    18         -Suppose, for example, you have a "noodle.src" file as a scratch pad
    19         -for source code, and you don't want to include your latest
    20         -hare-brained ideas in the repository?  You don't <code>add</code> it
    21         -to the repository, of course&#8212;though there are ways you might add
    22         -it unintentionally.  If your project is big, and you want to find
    23         -noodle.src, and anything else that isn't under source control within
    24         -the project directories, then<code> fossil&nbsp;extra </code> will
    25         -give you a list.
    26         -
    27         -If you don't think this is all that useful, then you've never had to write
    28         -a shell script that only affects project files and leaves everything
    29         -else alone.  ;)
    30         -
    31         -The <code>extra</code> command is almost, but not quite entirely, the exact
    32         -opposite of the [./cmd_ls.wiki | <code>ls</code>] command.
    33         -
    34         -See also: [./cmd_status.wiki | fossil status],
    35         -[./cmd_ls.wiki | fossil ls],
    36         -[./cmd_changes.wiki | fossil changes],
    37         -[./cmd_clean.wiki | fossil clean],
    38         -[./reference.wiki | Reference]

Deleted www/cmd_ls.wiki.

     1         -<h2>ls</h2>
     2         -
     3         -The <code>ls</code><a href="#notes">*</a> command is informational, it doesn't do anything to
     4         -a checked-out project, but it tells you something about it.
     5         -
     6         -A project consists of a "source tree" of "artifacts" (see [./concepts.wiki | Fossil concepts].)
     7         -From a practical standpoint this is a set of files and directories rooted
     8         -at a main project directory.  The files that are under source control aren't
     9         -particularly distinguishable from those that aren't.  The <code>ls</code> and
    10         -<code>extra</code> commands provide this information.
    11         -
    12         -<code>fossil ls</code> produces a listing of the files which are under source
    13         -control <i>and</i> their status within the repository.  The output is a simple
    14         -list of STATUS/filepath pairs on separate lines.  The status of a file will
    15         -likely be one of ADDED, UNCHANGED, UPDATED, or DELETED. <a href="#notes">*</a>
    16         -
    17         -It's important to realize that this is the status <i>relative to the repository</i>,
    18         -it's the status as <b>fossil</b> sees it and has nothing to do with
    19         -filesystem status.  If you're new to source-management/version-control
    20         -systems, you'll probably get bit by this concept-bug at least once.
    21         -
    22         -To really see the difference, issue an <code>ls</code> before and after doing
    23         -a [./cmd_commit.wiki | <code>commit</code>].  Before, the status of files may be any of the three,
    24         -but after <code>commit</code>ting changes the status will be UNCHANGED "across
    25         -the board."
    26         -
    27         -By way of example, here's what I see if I <code>fossil ls</code> in the
    28         -directory where I have checked out my testing repository:
    29         -<nowiki><pre>
    30         -    $ fossil ls
    31         -    ADDED     feegboing
    32         -    UNCHANGED fossil_docs.txt
    33         -    DELETED   nibcrod
    34         -</pre></nowiki>
    35         -But if I do a simple ls, what I get is
    36         -<nowiki><pre>
    37         -    $ ls
    38         -    feegboing  fossil_docs.txt  manifest.uuid  noodle.txt
    39         -    _FOSSIL_   manifest         nibcrod
    40         -</pre></nowiki>
    41         -
    42         -The <code>ls</code> command is almost, but not quite entirely, the exact
    43         -opposite of the
    44         -[./cmd_extra.wiki | <code>extra</code> command].
    45         -
    46         -<a name="notes">Notes:</a>
    47         -  *  If you come from the <b>Windows</b> world, it will help to know that 'ls' is the usual <b>unix</b> command for listing a directory.
    48         -  *  There are more states for a file to be in than those listed, including MISSING, EDITED, RENAMED and a couple of others.
    49         -
    50         -See also: [./cmd_add.wiki | fossil add],
    51         -[./rm.wiki | fossil rm],
    52         -[./cmd_extra.wiki | fossil extra],
    53         -[./cmd_commit.wiki | fossil commit],
    54         -[./concepts.wiki | Fossil concepts],
    55         -[./reference.wiki | Reference]

Deleted www/cmd_mv.wiki.

     1         -<h2>mv | rename</h2>
     2         -
     3         -The <code>mv</code> (alias "<code>rename</code>") command tells
     4         -<b>fossil</b> that a file has gone from one external name to another
     5         -without changing content.
     6         -
     7         -You could do this by renaming the file in the file system,
     8         -[./cmd_rm.wiki | deleting] the old name from the project, and
     9         -[./cmd_add.wiki | adding] the new name.  But you would lose the
    10         -continuity of the <u>content's</u> history that way.  Using
    11         -<code>mv</code> makes the name change a part of the history
    12         -maintained by <b>fossil</b>.  You will, of course, need a good
    13         -comment somewhere (say, the commit comment) if you want to
    14         -remember <em>why</em> you changed the name...  <b>fossil</b>
    15         -only maintains history, it doesn't (yet) explain it.
    16         -
    17         -<code>mv</code> is much like the [./cmd_rm.wiki | <code>rm</code>]
    18         -command, in that it manipulates <b>fossil</b>'s "idea" of what is
    19         -part of the project.  The difference is that <code>mv</code> assumes
    20         -you have actually made some change to the file system.
    21         -
    22         -See also: [./cmd_rm.wiki | fossil rm],
    23         -[./cmd_add.wiki | fossil add],
    24         -[./reference.wiki | Reference]

Deleted www/cmd_new.wiki.

     1         -<h2>new</h2>
     2         -
     3         -The <code>new</code> command allows you to create a brand new
     4         -repository.
     5         -
     6         -Pragmatically, this means that an SQLite database is created with
     7         -whatever name you specified, and set up with the appropriate tables
     8         -and initial data.
     9         -
    10         -There's not much to <code>new</code>, it's what happens afterward that
    11         -gets a project going:
    12         -
    13         -  Once you have a new repository file, you need to create and cd to a
    14         -  directory in which you will store your files, or move into an
    15         -  existing directory which contains the files for a project.
    16         -
    17         -  Then, you need to [./cmd_open.wiki | <code>open</code>] the new
    18         -  repository, and get the server running so you can set up the project
    19         -  name and so forth.
    20         -
    21         -  Finally, you'll [./cmd_add.wiki | <code>add</code>] files to it.  If
    22         -  you are adding exisiting files, you can add them individually, via
    23         -  globbing from the shell, or by adding the directory (which will add
    24         -  all of the directory's file-system descendants recursively.)
    25         -
    26         -But you can't do all that until you create a repository file with
    27         -<code>new</code>.
    28         -
    29         -See also:
    30         -[./cmd_open.wiki | fossil open],
    31         -[./cmd_add.wiki | fossil add],
    32         -[./cmd_server.wiki | fossil ui],
    33         -[./reference.wiki | Reference]

Deleted www/cmd_rm.wiki.

     1         -<h2>del | rm</h2>
     2         -
     3         -The <code>del</code> (alias <code>rm</code>) command takes a "file"
     4         -<em>out</em> of a project.
     5         -
     6         -It does <u>not</u> delete the file from the repository, it does
     7         -<u>not</u> remove the file from the file system on disk.  It tells
     8         -<b>fossil</b> that the file is no longer a part of the project for
     9         -which <b>fossil</b> is maintaining the sources.
    10         -
    11         -For example, if you have a nice, clean source tree and use the
    12         -[./cmd_extra.wiki | <code>extra</code>] command on it, you won't
    13         -get any output.  If you then <code>rm</code> some file and commit
    14         -the change, that file will be listed by the <code>extra</code>
    15         -command.
    16         -
    17         -The file is still on the disk, and it is still in the repository.
    18         -<strong>But</strong> the file <em>is not part of the project</em>
    19         -anymore.  Further changes to the file will not be checked in unless
    20         -you [./cmd_add.wiki | <code>add</code>] the file again.
    21         -
    22         -It can initially be confusing to see a file that's been "deleted"
    23         -still showing up in the files list in the repository, but remember
    24         -that the files list currently<a href="#vnote"><sup>*</sup></a> shows
    25         -all of the files that have ever been in the repository <em>because
    26         -<b>fossil</b> is a source control system and therefore keeps a record
    27         -of the history of a project.</em>
    28         -
    29         -To get a list of the files <em>only in the current version</em> of the
    30         -project, use the [./cmd_ls.wiki | <code>ls</code>] command.
    31         -
    32         -The <code>del</code> command is the logical opposite of the
    33         -[./cmd_add.wiki | <code>add</code>] command, in its single-file-add
    34         -form.
    35         -
    36         -<font size="-1"><a name="vnote"><sup>*</sup></a>version 7c281b629a on 20081220</font>
    37         -
    38         -See also: [./cmd_add.wiki | fossil add],
    39         -[./cmd_ls.wiki | fossil ls],
    40         -[./reference.wiki | Reference]

Deleted www/cmd_status.wiki.

     1         -<h2>status</h2>
     2         -
     3         -The <code>status</code> command is informational, it doesn't do anything to
     4         -a checked-out project, but it tells you something about it.
     5         -
     6         -Running <code>&nbsp;fossil&nbsp;status&nbsp;</code> currently prefixes
     7         -the output of the <code>[./cmd_changes.wiki | changes]</code> command
     8         -with information about the repository and checkout.  The information
     9         -is in the form of the [./concepts.wiki#aidex | Artifact ID]s of the
    10         -server code, the checkout, and the parent (of, I <em>think</em> the
    11         -checkout.)
    12         -
    13         -This is useful for getting an at-a-glance view of the state of your
    14         -project, especially in a situation where you need the artifact IDs.
    15         -
    16         -Here is what I get when I issue a <code>status</code> on my local
    17         -version of the <b>fossil</b> repository as I write this:
    18         -
    19         -<nowiki><pre>
    20         -   $ fossil status
    21         -   repository:   /home/me/myclone.fossil
    22         -   local-root:   /home/me/fossil/
    23         -   server-code:  99d6c9cf3f262720579db177503812814d712fc7
    24         -   checkout:     a8c3a7ea9249281e0a1fb55fb31d2ad57844f848
    25         -   parent:       21cecd209f7201f17e8a784c0d8f735603d440ae
    26         -   EDITED   www/cmd_.wiki-template
    27         -   EDITED   www/cmd_add.wiki
    28         -   EDITED   www/cmd_all.wiki
    29         -   EDITED   www/cmd_extra.wiki
    30         -   EDITED   www/cmd_ls.wiki
    31         -   EDITED   www/cmd_update.wiki
    32         -   EDITED   www/index.wiki
    33         -   $
    34         -</pre></nowiki>
    35         -
    36         -Once I actually make changes to the repository (say, a
    37         -[./cmd_commit.wiki | commit]) most of that will change&#8212;all
    38         -of those files showing as "EDITED" will be checked in and won't
    39         -show up, and the artifact IDs will reflect the new state of the
    40         -repository.
    41         -
    42         -If the only thing you want to see is which files in the checked-out
    43         -source tree have changed in some way, use the
    44         -[./cmd_changes.wiki | <code>changes</code>] command.
    45         -
    46         -If what you want is the files in the checked-out source tree which are
    47         -<em>not</em> part of the project, use the
    48         -[./cmd_extra.wiki | <code>extra</code>] command.
    49         -
    50         -See also: [./cmd_changes.wiki | fossil changes],
    51         -[./cmd_extra.wiki | fossil extra],
    52         -[./concepts.wiki | <i>Fossil concepts</i>],
    53         -[./reference.wiki | Reference]

Deleted www/cmd_sync.wiki.

     1         -<h2>sync</h2>
     2         -
     3         -The <code>sync</code> command [./cmd_pull.wiki | <code>pull</code>]s and
     4         -[./cmd_push.wiki | <code>push</code>]es repository changes simultaneously.
     5         -
     6         -This applies to repositories available via a URL, of course.  If your
     7         -project is strictly local you can do all of the distributed stuff as
     8         -long as you are "serving" the repository via http in some fashion, but
     9         -it's probably pointless to do so.
    10         -
    11         -Assuming you aren't running <b>fossil</b> as a high-powered version of
    12         -[http://www.gnu.org/software/rcs | RCS], your use of <code>sync</code>
    13         -in your projects is up to you.  <b>fossil</b> defaults to using a
    14         -[./cmd_setting.wiki | setting] of <code>autosync</code>
    15         -If you have cloned a repository you will automatically sync with the
    16         -original if you [./cmd_commit.wiki | commit] changes to your local
    17         -version <em>unless</em> you customize your configuration.
    18         -
    19         -See also: [./cmd_pull.wiki | fossil pull],
    20         -[./cmd_push.wiki | fossil push],
    21         -[./cmd_setting.wiki | fossil setting],
    22         -[./reference.wiki | Reference]

Deleted www/cmd_update.wiki.

     1         -<h2>update</h2>
     2         -
     3         -What do you do if you have changes out on a repository and
     4         -you want them merged with your checkout?
     5         -
     6         -You use the <code>update</code> command.
     7         -
     8         -<b>fossil</b> can [./about_checkout.wiki | overwrite] any
     9         -changes you've made to your checkout, or it can
    10         -[./about_merge.wiki | merge] whatever changes have occurred
    11         -in the repo into your checkout.
    12         -
    13         -Update <em>merges</em> changes from the repository into your checkout.
    14         -
    15         -<b>fossil</b> uses a simple conflict resolution strategy for merges:
    16         -the latest change wins.
    17         -
    18         -Local intranet <code>[./cmd_commit.wiki | commit]</code>s
    19         -(by someone else)
    20         -or Net <code>[./cmd_pull.wiki | pull]</code>s from a server
    21         -will usually require a <code>fossil&nbsp;update</code> afterward.
    22         -
    23         -Local commits are likely to be made with
    24         -[./cmd_settings.wiki#autosync | automatic syncing]
    25         -set to "on", however, so if you don't use <b>fossil</b> for Net-wide
    26         -projects you may never have to use <code>update</code>.
    27         -
    28         -See also: [./cmd_pull.wiki | fossil pull],
    29         -[./cmd_commit.wiki | fossil commit],
    30         -[./cmd_settings.wiki#autosync | fossil setting] (autosync),
    31         -[./about_checkout.wiki | <i>checkouts</i>],
    32         -[./about_merge.wiki | <i>merging</i>],
    33         -[./reference.wiki | Reference]

Deleted www/cmd_version.wiki.

     1         -<h2>version</h2>
     2         -
     3         -The <code>version</code> command is informational, it doesn't do
     4         -anything to a checked-out project, but it tells you something about
     5         -it.
     6         -
     7         -Issuing the version command will print out the short-form of the
     8         -artifact ID for the fossil executable.
     9         -
    10         -See also: [./cmd_status.wiki | fossil status],
    11         -[./cmd_info.wiki | fossil info],
    12         -[./reference.wiki | Reference]

Added www/contribute.wiki.

            1  +<title>Contributing To Fossil</title>
            2  +
            3  +Users are encouraged to contributed enhancements back to the Fossil
            4  +project.  This note outlines some of the procedures for making
            5  +useful contributions.
            6  +
            7  +<h2>1.0 Contributor Agreement</h2>
            8  +
            9  +In order to accept your contributions, we <u>must</u> have a
           10  +[./copyright-release.pdf | Contributor Agreement (PDF)]
           11  +(or [./copyright-release.html | as HTML]) on file for you.  We require
           12  +this in order to maintain clear title to the Fossil code and prevent
           13  +the introduction of code with incompatible licenses or other entanglements
           14  +that might cause legal problems for Fossil users.  Many larger companies
           15  +and other lawyer-rich organizations require this as a precondition to using 
           16  +Fossil.
           17  +
           18  +If you do not wish to submit a Contributor Agreement, we would still
           19  +welcome your suggestions and example code, but we will not use your code
           20  +directly - we will be forced to reimplement your changes from scratch which
           21  +might take longer.
           22  +
           23  +<h2>2.0 Submitting Patches</h2>
           24  +
           25  +Suggested changes or bug fixes can be submitted by creating a patch
           26  +against the current source tree.  Email patches to 
           27  +<a href="mailto:drh@sqlite.org">drh@sqlite.org</a>.  Be sure to 
           28  +describe in detail what the patch does and which version of Fossil
           29  +it is written against.  
           30  +
           31  +A contributor agreement is not strictly necessary to submit a patch.
           32  +However, without a contributor agreement on file, your patch will be
           33  +used for reference only - it will not be applied to the code.  This
           34  +may delay acceptance of your patch.
           35  +
           36  +Your patches or changes might not be accepted even if you do have
           37  +a contributor agreement on file.  Please do not take this personally
           38  +or as an affront to your coding ability.  Sometimes patches are rejected
           39  +because they seem to be taking the project in a direction that the
           40  +architect does not want to go.  Or, there might be an alternative
           41  +implementation of the same feature being prepared separately.
           42  +
           43  +<h2>3.0 Check-in Privileges</h2>
           44  +
           45  +Check-in privileges are granted on a case-by-case basis.   Your chances
           46  +of getting check-in privileges are much improved if you have a history
           47  +of submitting quality patches and/or making thoughtful posts on the
           48  +[http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/ | mailing list].
           49  +A contributor agreement is, of course, a prerequisite for check-in
           50  +privileges.</p>
           51  +
           52  +Contributors are asked to make all non-trivial changes on a branch.  The
           53  +Fossil Architect (Richard Hipp) will merge changes onto the trunk.</p>
           54  +
           55  +Contributors are required to following the
           56  +[./checkin.wiki | pre-checkin checklist] prior to every checkin to
           57  +the Fossil self-hosting repository.  This checklist is short and succinct
           58  +and should only require a few seconds to follow.  Contributors 
           59  +should print out a copy of the pre-checkin checklist and keep
           60  +it on a notecard beside their workstations, for quick reference.
           61  +
           62  +Contributors should review the
           63  +[./style.wiki | Coding Style Guidelines] and mimic the coding style
           64  +used through the rest of the Fossil source code.  Your code should
           65  +blend in.  A third-party reader should be unable to distinguish your
           66  +code from any other code in the source corpus. 
           67  +
           68  +<h2>4.0 Testing</h2>
           69  +
           70  +Fossil has the beginnings of a 
           71  +[../test/release-checklist.wiki | release checklist] but this is an
           72  +area that needs further work.  (Your contributions here are welcomed!)
           73  +Contributors with check-in privileges are expected to run the release
           74  +checklist on any major changes they contribute, and if appropriate expand
           75  +the checklist and/or the automated test scripts to cover their additions.
           76  +
           77  +
           78  +<h2>5.0 See Also</h2>
           79  +
           80  +  *  [./build.wiki | How To Compile And Install Fossil]
           81  +  *  [./makefile.wiki | The Fossil Build Process]
           82  +  *  [./tech_overview.wiki | A Technical Overview of Fossil]

Changes to www/copyright-release.html.

     1      1   <h1 align="center">
     2         -Assignment of Copyright for<br>
     3         -Contributions To The Fossil SCM
            2  +Fossil SCM Contributor Agreement
     4      3   </h1>
     5      4   
     6      5   <p>
     7         -This Agreement is made between Hipp, Wyrick &amp; Company, Inc., a
     8         -Georgia corporation with headquarters at 6200 Maple Cove Lane, Charlotte, NC,
     9         -(hereafter "Hwaci")
    10         -and <font color="blue">NAME AND ADDRESS OF PROGRAMMER</font> (hereafter
    11         -"Programmer").
    12         -</p>
    13         -
    14         -<p>For valuable consideration, receipt and sufficiency of which are hereby
    15         -acknowledged, Programmer and Hwaci agree as follows:
    16         -</p>
            6  +This agreement applies to your contribution of material to the
            7  +Fossil Software Configuration Management System ("Fossil") that is
            8  +mananged by Hipp, Wyrick &amp; Company, Inc. ("Hwaci") and
            9  +sets out the intellectual property rights you grant to Hwaci in the
           10  +contributed material.
           11  +The terms "contribution" and "contributed material" mean any source code, 
           12  +object code, patch, tool, sample, graphic, specification, manual, 
           13  +documentation, or any other material posted, submitted, or uploaded by 
           14  +you to the Fossil project.
           15  + The term "you" means the person identified
           16  +and signing at the bottom of this document.  If your contribution
           17  +is on behalf of a company, the term "you" also means the company
           18  +identified in the signature area below.
    17     19   
    18     20   <ol>
    19     21   <li><p>
    20         -Programmer does hereby sell, assign, and transfer to Hwaci, its successors
    21         -and assigns, the entire right, title and interest in and to the copyright
    22         -in Fossil Software Configuration Management System (hereafter "Fossil")
    23         -or any portions thereof whether created before or after this agreement
    24         -and any registrations and copyright applications relating thereto and any
    25         -renewals and extensions thereof, and in and to all works based upon,
    26         -derived from, or incorporating Fossil, and in and to all income,
    27         -royalties, damages, claims and payments now or hereafter due or payable 
    28         -with respect thereto, and in and to all causes of action, either in law 
    29         -or in equity for past, present, or future infringement based on the
    30         -copyrights, and in and to all rights corresponding to the foregoing
    31         -throughout the world.
           22  +With respect to any worldwide copyrights, or copyright applications and
           23  +registrations, in your contribution:
           24  +<ul>
           25  +<li> You hereby assign to Hwaci joint ownership, and to the extent that
           26  +     such assignment is or becomes invalid, ineffective or unenforceable,
           27  +     you hereby grant to Hwaci a perpetual, irrevocable, non-exclusive,
           28  +     worldwide, no-charge, royalty-free, unrestricted license to exercise
           29  +     all rights under those copyrights, including the right to sublicense.
           30  +<li> You agree that both you and Hwaci can do all things in relation to your
           31  +     contribution as if each of us were the sole owners, and if one of us
           32  +     makes a derivative work of your contribution, the one who makes
           33  +     (or has made) the derivative work will be the sole owner of that
           34  +     derivative work.
           35  +<li> You agree that you will not assert any moral rights in your
           36  +     contribution against Hwaci, Hwaci's licensees or transferees, or
           37  +     any other user or consumer of your contribution.
           38  +<li> You agree that Hwaci may register a copyright in your contribution and 
           39  +     exercise all ownership rights associated with it.
           40  +<li> You agree that neither you nor Hwaci has any duty to consult with,
           41  +     obtain the consent of, or pay or render an accounting to the other
           42  +     for any use or distribution of your contribution.
           43  +</ul>
           44  +
           45  +<li><p>
           46  +With respect to any patents you own, or that you can license without payment
           47  +to any third party, and which are relevant to your contribution, you hereby
           48  +grant to Hwaci a perpetual, irrevocable, non-exclusive, worldwide, no-charge,
           49  +royalty-free license to
           50  +make, have made, use, sell, offer to sell, import, and otherwise
           51  +transfer your contribution in whole or in part, alone or in
           52  +combination with or included in any product, work or materials arising
           53  +out of the Fossil project, and to sublicense these same rights.
           54  +
           55  +<li><p>
           56  +Except as set out above, you keep all right, title, and interest in your
           57  +contribution.  The rights that you grant to Hwaci under this agreement
           58  +are effective on the date that you first submitted your contribution to the
           59  +Fossil project, even if your submission took place before the date that
           60  +you sign this agreement.
    32     61   
    33     62   <li><p>
    34         -Programmer hereby represents and
    35         -warrants to Licensee that Programmer is the owner of those portions of
    36         -the Fossil software developed and produced by Programmer or
    37         -otherwise has the right to grant to Hwaci the rights set forth in
    38         -this Agreement.
           63  +You represent and warrant the following:
           64  +<ul>
           65  +<li> Your contribution is an original work and that you can legally
           66  +     grant the rights set out in this agreement.
           67  +<li> Your contribution does not, to the best of your knowledge and belief,
           68  +     violate any third party's copyrights, trademarks, patents,
           69  +     or other intellectual property rights.
           70  +<li> You are authorized to sign this agreement on behalf of your
           71  +     company (if appliable).
           72  +</ul>
    39     73   </ol>
    40     74   
    41         -<p>
    42         -In witness whereof, the parties have executed this Agreement, 
    43         -effective this <font color="blue">YYYY-MM-DD</font>
           75  +<p>By filling in the following information and signing your name,
           76  +you agree to be bound by all of the terms
           77  +set forth in this agreement.  Please print clearly.</p>
           78  + 
           79  +<center>
           80  +<p><table width="80%" border="1" cellpadding="0" cellspacing="0">
           81  +<tr><td width="20%" valign="top">Your name &amp email:</td><td width="80%">
           82  +
           83  +    <!-- Replace this line with your name and email --> &nbsp;<p>&nbsp;
           84  +
           85  +</td></tr>
           86  +<tr><td valign="top">Company name:<br>(if applicable)</td><td>
           87  +
           88  +    <!-- Replace this line with your company name --> &nbsp;<p>&nbsp;
           89  +
           90  +&nbsp;</td></tr>
           91  +<tr><td valign="top">Postal address:</td><td>
           92  +
           93  +    <!-- Replace this line and the next line with your postal address -->
           94  +    <p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
           95  +
           96  +</td></tr>
           97  +<tr><td valign="top">Signature:</td><td>&nbsp;<p>&nbsp;</td></tr>
           98  +<tr><td valign="top">Date:</td><td>&nbsp;<p>&nbsp;</td></tr>
           99  +</table>
          100  +</center>
    44    101   
    45         -<p><table width="100%" border="0">
    46         -<tr><td valign="top">
    47         -HWACI<br>
    48         -By:<hr>
    49         -<p>&nbsp;</p>
    50         -Print Name:<hr>
    51         -<p>&nbsp;</p>
    52         -Title:<hr>
    53         -<p>&nbsp;</p>
    54         -</td><td width="10%"></td>
    55         -<td valign="top">
    56         -PROGRAMMER<br>
    57         -By:<hr>
    58         -<p>&nbsp;</p>
    59         -Print Name:<hr>
    60         -<p>&nbsp;</p>
    61         -Title:<hr>
    62         -<p>&nbsp;</p>
    63         -</td></tr></table>
          102  +<p>Send completed forms to:
          103  +<blockquote>
          104  +Hipp, Wyrick &amp; Company, Inc.<br>
          105  +6200 Maple Cove Lane<br>
          106  +Charlotte, NC 28269-1086<br>
          107  +USA
          108  +</p>

Added www/copyright-release.pdf.

cannot compute difference between binary files

Changes to www/custom_ticket.wiki.

            1  +<title>Customizing The Ticket System</title>
     1      2   <nowiki>
     2         -<h1 align="center">Customizing The Ticket System</h1>
            3  +<h2>Introduction</h2>
     3      4   <p>
     4      5   This guide will explain how to add the "assigned_to" and "submitted_by" fields
     5      6   to the ticket system in Fossil, as well as making the system more useful.  You
     6      7   must have "admin" access to the repository to implement these instructions.
     7      8   </p>
     8      9   
     9     10   <h2>First modify the TICKET table</h2><blockquote>

Changes to www/delta_encoder_algorithm.wiki.

            1  +<title>Fossil Delta Encoding Algorithm</title>
     1      2   <nowiki>
     2         -<h1 align="center">
     3         -Fossil Delta Encoding Algorithm
     4         -</h1>
            3  +<h2>Abstract</h2>
     5      4   
     6      5   <p>A key component for the efficient storage of multiple revisions of
     7      6   a file in fossil repositories is the use of delta-compression, i.e. to
     8      7   store only the changes between revisions instead of the whole
     9      8   file.</p>
    10      9   
    11     10   <p>This document describes the encoding algorithm used by Fossil to

Changes to www/delta_format.wiki.

            1  +<title>Fossil Delta Format</title>
     1      2   <nowiki>
     2         -<h1 align="center">
     3         -Fossil Delta Format
     4         -</h1>
            3  +<h2>Abstract</h2>
     5      4   
     6      5   <p>Fossil achieves efficient storage and low-bandwidth synchronization
     7      6   through the use of delta-compression.  Instead of storing
     8      7   or transmitting the complete content of an artifact, fossil stores or
     9      8   transmits only the changes relative to a related artifact.
    10      9   </p>
    11     10   

Added www/event.wiki.

            1  +<title>Events</title>
            2  +
            3  +<h2>What Is An "Event"?</h2>
            4  +
            5  +In Fossil, and "event" is a special kind of [./wikitheory.wiki | wiki page]
            6  +that is associated with a point in time rather than having a page name.
            7  +Each event causes a single entry to appear on the [/timeline | Timeline Page].
            8  +Clicking on the hyperlink of the timeline entry cause a jump to the wiki
            9  +content for the event.  The wiki content, the timeline entry text, the
           10  +time of the event, and the timeline background color can all be edited.
           11  +
           12  +As with check-ins, wiki, and tickets, all events automatically synchronize
           13  +to other repositories.  Hence, events can be viewed, created, and edited
           14  +off-line.  And the complete edit history for events is maintained
           15  +for auditing purposes.
           16  +
           17  +Possible uses for events include:
           18  +
           19  +  *  <b>Milestones</b>.  Project milestones, such as releases or beta-test
           20  +      cycles, can be recorded as events.  The timeline entry for the event
           21  +      can be something simple like "Version 1.2.3" perhaps with a bright
           22  +      color background to draw attention to the entry and the wiki content
           23  +      can contain release notes, for example.
           24  +
           25  +  *  <b>Blog Entries</b>.  Blog entries from developers describing the current
           26  +     state of a project, or rational for various design decisions, or 
           27  +     roadmaps for future development, can be entered as events.
           28  +
           29  +  *  <b>Process Checkpoints</b>.  For projects that have a formal process,
           30  +     events can be used to record the completion or the initiation of
           31  +     various process steps.  For example, an event can be used to record
           32  +     the successful completion of a long-running test, perhaps with
           33  +     performance results and details of where the test was run and who
           34  +     ran it recorded in the wiki content.
           35  +
           36  +  *  <b>News Articles</b>.  Significant occurrences in the lifecycle of
           37  +     a project can be recorded as news articles using events.  Perhaps the
           38  +     domain name of the canonical website for a project changes, or new
           39  +     server hardware is obtained.  Such happenings are appropriate for
           40  +     reporting as news.
           41  +
           42  +  *  <b>Announcements</b>.  Changes to the composition of the development
           43  +     team or acquisition of new project sponsors can be communicated as
           44  +     announcements which can be implemented as events.
           45  +
           46  +No project is required to use events.  But events can help many projects
           47  +stay better organized and provide a better historical record of the
           48  +development progress.
           49  +
           50  +<h2>Viewing Events</h2>
           51  +
           52  +Because events are considered a special kind of wiki, 
           53  +users must have permission to read wiki in order read events.
           54  +Enable the "j" permission under the /Setup/Users menu in order
           55  +to give specific users or user classes the ability to view wiki
           56  +and events.
           57  +
           58  +Events show up on the timeline.  Click on the hyperlink beside the
           59  +event title to see the details of the event.
           60  +
           61  +<h2>Creating And Editing Events</h2>
           62  +
           63  +There is a hyperlink under the /Wiki menu that can be used to create
           64  +new events.  And there is a submenu hyperlink on event displays for
           65  +editing existing events.
           66  +
           67  +Users must have check-in privileges (permission "i") in order to 
           68  +create or edit events.  In addition, users must have create-wiki
           69  +privilege (permission "f") to create new events and edit-wiki
           70  +privilege (permission "k") in order to edit existing events.
           71  +
           72  +If the first non-whitespace text of the event wiki content is
           73  +&lt;title&gt;...&lt;/title&gt; then that markup is omitted from
           74  +the body of the wiki pages and is instead displayed as the page
           75  +title.

Changes to www/faq.tcl.

     7      7     set ::faq($::cnt) [list [string trim $question] [string trim $answer]]
     8      8     incr ::cnt
     9      9   }
    10     10   
    11     11   faq {
    12     12     What GUIs are available for fossil?
    13     13   } {
    14         -  The fossil executable comes with a web-based GUI built in.  Just run:
           14  +  The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
           15  +  Just run:
    15     16   
    16     17     <blockquote>
    17         -  <b>fossil ui</b> <i>REPOSITORY-FILENAME</i>
           18  +  <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i>
    18     19     </blockquote>
    19     20   
    20     21     And your default web browser should pop up and automatically point to
    21     22     the fossil interface.  (Hint:  You can omit the <i>REPOSITORY-FILENAME</i>
    22     23     if you are within an open check-out.)
    23     24   }
    24     25   
................................................................................
    28     29     This is a big question - too big to answer in a FAQ.  Please
    29     30     read the <a href="branching.wiki">Branching, Forking, Merging,
    30     31     and Tagging</a> document.
    31     32   }
    32     33   
    33     34   
    34     35   faq {
    35         -  How do I create a new branch in fossil?
           36  +  How do I create a new branch?
    36     37   } {
    37     38     There are lots of ways:
    38     39   
    39         -  When you are checking in a new change using the <b>commit</b>
           40  +  When you are checking in a new change using the <b>[/help/commit|commit]</b>
    40     41     command, you can add the option  "--branch <i>BRANCH-NAME</i>" to
    41         -  make the change be the founding check-in for a new branch.  You can
           42  +  make the new check-in be the first check-in for a new branch.  You can
    42     43     also add the "--bgcolor <i>COLOR</i>" option to give the branch a
    43     44     specific background color on timelines.
    44     45   
    45         -  If you want to create a new branch whose founding check-in is the
           46  +  If you want to create a new branch whose initial content is the
    46     47     same as an existing check-in, use this command:
    47     48   
    48     49     <blockquote>
    49         -  <b>fossil branch new</b> <i>BRANCH-NAME BASIS</i>
           50  +  <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i>
    50     51     </blockquote>
    51     52   
    52     53     The <i>BRANCH-NAME</i> argument is the name of the new branch and the
    53     54     <i>BASIS</i> argument is the name of the check-in that the branch splits
    54     55     off from.
    55     56   
    56     57     If you already have a fork in your check-in tree and you want to convert
    57     58     that fork to a branch, you can do this from the web interface.
    58     59     First locate the check-in that you want to be
    59         -  the founding check-in of your branch on the timeline and click on its
           60  +  the initial check-in of your branch on the timeline and click on its
    60     61     link so that you are on the <b>ci</b> page.  Then find the "<b>edit</b>"
    61     62     link (near the "Commands:" label) and click on that.  On the 
    62     63     "Edit Check-in" page, check the box beside "Branching:" and fill in 
    63     64     the name of your new branch to the right and press the "Apply Changes"
    64     65     button.
    65     66   }
           67  +
           68  +faq {
           69  +  How do I tag a check-in?
           70  +} {
           71  +  There are several ways:
           72  +
           73  +  When you are checking in a new change using the <b>[/help/commit|commit]</b>
           74  +  command, you can add a tag to that check-in using the
           75  +  "--tag <i>TAGNAME</i>" command-line option.
           76  +
           77  +  If you want add a tag to an existing check-in, you can use the
           78  +  <b>[/help/tag|tag]</b> command.  For example:
           79  +
           80  +  <blockquote>
           81  +  <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i>
           82  +  </blockquote>
           83  +
           84  +  The CHECK-IN in the previous line can be any
           85  +  [./checkin_names.wiki | valid check-in name format].
           86  +
           87  +  You can also add (and remove) tags from a check-in using the
           88  +  [./webui.wiki | web interface].  First locate the check-in that you 
           89  +  what to tag on the tmline, then click on the link to go the detailed
           90  +  information page for that check-in.  Then find the "<b>edit</b>"
           91  +  link (near the "Commands:" label) and click on that.  There are
           92  +  controls on the edit page that allow new tags to be added and existing
           93  +  tags to be removed.
           94  +}  
    66     95   
    67     96   faq {
    68     97     How do I create a private branch that won't get pushed back to the
    69     98     main repository.
    70     99   } {
    71    100     Use the <b>--private</b> command-line option on the 
    72    101     <b>commit</b> command.  The result will be a check-in which exists on
................................................................................
    82    111     you have everything in your private branch the way you want it, you can
    83    112     then merge your private branch back into the trunk and push.  Only the
    84    113     final merge operation will appear in other repositories.  It will seem
    85    114     as if all the changes that occurred on your private branch occurred in
    86    115     a single check-in.
    87    116     Of course, you can also keep your branch private forever simply
    88    117     by not merging the changes in the private branch back into the trunk.
          118  +
          119  +  [./private.wiki | Additional information]
    89    120   }
    90    121   
    91    122   faq {
    92    123     How can I delete inappropriate content from my fossil repository?
    93    124   } {
    94    125     See the article on [./shunning.wiki | "shunning"] for details.
    95    126   }
    96    127   
    97    128   faq {
    98    129     How do I make a clone of the fossil self-hosting repository?
    99    130   } {
   100    131     Any of the following commands should work:
   101    132     <blockquote><pre>
   102         -  fossil  clone  http://www.fossil-scm.org/  fossil.fossil<br>
   103         -  fossil  clone  http://www2.fossil-scm.org/  fossil.fossil<br>
   104         -  fossil  clone  http://www.hwaci.com/cgi-bin/fossil  fossil.fossil
          133  +  fossil [/help/clone|clone]  http://www.fossil-scm.org/  fossil.fossil
          134  +  fossil [/help/clone|clone]  http://www2.fossil-scm.org/  fossil.fossil
          135  +  fossil [/help/clone|clone]  http://www3.fossil-scm.org/site.cgi  fossil.fossil
   105    136     </pre></blockquote>
   106    137     Once you have the repository cloned, you can open a local check-out
   107    138     as follows:
   108    139     <blockquote><pre>
   109         -  mkdir src; cd src; fossil open ../fossil.fossil
          140  +  mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil
   110    141     </pre></blockquote>
   111    142     Thereafter you should be able to keep your local check-out up to date
   112    143     with the latest code in the public repository by typing:
   113    144     <blockquote><pre>
   114         -  fossil update
          145  +  fossil [/help/update|update]
   115    146     </pre></blockquote>
   116    147   }
          148  +
          149  +faq {
          150  +  How do I import or export content from and to other version control systems?
          151  +} {
          152  +  Please see [./inout.wiki | Import And Export]
          153  +}
          154  +
          155  +
   117    156   
   118    157   #############################################################################
   119    158   # Code to actually generate the FAQ
   120    159   #
   121    160   puts "<title>Fossil FAQ</title>"
   122    161   puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n"
   123    162   puts "<p>Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n"

Changes to www/faq.wiki.

     2      2   <h1 align="center">Frequently Asked Questions</h1>
     3      3   
     4      4   <p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>.
     5      5   
     6      6   <ol>
     7      7   <li><a href="#q1">What GUIs are available for fossil?</a></li>
     8      8   <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li>
     9         -<li><a href="#q3">How do I create a new branch in fossil?</a></li>
    10         -<li><a href="#q4">How do I create a private branch that won't get pushed back to the
            9  +<li><a href="#q3">How do I create a new branch?</a></li>
           10  +<li><a href="#q4">How do I tag a check-in?</a></li>
           11  +<li><a href="#q5">How do I create a private branch that won't get pushed back to the
    11     12     main repository.</a></li>
    12         -<li><a href="#q5">How can I delete inappropriate content from my fossil repository?</a></li>
    13         -<li><a href="#q6">How do I make a clone of the fossil self-hosting repository?</a></li>
           13  +<li><a href="#q6">How can I delete inappropriate content from my fossil repository?</a></li>
           14  +<li><a href="#q7">How do I make a clone of the fossil self-hosting repository?</a></li>
           15  +<li><a href="#q8">How do I import or export content from and to other version control systems?</a></li>
    14     16   </ol>
    15     17   <hr>
    16     18   <a name="q1"></a>
    17     19   <p><b>(1) What GUIs are available for fossil?</b></p>
    18     20   
    19         -<blockquote>The fossil executable comes with a web-based GUI built in.  Just run:
           21  +<blockquote>The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
           22  +Just run:
    20     23   
    21     24   <blockquote>
    22         -<b>fossil ui</b> <i>REPOSITORY-FILENAME</i>
           25  +<b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i>
    23     26   </blockquote>
    24     27   
    25     28   And your default web browser should pop up and automatically point to
    26     29   the fossil interface.  (Hint:  You can omit the <i>REPOSITORY-FILENAME</i>
    27     30   if you are within an open check-out.)</blockquote></li>
    28     31   
    29     32   <a name="q2"></a>
................................................................................
    30     33   <p><b>(2) What is the difference between a "branch" and a "fork"?</b></p>
    31     34   
    32     35   <blockquote>This is a big question - too big to answer in a FAQ.  Please
    33     36   read the <a href="branching.wiki">Branching, Forking, Merging,
    34     37   and Tagging</a> document.</blockquote></li>
    35     38   
    36     39   <a name="q3"></a>
    37         -<p><b>(3) How do I create a new branch in fossil?</b></p>
           40  +<p><b>(3) How do I create a new branch?</b></p>
    38     41   
    39     42   <blockquote>There are lots of ways:
    40     43   
    41         -When you are checking in a new change using the <b>commit</b>
           44  +When you are checking in a new change using the <b>[/help/commit|commit]</b>
    42     45   command, you can add the option  "--branch <i>BRANCH-NAME</i>" to
    43         -make the change be the founding check-in for a new branch.  You can
           46  +make the new check-in be the first check-in for a new branch.  You can
    44     47   also add the "--bgcolor <i>COLOR</i>" option to give the branch a
    45     48   specific background color on timelines.
    46     49   
    47         -If you want to create a new branch whose founding check-in is the
           50  +If you want to create a new branch whose initial content is the
    48     51   same as an existing check-in, use this command:
    49     52   
    50     53   <blockquote>
    51         -<b>fossil branch new</b> <i>BRANCH-NAME BASIS</i>
           54  +<b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i>
    52     55   </blockquote>
    53     56   
    54     57   The <i>BRANCH-NAME</i> argument is the name of the new branch and the
    55     58   <i>BASIS</i> argument is the name of the check-in that the branch splits
    56     59   off from.
    57     60   
    58     61   If you already have a fork in your check-in tree and you want to convert
    59     62   that fork to a branch, you can do this from the web interface.
    60     63   First locate the check-in that you want to be
    61         -the founding check-in of your branch on the timeline and click on its
           64  +the initial check-in of your branch on the timeline and click on its
    62     65   link so that you are on the <b>ci</b> page.  Then find the "<b>edit</b>"
    63     66   link (near the "Commands:" label) and click on that.  On the 
    64     67   "Edit Check-in" page, check the box beside "Branching:" and fill in 
    65     68   the name of your new branch to the right and press the "Apply Changes"
    66     69   button.</blockquote></li>
    67     70   
    68     71   <a name="q4"></a>
    69         -<p><b>(4) How do I create a private branch that won't get pushed back to the
           72  +<p><b>(4) How do I tag a check-in?</b></p>
           73  +
           74  +<blockquote>There are several ways:
           75  +
           76  +When you are checking in a new change using the <b>[/help/commit|commit]</b>
           77  +command, you can add a tag to that check-in using the
           78  +"--tag <i>TAGNAME</i>" command-line option.
           79  +
           80  +If you want add a tag to an existing check-in, you can use the
           81  +<b>[/help/tag|tag]</b> command.  For example:
           82  +
           83  +<blockquote>
           84  +<b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i>
           85  +</blockquote>
           86  +
           87  +The CHECK-IN in the previous line can be any
           88  +[./checkin_names.wiki | valid check-in name format].
           89  +
           90  +You can also add (and remove) tags from a check-in using the
           91  +[./webui.wiki | web interface].  First locate the check-in that you 
           92  +what to tag on the tmline, then click on the link to go the detailed
           93  +information page for that check-in.  Then find the "<b>edit</b>"
           94  +link (near the "Commands:" label) and click on that.  There are
           95  +controls on the edit page that allow new tags to be added and existing
           96  +tags to be removed.</blockquote></li>
           97  +
           98  +<a name="q5"></a>
           99  +<p><b>(5) How do I create a private branch that won't get pushed back to the
    70    100     main repository.</b></p>
    71    101   
    72    102   <blockquote>Use the <b>--private</b> command-line option on the 
    73    103   <b>commit</b> command.  The result will be a check-in which exists on
    74    104   your local repository only and is never pushed to other repositories.  
    75    105   All descendents of a private check-in are also private.
    76    106   
................................................................................
    82    112   your private branch in sync with the latest changes on the trunk.  Once
    83    113   you have everything in your private branch the way you want it, you can
    84    114   then merge your private branch back into the trunk and push.  Only the
    85    115   final merge operation will appear in other repositories.  It will seem
    86    116   as if all the changes that occurred on your private branch occurred in
    87    117   a single check-in.
    88    118   Of course, you can also keep your branch private forever simply
    89         -by not merging the changes in the private branch back into the trunk.</blockquote></li>
          119  +by not merging the changes in the private branch back into the trunk.
          120  +
          121  +[./private.wiki | Additional information]</blockquote></li>
    90    122   
    91         -<a name="q5"></a>
    92         -<p><b>(5) How can I delete inappropriate content from my fossil repository?</b></p>
          123  +<a name="q6"></a>
          124  +<p><b>(6) How can I delete inappropriate content from my fossil repository?</b></p>
    93    125   
    94    126   <blockquote>See the article on [./shunning.wiki | "shunning"] for details.</blockquote></li>
    95    127   
    96         -<a name="q6"></a>
    97         -<p><b>(6) How do I make a clone of the fossil self-hosting repository?</b></p>
          128  +<a name="q7"></a>
          129  +<p><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p>
    98    130   
    99    131   <blockquote>Any of the following commands should work:
   100    132   <blockquote><pre>
   101         -fossil  clone  http://www.fossil-scm.org/  fossil.fossil<br>
   102         -fossil  clone  http://www2.fossil-scm.org/  fossil.fossil<br>
   103         -fossil  clone  http://www.hwaci.com/cgi-bin/fossil  fossil.fossil
          133  +fossil [/help/clone|clone]  http://www.fossil-scm.org/  fossil.fossil
          134  +fossil [/help/clone|clone]  http://www2.fossil-scm.org/  fossil.fossil
          135  +fossil [/help/clone|clone]  http://www3.fossil-scm.org/site.cgi  fossil.fossil
   104    136   </pre></blockquote>
   105    137   Once you have the repository cloned, you can open a local check-out
   106    138   as follows:
   107    139   <blockquote><pre>
   108         -mkdir src; cd src; fossil open ../fossil.fossil
          140  +mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil
   109    141   </pre></blockquote>
   110    142   Thereafter you should be able to keep your local check-out up to date
   111    143   with the latest code in the public repository by typing:
   112    144   <blockquote><pre>
   113         -fossil update
          145  +fossil [/help/update|update]
   114    146   </pre></blockquote></blockquote></li>
   115    147   
          148  +<a name="q8"></a>
          149  +<p><b>(8) How do I import or export content from and to other version control systems?</b></p>
          150  +
          151  +<blockquote>Please see [./inout.wiki | Import And Export]</blockquote></li>
          152  +
   116    153   </ol>

Changes to www/fileformat.wiki.

    40     40   
    41     41   <ul>
    42     42   <li> [#manifest | Manifests] </li>
    43     43   <li> [#cluster | Clusters] </li>
    44     44   <li> [#ctrl | Control Artifacts] </li>
    45     45   <li> [#wikichng | Wiki Pages] </li>
    46     46   <li> [#tktchng | Ticket Changes] </li>
    47         -<li> [#artifact | Artifacts] </li>
           47  +<li> [#attachment | Attachments] </li>
           48  +<li> [#event | Events] </li>
    48     49   </ul>
    49     50   
    50         -These five artifact types are described in the sequel.
           51  +These seven artifact types are described in the sequel.
    51     52   
    52     53   In the current implementation (as of 2009-01-25) the artifacts that
    53     54   make up a fossil repository are stored in in as delta- and zlib-compressed
    54     55   blobs in an <a href="http://www.sqlite.org/">SQLite</a> database.  This
    55     56   is an implementation detail and might change in a future release.  For
    56     57   the purpose of this article "file format" means the format of the artifacts,
    57     58   not how the artifacts are stored on disk.  It is the artifact format that
    58     59   is intended to be enduring.  The specifics of how artifacts are stored on
    59     60   disk, though stable, is not intended to live as long as the
    60     61   artifact format.
           62  +
           63  +All of the artifacts can be extracted from a Fossil repository using
           64  +the "fossil deconstruct" command.
    61     65   
    62     66   <a name="manifest"></a>
    63     67   <h2>1.0 The Manifest</h2>
    64     68   
    65     69   A manifest defines a check-in or version of the project
    66     70   source tree.  The manifest contains a list of artifacts for
    67     71   each file in the project and the corresponding filenames, as
................................................................................
    89     93   No card may be duplicated.
    90     94   The entire manifest may be PGP clear-signed, but otherwise it
    91     95   may contain no additional text or data beyond what is described here.
    92     96   
    93     97   Allowed cards in the manifest are as follows:
    94     98   
    95     99   <blockquote>
          100  +<b>B</b> <i>baseline-manifest</i><br>
    96    101   <b>C</b> <i>checkin-comment</i><br>
    97    102   <b>D</b> <i>time-and-date-stamp</i><br>
    98    103   <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
    99    104   <b>P</b> <i>SHA1-hash</i>+<br>
          105  +<b>Q</b> (<b>+</b>|<b>-</b>)<i>SHA1-hash ?SHA1-hash?</i><br>
   100    106   <b>R</b> <i>repository-checksum</i><br>
   101    107   <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name  <b>*</b> ?value?</i><br>
   102    108   <b>U</b> <i>user-login</i><br>
   103    109   <b>Z</b> <i>manifest-checksum</i>
   104    110   </blockquote>
          111  +
          112  +A manifest may optionally have a single B-card.  The B-card specifies
          113  +another manifest that serves as the "baseline" for this manifest.  A
          114  +manifest that has a B-card is called a delta-manifest and a manifest
          115  +that omits the B-card is a baseline-manifest.  The other manifest
          116  +identified by the argument of the B-card must be a baseline-manifest.
          117  +A baseline-manifest records the complete contents of a checkin.
          118  +A delta-manifest records only changes from its baseline.  
   105    119   
   106    120   A manifest must have exactly one C-card.  The sole argument to
   107    121   the C-card is a check-in comment that describes the check-in that
   108    122   the manifest defines.  The check-in comment is text.  The following
   109    123   escape sequences are applied to the text:
   110    124   A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73).  A
   111         -newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E).  A backslash 
          125  +newline (ASCII 0x0a) is "\n" (ASCII 0x5C, x6E).  A backslash 
   112    126   (ASCII 0x5C) is represented as two backslashes "\\".  Apart from
   113    127   space and newline, no other whitespace characters are allowed in
   114    128   the check-in comment.  Nor are any unprintable characters allowed
   115    129   in the comment.
   116    130   
   117    131   A manifest must have exactly one D-card.  The sole argument to
   118    132   the D-card is a date-time stamp in the ISO8601 format.  The
   119    133   date and time should be in coordinated universal time (UTC).
   120         -The format is:
          134  +The format one of:
   121    135   
   122    136   <blockquote>
   123         -<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i>
          137  +<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><br>
          138  +<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><b>.</b><i>SSS</i>
   124    139   </blockquote>
   125    140   
   126         -A manifest has zero or more F-cards.  Each F-card defines a file
   127         -(other than the manifest itself) which is part of the check-in that
   128         -the manifest defines.  There are two, three, or four arguments.
          141  +A manifest has zero or more F-cards.  Each F-card identifies a file
          142  +that is part of the check-in.  There are one, two, three, or four arguments.
   129    143   The first argument
   130    144   is the pathname of the file in the check-in relative to the root
   131    145   of the project file hierarchy.  No ".." or "." directories are allowed
   132    146   within the filename.  Space characters are escaped as in C-card
   133    147   comment text.  Backslash characters and newlines are not allowed
   134    148   within filenames.  The directory separator character is a forward
   135    149   slash (ASCII 0x2F).  The second argument to the F-card is the
   136    150   full 40-character lower-case hexadecimal SHA1 hash of the content
   137         -artifact.  The optional 3rd argument defines any special access 
          151  +artifact.  The second argument is required for baseline manifests
          152  +but is optional for delta manifests.  When the second argument to the
          153  +F-card is omitted, it means that the file has been deleted relative
          154  +to the baseline.  The optional 3rd argument defines any special access 
   138    155   permissions associated with the file.  The only special code currently
   139    156   defined is "x" which means that the file is executable.  All files are
   140    157   always readable and writable.  This can be expressed by "w" permission
   141         -if desired but is optional.
          158  +if desired but is optional.  The file format might be extended with
          159  +new permission letters in the future.
   142    160   The optional 4th argument is the name of the same file as it existed in
   143    161   the parent check-in.  If the name of the file is unchanged from its
   144    162   parent, then the 4th argument is omitted.
   145    163   
   146    164   A manifest has zero or one P-cards.  Most manifests have one P-card.
   147    165   The P-card has a varying number of arguments that
   148    166   defines other manifests from which the current manifest
................................................................................
   151    169   to the P-card must be unique to that line.
   152    170   The first predecessor is the direct ancestor of the manifest.
   153    171   Other arguments define manifests with which the first was
   154    172   merged to yield the current manifest.  Most manifests have
   155    173   a P-card with a single argument.  The first manifest in the
   156    174   project has no ancestors and thus has no P-card.
   157    175   
          176  +A manifest has zero or more Q-cards.  A Q-card is similar to a P-card
          177  +in that it defines a predecessor to the current check-in.  But
          178  +whereas a P-card defines the immediate ancestor or a merge
          179  +ancestor, the Q-card is used to identify a single check-in or a small
          180  +range of check-ins which were cherry-picked for inclusion in or
          181  +exclusion from the current manifest.  The first argument of
          182  +the Q-card is the artifact ID of another manifest (the "target")
          183  +which has had its changes included or excluded in the current manifest.  
          184  +The target is preceeded by "+" or "-" to show inclusion or
          185  +exclusion, respectively.  The optional second argument to the
          186  +Q-card is another manifest artifact ID which is the "baseline"
          187  +for the cherry-pick.  If omitted, the baseline is the primary
          188  +parent of the target.  The
          189  +changes included or excluded consist of all changes moving from
          190  +the baseline to the target.  
          191  +
          192  +The Q-card was added to the interface specification on 2011-02-26.  
          193  +Older versions of Fossil will reject manifests that contain Q-cards.
          194  +
   158    195   A manifest may optionally have a single R-card.  The R-card has
   159    196   a single argument which is the MD5 checksum of all files in 
   160    197   the check-in except the manifest itself.  The checksum is expressed
   161    198   as 32-characters of lowercase hexadecimal.   The checksum is
   162    199   computed as follows:  For each file in the check-in (except for
   163    200   the manifest itself) in strict sorted lexicographical order, 
   164    201   take the pathname of the file relative to the root of the
   165    202   repository, append a single space (ASCII 0x20), the
   166    203   size of the file in ASCII decimal, a single newline
   167    204   character (ASCII 0x0A), and the complete text of the file.
   168         -Compute the MD5 checksum of the the result.
          205  +Compute the MD5 checksum of the result.
   169    206   
   170         -A manifest might contain one or more T-cards used to set tags or
   171         -properties on the check-in.  The format of the T-card is the same as
          207  +A manifest might contain one or more T-cards used to set
          208  +[./branching.wiki#tags | tags or properties]
          209  +on the check-in.  The format of the T-card is the same as
   172    210   described in <i>Control Artifacts</i> section below, except that the
   173    211   second argument is the single characcter "<b>*</b>" instead of an
   174    212   artifact ID.  The <b>*</b> in place of the artifact ID indicates that
   175    213   the tag or property applies to the current artifact.  It is not
   176    214   possible to encode the current artifact ID as part of an artifact,
   177    215   since the act of inserting the artifact ID would change the artifact ID,
   178    216   hence a <b>*</b> is used to represent "self".  T-cards are typically
................................................................................
   180    218   symbolic name when the check-in is intended to start a new branch.
   181    219   
   182    220   Each manifest has a single U-card.  The argument to the U-card is
   183    221   the login of the user who created the manifest.  The login name
   184    222   is encoded using the same character escapes as is used for the
   185    223   check-in comment argument to the C-card.
   186    224   
   187         -A manifest has an option Z-card as its last line.  The argument
          225  +A manifest must have a single Z-card as its last line.  The argument
   188    226   to the Z-card is a 32-character lowercase hexadecimal MD5 hash
   189    227   of all prior lines of the manifest up to and including the newline 
   190         -character that immediately precedes the "Z".  The Z-card is just
          228  +character that immediately precedes the "Z".  The Z-card is 
   191    229   a sanity check to prove that the manifest is well-formed and
   192    230   consistent.
   193    231   
   194    232   A sample manifest from Fossil itself can be seen
   195    233   [/artifact/28987096ac | here].
   196    234   
   197    235   <a name="cluster"></a>
................................................................................
   226    264   </blockquote>
   227    265   
   228    266   A cluster contains one or more "M" cards followed by a single "Z"
   229    267   line.  Each M card has a single argument which is the artifact ID of 
   230    268   another artifact in the repository.  The Z card work exactly like
   231    269   the Z card of a manifest.  The argument to the Z card is the
   232    270   lower-case hexadecimal representation of the MD5 checksum of all
   233         -prior cards in the cluster.  Note that the Z card is required
   234         -on a cluster.
          271  +prior cards in the cluster.  The Z-card is required.
   235    272   
   236    273   An example cluster from Fossil can be seen
   237    274   [/artifact/d03dbdd73a2a8 | here].
   238    275   
   239    276   <a name="ctrl"></a>
   240    277   <h2>3.0 Control Artifacts</h2>
   241    278   
   242    279   Control artifacts are used to assign properties to other artifacts
   243    280   within the repository.  The basic format of a control artifact is
   244    281   the same as a manifest or cluster.  A control artifact is a text
   245         -files divided into cards by newline characters.  Each card has a
          282  +file divided into cards by newline characters.  Each card has a
   246    283   single-character card type followed by arguments.  Spaces separate
   247    284   the card type and the arguments.  No surplus whitespace is allowed.
   248         -All cards must occur in strict lexigraphical order.
          285  +All cards must occur in strict lexicographical order.
   249    286   
   250    287   Allowed cards in a control artifact are as follows:
   251    288   
   252    289   <blockquote>
   253    290   <b>D</b> <i>time-and-date-stamp</i><br />
   254    291   <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name  artifact-id  ?value?</i><br />
   255    292   <b>U</b> <i>user-name</i><br />
................................................................................
   260    297   one or more T cards.  No other cards or other text is
   261    298   allowed in a control artifact.  Control artifacts might be PGP
   262    299   clearsigned.
   263    300   
   264    301   The D card and the Z card of a control artifact are the same
   265    302   as in a manifest.
   266    303   
   267         -The T card represents a "tag" or property that is applied to
          304  +The T card represents a [./branching.wiki#tags | tag or property]
          305  +that is applied to
   268    306   some other artifact.  The T card has two or three values.  The
   269    307   second argument is the 40 character lowercase artifact ID of the artifact
   270    308   to which the tag is to be applied. The
   271    309   first value is the tag name.  The first character of the tag
   272         -is either "+", "-", or "*".  A "+" means the tag should be added
          310  +is either "+", "-", or "*".  The "+" means the tag should be added
   273    311   to the artifact.  The "-" means the tag should be removed.
   274    312   The "*" character means the tag should be added to the artifact
   275         -and all direct descendants (but not branches) of the artifact down
          313  +and all direct descendants (but not descendents through a merge) down
   276    314   to but not including the first descendant that contains a 
   277         -more recent "-" tag with the same name.
          315  +more recent "-" or "+" tag with the same name.
   278    316   The optional third argument is the value of the tag.  A tag
   279    317   without a value is a boolean.
   280    318   
   281    319   When two or more tags with the same name are applied to the
   282    320   same artifact, the tag with the latest (most recent) date is
   283    321   used.
   284    322   
................................................................................
   286    324   to a check-in will override the check-in comment of that check-in
   287    325   for display purposes.  The "user" tag overrides the name of the
   288    326   check-in user.  The "date" tag overrides the check-in date.
   289    327   The "branch" tag sets the name of the branch that at check-in
   290    328   belongs to.  Symbolic tags begin with the "sym-" prefix.
   291    329   
   292    330   The U card is the name of the user that created the control 
   293         -artifact.  The Z card is the usual artifact checksum.
          331  +artifact.  The Z card is the usual required artifact checksum.
   294    332   
   295    333   An example control artifacts can be seen [/info/9d302ccda8 | here].
   296    334   
   297    335   
   298    336   <a name="wikichng"></a>
   299    337   <h2>4.0 Wiki Pages</h2>
   300    338   
................................................................................
   313    351   <b>Z</b> <i>checksum</i>
   314    352   </blockquote>
   315    353   
   316    354   The D card is the date and time when the wiki page was edited.
   317    355   The P card specifies the parent wiki pages, if any.  The L card
   318    356   gives the name of the wiki page.  The U card specifies the login
   319    357   of the user who made this edit to the wiki page.  The Z card is
   320         -the usual checksum over the either artifact.
          358  +the usual checksum over the either artifact and is required.
   321    359   
   322    360   The W card is used to specify the text of the wiki page.  The
   323    361   argument to the W card is an integer which is the number of bytes
   324    362   of text in the wiki page.  That text follows the newline character
   325    363   that terminates the W card.  The wiki text is always followed by one
   326    364   extra newline.
   327    365   
   328    366   An example wiki artifact can be seen
   329         -[/artifact/7b2f5fd0e0 | here].
          367  +[/artifact?name=7b2f5fd0e0&txt=1 | here].
   330    368   
   331    369   <a name="tktchng"></a>
   332    370   <h2>5.0 Ticket Changes</h2>
   333    371   
   334    372   A ticket-change artifact represents a change to a trouble ticket.
   335    373   The following cards are allowed on a ticket change artifact:
   336    374   
................................................................................
   340    378   <b>K</b> <i>ticket-id</i><br />
   341    379   <b>U</b> <i>user-name</i><br />
   342    380   <b>Z</b> <i>checksum</i>
   343    381   </blockquote>
   344    382   
   345    383   The D card is the usual date and time stamp and represents the point
   346    384   in time when the change was entered.  The U card is the login of the
   347         -programmer who entered this change.  The Z card is the checksum over
          385  +programmer who entered this change.  The Z card is the required checksum over
   348    386   the entire artifact.
   349    387   
   350         -Every ticket has a unique ID.  The ticket to which this change is applied
   351         -is specified by the K card.  A ticket exists if it contains one or
          388  +Every ticket has a distinct ticket-id:
          389  +40-character lower-case hexadecimal number.
          390  +The ticket-id is given in the K-card.  A ticket exists if it contains one or
   352    391   more changes.  The first "change" to a ticket is what brings the
   353    392   ticket into existence.
   354    393   
   355    394   J cards specify changes to the "value" of "fields" in the ticket.
   356    395   If the <i>value</i> parameter of the J card is omitted, then the
   357    396   field is set to an empty string.
   358    397   Each fossil server has a ticket configuration which specifies the fields its
................................................................................
   373    412   An example ticket-change artifact can be seen
   374    413   [/artifact/91f1ec6af053 | here].
   375    414   
   376    415   <a name="attachment"></a>
   377    416   <h2>6.0 Attachments</h2>
   378    417   
   379    418   An attachment artifact associates some other artifact that is the
   380         -attachment (the source artifact) with a ticket or wiki page to which
          419  +attachment (the source artifact) with a ticket or wiki page or event to which
   381    420   the attachment is connected (the target artifact).
   382    421   The following cards are allowed on an attachment artifact:
   383    422   
   384    423   <blockquote>
   385         -<b>A</b> <i>filename target</i> ?<i>source</i>?
   386         -<b>C</b> <i>comment</i><br>
          424  +<b>A</b> <i>filename target</i> ?<i>source</i>?<br />
          425  +<b>C</b> <i>comment</i><br />
   387    426   <b>D</b> <i>time-and-date-stamp</i><br />
   388    427   <b>U</b> <i>user-name</i><br />
   389    428   <b>Z</b> <i>checksum</i>
   390    429   </blockquote>
   391    430   
   392    431   The A card specifies a filename for the attachment in its first argument.
   393    432   The second argument to the A card is the name
   394         -of the wiki page or ticket to which the attachment is connected.  The
          433  +of the wiki page or ticket or event to which the attachment is connected.  The
   395    434   third argument is either missing or else it is the 40-character artifact 
   396    435   ID of the attachment itself.  A missing third argument means that the
   397    436   attachment should be deleted.
   398    437   
   399    438   The C card is an optional comment describing what the attachment is about.
   400    439   The C card is optional, but there can only be one.
   401    440   
................................................................................
   402    441   A single D card is required to give the date and time when the attachment
   403    442   was applied.
   404    443   
   405    444   A single U card gives the name of the user to added the attachment.
   406    445   If an attachment is added anonymously, then the U card may be omitted.
   407    446   
   408    447   The Z card is the usual checksum over the rest of the attachment artifact.
          448  +The Z card is required.
          449  +
          450  +
          451  +<a name="event"></a>
          452  +<h2>7.0 Events</h2>
          453  +
          454  +An event artifact associates a timeline comment and a page of text
          455  +(similar to a wiki page) with a point in time.  Events can be used
          456  +to record project milestones, release notes, blog entries, process
          457  +checkpoints, or news articles.
          458  +The following cards are allowed on an event artifact:
          459  +
          460  +<blockquote>
          461  +<b>C</b> <i>comment</i><br>
          462  +<b>D</b> <i>time-and-date-stamp</i><br />
          463  +<b>E</b> <i>event-time</i> <i>event-id</i><br />
          464  +<b>P</b> <i>parent-artifact-id</i>+<br />
          465  +<b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br />
          466  +<b>U</b> <i>user-name</i><br />
          467  +<b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br />
          468  +<b>Z</b> <i>checksum</i>
          469  +</blockquote>
          470  +
          471  +The C card contains text that is displayed on the timeline for the
          472  +event.  Exactly one C card is required on an event artifact.
          473  +
          474  +A single D card is required to give the date and time when the 
          475  +event artifact was created.  This is different from the time at which
          476  +the event occurs.
          477  +
          478  +A single E card gives the time of the event (the point on the timeline
          479  +where the event is displayed) and a unique identifier for the event.
          480  +When there are multiple artifacts with the same event-id, the one with
          481  +the most recent D card is the only one used.  The event-id must be a
          482  +40-character lower-case hexadecimal string.
          483  +
          484  +The option P card specifies a prior event with the same event-id from
          485  +which the current event is an edit.  The P card is a hint to the system
          486  +that it might be space efficient to store one event as a delta of the
          487  +other.
          488  +
          489  +An event might contain one or more T-cards used to set
          490  +[./branching.wiki#tags | tags or properties]
          491  +on the event.  The format of the T-card is the same as
          492  +described in [#ctrl | Control Artifacts] section above, except that the
          493  +second argument is the single characcter "<b>*</b>" instead of an
          494  +artifact ID and the name is always prefaced by "<b>+</b>".
          495  +The <b>*</b> in place of the artifact ID indicates that
          496  +the tag or property applies to the current artifact.  It is not
          497  +possible to encode the current artifact ID as part of an artifact,
          498  +since the act of inserting the artifact ID would change the artifact ID,
          499  +hence a <b>*</b> is used to represent "self".  The "<b>+</b>" on the
          500  +name means that tags can only be add and they can only be non-propagating
          501  +tags.  A an event, T cards are normally used to set the background
          502  +display color for timelines.
          503  +
          504  +The optional U card gives name of the user who entered the event.
          505  +
          506  +A single W card provides wiki text for the document associated with the
          507  +event.  The format of the W card is exactly the same as for a 
          508  +[#wikichng | wiki artifact].
          509  +
          510  +The Z card is the required checksum over the rest of the artifact.
   409    511   
   410    512   
   411    513   <a name="summary"></a>
   412         -<h2>7.0 Card Summary</h2>
          514  +<h2>8.0 Card Summary</h2>
   413    515   
   414    516   The following table summaries the various kinds of cards that
   415    517   appear on Fossil artifacts:
   416    518   
   417    519   <table border=1 width="100%">
   418    520   <tr>
   419    521   <th rowspan=2 valign=bottom>Card Format</th>
   420         -<th colspan=6>Used By</th>
          522  +<th colspan=7>Used By</th>
   421    523   </tr>
   422    524   <tr>
   423    525   <th>Manifest</th>
   424    526   <th>Cluster</th>
   425    527   <th>Control</th>
   426    528   <th>Wiki</th>
   427    529   <th>Ticket</th>
   428    530   <th>Attachment</th>
          531  +<th>Event</th>
   429    532   </tr>
   430    533   <tr>
   431    534   <td><b>A</b> <i>filename target source</i></td>
   432    535   <td>&nbsp;</td>
   433    536   <td>&nbsp;</td>
   434    537   <td>&nbsp;</td>
   435    538   <td>&nbsp;</td>
   436    539   <td>&nbsp;</td>
   437    540   <td align=center><b>X</b></td>
          541  +<td>&nbsp;</td>
   438    542   </tr>
   439    543   <tr>
   440         -<td><b>C</b> <i>coment-text</i></td>
          544  +<td><b>B</b> <i>baseline</i></td>
          545  +<td align=center><b>X</b></td>
          546  +<td>&nbsp;</td>
          547  +<td>&nbsp;</td>
          548  +<td>&nbsp;</td>
          549  +<td>&nbsp;</td>
          550  +<td>&nbsp;</td>
          551  +<td>&nbsp;</td>
          552  +</tr>
          553  +<tr>
          554  +<td><b>C</b> <i>comment-text</i></td>
   441    555   <td align=center><b>X</b></td>
   442    556   <td>&nbsp;</td>
   443    557   <td>&nbsp;</td>
   444    558   <td>&nbsp;</td>
   445    559   <td>&nbsp;</td>
          560  +<td align=center><b>X</b></td>
   446    561   <td align=center><b>X</b></td>
   447    562   </tr>
   448    563   <tr>
   449    564   <td><b>D</b> <i>date-time-stamp</i></td>
   450    565   <td align=center><b>X</b></td>
   451    566   <td align=center>&nbsp;</td>
   452    567   <td align=center><b>X</b></td>
   453    568   <td align=center><b>X</b></td>
   454    569   <td align=center><b>X</b></td>
   455    570   <td align=center><b>X</b></td>
          571  +<td align=center><b>X</b></td>
          572  +</tr>
          573  +<tr>
          574  +<td><b>E</b> <i>event-time event-id</i></td>
          575  +<td align=center>&nbsp;</td>
          576  +<td align=center>&nbsp;</td>
          577  +<td align=center>&nbsp;</td>
          578  +<td align=center>&nbsp;</td>
          579  +<td align=center>&nbsp;</td>
          580  +<td align=center>&nbsp;</td>
          581  +<td align=center><b>X</b></td>
   456    582   </tr>
   457    583   <tr>
   458    584   <td><b>F</b> <i>filename uuid permissions oldname</i></td>
   459    585   <td align=center><b>X</b></td>
          586  +<td align=center>&nbsp;</td>
   460    587   <td align=center>&nbsp;</td>
   461    588   <td align=center>&nbsp;</td>
   462    589   <td align=center>&nbsp;</td>
   463    590   <td align=center>&nbsp;</td>
   464    591   <td align=center>&nbsp;</td>
   465    592   </tr>
   466    593   <tr>
................................................................................
   467    594   <td><b>J</b> <i>name value</i></td>
   468    595   <td align=center>&nbsp;</td>
   469    596   <td align=center>&nbsp;</td>
   470    597   <td align=center>&nbsp;</td>
   471    598   <td align=center>&nbsp;</td>
   472    599   <td align=center><b>X</b></td>
   473    600   <td align=center>&nbsp;</td>
          601  +<td align=center>&nbsp;</td>
   474    602   </tr>
   475    603   <tr>
   476    604   <td><b>K</b> <i>ticket-uuid</i></td>
   477    605   <td align=center>&nbsp;</td>
   478    606   <td align=center>&nbsp;</td>
   479    607   <td align=center>&nbsp;</td>
   480    608   <td align=center>&nbsp;</td>
   481    609   <td align=center><b>X</b></td>
          610  +<td align=center>&nbsp;</td>
   482    611   <td align=center>&nbsp;</td>
   483    612   </tr>
   484    613   <tr>
   485    614   <td><b>L</b> <i>wiki-title</i></td>
   486    615   <td align=center>&nbsp;</td>
   487    616   <td align=center>&nbsp;</td>
   488    617   <td align=center>&nbsp;</td>
   489    618   <td align=center><b>X</b></td>
   490    619   <td align=center>&nbsp;</td>
   491    620   <td align=center>&nbsp;</td>
          621  +<td align=center>&nbsp;</td>
   492    622   </tr>
   493    623   <tr>
   494    624   <td><b>M</b> <i>uuid</i></td>
   495    625   <td align=center>&nbsp;</td>
   496    626   <td align=center><b>X</b></td>
          627  +<td align=center>&nbsp;</td>
   497    628   <td align=center>&nbsp;</td>
   498    629   <td align=center>&nbsp;</td>
   499    630   <td align=center>&nbsp;</td>
   500    631   <td align=center>&nbsp;</td>
   501    632   </tr>
   502    633   <tr>
   503    634   <td><b>P</b> <i>uuid ...</i></td>
   504    635   <td align=center><b>X</b></td>
   505    636   <td align=center>&nbsp;</td>
   506    637   <td align=center>&nbsp;</td>
   507    638   <td align=center><b>X</b></td>
   508    639   <td align=center>&nbsp;</td>
   509    640   <td align=center>&nbsp;</td>
          641  +<td align=center>&nbsp;</td>
          642  +</tr>
          643  +<tr>
          644  +<td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid uuid</i></td>
          645  +<td align=center><b>X</b></td>
          646  +<td align=center>&nbsp;</td>
          647  +<td align=center>&nbsp;</td>
          648  +<td align=center>&nbsp;</td>
          649  +<td align=center>&nbsp;</td>
          650  +<td align=center>&nbsp;</td>
          651  +<td align=center>&nbsp;</td>
   510    652   </tr>
   511    653   <tr>
   512    654   <td><b>R</b> <i>md5sum</i></td>
   513    655   <td align=center><b>X</b></td>
          656  +<td align=center>&nbsp;</td>
   514    657   <td align=center>&nbsp;</td>
   515    658   <td align=center>&nbsp;</td>
   516    659   <td align=center>&nbsp;</td>
   517    660   <td align=center>&nbsp;</td>
   518    661   <td align=center>&nbsp;</td>
   519    662   <tr>
   520    663   <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td>
   521    664   <td align=center><b>X</b></td>
   522    665   <td align=center>&nbsp;</td>
   523    666   <td align=center><b>X</b></td>
   524    667   <td align=center>&nbsp;</td>
   525    668   <td align=center>&nbsp;</td>
   526    669   <td align=center>&nbsp;</td>
          670  +<td align=center><b>X</b></td>
   527    671   </tr>
   528    672   <tr>
   529    673   <td><b>U</b> <i>username</i></td>
   530    674   <td align=center><b>X</b></td>
   531    675   <td align=center>&nbsp;</td>
          676  +<td align=center><b>X</b></td>
   532    677   <td align=center><b>X</b></td>
   533    678   <td align=center><b>X</b></td>
   534    679   <td align=center><b>X</b></td>
   535    680   <td align=center><b>X</b></td>
   536    681   </tr>
   537    682   <tr>
   538    683   <td><b>W</b> <i>size</i></td>
   539    684   <td align=center>&nbsp;</td>
   540    685   <td align=center>&nbsp;</td>
   541    686   <td align=center>&nbsp;</td>
   542    687   <td align=center><b>X</b></td>
   543    688   <td align=center>&nbsp;</td>
   544    689   <td align=center>&nbsp;</td>
          690  +<td align=center><b>X</b></td>
   545    691   </tr>
   546    692   <tr>
   547    693   <td><b>Z</b> <i>md5sum</i></td>
          694  +<td align=center><b>X</b></td>
   548    695   <td align=center><b>X</b></td>
   549    696   <td align=center><b>X</b></td>
   550    697   <td align=center><b>X</b></td>
   551    698   <td align=center><b>X</b></td>
   552    699   <td align=center><b>X</b></td>
   553    700   <td align=center><b>X</b></td>
   554    701   </tr>
   555    702   </table>

Added www/fiveminutes.wiki.

            1  +<title>Up and running in 5 minutes as a single user</title>
            2  +
            3  +<p align="center"><b><i>
            4  +The following document was contributed by Gilles Ganault on 2013-01-08.
            5  +</i></b>
            6  +</p><hr>
            7  +
            8  +<h1>Up and running in 5 minutes as a single user</h1>
            9  +<p>This short document explains the main basic Fossil commands for a single 
           10  +user, ie. with no additional users, with no need to synchronize with some remote 
           11  +repository, and no need for branching/forking.</p>
           12  +
           13  +<h2>Create a new repository</h2>
           14  +<p>fossil new c:\test.repo</p>
           15  +<p>This will create the new SQLite binary file that holds the repository, ie. 
           16  +files, tickets, wiki, etc. It can be located anywhere, although it's considered 
           17  +best practise to keep it outside the work directory where you will work on files 
           18  +after they've been checked out of the repository.</p>
           19  +
           20  +<h2>Open the repository</h2>
           21  +<p>cd c:\temp\test.fossil</p>
           22  +<p>fossil open c:\test.repo</p>
           23  +<p>This will check out the last revision of all the files in the repository, 
           24  +if any, into the current work directory. In addition, it will create a binary 
           25  +file _FOSSIL_ to keep track of changes (on non-Windows systems it is called
           26  +<tt>.fslckout</tt>).</p>
           27  +
           28  +<h2>Add new files</h2>
           29  +<p>fossil add .</p>
           30  +<p>To tell Fossil to add new files to the repository. The files aren't actually 
           31  +added until you run &quot;commit&quot;. When using &quot;.&quot;, it tells Fossil 
           32  +to add all the files in the current directory recursively, ie. including all 
           33  +the files in all the subdirectories.</p>
           34  +<p>Note: To tell Fossil to ignore some extensions:</p>
           35  +<p>fossil settings ignore-glob &quot;*.o,*.obj,*.exe&quot; --global</p>
           36  +
           37  +<h2>Remove files that haven't been commited yet</h2>
           38  +<p>fossil delete myfile.c</p>
           39  +<p>This will simply remove the item from the list of files that were previously 
           40  +added through &quot;fossil add&quot;.</p>
           41  +
           42  +<h2>Check current status</h2>
           43  +<p>fossil changes</p>
           44  +<p>This shows the list of changes that have been done and will be commited the 
           45  +next time you run &quot;fossil commit&quot;. It's a useful command to run before 
           46  +running &quot;fossil commit&quot; just to check that things are OK before proceeding.</p>
           47  +
           48  +<h2>Commit changes</h2>
           49  +<p>To actually apply the pending changes to the repository, eg. new files marked 
           50  +for addition, checked-out files that have been edited and must be checked-in, 
           51  +etc.</p>
           52  +
           53  +<p>fossil commit -m "Added stuff"</p>
           54  +
           55  +If no file names are provided on the command-line then all changes will be checked in,
           56  +otherwise just the listed file(s) will be checked in.
           57  +
           58  +<h2>Compare two revisions of a file</h2>
           59  +<p>If you wish to compare the last revision of a file and its checked out version 
           60  +in your work directory:</p>
           61  +<p>fossil gdiff myfile.c</p>
           62  +<p>If you wish to compare two different revisions of a file in the repository:</p>
           63  +<p>fossil finfo myfile: Note the first hash, which is the UUID of the commit 
           64  +when the file was commited</p>
           65  +<p>fossil gdiff --from UUID#1 --to UUID#2 myfile.c</p>
           66  +<h2>Cancel changes and go back to previous revision</h2>
           67  +<p>fossil revert myfile.c</p>
           68  +<p>Fossil does not prompt when reverting a file. It simply reminds the user about the
           69  +"undo" command, just in case the revert was a mistake.</p>
           70  +
           71  +
           72  +<h2>Close the repository</h2>
           73  +<p>fossil close</p>
           74  +<p>This will simply remove the _FOSSIL_ at the root of the work directory but 
           75  +will not delete the files in the work directory. From then on, any use of &quot;fossil&quot; 
           76  +will trigger an error since there is no longer any connection.</p>

Added www/foss-cklist.wiki.

            1  +<title>Checklist For Successful Open-Source Projects</title>
            2  +<nowiki>
            3  +
            4  +<p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score
            5  +blog post <a href="http://spot.livejournal.com/308370.html">
            6  +http://spot.livejournal.com/308370.html</a> (see also
            7  +<a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a> and
            8  +<a href="https://www.theopensourceway.org/wiki/How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL">[2]</a>).
            9  +Tom's original post assigned point scores to the various elements and
           10  +by adding together the individual points, the reader is suppose to be able
           11  +to judge the likelihood that the project will fail.
           12  +The point scores, and the items on the list, clearly reflect Tom's
           13  +biases and are not necessarily those of the larger open-source community.
           14  +Nevertheless, the policy of the Fossil shall be to strive for a perfect
           15  +score.</p>
           16  +
           17  +<p>This checklist is an inversion of Tom's original post in that it strives to
           18  +say what the project should do in order to succeed rather than what it
           19  +should not do to avoid failure.  The point values are omitted.</p>
           20  +
           21  +<p>See also:
           22  +<ul>
           23  +<li><a href="http://offog.org/articles/packaging/">
           24  +http://offog.org/articles/packaging/</a>
           25  +<li>
           26  +<a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases">
           27  +http://www.gnu.org/prep/standards/standards.html#Managing-Releases</a>
           28  +</ul>
           29  +
           30  +<hr>
           31  +
           32  +<ol>
           33  +<li><p>The source code size is less than 100 MB, uncompressed.
           34  +
           35  +<li><p>The project uses a Version Control System (VCS).
           36  +<ol type="a">
           37  +<li>The VCS has a working web interface.
           38  +<li>There is documentation on how to use the VCS.
           39  +<li>The VCS is general-purpose, not something hacked together for this
           40  +    one project.
           41  +</ol>
           42  +
           43  +<li><p>The project comes with documentation on how to build from source
           44  +and that documentation is lucid, correct, and up-to-date.
           45  +
           46  +<li><p>The project is configurable using an autoconf-generated configure
           47  +script, or the equivalent, and does not require:
           48  +<ol type="a">
           49  +<li>Manually editing flat files
           50  +<li>Editing code header files
           51  +</ol>
           52  +
           53  +<li><p>The project should be buildable using commonly available and
           54  +       standard tools like "make".
           55  +
           56  +<li><p>The project does not depend on 3rd-party proprietary build tools.
           57  +
           58  +<li><p>The project is able to dynamically link against standard libraries
           59  +      such as zlib and libjpeg.
           60  +<ol type="a">
           61  +<li>The project does not ship with the sources to standard libraries.
           62  +    <i>(On the Fossil project, we will allow the SQLite library sources
           63  +    to be included in the source tree as long as a system-installed
           64  +    SQLite library can be used in its stead.)</i>
           65  +<li>The project does not use slightly modified versions of standard
           66  +    libraries.  Any required bug fixes in standard libraries are pushed
           67  +    back upstream.
           68  +</ol>
           69  +
           70  +
           71  +<li><p>"make install" works and can be configured to target any
           72  +       directory of the installer's choosing.
           73  +<ol type="a">
           74  +<li>The project contains no unconfigurable hard-coded pathnames like
           75  +    "/opt" or "/usr/local".
           76  +<li>After installation, the source tree can be moved or deleted and
           77  +    the application will continue working.
           78  +</ol>
           79  +
           80  +
           81  +<li><p>The source code uses only \n for line endings, not \r\n.
           82  +
           83  +<li><p>The code does not depend on any special compiler features or bugs.
           84  +
           85  +<li><p>The project has a mailing list and announces releases on
           86  +       the mailing list.
           87  +
           88  +<li><p>The project has a bug tracker.
           89  +
           90  +<li><p>The project has a website.
           91  +
           92  +<li><p>Release version numbers are in the traditional X.Y or X.Y.Z format.
           93  +
           94  +<li><p>Releases can be downloaded as tarball using 
           95  +       gzip or bzip2 compression.
           96  +
           97  +<li><p>Releases unpack into a versioned top-level directory.
           98  +       (ex:  "projectname-1.2.3/").
           99  +
          100  +<li><p>A statement of license appears at the top of every source code file
          101  +       and the complete text of the license is included in the source code
          102  +       tarball.
          103  +
          104  +<li><p>There are no incompatible licenses in the code.
          105  +
          106  +<li><p>The project has not been blithely proclaimed "public domain" without
          107  +having gone through the tedious and exacting legal steps to actually put it 
          108  +in the public domain.
          109  +
          110  +<li><p>There is an accurate change log in the code and on the website.
          111  +
          112  +<li><p>There is documentation in the code and on the website.
          113  +</ol>

Added www/fossil-v-git.wiki.

            1  +<title>Fossil Versus Git</title>
            2  +
            3  +<h2>1.0 Don't Stress!</h2>
            4  +
            5  +If you start out using one DVCS and later decide you like the other better,
            6  +it is [./inout.wiki | easy to change].
            7  +
            8  +But it also helps to be informed about the differences between 
            9  +[http://git-scm.com | Git] and Fossil.  See the table below for
           10  +a high-level summary and the text that follows for more details.
           11  +
           12  +Keep in mind that you are reading this on a Fossil website, 
           13  +so the information here
           14  +might be biased in favor of Fossil.  Ask around with people who have
           15  +used both Fossil and Git for other opinions.
           16  +
           17  +<h2>2.0 Executive Summary:</h2>
           18  +
           19  +<blockquote><center><table border=1 cellpadding=5>
           20  +<tr><th width="50%">GIT</th><th width="50%">FOSSIL</th></tr>
           21  +<tr><td>File versioning only</td>
           22  +    <td>Versioning, Tickets, Wiki, and Blog/News</td></tr>
           23  +<tr><td>Sharding</td><td>Replicating</td></tr>
           24  +<tr><td>Developer branches</td><td>Feature branches</td></tr>
           25  +<tr><td>Complex</td><td>Intuitive</td></tr>
           26  +<tr><td>Separate web tools</td><td>Integrated Web interface</td></tr>
           27  +<tr><td>Lots of little tools</td><td>Single executable</td></tr>
           28  +<tr><td>Pile-of-files repository</td><td>Single file repository</td></tr>
           29  +<tr><td>Uses "<tt>rebase</tt>"</td><td>Immutable</td></tr>
           30  +<tr><td>GPL</td><td>BSD</td></tr>
           31  +</table></center></blockquote>
           32  +
           33  +<h2>3.0 Discussion</h2>
           34  +
           35  +<h3>3.1 Feature Set</h3>
           36  +
           37  +Git provides file versioning services only, whereas Fossil adds an
           38  +integrated [./wikitheory.wiki | wiki],
           39  +[./bugtheory.wiki | ticketing &amp; bug tracking],
           40  +[./embedddeddoc.wiki | embedded documentation], and
           41  +[./event.wiki | News/Blog features].
           42  +These additional capabilities are available for Git as 3rd-party
           43  +user-installed add-ons, but with Fossil they are integrated into
           44  +the design.  One way to describe Fossil is that it is
           45  +"[https://github.com/ | github]-in-a-box".
           46  +
           47  +<h3>3.2 Sharding versus Replicating</h3>
           48  +
           49  +Git makes it easy for each repository in a project to hold a subset of
           50  +the branches for that project.  In fact, it is entirely possible and not
           51  +uncommon for no repository in the project to hold all the different code
           52  +versions for a project.  Instead the information is distributed.  
           53  +Individual developers have one or more private branches.  A hierarchy
           54  +of integrators merge changes from individual developers into collaborative
           55  +branches, until all the changes are merged together at the top-level master
           56  +branch.  And all of this can be accomplished without having to have all the
           57  +code in any one repository.  Developers or groups of developers can share
           58  +only those branches that they want to share and keep other branchs of the
           59  +project private.  This is analogous to sharding an a distributed database.
           60  +
           61  +Fossil allows private branches, but its default mode is to share everything.
           62  +And so in a Fossil project, all respositories tend to contain all of the
           63  +content at all times.  This is analogous to replication in a
           64  +distributed database.
           65  +
           66  +The Git model works best for large projects, like the
           67  +Linux kernel for which Git was designed.
           68  +Linus Torvalds does not need or want to see a thousand
           69  +different branches, one for each contributor.  Git allows intermediary
           70  +"gate-keepers" to merge changes from multiple lower-level developers
           71  +into a single branch and only present Linus with a handful of branches
           72  +at a time.  Git encourages a programming model where each developer
           73  +works in his or her own branch and then merges changes up the hierarchy
           74  +until they reach the master branch.
           75  +
           76  +Fossil is designed for smaller and non-hierarchical teams where all
           77  +developers are operating directly on the master branch, or at most
           78  +a small number of well defined branches.  
           79  +The [concepts.wiki#workflow | autosync] mode of Fossil makes it easy
           80  +for multiple developers to work on a single branch and maintain
           81  +linear development on that branch and avoid needless forking
           82  +and merging.
           83  +
           84  +<h3>3.3 Branches</h3>
           85  +
           86  +Git (and especially GitHub) encourages a workflow where each developer 
           87  +has his or her own branch or branches.  Developers then send "pull requests"
           88  +to have their changes be merged into "official" branches by integrators.
           89  +For example, the Linux kernel team has a hierarchy of integrators with
           90  +Linus Torvalds at the root.  Individual developers each have their own
           91  +private branches of the source tree into which they make their own changes.
           92  +They then encourage first-tier integrators to pull those changes.  The
           93  +first-tier integrators merge together changes from multiple contributors
           94  +then try to get second-tier integrators to pull their branches.  The
           95  +changes merge up the hierarchy until (hopefully) they are pulled into 
           96  +"Linus's branch", at which time they become part of the "official" Linux.
           97  +
           98  +In Git, each branch is "owned" by the person who creates it and works
           99  +on it.  The owner might pull changes from others, but the owner is always
          100  +in control of the branch.  Branches are developer-centric.
          101  +
          102  +Fossil, on the other hand, encourages a workflow where branches are 
          103  +associated with features or releases, not individual developers.
          104  +All developers share all branches in common, and two
          105  +or more developers can and often do intersperse commits onto the same branch.
          106  +Branches do not belong to individuals.  All branches are read/write
          107  +accessible to all developers at all times.  There is no need
          108  +for integrators to merge together changes from various independent
          109  +developers.  Instead, all of the developers work together cooperatively
          110  +and the changes stay integrated naturally.
          111  +
          112  +So to a first approximation, branches in Git are developer-centric whereas
          113  +branches in Fossil are feature-centric.
          114  +
          115  +The Git approach scales much better for large projects like the Linux
          116  +kernel with thousands of contributors who in many cases don't even know
          117  +each others names.  The integrators serve a gatekeeper role to help keep
          118  +undesirable code out of the official Linux source tree.  On the other hand, 
          119  +not many projects are as big or as loosely organized as the Linux kernel.
          120  +Most project, have a small team of developers who all know each other
          121  +well and trust each other, and who enjoy working together collaboratively
          122  +without the overhead and hierarchy of integrators.
          123  +
          124  +<h3>3.4 Complexity</h3>
          125  +
          126  +Git is a complex system.  It can be tricky to use and requires a fair
          127  +amount of knowledge and experience to master.  Fossil strives to be
          128  +a much simpler system that can be learned and mastered much more quickly.
          129  +Fossil strives to have fewer "gotchas" and quirks that can trip up a
          130  +developer.
          131  +
          132  +The ideal VCS should just get out of the way of the developer and allow
          133  +the developer to focus 100% of their thinking on the project under
          134  +development.  One should not have to stop and think about how to operate
          135  +the VCS.  Of course, no VCS is ideal.  Every VCS requires the developer
          136  +to think about version control to some extent.  But one wants to minimize
          137  +the thinking about version control.
          138  +
          139  +Git requires the developer to maintain a more complex mental model than
          140  +most other DVCSes.  Git takes longer to learn.  And you have to spend
          141  +more time thinking about what you are doing with Git.  
          142  +
          143  +Fossil strives for simplicity.  Fossil wants to be easy to learn and to
          144  +require little thinking about how to operating it.  Reports from the
          145  +field indicate that Fossil is mostly successful at this effort.
          146  +
          147  +<h3>3.5 Web Interface</h3>
          148  +
          149  +Git has a web interface, but it requires a fair amount of setup and an
          150  +external web server.  Fossil comes with a fully functional
          151  +[./webui.wiki | built-in web-server]
          152  +and a really simple mechanism (the "<tt>fossil ui</tt>" command) to
          153  +automatically start the web server and bring up a web browser to navigate
          154  +it.  The web interface for Fossil is not only easier to set up, it is also
          155  +more powerful and easier to use.  The web interface to Fossil is a practical
          156  +replacement to the 3rd-party "GUI Tools" that users often employ to operate
          157  +Git.
          158  +
          159  +<h3>3.6 Implementation Strategy</h3>
          160  +
          161  +Git consists of a collection of many little programs.  Git needs to be
          162  +"installed" using some kind of installer or package tool.  Git can be
          163  +tricky to install and get working, especially for users without
          164  +administrative privileges.
          165  +
          166  +Fossil is a single self-contained executable.  To "install" Fossil one
          167  +has merely to download a precompiled binary and place that binary
          168  +somewhere on $PATH.  To uninstall Fossil, simply delete the binary.
          169  +To upgrade Fossil, replace the old binary with a new one.
          170  +
          171  +Fossil is designed to be trivial to install, uninstall, and upgrade so
          172  +that developers can spend more time working on their own projects and
          173  +much less time configuring their version control system.
          174  +
          175  +<h3>3.7 Repository Storage</h3>
          176  +
          177  +A Git repository is a "pile-of-files" in the ".git" directory at the
          178  +root of the working checkout.  There is a one-to-one correspondence
          179  +between repositories and working checkouts.  A power-loss or system crash
          180  +in the middle of Git operation can damage or corrupt the Git repository.
          181  +
          182  +A Fossil repository consists of a single disk file.  A single Fossil
          183  +repository can serve multiple simultaneous working checkouts.
          184  +A Fossil repository is an SQLite database, so it highly resistant
          185  +to damage from a power-loss or system crash - incomplete transactions
          186  +are simply rolled back after the system reboots.
          187  +
          188  +<h3>3.8 Audit Trail</h3>
          189  +
          190  +Git features the "rebase" command which can be used to change the
          191  +sequence of check-ins in the repository.  Rebase can be used to "clean up"
          192  +a complex sequence of check-ins to make their intent easier for others
          193  +to understand.  From another point of view, rebase can be used to
          194  +"rewrite history" - to do what
          195  +[http://en.wikipedia.org/wiki/Winston_Smith | Winston Smith] did for
          196  +a living in Orwell's novel
          197  +[http://en.wikipedia.org/wiki/Nineteen_Eighty-Four | 1984].
          198  +
          199  +Fossil deliberately avoids rewriting history.  Fossil strives to follow
          200  +the accountants philosophy of never erasing anything.  Mistakes are fixed
          201  +by entering a correction, with an explanation of why the correction is
          202  +needed.  This can make the history of a project messy, but it also
          203  +makes it more honest.  The lack of a "rebase" function is considered
          204  +a feature of Fossil, not a bug.
          205  +
          206  +<h3>3.9 License</h3>
          207  +
          208  +Both Git and Fossil are open-source.  Git is under 
          209  +[http://www.gnu.org/licenses/gpl.html | GPL] whereas Fossil is
          210  +under the 
          211  +[http://en.wikipedia.org/wiki/BSD_licenses | two-clause BSD license].
          212  +The difference should not be of a concern to most users.  However,
          213  +some corporate lawyers have objections to using GPL products and
          214  +are more comfortable with a BSD-style license.

Changes to www/fossil_logo_small.gif.

cannot compute difference between binary files

Changes to www/index.wiki.

     1         -<title>Fossil Home Page</title>
            1  +<title>Fossil</title>
     2      2   
     3      3   
     4      4   <p align="center">
     5         -<font size="5">
     6         -<b>Fossil:</b>
            5  +<font size="3">
     7      6   <i>Simple, high-reliability, distributed software configuration management</i>
     8      7   </font>
     9      8   </p>
    10      9   
    11     10   
    12     11   <h3>Why Use Fossil?</h3>
    13     12   
    14         -<table border="0" cellpadding="1" bgcolor="#558195" align="right" hspace="10">
    15         -<tr><td>
           13  +<table border="0" cellspacing="10" bgcolor="white" align="right"
           14  +cellpadding="2">
           15  +<tr><td bgcolor="#446979">
    16     16   <table border="0" cellpadding="10" bgcolor="white">
    17     17   <tr><td>
    18         -<h3>Quick Links</h3>
    19     18   <ul>
           19  +<li> [http://www.fossil-scm.org/download.html | Download]
    20     20   <li> [./quickstart.wiki | Quick Start]
    21         -<li> [http://www.fossil-scm.org/download.html | Download]
    22     21   <li> [./build.wiki | Install]
    23         -<li> [../COPYRIGHT-GPL2.txt | License]
           22  +<li> [../COPYRIGHT-BSD2.txt | License]
    24     23   <li> [/timeline | Recent changes]
    25     24   <li> [./faq.wiki | FAQ]
           25  +<li> [./contribute.wiki | Contributing]
           26  +<li> [./changes.wiki | Change Log]
           27  +<li> [./permutedindex.wiki#pindex | Doc Index]
           28  +<li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
    26     29   <li> Mailing list
    27     30        <ul>
    28     31        <li> [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | sign-up]
    29     32        <li> [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives]
    30     33        <ul>
    31     34   </ul>
    32     35   </td></tr>
................................................................................
    38     41   
    39     42   There are plenty of open-source version control systems available on the
    40     43   internet these days. What makes Fossil worthy of attention?
    41     44   
    42     45     1.  <b>Bug Tracking And Wiki</b> -
    43     46         In addition to doing [./concepts.wiki | distributed version control]
    44     47         like Git and Mercurial,
    45         -      Fossil also supports [./bugtheory.wiki | distributed bug tracking] and
    46         -      [./wikitheory.wiki | distributed wiki] all in a single
           48  +      Fossil also supports [./bugtheory.wiki | distributed bug tracking],
           49  +      [./wikitheory.wiki | distributed wiki], and a
           50  +      [./event.wiki | distributed blog] mechanism all in a single
    47     51         integrated package.
    48     52   
    49     53     2.  <b>Web Interface</b> - 
    50     54         Fossil has a built-in and easy-to-use [./webui.wiki | web interface]
    51     55         that simplifies project tracking and promotes situational awareness.
    52     56         Simply type "fossil&nbsp;ui" from within any check-out and Fossil
    53     57         automatically opens your web browser in a page that gives detailed
    54         -      history and status information on that project.
           58  +      [/timeline?n=100&y=ci | graphical history] and status information
           59  +      on that project.
           60  +
           61  +      This entire website (except the
           62  +      [http://www.fossil-scm.org/download.html | download] page)
           63  +      is just a running instance of Fossil.  The pages you see here
           64  +      are all [./wikitheory.wiki | wiki] or
           65  +      [./embeddeddoc.wiki | embedded documentation].
           66  +      When you clone Fossil from one of its
           67  +      [./selfhost.wiki | self-hosting repositories], 
           68  +      you get more than just source code - you get this entire website.
    55     69   
    56     70     3.  <b>Autosync</b> -
    57     71         Fossil supports [./concepts.wiki#workflow | "autosync" mode]
    58     72         which helps to keep projects moving
    59     73         forward by reducing the amount of needless 
    60     74         [./branching.wiki | forking and merging] often
    61     75         associated with distributed projects.
................................................................................
    82     96         No server is required to use fossil.  But a
    83     97         server does make collaboration easier.  Fossil supports three different
    84     98         yet simple [./quickstart.wiki#serversetup | server configurations].
    85     99         The most popular is a 2-line CGI script.  This is the approach
    86    100         used by the [./selfhost.wiki | self-hosting fossil repositories].
    87    101   
    88    102     7.  <b>Robust &amp; Reliable</b> -
    89         -      Fossil stores content in an SQLite database so that transactions are
          103  +      Fossil stores content using an [./fileformat.wiki | enduring file format]
          104  +      in an SQLite database so that transactions are
    90    105         atomic even if interrupted by a power loss or system crash.  Furthermore,
    91    106         automatic [./selfcheck.wiki | self-checks] verify that all aspects of
    92         -      the repository are consistent prior to each commit.  In over two years
          107  +      the repository are consistent prior to each commit.  In over three years
    93    108         of operation, no work has ever been lost after having been committed to
    94    109         a Fossil repository.
    95    110   
    96    111   <hr>
    97    112   <h3>Links For Fossil Users:</h3>
    98    113   
    99         -  *  [./reviews.wiki | Testimonials] from satisfied fossil users.
          114  +  *  [./reviews.wiki | Testimonials] from satisfied fossil users and
          115  +     [./quotes.wiki | Quotes] about Fossil and other DVCSes.
   100    116     *  [./faq.wiki | FAQ]
   101    117     *  The [./concepts.wiki | concepts] behind fossil
   102    118     *  [./quickstart.wiki | Quick Start] guide to using fossil
   103    119     *  [./qandc.wiki | Questions &amp; Criticisms] directed at fossil.
   104         -  *  [./build.wiki | Building And Installing]
          120  +  *  [./build.wiki | Compiling and Installing]
   105    121     *  Fossil supports [./embeddeddoc.wiki | embedded documentation]
   106    122        that is versioned along with project source code.
   107    123     *  Fossil uses an [./fileformat.wiki | enduring file format] that is 
   108    124        designed to be readable, searchable, and extensible by people
   109    125        not yet born.
   110    126     *  A tutorial on [./branching.wiki | branching], what it means and how
   111    127        to do it using fossil.
   112    128     *  The [./selfcheck.wiki | automatic self-check] mechanism
   113    129        helps insure project integrity.
   114    130     *  Fossil contains a [./wikitheory.wiki | built-in wiki].
          131  +  *  An [./event.wiki | Event] is a special kind of wiki page associated
          132  +     with a point in time rather than a name.
          133  +  *  [./settings.wiki | Settings] control the behaviour of fossil.
          134  +  *  [./ssl.wiki | Use SSL] to encrypt communication with the server.
   115    135     *  There is a
   116         -    [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publically readable
          136  +    [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable
   117    137        [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives]
   118    138        available for discussing fossil issues.
   119    139     *  [./stats.wiki | Performance statistics] taken from real-world projects
   120    140        hosted on fossil.
   121    141     *  How to [./shunning.wiki | delete content] from a fossil repository.
   122    142     *  How Fossil does [./password.wiki | password management].
   123         -  *  Some (unfinished but expanding) extended
   124         -      [./reference.wiki | reference documentation] for the fossil command line.
          143  +  *  On-line [/help | help].
   125    144     *  Documentation on the
   126    145        [http://www.sqliteconcepts.org/THManual.pdf | TH1 Script Language] used
   127    146        to configure the ticketing subsystem.
          147  +  *  A free hosting server for Fossil repositories is available at
          148  +     [http://chiselapp.com/].
   128    149     *  How to [./server.wiki | set up a server] for your repository.
   129    150     *  Customizing the [./custom_ticket.wiki | ticket system].
          151  +  *  Methods to [./checkin_names.wiki | identify a specific check-in].
          152  +  *  [./inout.wiki | Import and export] from and to Git.
          153  +  *  [./fossil-v-git.wiki | Fossil versus Git].
          154  +  *  [./fiveminutes.wiki | Up and running in 5 minutes as a single user]
          155  +     (contributed by Gilles Ganault on 2013-01-08).
   130    156   
   131    157   <h3>Links For Fossil Developer:</h3>
   132    158   
          159  +  *  [./contribute.wiki | Contributing] code or documentation to the
          160  +     Fossil project.
   133    161     *  [./theory1.wiki | Thoughts On The Design Of Fossil].
   134    162     *  [./pop.wiki | Principles Of Operation]
          163  +  *  [./tech_overview.wiki | A Technical Overview Of Fossil].
   135    164     *  The [./fileformat.wiki | file format] used by every content
   136    165        file stored in the repository.
   137    166     *  The [./delta_format.wiki | format of deltas] used to
   138    167        efficiently store changes between file revisions.
   139    168     *  The [./delta_encoder_algorithm.wiki | encoder algorithm] used to
   140    169        efficiently generate deltas.
   141    170     *  The [./sync.wiki | synchronization protocol].

Added www/inout.wiki.

            1  +<title>Import And Export</title>
            2  +
            3  +Fossil has the ability to import and export repositories from and to 
            4  +[http://git-scm.com/ | Git].  And since most other version control
            5  +systems will also import/export from Git, that means that you can 
            6  +import/export a Fossil repository to most version control systems using
            7  +Git as an intermediary.
            8  +
            9  +<h2>Git → Fossil</h2>
           10  +
           11  +To import a Git repository into Fossil, run commands like this:
           12  +
           13  +<blockquote><pre>
           14  +cd git-repo
           15  +git fast-export --all | fossil import --git new-repo.fossil
           16  +</pre></blockquote>
           17  +
           18  +In other words, simply pipe the output of the "git fast-export" command
           19  +into the "fossil import --git" command.  The 3rd argument to the "fossil import"
           20  +command is the name of a new Fossil repository that is created to hold the Git
           21  +content.
           22  +
           23  +The --git option is not actually required.  The git-fast-export file format
           24  +is currently the only VCS interchange format that Fossil understands.  But
           25  +future versions of Fossil might be enhanced to understand other VCS 
           26  +interchange formats, and so for compatibility, use of the
           27  +--git option is recommended.
           28  +
           29  +<h2>Fossil → Git</h2>
           30  +
           31  +To convert a Fossil repository into a Git repository, run commands like
           32  +this:
           33  +
           34  +<blockquote><pre>
           35  +git init new-repo
           36  +cd new-repo
           37  +fossil export --git ../repo.fossil | git fast-import
           38  +</pre></blockquote>
           39  +
           40  +In other words, create a new Git repository, then pipe the output from the
           41  +"fossil export --git" command into the "git fast-import" command.
           42  +
           43  +Note that the "fossil export --git" command only exports the versioned files.
           44  +Tickets and wiki and events are not exported, since Git does not understand
           45  +those concepts.
           46  +
           47  +As with the "import" command, the --git option is not required
           48  +since the git-fast-export file format is currently the only VCS interchange 
           49  +format that Fossil will generate.  However,
           50  +future versions of Fossil might add the ability to generate other
           51  +VCS interchange formats, and so for compatibility, the use of the --git 
           52  +option recommented.

Added www/makefile.wiki.

            1  +<title>The Fossil Build Process</title>
            2  +
            3  +<h1>1.0 Introduction</h1>
            4  +
            5  +The build process for Fossil is tricky in that the source code
            6  +needs to be processed by three different preprocessor programs
            7  +before it is compiled.  Most users will download a 
            8  +[http://www.fossil-scm.org/download.html | precompiled binary]
            9  +so this is of no consequence to them, and even those who
           10  +want to compile the code themselves can use one of the 
           11  +[./build.wiki | existing makefiles].  
           12  +So must people do not need to be concerned with the
           13  +build complexities of Fossil.  But hard-core developers who desire
           14  +a deep understanding of how Fossil is put together can benefit
           15  +from reviewing this article.
           16  +
           17  +<h1>2.0 Source Code Tour</h1>
           18  +
           19  +The source code for Fossil is found in the 
           20  +[/dir?ci=trunk&name=src | src/] subdirectory of the
           21  +source tree.  The src/ subdirectory contains all code, including
           22  +the code for the separate preprocessor programs.
           23  +
           24  +Each preprocessor program is a separate C program implemented in
           25  +a single file of C source code.  The three preprocessor programs
           26  +are:
           27  +
           28  +  1.  mkindex.c
           29  +  2.  translate.c
           30  +  3.  makeheaders.c
           31  +
           32  +Fossil makes use of [http://www.sqlite.org/ | SQLite] for on-disk
           33  +storage.  The SQLite implementation is contained in three source
           34  +code files that do not participate in the preprocessing steps.
           35  +These three files that implement SQLite are:
           36  +
           37  +  4.  sqlite3.c
           38  +  5.  sqlite3.h
           39  +  6.  shell.c
           40  +
           41  +The sqlite3.c and sqlite3.h source files are byte-for-byte copies of a
           42  +standard [http://www.sqlite.org/amalgamation.html | amalgamation].
           43  +The shell.c source file is code for the SQLite
           44  +[http://www.sqlite.org/sqlite.html | command-line shell] that is used
           45  +to help implement the [/help/sqlite3 | fossil sql] command.  The
           46  +shell.c source file has been modified slightly from the standard
           47  +shell.c file in the SQLite release.  Search for "Fossil Patch" in
           48  +the shell.c source file of Fossil to see the changes.
           49  +
           50  +The TH1 script engine is implemented using files:
           51  +
           52  +  7.  th.c
           53  +  8.  th.h
           54  +
           55  +These two files are imports like the SQLite source files, 
           56  +and so are not preprocessed.
           57  +
           58  +The VERSION.h header file is generated from other information sources
           59  +using a small program called:
           60  +
           61  +  9.  mkversion.c
           62  +
           63  +The src/ subdirectory also contains documentation about the
           64  +makeheaders preprocessor program:
           65  +
           66  +  10.  [../src/makeheaders.html | makeheaders.html]
           67  +
           68  +Click on the link to read this documentation.  In addition there is
           69  +a [http://www.tcl.tk/ | Tcl] script used to build the various makefiles:
           70  +
           71  +  11.  makemake.tcl
           72  +
           73  +Running this Tcl script will automatically regenerate all makefiles.
           74  +In order to add a new source file to the Fossil implementation, simply
           75  +edit makemake.tcl to add the new filename, then rerun the script, and
           76  +all of the makefiles for all targets will be rebuild.
           77  +
           78  +Finally, there is one of the makefiles generated by makemake.tcl:
           79  +
           80  +  12.  main.mk
           81  +
           82  +The main.mk makefile is invoked from the Makefile in the top-level
           83  +directory.  The main.mk is generated by makemake.tcl and should not
           84  +be hand edited.  Other makefiles generated by makemake.tcl are in 
           85  +other subdirectories (currently all in the win/ subdirectory).
           86  +
           87  +All the other files in the src/ subdirectory (79 files at the time of
           88  +this writing) are C source code files that are subject to the
           89  +preprocessing steps described below.  In the sequel, we will call these
           90  +other files "src.c" in order to have a convenient name.   The reader
           91  +should understand that whenever "src.c" or "src.h" is used in the text
           92  +that follows, we really mean all (79) other source files other than
           93  +the exceptions described above.
           94  +
           95  +<h1>3.0 Automatically generated files</h1>
           96  +
           97  +The "VERSION.h" header file contains some C preprocessor macros that
           98  +identify the version of Fossil that is to be build.  The VERSION.h file is
           99  +generated automatically from information extracted from the "manifest",
          100  +"manifest.uuid", and "VERSION" source files in the root directory of the
          101  +source tree.
          102  +(The "manifest" and "manifest.uuid" files are automatically generated and
          103  +updated by Fossil itself.  See the [/help/setting | fossil set manifest]
          104  +command for additional information.)
          105  +
          106  +The VERSION.h header file is generated by
          107  +a C program: src/mkversion.c.  
          108  +To run the VERSION.h generator, first compile the src/mkversion.c
          109  + source file into a command-line program (named "mkversion.exe")
          110  +than run:
          111  +
          112  +<blockquote><pre>
          113  +mkversion.exe manifest.uuid manifest VERSION &gt;VERSION.h
          114  +</pre></blockquote>
          115  +
          116  +The pathnames in the above command might need to be adjusted to get the
          117  +directories right.  The point is that the manifest.uuid, manifest, and
          118  +VERSION files
          119  +in the root of the source tree are the three arguments and
          120  +the generated VERSION.h file appears on standard output.
          121  +
          122  +<h1>4.0 Preprocessing</h1>
          123  +
          124  +There are three preprocessors for the Fossil sources.  The mkindex
          125  +and translate preprocessors can be run in any order.  The makeheaders
          126  +preprocessor must be run after translate.
          127  +
          128  +<h2>4.1 The mkindex preprocessor</h2>
          129  +
          130  +The mkindex program scans the "src.c" source files looking for special
          131  +comments that identify routines that implement various Fossil commands,
          132  +web interface methods, and help text comments.  The mkindex program
          133  +generates some C code that Fossil uses in order to dispatch commands and
          134  +HTTP requests and to show on-line help.  Compile the mkindex program
          135  +from the mkindex.c source file.  Then run:
          136  +
          137  +<blockquote><pre>
          138  +./mkindex src.c >page_index.h
          139  +</pre></blockquote>
          140  +
          141  +Note that "src.c" in the above is a stand-in for the (79) regular source
          142  +files of Fossil - all source files except for the exceptions described in
          143  +section 2.0 above.
          144  +
          145  +The output of the mkindex program is a header file that is #include-ed by
          146  +the main.c source file during the final compilation step.  
          147  +
          148  +<h2>4.2 The translate preprocessor</h2>
          149  +
          150  +The translate preprocessor looks for lines of source code that begin
          151  +with "@" and converts those lines into string constants or (depending on
          152  +context) into special "printf" operations for generating the output of
          153  +an HTTP request.  The translate preprocessor is a simple C program whose
          154  +sources are in the translate.c source file.  The translate preprocess
          155  +is run on each of the other ordinary source files separately, like this:
          156  +
          157  +<blockquote><pre>
          158  +./translate src.c >src_.c
          159  +</pre></blockquote>
          160  +
          161  +In this case, the "src.c" file represents any single source file from the
          162  +set of ordinary source files as described in section 2.0 above.  Note that
          163  +each source file is translated separately.  By convention, the names of
          164  +the translated source files are the names of the input sources with a 
          165  +single "_" character at the end.  But a new makefile can use any naming
          166  +convention it wants - the "_" is not critical to the build process.
          167  +
          168  +After being translated, the output files (the "src_.c" files) should be
          169  +used for all subsequent preprocessing and compilation steps.
          170  +
          171  +<h2>4.3 The makeheaders preprocessor</h2>
          172  +
          173  +For each C source module "src.c", there is an automatically generated
          174  +header module "src.h" that contains all of the datatype and procedure
          175  +declarations needed by the source module.  These header files are generated
          176  +automatically by the makeheaders program.  The sources to makeheaders
          177  +are contained in a single file "makeheaders.c".  Additional documentation
          178  +on makeheaders can be found in [../src/makeheaders.html | src/makeheaders.html].
          179  +
          180  +The makeheaders program is run once.  It scans all inputs source files and
          181  +generates header files for each one.  Note that the sqlite3.c and shell.c
          182  +source files are not scanned by makeheaders.  Makeheaders only runs over
          183  +"ordinary" source files, not the exceptional source files.  However,
          184  +makeheaders also uses some extra header files as input.  The general format
          185  +is like this:
          186  +
          187  +<blockquote><pre>
          188  +makeheaders src_.c:src.h sqlite3.h th.h VERSION.h
          189  +</pre></blockquote>
          190  +
          191  +In the example above the "src_.c" and "src.h" names represent all of the
          192  +(79) ordinary C source files, each as a separate argument.  
          193  +
          194  +<h1>5.0 Compilation</h1>
          195  +
          196  +After all generated files have been created and all ordinary source files
          197  +have been preprocessed, the generated and preprocessed files can be 
          198  +combined into a single executable using a C compiler.  This can be done
          199  +all at once, or each preprocessed source file can be compiled into a
          200  +separate object code file and the resulting object code files linked
          201  +together in a final step.
          202  +
          203  +Some files require special C-preprocessor macro definitions.
          204  +When compiling sqlite.c, the following macros are recommended:
          205  +
          206  +  *  -Dlocaltime=fossil_localtime
          207  +  *  -DSQLITE_OMIT_LOAD_EXTENSION=1
          208  +  *  -DSQLITE_ENABLE_LOCKING_STYLE=0
          209  +  *  -DSQLITE_THREADSAFE=0
          210  +  *  -DSQLITE_DEFAULT_FILE_FORMAT=4
          211  +  *  -DSQLITE_ENABLE_STAT2
          212  +
          213  +The first and second symbol definitions above are required; the others
          214  +are merely recommended.  The "localtime()" library function in SQLite must
          215  +be redefined to invoke fossil_localtime() instead.  The fossil_localtime()
          216  +routine will invoke either gmtime() or localtime() depending on the 
          217  +"Use UTC" setting for the fossil repository.  Extension loading is omitted
          218  +as a security measure.  Fossil is single-threaded so mutexing is disabled
          219  +in SQLite as a performance enhancement.
          220  +
          221  +When compiling the shell.c source file, these macros are required:
          222  +
          223  +  *  -Dmain=sqlite3_main
          224  +  *  -DSQLITE_OMIT_LOAD_EXTENSION=1
          225  +
          226  +The "main()" routine in the shell must be changed into sqlite3_main()
          227  +to prevent it from colliding with the real main() in Fossil, and to give
          228  +Fossil an entry point to jump to when the 
          229  +[/help/sqlite3 | fossil sql] command is invoked.
          230  +
          231  +All the other source code files can be compiled without any special
          232  +options.
          233  +
          234  +<h1>6.0 Linkage</h1>
          235  +
          236  +Fossil needs to be linked against [http://www.zlib.net | zlib].  If
          237  +the HTTPS option is enabled, then it will also need to link against
          238  +the appropriate SSL implementation.  And, of course, Fossil needs to
          239  +link against the standard C library.  No other libraries or external
          240  +dependences are used.

Changes to www/mkdownload.tcl.

     1      1   #!/usr/bin/tclsh
     2      2   #
     3         -# Run this script to build the "download" page on standard output.
            3  +# Run this script to build the "download.html" page.  Also generate
            4  +# the fossil_download_checksums.html page.
     4      5   #
     5      6   #
     6         -puts \
     7         -{<html>
            7  +set out [open download.html w]
            8  +fconfigure $out -encoding utf-8 -translation lf
            9  +puts $out \
           10  +{<!DOCTYPE html><html>
     8     11   <head>
     9         -<title>Fossil: Downloads</title>
           12  +<base href="http://www.fossil-scm.org/" />
           13  +<title>Fossil: Timeline</title>
    10     14   <link rel="stylesheet" href="/fossil/style.css" type="text/css"
    11     15         media="screen">
    12     16   </head>
    13     17   <body>
    14     18   <div class="header">
    15     19     <div class="logo">
    16         -    <img src="/fossil/doc/tip/www/fossil_logo_small.gif" alt="logo">
           20  +    <img src="/fossil/logo" alt="logo">
           21  +    <br /><nobr>Fossil</nobr>
    17     22     </div>
           23  +
    18     24     <div class="title">Fossil Downloads</div>
    19     25   </div>
    20         -<div class="mainmenu"><a href='/fossil/doc/tip/www/index.wiki'>Home</a><a href='/fossil/leaves'>Leaves</a><a href='/fossil/timeline'>Timeline</a><a href='/fossil/brlist'>Branches</a><a href='/fossil/taglist'>Tags</a><a href='/fossil/reportlist'>Tickets</a><a href='/fossil/wiki'>Wiki</a><a href='/fossil/login'>Login</a></div>
           26  +<div class="mainmenu">
           27  +<a href='/fossil/doc/trunk/www/index.wiki'>Home</a>
           28  +<a href='/fossil/timeline?y=ci'>Timeline</a>
           29  +<a href='/download.html'>Download</a>
           30  +<a href='/fossil/dir?ci=trunk'>Code</a>
           31  +<a href='/fossil/doc/trunk/www/permutedindex.wiki'>Documentation</a>
           32  +<a href='/fossil/brlist'>Branches</a>
           33  +<a href='/fossil/taglist'>Tags</a>
           34  +<a href='/fossil/reportlist'>Tickets</a>
           35  +</div>
    21     36   <div class="content">
    22     37   <p>
    23     38   
    24         -<p>
    25         -Click on links below to download prebuilt binaries and source tarballs for 
    26         -recent versions of <a href="/fossil">Fossil</a>.
    27         -The historical source code is also available in the
    28         -<a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting
    29         -Fossil repositories</a>.
    30         -</p>
    31         -
    32         -<p>
    33         -<u>Important Note:</u>
    34         -After upgrading to a newer version of fossil, it is always a good idea
    35         -to run:
    36         -<blockquote><pre>
    37         -<b><big><tt>fossil all rebuild</tt></big></b>
    38         -</pre></blockquote>
    39         -Running "rebuild" this way is not always necessary, but it never hurts.
    40         -</p>
           39  +<center><font size=4>}
           40  +puts $out \
           41  +"<b>To install Fossil \u2192</b> download the stand-alone executable"
           42  +puts $out \
           43  +{and put it on your $PATH.
           44  +</font><p><small>
           45  +RPMs available
           46  +<a href="http://download.opensuse.org/repositories/home:/rmax:/fossil/">
           47  +here.</a>
           48  +Cryptographic checksums for download files are
           49  +<a href="http://www.hwaci.com/fossil_download_checksums.html">here</a>.
           50  +</small></p>
           51  +</center>
    41     52   
    42     53   <table cellpadding="10">
    43     54   }
    44     55   
    45     56   # Find all all unique timestamps.
    46     57   #
    47     58   foreach file [glob -nocomplain download/fossil-*.zip] {
    48     59     if {[regexp {(\d+).zip$} $file all datetime]
    49         -       && [string length $datetime]==14} {
           60  +       && [string length $datetime]>=14} {
    50     61       set adate($datetime) 1
    51     62     }
    52     63   }
    53     64   
    54     65   # Do all dates from newest to oldest
    55     66   #
    56     67   foreach datetime [lsort -decr [array names adate]] {
    57     68     set dt [string range $datetime 0 3]-[string range $datetime 4 5]-
    58     69     append dt "[string range $datetime 6 7] "
    59     70     append dt "[string range $datetime 8 9]:[string range $datetime 10 11]:"
    60     71     append dt "[string range $datetime 12 13]"
    61     72     set link [string map {{ } +} $dt]
    62         -  set hr http://www.fossil-scm.org/fossil/timeline?c=$link&y=ci
    63         -  puts "<tr><td colspan=5 align=center><hr>"
    64         -  puts "<b>Fossil snapshot as of <a href=\"$hr\">$dt</a><td width=30></b>"
    65         -  puts "</td></tr>"
    66         -  
           73  +  set hr http://www.fossil-scm.org/fossil/timeline?c=$link&amp;y=ci
           74  +  puts $out "<tr><td colspan=6 align=left><hr>"
           75  +  puts $out "<center><b><a href=\"$hr\">$dt</a></b></center>"
           76  +  puts $out "</td></tr>"
           77  +  puts $out "<tr>"
           78  +
    67     79     foreach {prefix suffix img desc} {
    68     80       fossil-linux-x86 zip linux.gif {Linux x86}
    69         -    fossil-linux-amd64 zip linux64.gif {Linux x86_64}
    70     81       fossil-macosx-x86 zip mac.gif {Mac 10.5 x86}
           82  +    fossil-openbsd-x86 zip openbsd.gif {OpenBSD 4.7 x86}
    71     83       fossil-w32 zip win32.gif {Windows}
    72     84       fossil-src tar.gz src.gif {Source Tarball}
    73     85     } {
    74     86       set filename download/$prefix-$datetime.$suffix
    75     87       if {[file exists $filename]} {
    76     88         set size [file size $filename]
    77     89         set units bytes
................................................................................
    78     90         if {$size>1024*1024} {
    79     91           set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
    80     92           set units MiB
    81     93         } elseif {$size>1024} {
    82     94           set size [format %.2f [expr {$size/(1024.0)}]]
    83     95           set units KiB
    84     96         }
    85         -      puts "<td align=center valign=bottom><a href=\"$filename\">"
    86         -      puts "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>"
    87         -      puts "$size $units</td>"
           97  +      puts $out "<td align=center valign=bottom><a href=\"$filename\">"
           98  +      puts $out "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>"
           99  +      puts $out "$size $units</td>"
    88    100       } else {
    89         -      puts "<td>&nbsp;</td>"
          101  +      puts $out "<td>&nbsp;</td>"
    90    102       }
    91    103     }
    92         -  puts "</tr>"
          104  +  puts $out "</tr>"
          105  +  if {[file exists download/releasenotes-$datetime.html]} {
          106  +    puts $out "<tr><td colspan=6 align=left>"
          107  +    set rn [open download/releasenotes-$datetime.html]
          108  +    fconfigure $rn -encoding utf-8
          109  +    puts $out "[read $rn]"
          110  +    close $rn
          111  +    puts $out "</td></tr>"
          112  +  }
    93    113   }
    94         -puts "<tr><td colspan=5><hr></td></tr>"
          114  +puts $out "<tr><td colspan=5><hr></td></tr>"
    95    115   
    96         -puts {</table>
          116  +puts $out {</table>
    97    117   </body>
    98    118   </html>
    99    119   }
          120  +
          121  +close $out
          122  +
          123  +# Generate the checksum page
          124  +#
          125  +set out [open fossil_download_checksums.html w]
          126  +fconfigure $out -encoding utf-8 -translation lf
          127  +puts $out {<html>
          128  +<title>Fossil Download Checksums</title>
          129  +<body>
          130  +<h1 align="center">Checksums For Fossil Downloads</h1>
          131  +<p>The following table shows the SHA1 checksums for the precompiled
          132  +binaries available on the
          133  +<a href="http://www.fossil-scm.org/download.html">Fossil website</a>.</p>
          134  +<pre>}
          135  +
          136  +foreach file [lsort [glob -nocomplain download/fossil-*.zip]] {
          137  +  set sha1sum [lindex [exec sha1sum $file] 0]
          138  +  puts $out "$sha1sum   [file tail $file]"
          139  +}
          140  +puts $out {</pre></body></html>}
          141  +close $out

Added www/mkindex.tcl.

            1  +#!/bin/sh
            2  +#
            3  +# Run this TCL script to generate a WIKI page that contains a 
            4  +# permuted index of the various documentation files.
            5  +#
            6  +#    tclsh mkindex.tcl >permutedindex.wiki
            7  +#
            8  +
            9  +set doclist {
           10  +  bugtheory.wiki {Bug Tracking In Fossil}
           11  +  branching.wiki {Branching, Forking, Merging, and Tagging}
           12  +  build.wiki {Compiling and Installing Fossil}
           13  +  checkin_names.wiki {Checkin And Version Names}
           14  +  checkin.wiki {Check-in Checklist}
           15  +  changes.wiki {Fossil Changelog}
           16  +  copyright-release.html {Contributor License Agreement}
           17  +  concepts.wiki {Fossil Core Concepts}
           18  +  contribute.wiki {Contributing Code or Documentation To The Fossil Project}
           19  +  custom_ticket.wiki {Customizing The Ticket System}
           20  +  delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm}
           21  +  delta_format.wiki {Fossil Delta Format}
           22  +  embeddeddoc.wiki {Embedded Project Documentation}
           23  +  event.wiki {Events}
           24  +  faq.wiki {Frequently Asked Questions}
           25  +  fileformat.wiki {Fossil File Format}
           26  +  fiveminutes.wiki {Update and Running in 5 Minutes as a Single User}
           27  +  foss-cklist.wiki {Checklist For Successful Open-Source Projects}
           28  +  fossil-v-git.wiki {Fossil Versus Git}
           29  +  index.wiki {Home Page}
           30  +  inout.wiki {Import And Export To And From Git}
           31  +  makefile.wiki {The Fossil Build Process}
           32  +  newrepo.wiki {How To Create A New Fossil Repository}
           33  +  password.wiki {Password Management And Authentication}
           34  +  pop.wiki {Principles Of Operations}
           35  +  private.wiki {Creating, Syncing, and Deleting Private Branches}
           36  +  qandc.wiki {Questions And Criticisms}
           37  +  quickstart.wiki {Fossil Quick Start Guide}
           38  +  quotes.wiki
           39  +      {Quotes: What People Are Saying About Fossil, Git, and DVCSes in General}
           40  +  ../test/release-checklist.wiki {Pre-Release Testing Checklist}
           41  +  reviews.wiki {Reviews}
           42  +  selfcheck.wiki {Fossil Repository Integrity Self Checks}
           43  +  selfhost.wiki {Fossil Self Hosting Repositories}
           44  +  server.wiki {How To Configure A Fossil Server}
           45  +  settings.wiki {Fossil Settings}
           46  +  shunning.wiki {Shunning: Deleting Content From Fossil}
           47  +  stats.wiki {Performance Statistics}
           48  +  style.wiki {Source Code Style Guidelines}
           49  +  ssl.wiki {Using SSL with Fossil}
           50  +  sync.wiki {The Fossil Sync Protocol}
           51  +  tech_overview.wiki {A Technical Overview Of The Design And Implementation
           52  +                      Of Fossil}
           53  +  tech_overview.wiki {SQLite Databases Used By Fossil}
           54  +  tickets.wiki {The Fossil Ticket System}
           55  +  theory1.wiki {Thoughts On The Design Of The Fossil DVCS}
           56  +  webui.wiki {The Fossil Web Interface}
           57  +  wikitheory.wiki {Wiki In Fossil}
           58  +}
           59  +
           60  +set permindex {}
           61  +set stopwords {fossil and a in of on the to are about used by for or}
           62  +foreach {file title} $doclist {
           63  +  set n [llength $title]
           64  +  regsub -all {\s+} $title { } title
           65  +  lappend permindex [list $title $file]
           66  +  for {set i 0} {$i<$n-1} {incr i} {
           67  +    set prefix [lrange $title 0 $i]
           68  +    set suffix [lrange $title [expr {$i+1}] end]
           69  +    set firstword [string tolower [lindex $suffix 0]]
           70  +    if {[lsearch $stopwords $firstword]<0} {
           71  +      lappend permindex [list "$suffix &mdash; $prefix" $file]
           72  +    }
           73  +  }
           74  +}
           75  +set permindex [lsort -dict -index 0 $permindex]
           76  +set out [open permutedindex.wiki w]
           77  +fconfigure $out -encoding utf-8 -translation lf
           78  +puts $out "<title>Index Of Fossil Documentation</title>"
           79  +puts $out {
           80  +<h2>Primary Documents:</h2>
           81  +<ul>
           82  +<li> [./quickstart.wiki | Quick-start Guide]
           83  +<li> [./faq.wiki | FAQ]
           84  +<li> [./build.wiki | Compiling and installing Fossil]
           85  +<li> [../COPYRIGHT-BSD2.txt | License]
           86  +<li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
           87  +<li> [/help | Command-line help]
           88  +</ul>
           89  +<a name="pindex"></a>
           90  +<h2>Permuted Index:</h2>
           91  +<ul>}
           92  +foreach entry $permindex {
           93  +  foreach {title file} $entry break
           94  +  puts $out "<li><a href=\"$file\">$title</a></li>"
           95  +}
           96  +puts $out "</ul>"

Changes to www/newrepo.wiki.

     1         -<h1>HOWTO: creating a new repository</h1>
            1  +<title>How To Create A New Fossil Repository</title>
     2      2   
     3      3   <p> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how
     4      4   to get up and running with fossil. But once you're running, what can
     5      5   you do with it? This document will walk you through the process of
     6      6   creating a fossil repository, populating it with files, and then
     7      7   sharing it over the web.</p>
     8      8   
................................................................................
    30     30   </verbatim>
    31     31   
    32     32   The <tt>ui</tt> command starts up a server (with an optional <tt>-port
    33     33   NUMBER</tt> argument) and launches a web browser pointing at the
    34     34   fossil server. From there it takes just a few moments to configure the
    35     35   repo. Most importantly, go to the Admin menu, then the Users link, and
    36     36   set your account name and password, and grant your account all access
    37         -priviledges. (I also like to grant Clone access to the anonymous user,
           37  +privileges. (I also like to grant Clone access to the anonymous user,
    38     38   but that's personal preference.)
    39     39   
    40     40   Once you are done, kill the fossil server (with Ctrl-C or equivalent)
    41     41   and close the browser window.
    42     42   
    43     43   <blockquote>
    44     44   Tip: it is not strictly required to configure a repository
................................................................................
    61     61   That creates a file called <tt>_FOSSIL_</tt> in the current
    62     62   directory, and this file contains all kinds of fossil-related
    63     63   information about your local repository. You can ignore it
    64     64   for all purposes, but be sure not to accidentally remove it
    65     65   or otherwise damage it - it belongs to fossil, not you.
    66     66   
    67     67   The next thing we need to do is add files to our repository.  As it
    68         -happens, we have a few C source files laying around, which we'll
           68  +happens, we have a few C source files lying around, which we'll
    69     69   simply copy into our working directory.
    70     70   
    71     71   <verbatim>
    72     72   stephan@ludo:~/fossil/demo$ cp ../csnip/*.{c,h} .
    73     73   stephan@ludo:~/fossil/demo$ ls
    74     74   clob.c  clob.h  clobz.c  _FOSSIL_  mkdep.c  test-clob.c
    75     75   tokenize_path.c tokenize_path.h  vappendf.c  vappendf.h
    76     76   </verbatim>
    77     77   
    78     78   Fossil doesn't know about those files yet. Telling fossil about
    79     79   a new file is a two-step process. First we <em>add</em> the file
    80         -to the repo, then we <em>commit</em> the file. This is a familiar
           80  +to the repository, then we <em>commit</em> the file. This is a familiar
    81     81   process for anyone who's worked with SCM systems before:
    82     82   
    83     83   <verbatim>
    84     84   stephan@ludo:~/fossil/demo$ fossil add *.{c,h}
    85     85   stephan@ludo:~/fossil/demo$ fossil commit -m "egg"
    86     86   New_Version: d1296b4a08b9f8b943bb6c73698e51eed23f8f91
    87     87   </verbatim>
................................................................................
   157    157   command to push your local commits to the remote repository. You must
   158    158   of course have authorization to commit changes (access is configured
   159    159   via the Admin/Users page mentioned above).
   160    160   
   161    161   You may always use the <em>server</em> or <em>ui</em> commands to
   162    162   browse a cloned repository. You can even edit create or wiki entries,
   163    163   etc., and they will be pushed to the remote side the next time you
   164         -push data to the the remote.
          164  +push data to the remote.

Changes to www/password.wiki.

    58     58   all login for that user.   Thus, to lock a user out of the system,
    59     59   one has only to set their password to an empty string, using either
    60     60   the web interface or direct SQL manipulation of the USER table.
    61     61   Note also that the password field is
    62     62   essentially ignored for the special users named "anonymous", "developer",
    63     63   "reader", and "nobody".  It is not possible to authenticate as users
    64     64   "developer", "reader", or "nobody" and the authentication protocol
    65         -for "anonymous" use one-time captchas not persistent passwords.
           65  +for "anonymous" uses one-time captchas not persistent passwords.
    66     66   
    67     67   <h2>Web Interface Authentication</h2>
    68     68   
    69     69   When a user logs into Fossil using the web interface, the login name
    70     70   and password are sent in the clear to the server.  The server then
    71     71   hashes the password and compares it against the value stored in USER.PW.
    72     72   If they match, the server sets a cookie on the client to record the
    73     73   login.  This cookie contains a large amount of high-quality randomness
    74         -and is thus impossible to guess.  The value of the cookie and the IP
           74  +and is thus intractable to guess.  The value of the cookie and the IP
    75     75   address of the client is stored in the USER.COOKIE and USER.IPADDR fields
    76     76   of the USER table on the server.  
    77     77   The USER.CEXPIRE field holds an expiration date
    78     78   for the cookie, encoded as a julian day number.  On all subsequent
    79     79   HTTP requests, the cookie value is matched against the USER table to 
    80     80   enable access to the repository.
    81     81   

Added www/permutedindex.wiki.

            1  +<title>Index Of Fossil Documentation</title>
            2  +
            3  +<h2>Primary Documents:</h2>
            4  +<ul>
            5  +<li> [./quickstart.wiki | Quick-start Guide]
            6  +<li> [./faq.wiki | FAQ]
            7  +<li> [./build.wiki | Compiling and installing Fossil]
            8  +<li> [../COPYRIGHT-BSD2.txt | License]
            9  +<li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
           10  +<li> [/help | Command-line help]
           11  +</ul>
           12  +<a name="pindex"></a>
           13  +<h2>Permuted Index:</h2>
           14  +<ul>
           15  +<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Update and Running in</a></li>
           16  +<li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
           17  +<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
           18  +<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
           19  +<li><a href="fiveminutes.wiki">as a Single User &mdash; Update and Running in 5 Minutes</a></li>
           20  +<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
           21  +<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
           22  +<li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
           23  +<li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
           24  +<li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
           25  +<li><a href="makefile.wiki">Build Process &mdash; The Fossil</a></li>
           26  +<li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>
           27  +<li><a href="checkin.wiki">Check-in Checklist</a></li>
           28  +<li><a href="checkin_names.wiki">Checkin And Version Names</a></li>
           29  +<li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
           30  +<li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
           31  +<li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li>
           32  +<li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
           33  +<li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
           34  +<li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
           35  +<li><a href="build.wiki">Compiling and Installing Fossil</a></li>
           36  +<li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
           37  +<li><a href="server.wiki">Configure A Fossil Server &mdash; How To</a></li>
           38  +<li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
           39  +<li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li>
           40  +<li><a href="copyright-release.html">Contributor License Agreement</a></li>
           41  +<li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
           42  +<li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>
           43  +<li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li>
           44  +<li><a href="qandc.wiki">Criticisms &mdash; Questions And</a></li>
           45  +<li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li>
           46  +<li><a href="tech_overview.wiki">Databases Used By Fossil &mdash; SQLite</a></li>
           47  +<li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
           48  +<li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
           49  +<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
           50  +<li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
           51  +<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
           52  +<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
           53  +<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
           54  +<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
           55  +<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
           56  +<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
           57  +<li><a href="embeddeddoc.wiki">Embedded Project Documentation</a></li>
           58  +<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
           59  +<li><a href="event.wiki">Events</a></li>
           60  +<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
           61  +<li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
           62  +<li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
           63  +<li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
           64  +<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
           65  +<li><a href="changes.wiki">Fossil Changelog</a></li>
           66  +<li><a href="concepts.wiki">Fossil Core Concepts</a></li>
           67  +<li><a href="delta_encoder_algorithm.wiki">Fossil Delta Encoding Algorithm</a></li>
           68  +<li><a href="delta_format.wiki">Fossil Delta Format</a></li>
           69  +<li><a href="fileformat.wiki">Fossil File Format</a></li>
           70  +<li><a href="quickstart.wiki">Fossil Quick Start Guide</a></li>
           71  +<li><a href="selfcheck.wiki">Fossil Repository Integrity Self Checks</a></li>
           72  +<li><a href="selfhost.wiki">Fossil Self Hosting Repositories</a></li>
           73  +<li><a href="settings.wiki">Fossil Settings</a></li>
           74  +<li><a href="fossil-v-git.wiki">Fossil Versus Git</a></li>
           75  +<li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>
           76  +<li><a href="faq.wiki">Frequently Asked Questions</a></li>
           77  +<li><a href="shunning.wiki">From Fossil &mdash; Shunning: Deleting Content</a></li>
           78  +<li><a href="inout.wiki">From Git &mdash; Import And Export To And</a></li>
           79  +<li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
           80  +<li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
           81  +<li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
           82  +<li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
           83  +<li><a href="quickstart.wiki">Guide &mdash; Fossil Quick Start</a></li>
           84  +<li><a href="style.wiki">Guidelines &mdash; Source Code Style</a></li>
           85  +<li><a href="index.wiki">Home Page</a></li>
           86  +<li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
           87  +<li><a href="server.wiki">How To Configure A Fossil Server</a></li>
           88  +<li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li>
           89  +<li><a href="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
           90  +<li><a href="inout.wiki">Import And Export To And From Git</a></li>
           91  +<li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
           92  +<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
           93  +<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
           94  +<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
           95  +<li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
           96  +<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
           97  +<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Update and Running in 5</a></li>
           98  +<li><a href="checkin_names.wiki">Names &mdash; Checkin And Version</a></li>
           99  +<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
          100  +<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
          101  +<li><a href="pop.wiki">Operations &mdash; Principles Of</a></li>
          102  +<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
          103  +<li><a href="index.wiki">Page &mdash; Home</a></li>
          104  +<li><a href="password.wiki">Password Management And Authentication</a></li>
          105  +<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
          106  +<li><a href="stats.wiki">Performance Statistics</a></li>
          107  +<li><a href="../test/release-checklist.wiki">Pre-Release Testing Checklist</a></li>
          108  +<li><a href="pop.wiki">Principles Of Operations</a></li>
          109  +<li><a href="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
          110  +<li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>
          111  +<li><a href="contribute.wiki">Project &mdash; Contributing Code or Documentation To The Fossil</a></li>
          112  +<li><a href="embeddeddoc.wiki">Project Documentation &mdash; Embedded</a></li>
          113  +<li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
          114  +<li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
          115  +<li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
          116  +<li><a href="qandc.wiki">Questions And Criticisms</a></li>
          117  +<li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
          118  +<li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
          119  +<li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
          120  +<li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
          121  +<li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
          122  +<li><a href="reviews.wiki">Reviews</a></li>
          123  +<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Update and</a></li>
          124  +<li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
          125  +<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
          126  +<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
          127  +<li><a href="server.wiki">Server &mdash; How To Configure A Fossil</a></li>
          128  +<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
          129  +<li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
          130  +<li><a href="fiveminutes.wiki">Single User &mdash; Update and Running in 5 Minutes as a</a></li>
          131  +<li><a href="style.wiki">Source Code Style Guidelines</a></li>
          132  +<li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
          133  +<li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
          134  +<li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
          135  +<li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
          136  +<li><a href="style.wiki">Style Guidelines &mdash; Source Code</a></li>
          137  +<li><a href="foss-cklist.wiki">Successful Open-Source Projects &mdash; Checklist For</a></li>
          138  +<li><a href="sync.wiki">Sync Protocol &mdash; The Fossil</a></li>
          139  +<li><a href="private.wiki">Syncing, and Deleting Private Branches &mdash; Creating,</a></li>
          140  +<li><a href="custom_ticket.wiki">System &mdash; Customizing The Ticket</a></li>
          141  +<li><a href="tickets.wiki">System &mdash; The Fossil Ticket</a></li>
          142  +<li><a href="branching.wiki">Tagging &mdash; Branching, Forking, Merging, and</a></li>
          143  +<li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil &mdash; A</a></li>
          144  +<li><a href="../test/release-checklist.wiki">Testing Checklist &mdash; Pre-Release</a></li>
          145  +<li><a href="makefile.wiki">The Fossil Build Process</a></li>
          146  +<li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
          147  +<li><a href="tickets.wiki">The Fossil Ticket System</a></li>
          148  +<li><a href="webui.wiki">The Fossil Web Interface</a></li>
          149  +<li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
          150  +<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
          151  +<li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
          152  +<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
          153  +<li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li>
          154  +<li><a href="fiveminutes.wiki">User &mdash; Update and Running in 5 Minutes as a Single</a></li>
          155  +<li><a href="ssl.wiki">Using SSL with Fossil</a></li>
          156  +<li><a href="checkin_names.wiki">Version Names &mdash; Checkin And</a></li>
          157  +<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
          158  +<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
          159  +<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
          160  +<li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
          161  +<li><a href="ssl.wiki">with Fossil &mdash; Using SSL</a></li>
          162  +</ul>

Added www/private.wiki.

            1  +<title>Private Branches</title>
            2  +
            3  +By default, everything you check into a Fossil repository is shared
            4  +to all clones of that repository.  In Fossil, you don't push and pull
            5  +individual branches; you push and pull everything all at once.
            6  +
            7  +But sometimes users want to keep some private work that is not
            8  +shared with others.  This might be a preliminary or experimental change
            9  +that needs further refinement before it is shared and which might never
           10  +be shared at all.  To do this in Fossil, simply commit the change with
           11  +the --private command-line option:
           12  +
           13  +<blockquote><pre>
           14  +fossil commit --private
           15  +</pre></blockquote>
           16  +
           17  +The --private option causes Fossil to put the check-in in a new branch
           18  +named "private".  That branch will not participate in subsequent clone,
           19  +sync, push, or pull operations.  The branch will remain on the one local
           20  +repository where it was created.  Note that you only use the --private
           21  +option for the first checkin that creates the private branch.
           22  +Additional checkins into the private branch remain private automatically.
           23  +
           24  +<h2>Publishing Private Changes</h2>
           25  +
           26  +After additional work, one might desire to publish the changes associated
           27  +with a private branch.  The usual way to do this is to merge those
           28  +changes into a public branch.  For example:
           29  +
           30  +<blockquote><pre>
           31  +fossil update trunk
           32  +fossil merge private
           33  +fossil commit
           34  +</pre></blockquote>
           35  +
           36  +The private branch remains private.  (There is no way to convert a private
           37  +branch into a public branch.)  But all of the changes associated with
           38  +the private branch are now folded into the public branch and are hence
           39  +visible to other users of the project.
           40  +
           41  +<h2>Syncing Private Branches</h2>
           42  +
           43  +A private branch normally stays on the one repository where it was
           44  +originally created.  But sometimes you want to share private branches
           45  +with another repository.  For example, you might be building a cross-platform
           46  +application and have separate repositories on your windows laptop, 
           47  +your linux desktop, and your iMac.  You can transfer private branches
           48  +between these machines by using the --private option on the "sync",
           49  +"push", "pull", and "clone" commands.  For example, if you are running
           50  +"fossil server" on your linux box and you want to clone that repository
           51  +to your Mac, including all private branches, use:
           52  +
           53  +<blockquote><pre>
           54  +fossil clone --private http://user@linux.localnetwork:8080/ mac-clone.fossil
           55  +</pre></blockquote>
           56  +
           57  +You'll have to supply a username and password in order for this to work.
           58  +Fossil will not clone (or sync) private branches anonymously.  Furthermore,
           59  +you have to enable the "Private" capability (the "x" capability) in order
           60  +to enable private branch syncing.  The Admin ("a") and Superuser ("s") do
           61  +<u>not</u> imply Private ("x"); you'll have to set "x" in addition to
           62  +"a" or "s".  This is a little extra work, but there is a purpose here:
           63  +the interface is designed to make it very difficult to accidently
           64  +sync a private branch to a public server.  It is highly recommended that
           65  +you leave the "x" capability turned off on all repositories used for
           66  +collaboration (repositories to which many people push and pull) and
           67  +only enable "x" for local repositories when you need to share private
           68  +branches.
           69  +
           70  +Private branch sync only works if you use the --private command-line option.
           71  +Private branches are never synced via the auto-sync mechanism.  Once
           72  +again, this restriction is designed to make it hard to accidently 
           73  +push private branches beyond their intended audience.
           74  +
           75  +<h2>Purging Private Branches</h2>
           76  +
           77  +You can remove all private branches from a repository using this command:
           78  +
           79  +<blockquote><pre>
           80  +fossil scrub --private
           81  +</pre></blockquote>
           82  +
           83  +Note that the above is a permanent and irreversible change.  You will
           84  +be asked to confirm before continuing.  Once the private branches are
           85  +removed, they cannot be retrieved (unless you have synced them to another
           86  +repository.)  So be careful with the command.
           87  +
           88  +<h2>Additional Notes</h2>
           89  +
           90  +All of the features above apply to <u>all</u> private branches in a 
           91  +single repository at once.  There is no mechanism in Fossil (currently)
           92  +that allows you to push, pull, clone, sync, or scrub and individual
           93  +private branch within a repository that contains multiple private
           94  +branches.

Changes to www/qandc.wiki.

    36     36     <ol>
    37     37     <li> Fossil is distributed.  You can view and/or edit tickets, wiki, and
    38     38          code while off network, then sync your changes later.  With Trac, you
    39     39          can only view and edit tickets and wiki while you are connected to
    40     40          the server. </li>
    41     41     <li> Fossil is lightweight and fully self-contained.  It is very easy 
    42     42          to setup on a low-resource machine. Fossil does not require an
    43         -       administator.</li>
           43  +       administrator.</li>
    44     44     <li> Fossil integrates code versioning into the same repository with
    45     45          wiki and tickets.  There is nothing extra to add or install.
    46     46          Fossil is an all-in-one turnkey solution. </li>
    47     47     </ol>
    48     48   </blockquote>
    49     49   
    50     50   <b>Love the concept here. Anyone using this for real work yet?</b>
................................................................................
    61     61   fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>,
    62     62   for example.
    63     63   Other projects are also adopting fossil.  But fossil does not yet have
    64     64   the massive user base of git or mercurial.
    65     65   </blockquote>
    66     66   
    67     67   <b>Fossil looks like the bug tracker that would be in your 
    68         -Linksys Router's administration screen.</p>
           68  +Linksys Router's administration screen.</b>
    69     69   
    70     70   <blockquote>
    71     71   <p>I take a pragmatic approach to software: form follows function.
    72     72   To me, it is more important to have a reliable, fast, efficient,
    73     73   enduring, and simple DVCS than one that looks pretty.</p>
    74     74   
    75         -<p>On the other hand, if you have patches that improve the apparance
    76         -of Fossil without seriously compromising its reliablity, performance,
           75  +<p>On the other hand, if you have patches that improve the appearance
           76  +of Fossil without seriously compromising its reliability, performance,
    77     77   and/or maintainability, I will be happy to accept them.  Fossil is
    78     78   self-hosting.  Send email to request a password that will let
    79     79   you push to the main fossil repository.</p>
    80     80   </blockquote>
    81     81   
    82     82   <b>It would be useful to have a separate application that
    83     83   keeps the bug-tracking database in a versioned file. That file can

Changes to www/quickstart.wiki.

     1      1   <title>Fossil Quick Start Guide</title>
     2         -<nowiki>
     3      2   <h1 align="center">Fossil Quick Start</h1>
     4      3   
     5      4   <p>This is a guide to get you started using fossil quickly
     6      5   and painlessly.</p>
     7      6   
     8         -<h2>Installing</h2><blockquote>
            7  +<h2>Installing</h2>
     9      8   
    10      9       <p>Fossil is a single self-contained C program.  You need to
    11     10       either download a 
    12     11       <a href="http://www.fossil-scm.org/download.html">precompiled binary</a>
    13         -    or <a href="build.wiki">build it yourself</a> from sources.
           12  +    or <a href="build.wiki">compile it yourself</a> from sources.
    14     13       Install fossil by putting the fossil binary
    15         -    someplace on your PATH environment variable.</p>
           14  +    someplace on your $PATH.</p>
    16     15       
    17         -</blockquote>
    18     16   <a name="fslclone"></a>
    19         -<h2>Cloning An Existing Repository</h2><blockquote>
           17  +<h2>General Work Flow</h2>
           18  +
           19  +    <p>Fossil works with repository files (a database with the project's
           20  +    complete history) and with checked-out local trees (the working directory
           21  +    you use to do your work).
           22  +    The workflow looks like this:</p>
           23  +
           24  +    <ul>
           25  +        <li>Create or clone a repository file.  ([/help/init|fossil init] or
           26  +            [/help/clone | fossil clone])
           27  +        <li>Check out a local tree.  ([/help/open | fossil open])
           28  +        <li>Perform operations on the repository (including repository
           29  +            configuration).
           30  +    </ul>
           31  +
           32  +    <p>The following sections will give you a brief overview of these
           33  +    operations.</p>
           34  +
           35  +<h2>Starting A New Project</h2>
           36  +
           37  +    <p>To start a new project with fossil, create a new empty repository
           38  +    this way: ([/help/init | more info]) </p>
           39  +    
           40  +    <blockquote>
           41  +    <b>fossil init </b><i> repository-filename</i>
           42  +    </blockquote>
           43  +
           44  +<h2>Cloning An Existing Repository</h2>
    20     45   
    21     46       <p>Most fossil operations interact with a repository that is on the
    22     47       local disk drive, not on a remote system.  Hence, before accessing
    23     48       a remote repository it is necessary to make a local copy of that
    24     49       repository.  Making a local copy of a remote repository is called
    25     50       "cloning".</p>
    26     51       
    27         -    <p>Clone a remote repository as follows:</p>
           52  +    <p>Clone a remote repository as follows: ([/help/clone | more info])</p>
    28     53       
    29     54       <blockquote>
    30     55       <b>fossil clone</b> <i>URL  repository-filename</i>
    31     56       </blockquote>
    32     57       
    33     58       <p>The <i>URL</i> above is the http URL for the fossil repository
    34     59       you want to clone, and it may include a "user:password" part, e.g.
................................................................................
    37     62       restrictions.  As an example, you can clone the fossil repository
    38     63       this way:</p>
    39     64       
    40     65       <blockquote>
    41     66       <b>fossil clone http://www.fossil-scm.org/ myclone.fossil</b>
    42     67       </blockquote>
    43     68   
    44         -    <p>The new local copy of the respository is stored in a single file,
           69  +    <p>The new local copy of the repository is stored in a single file,
    45     70       which in the example above is named "myclone.fossil".
    46     71       You can name your repositories anything you want.  The ".fossil" suffix
    47     72       is not required.</p>
    48     73   
    49     74       <p>Note: If you are behind a restrictive firewall, you might need
    50         -    to <a href="#proxy">specify an HTTP proxy</a> to use.</p>
           75  +    to <a href="#proxy">specify an HTTP proxy</a>.</p>
           76  +
           77  +    <p>A Fossil repository is a single disk file.  Instead of cloning,
           78  +    you can just make a copy of the repository file (for example, using
           79  +    "scp").  Note, however, that the repository file contains auxiliary
           80  +    information above and beyond the versioned files, including some
           81  +    sensitive information such as password hashs and email addresses.  If you
           82  +    want to share Fossil repositories directly, consider running the
           83  +    [/help/scrub|fossil scrub] command to remove sensitive information
           84  +    before transmitting the file.
    51     85       
    52         -</blockquote><h2>Starting A New Project</h2><blockquote>
           86  +<h2>Importing From Another Version Control System</h2>
    53     87   
    54         -    <p>To start a new project with fossil, create a new empty repository
    55         -    this way:</p>
           88  +    <p>Rather than start a new project, or clone an existing Fossil project,
           89  +    you might prefer to 
           90  +    <a href="./inout.wiki">import an existing Git project</a>
           91  +    into Fossil using the [/help/import | fossil import] command.
           92  +
           93  +<h2>Checking Out A Local Tree</h2>
           94  +
           95  +    <p>To work on a project in fossil, you need to check out a local
           96  +    copy of the source tree.  Create the directory you want to be
           97  +    the root of your tree and cd into that directory.  Then
           98  +    do this: ([/help/open | more info])</p>
    56     99       
    57    100       <blockquote>
    58         -    <b>fossil new </b><i> repository-filename</i>
          101  +    <b>fossil open </b><i> repository-filename</i>
    59    102       </blockquote>
    60    103       
    61         -</blockquote><h2>Configuring Your Local Repository</h2><blockquote>
          104  +    <p>This leaves you with the newest version of the tree
          105  +    checked out.    
          106  +    From anywhere underneath the root of your local tree, you
          107  +    can type commands like the following to find out the status of
          108  +    your local tree:</p>
          109  +
          110  +    <blockquote>
          111  +    <b>[/help/info | fossil info]</b><br>
          112  +    <b>[/help/status | fossil status]</b><br>
          113  +    <b>[/help/changes | fossil changes]</b><br>
          114  +    <b>[/help/diff | fossil diff]</b><br>
          115  +    <b>[/help/timeline | fossil timeline]</b><br>
          116  +    <b>[/help/ls | fossil ls]</b><br>
          117  +    <b>[/help/branch | fossil branch]</b><br>
          118  +    </blockquote>
          119  +
          120  +    <p>Note that Fossil allows you to make multiple check-outs in
          121  +    separate directories from the same repository.  This enables you,
          122  +    for example, to do builds from multiple branches or versions at 
          123  +    the same time without having to generate extra clones.</p>
          124  +
          125  +<h2>Configuring Your Local Repository</h2>
    62    126       
    63    127       <p>When you create a new repository, either by cloning an existing
    64    128       project or create a new project of your own, you usually want to do some
    65         -    local configuration.  This is easily accomplished using the webserver
    66         -    that is built into fossil.  Start the fossil webserver like this:</p>
          129  +    local configuration.  This is easily accomplished using the web-server
          130  +    that is built into fossil.  Start the fossil webserver like this:
          131  +    ([/help/ui | more info])</p>
    67    132       
    68    133       <blockquote>
    69    134       <b>fossil ui </b><i> repository-filename</i>
    70    135       </blockquote>
          136  +
          137  +    <p>You can omit the <i>repository-filename</i> from the command above
          138  +    if you are inside a checked-out local tree.</p>
    71    139   
    72    140       <p>This starts a web server then automatically launches your
    73    141       web browser and makes it point to this web server.  If your system
    74    142       has an unusual configuration, fossil might not be able to figure out
    75    143       how to start your web browser.  In that case, first tell fossil
    76    144       where to find your web browser using a command like this:</p>
    77    145   
................................................................................
    82    150       <p>By default, fossil does not require a login for HTTP connections
    83    151       coming in from the IP loopback address 127.0.0.1.  You can, and perhaps
    84    152       should, change this after you create a few users.</p>
    85    153       
    86    154       <p>When you are finished configuring, just press Control-C or use
    87    155       the <b>kill</b> command to shut down the mini-server.</p>
    88    156   
    89         -</blockquote><h2>Checking Out A Local Tree</h2><blockquote>
    90         -
    91         -    <p>To work on a project in fossil, you need to check out a local
    92         -    copy of the source tree.  Create the directory you want to be
    93         -    the root of your tree and cd into that directory.  Then
    94         -    do this:</p>
    95         -    
    96         -    <blockquote>
    97         -    <b>fossil open </b><i> repository-filename</i>
    98         -    </blockquote>
    99         -    
   100         -    <p>This leaves you with the newest version of the tree
   101         -    checked out.    
   102         -    From anywhere underneath the root of your local tree, you
   103         -    can type commands like the following to find out the status of
   104         -    your local tree:</p>
   105         -
   106         -    <blockquote>
   107         -    <b>fossil info</b><br>
   108         -    <b>fossil status</b><br>
   109         -    <b>fossil changes</b><br>
   110         -    <b>fossil timeline</b><br>
   111         -    <b>fossil leaves</b><br>
   112         -    <b>fossil ls</b><br>
   113         -    <b>fossil branch list</b><br>
   114         -    </blockquote>
   115         -
   116         -</blockquote><h2>Making Changes</h2><blockquote>
          157  +<h2>Making Changes</h2>
   117    158   
   118    159       <p>To add new files to your project, or remove old files, use these
   119    160       commands:</p>
   120    161   
   121    162       <blockquote>
   122         -    <b>fossil add</b> <i>file...</i><br>
   123         -    <b>fossil rm</b> <i>file...</i>
          163  +    <b>[/help/add | fossil add]</b> <i>file...</i><br>
          164  +    <b>[/help/rm | fossil rm]</b> <i>file...</i><br>
          165  +    <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
   124    166       </blockquote>
   125    167   
   126    168       <p>You can also edit files freely.  Once you are ready to commit
   127    169       your changes, type:</p>
   128    170   
   129    171       <blockquote>
   130         -    <b>fossil commit</b>
          172  +    <b>[/help/commit | fossil commit]</b>
   131    173       </blockquote>
   132    174   
   133    175       <p>You will be prompted for check-in comments using whatever editor
   134         -    is specified by your VISUAL or EDITOR environment variable.  If you
   135         -    have GPG installed, you may be prompted for your GPG passphrase so
   136         -    that the check-in can be signed with your GPG signature.  After
   137         -    this your changes will be checked in.</p>
          176  +    is specified by your VISUAL or EDITOR environment variable.</p>
   138    177   
   139         -</blockquote><h2>Sharing Changes</h2><blockquote>
          178  +<h2>Sharing Changes</h2>
   140    179   
   141         -    <p>The changes you <b>commit</b> are only on your local repository.
          180  +    <p>The changes you [/help/commit | commit] are only
          181  +    on your local repository.
   142    182       To share those changes with other repositories, do:</p>
   143    183   
   144    184       <blockquote>
   145         -    <b>fossil push</b> <i>URL</i>
          185  +    <b>[/help/push | fossil push]</b> <i>URL</i>
   146    186       </blockquote>
   147    187   
   148    188       <p>Where <i>URL</i> is the http: URL of the server repository you
   149    189       want to share your changes with.  If you omit the <i>URL</i> argument,
   150    190       fossil will use whatever server you most recently synced with.</p>
   151    191   
   152         -    <p>The <b>push</b> command only sends your changes to others.  To
   153         -    Receive changes from others, use <b>pull</b>.  Or go both ways at
   154         -    once using <b>sync</b>:</p>
          192  +    <p>The [/help/push | push] command only sends your changes to others.  To
          193  +    Receive changes from others, use [/help/pull | pull].  Or go both ways at
          194  +    once using [/help/sync | sync]:</p>
   155    195   
   156    196       <blockquote>
   157         -    <b>fossil pull</b> <i>URL</i><br>
   158         -    <b>fossil sync</b> <i>URL</i>
          197  +    <b>[/help/pull | fossil pull]</b> <i>URL</i><br>
          198  +    <b>[/help/sync | fossil sync]</b> <i>URL</i>
   159    199       </blockquote>
   160    200   
   161    201       <p>When you pull in changes from others, they go into your repository,
   162    202       not into your checked-out local tree.  To get the changes into your
   163         -    local tree, use <b>update</b>:</p>
          203  +    local tree, use [/help/update | update]:</p>
          204  +
          205  +    <blockquote>
          206  +    <b>[/help/update | fossil update]</b> <i>VERSION</i>
          207  +    </blockquote>
          208  +
          209  +    <p>The <i>VERSION</i> can be the name of a branch or tag or any
          210  +    abbreviation to the 40-character
          211  +    artifact identifier for a particular check-in, or it can be a
          212  +    date/time stamp.  ([./checkin_names.wiki | more info])
          213  +    If you omit
          214  +    the <i>VERSION</i>, then fossil moves you to the
          215  +    latest version of the branch your are currently on.</p>
          216  +
          217  +<h2>Branching And Merging</h2>
          218  +
          219  +    <p>Use the --branch option to the [/help/commit | commit] command
          220  +    to start a new branch.  Note that in Fossil, branches are normally
          221  +    created when you commit, not before you start editing.  You can
          222  +    use the [/help/branch | branch new] command to create a new branch
          223  +    before you start editing, if you want, but most people just wait
          224  +    until they are ready to commit.
          225  +
          226  +    To merge two branches back together, first
          227  +    [/help/update | update] to the leaf of one branch.  Then do a
          228  +    [/help/merge | merge] of the leaf of the other branch:</p>
   164    229   
   165    230       <blockquote>
   166         -    <b>fossil update</b> <i>AID</i>
          231  +    <b>[/help/merge | fossil merge]</b> <i>VERSION</i>
   167    232       </blockquote>
   168    233   
   169         -    <p>The <i>AID</i> is some unique abbreviation to the 40-character
   170         -    artifact identifier (AID) for a particular check-in.  If you omit
   171         -    the <i>AID</i> fossil moves you to the
   172         -    leaf version of the branch your are currently on.  If your branch
   173         -    has multiple leaves, you get an error - you'll have to specify the
   174         -    leaf you want using a <i>AID</i> argument.</p>
          234  +    <p>The <i>VERSION</i> can be any of the forms allowed for 
          235  +    [/help/update | update].
          236  +    After performing the merge, you will normally want to test it to
          237  +    make sure it does not break anything, then
          238  +    [/help/commit | commit] your changes.
          239  +    In the default configuration, the [/help/commit|commit]
          240  +    command will also automatically [/help/push|push] your changes, but that
          241  +    feature can be disabled.  (More information about 
          242  +    [./concepts.wiki#workflow|autosync] and how to disable it.)
          243  +    Remember that your coworkers can not see your changes until you 
          244  +    commit and push them.</p>
   175    245   
   176         -</blockquote><h2>Branching And Merging</h2><blockquote>
          246  +    <p>The merge command has options to cherrypick individual
          247  +    changes, or to back out individual changes.</p>
   177    248   
   178         -    <p>You can create branches by doing multiple commits off of the
   179         -    same base version.  To merge to branches back together, first
   180         -    <b>update</b> to the leaf of one branch.  Then do a <b>merge</b>
   181         -    of the leaf of the other branch:</p>
          249  +    <p>Note that the merge command changes only your local check-out.
          250  +    The merge command does <em>not</em> modify the repository in any way.
          251  +    You must do a separate [/help/commit | commit] after the merge in order
          252  +    to put the merged code back into the repository.</p>
          253  +
          254  +    <p>If a merge or update doesn't work out (perhaps something breaks or
          255  +    there are many merge conflicts) then you back up using:</p>
   182    256   
   183    257       <blockquote>
   184         -    <b>fossil merge</b> <i>AID</i>
          258  +    <b>[/help/undo | fossil undo]</b>
   185    259       </blockquote>
   186    260   
   187         -    <p>Test to make sure your merge didn't mess up the code, then
   188         -    <b>commit</b> and possibly also <b>push</b> your changes.   Remember
   189         -    that nobody else can see your changes until you <b>commit</b> and
   190         -    if other are using a different repository you will also need to
   191         -    <b>push</b>.</p>
          261  +    <p>This will back out the changes that the merge or update made to the
          262  +    working checkout.  There is also a [/help/redo|redo] command if you undo by
          263  +    mistake.  Undo and redo only work for changes that have
          264  +    not yet been checked in using commit and there is only a single
          265  +    level of undo/redo.</p>
          266  +
   192    267   
   193    268   <a name="serversetup"></a>
   194         -</blockquote><h2>Setting Up A Server</h2><blockquote>
          269  +<h2>Setting Up A Server</h2>
   195    270   
   196    271       <p>The easiest way to set up a server is:</p>
   197    272   
   198    273       <blockquote>
   199         -    <b>fossil server</b> <i>repository-filename</i>
          274  +    <b>[/help/server | fossil server]</b> <i>repository-filename</i>
   200    275       </blockquote>
   201    276   
   202    277       <p>Or</b>
   203    278   
   204    279       <blockquote>
   205         -    <b>fossil ui</b> <i>repository-filename</i>
          280  +    <b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
   206    281       </blockquote>
   207    282   
   208         -    <p>The difference between these two command is that <b>ui</b>
   209         -    attempts to automatically start your web browser pointing at the
   210         -    server whereas <b>server</b> does not.
          283  +    <p>The <b>ui</b> command is intended for accessing the web interface
          284  +    from a local desktop.  The <b>ui</b> command binds to the loopback IP
          285  +    address only (and is thus makes the web interface visible only on the
          286  +    local machine) and it automatically start your web browser pointing at the
          287  +    server.  For cross-machine collaboration, use the <b>server</b> command,
          288  +    which binds on all IP addresses and does not try to start a web browser.
   211    289       You can omit the <i>repository-filename</i> if you are within
   212         -    a checked-out local tree.  This server uses port 8080 by default
   213         -    but you can specify a different port using the <b>-port</b> command.</p>
          290  +    a checked-out local tree.  The <b>server</b> uses port 8080 by default
          291  +    but you can specify a different port using the <b>-port</b> option.</p>
   214    292   
   215    293       <p>Command-line servers like this are useful when two people want
   216    294       to share a repository on temporary or ad-hoc basis.  For a more
   217    295       permanent installation, you should use either the CGI server or the
   218    296       inetd server.
   219    297       <a name="cgiserver"></a>
   220    298       To use the CGI server, create a CGI script that
................................................................................
   243    321       <p>Adjust the paths to suit your installation, of course.  Notice that
   244    322       fossil runs as root.  This is not required - you can run it as an
   245    323       unprivileged user.  But it is more secure to run fossil as root.
   246    324       When you do run fossil as root, it automatically puts itself in a
   247    325       chroot jail in the same directory as the repository, then drops
   248    326       root privileges prior to reading any information from the request.</p>
   249    327   
   250         -</blockquote><a name="proxy"></a><h2>HTTP Proxies</h2><blockquote>
          328  +<a name="proxy"></a>
          329  +<h2>HTTP Proxies</h2>
   251    330   
   252    331       <p>If you are behind a restrictive firewall that requires you to use
   253    332       an HTTP proxy to reach the internet, then you can configure the proxy
   254    333       in three different ways.  You can tell fossil about your proxy using
   255    334       a command-line option on commands that use the network,
   256    335       <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.</p>
   257    336   
   258    337       <blockquote>
   259    338       <b>fossil clone </b><i>URL</i>  <b>--proxy</b> <i>Proxy-URL</i>
   260    339       </blockquote>
   261    340   
   262    341       <p>It is annoying to have to type in the proxy URL every time you
   263    342       sync your project, though, so you can make the proxy configuration
   264         -    persistent using the <b>setting</b> command:</p>
          343  +    persistent using the [/help/setting | setting] command:</p>
   265    344   
   266    345       <blockquote>
   267    346       <b>fossil setting proxy </b><i>Proxy-URL</i>
   268    347       </blockquote>
   269    348   
   270    349       <p>Or, you can set the "<b>http_proxy</b>" environment variable:</p>
   271    350   
................................................................................
   286    365       is easily done on the command-line.  For example, to sync with
   287    366       a co-workers repository on your LAN, you might type:</p>   
   288    367   
   289    368       <blockquote>
   290    369       <b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
   291    370       </blockquote>
   292    371   
   293         -</blockquote><h2>More Hints</h2><blockquote>
          372  +<h2>More Hints</h2>
   294    373   
   295         -    <p>Try these commands:</p>
   296         -
   297         -    <blockquote><b>
   298         -    fossil help<br>
   299         -    fossil test-commands
   300         -    </b></blockquote>
          374  +    <p>A [/help | complete list of commands] is available.
   301    375   
   302    376       <p>Explore and have fun!</p>
   303         -
   304         -
   305         -</blockquote>
   306         -</nowiki>

Added www/quotes.wiki.

            1  +<title>What People Are Saying</title>
            2  +
            3  +The following are collected quotes from various forums and blogs about
            4  +Fossil, Git, and DVCSes in general.  This collection is put together
            5  +by the creator of Fossil, so of course there is selection bias...
            6  +
            7  +<h2>On The Usability Of Git:</h2>
            8  +
            9  +<ol>
           10  +<li>Git approaches the useability of iptables, which is to say, utterly 
           11  +unusable unless you have the manpage tattooed on you arm.
           12  +
           13  +<blockquote>
           14  +<i>by mml at [http://news.ycombinator.com/item?id=1433387]</i>
           15  +</blockquote>
           16  +
           17  +<li><nowiki>It's simplest to think of the state of your [git] repository
           18  +as a point in a high-dimensional "code-space",  in which branches are 
           19  +represented as n-dimensional membranes, mapping the spatial loci of
           20  +successive commits onto the projected manifold of each cloned
           21  +repository.</nowiki>
           22  +
           23  +<blockquote>
           24  +<i>At [http://tartley.com/?p=1267]</i>
           25  +</blockquote>
           26  +
           27  +<li>Git is not a Prius. Git is a Model T.
           28  +Its plumbing and wiring sticks out all over the place.
           29  +You have to be a mechanic to operate it successfully or you'll be
           30  +stuck on the side of the road when it breaks down. 
           31  +And it <b>will</b> break down.
           32  +
           33  +<blockquote>
           34  +<i>Nick Farina at [http://nfarina.com/post/9868516270/git-is-simpler]</i>
           35  +</blockquote>
           36  +
           37  +<li>We've been using git and github for a few months now, and it's not 
           38  +intuitive... I'm hoping someone will make a set of standard wrappers/GUI
           39  +for making git bearable.
           40  +
           41  +<blockquote>
           42  +<i>maro at [http://news.ycombinator.com/item?id=1433387]</i>
           43  +</blockquote>
           44  +
           45  +<li>Klingon Code Warriors embrace Git; we enjoy arbitrary conflicts. Git is not for the weak and feeble. TODAY IS A GOOD DAY TO CODE.
           46  +
           47  +<blockquote>
           48  +<i>teastain at [http://www.reddit.com/r/programming/comments/xpitj/10_things_i_hate_about_git/c5oj4fk]
           49  +</blockquote>
           50  +
           51  +
           52  +</ol>
           53  +
           54  +<h2>On The Usability Of Fossil:</h2>
           55  +
           56  +<ol>
           57  +<li value=6>
           58  +Fossil mesmerizes me with simplicity especially after I struggled to
           59  +get a bug-tracking system to work with mercurial.
           60  +
           61  +<blockquote>
           62  +<i>rawjeev at [http://stackoverflow.com/questions/156322/what-do-people-think-of-the-fossil-dvcs]</i>
           63  +</blockquote>
           64  +
           65  +<li>Fossil is awesome!!! I have never seen an app like that before, 
           66  +such simplicity and flexibility!!!
           67  +
           68  +<blockquote>
           69  +<i>zengr at [http://stackoverflow.com/questions/138621/best-version-control-for-lone-developer]</i>
           70  +</blockquote>
           71  +
           72  +
           73  +</ol>
           74  +
           75  +
           76  +<h2>On Git Versus Fossil</h2>
           77  +
           78  +<ol>
           79  +<li value=8>
           80  +Just want to say thanks for fossil making my life easier.... 
           81  +Also <nowiki>[for]</nowiki> not having a misanthropic command line interface.
           82  +
           83  +<blockquote>
           84  +<i>Joshua Paine at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg02736.html]</i>
           85  +</blockquote>
           86  +
           87  +<li>We use it at a large university to manage code that small teams write.
           88  +The runs everywhere, ease of installation and portability is something that 
           89  +seems to be a good fit with the environment we have (highly ditrobuted, 
           90  +sometimes very restrictive firewalls, OSX/Win/Linux).  We are happy with it 
           91  +and teaching a Msc/Phd student (read complete novice) fossil has just 
           92  +been a smoother ride than Git was.
           93  +
           94  +<blockquote>
           95  +<i>viablepanic at [http://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/]</i>
           96  +</blockquote>
           97  +
           98  +<li>In the fossil community - and hence in fossil itself - development history 
           99  +is pretty much sacrosanct. The very name "fossil" was to chosen to 
          100  +reflect the unchanging nature of things in that history.
          101  +
          102  +<p>In git (or rather, the git community), the development history is part of
          103  +the published aspect of the project, so it provides tools for rearranging
          104  +that history so you can present what you "should" have done rather
          105  +than what you actually did.
          106  +
          107  +<blockquote>
          108  +<i>Mike Meyer on the Fossil mailing list, 2011-10-04</i>
          109  +</blockquote>
          110  +</ol>

Deleted www/reference.wiki.

     1         -  <h2>Command Line Interface Reference</h2>
     2         -
     3         -  This is an easy introduction to the fossil command line interface
     4         -  (cli).  It assumes some familiarity with using the command line, and
     5         -  with Source Code Maintenence (SCM) systems&#8212;but not <i>too</i>
     6         -  much.
     7         -
     8         -  If you are trying to find information about fossil's web
     9         -  capabilities, see the <a href="index.wiki">Fossil Home</a> and
    10         -  <a href="wikitheory.wiki">Fossil Wiki</a> pages for pointers.
    11         -
    12         -  <h3>Things to note</h3>
    13         -  * Fossil cli commands do not use special delimeters, they use
    14         -  spaces.  This is traditional with VCS/SCM.  Some <i>options</i> to
    15         -  fossil commands <i>do</i> use special delimiters, particularly the
    16         -  '-' (hyphen, or dash) character.  This is very similar to Tcl.
    17         -  Think of fossil as a shell you invoke and feed a command to,
    18         -  including any options, and it will make more sense.
    19         -
    20         -  * Any fossil command is acceptable once enough of it has been
    21         -  entered to make the intent unambiguous.  'clo' is a proper prefix of
    22         -  both the 'clone' and 'close' commands, for instance, but 'clon' is
    23         -  enough to make the intent&#8212;the 'clone'
    24         -  command&#8212;unambiguous.
    25         -
    26         -  * Pragmatically, a [./concepts.wiki#keyconc | <u>version</u>]
    27         -  in <b>fossil</b> is a 40-character long string of hexadecimal.
    28         -  <b>fossil</b> will be able to figure out which version you want
    29         -  with any <i>distinct</i> prefix of that string which is at
    30         -  least four characters long.  <em>Commands which require a
    31         -  version are looking for the string, a distinct prefix of the
    32         -  string, or a <code>tag</code>.</em>
    33         -
    34         -  * SCM in a distributed environment can be a bit confusing with
    35         -  regard to branching, merging, and versions in general.  See the
    36         -  [./branching.wiki | explanation of branching] and it will all make
    37         -  much more sense.
    38         -
    39         -  * <i>Op.Ed.</i> An excellent way to learn to use <b>fossil</b>
    40         -    effectively is to
    41         -    [./quickstart.wiki#fslclone | clone the repository for fossil]
    42         -    itself.  You can then poke around using the <code>fossil ui</code>
    43         -    command, and look things up with no connection worries.  You can
    44         -    set up test repositories and try things out on-the-fly to see how
    45         -    they work, using their own ui's.  The CLI will far easier to
    46         -    understand if you can run a repository, watch it in a browser, and
    47         -    hack around with it in a simplified environment (your tests) with
    48         -    guaranteed and fast access to the sources & docs (your cloned fossil
    49         -    repository).
    50         -<br /><br /> <br />
    51         -  <b>You should</b> probably start interacting with fossil at the command
    52         -  line by asking it what it can
    53         -  do:&nbsp;&nbsp;&nbsp;&nbsp;<a name="tof">&#710;</a>
    54         -
    55         -  <code>$ fossil help</code><nowiki><pre>
    56         -Usage: fossil help COMMAND.
    57         -Available COMMANDs:</pre><table width="80%"
    58         -                          style="font-family: fixed, courier, monospace;">
    59         -  <tr>
    60         -    <td><a href="#add">add</a>*</td>
    61         -    <td><a href="#checkout">co</a>*</td>
    62         -    <td><a href="#http">http</a></td>
    63         -    <td><a href="#rebuild">rebuild</a></td>
    64         -    <td><a href="#sync">sync</a>*</td>
    65         -  </tr>
    66         -  <tr>
    67         -    <td><a href="#all">all</a>*</td>
    68         -    <td><a href="#commit">commit</a></td>
    69         -    <td><a href="#info">info</a></td>
    70         -    <td><a href="#reconstruct">reconstruct</a></td>
    71         -    <td><a href="#tag">tag</a></td>
    72         -  </tr>
    73         -  <tr>
    74         -    <td><a href="#branch">branch</a></td>
    75         -    <td><a href="#configuration">configuration</a></td>
    76         -    <td><a href="#leaves">leaves</a></td>
    77         -    <td><a href="#redo">redo</a></td>
    78         -    <td><a href="#timeline">timeline</a></td>
    79         -  </tr>
    80         -  <tr>
    81         -    <td><a href="#cgi">cgi</a>*</td>
    82         -    <td><a href="#deconstruct">deconstruct</a></td>
    83         -    <td><a href="#ls">ls</a>*</td>
    84         -    <td><a href="#mv">rename</a>*</td>
    85         -    <td><a href="#server">ui</a></td>
    86         -  </tr>
    87         -  <tr>
    88         -    <td><a href="#changes">changes</a>*</td>
    89         -    <td><a href="#rm">del</a>*</td>
    90         -    <td><a href="#merge">merge</a></td>
    91         -    <td><a href="#revert">revert</a></td>
    92         -    <td><a href="#undo">undo</a></td>
    93         -  </tr>
    94         -  <tr>
    95         -    <td><a href="#checkout">checkout</a>*</td>
    96         -    <td><a href="#descendants">descendants</a></td>
    97         -    <td><a href="#mv">mv</a>*</td>
    98         -    <td><a href="#rm">rm</a>*</td>
    99         -    <td><a href="#setting">unset</a></td>
   100         -  </tr>
   101         -  <tr>
   102         -    <td><a href="#commit">ci</a></td>
   103         -    <td><a href="#diff">diff</a></td>
   104         -    <td><a href="#new">new</a>*</td>
   105         -    <td><a href="#rstats">rstats</a></td>
   106         -    <td><a href="#update">update</a>*</td>
   107         -  </tr>
   108         -  <tr>
   109         -    <td><a href="#clean">clean</a></td>
   110         -    <td><a href="#extra">extra</a>*</td>
   111         -    <td><a href="#open">open</a></td>
   112         -    <td><a href="#server">server</a></td>
   113         -    <td><a href="#user">user</a></td>
   114         -  </tr>
   115         -  <tr>
   116         -    <td><a href="#clone">clone</a></td>
   117         -    <td><a href="#diff">gdiff</a></td>
   118         -    <td><a href="#pull">pull</a></td>
   119         -    <td><a href="#setting">settings</a></td>
   120         -    <td><a href="#version">version</a>*</td>
   121         -  </tr>
   122         -  <tr>
   123         -    <td><a href="#close">close</a></td>
   124         -    <td><a href="#help">help</a></td>
   125         -    <td><a href="#push">push</a></td>
   126         -    <td><a href="#status">status</a>*</td>
   127         -    <td><a href="#wiki">wiki</a></td>
   128         -  </tr>
   129         -</table><nowiki><pre>
   130         -This is fossil version [a89b436bc9] 2009-02-11 05:00:02 UTC
   131         -</pre>
   132         -<b>What follows</b> is a survey of what you get if you type<code>
   133         -fossil&nbsp;help&nbsp;<i>command</i> </code>for all of the
   134         -commands listed above.  There are links to individual pages for each
   135         -of them; pages with content (commands marked with a '*' are done) go
   136         -into the reason for a command in a bit more depth than the program help.
   137         -<pre>
   138         -<hr><a href="#tof">&#710;</a>
   139         -    <a name="add">Usage: </a><code><a href="cmd_add.wiki">fossil add</a></code> FILE...
   140         -    Make arrangements to add one or more files to the current checkout
   141         -    at the next commit.
   142         -
   143         -<hr><a href="#tof">&#710;</a>
   144         -    <a name="all">Usage: </a><code><a href="cmd_all.wiki">fossil all</a></code> (list|pull|push|rebuild|sync)
   145         -    The ~/.fossil file records the location of all repositories for a
   146         -    user.  This command performs certain operations on all repositories
   147         -    that can be useful before or after a period of disconnection operation.
   148         -    Available operations are:
   149         -
   150         -    list       Display the location of all repositories
   151         -
   152         -    pull       Run a "pull" operation on all repositories
   153         -
   154         -    push       Run a "push" on all repositories
   155         -
   156         -    rebuild    Rebuild on all repositories
   157         -
   158         -    sync       Run a "sync" on all repositories
   159         -
   160         -    Respositories are automatically added to the set of known repositories
   161         -    when one of the following commands against the repository: clone, info,
   162         -    pull, push, or sync
   163         -
   164         -<hr><a href="#tof">&#710;</a>
   165         -    <a name="branch">Usage: </a><code><a href="cmd_branch.wiki">fossil branch</a></code> SUBCOMMAND ... ?-R|--repository FILE?
   166         -
   167         -Run various subcommands on the branches of the open repository or
   168         -of the repository identified by the -R or --repository option.
   169         -
   170         -   fossil branch new BRANCH-NAME BASIS ?-bgcolor COLOR?
   171         -
   172         -       Create a new branch BRANCH-NAME off of check-in BASIS.
   173         -       You can optionally give the branch a default color.
   174         -
   175         -   fossil branch list
   176         -
   177         -       List all branches
   178         -
   179         -<hr><a href="#tof">&#710;</a>
   180         -    <a name="cgi">Usage: </a><a href="cmd_cgi.wiki">fossil cgi</a> SCRIPT
   181         -    The SCRIPT argument is the name of a file that is the CGI script
   182         -    that is being run.  The command name, "cgi", may be omitted if
   183         -    the GATEWAY_INTERFACE environment variable is set to "CGI" (which
   184         -    should always be the case for CGI scripts run by a webserver.)  The
   185         -    SCRIPT file should look something like this:
   186         -
   187         -    #!/usr/bin/fossil
   188         -    repository: /home/somebody/project.db
   189         -
   190         -    The second line defines the name of the repository.  After locating
   191         -    the repository, fossil will generate a webpage on stdout based on
   192         -    the values of standard CGI environment variables.
   193         -
   194         -<hr><a href="#tof">&#710;</a>
   195         -    <a name="changes">Usage: </a><a href="cmd_changes.wiki">fossil changes</a>
   196         -    Report on the edit status of all files in the current checkout.
   197         -    See also the "status" and "extra" commands.
   198         -
   199         -<hr><a href="#tof">&#710;</a>
   200         -    <a name="checkout">Usage: </a><a href="cmd_checkout.wiki">fossil checkout</a> VERSION ?-f|--force?
   201         -    Check out a version specified on the command-line.  This command
   202         -    will not overwrite edited files in the current checkout unless
   203         -    the --force option appears on the command-line.
   204         -
   205         -    See also the "update" command.
   206         -
   207         -<hr><a href="#tof">&#710;</a>
   208         -    <a name="commit">Usage: </a><a href="cmd_commit.wiki">fossil commit</a> ?-m COMMENT? ?--nosign? ?FILE...?    fossil ci  ...  (as above)
   209         -
   210         -    Create a new version containing all of the changes in the current
   211         -    checkout.  You will be prompted to enter a check-in comment unless
   212         -    the "-m" option is used to specify a comment line.  You will be
   213         -    prompted for your GPG passphrase in order to sign the new manifest
   214         -    unless the "--nosign" options is used.  All files that have
   215         -    changed will be committed unless some subset of files is specified
   216         -    on the command line.
   217         -
   218         -<hr><a href="#tof">&#710;</a>
   219         -    <a name="clean">Usage: </a><a href="cmd_clean.wiki">fossil clean</a> ?-all?
   220         -    Delete all "extra" files in the source tree.  "Extra" files are
   221         -    files that are not officially part of the checkout.  See also
   222         -    the "extra" command. This operation cannot be undone.
   223         -
   224         -    You will be prompted before removing each file. If you are
   225         -    sure you wish to remove all "extra" files you can specify the
   226         -    optional -all flag.
   227         -
   228         -<hr><a href="#tof">&#710;</a>
   229         -    <a name="clone">Usage: </a><a href="cmd_clone.wiki">fossil clone</a> URL FILENAME
   230         -    Make a clone of a repository specified by URL in the local
   231         -    file named FILENAME.
   232         -
   233         -<hr><a href="#tof">&#710;</a>
   234         -    <a name="close">Usage: </a><a href="cmd_close.wiki">fossil close</a> ?-f|--force?
   235         -    The opposite of "open".  Close the current database connection.
   236         -    Require a -f or --force flag if there are unsaved changed in the
   237         -    current check-out.
   238         -
   239         -<hr><a href="#tof">&#710;</a>
   240         -    <a name="configuration">Usage: </a><a href="cmd_configure.wiki">fossil configuration</a> METHOD ...
   241         -    Where METHOD is one of: export import merge pull push reset.  All methods
   242         -    accept the -R or --repository option to specific a repository.
   243         -
   244         -    fossil configuration export AREA FILENAME
   245         -
   246         -    Write to FILENAME exported configuraton information for AREA.
   247         -    AREA can be one of:  all ticket skin project
   248         -
   249         -    fossil configuration import FILENAME
   250         -
   251         -    Read a configuration from FILENAME, overwriting the current
   252         -    configuration.
   253         -
   254         -    fossil configuration merge FILENAME
   255         -
   256         -    Read a configuration from FILENAME and merge its values into
   257         -    the current configuration.  Existing values take priority over
   258         -    values read from FILENAME.
   259         -
   260         -    fossil configuration pull AREA ?URL?
   261         -
   262         -    Pull and install the configuration from a different server
   263         -    identified by URL.  If no URL is specified, then the default
   264         -    server is used.
   265         -    fossil configuration push AREA ?URL?
   266         -
   267         -    Push the local configuration into the remote server identified
   268         -    by URL.  Admin privilege is required on the remote server for
   269         -    this to work.
   270         -
   271         -    fossil configuration reset AREA
   272         -
   273         -    Restore the configuration to the default.  AREA as above.
   274         -
   275         -    WARNING: Do not import, merge, or pull configurations from an untrusted
   276         -    source.  The inbound configuration is not checked for safety and can
   277         -    introduce security vulnerabilities.
   278         -
   279         -<hr><a href="#tof">&#710;</a>
   280         -    COMMAND: deconstruct
   281         -    <a name="deconstruct">Usage: </a><a href="cmd_deconstruct.wiki">fossil deconstruct</a> ?-R|--repository REPOSITORY? DESTINATION
   282         -    Populates the indicated DESTINATION directory with copies of all
   283         -    artifcats contained within the repository.  Artifacts are named AA/bbbbb
   284         -    where AA is the first 2 characters of the artifact ID and bbbbb is the
   285         -    remaining 38 characters.
   286         -
   287         -<hr><a href="#tof">&#710;</a>
   288         -    <a name="rm">Usage: </a><a href="cmd_rm.wiki">fossil rm</a> FILE...    or: fossil del FILE...
   289         -    Remove one or more files from the tree.
   290         -
   291         -<hr><a href="#tof">&#710;</a>
   292         -    <a name="descendants">Usage: </a><a href="cmd_descendants.wiki">fossil descendants</a> ?CHECKIN-ID?
   293         -    Find all leaf descendants of the check-in specified or if the argument
   294         -    is omitted, of the check-in currently checked out.
   295         -
   296         -<hr><a href="#tof">&#710;</a>
   297         -    <a name="diff">Usage: </a><a href="cmd_diff.wiki">fossil diff</a>|gdiff ?-i? ?-r REVISION? FILE...
   298         -    Show the difference between the current version of a file (as it
   299         -    exists on disk) and that same file as it was checked out.
   300         -
   301         -    diff will show a textual diff while gdiff will attempt to run a
   302         -    graphical diff command that you have setup. If the choosen command
   303         -    is not yet configured, the internal textual diff command will be
   304         -    used.
   305         -
   306         -    If -i is supplied for either diff or gdiff, the internal textual
   307         -    diff command will be executed.
   308         -
   309         -    Here are a few external diff command settings, for example:
   310         -
   311         -    fossil setting diff-command diff
   312         -
   313         -    fossil setting gdiff-command tkdiff
   314         -    fossil setting gdiff-command eskill22
   315         -    fossil setting gdiff-command tortoisemerge
   316         -    fossil setting gdiff-command meld
   317         -    fossil setting gdiff-command xxdiff
   318         -    fossil setting gdiff-command kdiff3
   319         -
   320         -<hr><a href="#tof">&#710;</a>
   321         -    <a name="extra">Usage: </a><a href="cmd_extra.wiki">fossil extra</a>
   322         -    Print a list of all files in the source tree that are not part of
   323         -    the current checkout.  See also the "clean" command.
   324         -
   325         -<hr><a href="#tof">&#710;</a>
   326         -    <a name="help">Usage: </a><a href="cmd_help.wiki">fossil help</a> COMMAND
   327         -    Display information on how to use COMMAND
   328         -
   329         -<hr><a href="#tof">&#710;</a>
   330         -    <a name="http">Usage: </a><a href="cmd_http.wiki">fossil http</a> REPOSITORY
   331         -    Handle a single HTTP request appearing on stdin.  The resulting webpage
   332         -    is delivered on stdout.  This method is used to launch an HTTP request
   333         -    handler from inetd, for example.  The argument is the name of the    repository.
   334         -
   335         -<hr><a href="#tof">&#710;</a>
   336         -    <a name="info">Usage: </a><a href="cmd_info.wiki">fossil info</a> ?ARTIFACT-ID|FILENAME?
   337         -    With no arguments, provide information about the current tree.
   338         -    If an argument is specified, provide information about the object
   339         -    in the respository of the current tree that the argument refers
   340         -    to.  Or if the argument is the name of a repository, show
   341         -    information about that repository.
   342         -
   343         -<hr><a href="#tof">&#710;</a>
   344         -    <a name="leaves">Usage: </a><a href="cmd_leaves.wiki">fossil leaves</a>
   345         -    Find leaves of all branches.
   346         -
   347         -<hr><a href="#tof">&#710;</a>
   348         -    <a name="ls">Usage: </a><a href="cmd_ls.wiki">fossil ls</a>
   349         -    Show the names of all files in the current checkout
   350         -
   351         -<hr><a href="#tof">&#710;</a>
   352         -    <a name="merge">Usage: </a><a href="cmd_merge.wiki">fossil merge</a> VERSION
   353         -    The argument is a version that should be merged into the current
   354         -    checkout.
   355         -    Only file content is merged.  The result continues to use the
   356         -    file and directory names from the current check-out even if those
   357         -    names might have been changed in the branch being merged in.
   358         -
   359         -<hr><a href="#tof">&#710;</a>
   360         -    <a name="mv">Usage: </a><a href="cmd_mv.wiki">fossil mv|rename</a> OLDNAME NEWNAME       or: fossil mv|rename OLDNAME... DIR
   361         -
   362         -    Move or rename one or more files within the tree
   363         -
   364         -    This command does not rename the files on disk.  All this command does is
   365         -    record the fact that filenames have changed so that appropriate notations
   366         -    can be made at the next commit/checkin.
   367         -
   368         -<hr><a href="#tof">&#710;</a>
   369         -    <a name="new">Usage: </a><a href="cmd_new.wiki">fossil new</a> FILENAME
   370         -
   371         -    Create a repository for a new project in the file named FILENAME.
   372         -    This command is distinct from "clone".  The "clone" command makes
   373         -    a copy of an existing project.  This command starts a new project.
   374         -
   375         -<hr><a href="#tof">&#710;</a>
   376         -    <a name="open">Usage: </a><a href="cmd_open.wiki">fossil open</a> FILENAME
   377         -    Open a connection to the local repository in FILENAME.  A checkout
   378         -    for the repository is created with its root at the working directory.
   379         -    See also the "close" command.
   380         -
   381         -<hr><a href="#tof">&#710;</a>
   382         -    <a name="rstats">Usage: </a><a href="cmd_rstats.wiki">fossil rstats</a>
   383         -
   384         -    Deliver a report of the repository statistics for the
   385         -    current checkout.
   386         -
   387         -<hr><a href="#tof">&#710;</a>
   388         -    <a name="pull">Usage: </a><a href="cmd_pull.wiki">fossil pull</a> ?URL? ?-R|--respository REPOSITORY?
   389         -    Pull changes in a remote repository into the local repository.
   390         -    The repository is identified by the -R or --repository option.
   391         -    If there is no such option then the open repository is used.
   392         -    The URL of the remote server is specified on the command line
   393         -    If no URL is specified then the URL used by the most recent
   394         -    "pull", "push", or "sync" command is used.
   395         -
   396         -    The URL is of the following form:
   397         -
   398         -    http://USER@HOST:PORT/PATH
   399         -
   400         -    The "USER@" and ":PORT" substrings are optional.
   401         -    The "USER" substring specifies the login user.  You will be
   402         -    prompted for the password on the command-line.  The PORT
   403         -    specifies the TCP port of the server.  The default port is
   404         -    80.
   405         -
   406         -<hr><a href="#tof">&#710;</a>
   407         -    <a name="push">Usage: </a><a href="cmd_push.wiki">fossil push</a> ?URL? ?-R|--repository REPOSITORY?
   408         -    Push changes in the local repository over into a remote repository.
   409         -    See the "pull" command for additional information.
   410         -
   411         -<hr><a href="#tof">&#710;</a>
   412         -    <a name="rebuild">Usage: </a><a href="cmd_rebuild.wiki">fossil rebuild</a> REPOSITORY
   413         -    Reconstruct the named repository database from the core
   414         -    records.  Run this command after updating the fossil
   415         -    executable in a way that changes the database schema.
   416         -
   417         -<hr><a href="#tof">&#710;</a>
   418         -    COMMAND: reconstruct
   419         -    <a name="reconstruct">Usage: </a><a href="cmd_reconstruct.wiki">fossil reconstruct</a> REPOSITORY ORIGIN
   420         -    Creates the REPOSITORY and populates it with the artifacts in the
   421         -    indicated ORIGIN directory.
   422         -
   423         -<hr><a href="#tof">&#710;</a>
   424         -    <a name="redo">Usage: </a><a href="cmd_redo.wiki">fossil redo</a> ?FILENAME...?
   425         -    Redo the an update or merge operation that has been undone by the
   426         -    undo command.  If FILENAME is specified then restore the changes
   427         -    associated with the named file(s) but otherwise leave the update
   428         -    or merge undone.
   429         -
   430         -    A single level of undo/redo is supported.  The undo/redo stack
   431         -    is cleared by the commit and checkout commands.
   432         -
   433         -<hr><a href="#tof">&#710;</a>
   434         -    <a name="revert">Usage: </a><a href="cmd_revert.wiki">fossil revert</a> ?--yes? ?-r CHECKIN? FILE
   435         -    Revert to the current repository version of FILE, or to
   436         -    the version associated with check-in CHECKIN if the -r flag
   437         -    appears.  This command will confirm your operation unless the
   438         -    file is missing or the --yes option is used.
   439         -
   440         -<hr><a href="#tof">&#710;</a>
   441         -    <a name="server">Usage: </a><a href="cmd_server.wiki">fossil server</a> ?-P|--port TCPPORT? ?REPOSITORY?    Or: fossil ui ?-P|--port TCPPORT? ?REPOSITORY?
   442         -
   443         -    Open a socket and begin listening and responding to HTTP requests on
   444         -    TCP port 8080, or on any other TCP port defined by the -P or
   445         -    --port option.  The optional argument is the name of the repository.
   446         -    The repository argument may be omitted if the working directory is
   447         -    within an open checkout.
   448         -
   449         -    The "ui" command automatically starts a web browser after initializing
   450         -    the web server.
   451         -
   452         -<hr><a href="#tof">&#710;</a>
   453         -    COMMAND: settings
   454         -    COMMAND: unset
   455         -    <a name="setting">Usage: </a><a href="cmd_setting.wiki">fossil setting</a> ?PROPERTY? ?VALUE? ?-global?
   456         -    fossil unset PROPERTY ?-global?
   457         -
   458         -    The "setting" command with no arguments lists all properties and their
   459         -    values.  With just a property name it shows the value of that property.
   460         -    With a value argument it changes the property for the current repository.
   461         -
   462         -    The "unset" command clears a property setting.
   463         -
   464         -       autosync         If enabled, automatically pull prior to
   465         -		        commit or update and automatically push
   466         -		        after commit or tag or branch creation.
   467         -
   468         -       diff-command     External command to run when performing a diff.
   469         -		        If undefined, the internal text diff will be used.
   470         -
   471         -       editor           Text editor command used for check-in comments.
   472         -
   473         -       http-port        The TCP/IP port number to use by the "server"
   474         -		        and "ui" commands.  Default: 8080
   475         -
   476         -       gdiff-command    External command to run when performing a graphical
   477         -		        diff. If undefined, text diff will be used.
   478         -
   479         -       localauth        If enabled, require that HTTP connections from
   480         -		        127.0.0.1 be authenticated by password.  If
   481         -		        false, all HTTP requests from localhost have
   482         -		        unrestricted access to the repository.
   483         -
   484         -       clearsign        When enabled (the default), fossil will attempt to
   485         -		        sign all commits with gpg.  When disabled, commits will
   486         -		        be unsigned.
   487         -
   488         -       pgp-command      Command used to clear-sign manifests at check-in.
   489         -		        The default is "gpg --clearsign -o ".
   490         -
   491         -       mtime-changes    Use file modification times (mtimes) to detect when
   492         -		        files have been modified.
   493         -
   494         -       proxy            URL of the HTTP proxy.  If undefined or "on" then
   495         -		        the "http_proxy" environment variable is consulted.
   496         -		        If the http_proxy environment variable is undefined
   497         -		        then a direct HTTP connection is used.
   498         -
   499         -       web-browser      A shell command used to launch your preferred
   500         -		        web browser when given a URL as an argument.
   501         -		        Defaults to "start" on windows, "open" on Mac,
   502         -		        and "firefox" on Unix.
   503         -
   504         -<hr><a href="#tof">&#710;</a>
   505         -    <a name="status">Usage: </a><a href="cmd_status.wiki">fossil status</a>
   506         -    Report on the status of the current checkout.
   507         -
   508         -<hr><a href="#tof">&#710;</a>
   509         -    <a name="sync">Usage: </a><a href="cmd_sync.wiki">fossil sync</a> ?URL? ?-R|--repository REPOSITORY?
   510         -    Synchronize the local repository with a remote repository.  This is
   511         -    the equivalent of running both "push" and "pull" at the same time.
   512         -    See the "pull" command for additional information.
   513         -
   514         -<hr><a href="#tof">&#710;</a>
   515         -    <a name="tag">Usage: </a><a href="cmd_tag.wiki">fossil tag</a> SUBCOMMAND ...
   516         -    Run various subcommands to control tags and properties
   517         -
   518         -    fossil tag add ?--raw? TAGNAME CHECK-IN ?VALUE?
   519         -
   520         -    Add a new tag or property to CHECK-IN. The tag will
   521         -    be usable instead of a CHECK-IN in commands such as
   522         -    update and merge.
   523         -
   524         -    fossil tag branch ?--raw? ?--nofork? TAGNAME CHECK-IN ?VALUE?
   525         -
   526         -    A fork will be created so that the new checkin
   527         -    is a sibling of CHECK-IN and identical to it except
   528         -    for a generated comment. Then the new tag will
   529         -    be added to the new checkin and propagated to
   530         -    all direct children.  Additionally all symbolic
   531         -    tags of that checkin inherited from CHECK-IN will
   532         -    be cancelled.
   533         -
   534         -    However, if the option --nofork is given, no
   535         -    fork will be created and the tag/property will be
   536         -    added to CHECK-IN directly. No tags will be canceled.
   537         -
   538         -    fossil tag cancel ?--raw? TAGNAME CHECK-IN
   539         -
   540         -    Remove the tag TAGNAME from CHECK-IN, and also remove
   541         -    the propagation of the tag to any descendants.
   542         -
   543         -    fossil tag find ?--raw? TAGNAME
   544         -
   545         -    List all check-ins that use TAGNAME
   546         -
   547         -    fossil tag list ?--raw? ?CHECK-IN?
   548         -
   549         -    List all tags, or if CHECK-IN is supplied, list
   550         -    all tags and their values for CHECK-IN.
   551         -
   552         -    The option --raw allows the manipulation of all types of
   553         -    tags used for various internal purposes in fossil. You
   554         -    should not use this option to make changes unless you are
   555         -    sure what you are doing.
   556         -
   557         -    If you need to use a tagname that might be confused with
   558         -    a hexadecimal check-in or artifact ID, you can explicitly
   559         -    disambiguate it by prefixing it with "tag:". For instance:
   560         -
   561         -    fossil update decaf
   562         -
   563         -    will be taken as an artifact or check-in ID and fossil will
   564         -    probably complain that no such revision was found. However
   565         -
   566         -    fossil update tag:decaf
   567         -
   568         -    will assume that "decaf" is a tag/branch name.
   569         -
   570         -<hr><a href="#tof">&#710;</a>
   571         -    <a name="timeline">Usage: </a><a href="cmd_timeline.wiki">fossil timeline</a> ?WHEN? ?CHECK-IN|DATETIME? ?-n|--count N?
   572         -    Print a summary of activity going backwards in date and time
   573         -    specified or from the current date and time if no arguments
   574         -    are given.  Show as many as N (default 20) check-ins.  The
   575         -    WHEN argument can be any unique abbreviation of one of these
   576         -    keywords:
   577         -
   578         -    before
   579         -    after
   580         -    descendants | children
   581         -    ancestors | parents
   582         -
   583         -    The CHECK-IN can be any unique prefix of 4 characters or more.
   584         -    The DATETIME should be in the ISO8601 format.  For
   585         -    examples: "2007-08-18 07:21:21".  You can also say "current"
   586         -    for the current version or "now" for the current time.
   587         -
   588         -<hr><a href="#tof">&#710;</a>
   589         -    <a name="undo">Usage: </a><a href="cmd_undo.wiki">fossil undo</a> ?FILENAME...?
   590         -    Undo the most recent update or merge operation.  If FILENAME is
   591         -    specified then restore the content of the named file(s) but otherwise
   592         -    leave the update or merge in effect.
   593         -
   594         -    A single level of undo/redo is supported.  The undo/redo stack
   595         -    is cleared by the commit and checkout commands.
   596         -
   597         -<hr><a href="#tof">&#710;</a>
   598         -    <a name="update">Usage: </a><a href="cmd_update.wiki">fossil update</a> ?VERSION? ?--latest?
   599         -    The optional argument is a version that should become the current
   600         -    version.  If the argument is omitted, then use the leaf of the
   601         -    tree that begins with the current version, if there is only a    single leaf.  If there are a multiple leaves, the latest is used
   602         -    if the --latest flag is present.
   603         -
   604         -    This command is different from the "checkout" in that edits are
   605         -    not overwritten.  Edits are merged into the new version.
   606         -
   607         -<hr><a href="#tof">&#710;</a>
   608         -    <a name="user">Usage: </a><a href="cmd_user.wiki">fossil user</a> SUBCOMMAND ...  ?-R|--repository FILE?
   609         -    Run various subcommands on users of the open repository or of
   610         -    the repository identified by the -R or --repository option.
   611         -
   612         -    fossil user capabilities USERNAME ?STRING?
   613         -
   614         -    Query or set the capabilities for user USERNAME
   615         -
   616         -    fossil user default ?USERNAME?
   617         -
   618         -    Query or set the default user.  The default user is the
   619         -    user for command-line interaction.
   620         -
   621         -    fossil user list
   622         -
   623         -    List all users known to the repository
   624         -
   625         -    fossil user new ?USERNAME?
   626         -
   627         -    Create a new user in the repository.  Users can never be
   628         -    deleted.  They can be denied all access but they must continue
   629         -    to exist in the database.
   630         -
   631         -    fossil user password USERNAME
   632         -
   633         -    Change the web access password for a user.
   634         -
   635         -<hr><a href="#tof">&#710;</a>
   636         -    <a name="version">Usage: </a><a href="cmd_version.wiki">fossil version</a>
   637         -    Print the source code version number for the fossil executable.
   638         -
   639         -<hr><a href="#tof">&#710;</a>
   640         -    <a name="wiki">Usage: </a><a href="cmd_wiki.wiki">fossil wiki</a> (export|create|commit|list) WikiName
   641         -    Run various subcommands to fetch wiki entries.
   642         -
   643         -    fossil wiki export PAGENAME ?FILE?
   644         -
   645         -    Sends the latest version of the PAGENAME wiki
   646         -    entry to the given file or standard output.
   647         -
   648         -    fossil wiki commit PAGENAME ?FILE?
   649         -
   650         -    Commit changes to a wiki page from FILE or from standard.
   651         -
   652         -    fossil wiki create PAGENAME ?FILE?
   653         -
   654         -    Create a new wiki page with initial content taken from
   655         -    FILE or from standard input.
   656         -
   657         -    fossil wiki list
   658         -
   659         -    Lists all wiki entries, one per line, ordered
   660         -    case-insentively by name.
   661         -
   662         -    TODOs:
   663         -
   664         -    fossil wiki export ?-u ARTIFACT? WikiName ?FILE?
   665         -
   666         -    Outputs the selected version of WikiName.
   667         -
   668         -    fossil wiki delete ?-m MESSAGE? WikiName
   669         -
   670         -    The same as deleting a file entry, but i don't know if fossil
   671         -    supports a commit message for Wiki entries.
   672         -
   673         -    fossil wiki ?-u? ?-d? ?-s=[|]? list
   674         -
   675         -    Lists the artifact ID and/or Date of last change along with
   676         -    each entry name, delimited by the -s char.
   677         -
   678         -    fossil wiki diff ?ARTIFACT? ?-f infile[=stdin]? EntryName
   679         -
   680         -    Diffs the local copy of a page with a given version (defaulting
   681         -    to the head version).
   682         -
   683         -  </pre></nowiki>
   684         -
   685         -  <hr><a href="#tof">&#710;</a>
   686         -
   687         -    <h3>Caveats</h3>
   688         -    This is not actually a reference, it's the start of a reference.
   689         -    There are wikilinks to uncreated pages for the commands.  This was
   690         -    created by running the fossil help for each command listed by running
   691         -    fossil help...  Duplicate commands are only listed once (I
   692         -    <i>think</i>).  There are several bits of <b>fossil</b> that are not addressed
   693         -    in the help for commands (special wiki directories, special users, etc.)
   694         -    so they are (currently) not addressed here.  Clarity and brevity may be
   695         -    sacrificed for expediency at the authors indiscretion.  All spelling and
   696         -    grammatical mistakes are somebody elses fault.<code>  void * </code>
   697         -    prohibited where<code> __C_PLUS_PLUS__ </code>. Title and taxes extra.
   698         -    Not valid in Hooptigonia.

Changes to www/reviews.wiki.

     1         -<h2 align="center">What People Are Saying About Fossil</h2>
            1  +<title>Reviews</title>
            2  +<b>External links:</b>
            3  + 
            4  +  *  [http://sheddingbikes.com/posts/1276624594.html | Why I Use Fossil]
            5  +     by Zed A. Shaw.
            6  +  *  [http://nixtu.blogspot.com/2010/03/fossil-dvcs-on-go-first-impressions.html |
            7  +     Fossil DVCS on the Go - First Impressions]
            8  +  *  [http://blog.fupps.com/2010/12/04/exploring-the-fossil-dvcs |
            9  +     Exploring the Fossil DVCS] by Jan-Piet Mens.  <b>(Dead link)</b>
           10  +  *  [http://blog.mired.org/2011/02/fossil-sweet-spot-in-vcs-space.html |
           11  +     Fossil - a sweet spot in the VCS space] by Mike Meyer.
           12  +  *  [http://blog.s11n.net/?p=72|Four reasons to take a closer look at the Fossil SCM] by Stephan Beal
           13  +
           14  +<b>See Also:</b>
           15  +
           16  +  *  [./quotes.wiki | Short Quotes on Fossil, Git, And DVCSes]
     2     17   
     3     18   <b>Daniel writes on 2009-01-06:</b>
     4     19   
     5     20   <blockquote>
     6     21   The reasons I use fossil are that it's the only version control I
     7     22   have found that I can get working through the VERY annoying MS
     8     23   firewalls at work.. (albeit through an ntlm proxy) and I just love
     9     24   single .exe applications!
    10     25   </blockquote>
    11     26   
    12         -<b>Stephen Beal writes on 2009-01-11:</b>
           27  +
           28  +<b>Joshua Paine on 2010-10-22:</b>
           29  +
           30  +<blockquote>
           31  +With one of my several hats on, I'm in a small team using git. Another 
           32  +team member just checked some stuff into trunk that should have been on 
           33  +a branch. Nothing else had happened since, so in fossil I would have 
           34  +just edited that commit and put it on a new branch. In git that can't 
           35  +actually be done without danger once other people have pulled, so I had 
           36  +to create a new commit rolling back the changes, then branch and cherry 
           37  +pick the earlier changes, then figure out how to make my new branch 
           38  +shared instead of private. Just want to say thanks for fossil making my 
           39  +life easier on most of my projects, and being able to move commits to 
           40  +another branch after the fact and shared-by-default branches are good 
           41  +features. Also not having a misanthropic command line interface.
           42  +</blockquote>
           43  +
           44  +<b>Stephan Beal writes on 2009-01-11:</b>
    13     45   
    14     46   <blockquote>
    15     47   Sometime in late 2007 I came across a link to fossil on 
    16     48   <a href="http://www.sqlite.org/">sqlite.org</a>. It
    17     49   was a good thing I bookmarked it, because I was never able to find the
    18     50   link again (it might have been in a bug report or something). The
    19     51   reasons I first took a close look at it were (A) it stemmed from the
................................................................................
   102    134   Firefox, or the Linux Kernel), but 99.9% of projects never reach
   103    135   anywhere near that size or complexity.
   104    136   
   105    137   
   106    138   In summary:
   107    139   
   108    140   I remember my first reaction to fossil being, "this will be an
   109         -excellent solution for small projects [like the dozens we've all got
          141  +excellent solution for small projects (like the dozens we've all got
   110    142   sitting on our hard drives but which don't justify the hassle of
   111         -version control]." A year of daily use in over 15 source trees has
          143  +version control)." A year of daily use in over 15 source trees has
   112    144   confirmed that, and I continue to heartily recommend fossil to other
   113    145   developers I know who also have their own collection of "unhosted" pet
   114    146   projects.
   115    147   </blockquote>

Changes to www/selfcheck.wiki.

    11     11   Fossil has been hosting itself and many other projects for
    12     12   years now.  Many bugs have been encountered.  But, thanks in large
    13     13   part to the defensive measures described here, no data has been
    14     14   lost.  The integrity checks are doing their job well.</p>
    15     15   
    16     16   <h2>Atomic Check-ins With Rollback</h2>
    17     17   
    18         -The fossil repository is an
    19         -<a href="http://www.sqlite.org/">SQLite version 3</a> database file.  
           18  +The fossil repository is stored in an
           19  +<a href="http://www.sqlite.org/">SQLite</a> database file.  
           20  +([./tech_overview.wiki | Addition information] about the repository
           21  +file format.)
    20     22   SQLite is very mature and stable and has been in wide-spread use for many
    21     23   years, so we are confident it will not cause repository
    22     24   corruption.  SQLite
    23     25   databases do not corrupt even if a program or system crash or power
    24     26   failure occurs in the middle of the update.  If some kind of crash
    25     27   does occur in the middle of a change, then all the changes are rolled
    26     28   back the next time that the database is accessed.
    27     29   
    28     30   A check-in operation in fossil makes many changes to the repository
    29     31   database.  But all these changes happen within a single transaction.
    30         -If something goes wrong in the middle of the commit, then the transaction
           32  +If something goes wrong in the middle of the commit, even if that something
           33  +is a power failure or OS crash, then the transaction
    31     34   is rolled back and the database is unchanged.
    32     35   
    33     36   <h2>Verification Of Delta Encodings Prior To Transaction Commit</h2>
    34     37   
    35         -The content files that comprise the global state of a fossil respository
           38  +The content files that comprise the global state of a fossil repository
    36     39   are stored in the repository as a tree.  The leaves of the tree are
    37     40   stored as zlib-compressed BLOBs.  Interior nodes are deltas from their
    38         -decendants.  A lot of encoding is going on.  There is
           41  +descendants.  A lot of encoding is going on.  There is
    39     42   zlib-compression which is relatively well-tested but still might
    40     43   cause corruption if used improperly.  And there is the relatively
    41     44   new delta-encoding mechanism designed expressly for fossil.  We want
    42     45   to make sure that bugs in these encoding mechanisms do not lead to
    43     46   loss of data.
    44     47   
    45     48   To increase our confidence that everything in the repository is
................................................................................
    62     65   Hence bugs in fossil are unlikely to corrupt the repository in
    63     66   a way that prevents us from extracting historical versions of 
    64     67   files.
    65     68   
    66     69   <h2>Checksum Over All Files In A Check-in</h2>
    67     70   
    68     71   Manifest artifacts that define a check-in have two fields (the
    69         -R-card and Z-card) that record MD5 hashs of the manifest itself
           72  +R-card and Z-card) that record MD5 hashes of the manifest itself
    70     73   and of all other files in the manifest.  Prior to any check-in
    71         -commit, these checksums are verified to ensure that the check-in
    72         -checked in agrees exactly with what is on disk.  Similarly,
           74  +commit, these checksums are verified to ensure that the checkin
           75  +agrees exactly with what is on disk.  Similarly,
    73     76   the repository checksum is verified after a checkout to make
    74     77   sure that the entire repository was checked out correctly.
    75     78   Note that these added checks use a different hash (MD5 instead
    76     79   of SHA1) in order to avoid common-mode failures in the hash
    77     80   algorithm implementation.
    78     81   
    79     82   

Changes to www/selfhost.wiki.

     1      1   <title>Fossil Self-Hosting Repositories</title>
     2         -<h1 align="center">Fossil Self-Hosting Repositories</h1>
     3      2   
     4      3   Fossil has self-hosted since 2007-07-21.  As of this writing (2009-08-24)
     5      4   there are three publicly accessible repositories for the Fossil source code:
     6      5   
     7      6     1.  [http://www.fossil-scm.org/]
     8         -  2.  [http://www.hwaci.com/cgi-bin/fossil]
     9         -  3.  [http://www2.fossil-scm.org/]
            7  +  2.  [http://www2.fossil-scm.org/]
            8  +  3.  [http://www3.fossil-scm.org/site.cgi]
    10      9   
    11     10   
    12     11   The canonical repository is (1).  Repositories (2) and (3) automatically
    13     12   stay in synchronization with (1) via a 
    14     13   <a href="http://en.wikipedia.org/wiki/Cron">cron job</a> that invokes
    15     14   "fossil sync" at regular intervals.
    16     15   
................................................................................
    17     16   Note that the two secondary repositories are more than just read-only mirrors.
    18     17   All three servers support full read/write capabilities.
    19     18   Changes (such as new tickets or wiki or check-ins) can be implemented 
    20     19   on any of the three servers and those changes automatically propagate to the
    21     20   other two servers.
    22     21   
    23     22   Server (1) runs as a CGI script on a
    24         -<a href="http://www.linode.com/">Linode 720</a> located in Dallas, TX
           23  +<a href="http://www.linode.com/">Linode 1024</a> located in Dallas, TX
    25     24   - on the same virtual machine that 
    26     25   hosts <a href="http://www.sqlite.org/">SQLite</a> and over a
    27     26   dozen other smaller projects.  This demonstrates that Fossil does not
    28     27   require much server power.
    29     28   Multiple fossil-based projects can easily be hosted on the same machine,
    30     29   even if that machine is itself one of several dozen virtual machines on
    31     30   single physical box.  The CGI script that runs the canonical Fossil
................................................................................
    32     31   self-hosting repository is as follows:
    33     32   
    34     33   <blockquote><pre>
    35     34   #!/usr/bin/fossil
    36     35   repository: /fossil/fossil.fossil
    37     36   </pre></blockquote>
    38     37   
    39         -Server (2) runs as a CGI script on a shared hosting account at
    40         -<a href="http://www.he.net/">Hurricane Electric</a> in San Jose and
    41         -Fremont, CA.  This server demonstrates the ability of
           38  +Server (3) runs as a CGI script on a shared hosting account at
           39  +<a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA.  
           40  +This server demonstrates the ability of
    42     41   Fossil to run on an economical shared-host web account with no
    43     42   privileges beyond port 80 HTTP access and CGI.  It is not necessary
    44     43   to have a dedicated server to run Fossil.  As far as we are aware, 
    45     44   Fossil is the only full-featured configuration management system 
    46     45   that can run in
    47     46   such a restricted environment.  The CGI script that runs on the
    48     47   Hurricane Electric server is the same as the CGI script shown above,
    49         -except that the pathnames are modified to suite the environment:
           48  +except that the pathnames are modified to suit the environment:
    50     49   
    51     50   <blockquote><pre>
    52     51   #!/home/hwaci/bin/fossil
    53     52   repository: /home/hwaci/fossil/fossil.fossil
    54     53   </pre></blockquote>
    55     54   
    56         -Server (2) is synchronized with the canonical server (1) by running
           55  +Server (3) is synchronized with the canonical server (1) by running
    57     56   the following command via cron:
    58     57   
    59     58   <blockquote><pre>
    60     59   /home/hwaci/bin/fossil sync -R /home/hwaci/fossil/fossil.fossil
    61     60   </pre></blockquote>
    62     61   
    63         -Server (3) is a
    64         -<a href="http://www.linode.com/">Linode 360</a> located in Atlanta, GA
           62  +Server (2) is a
           63  +<a href="http://www.linode.com/">Linode 512</a> located in Newark, NJ
    65     64   and set up just like the canonical server (1) with the addition of a
    66         -cron job for synchronization as in server (2).
           65  +cron job for synchronization as in server (3).

Changes to www/server.wiki.

            1  +<title>How To Configure A Fossil Server</title>
     1      2   <nowiki>
     2         -<h1 align="center">Server Setup Guide</h1>
     3      3   <p>This guide is intended to help guide you in setting up a Fossil server.</p>
     4      4   
     5      5   <h2>Standalone server</h2><blockquote>
     6      6   The easiest way to set up a Fossil server is to use the <tt>server</tt> or
     7      7   <tt>ui</tt> command.  Assuming the repository you are interested in serving is
     8      8   in the file "<tt>repo.fossil</tt>", you can use either of these commands to
     9      9   start Fossil as a server:
................................................................................
    13     13   </ul>
    14     14   
    15     15   <p>
    16     16   Both of these commands start a Fossil server on port 8080 on the local machine,
    17     17   which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any
    18     18   handy web browser.  The difference between the two commands is that "ui", in
    19     19   addition to starting the Fossil server, also starts a web browser and points it
    20         -to the URL mentioned above.
           20  +to the URL mentioned above.  On the other hand, the "ui" command binds to
           21  +the loopback IP address only (127.0.0.1) so that the "ui" command cannot be
           22  +used to serve content to a different machine.
    21     23   </p>
    22     24   <p>
    23     25   NOTES:
    24     26   <ol>
    25     27   <li>The option "--port NNN" will start the server on port "NNN" instead of 8080.  
    26     28   <li>If port 8080 is already being used (perhaps by another Fossil server), then
    27     29   Fossil will use the next available port number.
................................................................................
    54     56   </p>
    55     57   </blockquote>
    56     58   
    57     59   <h3>One script per repository</h3><blockquote>
    58     60   <p>
    59     61   Create a script (let's call it 'repo') in your CGI directory which has content like this:
    60     62   <blockquote><tt>
    61         -#!/path-to/fossil
           63  +#!/path-to/fossil<br>
    62     64   repository: /path-to-repo/repository
    63     65   </tt></blockquote>
    64     66   </p>
    65     67   <p>
    66     68   It may be necessary to set permissions properly, or to modify an ".htaccess" file or other server-specific things like that.  Consult with your server provider if you need that sort of assistance.
    67     69   </p>
    68     70   <p>
    69     71   Once the script is set up correctly, and assuming your server is also set correctly, you should be able to access your repository with a URL like: <tt>http://mydomain.org/cgi-bin/repo</tt> (assuming the "repo" script is accessible under "cgi-bin", which would be a typical deployment on Apache for instance).
    70     72   </p>
    71     73   </blockquote>
    72     74   
    73     75   <h3>Serving multiple repositories with one script</h3><blockquote>
    74     76   <p>
    75         -This scenario is almost identical to the previous one.  However, here we will assume you have multiple repositories, in one directory (call it 'fossils').  So as before, create a script (again, 'repo'):
           77  +This scenario is almost identical to the previous one.  However, here we will assume you have multiple repositories, in one directory.
           78  +(Call the directory 'fossils').  All repositories served, in this case, must
           79  +use the ".fossil" filename suffix.
           80  +As before, create a script (again, 'repo'):
    76     81   <blockquote><tt>
    77         -#!/path-to/fossil
    78         -directory: /path-to-repo/fossils
           82  +#!/path-to/fossil<br>
           83  +directory: /path-to-repo/fossils<br>
    79     84   notfound: http://url-to-go-to-if-repo-not-found/
    80     85   </tt></blockquote>
    81     86   </p>
    82     87   <p>
    83         -Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX" (if it exists).  This makes serving multiple projects on one server pretty painless.
           88  +Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX.fossil" (if it exists).  This makes serving multiple projects on one server pretty painless.
    84     89   </p>
    85     90   </blockquote>
    86     91   
    87     92   <h2>Securing a repository with SSL</h2><blockquote>
    88     93   <p>
    89     94   Using either of the CGI script approaches, it is trivial to use SSL to secure the server.  Simply set up the Fossil CGI scripts etc. as above, but modify the Apache (or IIS, etc.) server to require SSL (that is, a URL with "https://") in order to access the CGI script directory.  This may also be accomplished (on Apache, at least) using appropriate ".htaccess" rules.
    90     95   </p>
    91     96   <p>
    92     97   If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil.
    93     98   </p>
    94     99   <p>
    95    100   At this stage, the standalone server (e.g. "fossil server") does not support SSL.
    96    101   </p>
          102  +<p>
          103  +For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>.
          104  +</p>
    97    105   </blockquote>
    98    106   
    99    107   <h2>Various security concerns with hosted repositories</h2><blockquote>
   100    108   <p>
   101    109   There are two main concerns relating to usage of Fossil for sharing sensitive information (source or any other data):
   102    110   <ul>
   103    111   <li>Interception of the Fossil synchronization stream, thereby capturing data, and

Added www/settings.wiki.

            1  +<title>Fossil Settings</title>
            2  +
            3  +<h2>Using Fossil Settings</h2>
            4  +
            5  +Settings control the behaviour of fossil. They are set with the <tt>fossil settings</tt> command, or through the web interface in the Settings page in the Admin section.
            6  +
            7  +For a list of all settings, view the Settings page, or type <tt>fossil help settings</tt> from the command line.
            8  +
            9  +
           10  +<h3>Repository settings</h3>
           11  +
           12  +Settings are set on a per-repository basis. When you clone a repository, a subset of settings are copied to your local repository.
           13  +
           14  +If you make a change to a setting on your local repository, it is not synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If you make a change on the server, you need to manually make the change on all repositories which are cloned from this repository.
           15  +
           16  +You can also set a setting globally on your local machine. The value will be used for all repositories cloned to your machine, unless overridden explicitly in a particular repository. Global settings can be set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> command.
           17  +
           18  +
           19  +<h3>"Versionable" settings</h3>
           20  +
           21  +Most of the settings control the behaviour of fossil on your local machine, largely acting to reflect your preference on how you want to use Fossil, how you communicate with the server, or options for hosting a repository on the web. 
           22  +
           23  +However, for historical reasons, some settings affect how you work with versioned files. These are <tt>allow-symlinks</tt>, <tt>binary-glob</tt>, <tt>crnl-glob</tt>, <tt>ignore-glob</tt>, <tt>empty-dirs</tt> and <tt>manifest</tt>. The most important is <tt>ignore-glob</tt> which specifies which files should be ignored when looking for unmanaged files with the <tt>extras</tt> command.
           24  +
           25  +Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the <tt>settings</tt> command or the web interface, you can created versioned files in the <tt>.fossil-settings</tt> directory named with the setting name. The contents of the file is the value of the setting, and these files are checked in, committed, merged, and so on, as with any other file.
           26  +
           27  +Where a setting is a list of values, such as <tt>ignore-glob</tt>, you can also a newline as a separator as well as a comma.
           28  +
           29  +For example, to set the list of ignored files, create a <tt>.fossil-settings/ignore-glob</tt> file where each line contains a glob for ignored files.
           30  +
           31  +If you set the value of a setting using the <tt>settings</tt> command as well as a versioned file, the versioned setting will take precedence. A warning will be displayed.

Changes to www/shunning.wiki.

    36     36   <h3>Shunning lists are local state</h3>
    37     37   
    38     38   The shunning list is part of the local state of a Fossil repository.
    39     39   In other words, shunning does not propagate using the normal "sync"
    40     40   mechanism.  An artifact can be
    41     41   shunned from one repository but be allowed to exist in another.  The fact that
    42     42   the shunning list does not propagate is a security feature.  If the
    43         -shunning list propagated then a malecious user (or
           43  +shunning list propagated then a malicious user (or
    44     44   a bug in the fossil code) might introduce a shun record that would
    45         -propagate through all respositories in a network and permanently 
           45  +propagate through all repositories in a network and permanently 
    46     46   destroy vital information.  By refusing to propagate the shunning list,
    47     47   Fossil insures that no remote user will ever be able to remove 
    48     48   information from your personal repositories without your permission.
    49     49   
    50     50   The shunning list does not propagate by the normal "sync" mechanism,
    51     51   but it is still possible to copy shuns from one repository to another
    52     52   using the "configuration" command:
................................................................................
    55     55       <b>fossil configuration push shun</b> <i>remote-url</i>
    56     56   
    57     57   The two command above will pull or push shunning lists from or to
    58     58   the <i>remote-url</i> indicated and merge the lists on the receiving
    59     59   end.  "Admin" privilege on the remote server is required in order to
    60     60   push a shun list.  
    61     61   
    62         -Note that the shunning list remains in the respository even after the
           62  +Note that the shunning list remains in the repository even after the
    63     63   shunned artifact has been removed.  This is to prevent the artifact
    64     64   from being reintroduced into the repository the next time it syncs with
    65     65   another repository that has not shunned the artifact.
    66     66   
    67     67   <h3>Managing the shunning list</h3>
    68     68   
    69     69   The complete shunning list for a repository can be viewed by a user
    70     70   with "admin" privilege on the "/shunned" URL of the web interface to Fossil.  
    71     71   That URL is accessible under the "Admin" button on the default menu
    72     72   bar.  Items can be added to or removed from the shunning list.  "Sync"
    73     73   operations are inhibited as soon as the artifact is added to the
    74     74   shunning list, but the content of the artifact is not actually removed
    75         -from the responstory until the next time the repository is rebuilt.
           75  +from the repository until the next time the repository is rebuilt.
    76     76   
    77     77   When viewing individual artifacts with the web interface, "admin"
    78     78   users will usually see a "Shun" option in the submenu that will take
    79     79   them directly to the shunning page and enable that artifact to be
    80     80   shunned with a single additional mouse click.

Added www/ssl.wiki.

            1  +<title>SSL and Fossil</title>
            2  +
            3  +<h2>Using SSL with Fossil</h2>
            4  +
            5  +If you are storing sensitive information in your repository, you should use SSL to encrypt all communications. This will protect the credentials used to access the server, as well preventing eavesdropping of the contents of your repository.
            6  +
            7  +To host a repository with SSL, you need to use an web server which supports SSL in front of the Fossil server. You can host it using the CGI option or by proxying Fossil's built in HTTP server.
            8  +
            9  +Your fossil client must be built with SSL support. The <tt>configure</tt> script will attempt to find OpenSSL on your system, but if necessary, you can specify the location with the <tt>--with-openssl</tt> option. Type <tt>./configure --help</tt> for details.
           10  +
           11  +Make sure the URL you clone from uses the <tt>https:</tt> scheme to ensure you're using SSL. If your server is configured to serve the repository from http as well as https, it's easy to accidentally use unencrypted HTTP if you forget the all important 's'.
           12  +
           13  +
           14  +<h2>Certificates</h2>
           15  +
           16  +To verify the identify of a server, SSL uses certificates. Fossil needs to know which certificates you trust.
           17  +
           18  +If you are using a self-signed certificate, you'll be asked if you want to accept the certificate the first time you communicate with the server. Verify the certificate fingerprint is correct, then answer "always" to remember your decision.
           19  +
           20  +If you are using a certificate signed by a certificate authority, you need to specify the certificates you trust with the <tt>ssl-ca-location</tt> setting. Set this globally with the <tt>-global</tt> option for convenience.
           21  +
           22  +This should be set to the location of a file containing all the PEM encoded certificates you trust. You can obtain a certificate using a web browser, for example, Firefox, or just refer to your system's trusted CA roots which are usually stored somewhere in <tt>/etc</tt>.
           23  +
           24  +
           25  +<h2>Client side certificates</h2>
           26  +
           27  +You can also use client side certificates to add an extra layer of authentication, over and above Fossil's built in user management. If you are particularly paranoid, you'll want to use this to remove the ability of anyone on the internet from making any request to Fossil. Without presenting a valid client side certificate, the web server won't invoke the fossil CGI handler.
           28  +
           29  +Configure your server to request a client side certificate, and set up a certificate authority to sign your client certificates. For each person who needs to access the repository, create a private key and certificate signed with that CA.
           30  +
           31  +The PEM encoded private key and certificate should be stored in a single file, simply by concatenating the key and certificate files. Specify the location of this file with the <tt>ssl-identity</tt> setting, or the <tt>--ssl-identity</tt> option to the <tt>clone</tt> command.
           32  +
           33  +If you've password protected the private key, the password will be requested every time you connect to the server. This password is not stored by fossil, as doing so would defeat the purpose of having a password.
           34  +
           35  +If you attempt to connect to a server which requests a client certificate, but don't provide one, fossil will show an error message which explains what to do to authenticate with the server.
           36  +

Changes to www/stats.wiki.

     1      1   <title>Fossil Performance</title>
     2      2   <h1 align="center">Performance Statistics</h1>
     3      3   
     4      4   The questions will inevitably arise:  How does Fossil perform? 
     5      5   Does it use a lot of disk space or bandwidth?  Is it scalable?
     6      6   
     7         -In an attempt to answers these questions, this report looks at five
            7  +In an attempt to answers these questions, this report looks at several
     8      8   projects that use fossil for configuration management and examines how
     9      9   well they are working.  The following table is a summary of the results.
           10  +(Last updated on 2012-02-26.)
    10     11   Explanation and analysis follows the table.
    11     12   
    12     13   <table border=1>
    13     14   <tr>
    14     15   <th>Project</th>
    15     16   <th>Number Of Artifacts</th>
    16     17   <th>Number Of Check-ins</th>
................................................................................
    19     20   <th>Uncompressed Size</th>
    20     21   <th>Repository Size</th>
    21     22   <th>Compression Ratio</th>
    22     23   <th>Clone Bandwidth</th>
    23     24   </tr>
    24     25   
    25     26   <tr align="center">
    26         -<td>SQLite
    27         -<td>28643
    28         -<td>6755
    29         -<td>3373&nbsp;days<br>9.24&nbsp;yrs
    30         -<td>2.00
    31         -<td>1.27&nbsp;GB
    32         -<td>35.4&nbsp;MB
    33         -<td>35:1
    34         -<td>982&nbsp;KB&nbsp;up<br>12.4&nbsp;MB&nbsp;down
           27  +<td>[http://www.sqlite.org/src/timeline | SQLite]
           28  +<td>41113
           29  +<td>9943
           30  +<td>4290&nbsp;days<br>11.75&nbsp;yrs
           31  +<td>2.32
           32  +<td>2.09&nbsp;GB
           33  +<td>33.2&nbsp;MB
           34  +<td>63:1
           35  +<td>23.2&nbsp;MB
           36  +</tr>
           37  +
           38  +<tr align="center">
           39  +<td>[http://core.tcl.tk/tcl/timeline | TCL]
           40  +<td>74806
           41  +<td>13541
           42  +<td>5085&nbsp;days<br>13.92&nbsp;yrs
           43  +<td>2.66
           44  +<td>5.2&nbsp;GB
           45  +<td>86&nbsp;MB
           46  +<td>60:1
           47  +<td>67.0&nbsp;MB
           48  +</tr>
           49  +
           50  +<tr align="center">
           51  +<td>[/timeline | Fossil]
           52  +<td>15561
           53  +<td>3764
           54  +<td>1681&nbsp;days<br>4.6&nbsp;yrs
           55  +<td>2.24
           56  +<td>721&nbsp;MB
           57  +<td>18.8&nbsp;MB
           58  +<td>38:1
           59  +<td>12.0&nbsp;MB
    35     60   </tr>
    36     61   
    37     62   <tr align="center">
    38         -<td>Fossil
    39         -<td>4981
    40         -<td>1272
    41         -<td>764&nbsp;days<br>2.1&nbsp;yrs
    42         -<td>1.66
    43         -<td>144&nbsp;MB
    44         -<td>8.74&nbsp;MB
    45         -<td>16:1
    46         -<td>128&nbsp;KB&nbsp;up<br>4.49&nbsp;MB&nbsp;down
           63  +<td>[http://www.sqlite.org/slt/timeline | SLT]
           64  +<td>2174
           65  +<td>100
           66  +<td>1183&nbsp;days<br>3.24&nbsp;yrs
           67  +<td>0.08
           68  +<td>1.94&nbsp;GB
           69  +<td>143&nbsp;MB
           70  +<td>12:1
           71  +<td>141&nbsp;MB
    47     72   </tr>
    48     73   
    49     74   <tr align="center">
    50         -<td>SLT
    51         -<td>2062
    52         -<td>67
    53         -<td>266&nbsp;days
    54         -<td>0.25
    55         -<td>1.76&nbsp;GB
    56         -<td>147&nbsp;MB
    57         -<td>11:1
    58         -<td>1.1&nbsp;MB&nbsp;up<br>141&nbsp;MB&nbsp;down
           75  +<td>[http://www.sqlite.org/th3.html | TH3]
           76  +<td>5624
           77  +<td>1472
           78  +<td>1248&nbsp;days<br>3.42&nbsp;yrs
           79  +<td>1.78
           80  +<td>252&nbsp;MB
           81  +<td>12.5&nbsp;MB
           82  +<td>20:1
           83  +<td>12.2&nbsp;MB
    59     84   </tr>
    60     85   
    61     86   <tr align="center">
    62         -<td>TH3
    63         -<td>1999
    64         -<td>429
    65         -<td>331&nbsp;days
    66         -<td>1.30
    67         -<td>70.5&nbsp;MB
    68         -<td>6.3&nbsp;MB
    69         -<td>11:1
    70         -<td>55&nbsp;KB&nbsp;up<br>4.66&nbsp;MB&nbsp;down
    71         -</tr>
    72         -
    73         -<tr align="center">
    74         -<td>SQLite Docs
    75         -<td>1787
    76         -<td>444
    77         -<td>650&nbsp;days<br>1.78&nbsp;yrs
    78         -<td>0.68
    79         -<td>43&nbsp;MB
    80         -<td>4.9&nbsp;MB
    81         -<td>8:1
    82         -<td>46&nbsp;KB&nbsp;up<br>3.35&nbsp;MB&nbsp;down
           87  +<td>[http://www.sqlite.org/docsrc/timeline | SQLite Docs]
           88  +<td>3664
           89  +<td>1003
           90  +<td>1567&nbsp;days<br>4.29&nbsp;yrs
           91  +<td>0.64
           92  +<td>108&nbsp;MB
           93  +<td>6.6&nbsp;MB
           94  +<td>16:1
           95  +<td>5.71&nbsp;MB
    83     96   </tr>
    84     97   
    85     98   </table>
    86     99   
    87         -<h2>The Five Projects</h2>
    88         -
    89         -The five projects listed above were chosen because they have been in
    90         -existance for a long time (relative to the age of fossil) or because
    91         -they have larges amounts of content.  The most important project using
    92         -fossil is SQLite.  Fossil itself
    93         -is built on top of SQLite and so obviously SQLite has to predate fossil.
    94         -SQLite was originally versioned using CVS, but recently the entire 9-year
    95         -and 320-MB CVS history of SQLite was converted over to Fossil.  This is
    96         -an important datapoint because it demonstrates fossil's ability to manage
    97         -a significant and long-running project.
    98         -The next-longest running fossil project is fossil itself, at 2.1 years.
    99         -The documentation for SQLite
   100         -(identified above as "SQLite Docs") was split off of the main SQLite 
   101         -source tree and into its own fossil repository about 1.75 years ago.
   102         -The "SQL Logic Test" or "SLT" project is a massive
   103         -collection of SQL statements and their output used to compare the 
   104         -processing of SQLite against MySQL, PostgreSQL, Microsoft SQL Server,
   105         -and Oracle.  
   106         -Finally "TH3" is a proprietary set of test cases for SQLite used to give
   107         -100% branch test coverage of SQLite on embedded platforms.  All projects
   108         -except for TH3 are open-source.
   109         -
   110    100   <h2>Measured Attributes</h2>
   111    101   
   112         -In fossil, every version of every file, every wiki page, every change to
          102  +In Fossil, every version of every file, every wiki page, every change to
   113    103   every ticket, and every check-in is a separate "artifact".  One way to
   114         -think of a fossil project is as a bag of artifacts.  Of course, there is
   115         -a lot more than this going on in fossil.  Many of the artifacts have meaning
          104  +think of a Fossil project is as a bag of artifacts.  Of course, there is
          105  +a lot more than this going on in Fossil.  Many of the artifacts have meaning
   116    106   and are related to other artifacts.  But at a low level (for example when
   117    107   synchronizing two instances of the same project) the only thing that matters
   118    108   is the unordered collection of artifacts.  In fact, one of the key 
   119         -characteristics of fossil is that the entire project history can be
          109  +characteristics of Fossil is that the entire project history can be
   120    110   reconstructed simply by scanning the artifacts in an arbitrary order.
   121    111   
   122    112   The number of check-ins is the number of times that the "commit" command
   123    113   has been run.  A single check-in might change a 3 or 4 files, or it might
   124         -change several dozen different files.  Regardless of the number of files
          114  +change dozens or hundreds of files.  Regardless of the number of files
   125    115   changed, it still only counts as one check-in.
   126    116   
   127    117   The "Uncompressed Size" is the total size of all the artifacts within
   128         -the fossil repository assuming they were all uncompressed and stored 
          118  +the repository assuming they were all uncompressed and stored 
   129    119   separately on the disk.  Fossil makes use of delta compression between related
   130    120   versions of the same file, and then uses zlib compression on the resulting
   131    121   deltas.  The total resulting repository size is shown after the uncompressed
   132         -size.  
          122  +size.  For this chart, "fossil rebuild --compress" was run on each repository
          123  +prior to measuring its compressed size.  Repository sizes would typically 
          124  +be 20% larger without that rebuild.
   133    125   
   134    126   On the right end of the table, we show the "Clone Bandwidth".  This is the
   135         -total number of bytes sent from client to server ("uplink") and from server
   136         -back to client ("downlink") in order to clone a repository.  These byte counts
   137         -include HTTP protocol overhead.
          127  +total number of bytes sent from server back to the client.  The number of
          128  +bytes sent from client to server is neglible in comparison.
          129  +These byte counts include HTTP protocol overhead.
   138    130   
   139    131   In the table and throughout this article,
   140    132   "GB" means gigabytes (10<sup><small>9</small></sup> bytes)
   141    133   not <a href="http://en.wikipedia.org/wiki/Gibibyte">gibibytes</a>
   142    134   (2<sup><small>30</small></sup> bytes).  Similarly, "MB" and "KB"
   143    135   means megabytes and kilobytes, not mebibytes and kibibytes.
   144    136   
   145    137   <h2>Analysis And Supplimental Data</h2>
   146    138   
   147    139   Perhaps the two most interesting datapoints in the above table are SQLite
   148    140   and SLT.  SQLite is a long-running project with long revision chains.
   149         -Some of the files in SQLite have been edited close to a thousand times.
          141  +Some of the files in SQLite have been edited over a thousand times.
   150    142   Each of these edits is stored as a delta, and hence the SQLite project
   151         -gets excellent 35:1 compression.  SLT, on the other hand, consists of
          143  +gets excellent 63:1 compression.  SLT, on the other hand, consists of
   152    144   many large (megabyte-sized) SQL scripts that have one or maybe two
   153         -versions.  There is very little delta compression occurring and so the
          145  +edits each.  There is very little delta compression occurring and so the
   154    146   overall repository compression ratio is much lower.  Note also that
   155    147   quite a bit more bandwidth is required to clone SLT than SQLite.
   156    148   
   157    149   For the first nine years of its development, SQLite was versioned by CVS.
   158    150   The resulting CVS repository measured over 320MB in size.  So, the
   159         -developers were
   160         -pleasently surprised to see that this entire project could be cloned in
   161         -fossil using only about 13MB of network traffic.  The "sync" protocol
          151  +developers were surprised to see that this entire project could be cloned in
          152  +fossil using only about 23.2MB of network traffic.  (This 23.2MB includes
          153  +all the changes to SQLite that have been made since the conversion from
          154  +CVS.  Of those changes are omitted, the clone bandwidth drops to 13MB.)
          155  +The "sync" protocol
   162    156   used by fossil has turned out to be surprisingly efficient.  A typical
   163    157   check-in on SQLite might use 3 or 4KB of network bandwidth total.  Hardly
   164    158   worth measuring.  The sync protocol is efficient enough that, once cloned,
   165         -fossil could easily be used over a dial-up connection.
          159  +Fossil could easily be used over a dial-up connection.

Added www/style.wiki.

            1  +<title>Coding Style</title>
            2  +
            3  +Fossil source code should following the style guidelines below.
            4  +
            5  +<b>General points:</b>:
            6  +
            7  +  10.  No line of code exceeds 80 characters in length.  (Occasional
            8  +       exceptions are made for HTML text on @-lines.)
            9  +
           10  +  11.  There are no tab characters.
           11  +
           12  +  12.  Line terminators are \n only.  Do not use a \r\n line terminator.
           13  +
           14  +  13.  2-space indentation is used.  Remember:  No tabs.
           15  +
           16  +  14.  Comments contain no spelling or grammatical errors.  (Abbreviations
           17  +       and sentence fragments are acceptable when trying to fit a comment
           18  +       on a single line as long as the meaning is clear.)
           19  +
           20  +  15.  The tone of comments is professional and courteous.  Comments
           21  +       contain no profanity, obscenity, or innuendo.
           22  +
           23  +  16.  All C-code conforms to ANSI C-89.
           24  +
           25  +  17.  All comments and identifiers are in English.
           26  +
           27  +  18.  The program is single-threaded.  Do not use threads.
           28  +       (One exception to this is the HTTP server implementation for windows,
           29  +       which we do not know how to implement without the use of threads.)
           30  +
           31  +
           32  +<b>C preprocessor macros</b>:
           33  +
           34  +  20.  The purpose of every preprocessor macros is clearly explained in a
           35  +       comment associated with its definition.
           36  +
           37  +  21.  Every preprocessor macro is used at least once.
           38  +
           39  +  22.  The names of preprocessor macros clearly reflect their use.
           40  +
           41  +  23.  Assumptions about the relative values of related macros are
           42  +       verified by asserts.  Example: <tt>assert(READ_LOCK+1==WRITE_LOCK);</tt>
           43  +
           44  +
           45  +<b>Function header comments</b>:
           46  +
           47  +  30.  Every function has a header comment describing the purpose and use
           48  +       of the function.
           49  +
           50  +  31.  Function header comment defines the behavior of the function in
           51  +       sufficient detail to allow the function to be reimplemented from
           52  +       scratch without reference to the original code.
           53  +
           54  +  32.  Functions that perform dynamic memory allocation (either directly
           55  +       or indirectly via subfunctions) say so in their header comments.
           56  +
           57  +
           58  +<b>Function bodies</b>:
           59  +
           60  +<ol>
           61  +  <li value=40>  The name of a function clearly reflects its purpose.
           62  +
           63  +  <li> Automatic variables are small, not large objects or arrays.  Avoid
           64  +       excessive stack usage.
           65  +
           66  +  <li>  The check-list items for functions also apply to major subsections
           67  +     within a function.
           68  +
           69  +  <li>  All code subblocks are enclosed in {...}.
           70  +
           71  +
           72  +  <li> <b>assert() macros are used as follows </b>:
           73  +    <ol type="a">
           74  +
           75  +  <li>  Function preconditions are clearly stated and verified by asserts.
           76  +
           77  +  <li>  Invariants are identified by asserts.
           78  +    </ol>
           79  +
           80  +</ol>
           81  +
           82  +
           83  +<b>Class (struct) declarations</b>:
           84  +
           85  +  50.  The purpose and use of every class (a.k.a. structure) is clearly defined
           86  +     in the header comment of its declaration.
           87  +
           88  +  51.  The purpose and use of every class member is clearly defined either
           89  +     in the header comment of the class declaration or when the member is
           90  +     declared or both.
           91  +
           92  +  52.  The names of class members clearly reflect their use.
           93  +
           94  +  53.  Invariants for classes are clearly defined.
           95  +
           96  +<b>Variables and class instances</b>:
           97  +
           98  +  60.  The purpose and use of every variable is defined by a comment at the
           99  +     variable definition.
          100  +
          101  +  61.  The names of variables clearly reflect their use.
          102  +
          103  +  62.  Related variables have related names. (ex: aSavepoint and nSavepoint.)
          104  +
          105  +  63.  Variables have minimum practical scope.
          106  +
          107  +  64.  Automatic variables are small, not large objects or arrays.
          108  +
          109  +  65.  Constants are "const".
          110  +
          111  +  66.  Invariants on variables or groups of variables are defined and
          112  +     tested by asserts.
          113  +
          114  +  67.  When a variable that refers to the same value is used within
          115  +     multiple scopes, the same name is used in all cases.
          116  +
          117  +  68.  When variables refer to different values, different names are used
          118  +     even when the names are in different scopes.
          119  +
          120  +  69.  Variable names with wide scope are sufficiently distinctive to allow
          121  +     searching for them using grep.

Changes to www/sync.wiki.

     1         -<h1 align="center">The Fossil Sync Protocol</h1>
            1  +<title>The Fossil Sync Protocol</title>
     2      2   
     3      3   <p>Fossil supports commands <b>push</b>, <b>pull</b>, and <b>sync</b>
     4      4   for transferring information from one repository to another.  The
     5      5   command is run on the client repository.  A URL for the server repository
     6      6   is specified as part of the command.  This document describes what happens
     7      7   behind the scenes in order to synchronize the information on the two
     8      8   repositories.</p>
................................................................................
    24     24   SHA1 hashes for this many artifacts can be large.  So optimizations are
    25     25   employed that usually reduce the number of SHA1 hashes that need to be
    26     26   shared to a few hundred.</p>
    27     27   
    28     28   <p>Each repository also has local state.  The local state determines
    29     29   the web-page formatting preferences, authorized users, ticket formats,
    30     30   and similar information that varies from one repository to another.
    31         -The local state is not transfered by the <b>push</b>, <b>pull</b>,
    32         -and <b>sync</b> command, though some local state is transfered during
           31  +The local state is not transferred by the <b>push</b>, <b>pull</b>,
           32  +and <b>sync</b> command, though some local state is transferred during
    33     33   a <b>clone</b> in order to initialize the local state of the new
    34     34   repository.  The <b>configuration push</b> and <b>configuration pull</b>
    35     35   commands can be used to send or receive local state.</p>
    36     36   
    37     37   
    38     38   <h2>2.0 Transport</h2>
    39     39   
................................................................................
   221    221   during a clone.  This is how the client determines what project
   222    222   code to put in the new repository it is constructing.</p>
   223    223   
   224    224   <h3>3.5 Clone Cards</h3>
   225    225   
   226    226   <p>A clone card works like a pull card in that it is sent from
   227    227   client to server in order to tell the server that the client
   228         -wants to pull content.  But unlike the pull card, the clone
   229         -card has no arguments.</p>
          228  +wants to pull content.  The clone card comes in two formats.  Older
          229  +clients use the no-argument format and newer clients use the
          230  +two-argument format.</p>
          231  +
          232  +<blockquote>
          233  +<b>clone</b><br>
          234  +<b>clone</b> <i>protocol-version sequence-number</i>
          235  +</blockquote>
          236  +
          237  +<h4>3.5.1 Protocol 2</h4>
          238  +
          239  +<p>The latest clients send a two-argument clone message with a
          240  +protocol version of "2".  (Future versions of Fossil might use larger
          241  +protocol version numbers.)  The sequence-number sent is the number
          242  +of artifacts received so far.  For the first clone message, the
          243  +sequence number if 0.  The server will respond by sending file
          244  +cards for some number of artifacts up to the maximum message size.
          245  +
          246  +<p>The server will also send a single "clone_seqno" card to the client
          247  +so that the client can know where the server left off.
   230    248   
   231    249   <blockquote>
   232         -<b>clone</b>
          250  +<b>clone_seqno</b>  <i>sequence-number</i>
   233    251   </blockquote>
   234    252   
   235         -<p>In response to a clone message, the server also sends the client
          253  +<p>The clone message in subquence HTTP requests for the same clone
          254  +operation will use the sequence-number from the
          255  +clone_seqno of the previous reply.</p>
          256  +
          257  +<p>In response to an initial clone message, the server also sends the client
   236    258   a push message so that the client can discover the projectcode for
   237    259   this project.</p>
   238    260   
          261  +<h4>3.5.2 Legacy Protocol</h4>
          262  +
          263  +<p>Older clients send a clone card with no argument.  The server responds
          264  +to a blank clone card by sending an "igot" card for every artifact in the
          265  +repository.  The client will then issue "gimme" cards to pull down all the
          266  +content it needs.
          267  +
          268  +<p>The legacy protocol works well for smaller repositories (50MB with 50,000
          269  +artifacts) but is too slow and unwieldy for larger repositories.
          270  +The version 2 protocol is an effort to improve performance.  Further
          271  +performance improvements with higher-numbered clone protocols are
          272  +possible in future versions of Fossil.
          273  +
   239    274   <h3>3.6 Igot Cards</h3>
   240    275   
   241    276   <p>An igot card can be sent from either client to server or from
   242    277   server to client in order to indicate that the sender holds a copy
   243    278   of a particular artifact.  The format is:</p>
   244    279   
   245    280   <blockquote>
................................................................................
   285    320   cookie and the server must structure the cookie payload in such
   286    321   a way that it can tell if the cookie it sees is its own cookie or
   287    322   a cookie from another server.  (Typically the server will embed
   288    323   its servercode as part of the cookie.)</p>
   289    324   
   290    325   <h3>3.9 Request-Configuration Cards</h3>
   291    326   
   292         -<i>TBD...</i>
          327  +<p>A request-configuration or "reqconfig" card is sent from client to
          328  +server in order to request that the server send back "configuration"
          329  +data.  "Configuration" data is information about users or website
          330  +appearance or other administrative details which are not part of the
          331  +persistent and versioned state of the project.  For example, the "name"
          332  +of the project, the default Cascading Style Sheet (CSS) for the web-interface,
          333  +and the project logo displayed on the web-interface are all configuration
          334  +data elements.
          335  +
          336  +<p>The reqconfig card is normally sent in response to the
          337  +"fossil configuration pull" command.  The format is as follows:
          338  +
          339  +<blockquote>
          340  +<b>reqconfig</b> <i>configuration-name</i>
          341  +</blockquote>
          342  +
          343  +<p>As of this writing ([2010-11-12]), the configuration-name must be one of the
          344  +following values:
          345  +
          346  +<center><table border=0>
          347  +<tr><td valign="top">
          348  +<ul>
          349  +<li> css
          350  +<li> header
          351  +<li> footer
          352  +<li> logo-mimetype
          353  +<li> logo-image
          354  +<li> project-name
          355  +<li> project-description
          356  +<li> manifest
          357  +<li> index-page
          358  +<ul></td><td valign="top"><ul>
          359  +<li> timeline-block-markup
          360  +<li> timeline-max-comment
          361  +<li> ticket-table
          362  +<li> ticket-common
          363  +<li> ticket-newpage
          364  +<li> ticket-viewpage
          365  +<li> ticket-editpage
          366  +<li> ticket-reportlist
          367  +<li> ticket-report-template
          368  +<ul></td><td valign="top"><ul>
          369  +<li> ticket-key-template
          370  +<li> ticket-title-expr
          371  +<li> ticket-closed-expr
          372  +<li> @reportfmt
          373  +<li> @user
          374  +<li> @concealed
          375  +<li> @shun
          376  +</ul></td></tr>
          377  +</table></center>
          378  +
          379  +<p>New configuration-names are likely to be added in future releases of
          380  +Fossil.  If the server receives a configuration-name that it does not
          381  +understand, the entire reqconfig card is silently ignored.  The reqconfig
          382  +card might also be ignored if the user lacks sufficient privilege to
          383  +access the requested information.
          384  +
          385  +<p>The configuration-names that begin with an alphabetic character refer
          386  +to values in the "config" table of the server database.  For example,
          387  +the "logo-image" configuration item refers to the project logo image
          388  +that is configured on the Admin page of the [./webui.wiki | web-interface].
          389  +The value of the configuration item is returned to the client using a
          390  +"config" card.
          391  +
          392  +<p>If the configuration-name begins with "@", that refers to a class of
          393  +values instead of a single value.  The content of these configuration items
          394  +is returned in a "config" card that contains pure SQL text that is 
          395  +intended to be evaluated by the client.
          396  +
          397  +<p>The @user and @concealed configuration items contain sensitive information
          398  +and are ignored for clients without sufficient privilege.
   293    399   
   294    400   <h3>3.10 Configuration Cards</h3>
   295    401   
   296         -<i>TBD...</i>
          402  +<p>A "config" card is used to send configuration information from client
          403  +to server (in response to a "fossil configuration push" command) or
          404  +from server to client (in response to a "fossil configuration pull" or
          405  +"fossil clone" command).  The format is as follows:
          406  +
          407  +<blockquote>
          408  +<b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i>
          409  +</blockquote>
          410  +
          411  +<p>The server will only accept a config card if the user has
          412  +"Admin" privilege.  A client will only accept a config card if
          413  +it had sent a corresponding reqconfig card in its request.
          414  +
          415  +<p>The content of the configuration item is used to overwrite the
          416  +corresponding configuration data in the receiver.
   297    417   
   298    418   <h3>3.11 Error Cards</h3>
   299    419   
   300    420   <p>If the server discovers anything wrong with a request, it generates
   301    421   an error card in its reply.  When the client sees the error card,
   302    422   it displays an error message to the user and aborts the sync
   303    423   operation.  An error card looks like this:</p>

Added www/tech_overview.wiki.

            1  +<title>Technical Overview</title>
            2  +<h2 align="center">
            3  +A Technical Overview<br>Of The Design And Implementation<br>Of Fossil
            4  +</h2>
            5  +
            6  +<h2>1.0 Introduction</h2>
            7  +
            8  +At its lowest level, a Fossil repository consists of an unordered set
            9  +of immutable "artifacts".  You might think of these artifacts as "files",
           10  +since in many cases the artifacts exactly that.  But other "control artifacts" 
           11  +are also included in the mix.  These control artifacts define the relationships
           12  +between artifacts - which files go together to form a particular
           13  +version of the project, who checked in that version and when, what was
           14  +the check-in comment, what wiki pages are included with the project, what
           15  +are the edit histories of each wiki page, what bug reports or tickets are
           16  +included, who contributed to the evolution of each ticket, and so forth.
           17  +This low-level file format is called the "global state" of
           18  +the repository, since this is the information that is synced to peer
           19  +repositories using push and pull operations.   The low-level file format
           20  +is also called "enduring" since it is intended to last for many years.
           21  +The details of the low-level, enduring, global file format 
           22  +are [./fileformat.wiki | described separately].
           23  +
           24  +This article is about how Fossil is currently implemented.  Instead of
           25  +dealing with vague abstractions of "enduring file formats" as the
           26  +[./fileformat.wiki | other document] does, this article provides
           27  +some detail on how Fossil actually stores information on disk.  
           28  +
           29  +<h2>2.0 Three Databases</h2>
           30  +
           31  +Fossil stores state information in 
           32  +[http://www.sqlite.org/ | SQLite] database files.
           33  +SQLite keeps an entire relational database, including multiple tables and
           34  +indices, in a single disk file.  The SQLite library allows the database
           35  +files to be efficiently queried and updated using the industry-standard
           36  +SQL language.  SQLite updates are atomic, so even in the event of
           37  +a system crashes or power failure the repository content is protected.
           38  +
           39  +Fossil uses three separate classes of SQLite databases:
           40  +
           41  +<ol>
           42  +<li>The configuration database
           43  +<li>Repository databases
           44  +<li>Checkout databases
           45  +</ol>
           46  +
           47  +The configuration database is a one-per-user database that holds
           48  +global configuration information used by Fossil.  There is one
           49  +repository database per project.  The repository database is the
           50  +file that people are normally referring to when they say 
           51  +"a Fossil repository".  The checkout database is found in the working
           52  +checkout for a project and contains state information that is unique
           53  +to that working checkout.
           54  +
           55  +Fossil does not always use all three database files.  The web interface,
           56  +for example, typically only uses the repository database.  And the
           57  +[/help/all | fossil setting] command only opens the configuration database
           58  +when the --global option is used.  But other commands use all three
           59  +databases at once.  For example, the [/help/status | fossil status]
           60  +command will first locate the checkout database, then use the checkout
           61  +database to find the repository database, then open the configuration
           62  +database.  Whenever multiple databases are used at the same time,
           63  +they are all opened on the same SQLite database connection using
           64  +SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command.
           65  +
           66  +The chart below provides a quick summary of how each of these
           67  +database files are used by Fossil, with detailed discussion following.
           68  +
           69  +<center><table border="1" width="80%" cellpadding="0">
           70  +<tr>
           71  +<td width="33%" valign="top">
           72  +<h3 align="center">Configuration Database<br>"~/.fossil"</h3>
           73  +<ul>
           74  +<li>Global [/help/setting |settings]
           75  +<li>List of active repositories used by the [/help/all | all] command
           76  +</ul>
           77  +</td>
           78  +<td width="34%" valign="top">
           79  +<h3 align="center">Repository Database<br>"<i>project</i>.fossil"</h3>
           80  +<ul>
           81  +<li>[./fileformat.wiki | Global state of the project]
           82  +    encoded using delta-compression
           83  +<li>Local [/help/setting|settings]
           84  +<li>Web interface display preferences
           85  +<li>User credentials and permissions
           86  +<li>Metadata about the global state to facilitate rapid
           87  +    queries
           88  +</ul>
           89  +</td>
           90  +<td width="33%" valign="top">
           91  +<h3 align="center">Checkout Database<br>"_FOSSIL_"</h3>
           92  +<ul>
           93  +<li>The repository database used by this checkout
           94  +<li>The version currently checked out
           95  +<li>Other versions [/help/merge | merged] in but not
           96  +    yet [/help/commit | committed]
           97  +<li>Changes from the [/help/add | add], [/help/delete | delete],
           98  +    and [/help/rename | rename] commands that have not yet been committed
           99  +<li>"mtime" values and other information used to efficiently detect
          100  +     local edits
          101  +<li>The "[/help/stash | stash]"
          102  +<li>Information needed to "[/help/undo|undo]" or "[/help/redo|redo]"
          103  +</ul>
          104  +</td>
          105  +</tr>
          106  +</table>
          107  +</center>
          108  +
          109  +<h3>2.1 The Configuration Database</h3>
          110  +
          111  +The configuration database holds cross-repository preferences and a list of all
          112  +repositories for a single user.
          113  +
          114  +The [/help/setting | fossil setting] command can be used to specify various
          115  +operating parameters and preferences for Fossil repositories.  Settings can
          116  +apply to a single repository, or they can apply globally to all repositories
          117  +for a user.  If both a global and a repository value exists for a setting,
          118  +then the repository-specific value takes precedence.  All of the settings
          119  +have reasonable defaults, and so many users will never need to change them.
          120  +But if changes to settings are desired, the configuration database provides
          121  +a way to change settings for all repositories with a single command, rather
          122  +than having to change the setting individually on each repository.
          123  +
          124  +The configuration database also maintains a list of repositories.  This
          125  +list is used by the [/help/all | fossil all] command in order to run various
          126  +operations such as "sync" or "rebuild" on all repositories managed by a user.
          127  +
          128  +On unix systems, the configuration database is named ".fossil" and is
          129  +located in the user's home directory.  On windows, the configuration
          130  +database is named "_fossil" (using an underscore as the first character
          131  +instead of a dot) and is located in the directory specified by the
          132  +LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order.
          133  +
          134  +<h3>2.2 Repository Databases</h3>
          135  +
          136  +The repository database is the file that is commonly referred to as 
          137  +"the repository".  This is because the repository database contains,
          138  +among other things, the complete revision, ticket, and wiki history for
          139  +a project.  It is customary to name the repository database after then
          140  +name of the project, with a ".fossil" suffix.  For example, the repository
          141  +database for the self-hosting Fossil repository is called "fossil.fossil"
          142  +and the repository database for SQLite is called "sqlite.fossil".
          143  +
          144  +<h4>2.2.1 Global Project State</h4>
          145  +
          146  +The bulk of the repository database (typically 75 to 85%) consists
          147  +of the artifacts that comprise the 
          148  +[./fileformat.wiki | enduring, global, shared state] of the project.
          149  +The artifacts are stored as BLOBs, compressed using
          150  +[http://www.zlib.net/ | zlib compression] and, where applicable,
          151  +using [./delta_encoder_algorithm.wiki | delta compression].
          152  +The combination of zlib and delta compression results in a considerable
          153  +space savings.  For the SQLite project, at the time of this writing,
          154  +the total size of all artifacts is over 2.0 GB but thanks to the
          155  +combined zlib and delta compression, that content only takes up
          156  +32 MB of space in the repository database, for a compression ratio
          157  +of about 64:1.  The average size of a content BLOB in the database
          158  +is around 500 bytes.
          159  +
          160  +Note that the zlib and delta compression is not an inherent part of the
          161  +Fossil file format; it is just an optimization.  
          162  +The enduring file format for Fossil is the unordered
          163  +set of artifacts. The compression techniques are just a detail of
          164  +how the current implementation of Fossil happens to store these artifacts
          165  +efficiently on disk.
          166  +
          167  +All of the original uncompressed and undeltaed artifacts can be extracted
          168  +from a Fossil repository database using 
          169  +the [/help/deconstruct | fossil deconstruct]
          170  +command. Individual artifacts can be extracted using the
          171  +[/help/artifact | fossil artifact] command.
          172  +When accessing the repository database using raw SQL and the
          173  +[/help/sqlite3 | fossil sql] command, the extension function
          174  +"<tt>content()</tt>" with a single argument which is the SHA1 hash
          175  +of an artifact will return the complete undeleted and uncompressed
          176  +content of that artifact.  
          177  +
          178  +Going the other way, the [/help/reconstruct | fossil reconstruct]
          179  +command will scan a directory hierarchy and add all files found to
          180  +a new repository database.  The [/help/import | fossil import] command
          181  +works by reading the input git-fast-export stream and using it to construct
          182  +corresponding artifacts which are then written into the repository database.
          183  +
          184  +<h4>2.2.2 Project Metadata</h4>
          185  +
          186  +The global project state information in the repository database is
          187  +supplemented by computed metadata that makes querying the project state
          188  +more efficient.  Metadata includes information such as the following:
          189  +
          190  +  *  The names for all files found in any checkin.
          191  +  *  All check-ins that modify a given file
          192  +  *  Parents and children of each checkin.
          193  +  *  Potential timeline rows.
          194  +  *  The names of all symbolic tags and the checkins they apply to.
          195  +  *  The names of all wiki pages and the artifacts that comprise each
          196  +     wiki page.
          197  +  *  Attachments and the wiki pages or tickets they apply to.
          198  +  *  Current content of each ticket.
          199  +  *  Cross-references between tickets, checkins, and wiki pages.
          200  +
          201  +The metadata is held in various SQL tables in the repository database.
          202  +The metadata is designed to facilitate queries for the various timelines and
          203  +reports that Fossil generates.
          204  +As the functionality of Fossil evolves,
          205  +the schema for the metadata can and does change.
          206  +But schema changes do no invalidate the repository.  Remember that the
          207  +metadata contains no new information - only information that has been
          208  +extracted from the canonical artifacts and saved in a more useful form.
          209  +Hence, when the metadata schema changes, the prior metadata can be discarded
          210  +and the entire metadata corpus can be recomputed from the canonical
          211  +artifacts.  That is what the
          212  +[/help/rebuild | fossil rebuild] command does.
          213  +
          214  +<h4>2.2.3 Display And Processing Preferences</h4>
          215  +
          216  +The repository database also holds information used to help format
          217  +the display of web pages and configuration settings that override the
          218  +global configuration settings for the specific repository.  All of
          219  +this information (and the user credentials and privileges too) is
          220  +local to each repository database; it is not shared between repositories
          221  +by [/help/sync | fossil sync].  That is because it is entirely reasonable
          222  +that two different websites for the same project might have completely
          223  +different display preferences and user communities.  One instance of the
          224  +project might be a fork of the other, for example, which pulls from the
          225  +other but never pushes and extends the project in ways that the keepers of
          226  +the other website disapprove of.  
          227  +
          228  +Display and processing information includes the following:
          229  +
          230  +  *  The name and description of the project
          231  +  *  The CSS file, header, and footer used by all web pages
          232  +  *  The project logo image
          233  +  *  Fields of tickets that are considered "significant" and which are
          234  +     therefore collected from artifacts and made available for display
          235  +  *  Templates for screens to view, edit, and create tickets
          236  +  *  Ticket report formats and display preferences
          237  +  *  Local values for [/help/setting | settings] that override the
          238  +     global values defined in the per-user configuration database.
          239  +
          240  +Though the display and processing preferences do not move between
          241  +repository instances using [/help/sync | fossil sync], this information
          242  +can be shared between repositories using the
          243  +[/help/config | fossil config push] and 
          244  +[/help/config | fossil config pull] commands.
          245  +The display and processing information is also copied into new
          246  +repositories when they are created using
          247  +[/help/clone | fossil clone].
          248  +
          249  +<h4>2.2.4 User Credentials And Privileges</h4>
          250  +
          251  +Just because two development teams are collaborating on a project and allow
          252  +push and/or pull between their repositories does not mean that they 
          253  +trust each other enough to share passwords and access privileges.
          254  +Hence the names and emails and passwords and privileges of users are
          255  +considered private information that is kept locally in each repository.
          256  +
          257  +Each repository database has a table holding the username, privileges,
          258  +and login credentials for users authorized to interact with that particular
          259  +database.  In addition, there is a table named "concealed" that maps the 
          260  +SHA1 hash of each users email address back into their true email address.
          261  +The concealed table allows just the SHA1 hash of email addresses to
          262  +be stored in tickets, and thus prevents actual email addresses from falling
          263  +into the hands of spammers who happen to clone the repository. 
          264  +
          265  +The content of the user and concealed tables can be pushed and pulled using the
          266  +[/help/config | fossil config push] and
          267  +[/help/config | fossil config pull] commands with the "user" and
          268  +"email" as the AREA argument, but only if you have administrative
          269  +privileges on the remote repository.
          270  +
          271  +<h4>2.2.5 Shunned Artifact List</h4>
          272  +
          273  +The set of canonical artifacts for a project - the global state for the
          274  +project - is intended to be an append-only database.  In other words,
          275  +new artifacts can be added but artifacts can never be removed.  But
          276  +it sometimes happens that inappropriate content is mistakenly or
          277  +maliciously added to a repository.  The only way to get rid of
          278  +the undesired content is to [./shunning.wiki | "shun"] it.
          279  +The "shun" table in the repository database records the SHA1 hash of 
          280  +all shunned artifacts.
          281  +
          282  +The shun table can be pushed or pulled using
          283  +the [/help/config | fossil config] command with the "shun" AREA argument.
          284  +The shun table is also copied during a [/help/clone | clone].
          285  +
          286  +<h3>2.3 Checkout Databases</h3>
          287  +
          288  +Unlike several other popular DVCSes, Fossil allows a single repository
          289  +to have multiple working checkouts.  Each working checkout has a single
          290  +database in its root directory that records the state of that checkout.
          291  +The checkout database is named "_FOSSIL_" by default, but can be renamed
          292  +to ".fslckout" if desired.  (Future versions of Fossil might make
          293  +".fslckout" the default name.)  The checkout database records information
          294  +such as the following:
          295  +
          296  +  *  The name of the repository database file.
          297  +  *  The version that is currently checked out.
          298  +  *  Files that have been [/help/add | added],
          299  +     [/help/rm | removed], or [/help/mv | renamed] but not
          300  +     yet committed.
          301  +  *  The mtime and size of files as they were originally checked out,
          302  +     in order to expedite checking which files have been edited.
          303  +  *  Other checkins that have been [/help/merge | merged] into the
          304  +     working checkout but not yet committed.
          305  +  *  Copies of files prior to the most recent undoable operation - needed to
          306  +     implement the [/help/undo | undo] and [/help/redo | redo] commands.
          307  +  *  The [/help/stash | stash].
          308  +  *  State information for the [/help/bisect | bisect] command.
          309  +
          310  +For Fossil commands that run from within a working checkout, the
          311  +first thing that happens is that Fossil locates the checkout database.
          312  +Fossil first looks in the current directory.  If not found there, it
          313  +looks in the parent directory.  If not found there, the parent of the
          314  +parent.  And so forth until either the checkout database is found
          315  +or the search reaches the root of the filesystem.  (In the latter case,
          316  +Fossil returns an error, of course.)  Once the checkout database is
          317  +located, it is used to locate the repository database.
          318  +
          319  +Notice that the checkout database contains a pointer to the repository
          320  +database but that the repository database has no record of the checkout
          321  +databases.  That means that a working checkout directory tree can be
          322  +freely renamed or copied or deleted without consequence.  But the
          323  +repository database file, on the other hand, has to stay in the same
          324  +place with the same name or else the open checkout databases will not
          325  +be able to find it.
          326  +
          327  +A checkout database is created by the [/help/open | fossil open] command.
          328  +A checkout database is deleted by [/help/close | fossil close].  The
          329  +fossil close command really isn't needed; one can accomplish the same
          330  +thing simply by deleting the checkout database.
          331  +
          332  +Note that the stash, the undo stack, and the state of the bisect command
          333  +are all contained within the checkout database.  That means that the
          334  +fossil close command will delete all stash content, the undo stack, and
          335  +the bisect state.  The close command is not undoable.  Use it with care.

Changes to www/theory1.wiki.

    72     72   the unchanging bag of artifacts.  The local relational database is an
    73     73   implementation detail which currently happens to use SQLite.
    74     74   
    75     75   Another way to think of the relational tables in a Fossil repository is
    76     76   as an index for the artifacts.  Without the relational tables,
    77     77   to generate a report like a timeline would require scanning every artifact -
    78     78   the equivalent of a full table scan.  The relational tables hold pointers
    79         -the the relevant artifacts in presorted order so that generating a timeline
           79  +the relevant artifacts in presorted order so that generating a timeline
    80     80   is much more efficient.  So like an index in a relational database, the
    81     81   relational tables in an Fossil repository do not add any new information,
    82     82   they merely make the information in the artifacts faster and easier to
    83     83   look up.
    84     84   
    85     85   Fossil is not "based" on SQLite.  Fossil simply exploits SQLite as
    86     86   a powerful tool to make the implementation easier.

Added www/tickets.wiki.

            1  +<title>The Fossil Ticket System</title>
            2  +
            3  +<h2>1.0 File Format</h2>
            4  +
            5  +At its lowest level, the tickets of Fossil consist solely of
            6  +[./fileformat.wiki#tktchng | ticket change artifacts].
            7  +Each ticket change artifact corresponds to a single change
            8  +to a ticket.  The act of creating a ticket is considered a
            9  +change.
           10  +
           11  +Each ticket change artifact contains the following information:
           12  +
           13  +<ul>
           14  +<li>The ID of the ticket that was changed
           15  +<li>The timestamp for when the change occurred
           16  +<li>The user who made the change
           17  +<li>A list of key/value pairs that show what changed in the ticket
           18  +</ul>
           19  +
           20  +To determine the current state of a particular ticket, Fossil orders
           21  +the change artifacts for that ticket from oldest to most recent,
           22  +then applies each change in timestamp order.
           23  +
           24  +On each change artifact, there are one or more key/value pairs that
           25  +implement the change.  The key corresponds to a field of the ticket
           26  +that is modified.  The value may either replace the earlier value for
           27  +that key, or the value may be appended to the prior value.
           28  +
           29  +<h2>2.0 Ticket Tables</h2>
           30  +
           31  +The low-level artifact format for ticket content is tedious and
           32  +cumbersome to access in realtime.  To facility reporting and display
           33  +of tickets, the low-level artifact information is collected and
           34  +summarized in a pair of SQL tables in each local repository.  Display
           35  +and reporting of tickets is accomplished by querying these two tables.
           36  +
           37  +Note that only the low-level ticket change artifacts are synced.  The
           38  +content of the two ticket tables can always be reconstructed from the
           39  +ticket change artifacts.  And, indeed, the reconstruction of the ticket
           40  +tables from low-level artifacts happens automatically whenever new
           41  +ticket change artifacts are received by the system.  The important point
           42  +to remember is that display of tickets is accomplished using SQL tables
           43  +but that recording and syncing of ticket information is accomplished using
           44  +ticket change artifacts.
           45  +
           46  +<h3>2.1 Ticket Table Schema</h3>
           47  +
           48  +The two ticket tables are called TICKET and TICKETCHNG.
           49  +The default schema (as of this writing) for these two tables is shown
           50  +below:
           51  +
           52  +<blockquote><verbatim>
           53  +CREATE TABLE ticket(
           54  +  -- Do not change any column that begins with tkt_
           55  +  tkt_id INTEGER PRIMARY KEY,
           56  +  tkt_uuid TEXT UNIQUE,
           57  +  tkt_mtime DATE,
           58  +  tkt_ctime DATE,
           59  +  -- Add as many fields as required below this line
           60  +  type TEXT,
           61  +  status TEXT,
           62  +  subsystem TEXT,
           63  +  priority TEXT,
           64  +  severity TEXT,
           65  +  foundin TEXT,
           66  +  private_contact TEXT,
           67  +  resolution TEXT,
           68  +  title TEXT,
           69  +  comment TEXT
           70  +);
           71  +CREATE TABLE ticketchng(
           72  +  -- Do not change any column that begins with tkt_
           73  +  tkt_id INTEGER REFERENCES ticket,
           74  +  tkt_rid INTEGER REFERENCES blob,
           75  +  tkt_mtime DATE,
           76  +  -- Add as many fields as required below this line
           77  +  login TEXT,
           78  +  username TEXT,
           79  +  mimetype TEXT,
           80  +  icomment TEXT
           81  +);
           82  +CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime);
           83  +</verbatim></blockquote>
           84  +
           85  +Generally speaking, there is one row in the TICKETCHNG table for each
           86  +change to each ticket.  In other words, there is one row in the
           87  +TICKETCHNG table for each low-level ticket change artifact.  The
           88  +TICKET table, on the other hand, contains a summary of the current
           89  +status of each ticket.
           90  +
           91  +Fields of the TICKET and TICKETCHNG tables that begin with "tkt_" are
           92  +used internally by Fossil.  The logic inside of Fossil that converts
           93  +ticket change artifacts into row data for the two ticket tables expects
           94  +the "tkt_" fields to always be present.  All of the other fields of the
           95  +TICKET and TICKETCHNG tables are "user defined" in the sense that they
           96  +can be anything the administrator of the system wants them to be.  The
           97  +user-defined fields should correspond to keys in the key/value pairs of
           98  +the ticket change artifacts.
           99  +
          100  +The <b>tkt_id</b> fields of TICKET and TICKETCHNG are an integer key
          101  +used to uniquely identify the ticket to which the row belongs.  These
          102  +keys are for internal use only and may change when doing a "fossil rebuild".
          103  +
          104  +The <b>tkt_uuid</b> field is the unique hexadecimal identifier for the ticket.
          105  +Ticket identifiers appear to be SHA1 hash strings, but they
          106  +are not really the hash of any identifible artifact.  They are
          107  +just random hexadecimal numbers.  When creating a new ticket, Fossil uses
          108  +a (high-quality) pseudo-random number generator to create the ticket 
          109  +number.  The ticket numbers are large so that the chance of collision
          110  +between any two tickets is vanishingly small.
          111  +
          112  +The <b>tkt_mtime</b> field of TICKET shows the time (as a Julian day number)
          113  +of the most recent ticket change artifact for that ticket.  The
          114  +<b>tkt_mtime</b> field of TICKETCHNG shows the timestamp on the ticket
          115  +change artifact that the TICKETCHNG row refers to.  The 
          116  +<b>tkt_ctime</b> field of TICKET is the time of the oldest ticket change
          117  +artifact for that ticket, thus holding the time that the ticket was
          118  +created.
          119  +
          120  +The <b>tkt_rid</b> field of TICKETCHNG is the integer primary key in the
          121  +BLOB table of the ticket change artifact that gave rise to the row in the
          122  +TICKETCHNG table.
          123  +
          124  +All the other fields of the TICKET and TICKETCHNG tables are available
          125  +for customization for individual projects.  None of the remaining fields
          126  +are required, but all of them are needed in order to use the default
          127  +ticket creating, viewing, and editing scripts.  It is recommended that
          128  +the other fields be retained and that customizations be restricted to
          129  +adding new fields above and beyond the default.
          130  +
          131  +<h3>2.2 Translating Artifacts To Tables</h3>
          132  +
          133  +Each row in the TICKETCHNG table corresponds to a single ticket change
          134  +artifact.  The tkt_id field is the integer primary key of the TICKET
          135  +table entry for the corresponding ticket.  The tkt_rid field is the
          136  +integer primary key for the BLOB table entry that contains the low-level
          137  +artifact text.  The tkt_mtime field is the timestamp on the ticket
          138  +change artifact, expressed as a julian day number.  If the ticket
          139  +change artifact contains a key/value pair where the key is "login",
          140  +then the corresponding value is stored in the login field of the
          141  +TICKETCHNG table.  The same it true for "username", "mimetype", and
          142  +"icomment" fields.  Any time there is a key/value pair in the ticket
          143  +change artifact and the key corresponds to the name of a field in the
          144  +TICKETCHNG table, then the value of that key/value pair is stored in
          145  +the TICKETCHNG table.  If the TICKETCHNG table has a field for which there
          146  +is no corresponding key/value pair in the artifact, then that field of
          147  +the TICKETCHNG table is NULL.  If there are key/value pairs in the
          148  +artifact that have no corresponding field in the TICKETCHNG table, those
          149  +key/value pairs are silently ignored.
          150  +
          151  +Each row in the TICKET table records the overall status of a ticket.
          152  +The tkt_id field is a unique integer primary key for the ticket.
          153  +the tkt_uuid field is the global ticket identifier - a larger random
          154  +hexadecimal constant.  The tkt_mtime and tkt_ctime fields hold the
          155  +times of the most recent and the oldest ticket change artifacts for
          156  +this ticket, respectively.
          157  +
          158  +To reconstruct the TICKET table, the ticket change
          159  +artifacts are visited in timestamp order.  As each ticket change artifact is
          160  +visited, its key/value pairs are examined.  For any key/value pair in
          161  +which the key is the same as a field in the TICKET table, the value
          162  +of that pair either replaces or is appended to the previous value
          163  +of the corresponding field in the TICKET table.  Whether a value is
          164  +replaced or appended is determined by markings in the ticket change
          165  +artifact itself.  Most fields are usually replaced. (For example, to change 
          166  +the status from "Open" to "Fixed" would involve a key value pair
          167  +"status/Fixed" with the replace attribute set).  The main exception
          168  +is the "comment" field, which is usually appended with new comment
          169  +text.
          170  +
          171  +Note that the replace-or-append mark on ticket change artifacts is
          172  +only used by the TICKET table.  Since the initial value of all fields
          173  +in the TICKETCHNG table is NULL, the replace-or-append mark makes no
          174  +difference there.
          175  +
          176  +<h3>2.3 Old-Style versus New-Style Tickets</h3>
          177  +
          178  +Older versions of Fossil
          179  +(before [/timeline?c=2012-11-27+16:26:29 | 2012-11-27]) 
          180  +only supported the TICKET table.
          181  +In this older style, new comments were added to tickets by using
          182  +the append-value feature on the comment field.  Thus the TICKET.COMMENT
          183  +field contains the complete text of all user comments already appended
          184  +together and ready for display.
          185  +
          186  +A problem with the old approach is that all comment text had to
          187  +be in the same format.  In other words, the all comment text had to be
          188  +either plaintext or wiki or HTML.  It was not possible for some comments
          189  +to be in HTML and others to be plaintext.  Some site adminstrators wanted the
          190  +ability to mix plaintext, wiki, and HTML comments and display each 
          191  +comment according to its chosen format.  Hence, Fossil was enhanced to
          192  +support the "new-style" tickets.
          193  +
          194  +The TICKETCHNG table was added to support new-style tickets.  In the new
          195  +style, comment text is stored with the "icomment" (for "Incremental Comment")
          196  +key and appears separately, and with its on mimetype, in multiple rows
          197  +of the TICKETCHNG table.  It then falls to the TH1 script code on the
          198  +View Ticket Page to query the TICKETCHNG table and extract and format
          199  +the various comments in timestamp order.

Added www/uitest.html.

            1  +<html>
            2  +<head>
            3  +<title>Fossil UI Test</title>
            4  +</head>
            5  +<body>
            6  +<script>
            7  +  var aTest = [
            8  +///////////////////////////////////////////////////////////////////////////
            9  +///  Add pages to be tested below:
           10  +//////////////////////////////////////////////////////////////////////////
           11  +{
           12  + url: "timeline",
           13  + desc: 
           14  +   "Simple timeline of most recent check-ins. Verify that all submenus work."
           15  +},
           16  +{
           17  + url: "timeline?n=125",
           18  + desc: 
           19  +   "Timeline with 125 entries.  Verify that submenus preserve the entry count."
           20  +},
           21  +{
           22  + url: "wiki",
           23  + desc: 
           24  +   "The wiki homepage"
           25  +}
           26  +//////////////////////////////////////////////////////////////////////////////
           27  +///  End of testing data
           28  +/////////////////////////////////////////////////////////////////////////////
           29  +  ];
           30  +  var iTest = 0;
           31  +  var nTest = aTest.length;
           32  +  var totalTest = nTest;
           33  +  var firstTest = aTest[0];
           34  +  function gebi(x){ return document.getElementById(x); }
           35  +</script>
           36  +<style type="text/css">
           37  +  a {
           38  +    padding-top:5px;
           39  +    padding-bottom:5px;
           40  +    padding-left:10px;
           41  +    padding-right:10px;
           42  +    text-align: right;
           43  +    color: #000;
           44  +    background-color: #eef;
           45  +    vertical-align:middle;
           46  +    box-shadow: 0px 3px 4px #999;
           47  +    border-radius: 10px;
           48  +  }
           49  +</style>
           50  +<p>Test frame for the Fossil server at <span id="x1">???</span>.</p>
           51  +<ul>
           52  +<li> <span id="x2">0</span> of <span id="x3">0</span> pages checked so far.
           53  +<li> Current page: <b><span id="x5"></span></b>
           54  +<li> <span id="x6">Press "Begin" to begin testing</span>
           55  +</ul>
           56  +<a id="x-start" target="fossiltest" onclick="startTest()">Begin</a>
           57  +<a id="x-prev" target="fossiltest" onclick="prevTest()">Previous</a>
           58  +<a id="x-next" target="fossiltest" onclick="nextTest()">Next</a>
           59  +<a id="x-pass" target="fossiltest" onclick="passTest()">Test Passes</a>
           60  +<p id="x-done" style="color:green;">Testing Complete!</p>
           61  +<script>
           62  +  var re = new RegExp("/doc/[^/]+/www/.*$");
           63  +  var baseURI = document.location.href.replace(re,"/");
           64  +  gebi("x1").innerHTML = '"' + baseURI + '"';
           65  +  gebi("x3").innerHTML = nTest;
           66  +  var xprev = gebi("x-prev");
           67  +  var xnext = gebi("x-next");
           68  +  var xpass = gebi("x-pass");
           69  +  var xstart = gebi("x-start");
           70  +  gebi("x-done").hidden = 1;
           71  +  function loadPage(){
           72  +    var x = aTest[iTest];
           73  +    gebi("x5").innerHTML = x.url;
           74  +    gebi("x6").innerHTML = x.desc;
           75  +    gebi("x2").innerHTML = totalTest-nTest;
           76  +    xstart.hidden = 1;
           77  +    xpass.hidden = 0;
           78  +    if( iTest>0 ){
           79  +      var y = aTest[iTest-1];
           80  +      xprev.href = baseURI + y.url;
           81  +      xprev.hidden = 0;
           82  +    }else{
           83  +      xprev.hidden = 1;
           84  +    }
           85  +    if( iTest+1<nTest ){
           86  +      var z = aTest[iTest+1];
           87  +      xnext.href = baseURI + z.url;
           88  +      xpass.href = baseURI + z.url;
           89  +      xnext.hidden = 0;
           90  +    }else{
           91  +      xnext.hidden = 1;
           92  +      if( nTest>1 ) xpass.href = xprev.href;
           93  +    }
           94  +  }
           95  +  gebi("x3").innerHTML = nTest;
           96  +  xprev.hidden = 1;
           97  +  xnext.hidden = 1;
           98  +  xpass.hidden = 1;
           99  +  xstart.hidden = 0;
          100  +  xstart.href = baseURI + aTest[0].url;
          101  +  function startTest(){
          102  +    setTimeout(loadPage,1);
          103  +  } 
          104  +  function prevTest(){
          105  +    if( iTest<=0 ) return false;
          106  +    iTest--;
          107  +    setTimeout(loadPage,1);
          108  +  }
          109  +  function nextTest(){
          110  +    if( iTest+1>=nTest ) return false;
          111  +    iTest++;
          112  +    setTimeout(loadPage,1);
          113  +  }
          114  +  function passTest(){
          115  +    if( nTest==1 ){
          116  +      xpass.hidden = 1;
          117  +      xnext.hidden = 1;
          118  +      xprev.hidden = 1;
          119  +      gebi("x2").innerHTML = totalTest;
          120  +      gebi("x-done").hidden = 0;
          121  +      return false;
          122  +    }else{
          123  +      aTest.splice(iTest, 1);
          124  +      nTest--;
          125  +      if( iTest>=nTest ) iTest = nTest-1;
          126  +      setTimeout(loadPage,1);
          127  +    }
          128  +  }
          129  +</script>

Changes to www/webui.wiki.

     1         -<h1 align="center">The Fossil Web Interface</h1>
            1  +<title>The Fossil Web Interface</title>
     2      2   
     3         -One of the innovative features of fossil is its built-in web interface.
            3  +One of the innovative features of Fossil is its built-in web interface.
     4      4   This web interface provides everything you need to run a software
     5      5   development project:
     6      6   
     7      7     *  [./bugtheory.wiki | Ticketing and bug tracking]
     8      8     *  [./wikitheory.wiki | Wiki]
     9      9     *  [./embeddeddoc.wiki | On-line documentation]
    10     10     *  Status information
    11     11     *  Timelines
           12  +  *  Graphs of revision and branching history
           13  +  *  [./event.wiki | Blogs, News, and Announcements]
    12     14     *  File and version lists and differences
           15  +  *  Download historical versions as ZIP archives
    13     16     *  Historical change data
    14         -  *  Links to download historical versions as ZIP archives
           17  +  *  Add and remove tags on checkins
           18  +  *  Move checkins between branches
           19  +  *  Revise checkin comments
           20  +  *  Manage user credentials and access permissions
           21  +  *  And so forth...
    15     22   
    16         -You get all of this, and more, for free when you use fossil. 
           23  +You get all of this, and more, for free when you use Fossil. 
    17     24   There are no extra programs to install or setup.
    18     25   Everything you need is already pre-configured and built into the
    19         -self-contained, stand-alone fossil executable.
           26  +self-contained, stand-alone Fossil executable.
    20     27   
    21     28   As an example of how useful this web interface can be,
    22         -the entire [./index.wiki | fossil website] (except for the
    23         -[http://www.fossil-scm.org/download.html | precompiled binary download page]),
           29  +the entire [./index.wiki | Fossil website] (except for the
           30  +[http://www.fossil-scm.org/download.html | download page]),
    24     31   including the document you are now reading,
    25         -is rendered using the stock fossil web interface, with no enhancements,
           32  +is rendered using the Fossil web interface, with no enhancements,
    26     33   and little customization.
    27     34   
    28         -Note also that because fossil is a distribute system, you can run
           35  +<blockquote>
           36  +<b>Key point:</b> <i>The Fossil website is just a running instance
           37  +of Fossil!
           38  +</blockquote>
           39  +
           40  +Note also that because Fossil is a distributed system, you can run
    29     41   the web interface on your local machine while off network (for example,
    30     42   while on an airplane) including
    31     43   making changes to wiki pages and/or trouble ticket, then synchronize with your
    32         -co-workers after you reconnect.
           44  +co-workers after you reconnect.  When you clone a Fossil repository, you
           45  +don't just get the project source code, you get the entire project
           46  +management website.
    33     47   
    34     48   <h2>Drop-Dead Simple Startup</h2>
    35     49   
    36         -To start using the built-in fossil web interface on an existing fossil
           50  +To start using the built-in Fossil web interface on an existing Fossil
    37     51   repository, simply type this:
    38     52   
    39     53      <b>fossil ui existing-repository.fossil</b>
    40     54   
    41     55   Substitute the name of your repository, of course.
    42     56   The "ui" command will start a webserver running (it figures out an
    43     57   available TCP port to use on its own) and then automatically launches 
    44     58   your web browser to point at that server.  If you run the "ui" command
    45     59   from within an open check-out, you can omit the repository name:
    46     60   
    47     61     <b>fossil ui</b>
    48     62   
    49     63   The latter case is a very useful short-cut when you are working on a
    50         -fossil project and you want to quickly do some work with the web interface.
    51         -Notice that fossil automatically finds an unused TCP port to run the
           64  +Fossil project and you want to quickly do some work with the web interface.
           65  +Notice that Fossil automatically finds an unused TCP port to run the
    52     66   server own and automatically points your web browser to the correct
    53     67   URL.  So there is never any fumbling around trying to find an open
    54     68   port or to type arcane strings into your browser URL entry box.
    55     69   The interface just pops right up, ready to run.
    56     70   
    57         -The fossil web interface is also very easy to setup and run on a
           71  +The Fossil web interface is also very easy to setup and run on a
    58     72   network server, as either a CGI program or from inetd.  Details on how
    59     73   to do that are described further below.
    60     74   
    61     75   <h2>Things To Do Using The Web Interface</h2>
    62     76   
    63     77   You can view <b>timelines</b> of changes to the project.  The default
    64     78   "Timeline" link on the menu bar takes you to a page that shows the 20
    65         -most recent check-ins, wiki page edits, and ticket/bug-report changes.
           79  +most recent check-ins, wiki page edits, ticket/bug-report changes,
           80  +and/or blog entries.
    66     81   This gives a very useful snapshot of what has been happening lately on the
    67     82   project.  You can click to go further back in time, if needed.  Or
    68     83   follow hyperlinks to see details, including diffs and annotated diffs,
    69         -of individual check-ins, wiki page edits, or ticket changes.
           84  +of individual check-ins, wiki page edits, ticket changes, and
           85  +blog edits.
    70     86   
    71     87   You can view and edit <b>tickets and bug reports</b> by following the
    72     88   "Tickets" link on the menu bar.
    73     89   Fossil is backed by an SQL database, so users with appropriate permissions
    74     90   can write new ticket report formats based on SQL query statements.
    75     91   Fossil is careful to prevent ticket report formats from doing any mischief
    76     92   on the database (it only allows SELECT statements to run) and it restricts
................................................................................
    84    100   You can view and edit <b>wiki</b> by following the "Wiki" link on the
    85    101   menu bar.  Fossil uses simple and easy-to-remember
    86    102   [/wiki_rules | wiki formatting rules] so you won't have to spend a lot
    87    103   of time learning a new markup language.  And, as with tickets, all of
    88    104   your edits will automatically merge with those of your co-workers when
    89    105   your repository synchronizes.
    90    106   
    91         -You can view summary reports of <b>leaves and branches</b> in the
    92         -check-in graph by visiting the "Leaves" or "Branches" links on the
          107  +You can view summary reports of <b>branches</b> in the
          108  +check-in graph by visiting the "Branche" links on the
    93    109   menu bar.  From those pages you can follow hyperlinks to get additional
    94    110   details.  These screens allow you to easily keep track of what is going
    95    111   on with separate subteams within your project team.
    96    112   
    97    113   The "Files" link on the menu allows you to browse though the <b>file
    98    114   hierarchy</b> of the project and to view complete changes histories on
    99    115   individual files, with hyperlinks to the check-ins that made those
................................................................................
   114    130   for the entire page.  You can even change around the main menu.
   115    131   Timeline display preferences can be edited.  The page that is brought
   116    132   up as the "Home" page can be changed.  It is often useful to set the
   117    133   "Home" page to be a wiki page or an embedded document.
   118    134   
   119    135   <h2>Installing On A Network Server</h2>
   120    136   
   121         -When you create a new fossil project and after you have configured it
          137  +When you create a new Fossil project and after you have configured it
   122    138   like you want it using the web interface, you can make the project
   123    139   available to a distributed team by simply copying the single
   124    140   repository file up to a web server that supports CGI.  Just put the
   125    141   <b>sample-project.fossil</b> file in a directory where CGI scripts
   126    142   have both read and write permission on the file and the directory that
   127    143   contains the file, then add a CGI script that looks something like this:
   128    144   
   129    145     <verbatim>
   130    146     #!/usr/local/bin/fossil
   131    147     repository: /home/www/sample-project.fossil
   132    148     </verbatim>
   133    149   
   134    150   Adjust the script above so that the paths are correct for your system,
   135         -of course, and also make sure the fossil binary is installed on the server.
          151  +of course, and also make sure the Fossil binary is installed on the server.
   136    152   But that is <u>all</u> you have to do.  You now have everything you need to host
   137    153   a distributed software development project in less than five minutes using a 
   138    154   two-line CGI script.
   139    155   
   140    156   You don't have a CGI-capable web server running on your server machine?
   141         -Not a problem.  The fossil interface can also be launched via inetd or
   142         -xinetd.  An inetd configuration line sufficient to launch the fossil
          157  +Not a problem.  The Fossil interface can also be launched via inetd or
          158  +xinetd.  An inetd configuration line sufficient to launch the Fossil
   143    159   web interface looks like this:
   144    160   
   145    161     <verbatim>
   146    162     80 stream tcp nowait.1000 root /usr/local/bin/fossil \
   147    163     /usr/local/bin/fossil http /home/www/sample-project.fossil 
   148    164     </verbatim>
   149    165   
   150    166   As always, you'll want to adjust the pathnames to whatever is appropriate
   151    167   for your system.  The xinetd setup uses a different syntax but follows
   152    168   the same idea.

Changes to www/wikitheory.wiki.

     1         -<h1>Wiki In [./index.wiki  | Fossil]</h1>
            1  +<title>Wiki In Fossil</title>
            2  +<h2>Introduction</h2>
     2      3   
     3      4   Fossil uses [/wiki_rules | wiki markup] for many things:
     4      5   
     5      6      *  Stand-alone wiki pages.
     6      7      *  Description and comments in [./bugtheory.wiki | bug reports].
     7      8      *  Check-in comments.
     8      9      *  [./embeddeddoc.wiki | Embedded documentation] files whose
     9     10         name ends in "wiki".
           11  +   *  [./event.wiki | Event descriptions].
    10     12   
    11     13   The [/wiki_rules | formatting rules] for fossil wiki
    12     14   are designed to be simple and intuitive.  The idea is that wiki provides
    13         -paragraph breaks, numbered and bulletted lists, and hyperlinking for
           15  +paragraph breaks, numbered and bulleted lists, and hyperlinking for
    14     16   simple documents together with a safe subset of HTML for more complex
    15     17   formatting tasks.
    16     18   
    17     19   Some commentators feel that the use of HTML is a mistake and that
    18     20   fossil should use the markup language of the
    19     21   <i>fill-in-your-favorite</i> wiki engine instead.  That approach
    20     22   was considered but was rejected for the following reasons:
................................................................................
    53     55   of type [./fileformat.wiki#wikichng | "Wiki Page"].
    54     56   
    55     57   <h2>Embedded Documentation</h2>
    56     58   
    57     59   Files in the source tree that use the ".wiki" suffix can be accessed
    58     60   and displayed using special URLs to the fossil server.  This allows
    59     61   project documentation to be stored in the source tree and accessed 
    60         -online.  (Details are descripted [./embeddeddoc.wiki | separately].)
           62  +online.  (Details are described [./embeddeddoc.wiki | separately].)
    61     63   
    62         -Some project prefer to store their documentation in wiki.  There is nothing
           64  +Some projects prefer to store their documentation in wiki.  There is nothing
    63     65   wrong with that.  But other projects prefer to keep documentation as part
    64     66   of the source tree, so that it is versioned along with the source tree and
    65     67   so that only developers with check-in privileges can change it.  
    66     68   Embedded documentation serves this latter purpose.  Both forms of documentation
    67     69   use the exact same wiki markup language.  Some projects may choose to
    68     70   use both forms of documentation at the same time.  Because the same
    69         -format is used, it is trival to move file from wiki to embedded documentation
           71  +format is used, it is trivial to move a file from wiki to embedded documentation
    70     72   or back again as the project evolves.
    71     73   
    72     74   <h2>Bug-reports and check-in comments</h2>
    73     75   
    74     76   The comments on check-ins and the text in the descriptions of bug reports
    75     77   both use wiki formatting.  Exactly the same set of formatting rules apply.
    76     78   There is never a need to learn one formatting language for documentation
    77     79   and a different markup for bugs or for check-in comments.